How to control the page parameters (margins, brochurisation) of `man -t` PostScript output?

An awesome shell script written some time ago by a friend: livre.

You'll be interested in the --book and --inner options.


#!/bin/sh
#---------------
# A parametrized replacement for psnup -2 using pstops.
#
# The script computes bounding boxes using gs and deduces optimized reductions
# on two pages per sheet with given margins or scaling. Pages can be arranged
# for various kinds of folding or binding.
#---------------
# Requires: gs, bc, pstops, psbook, pdftops
#---------------

VERSION=2009-12-02

# Output parameters

PAPER=a4
WIDTH=597   # 210mm
HEIGHT=845  # 297mm
MARGIN=30
INNER=1
SCALE=
TWO=false
BIND=top
BBRANGE=-
SIG=
IN=
OUT=
DASHQ=-q
VERBOSE=false
BB_ODD=
BB_EVEN=

# Parse the command line

while [ $# -gt 0 ]; do
        case "$1" in
        --help)
                cat <<EOF
usage: livre.sh [options] input output
The input can be in PostScript or PDF, possibly gzipped.
options:
  --margin SIZE    compute scaling to get SIZE-point margins (default: 30)
  --scale FACTOR   use the given scaling FACTOR (overrides --margin)
  --inner FACTOR   set inner margin to FACTOR times the outer one (default: 1)
  --two            input is two-sided
  --top            output should be bound at the top (default)
  --left           output should be bound on the left
  --book           output should be folded into a booklet
  --sig N          select the signature for psbook (implies --book)
  --bbrange PAGES  only use the specified PAGES for bounding box computation
  --bbox BBOX      provide the bounding box explictly, disable its computation
                     (use this twice if the input is two-sided)
  --verbose        do not use quiet mode
  --version        print version number and exit
EOF
                exit 0
                ;;
        --version)
                echo $VERSION
                exit 0
                ;;
        --margin)
                shift
                MARGIN="$1"
                ;;
        --scale)
                shift
                SCALE="$1"
                ;;
        --inner)
                shift
                INNER="$1"
                ;;
        --two*)
                TWO=true
                ;;
        --top|--left|--book)
                BIND=${1#--}
                ;;
        --sig)
                shift
                SIG="-s$1"
                BIND=book
                ;;
        --bbrange)
                shift
                BBRANGE="$1"
                ;;
        --bbox)
                shift
                if [ -z "$BB_ODD" ]; then
                        BB_ODD="$1"
                else
                        TWO=true
                        BB_EVEN="$1"
                fi
                ;;
        --verbose)
                DASHQ=
                VERBOSE=true
                ;;
        -*)
                echo "unknown option: $1" >&2
                exit 1
                ;;
        *)
                if [ -z "$IN" ]
                then IN="$1"
                elif [ -z "$OUT" ]
                then OUT="$1"
                else echo "unused argument: $1" >&2
                fi
        esac
        shift
done

if [ -z "$IN" ]; then
        echo "missing input file" >&2
        exit 1
fi

# Make a teporary file if needed

TMP=

make_ps ()
{
        case $(file -bL "$IN") in
        "PostScript document"*)
                ;;
        "PDF document"*)
                $VERBOSE && echo "converting to PS..." >&2
                TMP2=$(mktemp)
                pdftops "$IN" "$TMP2"
                test -n "$TMP" && rm "$TMP"
                TMP="$TMP2"
                IN="$TMP"
                ;;
        "gzip compressed "*)
                $VERBOSE && echo "unzipping..." >&2
                TMP2=$(mktemp)
                gunzip < "$IN" > "$TMP2"
                test -n "$TMP" && rm "$TMP"
                TMP="$TMP2"
                IN="$TMP"
                make_ps
                ;;
        *)
                echo "Unknown file type!" >&2
                exit 1
        esac
}

make_ps

# Extract the bounding box for all pages of a given file.

read_bbox()
{
        gs -sDEVICE=bbox -sPAPERSIZE=$PAPER -r300x300 -q -dBATCH -dNOPAUSE "$1" 2>&1 | awk '
function min(x,y) { if (x < y) return x; else return y }
function max(x,y) { if (x > y) return x; else return y }
BEGIN {
        left = top = 10000;
        right = bottom = 0
}
/%%BoundingBox:/ {
        left = min(left, $2);
        top = min(top, $3);
        right = max(right, $4);
        bottom = max(bottom, $5);
}
END { print left, top, right, bottom }
'
}

