Search code examples
bashsyntax-highlightingenscript

Prepend header to file without changing the file


Background

The enscript command can apply syntax highlighting to various types of source files, including SQL statements, shell scripts, PHP code, HTML files, and more. I am using enscript to generate 300dpi images of source code for a technical manual to:

  • Generate content for the book based on actual source code.
  • Distribute the source code along with the book, without any modification.
  • Run and test the scripts while writing the book.

Problem

The following shell script performs the conversion almost as desired:

#!/bin/bash

DIRNAME=$(dirname $1)
FILENAME=$(basename $1)

# Remove the extension from the filename.
BASENAME=${FILENAME%%.*}
FILETYPE=${FILENAME##*.}

LIGHTGRAY="#f3f3f3"

enscript --escapes --color -f Courier10 -X ps -B -1 --highlight=$FILETYPE \
  $2 -h -o - $1 | \
  gs -dSAFER -sDEVICE=pngalpha -dGraphicsAlphaBits=4 -dNOPAUSE -r300 \
  -sOutputFile=$BASENAME.png -dBackgroundColor=16$LIGHTGRAY > /dev/null && \
  convert -trim $BASENAME.png $BASENAME-trimmed.png && \
  mv $BASENAME-trimmed.png $BASENAME.png

The problem is that the background is not a light gray colour. According to the enscript man page, the --escapes (-e) option indicates that the file (i.e., $1) has enscript-specific control sequences embedded within it.

Adding the control sequences means having to duplicate code, which defeats the purpose of having a single source.

Solution

The enscript documentation implies that it should be possible to concatenate two files together (the target and a "header") before running the script, to create a third file:

^@shade{0.85}                  -- header line
#!/bin/bash                    -- start of source file

Then delete the third file once the command completes.

Questions

Q.1. What is a more efficient way to pipe the control sequences and the source file to the enscript program without using a third file?

Q.2. What other options are available to automate syntax highlighting for a book, while honouring the single source requirements I have described? (For example, write the book in LyX and use LaTeX commands for import and syntax highlighting.)


Solution

  • Q1 You can use braces '{}' to do I/O redirection:

    { echo "^@shade{0.85}"; cat $1; } |
    enscript --color -f Courier10 -X ps -B -1 --highlight=$FILETYPE $2 -h -o - |
    gs -dSAFER -sDEVICE=pngalpha -dGraphicsAlphaBits=4 -dNOPAUSE -r300 \
       -sOutputFile=$BASENAME.png -dBackgroundColor=16$LIGHTGRAY > /dev/null &&
    convert -trim $BASENAME.png $BASENAME-trimmed.png &&
    mv $BASENAME-trimmed.png $BASENAME.png
    

    This assumes that enscript reads its standard input when not given an explicit file name; if not, you may need to use an option (perhaps '-i -') or some more serious magic, possibly even 'process substitution' in bash.

    You could also use parentheses to run a sub-shell:

    (echo "^@shade{0.85}"; cat $1) | ...
    

    Note that the semi-colon after cat is necessary with braces and not necessary with parentheses (and a space is necessary after the open brace) - such are the mysteries of shell scripting.

    Q2 I don't have any alternatives to offer. When I produced a book (20 years ago now, using troff), I wrote a program to convert source into the the necessary markup, so that the book was produced from the source code, but by an automated process.

    (Is 300 dpi sufficiently high resolution?)

    Edit

    To work-around the enscript program interpreting the escape sequence embedded in the conversion script itself:

    { cat ../../enscript-header.txt $1; } |