summaryrefslogtreecommitdiff
path: root/support/pdfbook2
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2020-01-24 03:00:54 +0000
committerNorbert Preining <norbert@preining.info>2020-01-24 03:00:54 +0000
commite1192611f0655a1ccaff0dff2f53c7c65fa5db07 (patch)
tree232f5be17a2221a57cc6515bfe8692772b3a3e89 /support/pdfbook2
parentf7e5d81fe9880913485f7f8c541eeca7adf4c195 (diff)
CTAN sync 202001240300
Diffstat (limited to 'support/pdfbook2')
-rw-r--r--support/pdfbook2/README12
-rwxr-xr-xsupport/pdfbook2/pdfbook2380
-rw-r--r--support/pdfbook2/pdfbook2.16
3 files changed, 244 insertions, 154 deletions
diff --git a/support/pdfbook2/README b/support/pdfbook2/README
index 2b72d40f06..e27738188b 100644
--- a/support/pdfbook2/README
+++ b/support/pdfbook2/README
@@ -1,8 +1,8 @@
pdfbook2 - transform pdf files to booklets
==========================================
- pdfbook2 v1.3 (https://github.com/jenom/pdfbook2)
- (c) 2015 - 2019 Johannes Neumann (http://www.neumannjo.de)
+ pdfbook2 v1.4 (https://github.com/jenom/pdfbook2)
+ (c) 2015 - 2020 Johannes Neumann (http://www.neumannjo.de)
licensed under GPLv3 (http://www.gnu.org/licenses/gpl-3.0)
based on pdfbook by David Firth with help from Marco Pessotto
@@ -45,7 +45,7 @@ INSTALLATION
REQUIREMENTS
- python 2.7, pdfjam, pdfcrop and their dependencies.
+ python 3.6, pdfjam, pdfcrop and their dependencies.
EXAMPLES
@@ -115,6 +115,12 @@ OPTIONS
CHANGELOG
+ 1.4 2020/01/20
+
+ - migration to Python 3
+ - fixed bug if the input document had only one page
+ - fix for signature option not working
+
1.3 2019/08/12
- removed wait after popen to prevent deadlock with very large documents
diff --git a/support/pdfbook2/pdfbook2 b/support/pdfbook2/pdfbook2
index c5bc8677db..f9bb012cc7 100755
--- a/support/pdfbook2/pdfbook2
+++ b/support/pdfbook2/pdfbook2
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
""" pdfbook2 - transform pdf files to booklets
This program is free software: you can redistribute it and/or modify
@@ -16,219 +16,303 @@
"""
-import sys
-import subprocess
import os
-from optparse import OptionParser, OptionGroup, HelpFormatter
import shutil
+import subprocess
+import sys
+from optparse import HelpFormatter, OptionGroup, OptionParser
-
-#===============================================================================
+# ===============================================================================
# Create booklet for file $name
-#===============================================================================
+# ===============================================================================
-def booklify( name, opts ):
- #------------------------------------------------------ Check if file exists
- print "\nProcessing", name
- if not os.path.isfile( name ):
- print "SKIP: file not found."
+
+def booklify(name, opts):
+ # ------------------------------------------------------ Check if file exists
+ print("\nProcessing", name)
+ if not os.path.isfile(name):
+ print("SKIP: file not found.")
return
- print "Getting bounds...",
+ print("Getting bounds...", end=" ")
sys.stdout.flush()
- #---------------------------------------------------------- useful constants
- bboxName = "%%HiResBoundingBox:"
+ # ---------------------------------------------------------- useful constants
+ bboxName = b"%%HiResBoundingBox:"
tmpFile = ".crop-tmp.pdf"
- #------------------------------------------------- find min/max bounding box
+ # ------------------------------------------------- find min/max bounding box
if opts.crop:
- p = subprocess.Popen( ["pdfcrop", "--verbose",
- "--resolution", repr( opts.resolution ),
- name, tmpFile],
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE )
+ p = subprocess.Popen(
+ ["pdfcrop", "--verbose", "--resolution", repr(opts.resolution), name, tmpFile],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
out, err = p.communicate()
- if len( err ) != 0:
- print err
- print "\n\nABORT: Problem getting bounds"
- sys.exit( 1 )
+ if len(err) != 0:
+ print(err)
+ print("\n\nABORT: Problem getting bounds")
+ sys.exit(1)
lines = out.splitlines()
- bboxes = [s[len( bboxName ) + 1:] for s in lines if s.startswith( bboxName )]
- bounds = [[float( x ) for x in bbox.split()] for bbox in bboxes ]
- minLOdd = min( [bound[0] for bound in bounds[::2] ] )
- maxROdd = max( [bound[2] for bound in bounds[::2] ] )
- minLEven = min( [bound[0] for bound in bounds[1::2] ] )
- maxREven = max( [bound[2] for bound in bounds[1::2] ] )
- minT = min( [bound[1] for bound in bounds ] )
- maxB = max( [bound[3] for bound in bounds ] )
+ bboxes = [s[len(bboxName) + 1 :] for s in lines if s.startswith(bboxName)]
+ bounds = [[float(x) for x in bbox.split()] for bbox in bboxes]
+ minLOdd = min([bound[0] for bound in bounds[::2]])
+ maxROdd = max([bound[2] for bound in bounds[::2]])
+ if len(bboxes) > 1:
+ minLEven = min([bound[0] for bound in bounds[1::2]])
+ maxREven = max([bound[2] for bound in bounds[1::2]])
+ else:
+ minLEven = minLOdd
+ maxREven = maxROdd
+ minT = min([bound[1] for bound in bounds])
+ maxB = max([bound[3] for bound in bounds])
widthOdd = maxROdd - minLOdd
widthEven = maxREven - minLEven
- maxWidth = max( widthOdd, widthEven )
+ maxWidth = max(widthOdd, widthEven)
minLOdd -= maxWidth - widthOdd
maxREven += maxWidth - widthEven
- print "done"
+ print("done")
sys.stdout.flush()
- #--------------------------------------------- crop file to area of interest
- print "cropping...",
+ # --------------------------------------------- crop file to area of interest
+ print("cropping...", end=" ")
sys.stdout.flush()
- p = subprocess.Popen( ["pdfcrop",
- "--bbox-odd", "{L} {T} {R} {B}".format( L = minLOdd - opts.innerMargin / 2,
- T = minT - opts.topMargin,
- R = maxROdd + opts.outerMargin,
- B = maxB + opts.outerMargin ),
- "--bbox-even", "{L} {T} {R} {B}".format( L = minLEven - opts.outerMargin,
- T = minT - opts.topMargin,
- R = maxREven + opts.innerMargin / 2,
- B = maxB + opts.outerMargin ),
- "--resolution", repr( opts.resolution ),
- name,
- tmpFile],
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE )
+ p = subprocess.Popen(
+ [
+ "pdfcrop",
+ "--bbox-odd",
+ "{L} {T} {R} {B}".format(
+ L=minLOdd - opts.innerMargin / 2,
+ T=minT - opts.topMargin,
+ R=maxROdd + opts.outerMargin,
+ B=maxB + opts.outerMargin,
+ ),
+ "--bbox-even",
+ "{L} {T} {R} {B}".format(
+ L=minLEven - opts.outerMargin,
+ T=minT - opts.topMargin,
+ R=maxREven + opts.innerMargin / 2,
+ B=maxB + opts.outerMargin,
+ ),
+ "--resolution",
+ repr(opts.resolution),
+ name,
+ tmpFile,
+ ],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
out, err = p.communicate()
- if len( err ) != 0:
- print err
- print "\n\nABORT: Problem with cropping"
- sys.exit( 1 )
- print "done"
+ if len(err) != 0:
+ print(err)
+ print("\n\nABORT: Problem with cropping")
+ sys.exit(1)
+ print("done")
sys.stdout.flush()
else:
- shutil.copy( name, tmpFile )
+ shutil.copy(name, tmpFile)
- #-------------------------------------------------------- create the booklet
- print "create booklet...",
+ # -------------------------------------------------------- create the booklet
+ print("create booklet...", end=" ")
sys.stdout.flush()
- pdfJamCallList = [ "pdfjam",
- "--booklet", "true",
- "--landscape",
- "--suffix", "book",
- "--signature", repr( opts.signature ),
- tmpFile ]
+ pdfJamCallList = [
+ "pdfjam",
+ "--landscape",
+ "--suffix",
+ "book",
+ tmpFile,
+ ]
+
+ # add option signature if it is defined else booklet
+ if opts.signature != 0:
+ pdfJamCallList.append("--signature")
+ pdfJamCallList.append(repr(opts.signature))
+ else:
+ pdfJamCallList.append("--booklet")
+ pdfJamCallList.append("true")
# add option --paper to call
if opts.paper is not None:
- pdfJamCallList.append( "--paper" )
- pdfJamCallList.append( opts.paper )
+ pdfJamCallList.append("--paper")
+ pdfJamCallList.append(opts.paper)
# add option --short-edge to call
if opts.shortedge:
# check if everyshi.sty exists as texlive recommends
- p = subprocess.Popen( ["kpsewhich", "everyshi.sty"],
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE )
+ p = subprocess.Popen(
+ ["kpsewhich", "everyshi.sty"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
+ )
out, err = p.communicate()
- if len( out ) == 0:
- print "\n\nABORT: The everyshi.sty latex package is needed for short-edge."
- sys.exit( 1 )
+ if len(out) == 0:
+ print("\n\nABORT: The everyshi.sty latex package is needed for short-edge.")
+ sys.exit(1)
else:
- pdfJamCallList.append( "--preamble" )
- pdfJamCallList.append( r"\usepackage{everyshi}\makeatletter\EveryShipout{\ifodd\c@page\pdfpageattr{/Rotate 180}\fi}\makeatother" )
+ pdfJamCallList.append("--preamble")
+ pdfJamCallList.append(
+ r"\usepackage{everyshi}\makeatletter\EveryShipout{\ifodd\c@page\pdfpageattr{/Rotate 180}\fi}\makeatother"
+ )
# run call to pdfJam to make booklet
- p = subprocess.Popen( pdfJamCallList,
- stdout = subprocess.PIPE,
- stderr = subprocess.PIPE )
+ p = subprocess.Popen(pdfJamCallList, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
- #-------------------------------------------- move file and remove temp file
- os.rename( tmpFile[:-4] + "-book.pdf", name[:-4] + "-book.pdf" )
- os.remove( tmpFile )
- print "done"
+ # -------------------------------------------- move file and remove temp file
+ os.rename(tmpFile[:-4] + "-book.pdf", name[:-4] + "-book.pdf")
+ os.remove(tmpFile)
+ print("done")
sys.stdout.flush()
-#===============================================================================
+# ===============================================================================
# Help formatter
-#===============================================================================
+# ===============================================================================
+
-class MyHelpFormatter ( HelpFormatter ):
+class MyHelpFormatter(HelpFormatter):
"""Format help with indented section bodies.
"""
- def __init__( self,
- indent_increment = 4,
- max_help_position = 16,
- width = None,
- short_first = 0 ):
- HelpFormatter.__init__(
- self, indent_increment, max_help_position, width, short_first )
+ def __init__(self, indent_increment=4, max_help_position=16, width=None, short_first=0):
+ HelpFormatter.__init__(self, indent_increment, max_help_position, width, short_first)
- def format_usage( self, usage ):
- return ( "USAGE\n\n%*s%s\n" ) % ( self.indent_increment, "", usage )
+ def format_usage(self, usage):
+ return ("USAGE\n\n%*s%s\n") % (self.indent_increment, "", usage)
- def format_heading( self, heading ):
- return "%*s%s\n\n" % ( self.current_indent, "", heading.upper() )
+ def format_heading(self, heading):
+ return "%*s%s\n\n" % (self.current_indent, "", heading.upper())
-#===============================================================================
+# ===============================================================================
# main programm
-#===============================================================================
+# ===============================================================================
if __name__ == "__main__":
- #------------------------------------------------------------ useful strings
+ # ------------------------------------------------------------ useful strings
usageString = "Usage: %prog [options] file1 [file2 ...]"
versionString = """
- %prog v1.3 (https://github.com/jenom/pdfbook2)
- (c) 2015 Johannes Neumann (http://www.neumannjo.de)
+ %prog v1.4 (https://github.com/jenom/pdfbook2)
+ (c) 2015 - 2020 Johannes Neumann (http://www.neumannjo.de)
licensed under GPLv3 (http://www.gnu.org/licenses/gpl-3.0)
based on pdfbook by David Firth with help from Marco Pessotto\n"""
defaultString = " (default: %default)"
- #------------------------------------------------- create commandline parser
- parser = OptionParser( usage = usageString, version = versionString,
- formatter = MyHelpFormatter( indent_increment = 4 ) )
-
- generalGroup = OptionGroup( parser, "General" )
- generalGroup.add_option( "-p", "--paper", dest = "paper", type = "str", action = "store",
- metavar = "STR",
- help = "Format of the output paper dimensions as latex keyword (e.g. a4paper, letterpaper, legalpaper, ...)" )
- generalGroup.add_option( "-s", "--short-edge", dest = "shortedge", action = "store_true",
- help = "Format the booklet for short-edge double-sided printing",
- default = False )
- generalGroup.add_option( "-n", "--no-crop", dest = "crop", action = "store_false",
- help = "Prevent the cropping to the content area",
- default = True )
- parser.add_option_group( generalGroup )
-
- marginGroup = OptionGroup( parser, "Margins" )
- marginGroup.add_option( "-o", "--outer-margin", type = "int", default = 40,
- dest = "outerMargin", action = "store", metavar = "INT",
- help = "Defines the outer margin in the booklet" + defaultString )
- marginGroup.add_option( "-i", "--inner-margin", type = "int", default = 150,
- dest = "innerMargin", action = "store", metavar = "INT",
- help = "Defines the inner margin between the pages in the booklet" + defaultString )
- marginGroup.add_option( "-t", "--top-margin", type = "int", default = 30,
- dest = "topMargin", action = "store", metavar = "INT",
- help = "Defines the top margin in the booklet" + defaultString )
- marginGroup.add_option( "-b", "--bottom-margin", type = "int", default = 30, metavar = "INT",
- dest = "bottomMargin", action = "store",
- help = "Defines the bottom margin in the booklet" + defaultString )
- parser.add_option_group( marginGroup )
-
- advancedGroup = OptionGroup( parser, "Advanced" )
- advancedGroup.add_option( "--signature", dest = "signature", action = "store", type = "int",
- help = "Define the signature for the booklet handed to pdfjam, needs to be multiple of 4" + defaultString,
- default = 4, metavar = "INT" )
- advancedGroup.add_option( "--signature*", dest = "signature", action = "store", type = "int",
- help = "Same as --signature", metavar = "INT" )
- advancedGroup.add_option( "--resolution", dest = "resolution", action = "store", type = "int",
- help = "Resolution used by ghostscript in bp" + defaultString,
- metavar = "INT", default = 72 )
- parser.add_option_group( advancedGroup )
+ # ------------------------------------------------- create commandline parser
+ parser = OptionParser(
+ usage=usageString, version=versionString, formatter=MyHelpFormatter(indent_increment=4)
+ )
+
+ generalGroup = OptionGroup(parser, "General")
+ generalGroup.add_option(
+ "-p",
+ "--paper",
+ dest="paper",
+ type="str",
+ action="store",
+ metavar="STR",
+ help="Format of the output paper dimensions as latex keyword (e.g. a4paper, letterpaper, legalpaper, ...)",
+ )
+ generalGroup.add_option(
+ "-s",
+ "--short-edge",
+ dest="shortedge",
+ action="store_true",
+ help="Format the booklet for short-edge double-sided printing",
+ default=False,
+ )
+ generalGroup.add_option(
+ "-n",
+ "--no-crop",
+ dest="crop",
+ action="store_false",
+ help="Prevent the cropping to the content area",
+ default=True,
+ )
+ parser.add_option_group(generalGroup)
+
+ marginGroup = OptionGroup(parser, "Margins")
+ marginGroup.add_option(
+ "-o",
+ "--outer-margin",
+ type="int",
+ default=40,
+ dest="outerMargin",
+ action="store",
+ metavar="INT",
+ help="Defines the outer margin in the booklet" + defaultString,
+ )
+ marginGroup.add_option(
+ "-i",
+ "--inner-margin",
+ type="int",
+ default=150,
+ dest="innerMargin",
+ action="store",
+ metavar="INT",
+ help="Defines the inner margin between the pages in the booklet" + defaultString,
+ )
+ marginGroup.add_option(
+ "-t",
+ "--top-margin",
+ type="int",
+ default=30,
+ dest="topMargin",
+ action="store",
+ metavar="INT",
+ help="Defines the top margin in the booklet" + defaultString,
+ )
+ marginGroup.add_option(
+ "-b",
+ "--bottom-margin",
+ type="int",
+ default=30,
+ metavar="INT",
+ dest="bottomMargin",
+ action="store",
+ help="Defines the bottom margin in the booklet" + defaultString,
+ )
+ parser.add_option_group(marginGroup)
+
+ advancedGroup = OptionGroup(parser, "Advanced")
+ advancedGroup.add_option(
+ "--signature",
+ dest="signature",
+ action="store",
+ type="int",
+ help="Define the signature for the booklet handed to pdfjam, needs to be multiple of 4"
+ + defaultString,
+ default=0,
+ metavar="INT",
+ )
+ advancedGroup.add_option(
+ "--signature*",
+ dest="signature",
+ action="store",
+ type="int",
+ help="Same as --signature",
+ metavar="INT",
+ )
+ advancedGroup.add_option(
+ "--resolution",
+ dest="resolution",
+ action="store",
+ type="int",
+ help="Resolution used by ghostscript in bp" + defaultString,
+ metavar="INT",
+ default=72,
+ )
+ parser.add_option_group(advancedGroup)
opts, args = parser.parse_args()
- #------------------------------------ show help if started without arguments
- if len( args ) == 0:
+ # ------------------------------------ show help if started without arguments
+ if len(args) == 0:
parser.print_version()
parser.print_help()
- print ""
- sys.exit( 2 )
+ print("")
+ sys.exit(2)
- #------------------------------------------- run for each provided file name
+ # ------------------------------------------- run for each provided file name
parser.print_version()
for arg in args:
- booklify( arg, opts )
+ booklify(arg, opts)
diff --git a/support/pdfbook2/pdfbook2.1 b/support/pdfbook2/pdfbook2.1
index a6c61c0f90..c761447b53 100644
--- a/support/pdfbook2/pdfbook2.1
+++ b/support/pdfbook2/pdfbook2.1
@@ -1,4 +1,4 @@
-.TH pdfbook2 1 "August 12, 2019" "" "pdfbook2 - transform pdf files to booklets"
+.TH pdfbook2 1 "January 22, 2020" "" "pdfbook2 - transform pdf files to booklets"
.SH NAME
pdfbook2 \- transform pdf files into booklets for double-sided printing
@@ -107,9 +107,9 @@ Resolution used by ghostscript in bp (default: 72)
.BR pdfcrop (1)
.SH ABOUT
-pdfbook2 v1.3 (https://github.com/jenom/pdfbook2)
+pdfbook2 v1.4 (https://github.com/jenom/pdfbook2)
.br
-(c) 2015 - 2019 Johannes Neumann (http://www.neumannjo.de)
+(c) 2015 - 2020 Johannes Neumann (http://www.neumannjo.de)
.br
licensed under GPLv3 (http://www.gnu.org/licenses/gpl-3.0)
.br