summaryrefslogtreecommitdiff
path: root/support/view_print_ps_pdf/vpp
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/view_print_ps_pdf/vpp
Initial commit
Diffstat (limited to 'support/view_print_ps_pdf/vpp')
-rwxr-xr-xsupport/view_print_ps_pdf/vpp1012
1 files changed, 1012 insertions, 0 deletions
diff --git a/support/view_print_ps_pdf/vpp b/support/view_print_ps_pdf/vpp
new file mode 100755
index 0000000000..e2789eca44
--- /dev/null
+++ b/support/view_print_ps_pdf/vpp
@@ -0,0 +1,1012 @@
+#!/bin/bash
+
+shopt -s extglob
+Version=3.07
+Myname="${0##*/}"
+
+<<'DOC'
+= vpp - View and (selectively) Print PDF and PostScript
+
+= Synopsis
+vpp [options] [file]
+
+Options:
+-h,--help print a help message and exit
+-H,--Help print print full documentation via less and exit
+-V,--version print version and exit
+-b,--batch=STRING run in batch after running commands in STRING
+ --view view the document (this is the default)
+ --noview do not view the document
+ --viewer=KEY use pdf viewer named KEY
+ --print offer printing interaction (this is the default)
+ --noprint do not offer printing interaction
+-p,--printer=KEY print to printer named KEY
+-v,--verbose be verbose
+ --noverbose don’t be verbose (this is the default)
+-r,--rc[=STRING] use STRING as an rc file; if STRING is absent,
+ don’t read any rc file
+
+Arguments for short options are given without a separator, so you can write
+either |--rc=myrc| or |-rmyrc|.
+
+= Description
+vpp is a Bash script that displays a PDF or PostScript document (after
+conversion to PDF). The user can use the viewer to print the document or,
+alternatively, leave the viewer and use vpp’s facilities to print selected
+pages to a one- or two-sided hardcopy or an A5-booklet: see the section
+/Page selection and other commands/ for the details. Instead of printing your
+selections, you can also save them into PDF files.
+
+If |file| is specified with a |.ps| or a |.pdf| extension, vpp will simply use
+that |file|. Otherwise, vpp will look for |file.pdf|, |file.ps|, and |file|, in
+that order, and will use the first existing file. If |file| lacks, standard
+input is used.
+
+In any case, the first few characters /in/ the file determine whether it is
+treated as a PDF or as a PostScript file.
+
+vpp needs a viewer and a printer; see the section /Printers and viewers/.
+
+= Exit value
+
+vpp has four possible exit values:
+
+0 OK
+1 error
+2 edit, which is a signal to the calling program that a new
+ edit session is at order; this is used by |mk|.
+3 re-compile; this is used by |mk|
+
+= Dependencies
+
+kpsewhich from texlive
+pdflatex from texlive
+pdfpages.sty from texlive
+pdfinfo from poppler-utils
+ps2pdf from ghostscript
+texi2dvi version 1.152 or greater, from texinfo
+mktemp from coreutils
+readlink from coreutils
+getopt from util-linux
+lpr from cups-bsd
+lpoptions,lpstat from cups-client
+file,less
+texlog_extract from texlive
+
+= Printers and viewers
+For the operation of vpp, availability of printers and pdf viewers is important.
+Therefore, two associative arrays are defined:
+printers an array available printers, where the keys are printer names
+ and the values are |true| or |false|, depending on whether the
+ printer can print double sided (true) or not (false).
+
+viewers an array of available viewers, where the keys are viewer names
+ and the values are the corresponding commands, with arguments if any.
+
+For each array, an index is set: |printer| for the printers array. |viewer| for
+the viewers array. |printer| points to the current printer, |viewer| to the
+current viewer.
+
+You can inspect the contents of these arrays either with the short help option
+(|-h| or |--help|) or, when you are in vpp's command mode, with the |?| command.
+
+The |printers| array is automatically filled, using the |lpstat| and
+|lpoptions| commands.
+
+The |viewers| array can be set in several ways:
+
+- It is initially set to:
+
+ viewers=([xp]=xpdf [ev]=evince [gv]=gv [ac]=acroread)
+
+ but after that, unavailable viewers are silently removed. This may result in
+ an empty list.
+
+- After this, an rc file may be available, either in the form of |~/.vpprc| or specified
+ with the |--rc| option.
+
+- the |--viewer| option may specify a viewer.
+
+- Finally, a viewer may be specified on-the-fly, with the |v| command (see the
+ section on /Page selection and other commands/.
+
+= Options
+
+vpp comes with several options. Before evaluating any options, vpp will try to
+read the user rc-file, |~/.vpprc|, where you can set defaults for most options,
+by assigning values to variable named after the long form of the options. For
+example, there are three ways to select the printer named /k550/:
+
+- use the option |--printer=k550|.
+- enter a line with |printer=k550| in your rc file (|~/.vpprc|, for example).
+- in command mode, enter |pk550|.
+
+These are the variables that can be set in |~/.vpprc|:
+batch (string) sets the |--batch| option
+print (true or false) sets printing interaction on or off
+printer (string) sets the |--printer| option
+verbose (true or false) sets the |--verbose| option
+view (true or false) sets viewing on or off
+viewer (string) set the viewer; arguments may be added; example:
+
+ viewer='acroread -geometry 1450x1150+0+0'
+
+ You should use a basename here, that is: the name of the viewer
+ should contain no slashes, and it should be in your PATH.
+
+--help
+ Prints synopsis and available printers and viewers, then quits.
+--Help
+ Prints this documentation, /via/ less.
+--version
+ Prints version, then quits.
+--verbose
+ Prints messages about the progress vpp is making. Can be reverted
+ with |--noverbose|.
+--rc=rc-file
+ Read the specified |rc-file|, instead of the default rc-file, |~/.vpprc|.
+ If this option is used, it must be used before any other options.
+ If |rc-file| is an empty string, no rc-file will be read, thus skipping
+ reading of |~/.vpprc|.
+--batch=string
+ Prevents the --print option to interrogate the user about pages to
+ be printed. Instead the document is printed according to the commands
+ in the mandatory |string|. Also sets viewing off. Thus the command
+
+ vpp --batch '2-3 x3' test.pdf
+
+ prints 3 copies of pages 2 and 3 of |test.pdf| without interaction
+--print
+ Present the print prompt. This is the default. Can be reverted with
+ |--noprint|, normally used to suppress the print prompt, for
+ example when using vpp from other scripts that generate PDF or
+ PostScript documents that have only to be displayed or printed
+ without even being displayed.
+--view
+ Run the file viewer. This is the default. Can be reverted with
+ |--noview|, normally used to suppress starting the viewer, for
+ example when using vpp from other scripts that generate PDF or
+ PostScript documents that have only to be printed.
+--printer=key
+ Specifies the printer to be used instead of the system default
+ printer. See the section /Printers and viewers/ for more information.
+--viewer=[key|command]
+ Specifies the viewer to use. This script defines an associative
+ array |viewers| containing 4 viewers as follows:
+
+ viewers=([xp]=xpdf [ev]=evince [gv]=gv [ac]=acroread)
+
+ and the viewer is set to xp by default. However, you can define
+ your own set of viewers in the |~/.vpprc| file or in any rc file
+ given with the |--rc| option. For example:
+ viewers=(
+ [xp]="xpdf -g 970x1050+0+0 -font 8x13bold -z page -cont"
+ [ac]="acroread -geometry 850x890+0+0"
+ [ev]="evince --fullscreen --presentation"
+ )
+ viewer=xp
+
+= Page selection and other commands
+
+When you select the |--print| option, and you did not also use the |--batch|
+option, vpp interrogates you about the pages you want to print. To that end the
+following prompt appears:
+
+ vpp command (? for help):
+
+upon typing |?| or |h|, vpp displays examples of possible commands:
+
+ Command Examples:
+ 5 to print page 5
+ 5- to print pages 5 through the end
+ 5-7 to print pages 5, 6 and 7
+ 7-5 ox write the same pages, in reversed order, to x.pdf
+ -7 to print the first 7 pages
+ 5-7,19- to print pages 5, 6, 7 and 19 through the end
+ a to print the whole document
+ - to print the whole document
+ a x3 to print 3 copies of the document
+ x3 the same
+ 5 x3 to print 3 copies of page 5
+ t print the whole document twosided
+ t 2- print twosided starting at page 2
+ b to print the whole document as an a5 size booklet
+ b -12 to print the first 12 pages as an a5 size booklet
+ Other commands:
+ e (if called by mk) edit the tex source and rerun mk
+ c (if called by mk) rerun mk
+ v (re)view the ps/pdf file or, with an argument, specify a viewer.
+ w list errors and warnings from the log file
+ oxyz send pdf output to file xyz.pdf instead of printer
+ pxyz print to printer xyz
+ h display this help
+ ? display this help
+ q quit
+
+With these descriptions, no further explanation should be necessary, except for
+the following:
+
+When twosided (|t|) or booklet (|b|) printing is selected for a non-duplex
+printer, printing will be performed in two shifts, one for the front side and
+one for the backside. Between the shifts, another prompt appears:
+
+ printer ready? then turn stack and type return
+
+You will have to arrange your printer such that, with the printed sides up, the
+first page printed will be at the bottom of the stack, and the last page printed
+will be on top. Normally you will then have your output come out the back of
+your printer. /Turn the stack/ then means: rotate it over the long side of the
+paper and feed it back into the printer for the other side to be printed.
+
+When you use the |oxyz| subcommand, your selection will not be printed but
+instead will be saved in a PDF file named |xyz.pdf|. When you use a |t| or |b|
+selection, you will not, of course, be prompted to turn the paper stack.
+Instead, the odd and even pages of your selection will be saved in separate PDF
+files, |xyz_odd.pdf| and |xyz_even.pdf|.
+
+= Environment
+Two environment variables may be useful in scripts using vpp:
+VPPOUTDIR The directory where PDF files generated with the o command will
+ be saved; the default is the working directory.
+VPPCHECKSAVED If non-empty, vpp will check on exit that the inspected file
+ has been saved into a pdf file and will issue a warning if it hasn’t.
+
+= Examples
+
+Since vpp can read from standard input, it can be used to print (parts of)
+manpages. This example (we assume a printer which cannot print double
+sided) prints the full |ls| manpage first, followed by an A5 booklet of the
+first 8 pages:
+
+ $ man -t ls | vpp # (shows preview and is left with q)
+ vpp command (? for help): a
+ vpp command (? for help): b 1-8
+ printer ready? then turn pack over the long side and type enter (^D skips)
+ vpp command (? for help): q
+ $
+
+If you don’t need a preview, because you have seen the man page already, you
+can print it immediately as an A5 booklet with:
+
+ $ man -t ls | vpp --batch=b
+
+or, to make an A5 booklet of the first 8 pages:
+
+ $ man -t ls |vpp --batch='-8 b'
+
+If you just want to save a PDF copy of the man page in |ls.pdf|, you can say:
+
+ $ man -t ls |vpp -bols
+
+Some PDF-documents, like the CVS manual (|cvs.pdf|), have their Table of
+Contents in their back instead of behind the title page. You can use vpp to
+rearrange such documents:
+
+ $ vpp --batch='1,2,153-160,3-152 ocvs' cvs.pdf
+
+This overwrites the input document. Note that any links in the file will get
+broken, so that is only useful for documents that have to be printed. It would
+have been more sensible in this case to say:
+
+ $ vpp --batch='b 1,2,153-160,3-152' cvs
+
+which prints the reordered document as an A5 booklet without replacing it. You
+can even print or output page ranges in reverse order:
+
+ $ vpp --batch='12-1 otest' cvs.pdf
+
+= Changes
+- Changes with respect to version 3.06:
+- --doublesided option removed and d command
+ (vpp uses lpoptions to find out printer properties)
+- --norc option removed; use --rc=''
+- printers and viewers listed with ?-command and with --help option
+- v command with argument selects an other viewer
+- --batch did not work always
+
+= Author
+[Wybo Dekker](wybo@dekkerdocumenten.nl)
+
+= Copyright
+Released under the [GNU General Public License](www.gnu.org/copyleft/gpl.html)
+DOC
+
+ die() { echo -e "$Myname: $Red${*}$Nor" 1>&2; exit 1; }
+ Warn() { echo -e "$Myname: $Mag${*}$Nor" 1>&2; }
+ warn() { $verbose && Warn "$@"; }
+helpsrt() { sed -n '/^= Synopsis/,/^= /p' "$0"|sed '1d;$d';
+ echo -e "$(list_printers)";
+ echo -e "$(list_viewers)";
+ exit
+ }
+helpall() { sed -n '/^<<.DOC.$/,/^DOC$/p' "$0"|sed -n '1d;$d;p'|less; exit; }
+version() { echo $Version; exit; }
+install() { which instscript>&/dev/null && instscript --zip --pdf --markdown "$Myname"; exit; }
+
+Red='\e[38;5;1m' # light red ]
+Blu='\e[38;5;4m' # light blue ]
+Mag='\e[38;5;5m' # light magenta]
+Nor='\e[0m' # reset color ]
+
+<<'DOC' #------ function check_needs --------------------------------------------
+= check_needs
+parameters: -
+description: Verify the availability of executables and tex files
+globals set: neededex neededtx
+globals used: neededex neededtx
+returns: 1 if something is missing, 0 otherwise
+DOC
+#-------------------------------------------------------------------------------
+check_needs() {
+ local i err=false
+ # executables:
+ for i in "${!neededex[@]}"; do
+ which "${neededex[$i]}" >/dev/null && unset neededex["$i"]
+ done
+ if [[ ${#neededex[@]} -gt 0 ]]; then
+ Warn "Missing executables: ${neededex[*]}"
+ err=true
+ fi
+ # tex files:
+ for i in "${!neededtx[@]}"; do
+ kpsewhich "${neededtx[$i]}" >/dev/null && unset neededtx["$i"]
+ done
+ if [[ ${#neededtx[@]} -gt 0 ]]; then
+ Warn "Missing TeX files: ${neededtx[*]}"
+ err=true
+ fi
+ $err && die Quitting...
+}
+
+<<'DOC' #------ function check_viewers --------------------------------------------
+= check_viewers
+parameters: -
+description: Check pdf viewers
+ The variable |viewer| is a key to the viewers array.
+ Test viewers for executability.
+globals set: viewer
+globals used: viewer viewers
+returns: 0
+DOC
+#-------------------------------------------------------------------------------
+check_viewers() {
+ local i j
+ # any viewers defined?
+ [[ ${#viewers[@]} -eq 0 ]] && die "No viewer could be found"
+ # check if al viewers are executable
+ for i in "${!viewers[@]}"; do
+ j="${viewers[$i]%% *}"
+ which "$j" >& /dev/null || die "$j is not executable or not in your PATH"
+ done
+ # has a viewer been selected?
+ if [[ -n $viewer ]]; then
+ [[ -z ${viewers[$viewer]} ]] && die "viewer $viewer is undefined"
+ else # if not defined, take a random one
+ viewer="${!viewers[*]}"
+ viewer="${viewer%% *}"
+ fi
+}
+
+<<'DOC' #------ function handle_options --------------------------------------------
+= handle_options
+parameters: the script’s arguments
+description: Handles the options
+globals set: first batch input mk print printer rc verbose view viewer viewers writeto
+globals used: first Version viewer viewers printer printers
+returns: 1 on error, 0 otherwise
+DOC
+#-------------------------------------------------------------------------------
+first=true
+handle_options() {
+ local options rcfile
+ if ! options=$(getopt \
+ -n "$Myname" \
+ -o b:p:dr::VvhHqI \
+ -l batch:,printer:,rc::,norc,version,verbose,noverbose,view,noview,viewer:,print,noprint,help,Help,quiet,noquiet,mk: -- "$@"
+ ); then exit 1
+ fi
+ eval set -- "$options"
+ while [ $# -gt 0 ]; do
+ if $first; then
+ first=false
+ if [[ $1 = -r ]] || [[ $1 == --rc ]]; then # if the first option is --rc
+ # shellcheck disable=SC1090
+ test -n "$2" && source "$2" # if its argument is not empty, source it
+ shift 2
+ rcfile="$2"
+ continue
+ else
+ rcfile="$HOME/.${Myname}rc"
+ # shellcheck disable=SC1090
+ test -e "$HOME/.${Myname}rc" && source "$rcfile"
+ fi
+ fi
+ case $1 in
+ (-h|--help) # print a help message and exit
+ helpsrt
+ ;;
+ (-H|--Help) # print print full documentation via less and exit
+ helpall
+ ;;
+ (-V|--version) # print version and exit
+ echo $Version
+ exit
+ ;;
+ (-b|--batch) # run in batch using STRING for print command
+ batch="$2"
+ Warn() { die "$@"; }
+ shift 2
+ ;;
+ ( --view) # view the document (this is the default)
+ view=true
+ shift
+ ;;
+ ( --noview) # do not view the document
+ view=false
+ shift
+ ;;
+ ( --viewer) # specifies the pdf viewer to use
+ viewer="${2%% *}"
+ [[ -z ${viewers[$viewer]} ]] && viewers[$viewer]="$2"
+ shift 2
+ ;;
+ ( --print) # offer printing interaction (this is the default)
+ print=true
+ shift
+ ;;
+ ( --noprint) # do not offer printing interaction
+ print=false
+ shift
+ ;;
+ (-p|--printer) # print to printer named STRING
+ [[ -n ${printers["$2"]} ]] || die "specified printer ($2) does not exist"
+ printer="$2"
+ shift 2
+ ;;
+ (-v|--verbose) # be verbose
+ verbose=true
+ shift
+ ;;
+ ( --noverbose) # don’t be verbose (this is the default)
+ verbose=false
+ shift
+ ;;
+ (-r,--rc) # set rc file, but this must be the first option!
+ die 'the --rc option must be used before any other options'
+ ;;
+ ( --mk) mk="\n e edit the tex source and rerun mk\n c rerun mk"
+ writeto="$2"
+ shift 2
+ ;;
+ (-I) instscr 2>/dev/null || die "the -I option is for developers only"
+ ;;
+ (--) shift
+ break
+ ;;
+ (*) break
+ ;;
+ esac
+ done
+ [[ ${#@} -gt 1 ]] && die "expecting zero or one input files, not ${#@}"
+ input=${1%.} # remove final . (may be there by auto completion)
+ [[ -n $batch ]] && view=false
+}
+
+<<'DOC' #------ function find_pdf --------------------------------------------
+= find_pdf
+parameters: -
+description: Find the input and provide a pdf-copy;
+ If vpp had no file argument, standard input is used.
+ If the argument has one of the extensions .pdf, .ps or .eps, or
+ any uppercase variant, that file is used.
+ Any other argument is used as such, if the file exists or, if not,
+ a .pdf, PDF, PS, .ps, .eps or .EPS extension is added and the
+ first existing file is used.
+globals set: log tempdir
+globals used: input tempdir
+returns: 1 if no input is found, 0 otherwise.
+DOC
+#-------------------------------------------------------------------------------
+find_pdf() {
+ local tempdir
+ tempdir=$(mktemp -d -t vpp.XXXXXXXXXX)
+ # shellcheck disable=SC2064
+ trap "rm -rf $tempdir" 0 1 2 15
+ warn "running in temporary directory $tempdir"
+ shopt -s nocasematch
+ if [[ -z $input ]]; then
+ warn "using standard input"
+ cat - > "$tempdir/main"
+ exec 0>&-
+ exec 0</dev/tty
+ elif [[ $input =~ \.(pdf|ps|eps)$ ]]; then
+ [[ -e $input ]] || die "$input: not found"
+ [[ -s $input ]] || die "$input: empty"
+ [[ -r $input ]] || die "$input: not readable"
+ cat "$input" > "$tempdir/main"
+ warn "using $input"
+ log=$(readlink -m "${input%.pdf}.log")
+ else
+ local i found=false
+ for i in $input.{pdf,PDF,ps,PS,eps,EPS} $input; do
+ if [[ -e $i ]]; then
+ found=true
+ break
+ fi
+ done
+ $found || die "$input: not found; I tried $input.{pdf,PDF,ps,PS,eps,EPS} and $input"
+ [[ -s $i ]] || die "$i: empty file"
+ cat "$i" > "$tempdir/main"
+ fi
+ shopt -u nocasematch
+ cd "$tempdir" || exit 1
+ local typ
+ typ=$(file -b main)
+ typ=${typ%% *}
+ case $typ in
+ (PDF) mv main main.pdf;;
+ (PostScript) ps2pdf main main.pdf; rm main;;
+ (*) die "Input is neither PDF nor PostScript; file says: $typ"
+ esac
+}
+
+<<'DOC' #------ function pdfproperties --------------------------------------------
+= pdfproperties
+parameters: -
+description: Find page width, page height and the number of pages in the input file
+globals set: height pagecount width
+globals used: height pagecount width
+returns: 0
+DOC
+#-------------------------------------------------------------------------------
+pdfproperties() {
+ while true; do
+ [[ $x =~ ^PDF ]] && break
+ [[ $x =~ ^Pages:\ *([0-9]*) ]] && pagecount="${BASH_REMATCH[1]}"
+ [[ $x =~ ^Page\ size:\ *([0-9]*)(\.[0-9]*)?\ *x\ *([0-9]*)(\.[0-9]*)? ]] && {
+ width=$(printf "%0.0f" "${BASH_REMATCH[1]}${BASH_REMATCH[2]}")
+ height=$(printf "%0.0f" "${BASH_REMATCH[3]}${BASH_REMATCH[4]}")
+ }
+ read -r x
+ done < <(pdfinfo main.pdf)
+ warn "$pagecount pages, papersize $width x $height"
+}
+
+<<'DOC' #------ function ask --------------------------------------------
+= ask
+parameters: -
+description: Prompt for a command, return the command in com
+globals set: com
+globals used: com prompt
+returns: 0
+DOC
+#-------------------------------------------------------------------------------
+ask() {
+ read -r -e -p "$prompt" com </dev/tty
+ history -s -- "$com"
+ com=(${com//,/ })
+}
+
+<<'DOC' #------ function printhelp --------------------------------------------
+= printhelp
+parameters: -
+description: Print help for vpp-commands and show which viewer and printer
+ are active.
+globals set: -
+globals used: Blu Nor mk
+returns: 0
+DOC
+#-------------------------------------------------------------------------------
+printhelp() {
+ echo -ne "
+ ${Blu}Command Examples:$Nor
+ 5 to print page 5
+ 5- to print pages 5 through the end
+ 5-7 to print pages 5, 6 and 7
+ 7-5 ox write pages 7, 6 and 5, in that order, to x.pdf
+ -7 to print the first 7 pages
+ 5-7,19- to print pages 5, 6, 7 and 19 through the end
+ a to print the whole document
+ - to print the whole document
+ a x3 to print 3 copies of the document
+ x3 the same
+ 5 x3 to print 3 copies of page 5
+ t print the whole document twosided
+ t 2- print twosided starting at page 2
+ b to print the whole document as an a5 size booklet
+ b -12 to print the first 12 pages as an a5 size booklet
+ ${Blu}Other commands:$Nor$mk
+ e (if called by mk) edit the tex source and rerun mk
+ c (if called by mk) rerun mk
+ v (re)view the ps/pdf file
+ w list errors and warnings from the log file
+ oxyz send pdf output to file xyz.pdf instead of printer
+ pxyz print to printer xyz
+ h display this help
+ ? display this help
+ q quit
+ $(list_printers)
+ $(list_viewers)\n" |sed 's/ //'
+ [[ -n $batch ]] && exit
+}
+
+# ask user for pages to printed or exported as pdf
+<<'DOC' #------ function ask_selection --------------------------------------------
+= ask_selection
+parameters: zero to many user commands
+description: Interact with user, specifying pages to be printed or exported as pdf,
+ or to re-view the pdf or (if called from mk) re-edit the tex-source.
+ If called with arguments (caused by vpp’s |--batch| option) executes those.
+globals set: booklet com lpropt output printer saved selection twosided viewer
+globals used: Mag Myname Nor Red VPPCHECKSAVED batch booklet com compileexit editexit
+ log output pagecount printers saved selection twosided viewer viewers
+returns: 0
+DOC
+#-------------------------------------------------------------------------------
+ask_selection() {
+ trap 'echo; Warn "${Mag}exit with q command"; selection=continue; return' 2
+ com=($@)
+ [[ ${#com[@]} -eq 0 ]] && ask
+ if [[ ${#com} -eq 0 ]]; then
+ selection=continue
+ return
+ fi
+ output='' booklet=false twosided=false selection='' lpropt=''
+ while [[ ${#com[@]} -gt 0 ]]; do
+ local c=${com[0]}
+ com=("${com[@]:1}")
+ case $c in
+ (q) # drop on q
+ # useful if vpp is used to print or copy data from a scanner:
+ test -n "$VPPCHECKSAVED" && ! $saved && {
+ echo -e "${Red}You requested saving but did not use the o command"\
+ "\nAnother q will destroy your copy$Nor"
+ saved=true
+ selection=continue
+ return
+ }
+ exit 0
+ ;;
+ (e) # edit request for caller
+ exit "$editexit"
+ ;;
+ (c) # re-compile request for caller
+ exit "$compileexit"
+ ;;
+ (v) # (re)view the ps/pdf data
+ ${viewers[$viewer]} main.pdf 2>/dev/null &
+ selection=continue
+ return
+ ;;
+ (x+([1-9])*([0-9])) # x3 -> -#3
+ lpropt=${c/x/-#}
+ ;;
+ (o*) # output to file instead of printer
+ output=${c#o}
+ if [[ -z $output ]]; then
+ Warn "filename must follow o without spacing"
+ selection=continue
+ return
+ fi
+ [[ $output =~ ^/ ]] || output="$writeto/$output"
+ if [[ ! -w ${output%/*} ]]; then
+ Warn "Cannot write to $output"
+ selection=continue
+ return
+ fi
+ saved=true
+ ;;
+ (p*) # set printer
+ i=${c#p}
+ if [[ -z $i ]]; then
+ Warn "p must be followed by a printer name, without spacing"
+ elif [[ -z ${printers[$i]} ]]; then
+ Warn "printer $i does not exist"
+ else
+ printer=$i
+ fi
+ if [[ ${#com[@]} -eq 0 ]]; then
+ selection=continue
+ return
+ fi
+ ;;
+ (v*) # with argument, set viewer; without: run it
+ local i=${c#v}
+ if [[ -z ${viewers[$i]} ]]; then
+ Warn "viewer $i not found in ~/.${Myname}rc"
+ else
+ viewer=$i
+ fi
+ selection=continue
+ return
+ ;;
+ (b) booklet=true;; # print a5 booklet
+ (t) twosided=true;; # print twosided
+ (a) selection+=1-$pagecount,;; # print all
+ (-) selection+=1-$pagecount,;; # print all
+ (+([[:digit:]])) selection+=$c,;;
+ (+([[:digit:]])-) selection+=$c$pagecount,;;
+ (-+([[:digit:]])) selection+=1$c,;;
+ (+([[:digit:]])-+([[:digit:]]))
+ selection+=$c,
+ ;;
+ (\?|h) printhelp
+ selection=continue
+ return
+ ;;
+ (w) # show tex warning and error messages in log file
+ if [[ -s "$log" ]]; then
+ texlog_extract "$log"
+ else
+ Warn "No log file available"
+ fi
+ selection=continue
+ return
+ ;;
+ (*) local e="Unrecognized command «$c»$Nor"
+ if [[ -n $batch ]]; then
+ die "$e"
+ else
+ Warn "$e - try again"
+ selection=continue
+ return
+ fi
+ esac
+ done
+ selection=${selection%,}
+ : ${selection:=1-$pagecount}
+
+ # pages in range?
+ if [[ ! $selection =~ ^[-1-9] ]]; then
+ Warn "page selection must start with a minus sign or a digit between 1 and 9"
+ selection=continue
+ else
+ while read -r i; do
+ ((i>=1 && i<=pagecount)) && continue
+ Warn "Illegal page number$Nor $i: PDF has $pagecount pages"
+ selection=continue
+ done < <(sed 's/[,-]\+/\n/g' <<<"$selection")
+ fi
+}
+
+<<'DOC' #------ function wait_for_printer --------------------------------------------
+= wait_for_printer
+parameters: -
+description: Wait for user typing |enter|, signalling that the printer is ready
+ for next job. |^D| instead skips further output.
+globals set: -
+globals used: -
+returns: 0
+DOC
+#-------------------------------------------------------------------------------
+wait_for_printer() {
+ read -rp "printer ready? then turn pack over the long side and type enter (^D skips)" </dev/tty ||
+ exit 0
+}
+
+<<'DOC' #------ function printout --------------------------------------------
+= printout
+parameters: -
+description: Print selected pages or output them to pdf.
+ Calls doselection for the actual output.
+globals set: selection
+globals used: batch booklet output printer printers selection twosided writeto
+returns: 0
+DOC
+#-------------------------------------------------------------------------------
+printout() {
+ while true; do
+ ask_selection "$batch"
+ [[ $selection == continue ]] && continue
+ [[ -z $selection ]] && selection=-
+
+ if ${printers[$printer]}; then
+ doselection
+ else
+ if $booklet; then
+ doselection odd
+ [[ -n $output ]] || wait_for_printer
+ doselection even
+ elif $twosided; then
+ doselection odd
+ [[ -n $output ]] || wait_for_printer
+ doselection even
+ else
+ doselection
+ fi
+ fi
+ [[ -n $batch ]] && break
+ done
+}
+
+<<'DOC' #------ function doselection --------------------------------------------
+= doselection
+parameters: 1: (empty) if all pages of the selection are to be printed,
+ "odd" if only the odd pages,
+ "even" if only the even pages to be printed
+description: Make a selection of pdf pages and print it or output it to pdf file.
+globals set: LATEX selection
+globals used: Mag Nor Red booklet height lpr lpropt output printer
+ selection verbose width
+returns: 0
+DOC
+#-------------------------------------------------------------------------------
+doselection() {
+ local includeopt pagesel evenodd=$1 outpdf i
+ $booklet && includeopt=', booklet, landscape'
+ [[ -n $evenodd ]] && pagesel="\\usepackage[$evenodd]{pagesel}"
+ outpdf=vpp.pdf
+ # if the whole document is selected, without booklet or
+ # twosided printing, we can simply print the original pdf or,
+ # if a copy is requested with the o command, copy it to the
+ # new pdf, saving link information which is lost otherwise:
+ if [[ $includeopt$pagesel$selection != - ]]; then
+ # a selection of pages needed or pages need to be arranged to a booklet
+ # a twosided print:
+ echo '%!pdflatex
+ \documentclass{article}
+ \usepackage[papersize={'"$width"'bp,'"$height"'bp}]{geometry}
+ '"$pagesel"' % require *before* pdfpages
+ \usepackage{pdfpages}[2004/03/27]
+ \begin{document}
+ \includepdf[pages={'$selection'}'"$includeopt"']{main.pdf}
+ \end{document}
+ ' > vpp.tex
+ export LATEX='pdflatex -interaction=batchmode'
+ $verbose && i='' || i=-q
+ texi2dvi -p $i vpp.tex || die "${Red}Error running texi2dvi"
+ else
+ # all pages needed without rearrangement:
+ outpdf=main.pdf
+ fi
+ if [[ -n $output ]]; then
+ # An output pdf was specified with the o command:
+ if [[ -e $output.pdf ]]; then
+ # specified file exists; overwrite it?
+ echo -ne "${Mag}File $output.pdf exists$Nor - overwrite? (yN) "
+ read -r i </dev/tty
+ if [[ ! $i =~ y ]]; then
+ selection=continue
+ return
+ fi
+ fi
+ # eventually, append _even or _odd to the pdf’s filename:
+ [[ -n $evenodd ]] && output+=_$evenodd
+ warn "copying $outpdf to $output"
+ mv "$outpdf" "$output.pdf"
+ else
+ if [[ -n $printer ]]; then i=-P; else i=''; fi
+ local command="$lpr $i$printer $lpropt $outpdf"
+ warn "running: $command"
+ $command
+ fi
+}
+
+<<'DOC' #------ function find_printers --------------------------------------------
+= find_printers
+parameters: -
+description: Use |lpstat| to find any printers defined;
+ use |lpoptions| to detect if each printer is doublesided;
+ create associative array |printers| with printer names as keys
+ and set the values to |true| for doublesided printers and to false
+ for singlesided ones.
+ Issue a warning if no printers are found.
+globals set: printer printers
+globals used: printer printers
+returns: -
+DOC
+#-----------------------------------------------------------------------------------
+find_printers() {
+ # find current default printer:
+ printer=$(lpstat -d)
+ if [[ $printer =~ :\ (.*) ]]; then
+ printer=${BASH_REMATCH[1]}
+ else
+ printer=
+ fi
+ # find printers and their sidedness (makes each printer default in turn)
+ for i in $(lpstat -a 2>/dev/null |cut -d' ' -f1); do
+ printers["$i"]=$(lpoptions -d "$i" |
+ sed 's/.*sides=\([a-z]*\).*/\1/;s/one/false/;s/two/true/')
+ done
+ [[ -n "$printer" ]] && lpoptions -d $printer >/dev/null # restore defaultprinter
+ [[ ${#printers[@]} -eq 0 ]] && Warn "no printers found"
+}
+
+<<'DOC' #------ function list_printers --------------------------------------------
+= list_printers
+parameters: -
+description: List available printers and their sidedness; mark current printer.
+globals set: printers printer
+globals used: Blu Nor Red printers printer
+returns: -
+DOC
+#-----------------------------------------------------------------------------------
+list_printers() {
+ local i j k c
+ echo "${Blu}Available printers:$Nor"
+ for i in "${!printers[@]}"; do
+ j=doublesided
+ c=
+ k=
+ [[ $printer == "$i" ]] && k=' (current printer)' && c=$Red
+ ${printers[$i]} || j=singlesided
+ printf " %s%s\t→ %s%s%s\n" "$c" "$i" "$j" "$k" "$Nor"
+ done
+}
+
+<<'DOC' #------ function list_viewers --------------------------------------------
+= list_viewers
+parameters: -
+description: List available viewers; mark current viewer
+globals set: viewer viewers
+globals used: Blu Nor Red viewer viewers
+returns: -
+DOC
+#-----------------------------------------------------------------------------------
+list_viewers() {
+ local i j k c
+ echo "${Blu}Available viewers:$Nor"
+ for i in "${!viewers[@]}"; do
+ k=
+ c=
+ [[ $viewer == "$i" ]] && k=' (current viewer)' && c=$Red
+ printf " %s%s\t→ %s%s%s\n" "$c" "$i" "${viewers[$i]}" "$k" "$Nor"
+ done
+}
+
+<<'DOC' #------ function clean_viewers --------------------------------------------
+= clean_viewers
+parameters: -
+description: Remove unavailable viewers, no messages
+globals set: viewer viewers
+globals used: -
+returns: -
+DOC
+#-----------------------------------------------------------------------------------
+clean_viewers() {
+ local i
+ for i in "${!viewers[@]}"; do
+ which "${viewers[$i]}" >/dev/null || {
+ unset viewers["$i"]
+ [[ $viewer == "$i" ]] && unset viewer
+ }
+ done
+}
+
+declare -A viewers printers
+ viewers=([ev]=evince [xp]=xpdf [gv]=gv [ac]=acroread)
+ viewer=ev
+ neededex=( file getopt kpsewhich less lpoptions lpr lpstat mktemp pdfinfo
+ pdflatex ps2pdf readlink texi2dvi texlog_extract )
+ neededtx=()
+ saved=false
+ view=true
+ verbose=false
+ print=true
+ batch=
+ editexit=99
+compileexit=98
+ prompt='vpp command (? for help): '
+ writeto=${VPPOUTDIR:=$(pwd)}
+ lpr=lpr
+ printer=
+
+check_needs
+clean_viewers
+find_printers
+handle_options "$@"
+check_viewers
+find_pdf
+pdfproperties
+
+[[ -n $batch ]] || $view || $print || die "Nothing to do: use --view or --print or both"
+if $view; then
+ $verbose && echo "running: ${viewers[$viewer]} main.pdf"
+ ${viewers[$viewer]} main.pdf 2>/dev/null &
+fi
+if $print; then
+ printout
+else
+ wait
+fi