#!/bin/bash # man2pdf / man2lpr: render the specified 'man' "page" as PDF; # in the current directory; then browse or print # By Lee Rothstein, 2008-05-17, 01:44 PM # Type 'man2pdf -h', at a command prompt for a complete command # description #---------------------------------------------------------------- # Defaults & Other Initializations PName="$(basename $0)" # Name that command was invoked with Version="0.3.1.002" # Version number of this command UpDate="2010-10-19, 17:02:24" # Date of Last Update PaperFormat=letter # Format of output paper; for those not # Metricly challenged (as are USoids), this will # require change. See 'man psnup' for other choices, # and http://en.wikipedia.org/wiki/ISO_216 for # details. ManFileOK=0 # Does the 'man' "page" file exist, okay? 0 == True; # (that is, 'man' "pages" are innocent until proven # AWOL) BrowseFlag=false # Browse the specified page? GzipFlag=false # Was original 'man' "page" source file # 'gzip'ed? HelpFlag=false # Output help? InstalledFlag=false # Are we processing an installed 'man' # "page", or just a 'man' "page" file, by # path? Actually, this flag can be fooled, by # specifying an installed 'man' "page" via # full path/file spec. NoBrowseFlag=false # Allow NoBrowse'ing to be precedent over # Browse'ing, if true OverWriteFlag=false # Should the original 'man' "page" be # overwritten following normalization? Even # if this flag is set true, overwrite will # only occur if the page has been specified # by full file spec (pathname/filename). PrintFlag=false # Print the specified 'man' "page"? SaveFlag=false # Save the normalized 'man' "page" source # (nroff/troff (groff) SavePath="./" # Default path to save PDF rendering of 'man' # "page" TempFlag=false # Is master temporary directory going to be # the home of the normalized 'man' "page" and # the 'man' "page" rendered to PDF VersionFlag=false # Print version? NUp=1 # Number of pages of output to be printed per # page; default is 1, portrait; alternative is 2 up, # landscape. WhereFlag=false # Output to stdout the full path name of the # 'man' "page" file being processed? ShortOpts="2bhnop:Ptvw" # Short options specification for 'getopt' LongOpts="2-up,browse,help,no-browse,\ over-write,path:,print,temp,version,where" # Long options specification for 'getopt' Topic="" # Topic of 'man' "page" to be retrieved, if it is # retrieved from installed 'man' "pages" SectNum="" # Section number of 'man' "page" to be retrieved, if # it is retrieved via Topic and Section Number from # installed 'man' "pages" #---------------------------------------------------------------- version () { echo "${PName} -- v${Version}, $UpDate" } # If user has not defined pager app, use 'less' if [[ -z "$PAGER" ]] ; then export PAGER='less' fi # HelpEm () -- Help & exit with an appropriate exit code HelpEm () { $PAGER << !!!EOF!!! NAME man2pdf | man2lpr - Find 'man' "page", render it to PDF, and allow it to be browsed or printed, or a combination thereof SYNOPSIS man2pdf [-V|--version] [-h|--help] man2lpr [-V|--version] [-h|--help] man2pdf [] [] man2lpr [] [] man2pdf [] man2lpr [] DESCRIPTION The 'man2pdf' and 'man2lpr' commands find a 'man' "page" file, render it to PDF, and allow it to be browsed or printed (hardcopy output). These commands will find the 'man' "page" file based on either a topic and an optional section spec (for installed 'man' "pages"), or based on a 'man' "page" file path spec. Depending on options, these commands will (a) list the 'man' "page" files names to be processed, convert the 'man' "page" to pdf format, (b) render the 'man' "page" as a PDF file, (c) browse the file using the user's specified PDF browser, or (d) print the PDF file. USAGE : Note: all options specified apply to a single man page topic or a single 'man' "page" file specification, and must appear before the man page specification! Options to $PName, Version $Version are: -2 | --2-up -- Output the 'man' "page" pages, 2 abreast in landscape format. (While the printed output is 2 abreast landscape, the page is presented to the PDF browser rotated 90 degrees counter-clockwise. Adobe and other PDF browsers, however, allow rotation on screen.) -b | --browse -- Browse the page -h | --help -- Output help, and exit; help precludes all other options and processing. -n | --no-browse -- No Browsing. If this option is selected regardless of whether the -b|-- browse option or 'man2pdf' name is used, no browsing will be done. -o | --over-write -- Overwrite original 'man' nroff/troff (groff) input file following normalization. This option only applies to a 'man' "page" file that has been specified by path/file name rather than topic/section. In no instance, will an "installed" 'man' "page" be overwritten following normalization. In the case of this flag "installed" refers to how the 'man' "page" was located, not just whether it was located in an official \$MANPATH directory sub-tree. If a user were to invoke this flag and specify via path/'man' "page" file name an installed 'man' "page", it would indeed get overwritten. -p | --path -- to place PDF rendering file (output). The default condition is to save the PDF to the current working directory. To avoid saving the PDF file, use the -t/--temp option. -P | --print -- Print (hardcopy output) the page to the default printer. -t | --temp -- Temporary copy of the PDF rendered version of the 'man' "page" will be kept in the master temporary directory ($TMPDIR, /tmp, etc). If both this -t/--temp option and the -p/--path option are both requested, the -p/--path will be ignored. -V | --version -- Print version of \'$PName\' & exit. NOTES ON OPERATION * A page can be both browsed and printed (hardcopy output), at the same time (through this command). However, since most PDF browsers, such as Adobe Acrobat reader, or GSview, will allow printing at the time of browsing, the primary place for the print option is in background printing of one or more pages. * If command is invoked as 'man2lpr', the required operation is print (-P|--print), although browse (-b|--browse) can also be requested via option. * Print (hardcopy output) always occurs in the background, asynchronously to this command. * Print (hardcopy output) assumes that you have a printer capable of bit-map printing and compatible with Ghostgum, but since Ghostgum ('gsprint') is a Windows application, any Windows installed printer should work. * If no path is specified for the rendered PDF file, the command 'tmpdir' is used to find a temporary directory. * Any argument is determined to be a 'man' "page" file, if it has an appropriate file name extension, and a 'man' "page" topic, otherwise. * Only one 'man' "page" can be specified on the command line. If a topic is specified, but no section number is given, the 'man' "page" for the lowest 'man' section number available is retrieved, as is done with the 'man' command itself. * The help (-h|--help) or version (version (-v|--version) option must be requested first, and only that option will be honored, regardless of what else follows it. * If $PName is invoked as man2pdf, the default operation will be to browse, unless the -n|--non-browse option is selected, in which case the PDF rendering will be created, but won't be presented for viewing. EXAMPLES 1. Save btssb.1.pdf to current dir $ man2pdf btssb.1 Results: * man2pdf will process the 'man' "page" file 'btssb.1', in the current directory. As always, it will normalize the input file (btssb.1), for later 'man' Postscript processing. It will not overwrite the original file. * It will, on exit, save the PDF file to, 'btssb.1.pdf' in the current working directory * The file will be presented to the user with the user's default browser. 2. Save echo.3.pdf to a personal cache of PDFs $ man2pdf --SaveFile ~/PDF_Cache 3 echo Results: * Render the 'man' "page" source for the library 'curs_inopts(3X)' [which contains the function 'echo()']. as PDF. * Store the PDF file in the sub-directory, PDF_Cache, of the user's home directory. * Browse the PDF file with the user's preferred PDF browser. 3. Print the echo (1) man page in PDF format $ lpr2pdf -2t echo Results: * Render the 'man' "page" source for the echo command as PDF. Adjust the rendering so that it prints portrait pages 2 up in landscape output. __________ __________ | | | | _________________ | | | | | ------ ------ | | 1 | | 2 | ==> | | 1 | | 2 | | | | | | | | | | | | | | | | | ------ ------ | ---------- ---------- ----------------- * Store the PDF file in the user's master temporary directory, whether it is defined by '$TMPDIR', '/tmp', or '$TEMP'. * Note that 'man2lpr' ('man2pdf') will not delete the temporary PDF file that it creates; it assumes that the user has routines that carry this out. * Print the PDF file to the default Windows printer. VERSION $(version) ENVIRONMENT AND DEPENDENCIES This command depends on the Windows (NOT Cygwin) versions of the Ghostscript and Ghostgum packages. While it uses Ghostgum, and Ghostgum supplies the PDF browser, GSview, the command will invoke the user's preferred browser for displaying the generated PDF file. It also requires the following commands from Cygwin packages: * \$PAGER, if the environment variable is defined, otherwise 'less' * bash -- command shell and scripting language * fmt -- indent and wrap lines for pretty help messages * gawk -- string processing scripting language * getopt -- external (to Bash) command that normalizes command line options * man -- finds the 'man' "page" and invokes 'groff' and the right options and browser * ps2pdf -- converts the Postscript generated by man to PDF so that it can be browsed and printed on Windows * psnup -- converts Postscript pages to a two up format which outputs portrait pages two across in a single landscape page * sed -- stream editor for "massaging" strings * xargs -- convert standard input lines to arguments later in a pipeline This command also requires a number of other utility scripts from the Club-G package, including: * fn_ext_last * man_blrm * pdf2lpr * tmpdir The above scripts also invoke other commands. BUGS AND LIMITATIONS $PName only operates on 'man' "pages" that are contained in a single file. This means that $PName will fail on any page that contains a '.so' 'groff' command. Among others, it will fail miserably on 'zshall(1)'. This command requires (is bent to) the Eaton, et al. 'man' version (1.6e), and would require rewriting for the seemingly more advanced 'man' command that comes with the 'db-man' package (which is, for example, packaged with Ubuntu Linux). !!!EOF!!! exit 0 } # end of HelpEm() WarnAbort () { echo "${PName}: ** error **:" echo "$2"|sed 's/ +/ /g'|fmt -50|xargs --max-lines=1 echo " " echo "" echo " For help, type \"${PName} -h\"" exit $1 } #------------------------------------------------------------------ # Preliminaries case $1 in -h|--help) HelpEm ;; -v|--version) version exit 0 ;; esac TmpPath="$(tmpdir -l /tmp)" if [[ "$?" -ne 0 ]] ; then WarnAbort 99 "Neither \$TMPDIR, \$TEMP, nor /tmp reference a \ valid directory that can be used for temporary files and \ directories. Therefore execution has been aborted." fi # ----------------------------------------------------------------- # Validate and normalize options requested # Does the available version of 'getopt' supports long options? if $(getopt -T &> /dev/null) ; [ $? -eq 4 ] ; then echo "'getopt' is correct version!" >/dev/null else WarnAbort 98 "'getopt' command is obsolete! Update 'getopt'!" fi # Store results in $OptionsParsed , in case 'getopt' "barfs" OptionsParsed=$(getopt --options "$ShortOpts" --longoptions \ "$LongOpts" -n "$PName" -- "$@") error_code=$? if [[ $ErrorCode -ne 0 ]] ; then echo "$PName: * error *:" echo " Error in options passed to 'getopt'. Error code is:" echo " $ErrorCode ." HelpEm $ManFileOK fi # Reset args to this command terminating options with '--' eval set -- "$OptionsParsed" #------------------------------------------------------------------ # Process options and set all flags based on: # * Name, which this command is invoked by # * Command line options regardless of whether long or short # option format # Set Print Flags based on command name used case "$PName" in man2lpr) PrintFlag=true ;; man2pdf) BrowseFlag=true ;; man2pdf2) BrowseFlag=true NUp=2 ;; *) WarnAbort 200 "Script renamed to an unrecognized name!" ;; esac # Set all other flags required by options while [[ "$1" != "--" ]] ; do case $1 in -2|--2-up) # Print the output pages 2 up? NUp=2 shift ;; -b|--browse) # Browse the PDF file after creation if [[ $NoBrowseFlag == false ]] ; then BrowseFlag=true fi shift ;; -h|--help) # Provide help and nothing else HelpFlag=true shift ;; -n|--no-browse) # ... NoBrowseFlag=true BrowseFlag=false shift ;; -o|--over-write) # After normalizing the 'man' "page" source # overwrite the original file OverWriteFlag=true shift ;; -p|--path) shift SavePath="$1" shift ;; -P|--print) PrintFlag=true shift ;; -t|--temp) TempFlag=true SavePath=$(tmpdir -l /tmp) shift ;; -v|--version) VersionFlag=true shift ;; -w|--where) WhereFlag=true shift ;; *) WarnAbort 99 "$PName: Error in option processing escaped \ 'getopt' sanity checking. Option being processed is ${1}." ;; esac done shift # All options and terminating '--' have been processed and # purged from the command line arguments # Sanity Checking if [[ "$TempFlag" == true && "$SaveFlag" == true ]] ; then SaveFlag=false SavePath=$(tmpdir -l /tmp) fi #------------------------------------------------------------------ # Script status # Version? That's all you get. if [[ "$VersionFlag" == true ]] ; then version echo "" exit 0 fi # Help -- If you ask for help, that's all you get if [[ "$HelpFlag" == true ]] ; then HelpEm ; fi #------------------------------------------------------------------ # Get usable 'man' "page" spec # All others arguments should have been eaten ('shift') in # options processing # First, assume that 'man' "page" file will come from an # installed "page" case $# in 0) WarnAbort 2 "Requires 1 or 2 arguments, other than options \ (and their arguments), only." ;; 1) Topic=$1 SectNum="" ;; 2) Topic=$2 SectNum=$1 ;; *) WarnAbort 3 "Requires 1 or 2 arguments, other than options \ (and their arguments), only." ;; esac # At this point, it is still not clear whether $Topic is a 'man' # page topic, or a 'man' "page" file specification # Determine location of 'man' "page" file whether specified by # path/name, or Section Number and Topic if [[ -z "$SectNum" ]] ; then if [[ -z "$(fn_ext_last $Topic)" ]] ; then InstalledFlag=true ManPageFile="$(man -w $Topic)" ManFileOK=$? else InstalledFlag=false ManPageFile="$Topic" # This version of 'man' will only process a file in the # current directory if it is specified with the './' preamble if [[ ! "$ManPageFile" =~ / ]] ; then ManPageFile="./${ManPageFile}" fi if [[ ! -f "$ManPageFile" ]] ; then ManFileOK=255 fi fi else InstalledFlag=true ManPageFile="$(man -w $SectNum $Topic)" ManFileOK=$? fi # Show where 'man' "page" file came from if required if [[ "$WhereFlag" == true ]] ; then echo "${PName}: Processing: $ManPageFile !" fi if [[ $ManFileOK -ne 0 ]] ; then WarnAbort $ManFileOK "$ManPageFile does not exist." fi #------------------------------------------------------------------ # Process 'man' "page" file # Create a temporary copy of the 'man' "page" cp "$ManPageFile" "$TmpPath" ManPageFileBN="$(basename $ManPageFile)" TmpFile="${TmpPath}/${ManPageFileBN}" # Decompress temporary copy of the 'man' "page" if necessary if [[ "$ManPageFile" =~ \.gz$ ]] ; then GzipFlag=true gunzip "$TmpFile" ManPageFileBN=$(sed 's/\.gz$//'<<<"$ManPageFileBN") TmpFile="${TmpPath}/${ManPageFileBN}" fi # Remove superfluous blank lines from 'man' "page" groff source # that interfere with Postscript/PDF rendering man_blrm "$TmpFile" # Set the PDF file path to wherever the user requested PdfFile="${SavePath}/${ManPageFileBN}.pdf" # 'man -t' -- render in Postscript # 'ps2pdf' (part of the Cygwin -- not Windows Ghostscript # package) -- convert Postscript to PDF if [[ $NUp -eq 1 ]] ; then man -t "$TmpFile"|ps2pdf - "${PdfFile}" 2>/dev/null else man -t "$TmpFile"|psnup -2 -d -p${PaperFormat} 2> \ /dev/null|ps2pdf - "${PdfFile}" 2>/dev/null fi if [[ "$BrowseFlag" == true ]] ; then cygstart "${PdfFile}" fi if [[ "$PrintFlag" == true ]] ; then pdf2lpr "${PdfFile}" fi