Merge branch 'main' of git.phc.dm.unipi.it:phc/dm-scripts
commit
aa6bda62d5
@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import io
|
||||
import argparse
|
||||
from PyPDF2 import PageObject, PdfReader, PdfWriter, PaperSize, Transformation
|
||||
|
||||
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('input',
|
||||
help="""name of the input file to bookletify. Use '-'
|
||||
to read file from stdin""")
|
||||
parser.add_argument('output',
|
||||
help="""name of the output file to write to. Use '-' to
|
||||
write to stdout""")
|
||||
parser.add_argument('-q', '--quiet',
|
||||
action='store_true',
|
||||
help="""suppress stdout. Not suppressed if the flag
|
||||
this flag is absent. Automatically suppressed if
|
||||
output is '-' (see output), to avoid broken
|
||||
pdfs""")
|
||||
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=8.0,
|
||||
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 8""")
|
||||
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()
|
||||
|
||||
if args.output == '-':
|
||||
# If output is written to stdout, suppress other stdout output such as
|
||||
# current state of the program and progress bars
|
||||
args.quiet = True
|
||||
|
||||
info = lambda text='', end='\n': print(text, end=end)
|
||||
if args.quiet:
|
||||
info = lambda text='', end='\n': None
|
||||
|
||||
prog = lambda curr, max: info(f"\r{curr} / {max}", '\n' if curr == max else '')
|
||||
|
||||
reader = None
|
||||
if args.input == '-':
|
||||
reader = PdfReader(sys.stdin.buffer)
|
||||
else:
|
||||
if not os.path.isfile(args.input):
|
||||
print(f'file {args.input} does not exist')
|
||||
exit(1)
|
||||
reader = PdfReader(args.input)
|
||||
|
||||
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
|
||||
|
||||
# Read the input
|
||||
info("Reading...")
|
||||
n = len(reader.pages)
|
||||
pages = []
|
||||
for i in range(n):
|
||||
prog(i, n)
|
||||
pages.append(reader.pages[i])
|
||||
prog(n,n)
|
||||
|
||||
while(len(pages) % 4 != 0):
|
||||
pages.append(smallblank)
|
||||
|
||||
|
||||
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...")
|
||||
if args.output == '-':
|
||||
data = io.BytesIO()
|
||||
output.write(data)
|
||||
sys.stdout.buffer.write(data.getvalue())
|
||||
else:
|
||||
output.write(args.output)
|
||||
|
||||
info("\nDone!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
Loading…
Reference in New Issue