/ src / ppp / printydump.py
printydump.py
  1  
  2  #		        This program is part of
  3  #          Paul's Preponderating Prepresser v1.1
  4  #            (CC-BY-SA) 2025 era vulgaris, by
  5  #        The Rev. Paul T. Fusco-Gessick, J.D., SDA
  6  #                <<paul@neroots.net>>
  7  
  8  #                I.F.E.T.  --  I.V.V.S.
  9  
 10  import sys, os, math, subprocess, PyPDF2, csv
 11  
 12  # values for signature start and end pages, to 30 (and sometimes 40!) signatures apiece
 13  # sig X beginning page is sig##[x-1] -- sig X end page is sig##[x]
 14  
 15  sig48 =(1,48,49,96,97,144,145,192,193,240,241,288,289,336,337,384,385,432,433,480,481,528,529,576,577,624,625,672,673,720,721,768,769,816,817,864,865,912,913,960,961,1008,1009,1056,1057,1104,1105,1152,1153,1200,1201,1248,1249,1296,1297,1344,1345,1392,1393,1440,1441,1488,1489,1536,1537,1584,1585,1632,1633,1680,1681,1728,1729,1776,1777,1824,1825,1872,1873,1920)
 16  sig44 = (1,44,45,88,89,132,133,176,177,220,221,264,265,308,309,352,353,396,397,440,441,484,485,528,529,572,573,616,617,660,661,704,705,748,749,792,793,836,837,880,881,924,925,968,969,1012,1013,1056,1057,1100,1101,1144,1145,1188,1189,1232,1233,1276,1277,1320,1321,1364,1365,1408,1409,1452,1453,1496,1497,1540,1541,1584,1585,1628,1629,1672,1673,1716,1717,1760)
 17  sig40 = (1,40,41,80,81,120,121,160,161,200,201,240,241,280,281,320,321,360,361,400,401,440,441,480,481,520,521,560,561,600,601,640,641,680,681,720,721,760,761,800,801,840,841,880,881,920,921,960,961,1000,1001,1040,1041,1080,1081,1120,1121,1160,1161,1200,1201,1240,1241,1280,1281,1320,1321,1360,1361,1400,1401,1440,1441,1480,1481,1520,1521,1560,1561,1600)
 18  sig36 =(1,36,37,72,73,108,109,144,145,180,181,216,217,252,253,288,289,324,325,360,361,396,397,432,433,468,469,504,505,540,541,576,577,612,613,648,649,684,685,720,721,756,757,792,793,828,829,864,865,900,901,936,937,972,973,1008,1009,1044,1045,1080,1081,1116,1117,1152,1153,1188,1189,1224,1225,1260,1261,1296,1297,1332,1333,1368,1369,1404,1405,1440,1441,1476,1477,1512,1513,1548,1549,1584,1585,1620,1621,1656,1657,1692,1693,1728,1729,1764,1765,1800)
 19  sig32 = (1,32,33,64,65,96,97,128,129,160,161,192,193,224,225,256,257,288,289,320,321,352,353,384,385,416,417,448,449,480,481,512,513,544,545,576,577,608,609,640,641,672,673,704,705,736,737,768,769,800,801,832,833,864,865,896,897,928,929,960,961,992,993,1024,1025,1056,1057,1088,1089,1120,1121,1152,1153,1184,1185,1216,1217,1248,1249,1280,1281,1312,1313,1344,1345,1376,1377,1408,1409,1440,1441,1472,1473,1504,1505,1536,1537,1568,1569,1600)
 20  sig28 = (1,28,29,56,57,84,85,112,113,140,141,168,169,196,197,224,225,252,253,280,281,308,309,336,337,364,365,392,393,420,421,448,449,476,477,504,505,532,533,560,561,588,589,616,617,644,645,672,673,700,701,728,729,756,757,784,785,812,813,840,841,868,869,896,897,924,925,952,953,980,981,1008,1009,1036,1037,1064,1065,1092,1093,1120)
 21  sig24 = (1,24,25,48,49,72,73,96,97,120,121,144,145,168,169,192,193,216,217,240,241,264,265,288,289,312,313,336,337,360,361,384,385,408,409,432,433,456,457,480,481,504,505,528,529,552,553,576,577,600,601,624,625,648,649,672,673,696,697,720,721,744,745,768,769,792,793,816,817,840,841,864,865,888,889,912,913,936,937,960)
 22  sig20 = (1,20,21,40,41,60,61,80,81,100,101,120,121,140,141,160,161,180,181,200,201,220,221,240,241,260,261,280,281,300,301,320,321,340,341,360,361,380,381,400,401,420,421,440,441,460,461,480,481,500,501,520,521,540,541,560,561,580,581,600)
 23  sig16 = (1,16,17,32,33,48,49,64,65,80,81,96,97,112,113,128,129,144,145,160,161,176,177,192,193,208,209,224,225,240,241,256,257,272,273,288,289,304,305,320,321,336,337,352,353,368,369,384,385,400,401,416,417,432,433,448,449,465,480)
 24  sig12 = (1,12,13,24,25,36,37,48,49,60,61,72,73,84,85,96,97,108,109,120,121,132,133,144,145,156,157,168,169,180,181,192,193,204,205,216,217,228,229,240,241,252,253,264,265,276,277,288,289,300,301,312,313,324,325,336,337,348,349,360)
 25  sig8 = (1,8,9,16,17,24,25,32,33,40,41,48,49,56,57,64,65,72,73,80,81,88,89,96,97,104,105,112,113,120,121,128,129,136,137,144,145,152,153,160,161,168,169,176,177,184,185,192,193,200,201,208,209,216,217,224,225,232,233,240)
 26  sig4 = (1,4,5,8,9,12,13,16,17,20,21,24,25,28,29,32,33,36,37,40,41,44,45,48,49,52,53,56,57,60,61,64,65,68,69,72,73,76,77,80,81,84,85,88,89,92,93,96,97,100,101,104,105,108,109,112,113,116,117,120)
 27  
 28  VALID_SIG_SIZES = [4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48]
 29  SIG_TABLES = {
 30      4: sig4, 8: sig8, 12: sig12, 16: sig16, 20: sig20, 24: sig24,
 31      28: sig28, 32: sig32, 36: sig36, 40: sig40, 44: sig44, 48: sig48
 32  }
 33  
 34  def main():
 35      # Get and validate source file
 36      while True:
 37          print('source: ', end='')
 38          theSource = input().strip()
 39          if not theSource:
 40              sys.exit('ERROR: No source file specified.')
 41  
 42          # Add .pdf extension if not present
 43          if not theSource.endswith('.pdf'):
 44              theSource = theSource + '.pdf'
 45  
 46          # Check if file exists
 47          if not os.path.isfile(theSource):
 48              print(f'ERROR: File "{theSource}" not found. Please try again.')
 49              continue
 50  
 51          # Try to read the PDF and get page count
 52          try:
 53              with open(theSource, 'rb') as f:
 54                  reader = PyPDF2.PdfReader(f)
 55                  actualPageCount = len(reader.pages)
 56              print(f'Found PDF with {actualPageCount} pages.')
 57              break
 58          except Exception as e:
 59              print(f'ERROR: Could not read PDF file: {e}')
 60              continue
 61  
 62      # Get and validate signature bulk
 63      while True:
 64          print(f'Sig bulk to print (valid options: {", ".join(map(str, VALID_SIG_SIZES))}): ', end='')
 65          sigBulk = input().strip()
 66          try:
 67              sigBulk = int(sigBulk)
 68              if sigBulk not in VALID_SIG_SIZES:
 69                  print(f'ERROR: Signature size must be one of: {", ".join(map(str, VALID_SIG_SIZES))}')
 70                  continue
 71              break
 72          except ValueError:
 73              print('ERROR: Please enter a valid number.')
 74              continue
 75  
 76      # Check if page count is divisible by signature size
 77      if actualPageCount % sigBulk != 0:
 78          print(f'WARNING: Page count ({actualPageCount}) is not evenly divisible by signature size ({sigBulk}).')
 79          pagesNeeded = sigBulk - (actualPageCount % sigBulk)
 80          print(f'You need to add {pagesNeeded} blank page(s) to make this work correctly.')
 81          print('Use pdftk to concatenate the appropriate padding file (1pp.pdf, 2pp.pdf, or 3pp.pdf).')
 82          print('Example: pdftk A={} B={}pp.pdf cat A B output padded-{}'.format(theSource, pagesNeeded, theSource))
 83          sys.exit(1)
 84  
 85      # Calculate number of signatures automatically
 86      numSigs = actualPageCount // sigBulk
 87      print(f'This will create {numSigs} signature(s) of {sigBulk} pages each.')
 88      print('Proceed? [y/n]: ', end='')
 89      confirm = input().strip().lower()
 90      if confirm not in ['y', 'yes']:
 91          sys.exit('Aborted.')
 92  
 93      ite = int(numSigs) + 1
 94  
 95      # Get the signature table for the chosen size
 96      sig_table = SIG_TABLES[sigBulk]
 97  
 98      # Split the PDF into signatures
 99      print(f'Splitting {theSource} into {numSigs} signature(s)...')
100      for i in range(1, int(ite)):
101          s = (2 * i) - 1
102          j = s - 1
103          pagenums = f'{sig_table[j]}-{sig_table[s]}'
104          outputnum = f'sig{str(i).zfill(2)}.pdf'
105          print(f'  Creating {outputnum}...')
106          subprocess.run(["pdftk", str(theSource), "cat", str(pagenums), "output", str(outputnum)],
107                         check=True, capture_output=True)
108  
109      print(f'\nSuccess! Created {numSigs} signature file(s).')
110      print('Next steps:')
111      print('  1. Review the sig##.pdf files')
112      print('  2. Run "fppp" or manually run singledingle on each signature to impose for printing')
113  
114  if __name__ == '__main__':
115      main()