texlive[53521] trunk: pdfbook2 (23jan20)
commits+karl at tug.org
commits+karl at tug.org
Thu Jan 23 22:58:36 CET 2020
Revision: 53521
http://tug.org/svn/texlive?view=revision&revision=53521
Author: karl
Date: 2020-01-23 22:58:36 +0100 (Thu, 23 Jan 2020)
Log Message:
-----------
pdfbook2 (23jan20)
Modified Paths:
--------------
trunk/Build/source/texk/texlive/linked_scripts/pdfbook2/pdfbook2
trunk/Master/texmf-dist/doc/support/pdfbook2/README
trunk/Master/texmf-dist/scripts/pdfbook2/pdfbook2
Added Paths:
-----------
trunk/Master/texmf-dist/doc/man/man1/pdfbook2.1
trunk/Master/texmf-dist/doc/man/man1/pdfbook2.man1.pdf
Modified: trunk/Build/source/texk/texlive/linked_scripts/pdfbook2/pdfbook2
===================================================================
--- trunk/Build/source/texk/texlive/linked_scripts/pdfbook2/pdfbook2 2020-01-23 21:58:09 UTC (rev 53520)
+++ trunk/Build/source/texk/texlive/linked_scripts/pdfbook2/pdfbook2 2020-01-23 21:58:36 UTC (rev 53521)
@@ -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 at page\pdfpageattr{/Rotate 180}\fi}\makeatother" )
+ pdfJamCallList.append("--preamble")
+ pdfJamCallList.append(
+ r"\usepackage{everyshi}\makeatletter\EveryShipout{\ifodd\c at 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 ) )
+ # ------------------------------------------------- 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 )
+ 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 )
+ 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 )
+ 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)
Added: trunk/Master/texmf-dist/doc/man/man1/pdfbook2.1
===================================================================
--- trunk/Master/texmf-dist/doc/man/man1/pdfbook2.1 (rev 0)
+++ trunk/Master/texmf-dist/doc/man/man1/pdfbook2.1 2020-01-23 21:58:36 UTC (rev 53521)
@@ -0,0 +1,121 @@
+.TH pdfbook2 1 "January 22, 2020" "" "pdfbook2 - transform pdf files to booklets"
+
+.SH NAME
+pdfbook2 \- transform pdf files into booklets for double-sided printing
+
+.SH SYNOPSIS
+\fBpdfbook2\fR [ \fIoptions\fR ] \fIINPUT\fR [ \fIINPUT\fR, ...]
+
+.SH DESCRIPTION
+Create print-ready PDF files from some \fIINPUT\fR PDF files for booklet printing. The resulting files need to be printed in landscape/long edge double sided printing. The default paper format depends on the locale and is choosen by pdfjam. It can be set with the --paper option.
+.PP
+Before the pdf is composed the \fIINPUT\fR file is cropped to the relevant area in order to discard unnecessary white spaces. In this process, all pages are cropped to the same dimensions. Extra margins can be defined at the edges of the booklet and in the middle where the binding occurs.
+.PP
+The \fIOUTPUT\fR is written to \fIINPUT\fR-book.pdf. Existing files will be overwritten. All input files are processed seperatly.
+
+.SH EXAMPLE
+To simply \fBcreate a booklet\fR from input.pdf you can use
+.PP
+.nf
+.RS
+pdfbook2 input.pdf
+.RE
+.fi
+.PP
+to create input-book.pdf. To select a special \fBtype of paper\fR you can do
+.PP
+.nf
+.RS
+pdfbook2 --paper=letter input.pdf
+.RE
+.fi
+.PP
+for letter or
+.PP
+.nf
+.RS
+pdfbook2 --paper=a4paper input.pdf
+.RE
+.fi
+.PP
+for standard A4. To increase the \fBinner margin for binding\fR use
+.PP
+.nf
+.RS
+pdfbook2 --inner-margin=200 input.pdf
+.RE
+.fi
+.PP
+to increase the default value of 150. You can submit \fBmultiple files\fR to the
+script for processing like
+.PP
+.nf
+.RS
+pdfbook2 input1.pdf input2.pdf
+.RE
+.fi
+.PP
+which will result in input1-book.pdf and input2-book.pdf.
+
+.SH OPTIONS
+.TP
+.BR \-\-version
+show program's version number and exit
+.TP
+.BR -h ", " --help
+show help message and exit
+
+.SS GENERAL
+.TP
+.BR -p ", " --paper = \fISTR\fR
+Format of the output paper dimensions as latex keyword (e.g. a4paper, letterpaper, legalpaper, ...)
+.TP
+.BR -s ", " --short-edge
+Format the booklet for short-edge double-sided printing
+.TP
+.BR -n ", " --no-crop
+Prevent the cropping to the content area
+
+.SS MARGINS
+.TP
+.BR -o ", " --outer-margin = \fIINT\fR
+Defines the outer margin in the booklet (default: 40)
+.TP
+.BR -i ", " --inner-margin = \fIINT\fR
+Defines the inner margin between the pages in the booklet (default: 150)
+.TP
+.BR -t ", " --top-margin = \fIINT\fR
+Defines the top margin in the booklet (default: 30)
+.TP
+.BR -b ", " --bottom-margin = \fIINT\fR
+Defines the bottom margin in the booklet (default: 30)
+
+.SS ADVANCED
+.TP
+.BR --signature = \fIINT\fR
+Define the signature for the booklet handed to pdfjam, needs to be multiple of 4 (default: 4)
+.TP
+.BR --signature* = \fIINT\fR
+Same as --signature
+.TP
+.BR --resolution = \fIINT\fR
+Resolution used by ghostscript in bp (default: 72)
+
+.SH "SEE ALSO"
+.BR pdfbook (1),
+.BR pdfjam (1),
+.BR pdfcrop (1)
+
+.SH ABOUT
+pdfbook2 v1.4 (https://github.com/jenom/pdfbook2)
+.br
+(c) 2015 - 2020 Johannes Neumann (http://www.neumannjo.de)
+.br
+licensed under GPLv3 (http://www.gnu.org/licenses/gpl-3.0)
+.br
+based on pdfbook by David Firth with help from Marco Pessotto
+
+.SH LICENSE
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
Property changes on: trunk/Master/texmf-dist/doc/man/man1/pdfbook2.1
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/man/man1/pdfbook2.man1.pdf
===================================================================
(Binary files differ)
Index: trunk/Master/texmf-dist/doc/man/man1/pdfbook2.man1.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/man/man1/pdfbook2.man1.pdf 2020-01-23 21:58:09 UTC (rev 53520)
+++ trunk/Master/texmf-dist/doc/man/man1/pdfbook2.man1.pdf 2020-01-23 21:58:36 UTC (rev 53521)
Property changes on: trunk/Master/texmf-dist/doc/man/man1/pdfbook2.man1.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Modified: trunk/Master/texmf-dist/doc/support/pdfbook2/README
===================================================================
--- trunk/Master/texmf-dist/doc/support/pdfbook2/README 2020-01-23 21:58:09 UTC (rev 53520)
+++ trunk/Master/texmf-dist/doc/support/pdfbook2/README 2020-01-23 21:58:36 UTC (rev 53521)
@@ -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 @@
REQUIREMENTS
- python 2.7, pdfjam, pdfcrop and their dependencies.
+ python 3.6, pdfjam, pdfcrop and their dependencies.
EXAMPLES
@@ -115,6 +115,12 @@
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
Modified: trunk/Master/texmf-dist/scripts/pdfbook2/pdfbook2
===================================================================
--- trunk/Master/texmf-dist/scripts/pdfbook2/pdfbook2 2020-01-23 21:58:09 UTC (rev 53520)
+++ trunk/Master/texmf-dist/scripts/pdfbook2/pdfbook2 2020-01-23 21:58:36 UTC (rev 53521)
@@ -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 at page\pdfpageattr{/Rotate 180}\fi}\makeatother" )
+ pdfJamCallList.append("--preamble")
+ pdfJamCallList.append(
+ r"\usepackage{everyshi}\makeatletter\EveryShipout{\ifodd\c at 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 ) )
+ # ------------------------------------------------- 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 )
+ 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 )
+ 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 )
+ 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)
More information about the tex-live-commits
mailing list