[Tex/LaTex] How to compile a document with the pstool package using the pdflatexmk script

errorslatexmkpdftexpsfrag

To use psfrag with pdflatex I use the pstool package. Using TeXShop 3.25 I can compile the two minimal working examples given in this and this answer when I use the regular LaTeX typesetter.

The MWE from the first answer looks as follows:

% compiled by `pdflatex --shell-escape` enabled
\documentclass{book}
\listfiles % to show the list of packages loaded in .log file
\usepackage[crop=pdfcrop,process=all,cleanup={.tex,.dvi,.ps,.pdf,.log}]{pstool} 
% Good options for pstool package
\EndPreamble % Will help to pick the right preamble in pstool auxillary process 
\usepackage{color}
\begin{document}
\begin{figure}
\centering
\includegraphics[width=0.8\linewidth]{trial.eps}
\caption{Tagged eps image}
\end{figure}
\begin{figure}
\centering
\psfragfig[width=0.8\linewidth]{trial}{\color{red}
  \psfrag{[Mp]}{$M_A$}%
  \psfrag{[hb]}{$H_B$}} 
\caption{Replacing tags with real labels with psfrag}
\end{figure}
\end{document}

And the MWE from the second answer is:

\documentclass{article}
\usepackage{color}
\usepackage{pstool}
\def\a{A}
\EndPreamble
\begin{document}

\psfragfig*[mode=nonstop]{trial}{%
  \color{red}
  \tiny
  \psfrag{[Mp]}{$M_\a$!}%
}

\end{document}

When I use the pdflatexmk script – which I have modified according to this answer to include the shell escape option required by pstool – the compilation of both the aforementioned examples always ends in this error message:

Latexmk: Maximum runs of pdflatex reached without getting stable files
Latexmk: Did not finish processing file 'trialctan2.tex':
   'pdflatex' needed too many passes
Latexmk: Use the -f option to force complete processing,
 unless error was exceeding maximum runs of latex/pdflatex.
Latexmk: applying rule 'pdflatex'...
Latexmk: All targets (trialctan2.pdf) are up-to-date

Why is this error happening and how can I get rid of it?
I tried adding the -f option to the pdflatexmk script as follows:

"${LTMKBIN}"/latexmk -pdf -r "${LTMKEDIT}/latexmkrcedit" -r "${TSBIN}/pdflatexmkrc" -f -pdflatex="pdflatex --shell-escape %O %S" "$1"

But the same error persists.

Update 1:

Digging around I found that the culprit is the process=all option. In the second MWE it is locally enabled by the asterisk after psfragfig.
With this option the pdf file for each eps figure is recreated on every run. I suspect that the pdflatexmk script notices this and decides it needs to run again as something has changed, triggering a new eps to pdf conversion, noticing it again, etc…
I think I need to find out how to restrict the process=all option to the first run only.

Best Answer

The problem is that the file trial.pdf is created on every run, and it is always different from the previous version. Since latexmk notices the change, it presumes that pdflatex has to be run again. Near the end of its output is the reason:

Rule 'pdflatex': File changes, etc:
Changed files, or newly in use since previous run(s):
  'trial.pdf'

This is just above the output that is quoted in the question.

