It is a simple script to create a pdf from a tree of images.
An outline will be produced, according to the tree, with :
- for a folder : the name of the folder
- for a file : the first found of : ImageDescription Exif tag, JPEG comment, filename (without extension)
In a folder, the files are alphabetically sorted and folders are processed first.
For items provided on the command line, no sort is applied, you must sort yourself according your wish.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | #! /usr/bin/python # -*- encoding: utf-8 -*- """ (c) 2013 - Rémi Peyronnet pdf-build.py -o output.pdf files or folder to include Other usefull tools : - extract images from pdf : pdfimages <pdf> <prefix> (with -j for jpeg) - pdftk to uncompress / compress Examples : pdf-build.py --convert="-resample 150 -quality 80%" Classeur MPSI/* -o ClasseurMPSI.pdf -t "Classeur MPSI" -a "Rémi Peyronnet" -p """ import os import os.path import sys import argparse import subprocess from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A4 from reportlab.lib.utils import haveImages from reportlab.lib.units import cm, inch from reportlab import rl_config from PIL import Image from PIL.ExifTags import TAGS # No A85 encoding, to save 1/5 space rl_config.useA85 = 0 pageSize = A4 cur_page = 0 cur_dir = 0 def getCurBkKey(): global cur_page return "p%d" % cur_page def preparePage(canvas): global cur_page cur_page = cur_page + 1 canvas.bookmarkPage(getCurBkKey()) def processFile(canvas, file, args, level=0): global pageSize if args.verbose: print "Processing file <%s> / %d" % (file, level) if args.convert: (file_base, file_ext) = os.path.splitext(file) tmpfile = file_base + ".tmp" + file_ext command = ['convert',file] + args.convert.split(" ")+ [tmpfile] try: out = subprocess.check_output(command,stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print "Error in picasa (%r) : %s" % (command, e.output) raise if os.path.exists(tmpfile): file = tmpfile else: print "Error converting <%s> : %s" % out try: img = Image.open(file) except: if args.verbose: print "Error <%s> : unable to open the file" % file return if "_getexif" in dir(img): exif = img._getexif() else: exif = {} if args.verbose: print "Warning <%s> : no exif found" % file (img_width, img_height) = img.size # Get Title : exif ImageDescription -> jpeg comment -> filename title = "" if len(title) == 0: if 270 in exif: title = exif[270].decode('utf-8') if len(title) == 0: if ("app" in dir(img)) and ("COM" in img.app): title = img.app["COM"].decode(args.jpeg_comment_encoding) if len(title) == 0: (path_, basename) = os.path.split(file) (name, ext_) = os.path.splitext(basename) title = name # Get Page Size if args.pagesize_from_exif: # Get X/Y Resolution and unit if 282 in exif: exif_x_resolution = exif[282] else: exif_x_resolution = (72, 1) if args.verbose: print "Warning <%s>: no X resolution, using default." % file if 283 in exif: exif_y_resolution = exif[283] else: exif_y_resolution = (72, 1) if args.verbose: print "Warning <%s>: no Y resolution, using default." % file exif_unit = inch if (296 in exif) and (exif[296] == 3): exif_unit = cm page_xres = (img.size[0] / (exif_x_resolution[0] / exif_x_resolution[1])) * exif_unit page_yres = (img.size[1] / (exif_y_resolution[0] / exif_y_resolution[1])) * exif_unit else: if (img_width > img_height): page_xres = pageSize[1] page_yres = pageSize[0] else: page_xres = pageSize[0] page_yres = pageSize[1] canvas.addOutlineEntry(title,getCurBkKey(), level, 0) canvas.setPageSize((page_xres, page_yres)) canvas.drawImage(file, 0, 0, page_xres, page_yres, preserveAspectRatio=True) canvas.showPage() preparePage(canvas) if args.convert: if os.path.exists(tmpfile): os.unlink(tmpfile) def processPath(canvas, path, args, level=0): global cur_dir if os.path.isdir(path): cur_dir = cur_dir + 1 dir_bk = "d%d" % cur_dir (rootpath_, name) = os.path.split(path) canvas.bookmarkPage(dir_bk) canvas.addOutlineEntry(name, dir_bk, level, False if level < args.open_levels else True) files = [] lst = os.listdir(path) lst.sort() #Traverse dir and process folders before files for file in lst: if os.path.isdir(os.path.join(path,file)): processPath(canvas, os.path.join(path,file), args, level+1) else: files.append(file) # Process files for file in files: processFile(canvas, os.path.join(path,file), args, level+1) else: processFile(canvas, path, args, level) def process(args): c = canvas.Canvas(args.output) c.setPageCompression(1) c.setAuthor(args.set_author) c.setCreator(args.set_creator) c.setSubject(args.set_subject) c.setTitle(args.set_title) for (index, file) in enumerate(args.files): preparePage(c) processPath(c,file,args,0) c.showOutline() c.save() if not haveImages: print "You don't have support for images ; please install PIL" sys.exit(1) parser = argparse.ArgumentParser("Construct flat album folders to import photos in Picasa") parser.add_argument("-o", "--output", default="output.pdf", help="Filename of output file") parser.add_argument("-v", "--verbose", action="store_true", help="Verbose mode") parser.add_argument("-p","--pagesize-from-exif", action="store_true", help="Set the PDF pagesize according the size of the image") parser.add_argument("-a","--set-author", default="", help="Set the the Author metatag") parser.add_argument("--set-creator", default="pdf-build", help="Set the Creator metatag") parser.add_argument("--set-subject", default="", help="Set the Subject metatag") parser.add_argument("--jpeg-comment-encoding", default="latin1", help="Set encoding of JPEG comments") parser.add_argument("-t","--set-title", default="", help="Set the Title metatag") parser.add_argument("-c","--convert", default="", help="Convert image with ImageMagick's convert before inclusion (provide conversion string)") parser.add_argument("-l","--open-levels", type=int, default=0, help="Number of levels to open in outline") parser.add_argument("files", help="Images files to include", nargs="*") args = parser.parse_args() process(args) |