Sunday, September 30, 2007

SEEing LaTeX 11: Typesetting Extended

In installment 10 of this series, I refactored the shell scripts used to compile and view a LaTeX document from SubEthaEdit. To change how a LaTeX document is compiled or viewed, it is now only necessary to change a variable, rather than messing around with the logic of the script. Let's take things a step further.

The relevant variables in the script are LATEX, PRODUCT_TYPE, and VIEWER. Their values in the script shown before were to use latexmk to build a PDF and PDFView to display it. However, other settings are possible. For example, setting
LATEX='pdflatex "$FILE"'
VIEWER='open "$PRODUCT"'
PRODUCT_TYPE=pdf
builds a PDF using pdflatex and shows it in Preview. These settings would be quite suitable for a "vanilla" installation, assuming no extras are installed beyond TeX.

A more flexible approach is to use those vanilla settings as defaults, and allow them to be overridden, giving
LATEX=${SEE_LATEX_COMPILER:-'pdflatex "$FILE"'}
VIEWER=${SEE_LATEX_VIEWER:-'open "$PRODUCT"'}
PRODUCT_TYPE="${SEE_LATEX_PRODUCT_TYPE:-pdf}"
Now, if we run the script from the shell, we can set environment variables to change how the script does its work.

Within SEE, the environment variables will be provided by the AppleScript that runs the script. The change is relatively simple: we extend the string that calls our shell script to also set the environment variables. Since a number of minor changes to the AppleScript have accumulated, I'll show the whole thing:
tell application "SubEthaEdit"
    if exists path of front document then
        if modified of front document then
            try
                save front document
            end try
        end if
        set filePath to path of front document
        set lineNumber to startLineNumber of selection of front document
        set modeResources to resource path of mode of front document
    else
        error "You have to save the document first"
    end if
end tell

set buildScript to prependEnvironment onto (join of {quotedForm for (modeResources & "/Scripts/shell/buildlatex.sh"), quotedForm for filePath, lineNumber} by space)

do shell script buildScript

on seescriptsettings()
    return {displayName:"Typeset and View PDF", shortDisplayName:"Typeset", keyboardShortcut:"@b", toolbarIcon:"ToolbarIconBuildAndRun", inDefaultToolbar:"yes", toolbarTooltip:"Typeset and view the current document", inContextMenu:"no"}
end seescriptsettings

on join of tokenList by delimiter
    set oldTIDs to text item delimiters of AppleScript
    set text item delimiters of AppleScript to delimiter
    set joinedString to tokenList as string
    set text item delimiters of AppleScript to oldTIDs
    return joinedString
end join

on quotedForm for baseString  
    quote & baseString & quote
end quotedForm

to prependEnvironment onto scriptString
    "export SEE_LATEX_COMPILER='latexmk -pdf -quiet \"$FILE\"'; export SEE_LATEX_PRODUCT_TYPE=pdf; export SEE_LATEX_VIEWER='/Applications/Skim.app/Contents/SharedSupport/displayline \"$LINE\" \"$PRODUCT\" \"$FILE\"';" & scriptString
end prependEnvironment

The basic strategy is still to construct a string that calls our shell script and feed it into do shell script. The string is changed in two ways. First, since the shell script now takes a line number as a second argument, I get the current line from SEE and pass it in. Second, and of more immediate interest, I set the environment variables and prepend those to the earlier buildScript. I've simplified the construction by defining join, quotedForm, and prependEnvironment handlers.

PrependEnvironment sets the SEE_LATEX_COMPILER, SEE_LATEX_PRODUCT_TYPE, and SEE_LATEX_VIEWER environment variables, customizing the behavior of the shell script. In the above AppleScript, I have set the environment so that latexmk and Skim are used to build and view a PDF.

Sunday, September 23, 2007

SEEing LaTeX 10: Typesetting Refactored

Let's unify the scripts shown in the preceding installment. The approach I'll take is to move everything that I can into shell variables. My earlier script
PATH=/usr/texbin:/usr/local/bin:$PATH
export PATH

