You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dm-scripts/bookletify.py

153 lines
6.3 KiB
Python

2 years ago
#!/usr/bin/env python3
import os
import argparse
from PyPDF2 import PageObject, PdfReader, PdfWriter, PaperSize, Transformation
2 years ago
def main():
PROGRAM_NAME="bookletify"
DESCRIPTION = """Rearranges the pages of a pdf in order to print it as is,
so that you can take it, cut it in half and close it like you would
with a book. The result will be the original pdf in the correct order,
with the size of half a page. Especially useful to print pdfs to A5
size on A4 paper"""
parser = argparse.ArgumentParser(prog=PROGRAM_NAME, description=DESCRIPTION)
parser.add_argument('filename')
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'""")
parser.add_argument('-b', '--binding-margin',
default=10,
type=float,
metavar='MARGIN',
help="""internal margin for the binding, expressed in
millimeters relative to the final paper size (ie:
half of the height of the paper size specified by
--size). Set to 0 to disable. This setting is
independent to the trim setting, ie: increasing or
decreasing this margin does not trim any content on
the final pdf, the content gets scaled down (or up)
to fit to the final width minus the binding margin.
Default value is 10""")
parser.add_argument('-t', '--trim',
default=[0,0,0,0],
type=float,
nargs=4,
metavar='T',
help="""margins to trim, expressed in millimeters
relative to the original page size, as: top right
bottom left. This setting is independent to the
binding margin, ie: first the trim is applied, then
the trimmed content gets scaled up (or down) to the
full height and width, minus the binding margin.
Changing the trim does not increase or decrease the
final binding margin. A negative trim adds an empty
border on that side. Default value is 0 0 0 0""")
args = parser.parse_args()
info = lambda text='', end='\n': print(text, end=end)
if args.quiet:
info = lambda text='', end='\n': None
2 years ago
prog = lambda curr, max: info(f"\r{curr} / {max}", '\n' if curr == max else '')
input_name = args.filename
2 years ago
if not os.path.isfile(input_name):
print(f'file {input_name} does not exist')
exit(1)
output_name = f'{input_name}-rearranged'
if (input_name.endswith('.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.0 / 25.4
smallblank = PageObject.create_blank_page(width=smallwidth - bindingwidth, height=smallheight)
tt = args.trim[0] * 72.0 / 25.4
tr = args.trim[1] * 72.0 / 25.4
tb = args.trim[2] * 72.0 / 25.4
tl = args.trim[3] * 72.0 / 25.4
2 years ago
# Read the input
reader = PdfReader(input_name)
n = len(reader.pages)
pages = []
2 years ago
info("Reading...")
2 years ago
for i in range(n):
prog(i, n)
pages.append(reader.pages[i])
prog(n,n)
while(len(pages) % 4 != 0):
pages.append(smallblank)
2 years ago
2 years ago
output = PdfWriter()
info("\nRearranging...")
def get_scaling(page):
sx = float(smallwidth - bindingwidth) / (float(page.mediabox.width) - tr - tl)
sy = float(smallheight) / (float(page.mediabox.height) - tt - tb)
return (sx, sy)
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:
(sx, sy) = get_scaling(pages[m-1-i])
t = Transformation().scale(sx, sy).rotate(-90).translate(-tb * sy, fullheight + tl * sx)
pages[m-1-i].add_transformation(t)
pages[m-1-i].cropbox.upper_right = (smallheight, fullheight)
pages[m-1-i].cropbox.lower_left = (0, smallwidth + bindingwidth)
new_page.merge_page(pages[m-1-i])
(sx, sy) = get_scaling(pages[i])
t = Transformation().scale(sx, sy).rotate(-90).translate(-tb * sy, smallwidth - bindingwidth + tl * sx)
pages[i].add_transformation(t)
pages[i].cropbox.upper_right = (smallheight, smallwidth - bindingwidth)
pages[i].cropbox.lower_left = (0, 0)
new_page.merge_page(pages[i])
else:
(sx, sy) = get_scaling(pages[m-1-i])
t = Transformation().scale(sx, sy).rotate(90).translate(smallheight + tb * sy, smallwidth + bindingwidth - tl * sx)
pages[m-1-i].add_transformation(t)
pages[m-1-i].cropbox.upper_right = (smallheight, fullheight)
pages[m-1-i].cropbox.lower_left = (0, smallwidth + bindingwidth)
new_page.merge_page(pages[m-1-i])
(sx, sy) = get_scaling(pages[i])
t = Transformation().scale(sx, sy).rotate(90).translate(smallheight + tb * sy, 0 - tl * sx)
pages[i].add_transformation(t)
pages[i].cropbox.upper_right = (smallheight, smallwidth - bindingwidth)
pages[i].cropbox.lower_left = (0, 0)
new_page.merge_page(pages[i])
output.add_page(new_page)
prog(int(m/2), int(m/2))
info("\nSaving...")
2 years ago
output.write(output_name)
info("\nDone!")
2 years ago
if __name__ == "__main__":
main()