# Compute the maximum scaling factor given the bounding box

max_scale()
{
        bc <<EOF
scale = 3
hfactor = (($HEIGHT - (2 + $INNER) * $MARGIN) / 2) / ($3 - $1)
vfactor = ($WIDTH - 2 * $MARGIN) / ($4 - $2)
if (hfactor < vfactor) print hfactor else print vfactor
EOF
}

# Make a pstops specification

spec_left()
{
        bc <<EOF
scale = 3
factor = $1
shift_x = $WIDTH / 2 + ($3 + $5) * factor / 2
vmargin = ($HEIGHT - 2 * ($4 - $2) * factor) / (2 + $INNER)
shift_y = vmargin - $2 * factor + $6 * (($4 - $2) * factor + $INNER * vmargin)
print "L@", factor, "(", shift_x, ",", shift_y, ")"
EOF
}

spec_right()
{
        bc <<EOF
scale = 3
factor = $1
shift_x = $WIDTH / 2 - ($3 + $5) * factor / 2
vmargin = ($HEIGHT - 2 * ($4 - $2) * factor) / (2 + $INNER)
shift_y = vmargin + $4 * factor + $6 * (($4 - $2) * factor + $INNER * vmargin)
print "R@", factor, "(", shift_x, ",", shift_y, ")"
EOF
}

# Compute the bounding boxes for even and odd pages

if [ -z "$BB_ODD" ]; then
        $VERBOSE && echo "computing the bounding box..." >&2
        if $TWO; then
                BB_ODD=$(psselect -o -p"$BBRANGE" "$IN" 2>/dev/null | read_bbox -)
                BB_EVEN=$(psselect -e -p"$BBRANGE" "$IN" 2>/dev/null | read_bbox -)
        else
                if [ "$BBRANGE" = "-" ]; then
                        BB_ODD=$(read_bbox "$IN")
                else
                        BB_ODD=$(psselect -p"$BBRANGE" "$IN" 2>/dev/null | read_bbox -)
                fi
        fi
fi

test -z "$BB_EVEN" && BB_EVEN="$BB_ODD"

if $VERBOSE; then
        if $TWO; then
                echo "bounding box (odd): $BB_ODD"
                echo "bounding box (even): $BB_ODD"
        else
                echo "bounding box: $BB_ODD"
        fi
fi


# Deduce the scaling factor if needed

if [ -z "$SCALE" ]; then
        if $TWO; then
                SCALE=$(bc <<EOF
scale=3
s1=$(max_scale $BB_ODD)
s2=$(max_scale $BB_EVEN)
if (s1 < s2) print s1 else print s2
EOF
)
        else
                SCALE=$(max_scale $BB_ODD)
        fi
fi

$VERBOSE && echo "scale: $SCALE" >&2

# Process the file according to the chosen style

command()
{
        $VERBOSE && echo "-- $*"
        $*
}

case $BIND in
top)
        command pstops $DASHQ -p$PAPER "2:\
0$(spec_left $SCALE $BB_ODD 0)+1$(spec_left $SCALE $BB_EVEN 1)" "$IN" "$OUT"
        ;;
left)
        command pstops $DASHQ -p$PAPER "4:\
0$(spec_left $SCALE $BB_ODD 0)+1$(spec_left $SCALE $BB_EVEN 1),\
2$(spec_right $SCALE $BB_ODD 1)+3$(spec_right $SCALE $BB_EVEN 0)" "$IN" "$OUT"
        ;;
book)
        psbook $DASHQ $SIG $IN | command pstops $DASHQ -p$PAPER "4:\
0$(spec_left $SCALE $BB_EVEN 0)+1$(spec_left $SCALE $BB_ODD 1),\
2$(spec_right $SCALE $BB_EVEN 1)+3$(spec_right $SCALE $BB_ODD 0)" /dev/stdin "$OUT"
esac

test -n "$TMP" && rm "$TMP" || true