View Javadoc
1   /*
2    * The akquinet maven-latex-plugin project
3    *
4    * Copyright (c) 2011 by akquinet tech@spree GmbH
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package eu.simuline.m2latex.core;
20  
21  import java.io.File;
22  import java.io.FileFilter;
23  import java.util.Collection;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.Map;
28  import java.util.Optional;
29  import java.util.Set;
30  import java.util.TreeMap;
31  import java.util.TreeSet;
32  
33  /**
34   * The latex pre-processor is for preprocessing graphic files
35   * in formats which cannot be included directly into a latex-file
36   * and in finding the latex main files which is done in 
37   * {@link #processGraphicsSelectMain(File, DirNode, boolean)}
38   * and in clearing the created files from the latex source directory
39   * in {@link #clearCreated(File)}.
40   */
41  public class LatexPreProcessor extends AbstractLatexProcessor {
42  
43    /**
44     * Maps the suffix to the according handler.
45     * If the handler is <code>null</code>, there is no handler.
46     */
47    private final static Map<String, SuffixHandler> SUFFIX2HANDLER =
48        new TreeMap<String, SuffixHandler>();
49  
50    static {
51      for (SuffixHandler handler : SuffixHandler.values()) {
52        SUFFIX2HANDLER.put(handler.getSuffix(), handler);
53      }
54    } // static
55  
56    // used in preprocessing only (once)
57    private final static String SUFFIX_TEX = ".tex";
58  
59    // home-brewed ending to represent tex including postscript 
60    // TBD: used internally in methods runFig2TexInclDev, clearTargetPtxPdfEps, runGnuplot2Dev
61    // used outside only in TexFileTuils.filterInkscapeIncludeFile
62    final static String SUFFIX_PTX = ".ptx";
63    // the next two for preprocessing and in LatexDev only
64    // TBD: analyze: also: PDF, EPS
65    final static String SUFFIX_PDFTEX = ".pdf_tex";// LatexDev only 
66    final static String SUFFIX_EPSTEX = ".eps_tex";// LatexDev
67  
68    // suffix for xfig
69    private final static String SUFFIX_FIG = ".fig";
70    // suffix for svg
71    private final static String SUFFIX_SVG = ".svg";
72    // suffix for gnuplot
73    // FIXME: to be made configurable
74    private final static String SUFFIX_GP = ".gp";
75    // suffix for metapost
76    private final static String SUFFIX_MP = ".mp";
77    // from xxx.mp creates xxx1.mps, xxx.log and xxx.mpx
78    private final static String SUFFIX_MPS = ".mps";
79    private final static String SUFFIX_MPX = ".mpx";
80  
81    // just for message
82    private final static String SUFFIX_JPG = ".jpg";
83    private final static String SUFFIX_PNG = ".png";
84    // just for silently skipping
85    private final static String SUFFIX_BIB = ".bib";
86    // for latex main file creating html and for graphics.
87    static final String SUFFIX_EPS = ".eps";// LatexDev
88  
89    private final static String SUFFIX_XBB = ".xbb";
90    private final static String SUFFIX_BB = ".bb";
91  
92    LatexPreProcessor(Settings settings, CommandExecutor executor, LogWrapper log,
93        TexFileUtils fileUtils) {
94      super(settings, executor, log, fileUtils);
95    }
96  
97    // Formats that work with LaTeX (dvi mode, using dvips):
98    // eps
99    // Formats that work with LaTeX (dvi mode, using dvipdfm(x)):
100   // pdf, png, jpeg, eps (the latter not taken into account)
101   // eps-source files handled via package epstopdf:
102   // seemingly automatically converted eps-->pdf during latex run
103   // also there is a program epstopdf and epspdf
104   // There is a lot of experiments to do!!
105   // MISSING: pdf and eps
106   // NOTE: graphics is typically only included via dvipdfm(x)
107   // Formats that work with pdfLaTeX (pdf mode):
108   // pdf, png, jpeg, jbig2 (the latter not taken into account)
109   // LuaTeX can also read
110   // jpeg 2000 (not taken into account)
111   //
112   // Seemingly, it makes sense to distinguish from pdfViaDvi-parameter:
113   // if set, seemingly, pdf, pgn and jpg is includable only
114   // creating .bb or .xbb.
115 
116   // mp: besides mpost we also have mptopdf creating pdf:
117   // mptopdf 05someMetapost.mp creates 05someMetapost1.mps
118   // mptopdf 05someMetapost1.mps creates 05someMetapost1-mps.pdf
119 
120   /**
121    * Handler for each suffix of a source file.
122    * Mostly, these represent graphic formats
123    * but also {@link #SUFFIX_TEX} is required
124    * to detect the latex main files
125    * and {@link #SUFFIX_TEX} and {@link #SUFFIX_BIB}
126    * are needed for proper cleaning of the tex souce directory.
127    */
128   enum SuffixHandler {
129     /**
130      * Handler for .fig-files representing the native xfig format.
131      */
132     fig {
133       // converts a fig-file into pdf and ptx
134       // invoking {@link #runFig2Dev(File, LatexDev)}
135       // TEX01, EEX01, EEX02, EEX03, WEX04, WEX05
136       void procSrc(File file, LatexPreProcessor proc)
137           throws BuildFailureException {
138 
139         // may throw BuildFailureException TEX01,
140         // may log EEX01, EEX02, EEX03, WEX04, WEX05
141         proc.runFig2Dev(file);
142       }
143 
144       void clearTarget(File file, LatexPreProcessor proc) {
145         // may log EFU05
146         proc.clearTargetPtxPdfEps(file);
147       }
148 
149       String getSuffix() {
150         return LatexPreProcessor.SUFFIX_FIG;
151       }
152     },
153     /**
154      * Handler for .gp-files representing the native gnuplot format.
155      */
156     gp {
157       // converts a gnuplot-file into pdf and ptx
158       // invoking {@link #runGnuplot2Dev(File, LatexDev)}
159       // TEX01, EEX01, EEX02, EEX03, WEX04, WEX05
160       void procSrc(File file, LatexPreProcessor proc)
161           throws BuildFailureException {
162         proc.runGnuplot2Dev(file);
163       }
164 
165       void clearTarget(File file, LatexPreProcessor proc) {
166         // may log EFU05
167         proc.clearTargetPtxPdfEps(file);// TBD: clarify 
168       }
169 
170       String getSuffix() {
171         return LatexPreProcessor.SUFFIX_GP;
172       }
173     },
174     /**
175      * Handler for .mp-files representing the metapost format.
176      */
177     mp {
178       // converts a metapost-file into mps-format
179       // invoking {@link #runMetapost2mps(File)}
180       // TEX01, EEX01, EEX02, EEX03, WEX04, WEX05
181       void procSrc(File file, LatexPreProcessor proc)
182           throws BuildFailureException {
183         proc.runMetapost2mps(file);
184       }
185 
186       void clearTarget(File file, LatexPreProcessor proc) {
187         // may log WFU01, EFU05
188         proc.clearTargetMp(file);
189       }
190 
191       String getSuffix() {
192         return LatexPreProcessor.SUFFIX_MP;
193       }
194     },
195     /**
196      * Handler for .svg-files representing scaleable vector graphics.
197      */
198     svg {
199       // converts an svg-file into pdf and ptx
200       // invoking {@link #runFig2Dev(File, LatexDev)}
201       // TEX01, EEX01, EEX02, EEX03, WEX04, WEX05
202       // EFU07, EFU08, EFU09 if filtering a file fails.
203       void procSrc(File file, LatexPreProcessor proc)
204           throws BuildFailureException {
205         proc.runSvg2Dev(file);
206         // proc.log.info("Processing svg-file '" + file +
207         // "' deferred to LaTeX run by need. ");
208         // FIXME: this works for pdf but not for dvi:
209         // even in the latter case, .pdf and .pdf_tex are created
210       }
211 
212       void clearTarget(File file, LatexPreProcessor proc) {
213         // may log EFU05
214         proc.clearTargetPtxPdfEps(file);
215       }
216 
217       String getSuffix() {
218         return LatexPreProcessor.SUFFIX_SVG;
219       }
220     },
221     /**
222      * Handler for .jpg-files representing a format
223      * defined by the Joint Photographic Experts Group (jp(e)g).
224      */
225     jpg {
226       void procSrc(File file, LatexPreProcessor proc)
227           throws BuildFailureException {
228         proc.runEbbByConfig(file);
229       }
230 
231       // void clearTarget(File file,
232       // LatexPreProcessor proc,
233       // Map<File, SuffixHandler> file2handler) {
234       // // do not add to file2handler
235       // }
236       void clearTarget(File file, LatexPreProcessor proc) {
237         // throw new IllegalStateException
238         // ("File '" + file + "' has no targets to be cleared. ");
239         proc.clearTargetJpgPng(file);
240       }
241 
242       String getSuffix() {
243         return LatexPreProcessor.SUFFIX_JPG;
244       }
245     },
246     /**
247      * Handler for .png-files
248      * representing the Portable Network Graphics format.
249      */
250     png {
251       void procSrc(File file, LatexPreProcessor proc)
252           throws BuildFailureException {
253         proc.runEbbByConfig(file);
254 
255       }
256 
257       // void clearTarget(File file,
258       // LatexPreProcessor proc,
259       // Map<File, SuffixHandler> file2handler) {
260       // // do not add to file2handler
261       // }
262       void clearTarget(File file, LatexPreProcessor proc) {
263         // throw new IllegalStateException
264         // ("File '" + file + "' has no targets to be cleared. ");
265         proc.clearTargetJpgPng(file);
266       }
267 
268       String getSuffix() {
269         return LatexPreProcessor.SUFFIX_PNG;
270       }
271     },
272     /**
273      * Handler for .tex-files
274      * representing the TeX format, to be more precise the LaTeX format.
275      */
276     tex {
277       void scheduleProcSrc(File file,
278           Map<File, SuffixHandler> file2handler,
279           LatexPreProcessor proc,
280           Collection<LatexMainDesc> latexMainDescs) {
281         file2handler.put(file, this);// super
282         // may log WFU03, WPP02
283         proc.addIfLatexMain(file, latexMainDescs);
284       }
285 
286       void procSrc(File file, LatexPreProcessor proc) {
287         // do nothing: no source for the latex processor 
288       }
289 
290       void clearTarget(File file,
291           LatexPreProcessor proc,
292           Map<File, SuffixHandler> file2handler) {
293         // may log WPP02, WFU01, WFU03, EFU05
294         boolean isLatexMain = proc.clearTargetTexIfLatexMain(file);
295         if (!isLatexMain) {
296           file2handler.put(file, this);
297         }
298       }
299 
300       void clearTarget(File file, LatexPreProcessor proc) {
301         proc.clearIfTexNoLatexMain(file);
302       }
303 
304       String getSuffix() {
305         return LatexPreProcessor.SUFFIX_TEX;
306       }
307       boolean isToBePreprocessed() {
308         return false;
309       }
310     },
311     /**
312      * Handler for .bib-files
313      * representing the BibTeX format for bibliographies.
314      */
315     bib {
316       void procSrc(File file, LatexPreProcessor proc) {
317         proc.log.info("Found bib-file '" + file + "'. ");
318       }
319 
320       void clearTarget(File file, LatexPreProcessor proc,
321           Map<File, SuffixHandler> file2handler) {
322         // do not add to file2handler
323       }
324 
325       void clearTarget(File file, LatexPreProcessor proc) {
326         throw new IllegalStateException
327           ("File '" + file + "' has no targets to be cleared. ");
328       }
329 
330       String getSuffix() {
331         return LatexPreProcessor.SUFFIX_BIB;
332       }
333       boolean isToBePreprocessed() {
334         return false;
335       }
336     };
337 
338     // essentially, maps file to its handler
339     // overwritten for tex: in addition add to latexMainFiles
340     void scheduleProcSrc(File file,
341         Map<File, SuffixHandler> file2handler,
342         LatexPreProcessor proc,
343         Collection<LatexMainDesc> latexMainDescs) {
344       file2handler.put(file, this);
345     }
346 
347     // FIXME: to be updated
348     // if a graphic format: process source.
349     // For tex and for bib: do nothing.
350     /**
351      * Typically, .i.e. for {@link #fig}-, {@link #gp}-, {@link #mp}-
352      * 
353      * and associates <code>file</code>
354      * Does the transformation of the file <code>file</code>
355      * using <code>proc</code> immediately, except for
356      * <ul>
357      * <li>
358      * {@link #svg}-files for which an info message is logged,
359      * that transformation is done by need in the course of a LaTeX run.
360      * What occurs are files .pdf and .pdf_tex
361      * even if {@link Settings#pdfViaDvi} indicates creation of dvi files.
362      * <li>
363      * {@link #tex}-files which are only scheduled for later translation
364      * just by adding them to <code>latexMainFiles</code>
365      * if they are latex main files, and ignored otherwise
366      * (see {@link LatexPreProcessor#addIfLatexMain(File, Collection)}).
367      * <li>
368      * {@link #bib}-files for which just an info message
369      * that a bib file was found is logged.
370      * </ul>
371      * <p>
372      * Logging:
373      * <ul>
374      * <li>WFU03: cannot close
375      * <li>WPP02: tex file may be latex main file
376      * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
377      * if applications for preprocessing graphic files failed.
378      * <li>EFU07, EFU08, EFU09 if filtering a file fails.
379      * </ul>
380      *
381      * @param file
382      *    a file with ending given by {@link #getSuffix()}.
383      * @param proc
384      *     a latex pre-processor.
385      * @throws BuildFailureException
386      *    TEX01 only for {@link #fig}, {@link #gp} and {@link #mp}
387      *    because these invoke external programs.
388      */
389     abstract void procSrc(File file, LatexPreProcessor proc)
390         throws BuildFailureException;
391 
392     /**
393      * Typically, .i.e. for {@link #fig}-, {@link #gp}-, {@link #mp}-
394      * and {@link #svg}-files just associates <code>file</code>
395      * with this handler in <code>file2handler</code>
396      * to schedule according targets for deletion except for
397      * <ul>
398      * <li>
399      * {@link #tex}-files for which the target is cleared immediately
400      * if it is a latex main file, otherwise ignoring
401      * by invoking {@link #clearTargetTexIfLatexMain(File)}.
402      * <li>
403      * {@link #bib}-files
404      * (maybe appropriate also for jpg-files and for png-files)
405      * for which there are no targets
406      * and so the association is not added to <code>file2handler</code>.
407      * </ul>
408      * <p>
409      * Logging:
410      * <ul>
411      * <li>WPP02: tex file may be latex main file
412      * <li>WFU01: Cannot read directory...
413      * <li>WFU03: cannot close tex file
414      * <li>EFU05: Failed to delete file
415      * <ul>
416      *
417      * @param file
418      *    a file with ending given by {@link #getSuffix()},
419      *    i.e. a file which can be handled by this handler.
420      * @param proc
421      *    a latex pre-processor.
422      * @param file2handler
423      *    maps <code>file</code> to its handler.
424      *    In general, this method adds
425      *    <code>file</code> to <code>file2handler</code>
426      *    together with its handler which is just <code>this</code>.
427      * @see #clearTarget(File, LatexPreProcessor)
428      */
429     // overwritten for tex, jpg, png and for bib
430     // appropriate for svg although file may be removed from map later
431     // used in clearCreated(File, DirNode) only
432     void clearTarget(File file,
433         LatexPreProcessor proc,
434         Map<File, SuffixHandler> file2handler) {
435       file2handler.put(file, this);
436     }
437 
438     /**
439      * Deletes the files potentially
440      * created from the source file <code>file</code>
441      * using <code>proc</code>.
442      * <p>
443      * Logging:
444      * <ul>
445      * <li>WFU01: Cannot read directory...
446      * <li>EFU05: Failed to delete file
447      * <ul>
448      *
449      * @param file
450      *    a file with ending given by {@link #getSuffix()}.
451      * @param proc
452      *    a latex pre-processor.
453      * @throws IllegalStateException
454      *    <ul>
455      *    <li>
456      *    if <code>file</code> has no targets to be deleted
457      *    as for jpg-files, png-files and bib-files.
458      *    <li>
459      *    if targets of <code>file</code> should have been cleared already
460      *    by {@link #clearTarget(File, LatexPreProcessor, Map)}
461      *    as for tex-files.
462      *    </ul>
463      * @see #clearTarget(File, LatexPreProcessor, Map)
464      */
465     // used in clearCreated(File, DirNode) only
466     abstract void clearTarget(File file, LatexPreProcessor proc);
467 
468     /**
469      * Returns the suffix of the file type
470      * of the file type, this is the handler for.
471      */
472     abstract String getSuffix();
473 
474     /**
475      * Returns whether this type of file is to be preprocessed. 
476      * This means processed prior to compilation of latex files. 
477      * As a consequence, this is false for {@link #tex} 
478      * because these files are compiled directly 
479      * and it is also false for {@link #bib} 
480      * because these files are either bibliography or glossary databases 
481      * and are thus processed after latex files. 
482      * Currently, this method returns true only for graphic files. 
483      * 
484      * @return
485      *    whether this type of file is to be preprocessed. 
486      */
487     boolean isToBePreprocessed() {
488       return true;
489     }
490   } // enum SuffixHandler
491 
492   // FIXME: CAUTION with including pictures in xfig:
493   // This is done as reference to included file.
494   // Thus it breaks depencency chain.
495 
496   // The following shows the supported formats:
497   // l.10 \includegraphics{02gp2pdf000}
498   // %
499   // I could not locate the file with any of these extensions:
500   // .pdf,.PDF,.ai,.AI,.png,.PNG,.jpg,.JPG,.jpeg,.JPEG,.bmp,.BMP,.ps,.PS,.eps,.EPS,.
501   // pz,.eps.Z,.ps.Z,.ps.gz,.eps.gz
502   // Try typing <return> to proceed.
503   // If that doesn't work, type X <return> to quit.
504 
505   // )
506   // :<-
507   // Package srcltx Info: Expanded filename `03someGnuplot.ptx' to
508   // `03someGnuplot.pt
509   // x.tex' on input line 949.
510 
511   // FIXME: allow variants:
512   // - pdfhandler on .pdf,.PDF, (includable directly with pdflatex)
513   // - png/jpghandler on .png,.PNG,.jpg,.JPG,.jpeg,.JPEG,
514   // - maybe also for .fig
515 
516   // FIXME: questions:
517   // - how to include .pdf into .dvi?
518   // - how to include .eps into .pdf?
519   // Question: how to transform ps into eps?
520   // Research on the following:
521   // .ai,.AI,.bmp,.BMP,
522   // .ps,.PS,.eps,.EPS,.
523   // pz,.eps.Z,.ps.Z,.ps.gz,.eps.gz
524 
525   // FIXME: decide whether suffix .ptx is replaced by .tex:
526   // Advantage: because this is what it is.
527   // Disadvantage: Requires mechanism
528   // to determine whether tex is created or original
529   // but this works the same as for pdf and for svg.
530 
531   /**
532    * Converts the fig-file <code>figFile</code>
533    * into a tex-file with ending ptx
534    * including a pdf-file or an eps-file also created.
535    * To that end, invokes {@link #runFig2DevInTex(File, LatexDev)} twice
536    * to create a pdf-file and an eps-file
537    * and invokes {@link #runFig2TexInclDev(File)} (once)
538    * to create the tex-file.
539    * <p>
540    * Logging:
541    * <ul>
542    * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
543    * if running the fig2dev command failed.
544    * </ul>
545    *
546    * @param figFile
547    *    the fig file to be processed.
548    * @throws BuildFailureException
549    *    TEX01 if invocation of the fig2dev command
550    *    returned by {@link Settings#getFig2devCommand()} failed.
551    *    This is invoked twice: once for creating the pdf-file
552    *    and once for creating the pdf_t-file.
553    * @see #processGraphicsSelectMain(File, DirNode, boolean)
554    */
555   // used in fig.procSrc(File, LatexPreProcessor) only
556   private void runFig2Dev(File figFile) throws BuildFailureException {
557     this.log.info("Processing fig-file '" + figFile + "'. ");
558 
559     // all three
560     // may throw BuildFailureException TEX01,
561     // may log EEX01, EEX02, EEX03, WEX04, WEX05
562     runFig2DevInTex(figFile, LatexDev.pdf);
563     runFig2DevInTex(figFile, LatexDev.dvips);
564     runFig2TexInclDev(figFile);
565   }
566 
567   /**
568    * From <code>figFile</code> create pdf/eps-file
569    * containing graphics without text with special flag set.
570    * The output format depends on <code>dev</code>.
571    * The resulting file is included in some tex-file
572    * created by {@link #runFig2TexInclDev(File)}.
573    * Conversion is done by {@link Settings#getFig2devCommand()}.
574    * <p>
575    * Logging: FIXME:
576    * EEX01, EEX02, EEX03, WEX04, WEX05
577    *
578    * @param figFile
579    *    the fig-file to be processed
580    * @param dev
581    *    represents the target: either a pdf-file or an eps-file.
582    * @throws BuildFailureException
583    *    FIXME: TEX01,
584    */
585   private void runFig2DevInTex(File figFile, LatexDev dev)
586       throws BuildFailureException {
587 
588     // Result file: either .pdf or .eps
589     File figInTexFile =
590         TexFileUtils.replaceSuffix(figFile, dev.getGraphicsInTexSuffix());
591     String command = this.settings.getCommand(ConverterCategory.Fig2Dev);
592 
593     // if (update(figFile, pdfFile)) {
594     String[] args = buildArgumentsFig2PdfEps(dev.getXFigInTexLanguage(),
595         this.settings.getFig2devGenOptions(),
596         this.settings.getFig2devPdfEpsOptions(), figFile, figInTexFile);
597     //this.log.info("Running fig2dev"+java.util.Arrays.asList(args));
598     this.log.debug("Running " + command + " -L pdftex/pstex  ... on '"
599         + figFile.getName() + "'. ");
600     // may throw BuildFailureException TEX01,
601     // may log EEX01, EEX02, EEX03, WEX04, WEX05
602     this.executor.executeEnvR0(figFile.getParentFile(),
603         this.settings.getTexPath(), // ****
604         command,
605         args,
606         figInTexFile);
607     // }
608   }
609 
610   //
611   // PSTEX Options:
612   // -b width specify width of blank border around figure (1/72 inch)
613   // Found: affects clipping path and bounding box only.
614   // Not usable if latex text is used because parts no longer fit.
615   // -F use correct font sizes (points instead of 1/80inch)
616   // Found: no effect
617   // -g color background color
618   // No idea in which format color is given.
619   // -n name set title part of PostScript output to name
620   // Found: works. Without it is just the name of xxx.fig
621   //
622   // the strange thing is that this is only a subset of the postscript options
623   // to be verified whether all options apply or not.
624 
625   // The EPS driver has the following differences from PostScript:
626   // o No showpage is generated
627   // because the output is meant to be imported
628   // into another program or document and not printed
629   // o The landscape/portrait options are ignored
630   // o The centering option is ignored
631   // o The multiple-page option is ignored
632   // o The paper size option is ignored
633   // o The x/y offset options are ignored
634 
635   // The EPS driver has the following two special options:
636   //
637   // -B 'Wx [Wy X0 Y0]'
638   // This specifies that the bounding box of the EPS file
639   // should have the width Wx and the height Wy.
640   // Note that it doesn't scale the figure to this size,
641   // it merely sets the bounding box.
642   // If a value less than or equal to 0 is specified for Wx or Wy,
643   // these are set to the width/height respectively of the figure.
644   // Origin is relative to screen (0,0) (upper-left).
645   // Wx, Wy, X0 and Y0 are interpreted
646   // in centimeters or inches depending on the measure
647   // given in the fig-file.
648   // Remember to put either quotes (") or apostrophes (')
649   // to group the arguments to -B.
650   // -R 'Wx [Wy X0 Y0]'
651   // Same as the -B option except that X0 and Y0
652   // is relative to the lower left corner of the figure.
653   // Remember to put either quotes (") or apostrophes (')
654   // to group the arguments to -R.
655 
656   // The PDF driver uses all the PostScript options.
657 
658   // Explanation: many of these options do not make sense.
659   // Tried: -x, -y to shift: does not work and does not make sense
660   // What makes sense is
661   // -a don't output user's login name (anonymous)
662   // Found: login name occurs nowhere with and without -a
663   // -N convert all colors to grayscale
664   // Found: works
665 
666   // No information on PDFTEX options.
667   // Instead:
668   //
669   // PDF Options:
670   // -a don't output user's login name (anonymous)
671   // -b width specify width of blank border around figure (1/72 inch)
672   // -F use correct font sizes (points instead of 1/80inch)
673   // -g color background color
674   //
675   // seemingly not the same, so maybe separate options required.
676   // -n is pstex but not in pdf,
677   // -a is pdf but not pstex... strange: is postscript
678 
679   /**
680    * Returns an array of options of the form
681    * <code>-L &lt;language&gt; &lt;optionsGen&gt; &lt;optionsPdfEps&gt; xxx.fig xxx.pdf/xxx.eps 
682    * </code> for invocation of {@link Settings#getFig2devCommand()}
683    * for creation of the pdf/eps-part of a fig-figure
684    * as done in {@link #runFig2DevInTex(File, LatexDev)}.
685    *
686    * @param language
687    *    is the output language
688    *    which is either <code>pdftex</code> or <code>pstex</code>
689    * @param optionsGen
690    *    the general options, applying to both the pdf/eps part
691    *    and the tex part of the figure under consideration.
692    * @param optionsPdfEps
693    *    the options, specific for the pdf/eps part (which is the same)
694    *    of the figure under consideration.
695    * @param figFile
696    *    the fig-file to be transformed.
697    * @param grpFile
698    *    the graphics file (pdf/eps-file)
699    *    which is the result of the transformation.
700    */
701   private String[] buildArgumentsFig2PdfEps(String language,
702       String optionsGen,
703       String optionsPdfEps,
704       File figFile,
705       File grpFile) {
706     String[] optionsGenArr = optionsGen.isEmpty()
707         ? new String[0] : optionsGen.split(" ");
708     String[] optionsPdfEpsArr = optionsPdfEps.isEmpty()
709         ? new String[0] : optionsPdfEps.split(" ");
710     int lenSum = optionsGenArr.length + optionsPdfEpsArr.length;
711 
712     // add the four additional options
713     String[] args = new String[lenSum + 4];
714     // language
715     args[0] = "-L";
716     args[1] = language;
717     // general options
718     System.arraycopy(optionsGenArr, 0, args, 2, optionsGenArr.length);
719     // language specific options
720     System.arraycopy(optionsPdfEpsArr, 0, args, 2 + optionsGenArr.length,
721         optionsPdfEpsArr.length);
722     // input: fig-file
723     args[2 + lenSum] = figFile.getName();
724     // output: pdf/eps-file
725     args[3 + lenSum] = grpFile.getName();
726     return args;
727   }
728 
729   /**
730    * From <code>figFile</code> create tex-file
731    * containing text with special flag set and
732    * including a graphic file containing the rest of <code>figFile</code>.
733    * Inclusion is without file extension and so both possible results
734    * of {@link #runFig2DevInTex(File, LatexDev)} can be included
735    * when compiling with latex.
736    * Conversion is done by {@link Settings#getFig2devCommand()}.
737    * <p>
738    * Logging: FIXME:
739    * warning EEX01, EEX02, EEX03, WEX04, WEX05
740    *
741    * @param figFile
742    *    the fig-file to be processed
743    * @throws BuildFailureException
744    *    FIXME: TEX01,
745    */
746   private void runFig2TexInclDev(File figFile) throws BuildFailureException {
747 
748     // result file: .ptx
749     File ptxFile = TexFileUtils.replaceSuffix(figFile, SUFFIX_PTX);
750     String command = this.settings.getCommand(ConverterCategory.Fig2Dev);
751 
752     // if (update(figFile, pdf_tFile)) {
753     String[] args = buildArgumentsFig2Ptx(this.settings.getFig2devGenOptions(),
754         this.settings.getFig2devPtxOptions(),
755         figFile,
756         ptxFile);
757     this.log.debug("Running " + command + " -L (pdf/ps)tex_t... on '"
758         + figFile.getName() + "'. ");
759     // may throw BuildFailureException TEX01,
760     // may log EEX01, EEX02, EEX03, WEX04, WEX05
761     this.executor.executeEnvR0(figFile.getParentFile(),
762         this.settings.getTexPath(), // ****
763         command,
764         args,
765         ptxFile);
766     // }
767   }
768 
769   /**
770    * The name of the language
771    * used by the {@link Settings#getFig2devCommand()}
772    * to specify ``special'' text without graphic of an xfig-picture.
773    * Note that the languages <code>pdftex_t</code> and <code>pstex_t</code>
774    * are equivalent.
775    */
776   private final static String XFIG_TEX_LANGUAGE = "pdftex_t";
777 
778   // Since pstex_t is equivalent with pdftex_t,
779   // also the options are the same (hopefully)
780   //
781   // PSTEX_T Options:
782   // -b width specify width of blank border around figure (1/72 inch)
783   // -E num set encoding for text translation (0 no translation,
784   // 1 ISO-8859-1, 2 ISO-8859-2)
785   // -F don't set font family/series/shape, so you can
786   // set it from latex
787   // -p name name of the PostScript file to be overlaid
788 
789   /**
790    * Returns an array of options of the form
791    * <code>-L &lt;language&gt; &lt;optionsGen&gt; &lt;optionsPdfEps&gt; -p xxx xxx.fig xxx.ptx</code>
792    * for invocation of {@link Settings#getFig2devCommand()}
793    * for creation of the tex-part of a fig-figure
794    * as done in {@link #runFig2TexInclDev(File)}.
795    * Note that the option <code>-p xxx</code>
796    * specifies the name of the pdf/eps-file
797    * included in the result file <code>ptxFile</code>
798    * without suffix.
799    *
800    * @param optionsGen
801    *    the general options, applying to both the pdf/eps part
802    *    and the tex part of the figure under consideration.
803    * @param optionsPtx
804    *    the options, specific for the tex part
805    *    of the figure under consideration (for the ptx-file).
806    * @param figFile
807    *    the fig-file to be transformed.
808    * @param ptxFile
809    *    the ptx-file which is the result of the transformation.
810    */
811   private String[] buildArgumentsFig2Ptx(String optionsGen, String optionsPtx,
812       File figFile, File ptxFile) {
813     String[] optionsGenArr = optionsGen.isEmpty()
814         ? new String[0] : optionsGen.split(" ");
815     String[] optionsPtxArr = optionsPtx.isEmpty()
816         ? new String[0] : optionsPtx.split(" ");
817     int lenSum = optionsGenArr.length + optionsPtxArr.length;
818 
819     // add the six additional options
820     String[] args = new String[lenSum + 6];
821     // language
822     args[0] = "-L";
823     args[1] = XFIG_TEX_LANGUAGE;
824     // general options
825     System.arraycopy(optionsGenArr, 0, args, 2, optionsGenArr.length);
826     // language specific options
827     System.arraycopy(optionsPtxArr, 0, args, 2 + optionsGenArr.length,
828         optionsPtxArr.length);
829     // -p pdf/eps-file name in ptx-file without suffix
830     args[2 + lenSum] = "-p";
831     // full path without suffix
832     args[3 + lenSum] =
833         TexFileUtils.replaceSuffix(figFile, SUFFIX_VOID).getName();
834     // input: fig-file
835     args[4 + lenSum] = figFile.getName();
836     // output: ptx-file
837     args[5 + lenSum] = ptxFile.getName();
838     return args;
839   }
840 
841   /**
842    * Deletes the files <code>xxx.ptx</code>, <code>xxx.pdf</code> and
843    * <code>xxx.eps</code>
844    * created from the graphic file <code>grpFile</code>
845    * of the form <code>xxx.y</code>.
846    * <p>
847    * Logging:
848    * EFU05: Failed to delete file
849    *
850    * @param grpFile
851    *    a graphic file.
852    */
853   // for formats fig, gp and svg: since all of these create ptx, pdf and eps
854   private void clearTargetPtxPdfEps(File grpFile) {
855     this.log.info("Deleting targets of file '" + grpFile + "'. ");
856     // may log EFU05
857     deleteIfExists(grpFile, SUFFIX_PTX);
858     deleteIfExists(grpFile, LatexDev.pdf.getGraphicsInTexSuffix());// pdf
859     deleteIfExists(grpFile, LatexDev.dvips.getGraphicsInTexSuffix());// eps
860   }
861 
862   /**
863    * Converts a gnuplot-file into a tex-file with ending ptx
864    * including a pdf-file or an eps-file also created.
865    * To that end, invokes {@link #runGnuplot2Dev(File, LatexDev)} twice
866    * to create a pdf-file and an eps-file
867    * and to create the tex-file which can include both.
868    * <p>
869    * Logging:
870    * <ul>
871    * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
872    * if running the ptx/pdf-conversion built-in in gnuplot fails.
873    * </ul>
874    *
875    * @param gpFile
876    *    the gp-file (gnuplot format) to be converted to pdf and ptx.
877    * @throws BuildFailureException
878    *    TEX01 if invocation of the ptx/pdf-conversion built-in
879    *    in gnuplot fails.
880    * @see #processGraphicsSelectMain(File, DirNode, boolean)
881    */
882   // used in gp.procSrc(File, LatexPreProcessor) only
883   private void runGnuplot2Dev(File gpFile) throws BuildFailureException {
884     this.log.info("Processing gnuplot-file '" + gpFile + "'. ");
885     // both may throw BuildFailureException TEX01,
886     // and may log EEX01, EEX02, EEX03, WEX04, WEX05
887     runGnuplot2Dev(gpFile, LatexDev.dvips);
888     runGnuplot2Dev(gpFile, LatexDev.pdf);
889   }
890 
891   // may throw BuildFailureException TEX01,
892   // may log EEX01, EEX02, EEX03, WEX04, WEX05
893   private void runGnuplot2Dev(File gpFile, LatexDev dev)
894       throws BuildFailureException {
895 
896     String command = this.settings.getCommand(ConverterCategory.Gnuplot2Dev);
897     File grpFile =
898         TexFileUtils.replaceSuffix(gpFile, dev.getGraphicsInTexSuffix());
899     File ptxFile = TexFileUtils.replaceSuffix(gpFile, SUFFIX_PTX);
900 
901     // TBD: clarify how this is quoted 
902     String[] args = new String[] {"-e", // run a command string "..." with commands separated by ';'
903         //
904         "set terminal cairolatex " + dev.getGnuplotInTexLanguage() + " "
905             + this.settings.getGnuplotOptions() + ";set output '"
906             + ptxFile.getName() + "';load '" + gpFile.getName() + "'"};
907 
908     // if (update(gpFile, ptxFile)) {
909     this.log.debug(
910         "Running " + command + " -e...  on '" + gpFile.getName() + "'. ");
911     // may throw BuildFailureException TEX01,
912     // may log EEX01, EEX02, EEX03, WEX04, WEX05
913     this.executor.executeEnvR0(gpFile.getParentFile(), // workingDir
914         this.settings.getTexPath(), // ****
915         command, args, grpFile, ptxFile);
916     // }
917     // no check: just warning that no output has been created.
918   }
919 
920   /**
921    * Runs mpost on mp-files to generate mps-files.
922    * <p>
923    * Logging:
924    * <ul>
925    * <li>WFU03: cannot close log file
926    * <li>EAP01: Running <code>command</code> failed. For details...
927    * <li>EAP02: Running <code>command</code> failed. No log file
928    * <li>WAP04: if log file is not readable.
929    * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
930    * if running the mpost command failed.
931    * </ul>
932    *
933    * @param mpFile
934    *    the metapost file to be processed.
935    * @throws BuildFailureException
936    *    TEX01 if invocation of the mpost command failed.
937    * @see #processGraphicsSelectMain(File, DirNode, boolean)
938    */
939   // used in mp.procSrc(File, LatexPreProcessor) only
940   private void runMetapost2mps(File mpFile) throws BuildFailureException {
941     this.log.info("Processing metapost-file '" + mpFile + "'. ");
942     String command = this.settings.getCommand(ConverterCategory.MetaPost);
943     //File workingDir = mpFile.getParentFile();
944     // for more information just type mpost --help
945     String[] args = buildArguments(this.settings.getMetapostOptions(), mpFile);
946     this.log.debug("Running " + command + " on '" + mpFile.getName() + "'. ");
947     // FIXME: not check on all created files,
948     // but this is not worse than with latex
949 
950     // may throw BuildFailureException TEX01,
951     // may log EEX01, EEX02, EEX03, WEX04, WEX05
952     this.executor.executeEnvR0(mpFile.getParentFile(), //workingDir,
953         this.settings.getTexPath(), // ****
954         command,
955         args,
956         TexFileUtils.replaceSuffix(mpFile, SUFFIX_MPS));
957 
958     // from xxx.mp creates xxx.mps, xxx.log and for tex labels xxx.mpx
959     // with -recorder option in addition xxx.fls 
960     // FIXME: monitoring of those additional files. 
961     // keep trying to be in one line with the latex processors 
962     File logFile = TexFileUtils.replaceSuffix(mpFile, SUFFIX_LOG);
963     // may log WFU03, EAP01, EAP02, WAP04
964     logErrs (logFile, command, this.settings.getPatternErrMPost());
965     // may log warnings WFU03, WAP03, WAP04
966     logWarns(logFile, command, this.settings.getPatternWarnMPost());
967   }
968 
969   /**
970    * Deletes the graphic files
971    * created from the metapost-file <code>mpFile</code>.
972    * <p>
973    * Logging:
974    * <ul>
975    * <li>WFU01: Cannot read directory ...
976    * <li>EFU05: Failed to delete file
977    * </ul>
978    *
979    * @param mpFile
980    *    a metapost file.
981    */
982   private void clearTargetMp(File mpFile) {
983     this.log.info("Deleting targets of graphic-file '" + mpFile + "'. ");
984     // may log EFU05
985     deleteIfExists(mpFile, SUFFIX_LOG);
986     deleteIfExists(mpFile, SUFFIX_FLS);
987     deleteIfExists(mpFile, SUFFIX_MPX);
988     deleteIfExists(mpFile, SUFFIX_MPS);
989     // // delete files xxxNumber.mps
990     // String name1 = mpFile.getName();
991     // final String root = name1.substring(0, name1.lastIndexOf("."));
992     // FileFilter filter = new FileFilter() {
993     // 	public boolean accept(File file) {
994     // 		return !file.isDirectory()
995     // 				&&  file.getName().matches(root + SUFFIX_MPS);
996     // 	}
997     // };
998     // // may log WFU01, EFU05
999     // this.fileUtils.deleteX(mpFile, filter, false);
1000   }
1001 
1002   /**
1003    * Converts an svg-file into a tex-file with ending ptx
1004    * including a pdf-file or an eps-file also created.
1005    * To that end, invokes {@link #runSvg2Dev(File, LatexDev, boolean)} twice
1006    * to create a pdf-file and an eps-file
1007    * and to create the tex-file which can include both.
1008    * <p>
1009    * Logging:
1010    * <ul>
1011    * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1012    * if running the ptx/pdf-conversion built-in in svg2dev fails.
1013    * <li>EFU07, EFU08, EFU09 if filtering a file fails.
1014    * </ul>
1015    *
1016    * @param svgFile
1017    *    the svg-file to be converted to a pdf-file and a ptx-file.
1018    * @throws BuildFailureException
1019    *    TEX01 if invocation of the ptx/pdf-conversion built-in
1020    *    in svg2dev fails.
1021    * @see #processGraphicsSelectMain(File, DirNode, boolean)
1022    */
1023   // used in svg.procSrc(File, LatexPreProcessor) only
1024   private void runSvg2Dev(File svgFile) throws BuildFailureException {
1025     this.log.info("Processing svg-file '" + svgFile + "'. ");
1026     // both may throw BuildFailureException TEX01,
1027     // and may log EEX01, EEX02, EEX03, WEX04, WEX05
1028     // EFU07, EFU08, EFU09
1029     runSvg2Dev(svgFile, LatexDev.pdf, false);
1030     runSvg2Dev(svgFile, LatexDev.dvips, true);
1031   }
1032 
1033   // FIXME: still the included pdf/eps-file does not occur
1034   // with full path in ptx-file
1035   // may throw BuildFailureException TEX01,
1036   // may log EEX01, EEX02, EEX03, WEX04, WEX05, EFU07, EFU08, EFU09
1037   //
1038   // Note: in inkscape 1.4 the following works: 
1039   // inkscape --export-type=pdf,eps --export-area-drawing --export-latex F4_07someSvg.svg 
1040   // so all files are created at once. 
1041   // Currently, this is not used. 
1042   private void runSvg2Dev(File svgFile, LatexDev dev, boolean filterTex)
1043       throws BuildFailureException {
1044     // current:
1045     // inkscape --export-filename=F4_07someSvg -D F4_07someSvg.svg
1046     // --export-type=pdf,eps
1047     // inkscape --export-filename=F4_07someSvg.pdf -D F4_07someSvg.svg
1048     //
1049     // --export-pdf-version=1.4 may be nice
1050     String command = this.settings.getCommand(ConverterCategory.Svg2Dev);
1051 
1052     File grpFile =
1053         TexFileUtils.replaceSuffix(svgFile, dev.getGraphicsInTexSuffix());
1054     File texFile =
1055         TexFileUtils.replaceSuffix(svgFile, dev.getInkscapeTexSuffix());
1056 
1057     String[] args = buildArgumentsInkscp(grpFile,
1058         this.settings.getSvg2devOptions(), svgFile);
1059     this.log.debug("Running " + command + " on '" + svgFile.getName() + "'. ");
1060     // may throw BuildFailureException TEX01,
1061     // may log EEX01, EEX02, EEX03, WEX04, WEX05
1062     this.executor.executeEnvR0(svgFile.getParentFile(),
1063         this.settings.getTexPath(), // ****
1064         command,
1065         args,
1066         grpFile,
1067         texFile);
1068 
1069     if (filterTex) {
1070       // may log EFU07, EFU08, EFU09: cannot fiter
1071       // for eps only 
1072       String suffix = TexFileUtils.getSuffix(texFile);
1073       assert SUFFIX_EPSTEX.equals(suffix) : "Expected suffix '" + SUFFIX_EPSTEX
1074           + "' found '" + suffix + "'";
1075       File destFile = TexFileUtils.replaceSuffix(texFile, SUFFIX_PTX);
1076       File bareFile = TexFileUtils.replaceSuffix(texFile, SUFFIX_VOID);
1077 
1078       this.fileUtils.filterInkscapeIncludeFile(texFile, destFile,
1079           bareFile.getName(), SUFFIX_EPS);
1080     }
1081     this.fileUtils.deleteOrError(texFile, false);
1082   }
1083 
1084   protected static String[] buildArgumentsInkscp(File grpFile,
1085       String options,
1086       File file) {
1087     String optExp = "--export-filename=" + grpFile.getName();
1088     if (options.isEmpty()) {
1089       return new String[] {optExp, file.getName()};
1090     }
1091     String[] optionsArr = options.split(" ");
1092     String[] args = new String[optionsArr.length + 2];
1093     System.arraycopy(optionsArr, 0, args, 1, optionsArr.length);
1094     args[args.length - 1] = file.getName();
1095     args[0] = optExp;
1096     return args;
1097   }
1098 
1099   // Additional research:
1100   // Documentation says, that this is needed for interface eps,
1101   // but not for interface pdf.
1102   // Experiments show, that we can do without it in any case.
1103 
1104   private void runEbbByConfig(File file) throws BuildFailureException {
1105     if (!this.settings.getCreateBoundingBoxes()) {
1106       // suffix without dot 
1107       String suffix = TexFileUtils.getSuffix(file).substring(1);
1108       this.log.info(
1109           suffix.toUpperCase() + "-file '" + file + "' needs no processing. ");
1110       // FIXME: this works for pdf but not for dvi:
1111       // in the latter case:
1112       // ! LaTeX Error: Cannot determine size of graphic ...
1113       // FIXME: only for dvi
1114       return;
1115     }
1116 
1117     String command = this.settings.getCommand(ConverterCategory.EbbCmd);
1118     File workingDir = file.getParentFile();
1119     String[] args = buildNullArguments(this.settings.getEbbOptions(), file);
1120 
1121     // Creation of .xbb files for driver dvipdfmx
1122     // FIXME: literal
1123     args[0] = "-x";
1124     File resFile = TexFileUtils.replaceSuffix(file, SUFFIX_XBB);
1125 
1126     this.log
1127         .debug("Running " + command + " twice on '" + file.getName() + "'. ");
1128     // may throw BuildFailureException TEX01,
1129     // may log EEX01, EEX02, EEX03, WEX04, WEX05
1130     this.executor.executeEnvR0(workingDir, this.settings.getTexPath(), //****
1131         command, args, resFile);
1132 
1133     // Creation of .bb files for driver dvipdfm
1134     // FIXME: literal
1135     args[0] = "-m";
1136     resFile = TexFileUtils.replaceSuffix(file, SUFFIX_BB);
1137 
1138     this.executor.executeEnvR0(workingDir, this.settings.getTexPath(), //****
1139         command, args, resFile);
1140   }
1141 
1142   /**
1143    * Returns an array of strings,
1144    * where the 0th entry is <code>null</code>
1145    * and a placeholder for option <code>-x</code> or <code>-m</code>
1146    * when used by {@link #runEbbByConfig(File)}
1147    * and for the export option
1148    * when used by {@link #runSvg2Dev(File, LatexDev, boolean)}
1149    * then follow the options from <code>options</code>
1150    * and finally comes the name of <code>file</code>.
1151    */
1152   protected static String[] buildNullArguments(String options, File file) {
1153     if (options.isEmpty()) {
1154       return new String[] {null, file.getName()};
1155     }
1156     String[] optionsArr = options.split(" ");
1157     String[] args = new String[optionsArr.length + 2];
1158     System.arraycopy(optionsArr, 0, args, 1, optionsArr.length);
1159     args[args.length - 1] = file.getName();
1160 
1161     assert args[0] == null;
1162     return args;
1163   }
1164 
1165   /**
1166    * Deletes the graphic files
1167    * created from the svg-file <code>svgFile</code>.
1168    * <p>
1169    * Logging:
1170    * EFU05: Failed to delete file
1171    */
1172   private void clearTargetJpgPng(File file) {
1173     this.log
1174         .info("Deleting bounding boxes of file '" + file + "' if present. ");
1175     // may log EFU05
1176     deleteIfExists(file, SUFFIX_XBB);
1177     deleteIfExists(file, SUFFIX_BB);
1178     // deleteIfExists(svgFile, SUFFIX_PSTEX );
1179     // deleteIfExists(file, SUFFIX_PDF );
1180     // FIXME: this works for pdf but not for dvi:
1181     // even in the latter case, .pdf and .pdf_tex are created
1182   }
1183 
1184   /**
1185    *
1186    * <p>
1187    * Logging:
1188    * EFU05: Failed to delete file
1189    */
1190   private void deleteIfExists(File file, String suffix) {
1191     File delFile = TexFileUtils.replaceSuffix(file, suffix);
1192     if (!delFile.exists()) {
1193       return;
1194     }
1195     // may log EFU05
1196     this.fileUtils.deleteOrError(delFile, false);
1197     return;
1198   }
1199 
1200   /**
1201    * Returns an optional covering a <code>LatexMainDesc</code> 
1202   * if and only if <code>texFile</code> is recognized 
1203   * as latex main file which includes that it is readable.
1204    * If it is not readable, this method logs a warning 
1205   * and returns an empty optional.
1206    * <p>
1207    * Logging:
1208    * <ul>
1209    * <li>WFU03: cannot close
1210    * <li>WPP02: tex file may be latex main file
1211    * <ul>
1212    *
1213    * @param texFile
1214    *    the tex-file to decide on whether it is a latex main file. 
1215    * @return
1216   *    An {@link Optional} containing a {@link LatexMainDesc} if and only if 
1217   *    <code>texFile</code> is definitively a latex main file 
1218   *    in particular it is readable. 
1219   *    This can be asked by {@link Optional#isPresent()}. 
1220    */
1221   // used
1222   // by addIfLatexMain(File, Collection) and
1223   // by clearTargetTexIfLatexMain(File)
1224   private Optional<LatexMainDesc> optLatexMainFile(File texFile) {
1225     assert texFile.exists() && !texFile.isDirectory()
1226         : "Expected existing regular tex file " + texFile;
1227     // may log WFU03 cannot close
1228     FileMatch fileMatch = this.fileUtils.getMatchInFile(texFile,
1229         this.settings.getPatternLatexMainFile());
1230     if (!fileMatch.isFileReadable()) {
1231       this.log.warn("WPP02: Cannot read tex file '" + texFile
1232           + "'; may bear latex main file. ");
1233       return Optional.empty();
1234     }
1235     // System.out.println("readable and match: "+fileMatch.doesExprMatch()+" "+texFile);
1236     // if (!fileMatch.doesExprMatch()) {
1237     //   this.log.error("EPPXX: Pattern for detecting latex main file does not match,  '"
1238     //   + texFile + "' please correct; for the moment assume no latex main file. ");
1239     //   return Optional.empty();
1240     // }
1241 
1242     // return LatexMainDesc.getLatexMain(texFile, fileMatch.getMatcher());
1243     // // return fileMatch.doesExprMatch()
1244     // //   ? LatexMainDesc.getLatexMain(texFile, fileMatch.getMatcher())
1245     // //   : Optional.empty();
1246    return fileMatch.doesExprMatch()
1247       ? Optional.of(new LatexMainDesc(texFile, fileMatch.getMatchResult()))
1248       : Optional.empty();
1249   }
1250 
1251   /**
1252    * If the tex-file <code>texFile</code> is a latex main file,
1253    * add the according description to <code>latexMainFiles</code>.
1254    * <p>
1255    * Logging:
1256    * <ul>
1257    * <li>WFU03: cannot close
1258    * <li>WPP02: tex file may be latex main file
1259    * <ul>
1260    *
1261    * @param texFile
1262    *    the tex-file the description of which 
1263   *    is to be added to <code>latexMainDescs</code>
1264    *    if <code>texFile</code> is a latex main file.
1265    * @param latexMainDescs
1266    *    the collection of descriptions of latex main files found so far.
1267    */
1268   // invoked only by tex.procSrc(File, LatexPreProcessor)
1269   private void addIfLatexMain(File texFile,
1270       Collection<LatexMainDesc> latexMainDescs) {
1271     // may log WFU03, WPP02
1272     Optional<LatexMainDesc> optTexFileDesc = optLatexMainFile(texFile);
1273     //System.out.println("is main file: "+optTexFileDesc.isPresent()+" "+texFile);
1274     if (optTexFileDesc.isPresent()) {
1275       LatexMainDesc desc = optTexFileDesc.get();
1276       String docClass = desc.getDocClass();
1277       this.log.info("Detected " + docClass + "-file '" + texFile + "'. ");
1278       latexMainDescs.add(desc);
1279     }
1280   }
1281 
1282   /**
1283    * Deletes the files
1284    * created from the tex-file <code>texFile</code>,
1285    * if that is a latex main file.
1286    * <p>
1287    * Logging:
1288    * <ul>
1289    * <li>WPP02: tex file may be latex main file
1290    * <li>WFU01: Cannot read directory...
1291    * <li>WFU03: cannot close tex file
1292    * <li>EFU05: Failed to delete file
1293    * </ul>
1294    *
1295    * @param texFile
1296    *    the tex-file of which the created files shall be deleted
1297    *    if it is a latex main file.
1298   * @return
1299   *    whether <code>texFile</code> is a latex main file. 
1300    */
1301   private boolean clearTargetTexIfLatexMain(File texFile) {
1302     // exclude files which are no latex main files
1303     // may log WFU03, WPP02
1304     if (optLatexMainFile(texFile).isEmpty()) {
1305       return false;
1306     }
1307     this.log.info("Deleting targets of latex main file '" + texFile + "'. ");
1308     boolean allowsDir = true;
1309     FileFilter filter = TexFileUtils.getFileFilter(texFile,
1310         this.settings.getPatternCreatedFromLatexMain(), allowsDir);
1311     // may log WFU01, EFU05
1312     this.fileUtils.deleteX(texFile, filter, true);
1313     return true;
1314   }
1315 
1316   private void clearIfTexNoLatexMain(File texFile) {
1317     this.log.info("Deleting aux file of tex file '" + texFile
1318         + "' if present (included). ");
1319     deleteIfExists(texFile, LatexProcessor.SUFFIX_AUX);
1320   }
1321 
1322   /**
1323    * Detects files in the directory represented by <code>node</code>
1324    * and in subdirectories recursively:
1325    * <ul>
1326    * <li>
1327    * if <code>doPreprocessingInternally</code> is set, 
1328    * processes the types of files for which {@link SuffixHandler#isToBePreprocessed()} is set, 
1329    * This applies to various graphic formats incompatible with LaTeX
1330    * are converted into formats which can be inputed or included directly
1331    * into a latex file.
1332    * <li>
1333    * returns the set of descriptions of latex main files and skips other TEX files.
1334    * <li>skips files which are not compiled directly (which are all TEX files) 
1335    * and which are not suitable for preprocessing. 
1336    * Currently these are solelly BIB files. 
1337    * </ul>
1338    * <p>
1339    * Logging:
1340    * <ul>
1341    * <li>WFU03: cannot close
1342    * <li>WPP02: tex file may be latex main file
1343    * <li>WPP03: Skipped processing of files with suffixes ...
1344    * <li>WPP05: Included tex files which are no latex main files 
1345    * <li>WPP06: Included tex files which are no latex main files 
1346    * <li>WPP07: inluded/excluded files not identified by their names.
1347    * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1348    * if running graphic processors failed.
1349    * <li>EFU07, EFU08, EFU079: if filtering a file fails.
1350    * </ul>
1351    *
1352    * @param dir
1353    *    represents the tex source directory or a subdirectory.
1354    * @param node
1355    *    a node associated with <code>dir</code>.
1356    * @param doPreprocessingInternally
1357    *    whether preprocessing shall be done internally. 
1358    *    If not, this is done, e.g. by {@link Converter#Latexmk}. 
1359    * @return
1360    *    the collection of descriptions of latex main files.
1361    * @throws BuildFailureException
1362    *    TEX01 invoking
1363    * {@link #processGraphicsSelectMain(File,DirNode,Collection,Collection,boolean)}
1364    */
1365   // used in LatexProcessor.create()
1366   // and in LatexProcessor.processGraphics() only
1367   // where 'node' represents the tex source directory
1368   Collection<LatexMainDesc> processGraphicsSelectMain(File dir, DirNode node, boolean doPreprocessingInternally)
1369       throws BuildFailureException {
1370 
1371     Collection<String> skippedSuffixes = new TreeSet<String>();
1372     Collection<LatexMainDesc> latexMainDescs = new TreeSet<LatexMainDesc>();
1373     if (this.settings.getReadTexSrcProcDirRec()) {
1374       // may throw BuildFailureException TEX01,
1375       // may log EEX01, EEX02, EEX03,
1376       // WEX04, WEX05, WFU03, WPP02, EFU06
1377       processGraphicsSelectMainRec(dir, node, skippedSuffixes, latexMainDescs, doPreprocessingInternally);
1378     } else {
1379       // may throw BuildFailureException TEX01,
1380       // may log EEX01, EEX02, EEX03,
1381       // WEX04, WEX05, WFU03, WPP02, EFU07, EFU08, EFU09
1382       processGraphicsSelectMain(dir, node, skippedSuffixes, latexMainDescs, doPreprocessingInternally);
1383     }
1384 
1385     if (!skippedSuffixes.isEmpty()) {
1386       this.log.warn("WPP03: Skipped processing of files with suffixes "
1387           + skippedSuffixes + ". ");
1388     }
1389 
1390     Map<String, LatexMainDesc> name2desc = new HashMap<String, LatexMainDesc>();
1391     LatexMainDesc oldValue;
1392     Set<String> keysOverwritten = new HashSet<String>();
1393     String key;
1394     for (LatexMainDesc mainDesc : latexMainDescs) {
1395       key = mainDesc.xxxFile.getName();
1396       oldValue = name2desc.put(key, mainDesc);
1397       if (oldValue != null) {
1398         keysOverwritten.add(key);
1399       }
1400     }
1401 
1402     // original latexMainFiles, not modified by inclusion and exclusion 
1403     Set<String> keySetOrg = new HashSet<String>(name2desc.keySet());
1404 
1405     Set<String> mainFilesIncluded = this.settings.getMainFilesIncluded();
1406     if (!mainFilesIncluded.isEmpty()) {
1407       // check that mainFilesIncluded is a subset of name2file.keySet()
1408       if (!keySetOrg.containsAll(mainFilesIncluded)) {
1409         Set<String> includedNotMainFiles =
1410             new HashSet<String>(mainFilesIncluded);
1411         includedNotMainFiles.removeAll(keySetOrg);
1412         assert !includedNotMainFiles.isEmpty();
1413         this.log
1414             .warn("WPP05: Included latex files which are not latex main files: "
1415                 + includedNotMainFiles + ". ");
1416       }
1417       name2desc.keySet().retainAll(mainFilesIncluded);
1418     }
1419 
1420 
1421     Set<String> mainFilesExcluded = this.settings.getMainFilesExcluded();
1422     // check that mainFilesIncluded is a subset of name2file.keySet()
1423     if (!keySetOrg.containsAll(mainFilesExcluded)) {
1424       Set<String> excludedNotMainFiles = new HashSet<String>(mainFilesExcluded);
1425       excludedNotMainFiles.removeAll(keySetOrg);
1426       assert !excludedNotMainFiles.isEmpty();
1427       this.log.warn("WPP06: Excluded latex files "
1428       + "which are not latex main files: " + excludedNotMainFiles + ". ");
1429     }
1430     name2desc.keySet().removeAll(mainFilesExcluded);
1431 
1432     if (!(mainFilesIncluded.isEmpty() && mainFilesExcluded.isEmpty())) {
1433       Set<String> inclExcl = new HashSet<String>(mainFilesIncluded);
1434       inclExcl.addAll(mainFilesExcluded);
1435       inclExcl.retainAll(keysOverwritten);
1436       if (!inclExcl.isEmpty()) {
1437         this.log.warn("WPP07: Included/Excluded latex main files "
1438         + "not identified by their name: " + inclExcl + ". ");
1439       }
1440       this.log.info("After inclusion/exclusion latex main files are "
1441           + name2desc.keySet() + ". ");
1442     }
1443 
1444     return name2desc.values();
1445   }
1446 
1447   /**
1448    * <p>
1449    * Logging:
1450    * <ul>
1451    * <li>WFU03: cannot close file
1452    * <li>EFU07, EFU08, EFU09: if filtering of a file fails (svg)
1453    * <li>WPP02: tex file may be latex main file
1454    * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1455    * if applications for preprocessing graphic files failed.
1456    * </ul>
1457    *
1458    * @param dir
1459    *    represents the tex source directory or a subdirectory.
1460    * @param node
1461    *    a node associated with <code>dir</code>.
1462    * @param skippedSuffixes
1463    *    the collection of suffixes of files with handling skipped so far
1464    *    because there is no handler.
1465    *    FIXME: interesting for files without suffix or for hidden files.
1466    * @param latexMainDescs
1467    *    the collection of descriptions of latex main files found so far.
1468    * @throws BuildFailureException
1469    *    TEX01 invoking
1470    *    {@link LatexPreProcessor.SuffixHandler#procSrc(File, LatexPreProcessor)}
1471    *    only for {@link LatexPreProcessor.SuffixHandler#fig},
1472    *    {@link LatexPreProcessor.SuffixHandler#gp} and
1473    *    {@link LatexPreProcessor.SuffixHandler#mp}
1474    *    because these invoke external programs.
1475    */
1476   private void processGraphicsSelectMain(File dir, DirNode node,
1477       Collection<String> skippedSuffixes,
1478       Collection<LatexMainDesc> latexMainDescs,
1479       boolean doPreprocessingInternally) throws BuildFailureException {
1480 
1481     assert node.isValid();// i.e. node.regularFile != null
1482     // FIXME: processing of the various graphic files
1483     // may lead to overwrite
1484     // FIXME: processing of the latex main files
1485     // may lead to overwrite of graphic files or their targets
1486 
1487     File file;
1488     String suffix;
1489     SuffixHandler handler;
1490     Collection<LatexMainDesc> latexMainDescsLocal =
1491         new TreeSet<LatexMainDesc>();
1492     Map<File, SuffixHandler> file2handler = new TreeMap<File, SuffixHandler>();
1493     for (String fileName : node.getRegularFileNames()) {
1494       file = new File(dir, fileName);
1495       if (file.isHidden()) {
1496         this.log.debug("Skipping hidden file '" + file + "'. ");
1497         continue;
1498       }
1499       // TBD: treat discrepancy between .filename in unix (hidden) and windows... normal 
1500       // Maybe getSuffix is empty, if the rest of the name would be empty. 
1501       suffix = TexFileUtils.getSuffix(file);
1502       handler = SUFFIX2HANDLER.get(suffix);
1503       if (handler == null) {
1504         this.log.debug("Skipping file '" + file + "' without handler. ");
1505         // warning on skipped files if not hidden.
1506         skippedSuffixes.add(suffix);
1507         continue;
1508       }
1509       // Either performs transformation now
1510       // or schedule for later (latex main files)
1511       // or do nothing if no targets like bib-files
1512       // or tex-files to be inputted.
1513 
1514       if (handler.isToBePreprocessed() && !doPreprocessingInternally) {
1515         continue;
1516       }
1517       assert !handler.isToBePreprocessed() || doPreprocessingInternally;
1518 
1519       // may throw BuildFailureException TEX01,
1520       // may log EEX01, EEX02, EEX03, WEX04, WEX05
1521       // WFU03, WPP02
1522       handler.scheduleProcSrc(file, file2handler, this, latexMainDescsLocal);
1523     } // for
1524 
1525     latexMainDescs.addAll(latexMainDescsLocal);
1526 
1527     // From now on it is about processing graphic files 
1528     // and to message bib-files (for bibliography and for glossary)
1529     // The latter is required only for create 
1530 
1531     // remove sources from file2handler.keySet()
1532     // if created by local latex main files
1533     FileFilter filter;
1534     for (LatexMainDesc lmDesc : latexMainDescsLocal) {
1535       File lmFile = lmDesc.texFile;
1536       filter = TexFileUtils.getFileFilter(lmFile,
1537           this.settings.getPatternCreatedFromLatexMain(), false);
1538       Iterator<File> iter = file2handler.keySet().iterator();
1539       File src;
1540       while (iter.hasNext()) {
1541         src = iter.next();
1542         if (filter.accept(src)) {
1543           // FIXME: maybe this is too much:
1544           // better just one warning per latex main file
1545           // or just suffixes, i.e. handlers
1546           this.log.warn("WPP04: Skip processing '" + src
1547               + "': interpreted as target of '" + lmFile + "'. ");
1548           iter.remove();
1549           continue;
1550         }
1551         // Here, src is not overwritten processing lmFile
1552         // FIXME: to be checked, whether this is also true
1553         // for targets of src
1554       }
1555     }
1556 
1557     // Here process file, except tex (bib at least info)
1558     // with associated handler
1559     // FIXME: How to ensure, that nothing is overwritten?
1560     // NO: if a file is overwritten, then it is no source
1561     // and needs no processing
1562     for (Map.Entry<File, SuffixHandler> entry : file2handler.entrySet()) {
1563       // procSrc may throw BuildFailureException TEX01
1564       // and may log WFU03, WPP02,
1565       // EEX01, EEX02, EEX03, WEX04, WEX05 and EFU07, EFU08, EFU09
1566       entry.getValue().procSrc(entry.getKey(), this);
1567     }
1568   }
1569 
1570   /**
1571    * Like
1572    * {@link #processGraphicsSelectMain(File,DirNode,Collection,Collection,boolean)}
1573    * but with recursion to subdirectories.
1574    */
1575   private void processGraphicsSelectMainRec(File dir, DirNode node,
1576       Collection<String> skipped, Collection<LatexMainDesc> latexMainDescs,
1577       boolean processGrpFiles)
1578       throws BuildFailureException {
1579     processGraphicsSelectMain(dir, node, skipped, latexMainDescs, processGrpFiles);
1580 
1581     // go on recursively with subdirectories
1582     for (Map.Entry<String, DirNode> entry : node.getSubdirs().entrySet()) {
1583       // may throw BuildFailureException TEX01,
1584       // may log EEX01, EEX02, EEX03, WEX04, WEX05, WPP03
1585       // WFU03, WPP02, EFU06
1586       processGraphicsSelectMainRec(new File(dir, entry.getKey()),
1587           entry.getValue(), skipped, latexMainDescs, processGrpFiles);
1588     }
1589   }
1590 
1591   /**
1592    * Deletes all created files
1593    * in the directory represented by <code>texDir</code>
1594    * tracing subdirectories recursively.
1595    * For details of deletions within a single directory
1596    * see {@link #clearCreated(File, DirNode)}.
1597    * <p>
1598    * Logging:
1599    * <ul>
1600    * <li>WPP02: tex file may be latex main file
1601    * <li>WFU01: Cannot read directory...
1602    * <li>WFU03: cannot close tex file
1603    * <li>EFU05: Failed to delete file
1604    * </ul>
1605    *
1606    * @param texDir
1607    *    represents the tex source directory or a subdirectory.
1608    */
1609   // invoked in LatexProcessor.clearAll() only
1610   void clearCreated(File texDir) {
1611     clearCreated(texDir, new DirNode(texDir, this.fileUtils));
1612   }
1613 
1614   /**
1615    * Deletes all created files
1616    * in the directory represented by <code>node</code>, recursively.
1617    * In each directory, the sub-directories are not deleted themselves
1618    * but cleaned recursively, depth first.
1619    * The other files are cleaned, i.e.
1620    * their targets are deleted in an ordering reverse to creation
1621    * proceeding in the following steps:
1622    * <ul>
1623    * <li>
1624    * First the targets of the latex main files are deleted,
1625    * whereas the targets of the graphic (source) files
1626    * are just scheduled for deletion.
1627    * For details see
1628    * {@link LatexPreProcessor.SuffixHandler#clearTarget(File, LatexPreProcessor, Map)}.
1629    * FIXME: what about deletion of a graphic source file in this course?
1630    * <li>
1631    * Then the graphic source files scheduled are un-scheduled
1632    * if deleted by some latex main file.
1633    * <li>
1634    * Finally, the targets of the graphic souce files are deleted.
1635    * FIXME: what if this results in deletion of a graphic source file?
1636    * </ul>
1637    * Then the files with handler
1638    * If a file has a prefix without handler,
1639    * (see {@link SuffixHandler#getSuffix()}) it is ignored.
1640    * Else its target is cleared as described in
1641    * {@link SuffixHandler#clearTarget(File, LatexPreProcessor, Map)}.
1642    * <p>
1643    * Logging:
1644    * <ul>
1645    * <li>WPP02: tex file may be latex main file
1646    * <li>WFU01: Cannot read directory...
1647    * <li>WFU03: cannot close tex file
1648    * <li>EFU05: Failed to delete file
1649    * </ul>
1650    *
1651    * @param dir
1652    *    represents the tex source directory or a subdirectory.
1653    * @param node
1654    *    a node associated with <code>dir</code>.
1655    */
1656   private void clearCreated(File dir, DirNode node) {
1657     assert dir.isDirectory() : "Expected existing directory " + dir
1658         + " to be cleared. ";
1659     // Clearing depth first is mandatory; else the above assertion is invalid 
1660 
1661     // clear recursively depth first 
1662     for (Map.Entry<String, DirNode> entry : node.getSubdirs().entrySet()) {
1663       // may log WPP02, WFU01, WFU03, EFU05
1664       clearCreated(new File(dir, entry.getKey()), entry.getValue());
1665     }
1666 
1667     File file;
1668     SuffixHandler handler;
1669     Map<File, SuffixHandler> file2handler = new TreeMap<File, SuffixHandler>();
1670     for (String fileName : node.getRegularFileNames()) {
1671       file = new File(dir, fileName);
1672       handler = SUFFIX2HANDLER.get(TexFileUtils.getSuffix(file));
1673       if (handler != null) {
1674         // either clear targets now or schedule for clearing
1675         // (in particular do nothing if no target)
1676         // may log WPP02, WFU01, WFU03, EFU05
1677         handler.clearTarget(file, this, file2handler);
1678       }
1679     }
1680     // clear targets of all still existing files
1681     // which just scheduled for clearing
1682     for (Map.Entry<File, SuffixHandler> entry : file2handler.entrySet()) {
1683       file = entry.getKey();
1684       if (file.exists()) {
1685         entry.getValue().clearTarget(file, this);
1686       }
1687     }
1688   }
1689 
1690   // FIXME: suffix for tex files containing text and including pdf
1691 }