#!/bin/sh # +AMDG This document was begun just before Christmas, # 2010, and it is humbly dedicated to St. Wulfric, patron of # bookbinders, and to the Immaculate Heart of Mary for their # prayers, and to the Sacred Heart of Jesus for His mercy. # #**********************************************************# # makebook # # written by Donald P. Goodman III # # Copyright (C) 2011 # # # # Impose pdf pages for binding # #**********************************************************# # # 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. # # For a full copy of the GNU General Public License, see # . # #**********************************************************# # begin the code # #**********************************************************# # make script more portable and secure PATH=/bin:/usr/bin:/usr/local/bin ; export PATH umask 033 # define our revision number variable for rcs REVISION="2.1" # define error codes E_WRONG_ARGS=64 # too many or few args E_BAD_SIG_TYPE=65 # invalid type of signature E_BAD_UNIT=66 # unrecognized dimension unit used E_BAD_FILENAME=67 # filename contains insecure chars, # or doesn't exist E_BAD_OPT=68 # bad command-line flag requested E_NO_PROG=69 # missing a required program # define usage variables VERBOSE=0 # 0 if not verbose, 1 if -v SIG_TYPE="folio" # type of section desired PAGES_PER_SIG=4 # number of pages per signature SECT_TYPE=1 # number of signatures per section NUP="2x1" # default format of signatures FRONT_FAVOR=0 # favor front for blanks; off by default # define various variables to be zero by default TGT_PAGE_WIDTH=0 TGT_PAGE_HEIGHT=0 HORIZ_DELTA=0 VERT_DELTA=0 HORIZ_OFFSET=0 VERT_OFFSET=0 SCALE=1 # make sure user has the right programs installed, and die # horribly if not command -v od >/dev/null 2>&1 || { echo >&2 "makebook: error: requires od, but it's not installed"; exit $E_NO_PROG; } command -v pdflatex >/dev/null 2>&1 || { echo >&2 "makebook: error: requires pdflatex, but it's not installed"; exit $E_NO_PROG; } command -v pdfinfo >/dev/null 2>&1 || { echo >&2 "makebook: error: requires pdfinfo, but it's not installed"; exit $E_NO_PROG; } command -v pdftk >/dev/null 2>&1 || { echo >&2 "makebook: error: requires pdftk, but it's not installed"; exit $E_NO_PROG; } command -v dc >/dev/null 2>&1 || { echo >&2 "makebook: error: requires dc, but it's not installed"; exit $E_NO_PROG; } command -v bc >/dev/null 2>&1 || { echo >&2 "makebook: error: requires bc, but it's not installed"; exit $E_NO_PROG; } # print the version information and exit successfully versionfunc () { echo "makebook v${REVISION}. Copyright (C) 2011, Donald P." echo "Goodman III." echo "This program comes with ABSOLUTELY NO WARRANTY." echo "This is free software, and you are welcome to " echo "redistribute it under certain conditions; see " echo "the GNU GPL v3 for details." exit 0 } # print the online help and exit successfully helpfunc () { cat <&2 exit $E_BAD_UNIT fi if [ `expr match "$1" '.*\(in\)'` ] then OPTARG=`echo "scale=0; ${1%in} * 72" | bc` OPTARG=${OPTARG%.*} elif [ `expr match "$1" '.*\(cm\)'` ] then OPTARG=`echo "scale=0; ${1%cm} * 28.3464567" | bc` OPTARG=${OPTARG%.*} elif [ `expr match "$1" '.*\(mm\)'` ] then OPTARG=`echo "scale=0; ${1%mm} * 2.83464567" | bc` OPTARG=${OPTARG%.*} elif [ `expr match "$1" '.*\(pt\)'` ] then OPTARG=`echo "scale=0; ${1%pt} / 1.00375" | bc` OPTARG=${OPTARG%.*} elif [ `expr match "$1" '.*\(pc\)'` ] then OPTARG=`echo "scale=0; (${1%pc} / 1.00375) * 12" | bc` OPTARG=${OPTARG%.*} elif [ `expr match "$1" '.*\(bp\)'` ] then OPTARG=${1%bp} else OPTARG=${1%.*} fi } # define a for dealing with section types; convert () # to words; e.g., "4to" to "quarto" typearg () { if [ "$1" = "4to" ] || [ "$1" = "quarto" ] then PAGES_PER_SIG=8 NUP="2x2" OPTARG="quarto" elif [ "$1" = "2o" ] || [ "$1" = "folio" ] then PAGES_PER_SIG=4 NUP="2x1" OPTARG="folio" elif [ "$1" = "8vo" ] || [ "$1" = "octavo" ] then PAGES_PER_SIG=16 NUP="4x2" OPTARG="octavo" elif [ "$1" = "6to" ] || [ "$1" = "sexto" ] then PAGES_PER_SIG=12 NUP="2x3" OPTARG="sexto" elif [ "$1" = "12mo" ] || [ "$1" = "duodecimo" ] then PAGES_PER_SIG=24 NUP="4x3" OPTARG="duodecimo" else echo "ERROR: signature type \"$1\" not recognized." >&2 exit $E_BAD_SIG_TYPE fi } # clean up filename argument filefunc () { err_message="ERROR: bad characters in file name." if [ `echo "$1" | grep '\?'` ] then echo "$err_message" >&2 exit $E_BAD_FILENAME elif [ `echo "$1" | grep '\*'` ] then echo "$err_message" >&2 exit $E_BAD_FILENAME elif [ `echo "$1" | grep '"'` ] then echo "$err_message" >&2 exit $E_BAD_FILENAME elif [ `echo "$1" | grep ';'` ] then echo "$err_message" >&2 exit $E_BAD_FILENAME elif [ `echo "$1" | grep '[\\]'` ] then echo "$err_message" >&2 exit $E_BAD_FILENAME elif [ `echo "$1" | grep ' '` ] then echo "$err_message" >&2 exit $E_BAD_FILENAME fi OPTARG=`basename $1` } # now identify and parse the options while getopts "Vhvfn:t:H:w:d:D:m:M:s:i:o:" Option do case $Option in V ) versionfunc;; h ) helpfunc;; v ) VERBOSE=1;; f ) FRONT_FAVOR=1;; n ) SECT_TYPE=$OPTARG;; t ) typearg "$OPTARG"; SIG_TYPE=$OPTARG;; H ) unitarg "$OPTARG"; TGT_PAGE_HEIGHT=$OPTARG;; w ) unitarg "$OPTARG"; TGT_PAGE_WIDTH=$OPTARG;; d ) unitarg "$OPTARG"; HORIZ_DELTA=$OPTARG;; D ) unitarg "$OPTARG"; VERT_DELTA=$OPTARG;; m ) unitarg "$OPTARG"; HORIZ_OFFSET=$OPTARG;; M ) unitarg "$OPTARG"; VERT_OFFSET=$OPTARG;; s ) SCALE=$OPTARG;; i ) filefunc "$OPTARG"; FILE_NAME=$OPTARG;; o ) filefunc "$OPTARG"; OUTFILE_NAME=$OPTARG;; * ) echo "ERROR: unknown flag \ \"$Option\"." >&2; exit $E_BAD_OPT;; esac done # make random file name to use for input NEW_FILE_NAME="tmp_`od -An -N4 -t uL /dev/urandom | tr -d '\ '`.pdf" # if input file specified, use file; if not, read stdin if [ "$FILE_NAME" ] then cp "$FILE_NAME" "$NEW_FILE_NAME" else FILE_NAME="book.pdf" cat /dev/stdin > "$NEW_FILE_NAME" fi # declare holder variable for pdftk tmp_name="tmp_`od -An -N4 -t uL /dev/urandom | tr -d '\ '`.pdf" # if output file specified, use that; if not, name it; if # "-", use stdout [ "$OUTFILE_NAME" ] || OUTFILE_NAME="sigs_$FILE_NAME" if [ "$OUTFILE_NAME" = "stdout" ] then VERBOSE=0 fi # print our introduction message if [ $VERBOSE -eq 1 ] then echo "makebook. Copyright (C) 2011, Donald P. Goodman III." echo "This program comes with ABSOLUTELY NO WARRANTY." echo "This is free software, and you are welcome to " echo "redistribute it under certain conditions; see " echo "the GNU GPL v3 for details." echo "IMPOSING pdf pages onto \"$OUTFILE_NAME\"..." fi # get some information about our source document NUM_PAGES=`pdfinfo "$NEW_FILE_NAME" | awk '/Pages:/ {print $2}'`; SRC_PAGE_WIDTH=`pdfinfo "$NEW_FILE_NAME" | awk '/Page\ size:/ {print $3}'`; SRC_PAGE_HEIGHT=`pdfinfo "$NEW_FILE_NAME" | awk '/Page\ size:/ {print $5}'`; # find the number of pages we'll have per signature PAGES_PER_SIG=$(dc -e "$PAGES_PER_SIG $SECT_TYPE * p") # determine if extra pages will be necessary NUM_BLANKS=`expr $NUM_PAGES % $PAGES_PER_SIG` if [ $NUM_BLANKS -ne 0 ] then NUM_BLANKS=`expr $PAGES_PER_SIG - $NUM_BLANKS` fi tmp=`expr $NUM_PAGES + $NUM_BLANKS` NUM_SIGS=`expr $tmp / $PAGES_PER_SIG` # if extra pages are needed, generate blank page if [ $NUM_BLANKS -gt 0 ] && [ ! -e "./blank.pdf" ] then echo "\documentclass{article}" > blank.tex echo '\\thispagestyle{empty}' >> blank.tex echo "\usepackage[paperwidth=${SRC_PAGE_WIDTH}bp,paperheight="${SRC_PAGE_HEIGHT}bp"]{geometry}" >> blank.tex echo '\\begin{document}' >> blank.tex echo "\quad \\\\end{document}" >> blank.tex if [ $VERBOSE -eq 1 ] then echo "CREATING blank page to fill sections..."; fi pdflatex blank.tex > /dev/null 2&>1 fi # now insert blank pages as needed, preferring the back for # odd numbers by default, front if stated if [ $NUM_BLANKS -eq 1 ] then if [ $VERBOSE -eq 1 ] then echo "INSERTING one blank page..." fi if [ $FRONT_FAVOR -eq 0 ] then pdftk A="$NEW_FILE_NAME" B=blank.pdf cat A1-end \ B1 output "$tmp_name" else pdftk A="$NEW_FILE_NAME" B=blank.pdf cat B1 \ A1-end output "$tmp_name" fi mv "$tmp_name" "$NEW_FILE_NAME" else # tmp=`expr $NUM_BLANKS / 2` # START_BLANKS=${tmp/.*} START_BLANKS=`expr $NUM_BLANKS / 2` END_BLANKS=`expr $NUM_BLANKS - $START_BLANKS` if [ `expr $START_BLANKS % 2` -ne 0 ]; then : else START_BLANKS=`expr $START_BLANKS - 1` END_BLANKS=`expr $END_BLANKS + 1 ` fi if [ $FRONT_FAVOR -eq 1 ] then tmp=$START_BLANKS START_BLANKS=$END_BLANKS END_BLANKS=$tmp if [ `expr $START_BLANKS % 2` -ne 0 ]; then : else START_BLANKS=`expr $START_BLANKS - 1` END_BLANKS=`expr $END_BLANKS + 1 ` fi fi if [ $VERBOSE -eq 1 ] then echo "INSERTING $START_BLANKS blank pages at start..." echo "INSERTING $END_BLANKS blank pages at end..."; fi i=0 while [ $i -lt $START_BLANKS ] do pdftk A="$NEW_FILE_NAME" B=blank.pdf cat B1 A1-end output "$tmp_name" mv "$tmp_name" "$NEW_FILE_NAME" i=`expr $i + 1` done i=0 while [ $i -lt $END_BLANKS ] do pdftk A="$NEW_FILE_NAME" B=blank.pdf cat A1-end B1 output "$tmp_name" mv "$tmp_name" "$NEW_FILE_NAME" i=`expr $i + 1` done fi # begin the imposing, announcing if appropriate tmp=`expr $NUM_PAGES + $NUM_BLANKS` if [ $VERBOSE -eq 1 ] then echo "IMPOSING $NUM_PAGES pages ($tmp with blanks) on $(dc -e "$NUM_SIGS $SECT_TYPE * p") signature(s) gathered in sections of $SECT_TYPE signature(s) each..."; fi # arrange the pages for impression on signatures i=1 j=1 if [ $VERBOSE -eq 1 ] then echo "REARRANGING pages..."; fi while [ $j -le $NUM_SIGS ] do if [ "$SIG_TYPE" = "quarto" ] then # determine size of target page if [ $TGT_PAGE_HEIGHT -eq 0 ] then TGT_PAGE_HEIGHT=$(dc -e "$SRC_PAGE_HEIGHT 2 * p") fi if [ $TGT_PAGE_WIDTH -eq 0 ] then TGT_PAGE_WIDTH=$(dc -e "$SRC_PAGE_WIDTH 2 * p") fi # do the rearranging k=$i; n=1; total=$(dc -e "$SECT_TYPE 8 * p"); m=$total while [ $n -le $SECT_TYPE ] do pdftk "$NEW_FILE_NAME" cat `expr $k + $m - 1` $k \ `expr $k + $m - 4`south `expr $k + 3`south \ `expr $k + 1` `expr $k + $m - 2` `expr $k + 2`south \ `expr $k + $m - 3`south output ${tmp_name%.pdf}_$n; k=`expr $k + 4`; n=`expr $n + 1`; m=`expr $m - 8` done n=1 while [ `expr $n + 0` -le $SECT_TYPE ] do if [ ! -e "$tmp_name" ] then pdftk A=${tmp_name%.pdf}_$n cat A1-end \ output "$tmp_name" else pdftk A=$tmp_name B=${tmp_name%.pdf}_`expr $n` \ cat A1-end B1-end output ${tmp_name}_tmp mv ${tmp_name}_tmp "$tmp_name" fi n=`expr $n + 1` done if [ $VERBOSE -eq 1 ] then echo "REARRANGING section number $j..." fi j=`expr $j + 1`; i=`expr $i + $total`; elif [ "$SIG_TYPE" = "folio" ] then # determine size of target page if [ 1 -eq "$(echo "${TGT_PAGE_HEIGHT} == 0" | bc)" ] then TGT_PAGE_HEIGHT=$(dc -e "$SRC_PAGE_HEIGHT 1 * p") fi if [ 1 -eq "$(echo "${TGT_PAGE_WIDTH} == 0" | bc)" ] then TGT_PAGE_WIDTH=$(dc -e "$SRC_PAGE_WIDTH 2 * p") fi # do the rearranging k=$i; n=1; total=$(dc -e "$SECT_TYPE 4 * p"); m=$total while [ $n -le $SECT_TYPE ] do pdftk "$NEW_FILE_NAME" cat `expr $k + $m - 1` \ `expr $k` `expr $k + 1` `expr $k + $m - 2` \ output ${tmp_name%.pdf}_$n; k=`expr $k + 2`; n=`expr $n + 1`; m=`expr $m - 4` done n=1 while [ `expr $n + 0` -le $SECT_TYPE ] do if [ ! -e "$tmp_name" ] then pdftk A=${tmp_name%.pdf}_$n cat A1-end \ output "$tmp_name" else pdftk A="$tmp_name" B=${tmp_name%.pdf}_`expr $n` \ cat A1-end B1-end output ${tmp_name}_tmp mv ${tmp_name}_tmp "$tmp_name" fi n=`expr $n + 1` done if [ $VERBOSE -eq 1 ] then echo "REARRANGING section number $j..." fi j=`expr $j + 1`; i=`expr $i + $total`; elif [ "$SIG_TYPE" = "octavo" ] then # determine target page dimensions if [ 1 -eq "$(echo "${TGT_PAGE_HEIGHT} == 0" | bc)" ] then TGT_PAGE_HEIGHT=$(dc -e "$SRC_PAGE_HEIGHT 2 * p") fi if [ 1 -eq "$(echo "${TGT_PAGE_WIDTH} == 0" | bc)" ] then TGT_PAGE_WIDTH=$(dc -e "$SRC_PAGE_WIDTH 4 * p") fi # do the rearranging k=$i; n=1; total=$(dc -e "$SECT_TYPE 16 * p"); m=$total while [ $n -le $SECT_TYPE ] do pdftk "$NEW_FILE_NAME" cat `expr $k + 3` \ `expr $k + $m - 4` `expr $k + $m - 1` $k \ `expr $k + 4`south `expr $k + $m - 5`south \ `expr $k + $m - 8`south `expr $k + 7`south \ `expr $k + 1` `expr $k + $m - 2` `expr $k + $m - 3` \ `expr $k + 2` `expr $k + 6`south `expr $k + $m - 7`south \ `expr $k + $m - 6`south `expr $k + 5`south \ output ${tmp_name%.pdf}_$n; k=`expr $k + 8`; n=`expr $n + 1`; m=`expr $m - 16` done n=1 while [ `expr $n + 0` -le $SECT_TYPE ] do if [ ! -e "$tmp_name" ] then pdftk A=${tmp_name%.pdf}_$n cat A1-end \ output "$tmp_name" else pdftk A="$tmp_name" B=${tmp_name%.pdf}_`expr $n` \ cat A1-end B1-end output ${tmp_name}_tmp mv ${tmp_name}_tmp "$tmp_name" fi n=`expr $n + 1` done if [ $VERBOSE -eq 1 ] then echo "REARRANGING section number $j..." fi j=`expr $j + 1`; i=`expr $i + $total`; elif [ "$SIG_TYPE" = "sexto" ] then # determine target page dimensions if [ $TGT_PAGE_HEIGHT -eq 0 ] then TGT_PAGE_HEIGHT=$(dc -e "$SRC_PAGE_HEIGHT 3 * p") fi if [ $TGT_PAGE_WIDTH -eq 0 ] then TGT_PAGE_WIDTH=$(dc -e "$SRC_PAGE_WIDTH 2 * p") fi # do the rearranging k=$i; n=1; total=$(dc -e "$SECT_TYPE 12 * p"); m=$total while [ $n -le $SECT_TYPE ] do pdftk "$NEW_FILE_NAME" cat `expr $k + 5` \ `expr $k + 6` `expr $k + $m - 1` $k \ `expr $k + $m - 1 - 3`south `expr $k + 3`south \ `expr $k + 7` `expr $k + 4` \ `expr $k + 1` `expr $k + $m - 1 - 1` \ `expr $k + 2`south `expr $k + $m - 1 - 2`south \ output ${tmp_name%.pdf}_$n; k=`expr $k + 6`; n=`expr $n + 1`; m=`expr $m - 12` done n=1 while [ `expr $n + 0` -le $SECT_TYPE ] do if [ ! -e "$tmp_name" ] then pdftk A=${tmp_name%.pdf}_$n cat A1-end \ output "$tmp_name" else pdftk A=$tmp_name B=${tmp_name%.pdf}_`expr $n` \ cat A1-end B1-end output ${tmp_name}_tmp mv ${tmp_name}_tmp "$tmp_name" fi n=`expr $n + 1` done if [ $VERBOSE -eq 1 ] then echo "REARRANGING section number $j..." fi j=`expr $j + 1`; i=`expr $i + $total`; elif [ "$SIG_TYPE" = "duodecimo" ] then # determine target page dimensions if [ $TGT_PAGE_HEIGHT -eq 0 ] then TGT_PAGE_HEIGHT=$(dc -e "$SRC_PAGE_HEIGHT 3 * p") fi if [ $TGT_PAGE_WIDTH -eq 0 ] then TGT_PAGE_WIDTH=$(dc -e "$SRC_PAGE_WIDTH 4 * p") fi # do the rearranging k=$i; n=1; total=$(dc -e "$SECT_TYPE 24 * p"); m=$total while [ $n -le $SECT_TYPE ] do pdftk "$NEW_FILE_NAME" cat `expr $k + 8` \ `expr $k + $m - 1 - 8` `expr $k + 12` \ `expr $k + 11` `expr $k + 4` \ `expr $k + $m - 1 - 3` `expr $k + $m - 1` \ $k `expr $k + 4`south `expr $k + $m - 1 - 4`south \ `expr $k + $m - 1 - 7`south `expr $k + 7`south \ `expr $k + 10` `expr $k + 13` `expr $k + 14` \ `expr $k + 9` `expr $k + 1` `expr $k + $m - 1 - 1` \ `expr $k + $m - 1 - 2` `expr $k + 2` \ `expr $k + 6`south `expr $k + $m - 1 - 6`south \ `expr $k + $m - 1 - 5`south `expr $k + 5`south \ output ${tmp_name%.pdf}_$n; k=`expr $k + 12`; n=`expr $n + 1`; m=`expr $m - 24` done n=1 while [ `expr $n + 0` -le $SECT_TYPE ] do if [ ! -e "$tmp_name" ] then pdftk A=${tmp_name%.pdf}_$n cat A1-end \ output $tmp_name else pdftk A="$tmp_name" B=${tmp_name%.pdf}_`expr $n` \ cat A1-end B1-end output ${tmp_name}_tmp mv ${tmp_name}_tmp "$tmp_name" fi n=`expr $n + 1` done if [ $VERBOSE -eq 1 ] then echo "REARRANGING section number $j..." fi j=`expr $j + 1`; i=`expr $i + $total`; fi done mv "$tmp_name" "$NEW_FILE_NAME" rm ${tmp_name%.pdf}* echo "\documentclass{article} \usepackage{pdfpages} \usepackage[paperwidth=${TGT_PAGE_WIDTH}bp,paperheight=${TGT_PAGE_HEIGHT}bp]{geometry} \pagestyle{empty}" > ${OUTFILE_NAME%.pdf}.tex echo '\\begin{document}' >> ${OUTFILE_NAME%.pdf}.tex echo "\includepdf[nup=$NUP,pages=-,turn=false,columnstrict, noautoscale,delta=${HORIZ_DELTA}bp ${VERT_DELTA}bp, offset=${HORIZ_OFFSET}bp ${VERT_OFFSET}bp,scale=${SCALE}] {./"$NEW_FILE_NAME"}" >> ${OUTFILE_NAME%.pdf}.tex echo "\\\\end{document}" >> ${OUTFILE_NAME%.pdf}.tex if [ $VERBOSE -eq 1 ] then echo "IMPOSING sections..." fi pdflatex ${OUTFILE_NAME%.pdf}.tex > /dev/null 2>&1 if [ $VERBOSE -eq 1 ] then echo "CLEANING up..." fi rm ${OUTFILE_NAME%.pdf}.tex ${OUTFILE_NAME%.pdf}.aux \ ${OUTFILE_NAME%.pdf}.log "$NEW_FILE_NAME" if [ -e "./blank.tex" ]; then rm ./blank.tex fi if [ -e "./blank.aux" ]; then rm ./blank.aux fi if [ -e "./blank.log" ]; then rm ./blank.log fi if [ $VERBOSE -eq 1 ] then echo "`expr $NUM_PAGES + $NUM_BLANKS` pages imposed in $(dc -e "$NUM_SIGS $SECT_TYPE * p") $SIG_TYPE signatures gathered in sections of $SECT_TYPE signature(s) each and output to ${OUTFILE_NAME%.pdf}.pdf." fi if [ "$OUTFILE_NAME" = "stdout" ] then cat ${OUTFILE_NAME}.pdf rm ${OUTFILE_NAME}.pdf fi exit 0