BITESIZED: How do I pretty print source code?
Background
I've often run in situations where I'd like to print some source code, and found that just piping the file to lpr leaves you with same output that our forefathers enjoyed ... hardly fitting for modern PostScript laser printers (likely simulated through GhostScript). Thus began my quest to find a way to "pretty-print" source code files.
The tool to use is called enscript, which is designed to convert text files into PostScript. Among it's many options (the man page is 25 pages long!) are a few that will be absolutely perfect for pretty-print source code:
- line numbering
- highlight bars
- marking of wrapped lines
- customizable page headers
- syntax highlighting
Whoah, that last one almost snuck by and it was a doozie! By calling the states program, enscript can syntax highlight (by default) Ada95, ASM, AWK, C, ChangeLog files, C++, diffs, Delphi, elisp, Fortran, Haskell, HTML, idl, Java, JavaScript, email (!), Makefiles, nroff, ObjC, Pascal, Perl, PostScript, Python, Scheme, shell scripts, SQL, Synopsys, TCL, Verilog, vhdl, Visual Basic, and you can even add syntax recognition rules for virtually any type of file yourself! It's even smart enough to figure out what type of file it is on it's own, which simplifies our job by a big chunk.
In order to create our source code pretty-printer, then, we'll need to create a small script that calls enscript and passes it all the options that we want to use. Let's call it pr2src for "print (in 2 columns) some source code". We'll also need to create a custom header file (a .hdr file, for enscript) that will print the file name (with full path), date and time it was printed, and a page count at the top of every page.
Two pr2src scripts are presented, and a sample .hdr file, called tillman.hdr, is also demonstrated. The output is rather pretty looking, although you might want to tweak the darkness of the gray highlights to match your printer, since my tends to print somewhat lightly.
pr2src (generally goes in /usr/local/bin)
This is the original Perl script to perform the pretty-printing. It calls on enscript to do the actual work.
#!/usr/bin/perl
# Copyright 1999 Tillman Hodgson
# Based on a similar script by Mike MacNeill
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
IN: {
$fn = shift;
if (-e "$fn") {
&PrintMe;
} else {
print "You want me to print WHAT? \n";
exit(1);
}
}
sub PrintMe {
system( "/usr/bin/nenscript -E -C -2r -H4 -T4
--fancy-header=tillman
--highlight-bar-gray=0.87
--mark-wrapped-lines=arrow
-fCourier6.6 -P lp0 $fn" );
}
pr2src (alternate version, thanks to Scott Wunsch)
This alternate version of the pr2src script was developed by Scott Wunsch, fearless leader of the LOSURS. He took a different angle of attack at the problem; while it still calls enscript to do the dirty work, he used a shell script to put things together. One advantage of his approach is that you can pipe the output of a command to this script and it will attempt to print it, rather than having to use the command with a file name as a parameter explicitly.
#!/bin/sh
#
# Passes documents through nenscript to produce nicely formatted
# PostScript output on the printer. If no filename is given, it
# reads standard input. It is expected that you want to print to
# the "lp0" queue. If invoked as "pr2me", it will print two
# pages on one.
#
# Written by Scott Wunsch, 29 February 2000
# Based on original prme and pr2me scripts by Tillman Hodgson
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
SWITCHES="-E -C -2r -H4 -T4 --fancy-header=tillman \
--highlight-bar-gray=0.87 \
--mark-wrapped-lines=arrow-fCourier6.6 -P lp0"
[ `basename $0` = pr2me ] && SWITCHES="$SWITCHES -2r"
if [ -n "$1" ]; then
if [ -e "$1" ]; then
SWITCHES="$SWITCHES $1"
else
echo "What's a $1?"
exit
fi
fi
nenscript $SWITCHES
tillman.hdr (generally goes in /usr/share/enscript)
% Tillman Hodgson Source Code header
% Copyright 2000 Tillman Hodgson
% Author: Tillman Hodgson - tillman@hodgsonhouse.com
% =-=-=-=-=-=-=-=-=-=-
% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License as
% published by the Free Software Foundation; either version 2,
% or (at your option) any later version.
% =-=-=-=-=-=-=-=-=-=-
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
% =-=-=-=-=-=-=-=-=-=-
% You should have received a copy of the GNU General Public
% License along with this program; see the file COPYING. If not,
% write to the Free Software Foundation, 59 Temple Place - Suite
% 330, Boston, MA 02111-1307, USA.
% =-=-=-=-=-=-=-=-=-=-
% Format: datestr Printed on %D{%a %b %d, %Y at %l:%M%p}
% Format: pagestr $%
% Format: pagestr2 /$=
% HeaderHeight: 48
% Fonts.
/Helvetica-BoldOblique /helvetica-encoded MF
/SmallFont /helvetica-encoded findfont 12 scalefont def
/BigFont /helvetica-encoded findfont 18 scalefont def
/RBox { % x y w h r -> -
/r exch def
/h exch def
/w exch def
/y exch def
/x exch def
x y r add moveto
x y h add x w add y h add r arcto 4 {pop} repeat
x w add y h add x w add y r arcto 4 {pop} repeat
x w add y x y r arcto 4 {pop} repeat
x y x y h add r arcto 4 {pop} repeat
closepath
} def
/radius 8 def
/do_header { % print mp header
gsave
.5 setlinewidth
% header
d_header_x d_header_y 8 add d_header_w 65 sub 40 radius RBox
gsave
.83 setgray fill
grestore
stroke
SmallFont setfont
datestr dup stringwidth pop d_header_x d_header_w 75 sub add exch sub
d_header_y 22 add moveto show
d_header_x 10 add d_header_y 22 add moveto (File: ) show
fname show
d_header_x d_header_w add 60 sub d_header_y 8 add 60 40 radius RBox
stroke
SmallFont setfont
(Page) dup stringwidth pop 60 exch sub 2 div
d_header_x d_header_w add 60 sub add d_header_y 33 add moveto show
BigFont setfont
pagestr dup stringwidth pop 60 exch sub 2 div
d_header_x d_header_w add 75 sub add d_header_y 13 add moveto show
SmallFont setfont
pagestr2 dup stringwidth pop 60 exch sub 2 div
d_header_x d_header_w add 40 sub add d_header_y 13 add moveto show
grestore
} def