cd "$(dirname "$1")"
latexmk -pdf -quiet "$(basename "$1")"
PRODUCT="$(basename "$1" .tex).pdf"

if [ -s "$PRODUCT" ]
then
    open -a PDFView.app "$PRODUCT"
fi

becomes
PATH=/usr/texbin:/usr/local/bin:$PATH
export PATH

LATEX='latexmk -pdf -quiet "$FILE"'
PRODUCT_TYPE=pdf
VIEWER='open -a PDFView.app "$PRODUCT"'

FILE="$(basename "$1")"
DIRNAME="$(dirname "$1")"
LINE="$2"
PRODUCT="$(basename "$1" .tex).$PRODUCT_TYPE"

cd "$DIRNAME"
eval $LATEX
if [ -s "$PRODUCT" ]
then
    eval $VIEWER
fi

All I've really done is to move some things around; note the use of eval to allow shell variables to be referred to before they are defined. The script behaves the same. That's fine, the goal for the moment it just to refactor for some later behavioral changes, not to make those behavioral changes now.

The behavior of the script provided by Al Kasprzyk can be produced by the above script by changing just the line defining VIEWER. That line becomes
VIEWER='/Applications/Skim.app/Contents/SharedSupport/displayline "$LINE" "$PRODUCT" "$FILE"'

Nothing else need be changed; we now can compile our LaTeX file and see the resulting PDF in Skim.

To incorporate this into the SubEthaEdit mode, we do need to have the AppleScript pass the line number to the shell script. For now, I'll omit showing the AppleScript, since the essential changes are minor. I'll take a closer look at the AppleScript next time.

Update: I've renamed the LINENUM variable in the script to LINE. This is just to be more symmetric with how applications like PDFView and Skim work with pdfsync.

Tuesday, September 18, 2007

SEEing LaTeX 9: Typesetting Revisited

Earlier, I discussed typesetting a LaTeX document from SubEthaEdit and sending it to PDFView. PDFView works fine, but is no longer being developed; the developer recommends Skim.

Replacing PDFView with Skim is something that I've been thinking about for a while, but it hasn't been a high priority. Happily, Al Kasprzyk provided a script to make that replacement in a comment to the earlier post. For convenience, I'll reproduce it here, with syntax highlighting:
cd "`dirname "$1"`"
latexmk -pdf -quiet "`basename "$1"`"
pdfName=$(basename "$1" tex)pdf

if test -s "$pdfName"; then
/Applications/Skim.app/Contents/SharedSupport/displayline $2 "$pdfName" "$1"
fi

Perhaps the greatest difference is that Al uses the displayline script included with Skim, instead of just opening the PDF created from the LaTeX document. Thus, instead of having latexmk open the preview, he handles it himself. behaviorally, the preview is shown in Skim, with the current line selected in SEE being visible in Skim.

We can rewrite my earlier script to take a similar approach. Without either following Al's script exactly or avoiding looking at it, I came up with this:
PATH=/usr/texbin:/usr/local/bin:$PATH
export PATH

cd "$(dirname "$1")"
latexmk -pdf -quiet "$(basename "$1")"
PRODUCT="$(basename "$1" .tex).pdf"

if [ -s "$PRODUCT" ]
then
    open -a PDFView.app "$PRODUCT"
fi

The differences between the two scripts are minor. Apart from some formatting choices, there are just a few differences. First, I set the PATH environment variable. I'm pretty sure Al must have done so as well, but he didn't include it in his comment. Second, I have an extra layer of quoting for what I called the PRODUCT; based on restrictions for LaTeX file names, these are superfluous. Third is the obvious and essential difference of which viewer we call.

Right now, there is no change in behavior between my earlier script and the one I present above. However, I no longer need to define anything in a .latexmkrc file, which is helpful in making a portable LaTeX mode. Better, though, is that the obvious parallels between Al's script and mine can be taken advantage of. Next time.

No Real Surprise

I've registered SubEthaEdit. My trial period was finally at an end, and the decision was a no-brainer. As it turns out, my "thirty day" trial period was close to three months long; each time SEE was updated, the trial period was reset. Very fair of the Coding Monkeys, I think.