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