summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/pdfbook2
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2015-06-14 22:17:46 +0000
committerKarl Berry <karl@freefriends.org>2015-06-14 22:17:46 +0000
commit4cad9cf047cf4bb824a71c8af70edee1b20874f1 (patch)
tree9dca48848d460108937bd24f6d66179e16dfe1a2 /Master/texmf-dist/scripts/pdfbook2
parentbf587c17edfdc46c03e91570278a8faa0c4285a0 (diff)
pdfbook2 (4jun15)
git-svn-id: svn://tug.org/texlive/trunk@37537 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/pdfbook2')
-rwxr-xr-xMaster/texmf-dist/scripts/pdfbook2/pdfbook2237
1 files changed, 237 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/pdfbook2/pdfbook2 b/Master/texmf-dist/scripts/pdfbook2/pdfbook2
new file mode 100755
index 00000000000..a288df70fd5
--- /dev/null
+++ b/Master/texmf-dist/scripts/pdfbook2/pdfbook2
@@ -0,0 +1,237 @@
+#!/usr/bin/env python
+""" pdfbook2 - transform pdf files to booklets
+
+ 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/>.
+ """
+
+
+import sys
+import subprocess
+import os
+from optparse import OptionParser, OptionGroup, HelpFormatter
+import shutil
+
+
+#===============================================================================
+# 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."
+ return
+ print "Getting bounds...",
+ sys.stdout.flush()
+
+ #---------------------------------------------------------- useful constants
+ bboxName = "%%HiResBoundingBox:"
+ tmpFile = ".crop-tmp.pdf"
+
+ #------------------------------------------------- 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.wait()
+ out, err = p.communicate()
+ 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 ] )
+
+ widthOdd = maxROdd - minLOdd
+ widthEven = maxREven - minLEven
+ maxWidth = max( widthOdd, widthEven )
+ minLOdd -= maxWidth - widthOdd
+ maxREven += maxWidth - widthEven
+
+ print "done"
+ sys.stdout.flush()
+
+ #--------------------------------------------- crop file to area of interest
+ print "cropping...",
+ 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.wait()
+ out, err = p.communicate()
+ 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 )
+
+ #-------------------------------------------------------- create the booklet
+ print "create booklet...",
+ sys.stdout.flush()
+ pdfJamCallList = [ "pdfjam",
+ "--booklet", "true",
+ "--landscape",
+ "--suffix", "book",
+ "--signature", repr( opts.signature ),
+ tmpFile ]
+
+ # add option --paper to call
+ if opts.paper is not None:
+ 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.wait()
+ out, err = p.communicate()
+ 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" )
+
+ # run call to pdfJam to make booklet
+ p = subprocess.Popen( pdfJamCallList,
+ stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE )
+ p.wait()
+
+ #-------------------------------------------- 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 ):
+ """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 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() )
+
+
+#===============================================================================
+# main programm
+#===============================================================================
+
+if __name__ == "__main__":
+ #------------------------------------------------------------ useful strings
+ usageString = "Usage: %prog [options] file1 [file2 ...]"
+ versionString = """
+ %prog v1.2
+ (c) 2015 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 )
+
+ opts, args = parser.parse_args()
+
+ #------------------------------------ show help if started without arguments
+ if len( args ) == 0:
+ parser.print_version()
+ parser.print_help()
+ print ""
+ sys.exit( 2 )
+
+ #------------------------------------------- run for each provided file name
+ parser.print_version()
+ for arg in args:
+ booklify( arg, opts )