Advertisement
Guest User

Untitled

a guest
Nov 21st, 2020
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.18 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import math
  3. import os
  4. import sys
  5.  
  6. from PyPDF2 import PdfFileReader, PdfFileWriter
  7.  
  8.  
  9.  
  10. class AfMatrix:
  11.     """ A matrix of a 2D affine transform. """
  12.  
  13.     __slots__ = ('__a', '__b', '__c', '__d', '__e', '__f')
  14.  
  15.     def __init__(self, a, b, c, d, e, f):
  16.         self.__a = float(a)
  17.         self.__b = float(b)
  18.         self.__c = float(c)
  19.         self.__d = float(d)
  20.         self.__e = float(e)
  21.         self.__f = float(f)
  22.  
  23.     def __iter__(self):
  24.         yield self.__a
  25.         yield self.__b
  26.         yield self.__c
  27.         yield self.__d
  28.         yield self.__e
  29.         yield self.__f
  30.  
  31.     def __hash__(self):
  32.         return hash(tuple(self))
  33.  
  34.     def __eq__(self, other):
  35.         return tuple(self) == tuple(other)
  36.  
  37.     @classmethod
  38.     def compose(cls, *what):
  39.         a, b, c, d, e, f = (
  40.             1, 0,
  41.             0, 1,
  42.             0, 0,
  43.         )
  44.  
  45.         for rhs in what:
  46.             A, B, C, D, E, F = rhs
  47.             a, b, c, d, e, f = (
  48.                 a * A + b * C,
  49.                 a * B + b * D,
  50.                 c * A + d * C,
  51.                 c * B + d * D,
  52.                 e * A + f * C + E,
  53.                 e * B + f * D + F,
  54.             )
  55.  
  56.         return cls(
  57.             a, b,
  58.             c, d,
  59.             e, f
  60.         )
  61.  
  62.     @classmethod
  63.     def translate(cls, x=0, y=0):
  64.         return cls(
  65.             1, 0,
  66.             0, 1,
  67.             x, y
  68.         )
  69.  
  70.     def __takes_origin(func):
  71.         def translated_func(cls, *args, origin=(0, 0), **kwargs):
  72.             if origin == (0, 0):
  73.                 return func(cls, *args, **kwargs)
  74.             return cls.compose(
  75.                 cls.translate(-origin[0], -origin[1]),
  76.                 func(cls, *args, **kwargs),
  77.                 cls.translate(origin[0], origin[1])
  78.             )
  79.         return translated_func
  80.  
  81.     @classmethod
  82.     @__takes_origin
  83.     def scale (cls, x=1, y=None):
  84.         if y is None:
  85.             y = x
  86.         return cls(
  87.             x, 0,
  88.             0, y,
  89.             0, 0
  90.         )
  91.  
  92.     @classmethod
  93.     @__takes_origin
  94.     def rotate(cls, angle):
  95.         from math import cos, sin, radians
  96.  
  97.         angle = radians(angle)
  98.         C = cos(angle)
  99.         S = sin(angle)
  100.  
  101.         return cls(
  102.             C, -S,
  103.             S,  C,
  104.             0,  0
  105.         )
  106.  
  107.  
  108.  
  109. def page_size (page):
  110.  
  111.     return (page.mediaBox.getWidth (), page.mediaBox.getHeight ())
  112.  
  113.  
  114.  
  115. def convert (reader, sheets_per_slice, num_blanks):
  116.  
  117.     writer = PdfFileWriter ()
  118.  
  119.     SIZE = page_size (reader.getPage (0))
  120.  
  121.     print ("SIZE={}".format (SIZE))
  122.  
  123.     NUM_PAGES = reader.getNumPages ()
  124.  
  125.     PAGES_PER_SLICE = sheets_per_slice * 4
  126.  
  127.     MAX_PRODUCED_PAGES = NUM_PAGES + num_blanks + PAGES_PER_SLICE - 1
  128.  
  129.     NUM_SLICES = int (MAX_PRODUCED_PAGES / PAGES_PER_SLICE)
  130.  
  131.     def positive (x):
  132.         if x >= 0 and x < NUM_PAGES:
  133.             return x
  134.         else:
  135.             return None
  136.  
  137.     output_page_num = 0
  138.  
  139.     for i_slice in range (NUM_SLICES):
  140.  
  141.         print ("Slice {}/{} will read pages {} to {} of {}".format (
  142.             i_slice + 1,
  143.             NUM_SLICES,
  144.             i_slice * PAGES_PER_SLICE - num_blanks,
  145.             i_slice * PAGES_PER_SLICE + PAGES_PER_SLICE - num_blanks - 1,
  146.             NUM_PAGES))
  147.  
  148.         O = i_slice * PAGES_PER_SLICE - num_blanks
  149.  
  150.         for s in range (sheets_per_slice * 2):
  151.  
  152.             # Positive is clockwise (!)
  153.             angle = -90 if s%2 == 0 else +90
  154.  
  155.             out_page = writer.addBlankPage (*SIZE)
  156.  
  157.             i_bot = positive (O + sheets_per_slice * 2 - (s + 1))
  158.             i_top = positive (O + sheets_per_slice * 2 + s)
  159.  
  160.             def get (i):
  161.                 p = reader.getPage (i)
  162.                 if page_size (p) != SIZE:
  163.                     raise Exception ("Page {} has different size.".format (i+1))
  164.                 return p
  165.  
  166.             if i_bot is not None:
  167.                 out_page.mergeTransformedPage (
  168.                     get (i_bot),
  169.                     transform (angle, 0, SIZE))
  170.  
  171.             if i_top is not None:
  172.                 out_page.mergeTransformedPage (
  173.                     get (i_top),
  174.                     transform (angle, SIZE[1]/2, SIZE))
  175.  
  176.             print ("Output page {}:\n\tb: {}\n\tt: {}\n\ta: {}".format (
  177.                 output_page_num + 1,
  178.                 i_bot,
  179.                 i_top,
  180.                 angle))
  181.  
  182.             output_page_num += 1
  183.  
  184.     return writer
  185.  
  186.  
  187.  
  188. def transform (angle, y_offset, size):
  189.  
  190.     if angle < 0:
  191.         offset = (size[0], y_offset)
  192.     else:
  193.         offset = (0, y_offset + size[1]/2)
  194.  
  195.     # When witdh is 595 for A4, empirically I found offset of 88 to work :-S
  196.     HACK_OFFSET = 88 * size[0]/595
  197.  
  198.     return AfMatrix.compose (
  199.         # Rotate First
  200.         AfMatrix.rotate (angle, origin=(size[0]/2,size[1]/2)),
  201.         # Then shrink
  202.         AfMatrix.scale (1 / math.sqrt (2)),
  203.         # Then slide into position
  204.         AfMatrix.translate (0, y_offset),
  205.         # HACK
  206.         AfMatrix.translate (HACK_OFFSET, -HACK_OFFSET)
  207.     )
  208.  
  209.  
  210.  
  211. def main ():
  212.  
  213.     sys.argv.pop (0) # script name
  214.  
  215.     try:
  216.  
  217.         num_blanks = 0
  218.  
  219.         if '--blanks' == sys.argv[0]:
  220.             sys.argv.pop (0)
  221.             num_blanks = int (sys.argv.pop (0))
  222.  
  223.         sheets_per_slice = sys.argv.pop (0)
  224.         in_filename     = sys.argv.pop (0)
  225.         out_filename    = sys.argv.pop (0)
  226.  
  227.         sheets_per_slice = int (sheets_per_slice)
  228.  
  229.         reader = PdfFileReader (in_filename)
  230.  
  231.         if os.path.exists (out_filename):
  232.  
  233.             print ("{} exists. Overwrite? y/n".format (out_filename))
  234.  
  235.             yn = sys.stdin.readline ()
  236.  
  237.             if yn[0] not in ('Y', 'y'):
  238.                 sys.exit (1)
  239.  
  240.         ostream = open (out_filename, 'wb')
  241.  
  242.     except Exception as e:
  243.  
  244.         print ("{}\nArguments: sheets_per_slice in_file out_file".format (e))
  245.  
  246.         sys.exit (1)
  247.  
  248.     try:
  249.         writer = convert (reader, sheets_per_slice, num_blanks)
  250.     except Exception as e:
  251.         print ("Could not convert this document: {}".format (e))
  252.         raise
  253.         sys.exit (1)
  254.  
  255.     writer.write (ostream)
  256.  
  257.     ostream.close ()
  258.  
  259.  
  260.  
  261. if __name__ == '__main__':
  262.     main ()
  263.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement