Thursday, July 5, 2007

SEEing LaTeX 6: Typesetting

Up to this point, I've written about composing LaTeX in SubEthaEdit, but not said much about invoking LaTeX to typeset the document. What I'd been doing was to run latexmk in a Terminal. Latexmk has the nice feature that it will run latex repeatedly until the document is ready, instead of having to do it repeatedly yourself. It also handles bibtex. Maybe best of all, you can set it to a continuous preview mode, which sets latexmk to watch your LaTeX file, recompiling it whenever you save changes you make; if you use a PDF previewer, like PDFView, that can reload the document whenever it changes, you can just set latexmk going and pretty much always have an up-to-date PDF ready to examine.

More typical is to have a command in your editor that typesets the LaTeX document and sends the resulting PDF (or, if you must, dvi) file to a previewer. While it may lack the sheer coolness of the continuous preview that latexmk provides, it's a lot more natural and probably just as effective. Let's add that to SubEthaEdit now. Let's also start saving some typing by referring to it as SEE.

The strategy will be to write a shell script to handle the typesetting, and an AppleScript to get needed information from SEE and feed it to the shell script. The shell script is easy, but does something that would be pretty awkward in AppleScript. The combination of shell and AppleScript is a useful way to take advantage of the power of Unix with data from an application.

The shell script takes a single argument that is the path to the LaTeX file. First, we set a PATH that includes the directories for TeX (TeXlive, in my case, so /usr/texbin) and latexmk (/usr/local/bin/). The rest of the script then just changes to the directory for the LaTeX file, and calls latexmk to do the typesetting. The script is simple:

export PATH

cd "`dirname "$1"`"
latexmk -pv -pdf -quiet "`basename "$1"`"

The trickiest part was getting the quoting right for the arguments to cd and latexmk. They may look excessive, but all those quotes are necessary!

A copy of the script goes into the LaTeX mode bundle. I put it at ${RESOURCES}/Scripts/shell/, where RESOURCES refers to the Resources folder in the bundle. The executable bit of the script is set.

Now, let's look at the AppleScript. It's first going to save the document, if possible and necessary, since we'll normally want to see the effect of changes to a LaTeX document. We then extract the path to the file from the document, and the path to the shell script from the mode. These we glue together into a string that invokes the shell script on the document, and then we use do shell script to run it. Here's the script:
tell application "SubEthaEdit"
    if exists path of front document then
        if modified of front document then
                save front document
            end try
        end if
        set latexFilePath to path of front document
        set modeResources to resource path of mode of front document
        error "You have to save the document first"
    end if
end tell

set buildLatexScript to quote & modeResources & "/Scripts/shell/" & quote & space & quote & latexFilePath & quote

do shell script buildLatexScript

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

The structure of the tell block is based on the discussion at the end of the SEE scripting page. The string we construct to invoke the shell script once again has a lot of quoting; once again, it's necessary!

The boilerplate in the seescriptsettings handler sets up a keyboard shortcut and a toolbar icon. I just used ToolbarIconBuildAndRun for the toolbar icon image; I don't know that it's a great choice, but I didn't have anything more appropriate. For the keyboard shortcut, I went with command-B. It seemed appropriate, since the same shortcut is used in the Objective-C mode for compilation. The Java mode uses command-B for syntax checking, but it's inconsistent with several others that use command-control-B for syntax checking. I'd really liked to have used command-R to override the HTML preview, which is useless for the LaTeX mode, but it doesn't work; SEE just refuses to assign command-R to the script. Consistency for different modes is important, so command-B it is.

The combination works well, but isn't flawless. I have several items set in my .latexmkrc, which aren't, at the moment, handled in the LaTeX mode bundle. Also, latexmk seems to give an error due to bibtex if you have a bibliography file called without any citations; I haven't established the exact conditions, but they match a new LaTeX document with the templates I use! Also, the AppleScript is a little slow if the document needs to be saved. In fact, there is no need for testing whether the document has been modified; I only check because the try block for saving makes the script conspicuously slower.

Those are relatively minor problems. However, there is a conceptual problem that has become apparent: SubEthaEdit provides no means to define a mode-dependent environment. Amongst other problems, it makes it quite hard to write scripts like the ones shown in this post that won't break on someone else's computer. It would be quite appropriate to simply include latexmk in the bundle, but that obviously won't work for TeX! So, what to do? Put in paths for every TeX distribution I can think of? It's simply bad design, and I don't see any easy way for users to adjust the environment to suit their own systems (and, no, I don't think an environment.plist file is an acceptable solution).


Al Kasprzyk said...

I really like your ideas. I'm using "Skim" for my pdf viewer. I've changed your script slightly to take advantage of Skim's "displayline" script. Now I pass the current line number as well as the file path, and get Skim to jump to the correct line after the file compiles.
cd "`dirname "$1"`"
latexmk -pdf -quiet "`basename "$1"`"
pdfName=$(basename "$1" tex)pdf
if test -s "$pdfName"; then
/Applications/ $2 "$pdfName" "$1"

Michael said...

That's quite nice! As it turns out, I've been meaning to take a look at using Skim instead of PDFView, so this saves me a bit of effort.

Looking at your script, it looks like you nicely avoid the .latexmkrc file that I complained about. Are you using one at all?

Al Kasprzyk said...

Thanks for the hat tips in your later posts. Yes, I'm afraid that I'm using a .latexmkrc file. I can't see any obvious way to avoid this. It reads:
$pdf_previewer = 'open -a';
$pdfupdatemethod = 0;
$pscmd = 'ps -ww -u $ENV{USER}';

Michael said...

My pleasure to acknowledge your suggestions. Credit where credits due, after all.

I have essentially the same things in my .latexmkrc file. As I understand it, it is not necessary for the scripts we've been discussing. The three entries are used to show and update the PDF in a previewer, which is being handled separately in the shell script.

When I rename my .latexmkrc, I can still compile a LaTeX file and view it in Skim without any trouble. Of course, the file is still very desirable for command line use of latexmk.