#!/bin/bash shopt -s extglob shopt -s lastpipe VERSION="2.11" VDATE="2024/03/15" ## see release notes at the end of this file. ## (c) 2024/03/15 Nicolas Markey ## ## This work may be distributed and/or modified under the conditions of ## the LaTeX Project Public License, either version 1.3 of this license ## or (at your option) any later version. The latest version of this ## license is in ## ## http://www.latex-project.org/lppl.txt ## ## and version 1.3 or later is part of all distributions of LaTeX version ## 2005/12/01 or later. ## ## This work has the LPPL maintenance status `maintained'. ## The Current Maintainer of this work is Nicolas Markey. ## Roadmap ## 1) definition of some functions: ## defaultvalues() and setdefaultvalues() ## usage() ## myecho() ## dimtopt() and testdim() ## ifinrangelist() ## setconfigfile() [look for configuration file] ## cleancslor() [clean comma-sep. list of ranges] ## cleanup ## 2) script: ## - set default options ## - test for ghostscript and pdflatex ## - process command-line options ## - check number of pages ## - set up variables ## - compute bounding box ## - prepare latex file ## - create PDF ## - clean up ## 3) manual, examples and release notes ## ###################################################################### ## default values for all options. ###################################################################### ## Can be modified from command line ## For instance, "dfpdfxupBB=1-4 pdfxup ..." automatically sets "-bb=1-4" ## (might be useful for defining derived scripts) function defaultvalues() { : ${dfpdfxupNB_COLUMNS=2} : ${dfpdfxupNB_ROWS=1} : ${dfpdfxupLANDSCAPE=1} : ${dfpdfxupPAPERSIZE="a4"} : ${dfpdfxupBOOKLET=0} : ${dfpdfxupCLIP=1} : ${dfpdfxupOUTPUTFILE="pdfxup.pdf"} : ${dfpdfxupINNER_HMARGIN="5pt"} : ${dfpdfxupINNER_VMARGIN="5pt"} : ${dfpdfxupINNER_MARGINS="5pt"} : ${dfpdfxupHMARGIN="5pt"} : ${dfpdfxupVMARGIN="5pt"} : ${dfpdfxupMARGINS="5pt"} : ${dfpdfxupINTERM_HSPACE="1pt"} : ${dfpdfxupINTERM_VSPACE="1pt"} : ${dfpdfxupINTERM_SPACES="1pt"} : ${dfpdfxupFRAME_WIDTH=".4pt"} : ${dfpdfxupPAGES="-"} : ${dfpdfxupINCLUDE_FOR_BBOX="-"} : ${dfpdfxupEXCLUDE_FOR_BBOX=""} : ${dfpdfxupKEEP_ORIG_BBOX="0"} : ${dfpdfxupCOMPUTE_BBOX_ONLY="0"} : ${dfpdfxupVERBOSITY="1"} : ${dfpdfxupDEBUG="0"} : ${dfpdfxupWATERMARK_PERIOD="1"} : ${dfpdfxupSILENTLY_OVERWRITE="0"} : ${dfpdfxupSET_BBOX=""} : ${dfpdfxupTIGHT_FRAME="0"} : ${dfpdfxupCOLUMN="0"} : ${dfpdfxupBALANCELAST="0"} } function setdefaultvalues() { # number of columns NB_COLUMNS=$dfpdfxupNB_COLUMNS; # number of rows NB_ROWS=$dfpdfxupNB_ROWS; # landscape mode (1=landscape, 0=portrait) LANDSCAPE=$dfpdfxupLANDSCAPE; # booklet mode ('se'=booklet 'short-edge', 'le'=booklet 'long-edge') BOOKLET=$dfpdfxupBOOKLET; # clip pages to bounding box (1=yes, 0=no) CLIP=$dfpdfxupCLIP; # papersize ('a4', 'letter', ...) PAPERSIZE=$dfpdfxupPAPERSIZE; # name of output file OUTPUTFILE=$dfpdfxupOUTPUTFILE # (minimal) inner horizontal margin (between frame and logical page) INNER_HMARGIN=$dfpdfxupINNER_HMARGIN; # (minimal) inner vertical margin (between frame and logical page) INNER_VMARGIN=$dfpdfxupINNER_VMARGIN; # (minimal) inner margin (both horizontal and vertical) INNER_MARGINS=$dfpdfxupINNER_MARGINS; # horizontal margin (between content and border of physical page) HMARGIN=$dfpdfxupHMARGIN; # vertical margin (between content and border of physical page) VMARGIN=$dfpdfxupVMARGIN; # margin (between content and border of physical page) MARGINS=$dfpdfxupMARGINS; # intermediate horizontal space (between logical pages) INTERM_HSPACE=$dfpdfxupINTERM_HSPACE; # intermediate vertical space (between logical pages) INTERM_VSPACE=$dfpdfxupINTERM_VSPACE; # intermediate space (between logical pages) INTERM_SPACES=$dfpdfxupINTERM_SPACES; # frame width FRAME_WIDTH=$dfpdfxupFRAME_WIDTH; # verbosity level ## don't overwrite VERBOSITY if set on command line : ${VERBOSITY=$dfpdfxupVERBOSITY}; # debug mode DEBUG=$dfpdfxupDEBUG; # file used for watermarking WATERMARK_FILE=""; # number of pages to repeat when watermarking WATERMARK_PERIOD=$dfpdfxupWATERMARK_PERIOD; # keep original bounding box (hence do not recompute) KEEP_ORIG_BBOX=$dfpdfxupKEEP_ORIG_BBOX; # only compute bounding box (hence do not produce PDF output) COMPUTE_BBOX_ONLY=$dfpdfxupCOMPUTE_BBOX_ONLY; # overwrite output file without warning SILENTLY_OVERWRITE=$dfpdfxupSILENTLY_OVERWRITE; # make frame tight around logical pages TIGHT_FRAME=$dfpdfxupTIGHT_FRAME; # initial bounding box SET_BBOX=$dfpdfxupSET_BBOX # pages displayed left-to-right first (default), or top-to-bottom first COLUMN=$dfpdfxupCOLUMN # balance last page (corresponds to option 'columnstrict' option # in the pdfpages manual; also works in "non-column" mode) BALANCELAST=$dfpdfxupBALANCELAST ## Notice: the names for h and w should actually be X and Y: ## they are not width and height, but coordinates of upper right corner. ## Notice: PAGES, INCLUDE_FOR_BBOX and EXCLUDE_FOR_BBOX will be set to ## default values if empty after processing options } ###################################################################### ## Usage ###################################################################### function usage() { defaultvalues; if [[ "$dfpdfxupPAGES" == "-" ]]; then explicitPAGES=" (all)"; fi if [[ "$dfpdfxupEXCLUDE_FOR_BBOX" == "" ]]; then explicitEXCLUDE_FOR_BBOX=" (none)"; fi if [[ "$dfpdfxupINCLUDE_FOR_BBOX" == "-" ]]; then explicitBB=" (all)"; fi if [[ "$dfpdfxupSET_BBOX" == "" ]]; then explicitSET_BBOX=" (unset)"; fi echo "pdfxup: n-up pages of a PDF document, preserving readability usage: `basename $0` [OPTIONS] file The main OPTIONS are: -x n n columns per page [default: \"$dfpdfxupNB_COLUMNS\"] -y n n lines per page [default: \"$dfpdfxupNB_ROWS\"] -l [0|1] landscape-mode [default: \"$dfpdfxupLANDSCAPE\"] -b [0|1|le|se] booklet-mode [default: \"$dfpdfxupBOOKLET\"] -cf file import config. parameters from file [default: none] -m n margins [default: \"$dfpdfxupMARGINS\"] -fw n frame width [default: \"$dfpdfxupFRAME_WIDTH\"] -o file write output to file [default: \"$dfpdfxupOUTPUTFILE\"] -p pages only include these pages [default: \"$dfpdfxupPAGES\"$explicitPAGES] -nobb pages pages to omit when computing b.box [default: \"$dfpdfxupEXCLUDE_FOR_BBOX\"$explicitEXCLUDE_FOR_BBOX] -bb pages pages to use for computing b.box [default: \"$dfpdfxupINCLUDE_FOR_BBOX\"$explicitBB] -g [0|1] only computes bounding box [default: \"$dfpdfxupCOMPUTE_BBOX_ONLY\"] -s x y X Y force bounding box [default: \"$dfpdfxupSET_BBOX\"$explicitSET_BBOX] -w file add watermarking [default: none] -wp n repeat last n pages of watermark file [default: \"$dfpdfxupWATERMARK_PERIOD\"] -i ask before overwriting/removing files -d debug mode: keep intermediary files -v show version number and exit See the manual page ('man pdfxup') for the list of all options."; ## ## Some options are not presented, for the sake of brevity... ## ## -c [0|1] clip pages to bounding box [default: \"$dfpdfxupCLIP\"] ## -im n inner margins [default: \"$dfpdfxupINNER_MARGINS\"] ## -ihm n inner horizontal margin [default: \"$dfpdfxupINNER_HMARGIN\"] ## -ivm n inner vertical margin [default: \"$dfpdfxupINNER_VMARGIN\"] ## -hm n horizontal margin [default: \"$dfpdfxupHMARGIN\"] ## -vm n vertical margin [default: \"$dfpdfxupVMARGIN\"] ## -ihs n interm. horizontal space [detault: \"$dfpdfxupINTERM_HSPACE\"] ## -ivs n interm. vertical space [detault: \"$dfpdfxupINTERM_VSPACE\"] ## -is n interm. spaces [default: \"$dfpdfxupINTERM_SPACES\"] ## -kbb keep original bounding box [default: \"$dfpdfxupKEEP_ORIG_BBOX\"] ## -ow allow overwriting [default: \"$dfpdfxupSILENTLY_OVERWRITE\"] ## -ps s output paper size [default: \"$dfpdfxupPAPERSIZE\"] ## -tf draw tight frame around each page [default: \"$dfpdfxupTIGHT_FRAME\"] ## -col arrange pages top-to-bottom first [default: \"$dfpdfxupCOLUMN\"] ## -bal balance last page [default: \"$dfpdfxupBALANCELAST\"] ## -q run quietly (equiv. '-V=0') ## -h show this help message ## -V [0-3] select verbosity [default: \"$dfpdfxupVERBOSITY\"] ## ## ## exit 0; } ###################################################################### ## Auxiliary functions ###################################################################### ## myecho handles several levels of verbosity function myecho() { ECHO=0; case $1 in +([0-9])\+) if [[ $VERBOSITY -ge ${1%+} ]]; then ECHO=1; fi ;; +([0-9])) if [[ $VERBOSITY -eq $1 ]]; then ECHO=1; fi ;; +([0-9])-) if [[ $VERBOSITY -le ${1%-} ]]; then ECHO=1; fi ;; esac if [[ $ECHO == 1 ]]; then case $2 in -*) echo $2 "$3" ;; *) echo "$2" ;; esac fi } DELAYEDECHO=""; function delaymyecho () { DELAYEDECHO+="\n$1"; } ## transforms given dimension (in cm, mm, in) in pt function dimtopt() { dim=$1; case $dim in *pt) ;; *mm) dim=${dim%mm}; eval dim=$((dim*2845/100))pt; ;; *cm) dim=${dim%cm}; eval dim=$((dim*284/10))pt; ;; *in) dim=${dim%in}; eval dim=$((dim*723/10))pt; ;; *[!0-9]*) ## should not occur... has been filtered out by main 'case' myecho 1+ " illegal unit of measure in option '$3'; ignoring"; dim=$2; ;; *) dim=${dim}pt; ;; esac } ## check valid unit for given dimension function testdim() { dim=$1; case $dim in ?(\.)+([0-9])@(pt|in|cm|mm)) ;; +([0-9])\.*([0-9])@(pt|in|cm|mm)) ;; ?(\.)+([0-9])) dim=${dim}pt ;; +([0-9])\.*([0-9])) dim=${dim}pt ;; ?(\.)+([0-9])[!\.0-9]*) echo " illegal unit of measure in option '$3'; ignoring"; dim=$2; ;; +([0-9])\.*([0-9])[!\.0-9]*) echo " illegal unit of measure in option '$3'; ignoring"; dim=$2; ;; *) echo " not a valid dimension in option '$3'; ignoring"; dim=$2; ;; esac } ## sets variable $in to 1 if $1 is in list of ranges $2 function ifinrangelist() { n=$1; list=$2; in=0; for i in ${list//,/$IFS}; do case $i in +([0-9])) first=$i; last=$i;; -+([0-9])) first=1; last=`echo $i|tr -d -`;; +([0-9])-) first=`echo $i|tr -d -`; last=$nbp;; +([0-9])-+([0-9])) first=`echo $i|cut -d- -f1`; last=`echo $i|cut -d- -f2`;; -) first=1; last=$nbp;; *) myecho 1+ " error in range of pages (option '$3' contains '$i')"; esac if [[ $n -ge $first && $n -le $last ]]; then in=1; return; fi done return; } ## look for config file function setconfigfile() { if [[ `echo "$1"|grep -v "/"` ]]; then if [[ `echo "$1"|grep "\.xup$"` && $KPSEWHICH ]]; then CFFILE=`$KPSEWHICH $1` else CFFILE=`$KPSEWHICH $1.xup` fi fi if [[ (! -e $CFFILE) ]]; then if [[ -e $1 ]]; then CFFILE=$1 else if [[ -e $1.xup ]]; then CFFILE=$1.xup fi fi fi #echo $CFFILE; } ## normalize list + count number of pages to be displayed function cleancslor() { list=$1; result=""; for i in ${list//,/$IFS}; do case $i in +([0-9])) if [[ $i -le $nbp ]]; then result+="$i,"; nbpages+=1; else myecho 1+ " $file has $nbp page(s); page $i does not exist." fi ;; -+([0-9])) if [[ $i -le $nbp ]]; then result+="1$i,"; nbpages+=${i#-}; else myecho 1+ " $file has $nbp page(s); changed 1$i to 1-$nbp." result+="1$nbp,"; nbpages+=${nbp#-}; fi ;; +([0-9])-) if [[ $i -le $nbp ]]; then result+="$i$nbp,"; nbpages+=$(expr $nbp + 1 - ${i%-}); else myecho 1+ " $file has $nbp page(s); pages $i do not exist." fi ;; +([0-9])-+([0-9])) fstp=`echo $i|cut -d- -f1` if [[ $fstp -gt $nbp ]]; then myecho 1+ " $file has $nbp page(s); range $i omitted." fi lstp=`echo $i|cut -d- -f2` if [[ $lstp -gt $nbp ]]; then myecho 1+ " $file has $nbp page(s); range $i changed to $fstp-$nbp." lstp=$nbp; fi if [[ $lstp -ge $fstp ]]; then result+="$fstp-$lstp,"; nbpages+=$(expr 1 + $lstp - $fstp) fi ;; -) result+="1-$nbp,"; nbpages+=$nbp;; +([0-9])%+([0-9])) ## explicitly list all values... ## (simpler, but admitedly less efficient, ## than handling real modulos) declare -i cptr; cptr=`echo $i|cut -d% -f1`; step=`echo $i|cut -d% -f2`; while [[ $cptr -le $nbp ]]; do result+="$cptr,"; nbpages+=1; cptr+=$step; done;; *) ## anything else is discarded ;; esac done } function cleanup() { if [[ $DEBUG == 0 ]]; then myecho 1+ "-> cleaning"; myecho 2+ " * rm $RMopt *$filename.*" rm $RMopt *$filename.* fi } ARGS=$@; if [ $# -eq 0 ]; then usage; fi defaultvalues; setdefaultvalues; filename="temp-pdfxup-`date +%s`"; inputfilename="input-pdfxup-$filename"; watermarkfilename="watermark-pdfxup-$filename"; ###################################################################### ## check for ghostscript and pdflatex ###################################################################### ## GS could be specified from command-line : ${GS=`which gs`} ## regexp keeps only first two numbers of version number (eg. 9.53.3 -> 9.53) GSVERSION=`$GS --version 2>/dev/null |sed -e "s/\(.\.[^\.]*\)\..*/\\1/"`; if [ ! "$GSVERSION" ]; then echo " /!\\ ghostscript not found; aborting."; exit 1; fi GSNTS="9.27"; if [ ${GSVERSION%.*} -eq ${GSNTS%.*} ] && [ ${GSVERSION#*.} -gt ${GSNTS#*.} ] || \ [ ${GSVERSION%.*} -gt ${GSNTS%.*} ]; then GSOPTFILE=" --permit-file-read=$inputfilename.pdf " GSOPTWATERMARK=" --permit-file-read=$watermarkfilename.pdf " fi ## PDFLATEX could be specified on command-line : ${PDFLATEX=`which pdflatex`} PDFLATEXVERSION=`$PDFLATEX --version 2>/dev/null |grep 3.1415|grep -i tex`; if [ ! "$PDFLATEXVERSION" ]; then echo " /!\\ pdflatex not found; aborting."; exit 1; fi ## KPSEWHICH could be specified on command-line : ${KPSEWHICH=`which kpsewhich`} ###################################################################### ## command-line arguments ###################################################################### ## go through all arguments and options while [ $# != 0 ]; do case $1 in -x|--columns) NB_COLUMNS=$2; shift 2;; -x?(=)+([0-9])) NB_COLUMNS=`echo $1|sed -re "s/-x=?//"`; shift;; -y|--rows) NB_ROWS=$2; shift 2;; -y?(=)+([0-9])) NB_ROWS=`echo $1|sed -re "s/-y=?//"`; shift;; -nup|--nup) NB_COLUMNS=`echo $2|sed -re "s/x.*$//"`; NB_ROWS=`echo $2|sed -re "s/^.*x//"`; shift 2;; -?(-)nup?(=)+([0-9])x+([0-9])) NB_COLUMNS=`echo $1|sed -re "s/x.*$//"|sed -re "s/--?nup=?//"`; NB_ROWS=`echo $1|sed -re "s/^--?nup.*x//"`; shift;; -l|--landscape) case $2 in 1|yes|y|true) LANDSCAPE=1; SHIFT=2; ;; 0|no|n|false) LANDSCAPE=0; SHIFT=2; ;; -*) LANDSCAPE=1; SHIFT=1; ;; *) LANDSCAPE=1; #echo "Normal use of '$1' option is: '$1 [0|1]'"; #echo "Assuming you want landscape outupt..."; SHIFT=1; esac shift $SHIFT;; -l*) ANS=`echo $1|sed -re "s/-l=?//"`; case $ANS in 1|yes|y|true) LANDSCAPE=1; ;; 0|no|n|false) LANDSCAPE=0; ;; *) LANDSCAPE=1; #echo "Normal use of '$1' option is: '$1 [0|1]'"; #echo "Assuming you want landscape outupt..."; esac shift;; -nl|--portrait|--no-landscape|--nolandscape) LANDSCAPE=0; shift;; -b|--booklet) case $2 in se|short-edge) BOOKLET="2"; SHIFT=2; ;; 1|yes|y|true|le|long-edge) BOOKLET=1; SHIFT=2; ;; 0|no|n|false) BOOKLET=0; SHIFT=2; ;; -*) BOOKLET=1; SHIFT=1; ;; *) BOOKLET=1; #echo "Normal use of '$1' option is: '$1 [0|1]'"; #echo "Assuming you want booklet outupt..."; SHIFT=1; esac if [[ $BOOKLET != 0 ]]; then FRAME_WIDTH=0pt # this is the default, but can be changed by passing -fw option fi shift $SHIFT;; -b[^b]*|--booklet*) ANS=`echo $1|sed -re "s/--?b(ooklet)?=?//"`; case $ANS in se|short-edge) BOOKLET="2"; ;; 1|yes|y|true|le|long-edge) BOOKLET=1; ;; 0|no|n|false) BOOKLET=0; ;; *) BOOKLET=1; #echo "Normal use of '$1' option is: '$1 [0|1]'"; #echo "Assuming you want booklet outupt..."; esac if [[ $BOOKLET != 0 ]]; then FRAME_WIDTH=0pt # this is the default, but can be changed by passing -fw option fi shift;; -cf|--config|--config-file|-?(-)mode) case $2 in -*) echo "Error. Option '$1' expects a file name; found '$2'."; echo "Aborting."; exit 0; ;; *) setconfigfile $2 ;; esac if [[ -e "$CFFILE" ]]; then source $CFFILE else myecho 0+ " /!\ configuration file $2 not found" fi shift 2;; -cf=*|--config=*|--config-file=*|-?(-)mode=*) TMP=`echo $1|cut -d= -f2-` setconfigfile $TMP if [[ -e "$CFFILE" ]]; then source $CFFILE else myecho 0+ " /!\ configuration file $TMP not found" fi shift;; -?(-)col|--column|--column-mode|-?(-)vert?(ical)) COLUMN=1 shift;; -?(-)row|--row-mode|-?(-)line?(s)|-?(-)horiz?(ontal)) COLUMN=0 shift;; -?(-)bal|--balance|--balancelast|--balance-last) BALANCELAST=1 shift;; -?(-)no-bal|--no-balance|--no-balancelast|--no-balance-last) BALANCELAST=0 shift;; -c|--clip) case $2 in 1|yes|y|true) CLIP=1; SHIFT=2; ;; 0|no|n|false) CLIP=0; SHIFT=2; ;; *) CLIP=1; SHIFT=1; esac shift $SHIFT;; -c*|--clip=*) ANS=`echo $1|sed -re "s/--?c(lip)?=?//"`; case $ANS in 1|yes|y|true) CLIP=1; ;; 0|no|n|false) CLIP=0; ;; esac shift;; -nc|--no-clip) CLIP=0; shift;; -ps|--paper|--paper?(-)size) PAPERSIZE=$2; shift 2;; -ps=*|--paper=*|--paper?(-)size=*) ## '=' is compulsory, as we expect a string... ANS=`echo $1|sed -re "s/(-ps=|--paper=|--paper-?size=)//"`; PAPERSIZE=$ANS; shift;; -p|--pages|--page) case $2 in -) PAGES+="$2,"; ;; -*) echo "Error: option '$1' expects a range, not '$2'."; echo "Aborting."; exit 0; ;; *) PAGES+="$2,"; ;; esac shift 2;; -p*|--page*) ANS=`echo $1|sed -re "s/--?p(ages?)?=?//"`; PAGES+="$ANS,"; shift;; -o|--out|--output-file|--outfile) OUTPUTFILE=$2; shift 2;; -o=*|--out=*|--output-file=*|--outfile=*) ANS=`echo $1|sed -re "s/--?o(ut(put-)?(file)?)?=//"`; OUTPUTFILE=$ANS; shift;; -ow) SILENTLY_OVERWRITE="1"; shift;; -ihm|--innerhmargin) dimtopt $2 $dfpdfxupINNER_HMARGIN "$1 $2"; INNER_HMARGIN=$dim; shift 2;; -ihm?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-ihm=?//"`; dimtopt $ANS $dfpdfxupINNER_HMARGIN $1; INNER_HMARGIN=$dim; shift;; -ivm|--innervmargin) dimtopt $2 $dfpdfxupINNER_VMARGIN "$1 $2"; INNER_VMARGIN=$dim; shift 2;; -ivm?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-ivm=?//"`; dimtopt $ANS $dfpdfxupINNER_VMARGIN $1; INNER_VMARGIN=$dim; shift;; -im|--innermargins) dimtopt $2 $dfpdfxupINNER_MARGINS "$1 $2"; INNER_HMARGIN=$dim; INNER_VMARGIN=$dim; shift 2;; -im?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-im=?//"`; dimtopt $ANS $dfpdfxupINNER_MARGINS $1; INNER_HMARGIN=$dim; INNER_VMARGIN=$dim; shift;; -hm|--hmargin) testdim $2 $dfpdfxupHMARGIN "$1 $2"; HMARGIN=$dim; shift 2;; -hm?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-hm=?//"`; testdim $ANS $dfpdfxupHMARGIN $1; HMARGIN=$dim; shift;; -vm|--vmargin) testdim $2 $dfpdfxupVMARGIN "$1 $2"; VMARGIN=$dim; shift 2;; -vm?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-vm=?//"`; testdim $ANS $dfpdfxupVMARGIN $1; VMARGIN=$dim; shift;; -m|--margins) testdim $2 $dfpdfxupMARGINS "$1 $2"; HMARGIN=$dim; VMARGIN=$dim; shift 2;; -m?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-m=?//"`; testdim $ANS $dfpdfxupMARGINS $1; HMARGIN=$dim; VMARGIN=$dim; shift;; -ihs|--inthspace) testdim $2 $dfpdfxupINTERM_HSPACE "$1 $2"; INTERM_HSPACE=$dim; shift 2;; -ihs?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-ihs=?//"`; testdim $ANS $dfpdfxupINTERM_HSPACE $1; INTERM_HSPACE=$dim; shift;; -ivs|--intvspace) testdim $2 $dfpdfxupINTERM_VSPACE "$1 $2"; INTERM_VSPACE=$dim; shift 2;; -ivs?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-ivs=?//"`; testdim $ANS $dfpdfxupINTERM_VSPACE $1; INTERM_VSPACE=$dim; shift;; -is|--intspaces) testdim $2 $dfpdfxupINTERM_SPACES "$1 $2"; INTERM_HSPACE=$dim; INTERM_VSPACE=$dim; shift 2;; -is?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-is=?//"`; testdim $ANS $dfpdfxupINTERM_SPACES $1; INTERM_HSPACE=$dim; INTERM_VSPACE=$dim; shift;; -fw|--framewidth|--frame-width) testdim $2 $dfpdfxupFRAME_WIDTH "$1 $2"; FRAME_WIDTH=$dim; shift 2;; -fw?(=)+([0-9])*) ANS=`echo $1|sed -re "s/-fw=?//"`; testdim $ANS $dfpdfxupFRAME_WIDTH $1; FRAME_WIDTH=$dim; shift;; -tf|--tight-frame) case $2 in 1|yes|y|true) TIGHT_FRAME=1; SHIFT=1; ;; 0|no|n|false) TIGHT_FRAME=0; SHIFT=1; ;; *) TIGHT_FRAME=1; SHIFT=1; esac shift $SHIFT;; -tf*|--tight-frame*) TIGHT_FRAME=`echo $1|sed -re "s/--?t(ight-)?f(rame)?=?//"`; shift;; -i) MVopt="-i"; RMopt="-i"; shift;; -nobb|--nobb|-no-bb|--no-bb) case $2 in -) EXCLUDE_FOR_BBOX+="$2,"; ;; -*) echo "Error: option '$1' expects a range, not '$2'."; echo "Aborting."; exit 0; ;; *) EXCLUDE_FOR_BBOX+="$2,"; ;; esac shift 2;; -nobb*|--no-bb*) ANS=`echo $1|sed -re "s/--?no-?bb=?//"`; EXCLUDE_FOR_BBOX+="$ANS,"; shift;; -bb|--bb|--bounding-box) case $2 in -) INCLUDE_FOR_BBOX+="$2,"; ;; -*) echo "Error: option '$1' expects a range, not '$2'."; echo "Aborting."; exit 0; ;; *) INCLUDE_FOR_BBOX+="$2,"; ;; esac shift 2;; -bb*|--bb*|--bounding-box) ANS=`echo $1|sed -re "s/--?b(ounding-?)?b(ox)?=?//"`; INCLUDE_FOR_BBOX+="$ANS,"; shift;; -kbb|--keepbb|--keep-bb|--original-bb) case $2 in 1|yes|y|true) KEEP_ORIG_BBOX=1; SHIFT=2; ;; 0|no|n|false) KEEP_ORIG_BBOX=0; SHIFT=2; ;; -*) KEEP_ORIG_BBOX=1; SHIFT=1; ;; *) KEEP_ORIG_BBOX=1; SHIFT=1; esac shift $SHIFT;; -kbb*|--keepbb*|--keep-bb*|--original-bb*) ANS=`echo $1|sed -re "s/--?(k(eep-?)?bb|orig[^=]*bb)=?//"`; case $ANS in 1|yes|y|true) KEEP_ORIG_BBOX=1; ;; 0|no|n|false) KEEP_ORIG_BBOX=0; ;; *) KEEP_ORIG_BBOX=1; esac shift;; -g|--get-bb|--getbb) case $2 in 1|yes|y|true) COMPUTE_BBOX_ONLY=1; SHIFT=2; ;; 0|no|n|false) COMPUTE_BBOX_ONLY=0; SHIFT=2; ;; -*) COMPUTE_BBOX_ONLY=1; SHIFT=1; ;; *) COMPUTE_BBOX_ONLY=1; SHIFT=1; esac shift $SHIFT;; -g*|--get-bb=*|--getbb=*) ANS=`echo $1|sed -re "s/--?g(etbb|et-bb)?=?//"`; case $ANS in 1|yes|y|true) COMPUTE_BBOX_ONLY=1; ;; 0|no|n|false) COMPUTE_BBOX_ONLY=0; ;; *) COMPUTE_BBOX_ONLY=1; #echo "Normal use of '$1' option is: '$1 [0|1]'"; #echo "Assuming you want landscape outupt..."; esac shift;; -s|--set-bb|--setbb) case "$2$3$4$5" in +([0-9])) SET_BBOX="$2 $3 $4 $5" ;; *) echo "Error: option '$1' expects 4 numbers."; echo "Aborting."; exit 0; ;; esac shift 5;; -V) case $2 in @(0|1|2|3)) if [[ $VERBOSITY -lt $2 ]]; then VERBOSITY=$2; fi SHIFT=2;; *) if [[ $VERBOSITY -lt 2 ]]; then VERBOSITY=2; fi SHIFT=1;; esac shift $SHIFT;; -V?(=)@(0|1|2|3)) if [[ $VERBOSITY -lt `echo $1|sed -re "s/-V=?//"` ]]; then VERBOSITY=`echo $1|sed -re "s/-V=?//"`; fi shift;; -V?(=)+([0-9])) VERBOSITY=9; shift;; -VV) if [[ $VERBOSITY -lt 3 ]]; then VERBOSITY=3; fi shift;; -VVV) if [[ $VERBOSITY -lt 4 ]]; then VERBOSITY=4; fi shift;; -q|--quiet) VERBOSITY=0; shift;; -w|--watermark|--watermark-file) WATERMARK_FILE=$2; shift 2;; -w=*|--watermark=*|--watermark-file=*) ANS=`echo $1|sed -re "s/--?w[^=]*=//"`; WATERMARK_FILE=$ANS; shift;; -wp|--wperiod|--watermark-period) WATERMARK_PERIOD=$2; shift 2;; -wp=*|--wperiod=*|--watermark-period=*) ANS=`echo $1|sed -re "s/--?w[^=]*=//"`; WATERMARK_PERIOD=$ANS; shift;; -v|--version) echo "pdfxup version $VERSION (released $VDATE)"; exit 0;; -d|--debug) DEBUG=1; shift;; -*) usage;; *:+([0-9\-,\%])) ARG=$1; file=${ARG%:*}; PAGES+="${ARG#*:},"; ## only one file is allowed, and no options ## should appear after the file name... break;; *) file=$1; ## only one file is allowed, and no options ## should appear after the file name... break;; esac; done ## check input file if [[ "$file" == "" ]]; then echo "pdfxup error: no input file given; exiting."; exit 1; fi ## try adding .pdf suffix if needed if [[ ! -e "$file" && `basename "$file" .pdf` == "$file" && -e "${file}.pdf" ]]; then myecho 2+ " changing file name '$file' to '$file.pdf'"; file="${file}.pdf"; fi; if [[ ! -e "$file" ]]; then echo "pdfxup error: file \"$file\" not found; exiting."; exit 1; fi ## check output file ## set bbox SBB=0; read -r x0 y0 w0 h0 <<<$(echo "-1 -1 -1 -1"); if [[ "$SET_BBOX" ]]; then SBB=1; read -r BBX BBY BBW BBH <<<$(echo $SET_BBOX); fi ## summarize options: myecho 1+ "-> processing options"; if [[ "$FILE" != "" ]]; then myecho 2+ " * used configuration file $FILE"; fi if [[ $BOOKLET -eq 1 ]]; then myecho 2+ " * booklet mode (long-edge)"; else if [[ $BOOKLET -eq 2 ]]; then myecho 2+ " * booklet mode (short edge)"; else myecho 2+ -ne " * columns=$NB_COLUMNS, rows=$NB_ROWS, "; if [[ $LANDSCAPE -eq 1 ]]; then myecho 2+ "landscape mode"; else myecho 2+ "portrait mode"; fi fi fi myecho 2+ " * paper size=$PAPERSIZE"; if [[ $CLIP -eq 1 ]]; then myecho 2+ " * clipping content outside bounding box"; else myecho 2+ " * not clipping content outside bounding box"; fi myecho 2+ " * output file is \"$OUTPUTFILE\""; if [[ "$INNER_HMARGIN" == "$INNER_VMARGIN" ]]; then myecho 2+ " * inner margins=$INNER_HMARGIN"; else myecho 2+ " * inner horizontal margin=$INNER_HMARGIN, inner vertical margin=$INNER_VMARGIN"; fi if [[ "$HMARGIN" == "$VMARGIN" ]]; then myecho 2+ " * margins=$HMARGIN"; else myecho 2+ " * horizontal margin=$HMARGIN, vertical margin=$VMARGIN"; fi if [[ "$INTERM_HSPACE" == "$INTERM_VSPACE" ]]; then myecho 2+ " * space between pages=$INTERM_HSPACE"; else myecho 2+ " * horizontal space between pages=$INTERM_HSPACE, vertical space=$INTERM_VSPACE"; fi if [[ $TIGHT_FRAME -eq 1 ]]; then TIGHT_FRAMEMESSAGE=" (tight)"; else TIGHT_FRAMEMESSAGE=" (loose)"; fi myecho 2+ " * frame rule width=$FRAME_WIDTH $TIGHT_FRAMEMESSAGE"; if [[ $MVopt == "-i" && $RMopt == "-i" ]]; then myecho 2+ " * interactive clean up"; else myecho 2+ " * non-interactive clean up"; fi if [[ $COMPUTE_BBOX_ONLY -eq 1 ]]; then myecho 2+ " * only compute bounding box"; fi if [[ $KEEP_ORIG_BBOX -eq 1 ]]; then myecho 2+ " * keep original bounding box"; fi if [[ $SBB -eq 1 ]]; then myecho 2+ " * bounding box set to $BBX $BBY $BBW $BBH"; fi if [[ "$WATERMARK_FILE" ]]; then myecho 2+ " * watermarking with file $WATERMARK_FILE"; myecho 2+ " * watermarking period=$WATERMARK_PERIOD"; fi ## here we report on our earlier checks about GS and PDFLATEX ## (again, we had to know the level of verbosity first...) myecho 2+ "-> checking for ghostscript and pdflatex" myecho 2+ " ghostscript is $GS (version $GSVERSION)"; if [ "$GSOPTFILE" ]; then myecho 2+ " [gs version > $GSNTS; we will use --permit-file-read]"; else myecho 2+ " [gs version <= $GSNTS; not using --permit-file-read]\""; fi myecho 2+ " pdflatex is $PDFLATEX (version $PDFLATEXVERSION)"; ###################################################################### ## get number of pages of input file ###################################################################### ## for this we make a copy of input file, to avoid problem with ## ghostscript not correctly handling filenames containing white spaces ## (see https://stackoverflow.com/questions/42578061/ghostscript-and-spaces-in-filenames) cp "$file" $inputfilename.pdf myecho 2+ "-> computing number of pages"; myecho 2+ " * $GS $GSOPTFILE -sDEVICE=bbox -dNOPAUSE -dQUIET -c \"($inputfilename.pdf) (r) file runpdfbegin pdfpagecount = quit\"" nbp=`$GS $GSOPTFILE -sDEVICE=bbox -dNOPAUSE -dQUIET -c "($inputfilename.pdf) (r) file runpdfbegin pdfpagecount = quit"` myecho 2+ " File \"$file\" has $nbp pages"; ## now we can deal with ranges of pages (INCLUDE_FOR_BBOX, EXCLUDE_FOR_BBOX, PAGES) ## cleancslor uses $nbp, so we have to compute this before declare -i nbpages; if [[ ${INCLUDE_FOR_BBOX} != "" ]]; then INCLUDE_FOR_BBOX=${INCLUDE_FOR_BBOX%,}; cleancslor $INCLUDE_FOR_BBOX; INCLUDE_FOR_BBOX=${result%,}; myecho 3+ " * use for bounding-box computation: "${INCLUDE_FOR_BBOX}; else if [[ ${PAGES} != "" ]]; then INCLUDE_FOR_BBOX=${PAGES%,}; cleancslor $INCLUDE_FOR_BBOX; INCLUDE_FOR_BBOX=${result%,}; myecho 3+ " * use for bounding-box computation: "${INCLUDE_FOR_BBOX}; else INCLUDE_FOR_BBOX=$dfpdfxupINCLUDE_FOR_BBOX; fi fi if [[ ${EXCLUDE_FOR_BBOX} != "" ]]; then EXCLUDE_FOR_BBOX=${EXCLUDE_FOR_BBOX%,}; cleancslor $EXCLUDE_FOR_BBOX; EXCLUDE_FOR_BBOX=${result%,}; myecho 3+ " * exclude from bounding-box computation: "${EXCLUDE_FOR_BBOX}; else EXCLUDE_FOR_BBOX=$dfpdfxupEXCLUDE_FOR_BBOX; fi if [[ ${PAGES} != "" ]]; then PAGES=${PAGES%,}; else PAGES=$dfpdfxupPAGES; fi nbpages=0; cleancslor $PAGES; PAGES=${result%,}; myecho 2+ " * pages to display: ${PAGES} (${nbpages} pages)"; if [[ $nbpages -le 0 ]]; then myecho 1+ " No pages to include. Aborting." exit 0; fi ###################################################################### ## set up some variables ###################################################################### ## clipping option if [[ $CLIP != 0 ]]; then myecho 2+ " clipping pages (anything outside bounding box will be dropped)"; CLIPOPT=",clip"; else myecho 2+ " not clipping pages (anything outside bounding box will be displayed)"; myecho 2+ " (pages may overlap)"; CLIPOPT=""; fi ## whether the frame should be tight around pages, or loose (use all width) if [[ $TIGHT_FRAME -eq 1 ]]; then TIGHT_FRAMEBOX="\fbox"; WIDE_FRAMEBOX=""; else TIGHT_FRAMEBOX=""; WIDE_FRAMEBOX="\fbox"; fi ## special case: if booklet, we force the numbers of rows and cols if [[ $BOOKLET != 0 ]]; then if [[ $NB_COLUMNS != 2 || $NB_ROWS != 1 || $LANDSCAPE == 0 ]]; then myecho 1+ " forcing booklet mode: 2 columns, 1 row, landscape mode"; NB_COLUMNS=2; NB_ROWS=1; LANDSCAPE=1; fi fi ## check watermarking file nbwp=1; ## set even if no watemark if [[ $WATERMARK_FILE != "" ]]; then if [[ ! -e $WATERMARK_FILE ]]; then myecho 1+ " pdfxup: watermarking file not found. Omiting watermarking." else myecho 2+ " * using file $WATERMARK_FILE for watermarking"; fi fi ## get number of pages of WATERMARK_FILE (PDF) file if [[ $WATERMARK_FILE != "" ]]; then if [[ -e $WATERMARK_FILE ]]; then if [[ `file -b $WATERMARK_FILE | grep "PDF"` ]]; then ## copy file in case it contains spaces cp "$WATERMARK_FILE" $watermarkfilename.pdf nbwp=`$GS $GSOPTWATERMARK -sDEVICE=bbox -dNOPAUSE -dQUIET -c "($watermarkfilename.pdf) (r) file runpdfbegin pdfpagecount = quit"` if [[ $WATERMARK_PERIOD -gt $nbwp || $WATERMARK_PERIOD -lt 1 ]]; then WATERMARK_PERIOD=$nbwp; fi fi fi fi ###################################################################### ## start computing bounding box ###################################################################### x=$x0 y=$y0 w=$w0 h=$h0 curr=0 page=0 step=0 if [[ $KEEP_ORIG_BBOX == 0 ]]; then if [[ $SBB == 0 ]]; then myecho 1+ -n "-> computing bounding box"; myecho 2+ ""; for r in ${INCLUDE_FOR_BBOX//,/$IFS}; do case $r in +([0-9])) first=$r; last=$r;; -+([0-9])) first=1; last=`echo $r|tr -d -`;; +([0-9])-) first=`echo $r|tr -d -`; last=$nbp;; +([0-9])-+([0-9])) first=`echo $r|cut -d- -f1`; last=`echo $r|cut -d- -f2`;; -) first=1; last=$nbp;; esac myecho 2+ " * $GS -dNOPAUSE -dSAFER -dQUIET -dBATCH -dFirstPage=$first -dLastPage=$last -sDEVICE=bbox $inputfilename.pdf" $GS -dNOPAUSE -dSAFER -dQUIET -dBATCH -dFirstPage=$first -dLastPage=$last -sDEVICE=bbox $inputfilename.pdf 2>&1 | while IFS= read -r line; do bbox=`echo $line | grep "%%BoundingBox" | sed "s/^.*Box: //"`; if [[ $bbox != "" ]]; then for i in `echo $line | grep "%%BoundingBox" | sed "s/^.*Box: //"`; do if [[ $curr == 0 ]]; then eval page=$(( page+1 )); fi case $curr in 0) thisx=$i; #if [[ $x -gt $i ]]; then x=$i; fi ;; 1) thisy=$i; #if [[ $y -gt $i ]]; then y=$i; fi ;; 2) thisw=$i; #if [[ $w -lt $i ]]; then w=$i; fi ;; 3) thish=$i; #if [[ $h -lt $i ]]; then h=$i; fi ifinrangelist $page $EXCLUDE_FOR_BBOX; if [ $in == 0 ]; then eval step=$(( step+1 )); if [[ $x -lt 0 || $x -gt $thisx && $thisx+$thisw -gt 0 ]]; then x=$thisx; fi if [[ $y -lt 0 || $y -gt $thisy && $thisy+$thish -gt 0 ]]; then y=$thisy; fi if [[ $w -lt 0 || $w -lt $thisw && $thisx+$thisw -gt 0 ]]; then w=$thisw; fi if [[ $h -lt 0 || $h -lt $thish && $thisy+$thish -gt 0 ]]; then h=$thish; fi myecho 1 -n "." myecho 2 -ne " bbox: x=$x y=$y X=$w Y=$h (step $step) \\r"; myecho 3+ " bbox: x=$x y=$y X=$w Y=$h (step $step) "; fi ;; esac eval curr=$(( (curr+1) % 4 )); done fi done; done; myecho 2- ""; else x=$BBX; y=$BBY; w=$BBW; h=$BBH; fi fi EMPTYBB=$KEEP_ORIG_BBOX; if [[ $KEEP_ORIG_BBOX == 0 && $x -eq $x0 && $y -eq $y0 && $h -eq $h0 && $w -eq $w0 ]]; then myecho 1+ " * empty bounding box; keeping original margins"; EMPTYBB=1; #exit 1; fi if [[ $SINCLUDE_FOR_BBOX == 0 && $EMPTYBB == 0 ]]; then myecho 1 -n "." myecho 2+ -ne " bbox: x=$x y=$y X=$w Y=$h (final) \\r"; myecho 3+ ""; myecho 3+ -ne " (processed $page pages) \\r"; myecho 1+ ""; fi if [[ $COMPUTE_BBOX_ONLY != 0 ]]; then myecho 1+ " final bounding box: x=$x y=$y X=$w Y=$h"; cleanup; exit 0; fi ## compute options to pass to \includegraphics #INNER_HMARGIN=${INNER_HMARGIN%pt}; #INNER_VMARGIN=${INNER_VMARGIN%pt}; if [[ $EMPTYBB -eq 0 ]]; then # eval x=$((x-INNER_HMARGIN)) # eval y=$((y-INNER_VMARGIN)) # eval w=$((w+INNER_HMARGIN)) # eval h=$((h+INNER_VMARGIN)) BBOPTION="viewport=$x $y $w $h"; else # BBOPTION="trim=-$INNER_HMARGIN -$INNER_VMARGIN -$INNER_HMARGIN -$INNER_VMARGIN"; BBOPTION="trim=0 0 0 0"; fi ###################################################################### ## start writing .tex file ###################################################################### cat > $filename.tex <> $filename.tex;; 2) echo " \booklettrue" >> $filename.tex echo " \longedgefalse" >> $filename.tex;; *) echo " \booklettrue" >> $filename.tex echo " \longedgetrue" >> $filename.tex;; esac cat >> $filename.tex < producing final file"; case $VERBOSITY in 0|1|2) myecho 2+ " * $PDFLATEX -interaction=nonstopmode $filename.tex" $PDFLATEX -interaction=batchmode $filename.tex > /dev/null 2>&1 || \ LATEXFAILED=1;; *) myecho 3+ " * $PDFLATEX -interaction=nonstopmode $filename.tex" $PDFLATEX -interaction=nonstopmode $filename.tex || LATEXFAILED=1;; esac ###################################################################### ## clean up ###################################################################### if [[ $LATEXFAILED -eq 1 ]]; then myecho 1+ "$PDFLATEX: compilation failed!" myecho 1+ "Use option '-d' to get and analyse failing file." cleanup exit 0; fi if [[ $EMPTYBB -eq 0 ]]; then myecho 0+ " final scale: "`cat $filename.scl | sed -e "s/pt$//"`"%" fi if [[ $SILENTLY_OVERWRITE -eq 1 ]]; then myecho 2+ " * mv $MVopt $filename.pdf \"$OUTPUTFILE\"" mv $MVopt $filename.pdf "$OUTPUTFILE" else myecho 2+ " * mv $MVopt -i $filename.pdf \"$OUTPUTFILE\"" mv $MVopt -i $filename.pdf "$OUTPUTFILE" fi cleanup exit 0 ################################################################## ## ## Manual ## ################################################################## pdfxup creates a PDF document where each page is obtained by combining several pages of a PDF file given as output. The important feature of pdfxup, compared to similar programs, is that it tries to compute the (global) bounding box of the input PDF file, in order to remove the margins and to keep the text only. Instead of having the font size divided by 2 (for the case of 2-up output), in some case you may end up with the same font size as in the original document (as is the case for a default 'article' document produced by LaTeX). pdfxup has numerous options: * '-x n' and '-y n' can be used to change the number of rows and columns of the resulting document. [default: -x2 -y1] * '-l n' turns the output to landscape (n=1) or portrait (n=0). [default: -l1] * '-b n' toggles the 'booklet' mode: the resulting PDF can be printed and folded as a booklet. Printing such a booklet requires 2-sided printing, which can follow either the 'long-edge' or the 'short edge' of the paper. 'long-edge' seems to be the most commonly used, but using '-b se' while produce a PDF booklet for 'short-edge' 2-sided printing. * '-c' clips pages to their bounding box: this is set by default, so that any content outside bounding box will be lost. Using "-c 0", anything outside the bounding box will be displayed, and may overlap neighbouring pages. * '-p' can be used to only consider a set of pages of the input file. The set of pages to include is a comma- separated list of pages, ranges of pages of the form 'm-n', or sets of pages of the form "0%2" (all even- numbered pages). Alternatively to '-p', the page range can be specified after the file name, writing 'file.pdf:' in place of '-p '. * '-bb' can be used to list the pages to be taken into account when computing the bounding box. This is especially useful for large documents, because computing the bounding box on each page can be long, and because computing the bounding box over the first 10 pages is usually enough... This will not change the number of pages included in the final document. Several '-bb' options can be given, which will result in considering the union of all the intervals. * '-nobb' is used to remove pages from the computation of the bounding box. This is useful if one page has e.g. special headers (e.g. for arXiv papers). Notice that the bounding box of those pages will still be computed by ghostscript, so this does not save time. I'd be grateful to anyone willing to implement difference of unions of intervals... ;-) * '-fw d' defines the width of the frame around each page. Set to 0pt to have no frame at all. * inner margins: margins to be added between each frame and its included page. * margins: margins of the final document * spacing: spacing between frames * '-w file' can be used to add watermarking to all pages. The file can be anything that pdflatex can handle (eg. png, pdf). If it is a PDF with several pages, page n of the watermarking file will be used with page n of the input file. By default, the last page will be repeated if the input file has more pages than the watermarking file. This behaviour can be modified with the '-wp n' options, which asks to repeat the last n pages instead of only the last one. Setting `-wp 0` will repeat all the pages of the watermarking file. ################################################################## ## ## Examples ## ################################################################## # pdfxup file.pdf -> creates pdfxup.pdf from file.pdf with default options (2-up, margins=5pt) # pdfxup -bb 1-4 file.pdf -> same behaviour, but computes the bounding box only using the first 4 pages (this saves time when processing long documents) # pdfxup -b file.pdf -> same behaviour, but creates a booklet # pdfxup -kbb -b -o booklet.pdf file.pdf:3-25 -> creates a booklet from pages 3-25 of file.pdf, without reducing margins (option '-kbb' disables bounding-box computation; equivalent to e.g. '-bb 0'); name the resulting file booklet.pdf. # pdfxup -kbb -x1 -y2 -l0 beamer-frames.pdf -> arranges 2 beamer frames per page (not reducing margins) # pdfxup -kbb -x2 -y2 -l1 beamer-frames.pdf -> arranges 4 beamer frames per page (not reducing margins) # pdfxup -fw0 -w draft.png file.pdf -> remove frame border; add 'draft' watermarking on all pages ################################################################## ## ## Releases ## ################################################################## Author: Nicolas Markey Please send your comments and bug reports to v1.00 (2015/08/23) - first release v1.10 (2015/09/08) - added option 'booklet' - corrected numerous computation/spacing bugs in LaTeX file - added succinct documentation v1.20 (2015/09/11) - added options -nobb, -bb, -p - solved bug with dots in PDF file names v1.30 (2015/10/06) - added options -g and -s for getting and then manually - setting bounding box. - created man page v1.50 (2019/12/31) - added the -c option (to clip pages) - corrected + improved option -p - added watermarking v1.51 (2020/01/06) - corrected bug in dimtopt() (conversion mm to pt) v1.60 (2020/06/24) - added --allow-file-read=... option for ghostscript >= 9.28 (thanks to Janis Kalofolias for pointing out the problem) - allow file names containing spaces - better inclusion of watermarks v1.61 (2020/10/21) - clean up after option '-g' (thanks to Oliver Redner) v2.00 (2020/12/17) - fixed a bug with inner margins - added --tight-frame option: tells whether the frame should be tight around the page (and leave horizontal white space outside the frame), or as large as possible. - added --config-file option: configuration files (default extension .xup) can be used to define default values for frequent situations. - cleaned-up man page, adding some missing options (thanks to Louis Gostiaux for pointing undocumented options) v2.10 (2021/04/25) - fixed a bug with ghostscript adding a patch-level number in their version number. This is the bug reported in https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=976080 It was solved with the help of Christian Gnaegi. - fixed 2 bugs in the computations of size of pages - fixed a bug which inhibited short-edge option of booklets - added options --col (to fill in pages vertically first) and --bal (to balance columns (or rows) the last page). This feature was requested by Philipp Killinger. v2.11 (2024/03/15) - fixed a bug in testdim, which did not support fractional values (bug reported by Walt Tuvell)