diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/wheretotrim |
Initial commit
Diffstat (limited to 'support/wheretotrim')
-rw-r--r-- | support/wheretotrim/README | 49 | ||||
-rwxr-xr-x | support/wheretotrim/wheretotrim | 499 | ||||
-rw-r--r-- | support/wheretotrim/wheretotrim-pl.dtx | 756 | ||||
-rw-r--r-- | support/wheretotrim/wheretotrim.1 | 324 | ||||
-rw-r--r-- | support/wheretotrim/wheretotrim.dtx | 725 | ||||
-rw-r--r-- | support/wheretotrim/wheretotrim.ins | 59 | ||||
-rw-r--r-- | support/wheretotrim/wheretotrim.pdf | bin | 0 -> 340989 bytes | |||
-rw-r--r-- | support/wheretotrim/wheretotrim.sty | 94 |
8 files changed, 2506 insertions, 0 deletions
diff --git a/support/wheretotrim/README b/support/wheretotrim/README new file mode 100644 index 0000000000..e8c360329a --- /dev/null +++ b/support/wheretotrim/README @@ -0,0 +1,49 @@ + +---------------------+ + | WHERETOTRIM | + | | + | Scott Pakin | + | scott+wtt@pakin.org | + +---------------------+ + +Description +=========== + +wheretotrim is a Perl script and auxiliary LaTeX package that helps +LaTeX users reduce their document's page count. It is intended to be +used with documents that exceed a publisher's specified page-length +limitation by a small amount (much less than a full column or page). +wheretotrim reports the page and column on which the least amount of +text needs to be trimmed to reduce the page count. It works by +repeatedly rebuilding the given document, analyzing the benefit of +trimming from each column or page one line of text, then two, then +three, and so forth until the document's page count decreases. + +wheretotrim has been tested only on Linux. It might work on OS X. It +probably won't work on Windows. + + +Installation +============ + +To create wheretotrim.pl and wheretotrim.sty simply run "tex +wheretotrim.ins". Rename wheretotrim.pl to wheretotrim and install it +somewhere in your path. Install wheretotrim.sty somewhere where LaTeX +can find it. See +http://www.tex.ac.uk/cgi-bin/texfaq2html?label=instpackages for +general information about installing packages. + + +Copyright and license +===================== + +Copyright (C) 2013 Scott Pakin + +This file may be distributed and/or modified under the conditions of +the LaTeX Project Public License, either version 1.3c 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.3c or later is part of all distributions of LaTeX +version 2008/05/04 or later. diff --git a/support/wheretotrim/wheretotrim b/support/wheretotrim/wheretotrim new file mode 100755 index 0000000000..2e52478f08 --- /dev/null +++ b/support/wheretotrim/wheretotrim @@ -0,0 +1,499 @@ +#! /usr/bin/env perl + +######################################## +# Identify opportunities for reducing # +# the page count of a LaTeX document # +# # +# By Scott Pakin <scott+wtt@pakin.org> # +######################################## + +use File::Basename; +use File::Temp qw(tempfile); +use Getopt::Long; +use POSIX; +use Pod::Usage; +use warnings; +use strict; + +# Define some global variables. +my $progname = basename $0; # Name of this program +my $logfile; # LaTeX-generated log file +my $verbosity = 1; # Level of output verbosity +my $allpages = 0; # 1=report changes needed for all pages; 0=any page +my @latexcmd; # Complete command to run LaTeX +my $ltxfile; # Name of input file +my $colsperpage = 1; # Number of columns per page (1 or 2) +my %column2page; # Map from absolute column number to {page, column} +my $debugexp; # Typeset using an expansion of <page>,<column>,<expansion lines> for debugging +our $VERSION = "1.0"; # Version number of this program + +# Define a subroutine that replaces a file name with its base name and +# (optionally) new suffix. +sub basename_newsuffix ($;$) +{ + my ($fname, $newsuffix) = @_; + my ($basename, undef, undef) = fileparse($fname, qr/\.[^.]*/); + $newsuffix = "" if !defined $newsuffix; + return $basename . $newsuffix; +} + +# Define a subroutine to create a temporary LaTeX file that modifies a +# few LaTeX commands then loads the user's document. The subroutine +# returns the name of the temporary file. +sub create_latex_file ($$$) +{ + my ($columntoexpand, $columnexpandlines, $extrafullcolumns) = @_; + my ($modltx, $modltxfile) = tempfile("wtt-XXXXXX", + TMPDIR => 1, + SUFFIX => ".tex", + UNLINK => 1); + print $modltx "\\RequirePackage[column=$columntoexpand,expansion=$columnexpandlines,extracols=$extrafullcolumns]{wheretotrim}\n"; + print $modltx "\\PassOptionsToPackage{draft}{hyperref}\n"; # Avoid "\pdfendlink ended up in different nesting level than \pdfstartlink" errors. + print $modltx "\\input{$ARGV[$#ARGV]}\n"; + close $modltx; + return $modltxfile; +} + +# Define a subroutine to run LaTeX on a given filename. +sub run_latex ($$$$) +{ + # Add some additional arguments to the LaTeX command. + my ($modltxfile, $columntoexpand, $columnexpandlines, $extrafullcolumns) = @_; + my $jobname = basename_newsuffix($ltxfile); + @latexcmd = (@ARGV[0..$#ARGV-1], "-jobname=$jobname", $modltxfile); + + # Run LaTeX. + if ($verbosity == 1) { + if ($columntoexpand == 0) { + print "Compiling $ltxfile normally"; + if ($extrafullcolumns > 0) { + printf ", but with %s column%s of padding", $extrafullcolumns, $extrafullcolumns == 1 ? "" : "s"; + } + print " ... "; + } + elsif ($colsperpage == 1) { + my ($page, $col) = @{$column2page{$columntoexpand}}; + printf "Compiling %s with page %d expanded by %d line%s ... ", + $ltxfile, $page, $columnexpandlines, $columnexpandlines == 1 ? "" : "s"; + } + else { + my ($page, $col) = @{$column2page{$columntoexpand}}; + printf "Compiling %s with page %d, column %d expanded by %d line%s ... ", + $ltxfile, $page, $col, $columnexpandlines, $columnexpandlines == 1 ? "" : "s"; + } + } + elsif ($verbosity > 1) { + print "Running @latexcmd\n"; + } + open(LATEX, "-|", "sh", "-c", 'echo X | "$@" 2>&1', "--", $latexcmd[0], @latexcmd[1..$#latexcmd]) || die; # Redirect stderr to stdout as we run. + while (my $oneline = <LATEX>) { + print $oneline if $verbosity > 1; + } + close LATEX; + my $errcode = $?; + if ($verbosity == 1) { + print $errcode == 0 ? "done.\n" : "failed.\n"; + } + elsif ($verbosity > 1) { + print "Finished running.\n"; + } + return $errcode; +} + +# Define a subroutine to process a log file and return various data +# extracted from it. +sub process_log_file ($$$) +{ + my ($columntoexpand, $columnexpandlines, $extrafullcolumns) = @_; + my %column_map; + + # Extract wheretotrim information lines and the final page count. + print "Processing $logfile ... " if $verbosity > 0; + my ($numpages, $baselineskip, $textheight) = (0, 0, 0); + open(LOGFILE, "<", $logfile) || die "${progname}: Failed to open $logfile ($!)\n"; + my $infostr = "Package wheretotrim Info"; + while (my $oneline = <LOGFILE>) { + $baselineskip = $1+0 if $oneline =~ /^$infostr: Baseline skip: ([\d.]+)pt/; + $textheight = $1+0 if $oneline =~ /^$infostr: Text height: ([\d.]+)pt/; + $column_map{$1} = [$2, $3] if $oneline =~ /^$infostr: Column (\d+) is on page (\d+) \((.*)\) on input line/; + $numpages = $1 if $oneline =~ /^Output written on.*\((\d+) page/; + } + close LOGFILE; + $numpages-- if $extrafullcolumns > 0; + if ($verbosity > 0) { + printf "done (%d page%s).\n", + $numpages, $numpages == 1 ? "" : "s", + } + return ($numpages, $baselineskip, $textheight, \%column_map); +} + +# Define a subroutine to run LaTeX and return a page count and other +# information. +sub latex_page_count ($$$) +{ + # LaTeX wrapper scripts might not like being given LaTeX code on + # the command line. We therefore create a temporary file that + # prepares LaTeX for programmatically modifying column heights. + my ($columntoexpand, $columnexpandlines, $extrafullcolumns) = @_; + my $modltxfile = create_latex_file($columntoexpand, $columnexpandlines, $extrafullcolumns); + + # Run latex on the temporary file. + my $errcode = run_latex($modltxfile, $columntoexpand, $columnexpandlines, $extrafullcolumns); + unlink $modltxfile; + + # Process the log file. + return (0, undef, undef, undef) if $errcode != 0; + return process_log_file($columntoexpand, $columnexpandlines, $extrafullcolumns); +} + +########################################################################### + +# Parse the command line. +my $wanthelp = 0; +my $wantversion = 0; +Getopt::Long::Configure("require_order"); +GetOptions("h|help" => \$wanthelp, + "V|version" => \$wantversion, + "a|allpages" => \$allpages, + "l|logfile=s" => \$logfile, + "v|verbose+" => \$verbosity, + "d|debug=s" => \$debugexp, + "q|quiet" => sub {$verbosity = 0}) + || pod2usage(-exitval => 2); +if ($wantversion) { + print "wheretotrim $VERSION\n"; + exit 0; +} +pod2usage(-verbose => $verbosity, + -exitval => 1) if $wanthelp; +pod2usage(-message => "${progname}: A latex command must be specified", + -exitval => 2) if $#ARGV == -1; +$ltxfile = basename($ARGV[$#ARGV]); +$logfile = basename_newsuffix($ARGV[$#ARGV], ".log") if !defined $logfile; + +# Determine the document's baseline characteristics. +my ($basepages, $baselineskip, $textheight, $c2p_ptr) = latex_page_count 0, 0, 0; +die "${progname}: Failed to build $ltxfile\n" if $basepages == 0; +%column2page = %$c2p_ptr; +print "\n" if $verbosity > 0; + +# Map an absolute column to a page and column number. +my $prevpage = 0; +foreach my $col (sort {$a <=> $b} keys %column2page) { + my ($pagenum, $pagename) = @{$column2page{$col}}; + if ($pagenum == $prevpage) { + $column2page{$col} = [$pagenum, 2, $pagename]; + $colsperpage = 2; + } + else { + $column2page{$col} = [$pagenum, 1, $pagename]; + } + $prevpage = $pagenum; +} + +# If we were given a page, column, and expansion, typeset the document +# with those parameters and exit. +if (defined $debugexp) { + die "${progname}: Failed to parse \"$debugexp\" into {page, column, expansion}\n" if $debugexp !~ /^(\d+)\D+(\d+)\D+(\d+)$/; + + # Convert page and column number to absolute column number. + my ($target_page, $target_col, $expansion) = ($1, $2, $3); + my $testcol; + while (my ($abscol, $page_col) = each %column2page) { + if ($target_page == $page_col->[0] && $target_col == $page_col->[1]) { + $testcol = $abscol; + last; + } + } + die "${progname}: Failed to map page $target_page, column $target_col to an absolute column number\n" if !defined $testcol; + + # Enlarge the given page. + my ($numpages, undef) = latex_page_count $testcol, $expansion, $colsperpage; + print "\n" if $verbosity > 0; + latex_page_count $testcol, $expansion, 0; # Run again without appending any extra columns. + print "\n" if $verbosity > 0; + print "Expanding page $target_page, column $target_col by $expansion lines "; + if ($numpages == $basepages) { + print "does not reduce the page count below $numpages pages.\n"; + } + else { + print "reduces the page count from $basepages pages to $numpages pages.\n"; + } + exit 0; +} + +# Determine columns for which no amount of expansion will reduce the +# page count. +my $maxexpansion = int($textheight/$baselineskip + 1); +my @complete = (0, 0+keys %column2page); # Fraction complete (numerator and denominator) +foreach my $expcol (sort {$a <=> $b} keys %column2page) { + my ($numpages, undef) = latex_page_count $expcol, $maxexpansion, $colsperpage; + if ($verbosity > 0) { + $complete[0]++; + printf "Trial runs are %.1f%% complete.\n\n", 100.0*$complete[0]/$complete[1]; + } + delete $column2page{$expcol} if $numpages > 0 && $numpages == $basepages; +} + +# Keep expanding a page by greater and greater amounts until we reduce +# our page count. +my %col2savings; # Map from an absolute column to an {expansion, page count} tuple. +my $target_num_cols = $allpages ? (keys %column2page) : 1; # Minimum number of columns for which to find an expansion amount +my $minexpansion; # Minimum value of the above that saves a page +@complete = (0, $maxexpansion*keys %column2page); +foreach my $expansion (1 .. $maxexpansion) { + # Expand each column in turn. + foreach my $expcol (sort {$a <=> $b} keys %column2page) { + $complete[0]++; + next if defined $col2savings{$expcol}; # Already finished + next if $column2page{$expcol}->[0] == $basepages && $column2page{$expcol}->[1] == 2; # Second column on the last page + my ($numpages, undef) = latex_page_count $expcol, $expansion, $colsperpage; + if ($numpages > 0 && $numpages < $basepages) { + $col2savings{$expcol} = [$expansion, $numpages]; + $minexpansion = $expansion if !defined $minexpansion; + } + if ($verbosity > 0) { + printf "Execution is %.1f%% complete.\n\n", 100.0*$complete[0]/$complete[1]; + } + } + last if keys %col2savings >= $target_num_cols; # Success +} + +# Restore the document to its original form. +run_latex $ltxfile, 0, 0, 0; +printf "Execution is 100.0%% complete.\n\n" if $verbosity > 0; + +# Output the space savings. +if (keys %col2savings == 0) { + printf "It does not appear possible to reduce the page count from %d to %d\n", + $basepages, $basepages-1; + print "by removing any amount of text from any single column.\n\n"; + exit 0; +} +printf "To reduce the page count from %d to %d, do %s following:\n\n", + $basepages, $basepages-1, keys %col2savings == 1 ? "the" : "any of the"; +foreach my $abscol (sort {$a <=> $b} keys %col2savings) { + my ($expansion, $numpages) = @{$col2savings{$abscol}}; + my ($page, $col, $pagename) = @{$column2page{$abscol}}; + print " * Reduce page $page"; + print " (\"$pagename\")" if $pagename ne $page; + print ", column $col" if $colsperpage > 1; + printf " by %d %s", $expansion, $expansion == 1 ? "line" : "lines"; + if ($numpages < $basepages - 1) { + printf " (produces %d %s)", $numpages, $numpages == 1 ? "page" : "pages"; + } + print ".\n"; +} +print "\n"; +my $minpoints = $minexpansion*$baselineskip; +printf "Note: %d lines = %.1f\" = %.1f cm = %.1f%% of the %s height\n", + $minexpansion, $minpoints/72.27, $minpoints/28.45, + 100.0*$minpoints/$textheight, $colsperpage == 1 ? "page" : "column"; + +########################################################################### + +__END__ + +=head1 NAME + +wheretotrim - Help reduce the page count of a LaTeX document + +=head1 SYNOPSIS + +wheretotrim +[B<--verbose> | B<--quiet>] +[B<--allpages>] +[B<--debug>=I<page>,I<column>,I<lines>] +I<latex command> + +wheretotrim [B<--verbose>] B<--help>|B<--version> + +=head1 DESCRIPTION + +B<wheretotrim> is a tool to help LaTeX users reduce their document's +page count. It is intended to be used with documents that exceed a +publisher's specified page-length limitation by a small amount (much +less than a full column or page). B<wheretotrim> operates by building +the document repeatedly, successively expanding each column on each +page by one line height to mimic reducing the amount of text in that +column by an equivalent amount. If doing so does not reduce the page +count, B<wheretotrim> repeats the process with two line heights' +expansion of each column, then three, and so forth until it expands +each column in turn by the full height of the column. The following +is some sample output: + + To reduce the page count from 10 to 9, do any of the following: + + * Reduce page 9, column 1 by 12 lines. + * Reduce page 9, column 2 by 12 lines. + * Reduce page 10, column 1 by 12 lines. + + Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height + +That is, reducing either column on S<page 9> or the first column on +S<page 10> by 12 lines is the most expedient way to reduce the +document's page count. More than S<12 lines> would need to be cut on +other columns and other pages to achieve the same effect. + +=head1 OPTIONS + +B<wheretotrim> accepts the following command-line options: + +=over 4 + +=item B<-a>, B<--allpages> + +Perform enough extra runs of B<latex> to report the amount of space +that must be trimmed from I<each> column or page to reduce page count, +not just the columns or pages to which the page count is the most +sensitive. + +=item B<-v>, B<--verbose> + +Display the output of each run of B<latex>. This is useful for +troubleshooting and to help monitor the progress of long B<latex> +runs. + +=item B<-q>, B<--quiet> + +Suppress progress updates and output only the final report. + +=item B<-d> I<page>,I<column>,I<lines>, B<--debug>=I<page>,I<column>,I<lines> + +Debug B<wheretotrim>'s execution by expanding page I<page>, column +I<column> by I<lines> line heights and leaving the B<latex> output in +that state. + +=item B<-h>, B<--help> + +Summarize usage information and exit. These may be used with +B<--verbose> to display more extended documentation. + +=item B<-V>, B<--version> + +Display B<wheretotrim>'s version number and exit. + +=back + +In addition to the preceding options, B<wheretotrim> requires a +I<latex command> argument that specifies how to build the document. + +=head1 EXAMPLES + +For the most basic usage, simply provide a B<latex> command to run: + + wheretotrim latex myfile.tex + +or, for example, + + wheretotrim pdflatex myfile.tex + +B<wheretotrim> executes the specified command a large number of times +and finally terminates with a report resembling the following: + + To reduce the page count from 10 to 9, do any of the following: + + * Reduce page 9, column 1 by 12 lines. + * Reduce page 9, column 2 by 12 lines. + * Reduce page 10, column 1 by 12 lines. + + Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height + +To ask B<wheretotrim> to report how much space needs to be trimmed on +each column and page to reduce the total page count, specify the +B<--allpages> option: + + wheretotrim --allpages latex myfile.tex + +The output now looks like the following: + + To reduce the page count from 10 to 9, do any of the following: + + * Reduce page 1, column 1 by 13 lines. + * Reduce page 1, column 2 by 13 lines. + * Reduce page 2, column 1 by 13 lines. + * Reduce page 2, column 2 by 13 lines. + * Reduce page 4, column 1 by 13 lines. + * Reduce page 4, column 2 by 13 lines. + * Reduce page 5, column 1 by 13 lines. + * Reduce page 5, column 2 by 13 lines. + * Reduce page 6, column 1 by 13 lines. + * Reduce page 6, column 2 by 13 lines. + * Reduce page 7, column 1 by 13 lines. + * Reduce page 7, column 2 by 13 lines. + * Reduce page 8, column 1 by 13 lines. + * Reduce page 8, column 2 by 13 lines. + * Reduce page 9, column 1 by 12 lines. + * Reduce page 9, column 2 by 12 lines. + * Reduce page 10, column 1 by 12 lines. + + Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height + +If you're curious how the document managed to shrink substantially as +the result of a relatively minor text reduction, you can typeset the +document with a particular page and column enlarged by a given amount: + + wheretotrim --debug=9,1,12 latex myfile.tex + +=head1 CAVEATS + +B<wheretotrim> hooks into LaTeX's output routines, which are +notoriously arcane and somewhat fragile. As a result, it is quite +likely that B<wheretotrim> will fail to analyze a large set of +documents. Use the B<--verbose> flag to help identify any problems +that B<latex> encounters. + +In many cases, B<wheretotrim> will recover by simply ignoring a few +possible page and column expansions. For example, certain expansions +may result in a C<L<Float(s) +lost|http://www.tex.ac.uk/cgi-bin/texfaq2html?label=fllost>> message. +In other cases, B<wheretotrim> will fail to analyze any modification +to the document. For example, it may receive an C<Infinite glue +shrinkage found in box being split> error from every page and column +variation it tries. In this particular case, see the discussion at +L<http://www.michaelshell.org/tex/ieeetran/>. + +When B<wheretotrim> is used with a B<latex> auto-build script you may +need to take measures to force the script to rebuild the document even +if it appears that no files have changed. For example, B<latexmk> +should be given the B<-CF> option to force rebuilding: + + wheretotrim latexmk -CF myfile.tex + +=head1 RESTRICTIONS + +B<wheretotrim> is implemented as a Perl script with an auxiliary LaTeX +package. It has been tested only on Linux, but I suspect that it +should also work on S<OS X>. I doubt it will work under Windows, +though, due to the way the script uses a B<bash>-specific technique +for redirecting the standard error device into the standard output +device. + +=head1 AUTHOR + +Scott Pakin, I<scott+wtt@pakin.org> + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2013, Scott Pakin <scott+wtt@pakin.org> + +This file may be distributed and/or modified under the conditions of +the LaTeX Project Public License, either version 1.3c of this license +or (at your option) any later version. The latest version of this +license is in: + +=over 4 + +=item E<nbsp> + +L<http://www.latex-project.org/lppl.txt> + +=back + +and version 1.3c or later is part of all distributions of LaTeX +version 2008/05/04 or later. + +=head1 SEE ALSO + +latex(1), +L<the savetrees package|http://www.ctan.org/pkg/savetrees/> diff --git a/support/wheretotrim/wheretotrim-pl.dtx b/support/wheretotrim/wheretotrim-pl.dtx new file mode 100644 index 0000000000..f4c41db0f4 --- /dev/null +++ b/support/wheretotrim/wheretotrim-pl.dtx @@ -0,0 +1,756 @@ +% ^^A ============================================================ +% ^^A This documented LaTeX code was generated mechanically from a +% ^^A source Perl script. Please edit the original Perl script +% ^^A instead of this file. +% ^^A ============================================================ +% +% \begingroup +% +% ^^A Define a perlsub environment for typesetting and indexing +% ^^A Perl subroutines. +% \catcode`\^^B=\active +% \makeatletter +% \def\SpecialMainPerlIndex#1{\@bsphack\special@index{% +% #1\actualchar +% {\string\ttfamily\space#1} +% (Perl)% +% \encapchar main}% +% \special@index{Perl subroutines:\levelchar#1\actualchar{% +% \string\ttfamily\space#1}\encapchar +% main}\@esphack} +% \newenvironment{perlsub}{^^A +% \def\bslash{}^^A +% \catcode`_=12^^A +% \SpecialEscapechar{\^^B}^^A +% \let\SpecialMainIndex=\SpecialMainPerlIndex +% \begin{macro} +% }{^^A +% \end{macro} +% } +% \makeatother +% +% ^^A Here's a hack to prevent code lines beginning with "%" from +% ^^A being discarded by docstrip yet still have doc typeset them normally. +% ^^A We prefix each such line with "%<script>" so docstrip will +% ^^A honor it yet redefine \Module as a no-op so doc won't include +% ^^A the "%<script>" when typesetting the line. +% \def\Module#1{} +% +% \subsection{Subroutine defintions} +% \begin{macrocode} +#! /usr/bin/env perl +% \end{macrocode} +% \iffalse + +######################################## +# Identify opportunities for reducing # +# the page count of a LaTeX document # +# # +# By Scott Pakin <scott+wtt@pakin.org> # +######################################## + +% \fi +% \begin{macrocode} +use File::Basename; +use File::Temp qw(tempfile); +use Getopt::Long; +use POSIX; +use Pod::Usage; +use warnings; +use strict; +% \end{macrocode} +% \iffalse + +% \fi +% +% Define some global variables. +% \iffalse +# Define some global variables. +% \fi +% \begin{macrocode} +my $progname = basename $0; # Name of this program +my $logfile; # LaTeX-generated log file +my $verbosity = 1; # Level of output verbosity +my $allpages = 0; # 1=report changes needed for all pages; 0=any page +my @latexcmd; # Complete command to run LaTeX +my $ltxfile; # Name of input file +my $colsperpage = 1; # Number of columns per page (1 or 2) +my %column2page; # Map from absolute column number to {page, column} +my $debugexp; # Typeset using an expansion of <page>,<column>,<expansion lines> for debugging +our $VERSION = "1.0"; # Version number of this program +% \end{macrocode} +% \iffalse + +% \fi +% +% \begin{perlsub}{basename_newsuffix} +% Define a subroutine that replaces a file name with its base name and +% (optionally) new suffix. +% \iffalse +# Define a subroutine that replaces a file name with its base name and +# (optionally) new suffix. +% \fi +% \begin{macrocode} +sub basename_newsuffix ($;$) +{ + my ($fname, $newsuffix) = @_; + my ($basename, undef, undef) = fileparse($fname, qr/\.[^.]*/); + $newsuffix = "" if !defined $newsuffix; + return $basename . $newsuffix; +} +% \end{macrocode} +% \end{perlsub} +% \iffalse + +% \fi +% +% \begin{perlsub}{create_latex_file} +% Define a subroutine to create a temporary \LaTeX{} file that modifies a +% few \LaTeX{} commands then loads the user's document. The subroutine +% returns the name of the temporary file. +% \iffalse +# Define a subroutine to create a temporary LaTeX file that modifies a +# few LaTeX commands then loads the user's document. The subroutine +# returns the name of the temporary file. +% \fi +% \begin{macrocode} +sub create_latex_file ($$$) +{ + my ($columntoexpand, $columnexpandlines, $extrafullcolumns) = @_; + my ($modltx, $modltxfile) = tempfile("wtt-XXXXXX", + TMPDIR => 1, + SUFFIX => ".tex", + UNLINK => 1); + print $modltx "\\RequirePackage[column=$columntoexpand,expansion=$columnexpandlines,extracols=$extrafullcolumns]{wheretotrim}\n"; + print $modltx "\\PassOptionsToPackage{draft}{hyperref}\n"; # Avoid "\pdfendlink ended up in different nesting level than \pdfstartlink" errors. + print $modltx "\\input{$ARGV[$#ARGV]}\n"; + close $modltx; + return $modltxfile; +} +% \end{macrocode} +% \end{perlsub} +% \iffalse + +% \fi +% +% \begin{perlsub}{run_latex} +% Define a subroutine to run \LaTeX{} on a given filename. +% \iffalse +# Define a subroutine to run LaTeX on a given filename. +% \fi +% \begin{macrocode} +sub run_latex ($$$$) +{ +% \end{macrocode} +% \end{perlsub} +% +% Add some additional arguments to the \LaTeX{} command. +% \iffalse + # Add some additional arguments to the LaTeX command. +% \fi +% \begin{macrocode} + my ($modltxfile, $columntoexpand, $columnexpandlines, $extrafullcolumns) = @_; + my $jobname = basename_newsuffix($ltxfile); + @latexcmd = (@ARGV[0..$#ARGV-1], "-jobname=$jobname", $modltxfile); +% \end{macrocode} +% \iffalse + +% \fi +% +% Run \LaTeX{}. +% \iffalse + # Run LaTeX. +% \fi +% \begin{macrocode} + if ($verbosity == 1) { + if ($columntoexpand == 0) { + print "Compiling $ltxfile normally"; + if ($extrafullcolumns > 0) { + printf ", but with %s column%s of padding", $extrafullcolumns, $extrafullcolumns == 1 ? "" : "s"; + } + print " ... "; + } + elsif ($colsperpage == 1) { + my ($page, $col) = @{$column2page{$columntoexpand}}; + printf "Compiling %s with page %d expanded by %d line%s ... ", + $ltxfile, $page, $columnexpandlines, $columnexpandlines == 1 ? "" : "s"; + } + else { + my ($page, $col) = @{$column2page{$columntoexpand}}; + printf "Compiling %s with page %d, column %d expanded by %d line%s ... ", + $ltxfile, $page, $col, $columnexpandlines, $columnexpandlines == 1 ? "" : "s"; + } + } + elsif ($verbosity > 1) { + print "Running @latexcmd\n"; + } + open(LATEX, "-|", "sh", "-c", 'echo X | "$@" 2>&1', "--", $latexcmd[0], @latexcmd[1..$#latexcmd]) || die; # Redirect stderr to stdout as we run. + while (my $oneline = <LATEX>) { + print $oneline if $verbosity > 1; + } + close LATEX; + my $errcode = $?; + if ($verbosity == 1) { + print $errcode == 0 ? "done.\n" : "failed.\n"; + } + elsif ($verbosity > 1) { + print "Finished running.\n"; + } + return $errcode; +} +% \end{macrocode} +% \iffalse + +% \fi +% +% \begin{perlsub}{process_log_file} +% Define a subroutine to process a log file and return various data +% extracted from it. +% \iffalse +# Define a subroutine to process a log file and return various data +# extracted from it. +% \fi +% \begin{macrocode} +sub process_log_file ($$$) +{ + my ($columntoexpand, $columnexpandlines, $extrafullcolumns) = @_; + my %column_map; +% \end{macrocode} +% \end{perlsub} +% \iffalse + +% \fi +% +% Extract wheretotrim information lines and the final page count. +% \iffalse + # Extract wheretotrim information lines and the final page count. +% \fi +% \begin{macrocode} + print "Processing $logfile ... " if $verbosity > 0; + my ($numpages, $baselineskip, $textheight) = (0, 0, 0); + open(LOGFILE, "<", $logfile) || die "${progname}: Failed to open $logfile ($!)\n"; + my $infostr = "Package wheretotrim Info"; + while (my $oneline = <LOGFILE>) { + $baselineskip = $1+0 if $oneline =~ /^$infostr: Baseline skip: ([\d.]+)pt/; + $textheight = $1+0 if $oneline =~ /^$infostr: Text height: ([\d.]+)pt/; + $column_map{$1} = [$2, $3] if $oneline =~ /^$infostr: Column (\d+) is on page (\d+) \((.*)\) on input line/; + $numpages = $1 if $oneline =~ /^Output written on.*\((\d+) page/; + } + close LOGFILE; + $numpages-- if $extrafullcolumns > 0; + if ($verbosity > 0) { + printf "done (%d page%s).\n", + $numpages, $numpages == 1 ? "" : "s", + } + return ($numpages, $baselineskip, $textheight, \%column_map); +} +% \end{macrocode} +% \iffalse + +% \fi +% +% \begin{perlsub}{latex_page_count} +% Define a subroutine to run \LaTeX{} and return a page count and other +% information. +% \iffalse +# Define a subroutine to run LaTeX and return a page count and other +# information. +% \fi +% \begin{macrocode} +sub latex_page_count ($$$) +{ +% \end{macrocode} +% \end{perlsub} +% +% \LaTeX{} wrapper scripts might not like being given \LaTeX{} code on +% the command line. We therefore create a temporary file that +% prepares \LaTeX{} for programmatically modifying column heights. +% \iffalse + # LaTeX wrapper scripts might not like being given LaTeX code on + # the command line. We therefore create a temporary file that + # prepares LaTeX for programmatically modifying column heights. +% \fi +% \begin{macrocode} + my ($columntoexpand, $columnexpandlines, $extrafullcolumns) = @_; + my $modltxfile = create_latex_file($columntoexpand, $columnexpandlines, $extrafullcolumns); +% \end{macrocode} +% \iffalse + +% \fi +% +% Run latex on the temporary file. +% \iffalse + # Run latex on the temporary file. +% \fi +% \begin{macrocode} + my $errcode = run_latex($modltxfile, $columntoexpand, $columnexpandlines, $extrafullcolumns); + unlink $modltxfile; +% \end{macrocode} +% \iffalse + +% \fi +% +% Process the log file. +% \iffalse + # Process the log file. +% \fi +% \begin{macrocode} + return (0, undef, undef, undef) if $errcode != 0; + return process_log_file($columntoexpand, $columnexpandlines, $extrafullcolumns); +} +% \end{macrocode} +% \iffalse + +########################################################################### + +% \fi +% +% \subsection{Main program execution} +% Parse the command line. +% \iffalse +# Parse the command line. +% \fi +% \begin{macrocode} +my $wanthelp = 0; +my $wantversion = 0; +Getopt::Long::Configure("require_order"); +GetOptions("h|help" => \$wanthelp, + "V|version" => \$wantversion, + "a|allpages" => \$allpages, + "l|logfile=s" => \$logfile, + "v|verbose+" => \$verbosity, + "d|debug=s" => \$debugexp, + "q|quiet" => sub {$verbosity = 0}) + || pod2usage(-exitval => 2); +if ($wantversion) { + print "wheretotrim $VERSION\n"; + exit 0; +} +pod2usage(-verbose => $verbosity, + -exitval => 1) if $wanthelp; +pod2usage(-message => "${progname}: A latex command must be specified", + -exitval => 2) if $#ARGV == -1; +$ltxfile = basename($ARGV[$#ARGV]); +$logfile = basename_newsuffix($ARGV[$#ARGV], ".log") if !defined $logfile; +% \end{macrocode} +% \iffalse + +% \fi +% +% Determine the document's baseline characteristics. +% \iffalse +# Determine the document's baseline characteristics. +% \fi +% \begin{macrocode} +my ($basepages, $baselineskip, $textheight, $c2p_ptr) = latex_page_count 0, 0, 0; +die "${progname}: Failed to build $ltxfile\n" if $basepages == 0; +%<script>%column2page = %$c2p_ptr; +print "\n" if $verbosity > 0; +% \end{macrocode} +% \iffalse + +% \fi +% +% Map an absolute column to a page and column number. +% \iffalse +# Map an absolute column to a page and column number. +% \fi +% \begin{macrocode} +my $prevpage = 0; +foreach my $col (sort {$a <=> $b} keys %column2page) { + my ($pagenum, $pagename) = @{$column2page{$col}}; + if ($pagenum == $prevpage) { + $column2page{$col} = [$pagenum, 2, $pagename]; + $colsperpage = 2; + } + else { + $column2page{$col} = [$pagenum, 1, $pagename]; + } + $prevpage = $pagenum; +} +% \end{macrocode} +% \iffalse + +% \fi +% +% If we were given a page, column, and expansion, typeset the document +% with those parameters and exit. +% \iffalse +# If we were given a page, column, and expansion, typeset the document +# with those parameters and exit. +% \fi +% \begin{macrocode} +if (defined $debugexp) { + die "${progname}: Failed to parse \"$debugexp\" into {page, column, expansion}\n" if $debugexp !~ /^(\d+)\D+(\d+)\D+(\d+)$/; +% \end{macrocode} +% \iffalse + +% \fi +% +% Convert page and column number to absolute column number. +% \iffalse + # Convert page and column number to absolute column number. +% \fi +% \begin{macrocode} + my ($target_page, $target_col, $expansion) = ($1, $2, $3); + my $testcol; + while (my ($abscol, $page_col) = each %column2page) { + if ($target_page == $page_col->[0] && $target_col == $page_col->[1]) { + $testcol = $abscol; + last; + } + } + die "${progname}: Failed to map page $target_page, column $target_col to an absolute column number\n" if !defined $testcol; +% \end{macrocode} +% \iffalse + +% \fi +% +% Enlarge the given page. +% \iffalse + # Enlarge the given page. +% \fi +% \begin{macrocode} + my ($numpages, undef) = latex_page_count $testcol, $expansion, $colsperpage; + print "\n" if $verbosity > 0; + latex_page_count $testcol, $expansion, 0; # Run again without appending any extra columns. + print "\n" if $verbosity > 0; + print "Expanding page $target_page, column $target_col by $expansion lines "; + if ($numpages == $basepages) { + print "does not reduce the page count below $numpages pages.\n"; + } + else { + print "reduces the page count from $basepages pages to $numpages pages.\n"; + } + exit 0; +} +% \end{macrocode} +% \iffalse + +% \fi +% +% Determine columns for which no amount of expansion will reduce the +% page count. +% \iffalse +# Determine columns for which no amount of expansion will reduce the +# page count. +% \fi +% \begin{macrocode} +my $maxexpansion = int($textheight/$baselineskip + 1); +my @complete = (0, 0+keys %column2page); # Fraction complete (numerator and denominator) +foreach my $expcol (sort {$a <=> $b} keys %column2page) { + my ($numpages, undef) = latex_page_count $expcol, $maxexpansion, $colsperpage; + if ($verbosity > 0) { + $complete[0]++; + printf "Trial runs are %.1f%% complete.\n\n", 100.0*$complete[0]/$complete[1]; + } + delete $column2page{$expcol} if $numpages > 0 && $numpages == $basepages; +} +% \end{macrocode} +% \iffalse + +% \fi +% +% Keep expanding a page by greater and greater amounts until we reduce +% our page count. +% \iffalse +# Keep expanding a page by greater and greater amounts until we reduce +# our page count. +% \fi +% \begin{macrocode} +my %col2savings; # Map from an absolute column to an {expansion, page count} tuple. +my $target_num_cols = $allpages ? (keys %column2page) : 1; # Minimum number of columns for which to find an expansion amount +my $minexpansion; # Minimum value of the above that saves a page +@complete = (0, $maxexpansion*keys %column2page); +foreach my $expansion (1 .. $maxexpansion) { +% \end{macrocode} +% +% Expand each column in turn. +% \iffalse + # Expand each column in turn. +% \fi +% \begin{macrocode} + foreach my $expcol (sort {$a <=> $b} keys %column2page) { + $complete[0]++; + next if defined $col2savings{$expcol}; # Already finished + next if $column2page{$expcol}->[0] == $basepages && $column2page{$expcol}->[1] == 2; # Second column on the last page + my ($numpages, undef) = latex_page_count $expcol, $expansion, $colsperpage; + if ($numpages > 0 && $numpages < $basepages) { + $col2savings{$expcol} = [$expansion, $numpages]; + $minexpansion = $expansion if !defined $minexpansion; + } + if ($verbosity > 0) { + printf "Execution is %.1f%% complete.\n\n", 100.0*$complete[0]/$complete[1]; + } + } + last if keys %col2savings >= $target_num_cols; # Success +} +% \end{macrocode} +% \iffalse + +% \fi +% +% Restore the document to its original form. +% \iffalse +# Restore the document to its original form. +% \fi +% \begin{macrocode} +run_latex $ltxfile, 0, 0, 0; +printf "Execution is 100.0%% complete.\n\n" if $verbosity > 0; +% \end{macrocode} +% \iffalse + +% \fi +% +% Output the space savings. +% \iffalse +# Output the space savings. +% \fi +% \begin{macrocode} +if (keys %col2savings == 0) { + printf "It does not appear possible to reduce the page count from %d to %d\n", + $basepages, $basepages-1; + print "by removing any amount of text from any single column.\n\n"; + exit 0; +} +printf "To reduce the page count from %d to %d, do %s following:\n\n", + $basepages, $basepages-1, keys %col2savings == 1 ? "the" : "any of the"; +foreach my $abscol (sort {$a <=> $b} keys %col2savings) { + my ($expansion, $numpages) = @{$col2savings{$abscol}}; + my ($page, $col, $pagename) = @{$column2page{$abscol}}; + print " * Reduce page $page"; + print " (\"$pagename\")" if $pagename ne $page; + print ", column $col" if $colsperpage > 1; + printf " by %d %s", $expansion, $expansion == 1 ? "line" : "lines"; + if ($numpages < $basepages - 1) { + printf " (produces %d %s)", $numpages, $numpages == 1 ? "page" : "pages"; + } + print ".\n"; +} +print "\n"; +my $minpoints = $minexpansion*$baselineskip; +printf "Note: %d lines = %.1f\" = %.1f cm = %.1f%% of the %s height\n", + $minexpansion, $minpoints/72.27, $minpoints/28.45, + 100.0*$minpoints/$textheight, $colsperpage == 1 ? "page" : "column"; +% \end{macrocode} +% \iffalse + +########################################################################### + +__END__ + +=head1 NAME + +wheretotrim - Help reduce the page count of a LaTeX document + +=head1 SYNOPSIS + +wheretotrim +[B<--verbose> | B<--quiet>] +[B<--allpages>] +[B<--debug>=I<page>,I<column>,I<lines>] +I<latex command> + +wheretotrim [B<--verbose>] B<--help>|B<--version> + +=head1 DESCRIPTION + +B<wheretotrim> is a tool to help LaTeX users reduce their document's +page count. It is intended to be used with documents that exceed a +publisher's specified page-length limitation by a small amount (much +less than a full column or page). B<wheretotrim> operates by building +the document repeatedly, successively expanding each column on each +page by one line height to mimic reducing the amount of text in that +column by an equivalent amount. If doing so does not reduce the page +count, B<wheretotrim> repeats the process with two line heights' +expansion of each column, then three, and so forth until it expands +each column in turn by the full height of the column. The following +is some sample output: + + To reduce the page count from 10 to 9, do any of the following: + + * Reduce page 9, column 1 by 12 lines. + * Reduce page 9, column 2 by 12 lines. + * Reduce page 10, column 1 by 12 lines. + + Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height + +That is, reducing either column on S<page 9> or the first column on +S<page 10> by 12 lines is the most expedient way to reduce the +document's page count. More than S<12 lines> would need to be cut on +other columns and other pages to achieve the same effect. + +=head1 OPTIONS + +B<wheretotrim> accepts the following command-line options: + +=over 4 + +=item B<-a>, B<--allpages> + +Perform enough extra runs of B<latex> to report the amount of space +that must be trimmed from I<each> column or page to reduce page count, +not just the columns or pages to which the page count is the most +sensitive. + +=item B<-v>, B<--verbose> + +Display the output of each run of B<latex>. This is useful for +troubleshooting and to help monitor the progress of long B<latex> +runs. + +=item B<-q>, B<--quiet> + +Suppress progress updates and output only the final report. + +=item B<-d> I<page>,I<column>,I<lines>, B<--debug>=I<page>,I<column>,I<lines> + +Debug B<wheretotrim>'s execution by expanding page I<page>, column +I<column> by I<lines> line heights and leaving the B<latex> output in +that state. + +=item B<-h>, B<--help> + +Summarize usage information and exit. These may be used with +B<--verbose> to display more extended documentation. + +=item B<-V>, B<--version> + +Display B<wheretotrim>'s version number and exit. + +=back + +In addition to the preceding options, B<wheretotrim> requires a +I<latex command> argument that specifies how to build the document. + +=head1 EXAMPLES + +For the most basic usage, simply provide a B<latex> command to run: + + wheretotrim latex myfile.tex + +or, for example, + + wheretotrim pdflatex myfile.tex + +B<wheretotrim> executes the specified command a large number of times +and finally terminates with a report resembling the following: + + To reduce the page count from 10 to 9, do any of the following: + + * Reduce page 9, column 1 by 12 lines. + * Reduce page 9, column 2 by 12 lines. + * Reduce page 10, column 1 by 12 lines. + + Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height + +To ask B<wheretotrim> to report how much space needs to be trimmed on +each column and page to reduce the total page count, specify the +B<--allpages> option: + + wheretotrim --allpages latex myfile.tex + +The output now looks like the following: + + To reduce the page count from 10 to 9, do any of the following: + + * Reduce page 1, column 1 by 13 lines. + * Reduce page 1, column 2 by 13 lines. + * Reduce page 2, column 1 by 13 lines. + * Reduce page 2, column 2 by 13 lines. + * Reduce page 4, column 1 by 13 lines. + * Reduce page 4, column 2 by 13 lines. + * Reduce page 5, column 1 by 13 lines. + * Reduce page 5, column 2 by 13 lines. + * Reduce page 6, column 1 by 13 lines. + * Reduce page 6, column 2 by 13 lines. + * Reduce page 7, column 1 by 13 lines. + * Reduce page 7, column 2 by 13 lines. + * Reduce page 8, column 1 by 13 lines. + * Reduce page 8, column 2 by 13 lines. + * Reduce page 9, column 1 by 12 lines. + * Reduce page 9, column 2 by 12 lines. + * Reduce page 10, column 1 by 12 lines. + + Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height + +If you're curious how the document managed to shrink substantially as +the result of a relatively minor text reduction, you can typeset the +document with a particular page and column enlarged by a given amount: + + wheretotrim --debug=9,1,12 latex myfile.tex + +=head1 CAVEATS + +B<wheretotrim> hooks into LaTeX's output routines, which are +notoriously arcane and somewhat fragile. As a result, it is quite +likely that B<wheretotrim> will fail to analyze a large set of +documents. Use the B<--verbose> flag to help identify any problems +that B<latex> encounters. + +In many cases, B<wheretotrim> will recover by simply ignoring a few +possible page and column expansions. For example, certain expansions +may result in a C<L<Float(s) +lost|http://www.tex.ac.uk/cgi-bin/texfaq2html?label=fllost>> message. +In other cases, B<wheretotrim> will fail to analyze any modification +to the document. For example, it may receive an C<Infinite glue +shrinkage found in box being split> error from every page and column +variation it tries. In this particular case, see the discussion at +L<http://www.michaelshell.org/tex/ieeetran/>. + +When B<wheretotrim> is used with a B<latex> auto-build script you may +need to take measures to force the script to rebuild the document even +if it appears that no files have changed. For example, B<latexmk> +should be given the B<-CF> option to force rebuilding: + + wheretotrim latexmk -CF myfile.tex + +=head1 RESTRICTIONS + +B<wheretotrim> is implemented as a Perl script with an auxiliary LaTeX +package. It has been tested only on Linux, but I suspect that it +should also work on S<OS X>. I doubt it will work under Windows, +though, due to the way the script uses a B<bash>-specific technique +for redirecting the standard error device into the standard output +device. + +=head1 AUTHOR + +Scott Pakin, I<scott+wtt@pakin.org> + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2013, Scott Pakin <scott+wtt@pakin.org> + +This file may be distributed and/or modified under the conditions of +the LaTeX Project Public License, either version 1.3c of this license +or (at your option) any later version. The latest version of this +license is in: + +=over 4 + +=item E<nbsp> + +L<http://www.latex-project.org/lppl.txt> + +=back + +and version 1.3c or later is part of all distributions of LaTeX +version 2008/05/04 or later. + +=head1 SEE ALSO + +latex(1), +L<the savetrees package|http://www.ctan.org/pkg/savetrees/> +% \fi +% +% \endgroup +% +% +% +% The \progname{wheretotrim} script ends with +% \href{http://perldoc.perl.org/perlpod.html}{POD}-format documentation. +% This is not listed here because it is largely redundant with the +% contents of Sections~\ref{sec:introduction} and~\ref{sec:usage}. +% See those sections for documentation about \progname{wheretotrim}'s usage. +% \endinput diff --git a/support/wheretotrim/wheretotrim.1 b/support/wheretotrim/wheretotrim.1 new file mode 100644 index 0000000000..2cf8ba4deb --- /dev/null +++ b/support/wheretotrim/wheretotrim.1 @@ -0,0 +1,324 @@ +.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "WHERETOTRIM 1" +.TH WHERETOTRIM 1 "2013-05-14" "v1.0" "" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +wheretotrim \- Help reduce the page count of a LaTeX document +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +wheretotrim +[\fB\-\-verbose\fR | \fB\-\-quiet\fR] +[\fB\-\-allpages\fR] +[\fB\-\-debug\fR=\fIpage\fR,\fIcolumn\fR,\fIlines\fR] +\&\fIlatex command\fR +.PP +wheretotrim [\fB\-\-verbose\fR] \fB\-\-help\fR|\fB\-\-version\fR +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +\&\fBwheretotrim\fR is a tool to help LaTeX users reduce their document's +page count. It is intended to be used with documents that exceed a +publisher's specified page-length limitation by a small amount (much +less than a full column or page). \fBwheretotrim\fR operates by building +the document repeatedly, successively expanding each column on each +page by one line height to mimic reducing the amount of text in that +column by an equivalent amount. If doing so does not reduce the page +count, \fBwheretotrim\fR repeats the process with two line heights' +expansion of each column, then three, and so forth until it expands +each column in turn by the full height of the column. The following +is some sample output: +.PP +.Vb 1 +\& To reduce the page count from 10 to 9, do any of the following: +\& +\& * Reduce page 9, column 1 by 12 lines. +\& * Reduce page 9, column 2 by 12 lines. +\& * Reduce page 10, column 1 by 12 lines. +\& +\& Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height +.Ve +.PP +That is, reducing either column on page\ 9 or the first column on +page\ 10 by 12 lines is the most expedient way to reduce the +document's page count. More than 12\ lines would need to be cut on +other columns and other pages to achieve the same effect. +.SH "OPTIONS" +.IX Header "OPTIONS" +\&\fBwheretotrim\fR accepts the following command-line options: +.IP "\fB\-a\fR, \fB\-\-allpages\fR" 4 +.IX Item "-a, --allpages" +Perform enough extra runs of \fBlatex\fR to report the amount of space +that must be trimmed from \fIeach\fR column or page to reduce page count, +not just the columns or pages to which the page count is the most +sensitive. +.IP "\fB\-v\fR, \fB\-\-verbose\fR" 4 +.IX Item "-v, --verbose" +Display the output of each run of \fBlatex\fR. This is useful for +troubleshooting and to help monitor the progress of long \fBlatex\fR +runs. +.IP "\fB\-q\fR, \fB\-\-quiet\fR" 4 +.IX Item "-q, --quiet" +Suppress progress updates and output only the final report. +.IP "\fB\-d\fR \fIpage\fR,\fIcolumn\fR,\fIlines\fR, \fB\-\-debug\fR=\fIpage\fR,\fIcolumn\fR,\fIlines\fR" 4 +.IX Item "-d page,column,lines, --debug=page,column,lines" +Debug \fBwheretotrim\fR's execution by expanding page \fIpage\fR, column +\&\fIcolumn\fR by \fIlines\fR line heights and leaving the \fBlatex\fR output in +that state. +.IP "\fB\-h\fR, \fB\-\-help\fR" 4 +.IX Item "-h, --help" +Summarize usage information and exit. These may be used with +\&\fB\-\-verbose\fR to display more extended documentation. +.IP "\fB\-V\fR, \fB\-\-version\fR" 4 +.IX Item "-V, --version" +Display \fBwheretotrim\fR's version number and exit. +.PP +In addition to the preceding options, \fBwheretotrim\fR requires a +\&\fIlatex command\fR argument that specifies how to build the document. +.SH "EXAMPLES" +.IX Header "EXAMPLES" +For the most basic usage, simply provide a \fBlatex\fR command to run: +.PP +.Vb 1 +\& wheretotrim latex myfile.tex +.Ve +.PP +or, for example, +.PP +.Vb 1 +\& wheretotrim pdflatex myfile.tex +.Ve +.PP +\&\fBwheretotrim\fR executes the specified command a large number of times +and finally terminates with a report resembling the following: +.PP +.Vb 1 +\& To reduce the page count from 10 to 9, do any of the following: +\& +\& * Reduce page 9, column 1 by 12 lines. +\& * Reduce page 9, column 2 by 12 lines. +\& * Reduce page 10, column 1 by 12 lines. +\& +\& Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height +.Ve +.PP +To ask \fBwheretotrim\fR to report how much space needs to be trimmed on +each column and page to reduce the total page count, specify the +\&\fB\-\-allpages\fR option: +.PP +.Vb 1 +\& wheretotrim \-\-allpages latex myfile.tex +.Ve +.PP +The output now looks like the following: +.PP +.Vb 1 +\& To reduce the page count from 10 to 9, do any of the following: +\& +\& * Reduce page 1, column 1 by 13 lines. +\& * Reduce page 1, column 2 by 13 lines. +\& * Reduce page 2, column 1 by 13 lines. +\& * Reduce page 2, column 2 by 13 lines. +\& * Reduce page 4, column 1 by 13 lines. +\& * Reduce page 4, column 2 by 13 lines. +\& * Reduce page 5, column 1 by 13 lines. +\& * Reduce page 5, column 2 by 13 lines. +\& * Reduce page 6, column 1 by 13 lines. +\& * Reduce page 6, column 2 by 13 lines. +\& * Reduce page 7, column 1 by 13 lines. +\& * Reduce page 7, column 2 by 13 lines. +\& * Reduce page 8, column 1 by 13 lines. +\& * Reduce page 8, column 2 by 13 lines. +\& * Reduce page 9, column 1 by 12 lines. +\& * Reduce page 9, column 2 by 12 lines. +\& * Reduce page 10, column 1 by 12 lines. +\& +\& Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height +.Ve +.PP +If you're curious how the document managed to shrink substantially as +the result of a relatively minor text reduction, you can typeset the +document with a particular page and column enlarged by a given amount: +.PP +.Vb 1 +\& wheretotrim \-\-debug=9,1,12 latex myfile.tex +.Ve +.SH "CAVEATS" +.IX Header "CAVEATS" +\&\fBwheretotrim\fR hooks into LaTeX's output routines, which are +notoriously arcane and somewhat fragile. As a result, it is quite +likely that \fBwheretotrim\fR will fail to analyze a large set of +documents. Use the \fB\-\-verbose\fR flag to help identify any problems +that \fBlatex\fR encounters. +.PP +In many cases, \fBwheretotrim\fR will recover by simply ignoring a few +possible page and column expansions. For example, certain expansions +may result in a \f(CW\*(C`Float(s) +lost <http://www.tex.ac.uk/cgi-bin/texfaq2html?label=fllost>\*(C'\fR message. +In other cases, \fBwheretotrim\fR will fail to analyze any modification +to the document. For example, it may receive an \f(CW\*(C`Infinite glue +shrinkage found in box being split\*(C'\fR error from every page and column +variation it tries. In this particular case, see the discussion at +<http://www.michaelshell.org/tex/ieeetran/>. +.PP +When \fBwheretotrim\fR is used with a \fBlatex\fR auto-build script you may +need to take measures to force the script to rebuild the document even +if it appears that no files have changed. For example, \fBlatexmk\fR +should be given the \fB\-CF\fR option to force rebuilding: +.PP +.Vb 1 +\& wheretotrim latexmk \-CF myfile.tex +.Ve +.SH "RESTRICTIONS" +.IX Header "RESTRICTIONS" +\&\fBwheretotrim\fR is implemented as a Perl script with an auxiliary LaTeX +package. It has been tested only on Linux, but I suspect that it +should also work on \s-1OS\s0\ X. I doubt it will work under Windows, +though, due to the way the script uses a \fBbash\fR\-specific technique +for redirecting the standard error device into the standard output +device. +.SH "AUTHOR" +.IX Header "AUTHOR" +Scott Pakin, \fIscott+wtt@pakin.org\fR +.SH "COPYRIGHT AND LICENSE" +.IX Header "COPYRIGHT AND LICENSE" +Copyright (C) 2013, Scott Pakin <scott+wtt@pakin.org> +.PP +This file may be distributed and/or modified under the conditions of +the LaTeX Project Public License, either version 1.3c of this license +or (at your option) any later version. The latest version of this +license is in: +.IP "\" 4 +http://www.latex\-project.org/lppl.txt <http://www.latex-project.org/lppl.txt> +.PP +and version 1.3c or later is part of all distributions of LaTeX +version 2008/05/04 or later. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\fIlatex\fR\|(1), +the savetrees package <http://www.ctan.org/pkg/savetrees/> diff --git a/support/wheretotrim/wheretotrim.dtx b/support/wheretotrim/wheretotrim.dtx new file mode 100644 index 0000000000..de8c5c1df1 --- /dev/null +++ b/support/wheretotrim/wheretotrim.dtx @@ -0,0 +1,725 @@ +% \iffalse meta-comment +% +% Copyright (C) 2013 by Scott Pakin <scott+wtt@pakin.org> +% ------------------------------------------------------- +% +% This file may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3c +% 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.3c or later is part of all distributions of LaTeX +% version 2008/05/04 or later. +% +% \fi +% +% \iffalse +%<*driver> +\ProvidesFile{wheretotrim.dtx} +%</driver> +%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%<package>\ProvidesPackage{wheretotrim} +%<*package> + [2013/05/15 v1.0 Helper package for the wheretotrim script] +%</package> +% +%<*driver> +\documentclass{ltxdoc} +\usepackage{tabularx} +\usepackage{type1cm} +\usepackage{pifont} +\usepackage{needspace} +\usepackage[bookmarksopen]{hyperref} +\EnableCrossrefs +\CodelineIndex +\RecordChanges +\begin{document} + \DocInput{wheretotrim.dtx} +% \PrintChanges + \makeatletter + \let\orig@index@prologue=\index@prologue + \def\index@prologue{% + \phantomsection\addcontentsline{toc}{section}{Index} + \orig@index@prologue + }% + \makeatother + \Needspace{12\baselineskip} + \begingroup + \catcode`\_=12 + \PrintIndex + \endgroup +\end{document} +%</driver> +% \fi +% +% \CheckSum{180} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% +% \changes{v1.0}{2013/05/15}{Initial version} +% +% \GetFileInfo{wheretotrim.dtx} +% +% \DoNotIndex{\",\$,\%,\(,\),!,\D,\d,\n} +% \DoNotIndex{\addtocounter,\def,\edef,\fi,\gdef,\ifnum,\let} +% \DoNotIndex{\newcommand,\newcounter,\noexpand,\noindent,\par,\relax} +% \DoNotIndex{\setcounter,\space,\the,\value,\xdef} +% \newcommand{\see}[1]{\textit{see} #1} +% \index{command-line options|see{program options}} +% +% ^^A Define a few logical styles to help typeset the documentation. +% \DeclareRobustCommand{\pkgname}[1]{^^A +% \mbox{\textsf{#1}}^^A +% \SortIndex{packages}{packages>\textsf{#1}}^^A +% \SortIndex{#1}{\textsf{#1} (package)}} +% \DeclareRobustCommand{\optname}[1]{^^A +% \mbox{\texttt{#1}}^^A +% \SortIndex{package options}{package options>\texttt{#1}}^^A +% \SortIndex{#1}{\texttt{#1} (package option)}} +% \DeclareRobustCommand{\progname}[1]{^^A +% \mbox{\texttt{#1}}^^A +% \SortIndex{programs}{programs>\texttt{#1}}^^A +% \SortIndex{#1}{\texttt{#1} (program)}} +% \DeclareRobustCommand{\longopt}[1]{^^A +% \mbox{\textbf{-{}-#1}}^^A +% \SortIndex{program options}{program options>\texttt{-{}-#1}}^^A +% \SortIndex{#1}{\texttt{-{}-#1} (program option)}} +% +% ^^A Specify this document's metadata. +% \title{The \textsf{wheretotrim} package\thanks{This document +% corresponds to \textsf{wheretotrim}~\fileversion, dated \filedate.}} +% \author{Scott Pakin \\ \texttt{scott+wtt@pakin.org}} +% \hypersetup{^^A +% pdfauthor={Scott Pakin}, +% pdftitle={The wheretotrim package}, +% pdfsubject={Determine where to cut text to most easily reduce page count} +% } +% +% \maketitle +% \sloppy +% +% \section{Introduction} +% \label{sec:introduction} +% +% \progname{wheretotrim} is a tool to help \LaTeX\ users reduce their +% document's page count. It is intended to be used with documents that +% exceed a publisher's specified page-length limitation by a small +% amount (much less than a full column or page). \progname{wheretotrim} +% is therefore similar to the \pkgname{savetrees} package in that both +% seek to reduce page count. The two differ in that \pkgname{savetrees} +% saves space by altering document formatting while +% \progname{wheretotrim} suggests where text can be removed to reduce +% page count without altering any formatting. \progname{wheretotrim} +% and \pkgname{savetrees} are compatible with each other, though: +% \progname{wheretotrim} can safely be run on a document that includes a +% |\usepackage{savetrees}|. +% +% \progname{wheretotrim} operates by building the document repeatedly, +% successively expanding each column on each page by one line height to +% mimic reducing the amount of text in that column by an equivalent +% amount. If doing so does not reduce the page count, +% \progname{wheretotrim} repeats the process with two line heights' +% expansion of each column, then three, and so forth until it expands +% each column in turn by the full height of the column. The following +% is some sample output for a single-column document when +% \progname{wheretotrim} is run with the \longopt{allpages} option +% (cf.~Section~\ref{sec:cmdline}): +% \begin{verbatim} +% To reduce the page count from 11 to 10, do any of the following: +% +% * Reduce page 2 by 8 lines. +% * Reduce page 5 by 7 lines. +% * Reduce page 6 by 7 lines. +% * Reduce page 7 by 7 lines. +% * Reduce page 8 by 7 lines. +% * Reduce page 9 by 7 lines. +% * Reduce page 10 by 5 lines. +% * Reduce page 11 by 5 lines. +% +% Note: 5 lines = 1.0" = 2.5 cm = 11.1% of the page height +% \end{verbatim} +% +% \noindent +% That is, reducing either page~10 or page~11 by five lines is the most +% expedient way to reduce the document's page count. Seven lines would +% need to be cut from page~5, 6, 7, or~8 to achieve the same effect, and +% eight lines would need to be cut from page~2. In contrast, no amount +% of text trimming on pages~1, 3, or~4 will reduce the page count. +% +% \section{Usage} +% \label{sec:usage} +% +% Sections~\ref{sec:cmdline}--\ref{sec:restrictions} explain how to use +% \progname{wheretotrim}. +% +% \subsection{Command-line options} +% \label{sec:cmdline} +% +% Run \progname{wheretotrim} as follows: +% +% \DeleteShortVerb{\|} +% \begin{quote} +% \raggedright +% \progname{wheretotrim} +% [\longopt{allpages}] +% [\longopt{verbose} \textbar\ \longopt{quiet}] +% [\longopt{debug}=\meta{page},\meta{column},\meta{lines}] +% \meta{latex command} +% \end{quote} +% \noindent +% or +% \begin{quote} +% \raggedright +% \progname{wheretotrim} [\longopt{verbose}] \longopt{help} \textbar\ \longopt{version} +% \end{quote} +% \MakeShortVerb{\|} +% +% \progname{wheretotrim} accepts the following command-line options: +% +% \begin{description} +% \item[-a, \longopt{allpages}] Perform enough extra runs of +% \progname{latex} to report the amount of space that must be +% trimmed from \emph{each} column or page to reduce page count, not +% just the columns or pages to which the page count is the most +% sensitive. +% +% \item[-v, \longopt{verbose}] Display the output of each run of +% \progname{latex}. This is useful for troubleshooting and to help +% monitor the progress of long \progname{latex} runs. +% +% \item[-q, \longopt{quiet}] Suppress progress updates and output only the +% final report. +% +% \item[-d \meta{page},\meta{column},\meta{lines}, +% \longopt{debug}=\meta{page},\meta{column},\meta{lines}] Debug +% \progname{wheretotrim}'s execution by expanding page \meta{page}, +% column \meta{column} by \meta{lines} line heights and leaving the +% \progname{latex} output in that state. +% +% \item[-h, \longopt{help}] Summarize usage information and exit. +% These may be used with \longopt{verbose} to display more extended +% documentation. +% +% \item[-V, \longopt{version}] Display \progname{wheretotrim}'s version +% number and exit. +% \end{description} +% +% In addition to the preceding options, \progname{wheretotrim} requires +% a \meta{latex command} argument that specifies how to build the +% document. +% +% \subsection{Examples} +% \label{sec:examples} +% +% For the most basic usage, simply provide a \progname{latex} command to +% run: +% +% \begin{verbatim} +% wheretotrim latex myfile.tex +% \end{verbatim} +% +% \noindent +% or, for example, +% +% \begin{verbatim} +% wheretotrim pdflatex myfile.tex +% \end{verbatim} +% +% \progname{wheretotrim} executes the specified command a large number +% of times and finally terminates with a report resembling the +% following: +% +% \begin{verbatim} +% To reduce the page count from 10 to 9, do any of the following: +% +% * Reduce page 9, column 1 by 12 lines. +% * Reduce page 9, column 2 by 12 lines. +% * Reduce page 10, column 1 by 12 lines. +% +% Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height +% \end{verbatim} +% +% To ask \progname{wheretotrim} to report how much space needs to be +% trimmed on each column and page to reduce the total page count, +% specify the \longopt{allpages} option: +% +% \begin{verbatim} +% wheretotrim --allpages latex myfile.tex +% \end{verbatim} +% +% The output now looks like the following: +% +% \begin{verbatim} +% To reduce the page count from 10 to 9, do any of the following: +% +% * Reduce page 1, column 1 by 13 lines. +% * Reduce page 1, column 2 by 13 lines. +% * Reduce page 2, column 1 by 13 lines. +% * Reduce page 2, column 2 by 13 lines. +% * Reduce page 4, column 1 by 13 lines. +% * Reduce page 4, column 2 by 13 lines. +% * Reduce page 5, column 1 by 13 lines. +% * Reduce page 5, column 2 by 13 lines. +% * Reduce page 6, column 1 by 13 lines. +% * Reduce page 6, column 2 by 13 lines. +% * Reduce page 7, column 1 by 13 lines. +% * Reduce page 7, column 2 by 13 lines. +% * Reduce page 8, column 1 by 13 lines. +% * Reduce page 8, column 2 by 13 lines. +% * Reduce page 9, column 1 by 12 lines. +% * Reduce page 9, column 2 by 12 lines. +% * Reduce page 10, column 1 by 12 lines. +% +% Note: 12 lines = 2.4" = 6.1 cm = 26.8% of the column height +% \end{verbatim} +% +% If you're curious how the document managed to shrink substantially as +% the result of a relatively minor text reduction, you can typeset the +% document with a particular page and column enlarged by a given amount: +% +% \begin{verbatim} +% wheretotrim --debug=9,1,12 latex myfile.tex +% \end{verbatim} +% +% \subsection{Caveats} +% \label{sec:caveats} +% +% \progname{wheretotrim} hooks into \LaTeX's output routines, which are +% notoriously arcane and somewhat fragile. As a result, it is quite +% likely that \progname{wheretotrim} will fail to analyze a large set of +% documents. Use the \longopt{verbose} flag to help identify any +% problems that \progname{latex} encounters. +% +% In many cases, \progname{wheretotrim} will recover by simply ignoring +% a few possible page and column expansions. For example, certain +% expansions may result in a +% \href{http://www.tex.ac.uk/cgi-bin/texfaq2html?label=fllost}{\texttt{Float(s) +% lost}} message. In other cases, \progname{wheretotrim} will fail +% to analyze any modification to the document. For example, it may +% receive an \texttt{Infinite glue shrinkage found in box being split} +% error from every page and column variation it tries. In this +% particular case, see the discussion at +% \url{http://www.michaelshell.org/tex/ieeetran/}. +% +% When \progname{wheretotrim} is used with a \progname{latex} auto-build +% script you may need to take measures to force the script to rebuild +% the document even if it appears that no files have changed. For +% example, \progname{latexmk} should be given the |-CF| option to force +% rebuilding: +% +% \begin{verbatim} +% wheretotrim latexmk -CF myfile.tex +% \end{verbatim} +% +% \subsection{Restrictions} +% \label{sec:restrictions} +% +% \progname{wheretotrim} is implemented as a Perl script with an +% auxiliary \LaTeXe\ package. It has been tested only on Linux, but I +% suspect that it should also work on OS~X. I doubt it will work +% under Windows, though, due to the way the script uses a +% \progname{bash}-specific technique for redirecting the standard error +% device into the standard output device. +% +% +% \StopEventually{} +% +% \iffalse +%<*package> +% \fi +% +% \section{Package implementation} +% +% This section presents the commented \LaTeX\ source code for the +% \pkgname{wheretotrim} package. Read this section if you want to learn +% how the package is implemented. Note that the package is not intended +% to be used explicitly (i.e.,~via |\usepackage|) but rather implicitly +% by the \progname{wheretotrim} Perl script. +% +% \subsection{Theory of operation} +% +% The \pkgname{wheretotrim} package mimics the effect of reducing a +% given page and column of a document by a given number of line heights. +% For simplicity, it does so by enlarging the specified column (so +% as to fit additional lines of text) rather than by reducing the +% amount of text in that column. +% +% Users are not expected to load the \pkgname{wheretotrim} package +% explicitly. Instead, whenever the \progname{wheretotrim} script needs +% to observe the effect of enlarging a given column, it creates a +% temporary |.tex| file using the following template: +% +% \begingroup +% \ttfamily +% \begin{tabbing} +% ~~~~\=|\RequirePackage[|\=\kill +% \>|\RequirePackage[|\> column={\normalfont\meta{absolute column}}], \\ +% \> \> expansion={\normalfont\meta{lines}}, \\ +% \> \> extracols={\normalfont\meta{padding columns}}]|{wheretotrim}| \\ +% \> |\PassOptionsToPackage{draft}{hyperref}| \\ +% \> |\input{|{\normalfont\meta{filename}}|}| \\ +% \end{tabbing} +% \endgroup +% +% \noindent +% where \meta{absolute column} is the absolute column number to expand +% (with the first column on the first page being numbered~ 1); +% \meta{lines} is the number of line heights (multiples of +% |\baselineskip|) by which to enlarge that column; \meta{padding +% columns} is the number of extra full columns to append to the +% document (cf.~Section~\ref{sec:page-spillover}); and \meta{filename} +% is the name of the user's top-level \LaTeX\ file. +% +% The \pkgname{wheretotrim} package works by modifying various \TeX- and +% \LaTeX-internal commands. At every |\shipout|, \pkgname{wheretotrim} +% increases the absolute page counter. Whenever \LaTeX\ constructs a +% column using |\@makecol|, \pkgname{wheretotrim} logs the current +% absolute page and column numbers and invokes \LaTeX's +% |\enlargethispage| macro when on the target page and column number. +% Because |\@makecol| is not called for every column, +% \pkgname{wheretotrim} additionally modifies |\clearpage| and +% |\maketitle| also to conditionally enlarge the current column. +% +% At the end of the document, \pkgname{wheretotrim} outputs +% |\baselineskip| and |\textheight|, as these are needed by the +% \progname{wheretotrim} script. +% +% \subsection{Package options} +% \label{sec:pkg-opts} +% +% The \pkgname{wheretotrim} package accepts three package +% options---\optname{column}, \optname{expansion}, and +% \optname{extracols}---which are described +% below in the context of, respectively, |\wtt@target@column|, +% |\wtt@column@expand|, and |\wtt@extra@full@columns|. +% +% \begin{macro}{\wtt@target@column} +% |\wtt@target@column| is set by the \optname{column} option and +% defaults to nonexistent column~0. It specifies the absolute column +% number to expand. +% \begin{macrocode} +\newcommand{\wtt@target@column}{0} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\wtt@column@expand} +% The |\wtt@column@expand| length---implemented as an ordinary +% macro---is set by the \optname{expansion} option and defaults to +% 0\,pt. It specifies the number of lines by which to expand that +% column (i.e.,~the multiple of |\baselineskip|). +% \begin{macrocode} +\newcommand{\wtt@column@expand}{0pt} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\wtt@extra@full@columns} +% |\wtt@extra@full@columns| is set by the \optname{extracols} option and +% specifies the number of additional, dummy, full columns to append to +% the end of the document to force spillover onto an additional page. +% \begin{macrocode} +\newcommand{\wtt@extra@full@columns}{0} +% \end{macrocode} +% \end{macro} +% +% We use the \pkgname{keyval} package to help with option processing as +% it's widely available and \pkgname{wheretotrim}'s option-processing +% needs are fairly simple. +% \begin{macrocode} +\RequirePackage{keyval} +% \end{macrocode} +% \begin{macrocode} +\define@key{wtt}{column}{\gdef\wtt@target@column{#1}} +\define@key{wtt}{expansion}{% + \xdef\wtt@column@expand{#1\noexpand\baselineskip}% +} +\define@key{wtt}{extracols}{\gdef\wtt@extra@full@columns{#1}} +% \end{macrocode} +% +% \begin{macro}{\next} +% Process our options. We need to expand |\CurrentOption| before +% passing it to \pkgname{keyval}'s |\setkeys| macro, however. +% \begin{macrocode} +\DeclareOption*{% + \edef\next{\noexpand\setkeys{wtt}{\CurrentOption}}% + \next +} +\ProcessOptions\relax +% \end{macrocode} +% \end{macro} +% +% \subsection{Column enlargement} +% \label{sec:col-enlarge} +% +% \begin{macro}{\c@wtt@true@page} +% The \pkgname{wheretotrim} package needs to keep track of the current +% page number. The |page| counter is unsuitable for this task because +% it is really a page \emph{name}. That is, (1)~it is not necessarily +% numeric (e.g.,~it may be a roman numeral while in the document's front +% matter), and (2)~it is not necessarily unique (e.g.,~|page| may be~|1| +% on the title page, abstract, and first page of text). To address this +% limitation we define a |wtt@true@page| counter and, with the help of +% the \pkgname{everyshi} package, prepare for it to be incremented on +% every \TeX\ page shipout. +% \begin{macrocode} +\newcounter{wtt@true@page} +\setcounter{wtt@true@page}{1} +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +\RequirePackage{everyshi} +\EveryShipout{\addtocounter{wtt@true@page}{1}} +% \end{macrocode} +% +% \begin{macro}{\c@wtt@column@num} +% The \pkgname{wheretotrim} package also needs to keep track of the +% current absolute column number. By ``absolute'' we mean a running +% column number that does not reset to~1 on each page. We define a +% |wtt@column@num| counter to hold the current column number, and, +% below, we modify \LaTeXe's |\@makecol| macro to increment it. Note +% that some pages may contain fewer pages than others due to, for +% example, |\clearpage| calls that cause pages to ship out early. +% \begin{macrocode} +\newcounter{wtt@column@num} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\wtt@makecol} +% Before redefining |\@makecol|, we store its old definition in +% |\wtt@makecol|. +% \begin{macrocode} +\let\wtt@makecol=\@makecol +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@makecol} +% |\@makecol| is \LaTeXe's primary mechanism for typesetting a column: +% +% \begin{center} +% \small +% \begin{tabularx}{\linewidth}{@{}rX@{}} +% \hspace{1em}|\@makecol|: +% & Makes the contents of |\box||255| plus the accumulated footnotes, +% plus the floats in |\@toplist| and |\@botlist|, into a single +% column of height |\@colht| (unless the page height has been +% locally changed), which it puts into box |\@outputbox|. It puts +% boxes in |\@midlist| back onto |\@freelist| and restores +% |\maxdepth|. +% \end{tabularx} +% \end{center} +% +% Here, we augment |\@makecol| with code to report the current column +% and page number---and for the user's convenience, page name +% (|\thepage|). Our redefined |\@makecol| then increments the current +% absolute column number and compares it against |\wtt@target@column|. +% If equal, it uses \LaTeXe's |\enlargethispage| to increase the column +% height. Finally, it invokes the original |\@makecol| (stored in +% |\wtt@makecol| to typeset the column. +% \begin{macrocode} +\def\@makecol{% + \PackageInfo{wheretotrim}% + {Column \thewtt@column@num\space is on page + \thewtt@true@page\space (\thepage)}% + \addtocounter{wtt@column@num}{1}% + \ifnum\value{wtt@column@num}=\wtt@target@column + \enlargethispage{\wtt@column@expand}% + \fi + \wtt@makecol +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\wtt@clearpage} +% Before redefining |\clearpage|, we store its old definition in +% |\wtt@clearpage|. +% \begin{macrocode} +\let\wtt@clearpage=\clearpage +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\clearpage} +% \LaTeXe's |\clearpage| macro frustrates the \pkgname{wheretotrim} +% package's attempts to enlarge a given column. We therefore redefine +% |\clearpage| first to perform its ordinary behavior (stored in +% |\wtt@clearpage|, then to check the value of the preceding penalty +% item. If the last penalty is -10001 then this is an opportune time to +% insert an |\enlargethispage| (assuming the current column is equal to +% |\wtt@target@column|). Because |\clearpage| may be called multiple +% times in a row and may be followed by |\@makecol| we restore the +% column counter to its prior value after comparing it to +% |\wtt@target@column| so it is not multiply incremented. +% +% Note that |\cleardoublepage| internally calls |\clearpage| so it is +% sufficient to redefine only |\clearpage|. +% \begin{macrocode} +\gdef\clearpage{% + \wtt@clearpage + \ifnum\lastpenalty=-10001\relax + \addtocounter{wtt@column@num}{1}% + \ifnum\value{wtt@column@num}=\wtt@target@column + \enlargethispage{\wtt@column@expand}% + \fi + \addtocounter{wtt@column@num}{-1}% + \fi +} +% \end{macrocode} +% \end{macro} +% +% Wait until after the |\begin{document}| to redefine |\maketitle| in +% case |\maketitle| is modified before that point. +% \begin{macrocode} +\AtBeginDocument{% +% \end{macrocode} +% \begin{macro}{\wtt@maketitle} +% Before redefining |\maketitle|, we store its old definition in +% |\wtt@maketitle|. +% \begin{macrocode} + \let\wtt@maketitle=\maketitle +% \end{macrocode} +% \end{macro} +% \begin{macro}{\maketitle} +% |\maketitle| is problematic macro for the \pkgname{wheretotrim} +% package because of the way it switches into two-column mode within a +% one-column document (via \LaTeXe's |\twocolumn| macro). For lack of a +% more general solution we redefine |\maketitle| to enlarge the column +% only after typesetting the title and only when in two-column mode. +% Otherwise, the |\enlargethispage| inserted by |\@makecol| already had +% its intended effect. +% \begin{macrocode} + \gdef\maketitle{% + \wtt@maketitle + \if@twocolumn + \ifnum\value{wtt@column@num}=\wtt@target@column + \enlargethispage{\wtt@column@expand}% + \fi + \fi + }% +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Page spillover} +% \label{sec:page-spillover} +% +% Normally, it would not be possible to reduce page count by enlarging +% the last column by any amount. The trick we use here is to add to the +% end of the document a full column or two to make the document spill +% over onto an additional page, as illustrated by +% Figure~\ref{fig:page-spillover}. Thus, enlarging the last column by +% the height of the text it contains will enable a padding column to +% shift into that column and reduce the page count. +% +% \begin{figure}[htbp] +% \centering +% \fbox{^^A +% \begin{minipage}[t][3cm]{1cm} +% \fontsize{2}{2}\selectfont\raggedright +% \vspace{4pt} +% Quisque fringilla, felis eu ultricies commodo, tortor justo lobortis +% est, sit amet molestie ligula lorem at urna. Duis sit amet ligula +% odio, sed varius massa. Duis lacinia odio elit. Nam adipiscing orci +% eu lectus tincidunt porta rhoncus in nisl. Mauris tincidunt mollis +% malesuada. +% \end{minipage} +% \begin{minipage}[t][3cm]{1cm} +% \mbox{} +% \end{minipage} +% } +% \begin{minipage}[t][3cm]{1cm} +% \vspace{1cm} +% \fontsize{36}{36}\ding{220} +% \end{minipage} +% ~^^A +% \fbox{^^A +% \begin{minipage}[t][3cm]{1cm} +% \fontsize{2}{2}\selectfont\raggedright +% \vspace{4pt} +% Quisque fringilla, felis eu ultricies commodo, tortor justo lobortis +% est, sit amet molestie ligula lorem at urna. Duis sit amet ligula +% odio, sed varius massa. Duis lacinia odio elit. Nam adipiscing orci +% eu lectus tincidunt porta rhoncus in nisl. Mauris tincidunt mollis +% malesuada. +% \end{minipage} +% \begin{minipage}[t][3cm]{1cm} +% \rule[-2.9cm]{1cm}{2.8cm} +% \end{minipage} +% } +% \fbox{^^A +% \begin{minipage}[t][3cm]{1cm} +% \rule[-2.9cm]{1cm}{2.8cm} +% \end{minipage} +% \begin{minipage}[t][3cm]{1cm} +% \mbox{} +% \end{minipage} +% } +% \caption{Padding a document with extra columns to induce page spillover} +% \label{fig:page-spillover} +% \end{figure} +% +% \begin{macrocode} +\AtEndDocument{% +% \end{macrocode} +% Add zero, one, or two columns of padding (a |\parbox| of width +% |\linewidth| and height |\textheight|) based on the value of +% |\wtt@extra@full@columns| (set by the \optname{extracols} package +% option). +% \begin{macrocode} + \ifnum\wtt@extra@full@columns>0\relax + \noindent\parbox[t][\textheight]{\linewidth}{% + \rule{\linewidth}{\baselineskip}}\par + \ifnum\wtt@extra@full@columns>1\relax + \noindent\parbox[t][\textheight]{\linewidth}{% + \rule{\linewidth}{\baselineskip}}\par + \fi + \fi +% \end{macrocode} +% Also at the end of the document, output the value of |\baselineskip| +% and the value of |\textheight|, as these are read by the +% \progname{wheretotrim} script. +% \begin{macrocode} + \PackageInfo{wheretotrim}% + {Baseline skip: \the\baselineskip}% + \PackageInfo{wheretotrim}% + {Text height: \the\textheight}% +} +% \end{macrocode} +% +% \iffalse +%</package> +% \fi +% +% \section{Script implementation} +% +% This section presents the commented \LaTeX\ source code for the +% \progname{wheretotrim} Perl script. Read this section if you want +% to learn how the script is implemented. +% +% \iffalse +%<*script> +% \fi +% +% \DocInput{wheretotrim-pl.dtx} +% +% \iffalse +%</script> +% \fi +% +% \Finale +\endinput diff --git a/support/wheretotrim/wheretotrim.ins b/support/wheretotrim/wheretotrim.ins new file mode 100644 index 0000000000..8181629029 --- /dev/null +++ b/support/wheretotrim/wheretotrim.ins @@ -0,0 +1,59 @@ +%% +%% Copyright (C) 2013 by Scott Pakin <scott+wtt@pakin.org> +%% +%% This file may be distributed and/or modified under the conditions of +%% the LaTeX Project Public License, either version 1.3c 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.3c or later is part of all distributions of LaTeX version +%% 2008/05/04 or later. +%% + +\input docstrip.tex +\keepsilent + +\usedir{tex/latex/wheretotrim} + +\preamble + +This is a generated file. + +Copyright (C) 2013 by Scott Pakin <scott+wtt@pakin.org> + +This file may be distributed and/or modified under the conditions of +the LaTeX Project Public License, either version 1.3c 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.3c or later is part of all distributions of LaTeX +version 2008/05/04 or later. + +\endpreamble + +\generate{\file{wheretotrim.sty}{\from{wheretotrim.dtx}{package}}} + +\nopreamble +\nopostamble +\generate{\file{wheretotrim.pl}{\from{wheretotrim-pl.dtx}{script}}} + +\obeyspaces +\Msg{*************************************************************} +\Msg{* *} +\Msg{* To finish the installation, move wheretotrim.sty into a *} +\Msg{* directory searched by TeX and wheretotrim.pl into a *} +\Msg{* directory in your executable search path (and preferably *} +\Msg{* rename the file from wheretotrim.pl to just wheretotrim). *} +\Msg{* *} +\Msg{* To produce the documentation run the file wheretotrim.dtx *} +\Msg{* through LaTeX. *} +\Msg{* *} +\Msg{* Happy TeXing! *} +\Msg{* *} +\Msg{*************************************************************} + +\endbatchfile diff --git a/support/wheretotrim/wheretotrim.pdf b/support/wheretotrim/wheretotrim.pdf Binary files differnew file mode 100644 index 0000000000..eaf44c967c --- /dev/null +++ b/support/wheretotrim/wheretotrim.pdf diff --git a/support/wheretotrim/wheretotrim.sty b/support/wheretotrim/wheretotrim.sty new file mode 100644 index 0000000000..b81ecc5815 --- /dev/null +++ b/support/wheretotrim/wheretotrim.sty @@ -0,0 +1,94 @@ +%% +%% This is file `wheretotrim.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% wheretotrim.dtx (with options: `package') +%% +%% This is a generated file. +%% +%% Copyright (C) 2013 by Scott Pakin <scott+wtt@pakin.org> +%% +%% This file may be distributed and/or modified under the conditions of +%% the LaTeX Project Public License, either version 1.3c 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.3c or later is part of all distributions of LaTeX +%% version 2008/05/04 or later. +%% +\NeedsTeXFormat{LaTeX2e}[1999/12/01] +\ProvidesPackage{wheretotrim} + [2013/05/15 v1.0 Helper package for the wheretotrim script] +\newcommand{\wtt@target@column}{0} +\newcommand{\wtt@column@expand}{0pt} +\newcommand{\wtt@extra@full@columns}{0} +\RequirePackage{keyval} +\define@key{wtt}{column}{\gdef\wtt@target@column{#1}} +\define@key{wtt}{expansion}{% + \xdef\wtt@column@expand{#1\noexpand\baselineskip}% +} +\define@key{wtt}{extracols}{\gdef\wtt@extra@full@columns{#1}} +\DeclareOption*{% + \edef\next{\noexpand\setkeys{wtt}{\CurrentOption}}% + \next +} +\ProcessOptions\relax +\newcounter{wtt@true@page} +\setcounter{wtt@true@page}{1} +\RequirePackage{everyshi} +\EveryShipout{\addtocounter{wtt@true@page}{1}} +\newcounter{wtt@column@num} +\let\wtt@makecol=\@makecol +\def\@makecol{% + \PackageInfo{wheretotrim}% + {Column \thewtt@column@num\space is on page + \thewtt@true@page\space (\thepage)}% + \addtocounter{wtt@column@num}{1}% + \ifnum\value{wtt@column@num}=\wtt@target@column + \enlargethispage{\wtt@column@expand}% + \fi + \wtt@makecol +} +\let\wtt@clearpage=\clearpage +\gdef\clearpage{% + \wtt@clearpage + \ifnum\lastpenalty=-10001\relax + \addtocounter{wtt@column@num}{1}% + \ifnum\value{wtt@column@num}=\wtt@target@column + \enlargethispage{\wtt@column@expand}% + \fi + \addtocounter{wtt@column@num}{-1}% + \fi +} +\AtBeginDocument{% + \let\wtt@maketitle=\maketitle + \gdef\maketitle{% + \wtt@maketitle + \if@twocolumn + \ifnum\value{wtt@column@num}=\wtt@target@column + \enlargethispage{\wtt@column@expand}% + \fi + \fi + }% +} +\AtEndDocument{% + \ifnum\wtt@extra@full@columns>0\relax + \noindent\parbox[t][\textheight]{\linewidth}{% + \rule{\linewidth}{\baselineskip}}\par + \ifnum\wtt@extra@full@columns>1\relax + \noindent\parbox[t][\textheight]{\linewidth}{% + \rule{\linewidth}{\baselineskip}}\par + \fi + \fi + \PackageInfo{wheretotrim}% + {Baseline skip: \the\baselineskip}% + \PackageInfo{wheretotrim}% + {Text height: \the\textheight}% +} +\endinput +%% +%% End of file `wheretotrim.sty'. |