updated bookletify to automatically merge 2 pages per sheet

This commit is contained in:
2023-05-24 11:32:38 +02:00
parent 789c9308d4
commit 76ceb6dfb8
+77 -86
View File
@@ -1,72 +1,50 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import sys import argparse
from math import ceil from PyPDF2 import PageObject, PdfReader, PdfWriter, PaperSize, Transformation
from PyPDF2 import PdfReader, PdfWriter
def main(): def main():
def print_help(): PROGRAM_NAME="bookletify"
print('bookletify') DESCRIPTION = """Rearranges the pages of a pdf in order to print it as is,
print() so that you can take it, cut it in half and close it like you would
print('USAGE') with a book. The result will be the original pdf in the correct order,
print(' bookletify [OPTIONS] file') with the size of half a page. Especially useful to print pdfs to A5
print() size on A4 paper"""
print('DESCRIPTION')
print(' Rearranges the pages of a pdf in order to print it as is (double')
print(' sided, flipped on the short edge) so that you can take it, cut ')
print(' in half and close it like you would do with a book. The result ')
print(' will be the original pdf in the correct order, with the size of ')
print(' half a page')
print(' Especially useful to print pdfs to A5 size on A4 paper')
print()
print('OPTIONS')
print(' -h print help')
print(' -q do not print anything to stdout')
def parse_argv(): parser = argparse.ArgumentParser(
opts = { prog=PROGRAM_NAME,
"h": False, description=DESCRIPTION
"q": False, )
}
filename = None
if len(sys.argv) < 2: parser.add_argument('filename')
opts["h"] = True parser.add_argument('-q', '--quiet',
action='store_true',
help='suppress stdout')
parser.add_argument('-s', '--size',
action='store',
choices=['A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'C4'],
default='A4',
metavar='SIZE',
help="""set final paper size. Possible values: 'A0',
..., 'A8' or 'C4'. Default value is 'A4'""")
for arg in sys.argv[1:]: parser.add_argument('-b', '--binding-margin',
if arg.startswith('-'): default=10,
for opt in opts: type=float,
if opt in arg: metavar='MARGIN',
opts[opt] = True help="""internal margin for the binding, expressed in
elif not filename: millimeters. Default value is 10""")
filename = arg
return (opts, filename) args = parser.parse_args()
def info(text = '', end='\n'): info = lambda text='', end='\n': print(text, end=end)
if not opts["q"]: if args.quiet:
print(text, end=end) info = lambda text='', end='\n': None
def prog(curr, max_val): prog = lambda curr, max: info(f"\r{curr} / {max}", '\n' if curr == max else '')
# Overrides the current terminal line with a progressbar filled at curr/max_val
perc = float(curr) / float(max_val)
remaining = f'{curr:{len(str(max_val))}d}/{max_val}'
(cols, _) = os.get_terminal_size()
max_width = cols - len(remaining) - 3
size = min(max_width, max_val, 80)
bar = int(perc * size)
end = '\n' if curr == max_val else ''
info("\r|" + ("\u2588" * bar) + "_" * (size - bar) + "| " + remaining, end)
(opts, input_name) = parse_argv()
if opts["h"] or not input_name:
print_help()
exit(0)
input_name = args.filename
if not os.path.isfile(input_name): if not os.path.isfile(input_name):
print(f'file {input_name} does not exist') print(f'file {input_name} does not exist')
exit(1) exit(1)
@@ -76,46 +54,59 @@ def main():
output_name = f'{input_name[0:-4]}-rearranged.pdf' output_name = f'{input_name[0:-4]}-rearranged.pdf'
fullwidth = getattr(PaperSize, args.size).width
fullheight = getattr(PaperSize, args.size).height
smallwidth = fullheight / 2
smallheight = fullwidth
# Convert mm to pixels at 72 dpi
bindingwidth = args.binding_margin * 72 / float(25.4)
smallblank = PageObject.create_blank_page(width=smallwidth - bindingwidth, height=smallheight)
# Read the input # Read the input
reader = PdfReader(input_name) reader = PdfReader(input_name)
# Write the result to a file
middle = PdfWriter()
info("Loading...")
n = len(reader.pages) n = len(reader.pages)
# Set m as the smallest multiple of 4 bigger or equal to n pages = []
m = ceil(n / 4) * 4
info("Reading...")
for i in range(n): for i in range(n):
prog(i, m) prog(i, n)
middle.add_page(reader.pages[i]) pages.append(reader.pages[i])
prog(n,n)
for i in range(m - n): while(len(pages) % 4 != 0):
prog(n + i, m) pages.append(smallblank)
middle.add_blank_page()
prog(m, m)
output = PdfWriter() output = PdfWriter()
info() info("\nRearranging...")
info("Rearranging...")
for i in range(int(m / 4)):
mod = i * 2
prog(i * 4, m)
output.add_page(middle.pages[m - 1 - mod])
prog(i * 4 + 1, m)
output.add_page(middle.pages[mod])
prog(i * 4 + 2, m)
output.add_page(middle.pages[1 + mod])
prog(i * 4 + 3, m)
output.add_page(middle.pages[m - 2 - mod])
prog(m, m)
info() def merge_srt_page(base, over, angle, tx, ty):
info("Saving...") sx = float(smallwidth - bindingwidth) / float(over.mediabox.width)
sy = float(smallheight) / float(over.mediabox.height)
t = Transformation().scale(sx, sy).rotate(angle). translate(tx, ty)
over.add_transformation(t)
base.merge_page(over)
m = len(pages)
for i in range(int(m / 2)):
prog(i, int(m / 2))
new_page = PageObject.create_blank_page(width=fullwidth, height=fullheight)
if i % 2 == 0:
merge_srt_page(new_page, pages[m - 1 - i], -90, 0, fullheight)
merge_srt_page(new_page, pages[i], - 90, 0, smallwidth - bindingwidth)
pass
else:
merge_srt_page(new_page, pages[m - 1 - i], 90, smallheight, smallwidth + bindingwidth)
merge_srt_page(new_page, pages[i], 90, smallheight, 0)
output.add_page(new_page)
prog(int(m/2), int(m/2))
info("\nSaving...")
output.write(output_name) output.write(output_name)
info("Done!") info("\nDone!")
info()
if __name__ == "__main__": if __name__ == "__main__":
main() main()