forked from igor-niv/img2pdf
-
Notifications
You must be signed in to change notification settings - Fork 1
/
img2pdf.py
executable file
·241 lines (209 loc) · 8.81 KB
/
img2pdf.py
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#!/usr/bin/python3
from os.path import exists, isfile, join, expanduser, basename, isdir, dirname
from shutil import copyfile, rmtree
import sys
from reportlab import platypus
from reportlab.platypus import Paragraph
from reportlab.lib.units import inch
from reportlab.lib.styles import getSampleStyleSheet
from PIL import Image
from tempfile import mkdtemp
from re import search
from os import remove, listdir
import argparse
A4_WIDTH_MM = 210 #A4 width (mm)
A4_HEIGHT_MM = 297 #A4 height (mm)
#current program is set to put 2 images per A4 size page in portrait mode,
#change below 2 globals to other values like Letter size etc
PAGE_WIDTH_MM = A4_WIDTH_MM
PAGE_HEIGHT_MM = A4_HEIGHT_MM
#SCALE_* globals denote a number found by trial by which any sized image
#be will be fit into the page (width/height variables set above)
#occupy most of page leaving little white space around images. Modify
#for sizes other than A4 (also modify PAGE_* globals above for non-A4)
SCALE_FULL_A4 = 2.3
SCALE_FULL = SCALE_FULL_A4
#leave some white space around images
SCALE_SPACED_A4 = 1.9
SCALE_SPACED = SCALE_SPACED_A4
#white spaces to put before and after each image (inches)
SPACE_BEFORE_IMAGE1 = 0.2 #inch
SPACE_AFTER_IMAGE1 = 0.2 #inch
SPACE_BEFORE_IMAGE2 = 0.4 #inch
SPACE_AFTER_IMAGE2 = 0.2 #inch
DESC_FILE_NAME = 'description.txt'
PATH_DESC_MAP = {}
DESC_DONE = {}
styles = getSampleStyleSheet()
style_heading1 = styles['Heading1']
class PdfCreator:
def __init__(self, src_images_path, dest_pdf_path = None, scale = SCALE_FULL,
space_before_image1 = SPACE_BEFORE_IMAGE1,
space_after_image1 = SPACE_AFTER_IMAGE1,
space_before_image2 = SPACE_BEFORE_IMAGE2,
space_after_image2 = SPACE_AFTER_IMAGE2):
if not src_images_path:
print('No input images specified...exiting')
sys.exit(1)
self.__imagePaths = src_images_path
self.__tempDir = mkdtemp()
self.__pdfPath = join(expanduser('~'), "workout.pdf") if not dest_pdf_path else dest_pdf_path
self.__scale = scale
self.__space_before_image1 = space_before_image1
self.__space_after_image1 = space_after_image1
self.__space_before_image2 = space_before_image2
self.__space_after_image2 = space_after_image2
@property
def tempDir(self):
return self.__tempDir
@tempDir.setter
def tempDir(self, tempDir):
if not isinstance(tempDir, str):
raise TypeError
import sys
assert exists(tempDir), "The temp directory path must be valid"
self.__tempDir = tempDir
@property
def dest_pdf_path(self):
return self.__pdfPath
def __filterImgFiles(self):
paths = list(filter(self.__isImageFile, self.__imagePaths))
if not len(paths):
print("Error: there are no files for processing!")
return None
return paths
#removed because copying to temp files not really required
# tmpPaths = []
# for src in paths:
# dst = join(self.__tempDir, basename(src))
# copyfile(src, dst)
# tmpPaths.append(dst)
# return tmpPaths
#internal function to put 2 images per A4 sized PDF page in portrait mode
def __createPdfFromImages(self, src_images_path):
doc = platypus.SimpleDocTemplate(self.__pdfPath)
doc.leftMargin = 0
doc.bottomMargin = 0
doc.rightMargin = 0
doc.topMargin = 0
pageWidth = PAGE_WIDTH_MM * self.__scale
story = []
hasStory = False
i = 1;
for image_file in src_images_path:
try:
pilImg = Image.open(image_file)
print('Adding ' + basename(image_file) + ' to pdf document...')
except Exception:
print('Cannot access a file: ' + image_file)
continue
imageWidth = pilImg.size[0]
print('imageWidth:', imageWidth)
imageHeight = pilImg.size[1]
print('imageHeight:', imageHeight)
# desc = 'Paragraph number
desc_file = join(dirname(image_file), DESC_FILE_NAME)
print('desc_file:', desc_file)
desc = None
if exists(desc_file):
desc = read_desc(desc_file);
print('desc:', desc)
if desc is not None and not DESC_DONE.get(desc_file, False):
para = Paragraph(desc, style_heading1)
story.append(para)
DESC_DONE[desc_file] = True
#put different spaces before first and second images on every page
if not i % 2:
story.append(platypus.Spacer(1, self.__space_before_image1 * inch))
else:
story.append(platypus.Spacer(1, self.__space_before_image2 * inch))
repImg = platypus.Image(image_file, pageWidth,
pageWidth * (imageHeight/imageWidth))
story.append(repImg)
#put some space after every image
if not i % 2:
story.append(platypus.Spacer(1, self.__space_after_image1 * inch))
else:
story.append(platypus.Spacer(1, self.__space_after_image2 * inch))
#break page after every 2 images
if not i % 2:
story.append(platypus.PageBreak())
i += 1
print('OK')
hasStory = True
doc.build(story)
if hasStory:
print("Pdf file was created successfully")
else:
print("Pdf file was not created")
if exists(self.__pdfPath):
remove(self.__pdfPath)
return hasStory
def createPdf(self):
src_images_path = self.__filterImgFiles()
if not src_images_path:
return False
result = self.__createPdfFromImages(src_images_path)
rmtree(self.__tempDir)
return result
def __isImageFile(self, p):
return exists(p) and isfile(p) and search(r'\.jpg$|\.bmp$|\.tiff$|\.png$|\.gif$|\.jpeg$', p) != None
########################################################################
def recursiveSearch(p, fps):
def enumFilesInDir():
_files = listdir(p)
desc_found = False
for i in range(0, len(_files)):
file = _files[i]
_files[i] = join(p, file)
if not desc_found and exists(file) and isfile(file) and basename(file) == DESC_FILE_NAME:
PATH_DESC_MAP[p] = _files[i]
desc_found = True
return _files
files = enumFilesInDir()
for f in files:
assert exists(f), "File or directory not found: " + f
if isdir(f):
recursiveSearch(f, fps)
elif isfile(f):
fps.append(f)
# def recurse_create_pdf(p, fps):
def parseArgs():
parser = argparse.ArgumentParser(description="img2pdf.py is very simple python script to convert "
"image files to a single pdf file (A4 paper size)")
parser.add_argument("-d", "--directories",
help="search image files in specified directories (recursive search only)", type=str, nargs="+")
# parser.add_argument("-f", "--files",
# help="image file names", type=str, nargs="+")
# parser.add_argument("--printer",
# help="if this option is enabled, "
# "script createPdf the pdf file and print it on a default printer", action="store_true")
parser.add_argument("--out",
help="Full path of the PDF file (~/workout.pdf is default) ", type=str)
args = parser.parse_args()
src_images_path = []
if args.directories:
for src_dir in args.directories:
recursiveSearch(src_dir, src_images_path)
# if args.files:
# for file in args.files:
# assert exists(file) and isfile(file), "File or directory not found: " + file
# src_images_path.append(file)
return src_images_path, args.out
########################################################################
#read and return text from description file in each images folder. Description
#text will most likely be a single line of text which describes the images in that
#folder
def read_desc(file_name):
desc = None
with open(file_name, 'r') as myfile:
desc = myfile.read()
return desc
if __name__ == "__main__":
src_images_path, dest_pdf_path = parseArgs()
pdfCreator = PdfCreator(src_images_path, dest_pdf_path, SCALE_FULL,
SPACE_BEFORE_IMAGE1, SPACE_AFTER_IMAGE1,
SPACE_BEFORE_IMAGE2, SPACE_AFTER_IMAGE2)
pdfCreator.createPdf()
# if isPrint:
# subprocess.call(["lpr", pdfCreator.dest_pdf_path])