Possible solutions:

  1. The changes are irrelevant to the final output, so you have to tell latexmk to ignore them. This can be done by adding the following line to an appropriate latexmkrc file:

    $hash_calc_ignore_pattern{'pdf'} = '^/CreationDate |^/ModDate |^/ID \\[<';
    

    However, there remains the problem that with the posted MWE the .pdf file is recreated on every run of pdflatex, whether or not it is needed, which can be excessively time consuming.

  2. Alternatively, you can stop pstool from recreating trial.pdf on every run by replacing process=all by process=auto in the options list for pstool in the first example. In the second example you remove the * after \psfragfig. But this won't update the pdf file if the psfrag substitutions are changed; the only updates are when the .eps file changes.

  3. Another solution is to delegate the making of the .pdf file to latexmk. For this you should turn off the use of the -shell-escape option to pdflatex. To make this work needs a bit of trickery in a latexmkrc file. Here is appropriate code to put into one of the latexmkrc files:

    $pdf_mode = 1;
    $dvi_mode = $postscript_mode = 0;
    # Don't use the following files for a default make
    # by latexmk (i.e., with no files on the command line):
    @default_excluded_files = ( '*-pstool.tex' );
    add_cus_dep( 'eps', 'pdf', 0, eps2pdf_pstool );
    $pdflatex = 'internal pdflatex_pstool %O %S';
    # Patterns to ignore in detecting changes in pdf files made by pstool
    $hash_calc_ignore_pattern{'pdf'} = '^/CreationDate |^/ModDate |^/ID \\[<';
    
    sub pdflatex_pstool {
       # This subroutine runs pdflatex with some pre- and post-processing
       #    that is appropriate when the  pstool package is used.
       #
       # Ensure that there is a dummy pstool-statusfile.txt file so that 
       #   during the run of pdflatex, the pstool package isn't unhappy
       #   enough to report an error.
       if ( ! -e "pstool-statusfile.txt" ) {
          system( "echo 1 > pstool-statusfile.txt" );
       }
       # The actual run of pdflatex, with the specified arguments this
       #   subroutine has  been given
       my $return = system( 'pdflatex', @_ );
       # Fix up the dependencies on eps files given special processing by
       #    pstool.  These are flagged by the existence of files named
       #    "*-pstool.tex".  To get latexmk to use these, it is sufficient
       #    to tell it that there is a corresponding pdf file that needs
       #    to be made. 
       foreach my $cand ( glob "*-pstool.tex" ) {
          # Get the basename of the associated graphics file.
          # This is done by Perl regex on the r.h.s. of the assignment in
          #    the next statement.  The variable $base is surrounded by
          #    parentheses, so that ($base) is a one-element array to
          #    which is assigned the first pattern ($1) in parentheses in
          #    the regex. Without the parentheses around $base, $base
          #    would be set to an essentially boolean value for a
          #    successful search.
          my ($base) = ($cand =~ /^(.*)-pstool.tex$/ );
          if ( -e "$base.eps" ) {
             # We get here if we know that both a $base-pstool.tex file
             #   and a $base.eps file exists.  This indicates that there
             #   is a file $base.pdf that needs to be made from $base.eps
             #   with the aid of applying latex to $base-pstool.tex.
             # The important thing is that this pdf file is a source file
             #   of the main run of pdflatex (and hence for the rule used
             #   to invoke the current subroutine).
             # Once the dependency on the pdf file is set, latexmk
             #   will do the rest, by finding the eps file from which the
             #   pdf file is made and setting a suitable rule to make the
             #   pdf file, by the custom dependency method.
             rdb_ensure_file( $rule, "$base.pdf" );
          }
       }
       return $return;
    }
    
    sub eps2pdf_pstool {
       # Note that when this subroutine is invoked, $rule is set to the
       #   name of the current rule (which is an eps to pdf custom
       #   dependency). 
       my $base = $_[0];
       my $source = "$base.eps";
       my $dest = "$base.pdf";
       my $pstool_base = "$base-pstool";
       if ( -e "$pstool_base.tex" ) {
          # Use pstool processing chain
          #
          # This rule needs a dependency on the tex file used to make
          #    the pdf file:
          rdb_ensure_file( $rule, "$pstool_base.tex" );
          my $return = 0;
          # Generate the pdf file by the method used by pstool
          # I assume that the image is to be cropped.
          # The following code copies what pstool would do directly from
          #   the main run of pdflatex if it were allowed by a option
          #   shell-escape to pdflatex.
          $return = 
             system( "latex",
                     "-output-format=dvi",
                     "-interaction=batchmode",
                     "-shell-escape",
                     "$pstool_base.tex" )
             || system( "dvips", "-q", "-Ppdf", "$pstool_base.dvi" )
             || system( "ps2pdf",
                        "-dAutoFilterColorImages=false",
                        "-dAutoFilterGrayImages=false",
                        "-dColorImageFilter=/FlateEncode",
                        "-dGrayImageFilter=/FlateEncode",
                        "-dPDFSETTINGS=/prepress",
                        "$pstool_base.ps",
                        "$pstool_base.pdf" )
             || system( "pdfcrop", "$pstool_base.pdf", "$base.pdf" );
          # The pstool package likes the result of the commands to be
          #    stored in a status file:
          system( "echo $return > pstool-statusfile.txt" );
          if ( $return == 0 ) {
             # If there was no error, clean up intermediate files, but
             #   leave them as they if there was an error; the user may
             #   want to  use the files to diagnose the error.  
             # But leave the aux file, since pstool wants to read it.
             foreach my $ext ( "log", "dvi", "ps", "pdf" ) {
                unlink "$pstool_base.$ext";
             }
          }
          return $return;
       }
       else {
          # Use standard epstopdf processing
          return system( "epstopdf $_[0].eps --outfile=$_[0].pdf" );
       }
    }
    

    Note: When using this solution, it is a good idea to invoke the pstool package in the main latex document by

    \usepackage[crop=pdfcrop,process=auto,cleanup={.dvi,.ps,.pdf,.log}]{pstool}
    

    This will ensure that if you do happen to invoke pdflatex with the -shell-escape option, it won't interfere too much with the work that latexmk does.