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 <language> <optionsGen> <optionsPdfEps> 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 <language> <optionsGen> <optionsPdfEps> -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 }