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.io.IOException;
24 import java.io.InputStream;
25 import java.io.PrintStream;
26 import java.time.Instant;
27 import java.time.ZonedDateTime;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.TreeMap;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.SortedSet;
36 import java.util.TreeSet;
37
38 import com.florianingerl.util.regex.Matcher;
39 import com.florianingerl.util.regex.Pattern;
40
41 //import java.util.regex.Pattern;
42 //import java.util.regex.Matcher;
43
44 import eu.simuline.m2latex.core.CommandExecutor.CmdResult;
45 // TBD: all this must be eliminated.
46 // import eu.simuline.m2latex.antTask.LatexCfgTask;
47 // import eu.simuline.m2latex.antTask.LatexClrTask;
48 import eu.simuline.m2latex.mojo.CfgLatexMojo;
49 // import eu.simuline.m2latex.mojo.ChkMojo;
50 // import eu.simuline.m2latex.mojo.ClearMojo;
51 // import eu.simuline.m2latex.mojo.GraphicsMojo;
52
53 // idea: use latex2rtf and unoconv
54 // idea: targets for latex2html, latex2man, latex2png and many more.
55
56 // TBD: rework
57 /**
58 * The latex processor creates various output from latex sources
59 * including also preprocessing of graphic files in several formats.
60 * This is the core class of this piece of software.
61 * The main method is {@link #create(SortedSet<Target>)} which is executed by
62 * the ant task
63 * and by the maven plugin given by { @link CfgLatexMojo}.
64 * Also important are {@link #clearAll()} which is executed by
65 * the maven plugin given by { @link ClearMojo}.
66 * also {@link #processGraphics()} which is executed by
67 * the maven plugin given by { @link GraphicsMojo}
68 * which is helpful for information development.
69 * <p>
70 * This class delegates preprocessing of the graphic files,
71 * selection of the latex main files and deleting their targets
72 * to {@link LatexPreProcessor}.
73 * Processing of the latex main files is done in
74 * {@link #create(SortedSet<Target>)}
75 * according to the target(s) given by the parameters.
76 * The elements of the enumeration {@link Target} use methods
77 * {@link #processLatex2rtf(LatexMainDesc)},
78 * {@link #processLatex2dvi(LatexMainDesc)},
79 * {@link #processLatex2pdf(LatexMainDesc)},
80 * {@link #processLatex2html(LatexMainDesc)},
81 * {@link #processLatex2odt(LatexMainDesc)},
82 * {@link #processLatex2docx(LatexMainDesc)}
83 * and {@link #processLatex2txt(LatexMainDesc)}.
84 */
85 public class LatexProcessor extends AbstractLatexProcessor {
86
87
88
89
90 // Note that two \\ represent a single \ in the string.
91 // Thus \\\\ represents '\\' in the pattern,
92 // which in turn represents a single \.
93 static final String PATTERN_OUFULL_HVBOX = "^(Ov|Und)erfull \\\\[hv]box \\(";
94
95 // LaTeX (notably, .tex is not needed )
96 final static String SUFFIX_TOC = ".toc";
97 final static String SUFFIX_LOF = ".lof";
98 final static String SUFFIX_LOT = ".lot";
99 final static String SUFFIX_LOL = ".lol";
100
101 final static String SUFFIX_AUX = ".aux";
102 final static String SUFFIX_DVI = ".dvi";
103 final static String SUFFIX_XDV = ".xdv";
104
105 // bibtex
106 final static String SUFFIX_BLG = ".blg";
107 final static String SUFFIX_BBL = ".bbl";
108
109 // makeindex for index
110 // unsorted and not unified index created by latex
111 final static String SUFFIX_IDX = TexFileUtils.SUFFIX_IDX;
112 // sorted and unified index created by makeindex
113 final static String SUFFIX_IND = TexFileUtils.SUFFIX_IND;
114 // log file created by makeindex
115 final static String SUFFIX_ILG = TexFileUtils.SUFFIX_ILG;
116
117 // sorted and unified glossary created by makeindex
118 final static String SUFFIX_GLS = ".gls";
119 // logging file for makeindex used with glossaries
120 final static String SUFFIX_GLG = ".glg";
121
122
123 // logging file for pythontex written by pythontexW
124 final static String SUFFIX_PLG = ".plg";
125
126
127 // file in PREFIX_PYTEX_OUT_FOLDER
128 // created by auxiliary program pythontex
129 private final static String SUFFIX_PYTXMCR = ".pytxmcr";
130
131 // latex2rtf
132 private final static String SUFFIX_RTF = ".rtf";
133
134 // odt2doc
135 private final static String SUFFIX_ODT = ".odt";
136
137 // tex4ht
138 // FIXME: works for unix only
139 final static String SUFFIX_HTML = ".html";
140
141 // pdftotext
142 private final static String SUFFIX_TXT = ".txt";
143
144 // ChkTeX: log file
145 private final static String SUFFIX_CLG = ".clg";
146
147 /**
148 * The implicit default identifier of an index
149 * hardcoded into the package <code>splitidx</code>
150 * and into the program <code>splitindex</code>.
151 */
152 private final static String IMPL_IDENT_IDX = "idx";
153
154 /**
155 * Separator <code>-</code> of the index identifier
156 * used in files <code>xxx-yy.idx</code>, <code>xxx-yy.ind</code>
157 * and <code>xxx-yy.ilg</code>.
158 * This is hardcoded by the package <code>splitidx</code>
159 * when reading <code>xxx-yy.ind</code> files
160 * but is configurable as an option in the program <code>splitindex</code>.
161 */
162 private final static String SEP_IDENT_IDX = "-";
163
164 private final ParameterAdapter paramAdapt;
165
166 /**
167 * The graphics preprocessor used by this processor.
168 */
169 private final LatexPreProcessor preProc;
170
171 /**
172 * The meta info provider used by this processor.
173 */
174 private final MetaInfo metaInfo;
175
176 /**
177 * An <code>Optional</code> which contains the name of a latex2pdf converter
178 * while processing a latex file specifying such a converter
179 * in a magic comment.
180 * Else this <code>Optional</code> is empty.
181 */
182 private Optional<String> latex2PdfCmdMagic = Optional.empty();
183
184 /**
185 * Index of the group in {@link Settings#patternMultiIndex}
186 * containing the string <code>\indexentry</code>.
187 * The default is 3 and this may hardly change.
188 */
189 final static int GRP_IDX_KEYPAGE = 3;
190
191 /**
192 * Index of the group in {@link Settings#patternMultiIndex}
193 * containing the identifier of the index.
194 * The default is 2 and this may hardly change.
195 */
196 final static int GRP_IDX_IDENT = 2;
197
198 /**
199 * Index of the group in {@link Settings#patternMultiIndex}
200 * containing the string <code>\indexentry</code>.
201 * The default is 1 and this may hardly change.
202 */
203 final static int GRP_IDX_IDXENTRY = 1;
204
205
206 // also for tests
207 LatexProcessor(Settings settings, CommandExecutor executor, LogWrapper log,
208 TexFileUtils fileUtils, ParameterAdapter paramAdapt) {
209 super(settings, executor, log, fileUtils);
210 this.paramAdapt = paramAdapt;
211 this.preProc = new LatexPreProcessor(this.settings, this.executor, this.log,
212 this.fileUtils);
213 this.metaInfo = new MetaInfo(this.executor, this.log);
214 this.latex2PdfCmdMagic = Optional.empty();
215 }
216
217 /**
218 * Creates a LatexProcessor with parameters given by <code>settings</code>
219 * which logs onto <code>log</code> and used by <code>paramAdapt</code>.
220 *
221 * @param settings
222 * the settings controlling latex processing
223 * @param log
224 * the logger to write on events while processing
225 * @param paramAdapt
226 * the parameter adapter, refers to maven-plugin or ant-task.
227 */
228 public LatexProcessor(Settings settings, LogWrapper log,
229 ParameterAdapter paramAdapt) {
230 this(settings, new CommandExecutor(log), log, new TexFileUtils(log),
231 paramAdapt);
232 }
233
234 /**
235 * Returns the set of targets to be built.
236 * If <code>desc</code> indicates
237 * that the TEX file specifies targets via a magic comment,
238 * then this is the build target to be returned.
239 * <p>
240 * Else, the target set from the configuration, <code>targetSet/code>
241 * is the base.
242 * If <code>docClasses2Targets</code> excludes targets
243 * for the document class specified by <code>desc</code>,
244 * this must be taken into account also.
245 * @param desc
246 * the description of the main file to be built.
247 * In this context, the document class is relevant
248 * and optionally the targets.
249 * @param docClasses2Targets
250 * reflects the according setting.
251 * @param targetSet
252 * reflects the setting 'targets' for goal <code>cfg</code>
253 * or the one element target set given by the goal directly.
254 * @return
255 * the set of targets to be built.
256 * @throws BuildFailureException
257 */
258 Set<Target> getTargetsForBuild(LatexMainDesc desc,
259 Map<String, Set<Target>> docClasses2Targets,
260 SortedSet<Target> targetSet)
261 throws BuildFailureException {
262
263 Optional<String> targetsMagic =
264 desc.groupMatch(LatexMainParameterNames.targetsMagic);
265 if (targetsMagic.isPresent()) {
266 this.log.info("Magic comment 'targets=" + targetsMagic.get() + "' overrides settings. ");
267 return Settings.getTargets(targetsMagic.get(),
268 TargetsContext.targetsMagic);
269 }
270 // Here, the targets are not given by a magic comment
271 // Thus they come from the setting targets
272 // combined with the targets giben by docClasses2Targets and docClass
273
274 //String docClassOpt = desc.getDocClass();
275 // if (docClassOpt.isEmpty()) {
276 // this.log.warn("WLPXX: For file '" + desc.texFile
277 // + "' targets are not restricted by unknown document class. ");
278 // reachableTargets = targetSet;
279 // } else {
280
281 String docClass = desc.getDocClass();
282 Set<Target> possibleTargets = docClasses2Targets.get(docClass);
283 if (possibleTargets == null) {
284 // Here, to docClass no restriction is attached
285 this.log.warn("WLP09: For file '" + desc.texFile
286 + "' targets are neither specified by magic comment nor restricted by document class '" + docClass
287 + "'. ");
288 return targetSet;
289 }
290 // Here, targetSet must be intersected with the targets possible for the docClass
291 Set<Target> unreachableTargets = new TreeSet<Target>(targetSet);
292 unreachableTargets.removeAll(possibleTargets);
293 if (!unreachableTargets.isEmpty()) {
294 this.log.info("Skipping targets " + unreachableTargets + ". ");
295 }
296 Set<Target> reachableTargets = new TreeSet<Target>(targetSet);
297 reachableTargets.retainAll(possibleTargets);
298 return reachableTargets;
299
300 //}
301 }
302
303 // TBD: rework
304 /**
305 * Defines creational ant-task defined in { @link LatexCfgTask}
306 * and the according goals in { @link CfgLatexMojo}
307 * and subclasses of the maven plugin.
308 * <p>
309 * This consists in reading the parameters
310 * via {@link ParameterAdapter#initialize()}
311 * processing graphic-files
312 * via {@link LatexPreProcessor#processGraphicsSelectMain(File, DirNode, boolean)}
313 * and processing the tex main files
314 * via {@link Target#processSource(LatexProcessor, LatexMainDesc)}.
315 * If a diff-tool for the target format is available
316 * and if check by diff is specified,
317 * the resulting file is checked to be equal to a specified file.
318 * A {@link BuildFailureException} is thrown
319 * if the files are not equal according to the diff tool.
320 * The resulting files are identified by its suffixes
321 * via {@link Target#getPatternOutputFiles(Settings)}.
322 * If no exception occurs before, they are copied to the target folder.
323 * Finally, by default a cleanup is performed
324 * invoking {@link TexFileUtils#cleanUp(DirNode, File, String)}.
325 * <p>
326 * Logging:
327 * <ul>
328 * <li> WFU01: Cannot read directory...
329 * <li> WFU03: cannot close file
330 * <li> EFU05: Cannot delete file
331 * <li> EFU07, EFU08, EFU09: if filtering a file fails.
332 * <li> WPP02: tex file may be latex main file
333 * <li> WPP03: Skipped processing of files with suffixes ...
334 * <li> EEX01, EEX02, EEX03, WEX04, WEX05:
335 * applications for preprocessing graphic files
336 * or processing a latex main file fails.
337 * </ul>
338 *
339 * FIXME: exceptions not really clear.
340 *
341 * @param targetSet
342 * the set of targets to be created.
343 * The target 'chk' does not create artifacts except the log file clg.
344 * The target set may have a single entry coming from the goal
345 * or, for goal {@link CfgLatexMojo}
346 * may be given by {@link Settings#getTargets()}.
347 * @throws BuildFailureException
348 * <ul>
349 * <li> TSS01 if
350 * the tex source directory does either not exist
351 * or is not a directory.
352 * <li> TSS02 if
353 * the tex source processing directory does either not exist
354 * or is not a directory.
355 * <li> TSS03 if
356 * the output directory exists and is no directory.
357 * <li> TSS04 if
358 * the target set is not a subset of the set given by {@link Target}.
359 * <li> TEX01 if
360 * invocation of applications for preprocessing graphic files
361 * or processing a latex main file fails
362 * <li> TFU01 if
363 * the target directory that would be returned
364 * exists already as a regular file.
365 * <li> TFU03, TFU04, TFU05, TFU06 if
366 * copy of output files to target folder fails.
367 * For details see
368 * {@link TexFileUtils#copyOutputToTargetFolder(File, FileFilter, File)}
369 * <li>TLP01 if difference check is specified in settings and if
370 * the artifact could not be reproduced (currently for pdf only).
371 * <li>WPP05: Included tex files which are no latex main files
372 * <li>WPP06: Included tex files which are no latex main files
373 * <li>WPP07: inluded/excluded files not identified by their names.
374 * <li>TBD: complete.
375 * </ul>
376 */
377 public void create(SortedSet<Target> targetSet) throws BuildFailureException {
378
379 // TBD: clarify whether this is superfluous
380 this.paramAdapt.initialize();
381 this.log.info("-----------create-------------");
382 this.log.debug("Settings: " + this.settings.toString());
383
384 // may emit WSS01, WSS02 TBD: correct
385 // may throw BuildFailureException TSS04, TSS11, TBD: complete.
386 Map<String, Set<Target>> docClasses2Targets =
387 this.settings.getDocClassesToTargets();
388
389 // may throw BuildFailureException TSS01
390 File texDir = this.settings.getTexSrcDirectoryFile();
391 assert texDir.exists()
392 && texDir.isDirectory() : "Expected existing tex folder " + texDir;
393
394 // may throw BuildFailureException TSS02
395 File texProcDir = this.settings.getTexSrcProcDirectoryFile();
396 assert texProcDir.exists() && texProcDir
397 .isDirectory() : "Expected existing tex processing folder " + texDir;
398
399 // constructor DirNode may log warning WFU01 Cannot read directory
400 DirNode node = new DirNode(texProcDir, this.fileUtils);
401 try {
402 // does two things at the same time
403 // - determine latexMainFiles
404 // - process graphics if preProcessInternally()
405 // may throw BuildFailureException TEX01,
406 // log warning WFU03, WPP02, WPP03,
407 // EEX01, EEX02, EEX03, WEX04, WEX05, EFU07, EFU08,
408 // EFU09: if filtering a file fails.
409 Collection<LatexMainDesc> latexMainDescs =
410 this.preProc.processGraphicsSelectMain(texProcDir, node,
411 this.settings.getLatexmkUsage().preProcessInternally());
412
413 for (LatexMainDesc desc : latexMainDescs) {
414 File texFile = desc.texFile;
415 this.log.info("Processing LaTeX file '" + desc.texFile + "'. ");
416
417 // throws BuildFailureException TFU01
418 // if targetDir would be an existing non-directory
419 File targetDir = this.fileUtils.getTargetDirectory(texFile, texDir,
420 // throws BuildFailureException TSS03
421 // if exists and is no dir
422 this.settings.getOutputDirectoryFile());
423 assert !targetDir.exists()
424 || targetDir.isDirectory() : "Expected target folder " + targetDir
425 + " folder if exists. ";
426
427 Set<Target> targetsForBuild =
428 getTargetsForBuild(desc, docClasses2Targets, targetSet);
429 // currently, this is the only magic comment applying to all targets
430 // even that is not really true: htlatex vs xhtlatex
431 // and targets like txt
432 // but chkDiff and latexmk are specific for target pdf
433 // and targets is more globally and a special case.
434 this.latex2PdfCmdMagic = desc
435 .groupMatch(LatexMainParameterNames.programMagic);
436 if (this.latex2PdfCmdMagic.isPresent()) {
437 // TBD: this comes before message converting to pdf: file.
438 // Better:
439 // - processing file..
440 // - then targets
441 if (!this.latex2PdfCmdMagic.get().equals(this.settings.getCommand(ConverterCategory.LaTeX))) {
442 this.log.info("Magic comment 'program=" + this.latex2PdfCmdMagic.get() + "' overrides settings.");
443 }
444 }
445
446 // may throw BuildFailureException TSS04
447 for (Target target : targetsForBuild) {
448 Optional<File> pdfFileCmpOpt = Optional.empty();
449 // Do it really, if an original artifact exists
450
451 // Pattern patStd = Pattern.compile("^.*pdfstandard.*$");
452 // boolean claimsStd = patStd.matcher(docMetadataString).matches();
453 MetaData docMeta = docMetadata(desc);
454 if (!docMeta.isSpecMetadata()) {
455 this.log.debug("No DocumentMetadata.");
456 }
457 Optional<String> stdX = docMeta.claimedStandard(StdType.x);
458 if (stdX.isPresent()) {
459 this.log.warn("Specified standard " + stdX.get() + " which cannot be verified. ");// TBD: identifier
460 }
461 Optional<String> stdA = docMeta.claimedStandard(StdType.a);
462 if (stdA.isPresent()) {
463 this.log.info("Specified standard " + stdA.get() + " to be verified later. ");
464 }
465 Optional<String> stdU = docMeta.claimedStandard(StdType.ua);
466 if (stdU.isPresent()) {
467 if (docMeta.tagging == TaggingState.on) {
468 this.log.info("Specified standard " + stdU.get() + " to be verified later. ");
469 } else {
470 this.log.warn("Specified standard " + stdU.get() + " without tagging; verification will fail. ");// TBD: identifier
471 }
472 }
473
474 //boolean claimsStd = docMeta.claimsSomeStandard();
475 // if (claimsStd) {
476 // this.log.info("DocumenMetadata specifies standard.");
477 // } else {
478 // this.log.info("DocumenMetadata does not specify standard.");
479 // }
480
481 if ((stdX.isPresent() || stdA.isPresent() || stdU.isPresent()) && !docMeta.withXMP) {
482 this.log.warn("Specified standard without XMP. ");
483 }
484
485
486
487
488 boolean doTryVeri = target.hasVerificationTool() && (stdA.isPresent() || stdU.isPresent());
489 boolean doTryDiff = target.hasDiffTool() && isChkDiff(desc);
490 if (doTryDiff) {
491 File pdfFileCmp = TexFileUtils.getPdfFileDiff(desc.pdfFile,
492 this.settings.getTexSrcDirectoryFile(),
493 this.settings.getDiffDirectoryFile().getAbsoluteFile());
494 this.log.debug(String.format("cmp file %s", pdfFileCmp));
495 pdfFileCmpOpt = Optional.of(pdfFileCmp);
496
497 // assert pdfFileCmp.exists();
498 if (pdfFileCmp.exists()) {
499 //runPdfInfo(desc.pdfFile);
500 long timestampSec = runPdfInfo(pdfFileCmp);
501
502 this.log.info("Process with timestamp "
503 + Instant.ofEpochSecond(timestampSec)
504 + " (" + timestampSec + "sec)");
505
506 assert pdfFileCmpOpt.isPresent();
507 this.executor.envSetTimestampAndTZutc(timestampSec);
508 } else {
509 // just a placeholder to signify that a reproducible artifact must be created
510 // but there is no original
511 this.executor.envUtc();
512 this.log.info("Process with time zone UTC. ");
513 }
514 } else {
515 this.executor.envReset();
516 // process with empty environment: neither timestamp nor timezone
517 }
518 // Here, metaDataDescOpt is either
519 // - empty meaning no diff requested
520 // - not empty but without timestamp
521 // meaning shall be reproducible, i.e. env=TZ
522 // - not empty with timestamp
523 // meaning reproducible with timestamp
524 // i.e. env=TZ, SOURCE_DATE_EPOCH, FORCE_SOURCE_DATE
525 // Strictly speaking, FORCE_SOURCE_DATE not needed for DVI2PDF
526
527 // may throw BuildFailureException TEX01,
528 // log warning EEX01, EEX02, EEX03, WEX04, WEX05
529 //target.processSource(this, desc, timestampOpt);
530
531 // perform the proper processing
532 target.processSource(this, desc);
533
534 // copy the resulting artifacts to the target folder
535 FileFilter fileFilter = TexFileUtils.getFileFilter(texFile,
536 target.getPatternOutputFiles(this.settings), false);
537 // may throw BuildFailureException
538 // TFU03, TFU04, TFU05, TFU06
539 // may log warning WFU01 Cannot read directory
540 Set<File> targetFiles = this.fileUtils
541 .copyOutputToTargetFolder(texFile, fileFilter, targetDir);
542
543 // Here, we assume that targets viable for verification and comparison
544 // have a single file as an artifact
545 if (targetFiles.size() != 1) {
546 continue;
547 }
548
549 File pdfFileAct = targetFiles.iterator().next();
550 this.log.debug(String.format("act file %s", pdfFileAct));
551 if (!pdfFileAct.exists()) {
552 // Here, the logging is done already above
553 continue;
554 }
555
556 // // but this shall be clear also above before trying to copy to target folder
557 // boolean coincideChecked = runDiffPdf(pdfFileCmpOpt.get(), pdfFileAct);
558 // null: not checked, else the result of the check as a boolean.
559 Boolean coincideChecked = diffByNeedAndReturnEqual(doTryDiff, pdfFileAct, pdfFileCmpOpt);
560
561 // Of course, verification can be done only if supported by the target.
562 // If so, for !this.settings.getVerifyByCmp(), validation must be run.
563 // else, it must only be done if no check, i.e. coincideChecked==null:
564 // or comparison did not yiels confirmation that the files coincide
565 if (doTryVeri
566 && (!this.settings.getVerifyByCmp() || coincideChecked == null || !coincideChecked)) {
567 runValidatePdf(desc);
568 }
569
570 // for coincideChecked == null diffByNeedAndReturnEqual emitted an according warnind already
571 if (coincideChecked != null && !coincideChecked) {
572 throw new BuildFailureException(
573 "TLP01: Artifact '" + pdfFileAct.getName() +
574 "' from '" + texFile + "' could not be savely reproduced. ");
575 }
576 } // target
577 } // texFile
578 } finally {
579 if (this.settings.isCleanUp()) {
580 // may log warning WFU01, EFU05
581 this.fileUtils.cleanUp(node, texProcDir, this.settings.getPrefixPytexOutFolder());
582 }
583 this.log.debug(this.settings.isCleanUp() ? ("cleanup: " + texProcDir)
584 : "No cleanup");
585 this.latex2PdfCmdMagic = Optional.empty();// superfluous
586 }
587 }
588
589 /**
590 * Runs {@link #runDiffPdf(File, File)} by need
591 * and returns whether the comparison was not executed, passed for failed.
592 * Note that currently, a diff is performed for PDF files only.
593 *
594 * @param doTryDiff
595 * whether a diff check was resquested, i.e. possible by target (PDF only)
596 * and requested globally or particularly for the actual document.
597 * @param pdfFileAct
598 * The actual PDF file
599 * @param pdfFileCmpOpt
600 * An Optional carrying another PDF file for comparison, if available.
601 * @return
602 * <code>null</code> if it was not tried to compare.
603 * This is if <code>doTryDiff</code> is false
604 * or if <code>pdfFileCmpOpt</code> contains not original PDF file for comparison.
605 * Else it returns whether the trial succeeded and showed equality.
606 * @throws BuildFailureException
607 * TEX01 if invocation of the diff command failed.
608 */
609 private Boolean diffByNeedAndReturnEqual(boolean doTryDiff,
610 File pdfFileAct,
611 Optional<File> pdfFileCmpOpt)
612 throws BuildFailureException {
613 if (!doTryDiff) {
614 this.log.debug("No artifact diff specified.");
615 return null;
616 }
617
618 File pdfFileCmp = pdfFileCmpOpt.get();
619 if (!pdfFileCmp.exists()) {
620 // TBD: adapt identifier of warning
621 // THis shall occur only if a newly created or changed file shall be
622 // reproducible
623 // in the message: 'Modification of reproducible file?' - add artifact as
624 // original file '
625 // TBD: pdfFileCmp is absolute but shall be relative to project base directory
626 this.log.warn("TLP02: Add file '" + pdfFileCmp
627 + "' to compare with artifact '" + pdfFileAct + "'! ");
628 return null;
629 }
630 this.log.debug("Prepare verification by diffing: ");
631
632 // but this shall be clear also above before trying to copy to target folder
633 // may throw BuildFailureException
634 boolean coincideChecked = runDiffPdf(pdfFileCmpOpt.get(), pdfFileAct);
635 if (coincideChecked) {
636 this.log.info("Checked result: coincides with expected artifact. ");
637 }
638 return coincideChecked;
639 }
640
641 // TBD: rework documentation
642 /**
643 * Returns whether the pdf file under construction
644 * is going to be checked whether an original is reproduced.
645 * If the magic comment <code>% !LMP chkDiff...</code> is set,
646 * it takes precedence over {@link Settings#isChkDiff()}
647 * which determines whether checks are performed
648 * only if there is no magic comment.
649 * The magic comment has two variants:
650 * besides the one with boolean values,
651 * also one without value which defaults to boolean true value.
652 * <p>
653 * This applies to all kinds of compilation,
654 * except for latexmk
655 *
656 * @param desc
657 * description of a TEX file.
658 * @return
659 * whether the PDF file under construction
660 * is checked to reproduced the original
661 * This is determined via magic comment or,
662 * if not present, via settings.
663 * @see #buildLatexmkArguments(Settings, LatexMainDesc)
664 */
665 private boolean isChkDiff(LatexMainDesc desc) {
666 boolean chkDiffSetting = this.settings.isChkDiff();
667 if (!desc.groupMatches(LatexMainParameterNames.chkDiffMagic)) {
668 // Here, result is according to setting
669 return chkDiffSetting;
670 }
671 // Here, the magic comment overrides the settings
672 Optional<String> chkDiffValue =
673 desc.groupMatch(LatexMainParameterNames.chkDiffMagicVal);
674 boolean chkDiff = chkDiffValue.isEmpty()
675 ? true
676 : Boolean.parseBoolean(chkDiffValue.get());
677 if (chkDiff != chkDiffSetting) {
678 this.log.info("Magic comment 'chkDiff=" + chkDiff + "' overrides setting.");
679 }
680 return chkDiff;
681 }
682
683 /**
684 * Represents the values of tagging in DocumentMetadata.
685 */
686 static enum TaggingState {
687 /**
688 * Tagging is active.
689 */
690 on,
691 /**
692 * Tagging is not active.
693 */
694 off,
695 /**
696 * Tagging commands are active emitting warnings by need,
697 * but finally the tagging structure is not written to the final output.
698 */
699 draft;
700 } // enum TaggingState
701
702 static enum StdType {
703 ua, a, x;
704 } // enum StdType
705
706 /**
707 * Represents the settings in DocumentMetadata.
708 * This means that all data is a request not reality.
709 * For example, standards are only claimed, not validated.
710 * Some aspects, as compression should be quite reliable,
711 * whereas for example the pdfversion can be manipulated.
712 */
713 static class MetaData {
714
715 // TBD: value must admit also nested braces
716 private static final Pattern PATTERN_METADATA = Pattern
717 .compile("(\\s*,\\s*)?(?<key>[-a-z]+)(?:\\s*=\\s*(?<value>[^{}, ]+|\\{[^{}]*\\}))?");
718
719 private static final String KEY_PDFVERSION = "pdfversion";
720 private static final String KEY_UNCOMP = "uncompress";
721 private static final String KEY_STD = "pdfstandard";
722 private static final String KEY_XMP = "xmp";
723 private static final String KEY_TAGGING = "tagging";
724
725
726 final static MetaData NO_METADATA = new MetaData(KEY_XMP + "=" +false);
727 /**
728 * The version as an optional which is empty if the version is not given explicitly.
729 */
730 final Optional<String> pdfversion;
731
732 final boolean isCompressed;
733
734 //EnumSet<PdfStandard> standards;
735
736 final boolean withXMP;
737 final TaggingState tagging;
738
739
740
741 private final Map<String, String> key2value;
742
743 private static String unwrap(String str) {
744 str = str.trim();
745 return (str.startsWith("{") && str.endsWith("}"))
746 ? str.substring(1,str.length()-1)
747 : str;
748 }
749
750 MetaData(String docMetadataString) {
751 this.key2value = new TreeMap<String, String>();
752 Matcher matcher = PATTERN_METADATA.matcher(docMetadataString);
753 String key, value;
754 while (matcher.find()) {
755 key = matcher.group("key");
756 value = matcher.group("value");
757 System.out.println("key: |" + key + "| value: |" + value + "|");
758 switch (key) {
759 case KEY_STD:
760 String org = this.key2value.get(KEY_STD);
761 value = unwrap(value).toLowerCase();
762 if (org == null) {
763 this.key2value.put(KEY_STD, value);
764
765 } else {
766 this.key2value.put(KEY_STD, org + "," + value);
767 }
768 continue;
769 case KEY_UNCOMP:
770 assert value == null;
771 // continue; fallthrough
772 default:
773 this.key2value.put(key, value);
774 } // switch
775 } // while
776
777 // no action for 'backend'
778 // pdfversion may be given explicitly or not
779 this.pdfversion = this.key2value.containsKey(KEY_PDFVERSION)
780 ? Optional.of(this.key2value.get(KEY_PDFVERSION))
781 : Optional.empty();
782 // uncompression TBD: there is a second way to reach this.
783 this.isCompressed = !this.key2value.containsKey(KEY_UNCOMP);
784
785
786 // xmp
787 this.withXMP = this.key2value.containsKey(KEY_XMP)
788 ? Boolean.parseBoolean(this.key2value.get(KEY_XMP))
789 : true; // the default value.
790
791 // tagging
792 this.tagging = this.key2value.containsKey(KEY_TAGGING)
793 ? TaggingState.valueOf(this.key2value.get(KEY_TAGGING))
794 : TaggingState.off;
795
796 // value may be well null but only for uncompress,
797 // currently, this is not checked.
798 // for pdfstandard, the key may repeat.
799 // currently this is just overwritten.
800
801 } // MetaData
802
803 // Set<String> getClaimedStandard(String xaua) {
804
805 // }
806
807 // boolean claimsSomeStandard() {
808 // return this.key2value.containsKey(KEY_STD);
809 // }
810
811 boolean isSpecMetadata() {
812 return this != NO_METADATA;
813 }
814
815 Optional<String> claimedStandard(StdType stdType) {
816 String stdsStr = this.key2value.get(KEY_STD);
817 //System.out.println("stdsStr: "+stdsStr);
818 if (stdsStr == null) {
819 return Optional.empty();
820 }
821 Matcher matcher = Pattern
822 .compile(String.format("^(.*,)?(?<std>%s-\\d+[a-z]*)", stdType.toString()))
823 .matcher(stdsStr);
824 return matcher.find()
825 ? Optional.of(matcher.group("std"))
826 : Optional.empty();
827 //boolean found = matcher.find();
828 // if (found) {
829 // return Optional.of(matcher.group("std"));
830 // } else {
831 // return Optional.empty();
832 // }
833 }
834
835 } // class MetaData
836
837
838
839 private MetaData docMetadata(LatexMainDesc desc) {
840 //boolean chkDiffSetting = this.settings.isChkDiff();
841 if (!desc.groupMatches(LatexMainParameterNames.docMetadata)) {
842 // Here, no metadata at all, so no claim
843 //this.log.info("No DocumentMetadata.");
844 return MetaData.NO_METADATA;
845 }
846 // Here, metadata are given
847 // TBD: make this more precise later.
848 Optional<String> docMetadataValue =
849 desc.groupMatch(LatexMainParameterNames.docMetadata);
850 String docMetadataString = docMetadataValue.get();
851
852 System.out.println("docMetadata: |"+docMetadataString+"|");
853 return new MetaData(docMetadataString);
854 }
855
856 /**
857 * Returns whether the tex file under consideration
858 * is going to be compiled using {@link Converter#Latexmk}.
859 * If the magic comment <code>% !LMP latexmk..</code> is set,
860 * it takes precedence over {@link Settings#getLatexmkUsage()}
861 * which determines among other whether latexmk is used.
862 * The magic comment has two variants:
863 * besides the one with boolean values,
864 * also one without value which defaults to boolean true value.
865 * <p>
866 * This applies to all kinds of compilation,
867 * except for latexmk
868 *
869 * @param desc
870 * description of a TEX file.
871 * @return
872 * whether the tex file under consideration
873 * is going to be compiled using {@link Converter#Latexmk}.
874 * This is determined via magic comment or,
875 * if not present, via settings.
876 * @see #processLatex2pdf(LatexMainDesc)
877 */
878 // TBD: generalize together with isChkDiff
879 private boolean isCompileWithLatexmk(LatexMainDesc desc) {
880 boolean runLatexmkSetting = this.settings.getLatexmkUsage().runLatexmk();
881 if (!desc.groupMatches(LatexMainParameterNames.latexmkMagic)) {
882 return runLatexmkSetting;
883 }
884 // Here, the magic comment overrides the settings
885 Optional<String> runLatexmkValue =
886 desc.groupMatch(LatexMainParameterNames.latexmkMagicVal);
887 boolean runLatexmk = (runLatexmkValue.isEmpty())
888 ? true
889 : Boolean.parseBoolean(runLatexmkValue.get());
890 if (runLatexmk != runLatexmkSetting) {
891 this.log.info("Magic comment 'latexmk="+runLatexmk+"' overrides settings. ");
892 }
893 return runLatexmk;
894 }
895
896 // TBD: rework
897 /*
898 * Defines check goal of the maven plugin in { @link ChkMojo}.
899 * This includes also creation of graphic files.
900 * <p>
901 * TBD: logging
902 * <ul>
903 * <li>WPP05: Included tex files which are no latex main files
904 * <li>WPP06: Included tex files which are no latex main files
905 * <li>WPP07: inluded/excluded files not identified by their names.
906 * </ul>
907 *
908 * @throws BuildFailureException
909 * <ul>
910 * <li>
911 * TSS02 if the tex source processing directory
912 * does either not existor is not a directory.
913 * <li>
914 * TEX01 invoking FIXME
915 * </ul>
916 */
917 // used in ChkMojo.execute() only
918 // FIXME: maybe sufficient not to create graphics, if no \input.
919 // Also, maybe good not to remove log file
920 // maybe good to make suffix configurable rather than hardcoded.
921 // public void checkAll() throws BuildFailureException {
922
923 // this.paramAdapt.initialize();
924 // this.log.debug("Settings: " + this.settings.toString());
925
926 // // may throw BuildFailureException TSS02
927 // File texProcDir = this.settings.getTexSrcProcDirectoryFile();
928 // assert texProcDir.exists() && texProcDir.isDirectory()
929 // : "Expected existing tex processing folder " + texProcDir;
930
931 // // constructor DirNode may log warning WFU01 Cannot read directory
932 // DirNode node = new DirNode(texProcDir, this.fileUtils);
933
934 // try {
935 // // TBD: eliminate processing graphics.
936 // // may throw BuildFailureException TEX01,
937 // // log warning WFU03, WPP02, WPP03,
938 // // EEX01, EEX02, EEX03, WEX04, WEX05, EFU07, EFU08, EFU09
939 // Collection<File> latexMainFiles = this.preProc
940 // .processGraphicsSelectMain(texProcDir, node);
941
942 // for (File latexMain : latexMainFiles) {
943 // processCheck(getLatexMainDesc(latexMain));
944 // }
945 // } finally {
946 // // FIXME: also removes the clg-files
947 // if (this.settings.isCleanUp()) {
948 // // may log warning WFU01, EFU05
949 // this.fileUtils.cleanUp(node, texProcDir);
950 // }
951 // }
952 // }
953
954 // TBD: rework
955 /**
956 * Defines graphics goal of the maven plugin in { @link GraphicsMojo}.
957 * <p>
958 * Logging:
959 * <ul>
960 * <li>WFU01: Cannot read directory
961 * <li>WFU03: cannot close file
962 * <li>EFU07, EFU08, EFU09: if filtering a file fails.
963 * <li>WPP02: tex file may be latex main file
964 * <li>WPP03: Skipped processing of files with suffixes ...
965 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
966 * if running graphic processors failed.
967 * <li>WPP05: Included tex files which are no latex main files
968 * <li>WPP06: Included tex files which are no latex main files
969 * <li>WPP07: inluded/excluded files not identified by their names.
970 * </ul>
971 *
972 * @throws BuildFailureException
973 * <ul>
974 * <li>
975 * TSS02 if the tex source processing directory
976 * does either not exist or is not a directory.
977 * <li>
978 * TEX01 invoking FIXME
979 * </ul>
980 */
981 // used in GraphicsMojo.execute() only
982 public void processGraphics() throws BuildFailureException {
983 // may throw BuildFailureException TSS02
984 File texProcDir = this.settings.getTexSrcProcDirectoryFile();
985 assert texProcDir.exists()
986 && texProcDir.isDirectory() : "Expected existing tex processing folder "
987 + texProcDir;
988
989 // constructor DirNode may log warning WFU01 Cannot read directory
990 DirNode node = new DirNode(texProcDir, this.fileUtils);
991 // TBD: eliminate here selecting the main files.
992 // may throw BuildFailureException TEX01,
993 // log warning WFU03, WPP02, WPP03,
994 // EEX01, EEX02, EEX03, WEX04, WEX05, EFU07, EFU08, EFU09
995 this.preProc.processGraphicsSelectMain(texProcDir, node, true);
996 }
997
998 // TBD: rework
999 /**
1000 * Defines clearing ant-task defined in { @link LatexClrTask}
1001 * and the according goal in { @link ClearMojo} of the maven plugin.
1002 * Consists in clearing created graphic files
1003 * and created files derived from latex main file.
1004 * <p>
1005 * The parameters this method depends on are (currently):
1006 * <ul>
1007 * <li>
1008 * {@link Settings#getTexSrcProcDirectoryFile()}
1009 * <li>
1010 * {@link Settings#getPatternLatexMainFile()}
1011 * <li>
1012 * {@link Settings#getPatternCreatedFromLatexMain()}
1013 * </ul>
1014 * <p>
1015 * Logging:
1016 * <ul>
1017 * <li>WPP02: tex file may be latex main file
1018 * <li>WFU01: Cannot read directory...
1019 * <li>WFU03: cannot close tex file
1020 * <li>EFU05: Failed to delete file
1021 * <li> WFU10, WFU11: if a config file is not written by this software
1022 * or it is not clear or the reader cannot close.
1023 * </ul>
1024 *
1025 * @throws BuildFailureException
1026 * <ul>
1027 * <li> TSS02 if the tex source processing directory
1028 * does either not exist or is not a directory.</li>
1029 * <li> TLP04 if a config file cannot be decided whether to be cleared
1030 * or could not be cleared. </li>
1031 * </ul>
1032 * TBD: synchronize: Really a throwable?
1033 */
1034 public void clearAll() throws BuildFailureException {
1035 this.paramAdapt.initialize();
1036 this.log.debug("Settings: " + this.settings.toString());
1037
1038 // may throw BuildFailureException TSS02
1039 File texProcDir = this.settings.getTexSrcProcDirectoryFile();
1040 assert texProcDir.exists()
1041 && texProcDir.isDirectory() : "Expected existing tex processing folder "
1042 + texProcDir;
1043
1044 // constructor DirNode may log warning WFU01 Cannot read directory
1045 // clearCreated may log warnings WPP02, WFU01, WFU03, EFU05
1046 this.preProc.clearCreated(texProcDir);
1047 // may log WFU10, WFU11, EFU05
1048 clearInjFiles();
1049 }
1050
1051 // FIXME: use the -recorder option to resolve dependencies.
1052 // With that option, a file xxx.fls is generated with form
1053 // PWD
1054 // /home/ernst/OpenSource/maven-latex-plugin/maven-latex-plugin.git/trunk/maven-latex-plugin/src/site/tex
1055 // INPUT /usr/local/texlive/2014/texmf.cnf
1056 // INPUT /usr/local/texlive/2014/texmf-dist/web2c/texmf.cnf
1057 // INPUT /usr/local/texlive/2014/texmf-var/web2c/pdftex/pdflatex.fmt
1058 // INPUT manualLatexMavenPlugin.tex
1059 // OUTPUT manualLatexMavenPlugin.log
1060 // INPUT /usr/local/texlive/2014/texmf-dist/tex/latex/base/article.cls
1061 // INPUT /usr/local/texlive/2014/texmf-dist/tex/latex/base/article.cls
1062 // INPUT /usr/local/texlive/2014/texmf-dist/tex/latex/base/size12.clo
1063 //
1064 // The first line starts has the form 'PWD <working directory>'
1065 // The other lines have the form '(INPUT|OUTPUT) <file>'
1066 // We distinguishe those in the installation,
1067 // here '/usr/local/texlive/2014/...' which do not change ever
1068 // and others.
1069 // In this example, the others are (unified and sorted):
1070
1071 // INPUT manualLatexMavenPlugin.tex
1072
1073 // OUTPUT manualLatexMavenPlugin.log
1074 // INPUT manualLatexMavenPlugin.aux
1075 // OUTPUT manualLatexMavenPlugin.aux
1076 // INPUT manualLatexMavenPlugin.out
1077 // OUTPUT manualLatexMavenPlugin.out
1078 // INPUT manualLatexMavenPlugin.toc
1079 // OUTPUT manualLatexMavenPlugin.toc
1080 // INPUT manualLatexMavenPlugin.lof
1081 // OUTPUT manualLatexMavenPlugin.lof
1082 // INPUT manualLatexMavenPlugin.lot
1083 // OUTPUT manualLatexMavenPlugin.lot
1084
1085 // OUTPUT manualLatexMavenPlugin.idx
1086 // INPUT manualLatexMavenPlugin.ind
1087
1088 // OUTPUT manualLatexMavenPlugin.pdf
1089
1090 // INPUT 1fig2dev.ptx
1091 // INPUT 1fig2dev.pdf
1092 // INPUT 2plt2pdf.ptx
1093 // INPUT 2plt2pdf.pdf
1094 // INPUT 4tex2pdf.ptx
1095 // INPUT 5aux2bbl.ptx
1096 // INPUT 5aux2bbl.pdf
1097 // INPUT 6idx2ind.ptx
1098 // INPUT 6idx2ind.pdf
1099 // INPUT 7tex2xml.ptx
1100 // INPUT 7tex2xml.pdf
1101 // what is missing is all to do with bibliography, i.e. the bib-file.
1102
1103 // FIXME: determine whether to use latexmk makes sense
1104
1105
1106 public String getLatex2pdfCommand() throws BuildFailureException {
1107 return this.latex2PdfCmdMagic
1108 .orElse(this.settings.getCommand(ConverterCategory.LaTeX));
1109 }
1110
1111 private String getDvi2pdfCommand() throws BuildFailureException {
1112 return this.settings.getCommand(ConverterCategory.Dvi2Pdf);
1113 }
1114
1115 /**
1116 * Runs LaTeX on on the latex main file <code>texFile</code>
1117 * described by <code>desc</code> once,
1118 * runs BibTeX, MakeIndex and MakeGlossaries by need
1119 * and returns whether a second LaTeX run is required.
1120 * The latter also holds, if a table of contents, a list of figures
1121 * or a list of tables is specified.
1122 * The output format of the LaTeX run is given by <code>dev</code>,
1123 * to be more precise by {@link LatexDev#getLatexOutputFormat()}.
1124 * <p>
1125 * A warning is logged if the LaTeX, a BibTeX run a MakeIndex
1126 * or a MakeGlossaries run fails
1127 * or if a BibTeX run or a MakeIndex or a MakeGlossary run issues a warning
1128 * in the according methods
1129 * {@link #runLatex2dev(LatexMainDesc, LatexDev)} and
1130 * {@link Auxiliary#process(LatexMainDesc, LatexProcessor)}.
1131 * <p>
1132 * Logging:
1133 * <ul>
1134 * <li>EAP01: Running <code>command</code> failed. For details...
1135 * <li>EAP02: Running <code>command</code> failed. No log file
1136 * <li>WAP04: if <code>logFile</code> is not readable.
1137 * <li>WLP04: Cannot read idx file; skip creation of index
1138 * <li>WLP05: Use package 'splitidx' without option 'split'
1139 * <li>WLP02: Cannot read blg file: BibTeX run required?
1140 * <li>WFU03: cannot close log file
1141 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1142 * if one of the commands mentioned in the throws-tag fails
1143 * </ul>
1144 *
1145 * @param desc
1146 * the description of a latex main file <code>texFile</code>
1147 * to be processed.
1148 * @param dev
1149 * the device describing the output format which is either pdf or
1150 * dvi.
1151 * See {@link LatexDev#getLatexOutputFormat()}.
1152 * @return
1153 * the number of LaTeX runs required after invocation of this method
1154 * because bibtex, makeindex or makeglossaries had been run
1155 * or to update a table of contents or a list figures or tables.
1156 * <ul>
1157 * <li>
1158 * If neither of these are present, no rerun is required.
1159 * <li>
1160 * If a bibliography, an index and a glossary is included
1161 * and a table of contents,
1162 * we assume that these are in the table of contents.
1163 * Thus two reruns are required:
1164 * one to include the bibliography or that like
1165 * and the second one to make it appear in the table of contents.
1166 * <li>
1167 * In all other cases, a single rerun suffices
1168 * </ul>
1169 * @throws BuildFailureException
1170 * TEX01 if invocation one of the following commands fails: the
1171 * <ul>
1172 * <li>latex2pdf command from
1173 * {@link #getLatex2pdfCommand()}
1174 * <li>BibTeX command from
1175 * {@link Settings#getBibtexCommand()}
1176 * <li>makeindex command from
1177 * {@link Settings#getMakeIndexCommand()}
1178 * <li>makeglossaries command
1179 * from {@link Settings#getMakeGlossariesCommand()}
1180 * </ul>
1181 * @see #processLatex2devCore(LatexMainDesc, LatexDev)
1182 * @see #processLatex2html(LatexMainDesc)
1183 * @see #processLatex2odt(LatexMainDesc)
1184 * @see #processLatex2docx(LatexMainDesc)
1185 */
1186 private int preProcessLatex2dev(LatexMainDesc desc, LatexDev dev)
1187 throws BuildFailureException {
1188
1189 // initial latex run
1190 // may throw BuildFailureException TEX01
1191 // may log warnings EEX01, EEX02, EEX03, WEX04, WEX05,
1192 // EAP01, EAP02, WAP04, WFU03
1193 runLatex2dev(desc, dev);
1194 //File texFile = desc.texFile;
1195
1196 // need not be a file with ending aux, but is source for an auxiliary process
1197 File auxFile;
1198 boolean posterioryEntryInToc = false;
1199 int minNumRunsAfter = 0;
1200 for (Auxiliary aux : Auxiliary.values()) {
1201 auxFile = desc.withSuffix(aux.extension());
1202 assert !auxFile.isDirectory();
1203 if (!aux.doesFitAuxiliary(auxFile)) {
1204 continue;
1205 }
1206 // TBC: can additional keys occur later?
1207 desc.aux2fileId.put(aux, getIdent(aux, auxFile));
1208
1209 posterioryEntryInToc = aux.mayBeEntryInToc();
1210 this.log.debug("Running auxiliary " + aux + ". ");
1211 aux.process(desc, this);
1212 minNumRunsAfter = Math.max(minNumRunsAfter,aux.numRunsAfter());
1213 } // for
1214 assert minNumRunsAfter >= 0 && minNumRunsAfter <= 2;
1215
1216 // // create bibliography, index and glossary by need
1217 // // may throw BuildFailureException TEX01
1218 // // may log warnings EEX01, EEX02, EEX03, WEX04, WEX05,
1219 // // EAP01, EAP02, WAP03, WAP04, WLP02, WFU03
1220 // boolean hasBib = runBibtex(desc);
1221 // // may both throw BuildFailureException, both TEX01
1222 // // may both log warnings EEX01, EEX02, EEX03, WEX04, WEX05,
1223 // // EAP01, EAP02, WLP04, WLP05, WAP03, WAP04, WFU03
1224 // boolean hasIdxGls = makeIndex(desc) | runMakeGlossary(desc);
1225 // boolean hasPyCode = runPythontex(desc);
1226
1227
1228 boolean hasToc = desc.withSuffix(SUFFIX_TOC).exists();
1229 if (hasToc && posterioryEntryInToc) {
1230 minNumRunsAfter = Math.max(minNumRunsAfter,2);
1231 }
1232
1233 if (hasToc
1234 || desc.withSuffix(SUFFIX_LOF).exists()
1235 || desc.withSuffix(SUFFIX_LOT).exists()
1236 || desc.withSuffix(SUFFIX_LOL).exists()) {
1237 minNumRunsAfter = Math.max(minNumRunsAfter,1);
1238 }
1239
1240 return minNumRunsAfter;
1241 }
1242
1243 private static final FileId EMPTY_FILE_ID = new FileId().finalizFileId();
1244
1245 /**
1246 * Wraps {@link Auxiliary#getIdent(File)}
1247 * catching the IOException
1248 * and transforming it into WLP10
1249 * indicating that rerun check is degraded.
1250 *
1251 * @param aux
1252 * the auxiliary which determines the aspects of
1253 * @param file
1254 * the file for which an identifier is requested.
1255 * @return
1256 * the identifier for the file <code>file</code>
1257 * tied to the auxiliary <code>aux</code>
1258 * according to {@link Auxiliary#getIdent(File)},
1259 * except if the latter throws an exception.
1260 * In that case, {@link #EMPTY_FILE_ID} is returned.
1261 */
1262 private FileId getIdent(Auxiliary aux, File file) {
1263 try {
1264 return aux.getIdent(file).finalizFileId();
1265 } catch(IOException ioe) {
1266 this.log.warn("WLP10: Degraded identifier for '" +file +
1267 "'; augmented risk not to rerun although necessary. ");
1268 return EMPTY_FILE_ID;
1269 }
1270 }
1271
1272 /**
1273 * Runs LaTeX on the latex main file <code>texFile</code>
1274 * described by <code>desc</code> once,
1275 * runs BibTeX, MakeIndex and MakeGlossaries by need
1276 * according to {@link #preProcessLatex2dev(LatexMainDesc,LatexDev)}
1277 * and reruns MakeIndex, MakeGlossaries and LaTeX
1278 * as often as needed to get all links satisfied
1279 * or as threshold {@link Settings#maxNumReRunsLatex} specifies.
1280 * <p>
1281 * Note that still no logging of warnings from a latex run is done.
1282 * This is done
1283 * in {@link #processLatex2dev(LatexMainDesc, LatexDev)}.
1284 * The exclusion of logging of warnings is indicated by the name part
1285 * 'Core'.
1286 * Processing without logging of warnings
1287 * is required by {@link #processLatex2txt(LatexMainDesc)}.
1288 * <p>
1289 * The output format of the LaTeX run is given by <code>dev</code>,
1290 * to be more precise by {@link LatexDev#getLatexOutputFormat()}.
1291 * <p>
1292 * Logging:
1293 * WLP01: if another rerun is required but the maximum number of runs
1294 * {@link Settings#getMaxNumReRunsLatex()} is reached.
1295 * Further logging is inherited by invoked methods:
1296 * <ul>
1297 * <li>WLP04: Cannot read idx file; skip creation of index
1298 * <li>WLP05: Use package 'splitidx' without option 'split'
1299 * <li>EAP01: Running <code>command</code> failed. For details...
1300 * <li>EAP02: Running <code>command</code> failed. No log file
1301 * <li>WAP04: if <code>logFile</code> is not readable.
1302 * <li>WLP02: Cannot read log file: run BibTeX/
1303 * (re)run MakeIndex/LaTeX required?
1304 * <li>WFU03: cannot close
1305 * <li>EEX01, EEX02, EEX03, WEX04, WEX05: as for
1306 * {@link #preProcessLatex2dev(LatexMainDesc, LatexDev)}
1307 * maybe caused by subsequent runs.
1308 * </ul>
1309 *
1310 * @param desc
1311 * the description of a latex main file <code>texFile</code>
1312 * to be processed.
1313 * @param dev
1314 * the device describing the output format
1315 * which is either pdf or dvi.
1316 * See {@link LatexDev#getLatexOutputFormat()}.
1317 * @throws BuildFailureException
1318 * TEX01 as for
1319 * {@link #preProcessLatex2dev(LatexMainDesc, LatexDev)}
1320 * maybe caused by subsequent runs.
1321 * @see #processLatex2dvi(LatexMainDesc)
1322 * @see #processLatex2txt(LatexMainDesc)
1323 */
1324 private void processLatex2devCore(LatexMainDesc desc, LatexDev dev)
1325 throws BuildFailureException {
1326
1327 // may throw BuildFailureException TEX01,
1328 // log warning WLP04, WLP05, EAP01, EAP02, WAP04, WLP02, WFU03,
1329 // EEX01, EEX02, EEX03, WEX04, WEX05
1330 int numLatexReRuns = preProcessLatex2dev(desc, dev);
1331
1332 String latexCmd = getLatex2pdfCommand();
1333
1334 assert numLatexReRuns == 0 || numLatexReRuns == 1 || numLatexReRuns == 2;
1335 if (numLatexReRuns > 0) {
1336 // rerun LaTeX without makeindex and makeglossaries
1337 this.log.debug("Rerun " + latexCmd + " to update table of contents, ... "
1338 + "bibliography, index, or that like. ");
1339 // may throw BuildFailureException TEX01
1340 // may log warnings EEX01, EEX02, EEX03, WEX04, WEX05,
1341 // EAP01, EAP02, WAP04, WFU03
1342 runLatex2dev(desc, dev);
1343 numLatexReRuns--;
1344 }
1345 assert numLatexReRuns == 0 || numLatexReRuns == 1;
1346
1347 boolean needLatexReRun = (numLatexReRuns == 1)
1348 || needRun(true, latexCmd,
1349 desc.logFile, this.settings.getPatternReRunLatex());
1350
1351 int maxNumReruns = this.settings.getMaxNumReRunsLatex();
1352 for (int num = 0; maxNumReruns == -1 || num < maxNumReruns; num++) {
1353 FileId fileId;
1354 for (Auxiliary aux : desc.aux2fileId.keySet()) {
1355 fileId = getIdent(aux, desc.withSuffix(aux.extension()));
1356
1357 if (desc.aux2fileId.get(aux).equals(fileId)) {
1358 continue;
1359 }
1360 this.log.debug("Rerunning auxiliary " + aux + ". ");
1361 desc.aux2fileId.put(aux, fileId);
1362 aux.process(desc, this);
1363 needLatexReRun = true;
1364 }
1365
1366 if (!needLatexReRun) {
1367 return;
1368 }
1369 this.log.debug("Latex must be rerun. ");
1370
1371
1372 // may throw BuildFailureException TEX01
1373 // may log warnings EEX01, EEX02, EEX03, WEX04, WEX05,
1374 // EAP01, EAP02, WAP04, WFU03
1375 runLatex2dev(desc, dev);
1376 needLatexReRun = needRun(true, latexCmd, desc.logFile,
1377 this.settings.getPatternReRunLatex());
1378 } // for
1379 this.log.warn("WLP01: LaTeX requires rerun but maximum number "
1380 + maxNumReruns + " reached. ");
1381 }
1382
1383 /**
1384 * Returns whether a(n other) run (see <code>another</code>)
1385 * of the application <code>application</code> is necessary
1386 * based on a pattern <code>pattern</code>
1387 * matching in the log file <code>logFile</code>.
1388 * Note that only <code>logFile</code> and <code>pattern</code>
1389 * are required unless a warning needs to be issued.
1390 * <p>
1391 * Logging:
1392 * <ul>
1393 * <li>WLP02: Cannot read log file: (re)run required?
1394 * <li>WFU03: cannot close log file
1395 * </ul>
1396 *
1397 * @param another
1398 * whether it is requested whether another run
1399 * (a 'rerun') is required.
1400 * If false, just a run is required
1401 * @param cmdStr
1402 * Determines the command string of the application to be rerun.
1403 * This may be of category {@link ConverterCategory#LaTeX},
1404 * {@link ConverterCategory#MakeIndex}
1405 * but also {@link ConverterCategory#BibTeX}.
1406 * @param logAuxFile
1407 * the log file or aux file which determines
1408 * whether to rerun <code>cmdStr</code>.
1409 * @param pattern
1410 * the pattern in the <code>logFile</code>
1411 * which determines whether to rerun <code>cmdStr</code>.
1412 * @return
1413 * whether <code>cmdStr</code> needs to be rerun
1414 * based on a pattern <code>pattern</code>
1415 * matching in the log file <code>logFile</code>.
1416 * @see TexFileUtils#matchInFile(File, String)
1417 */
1418 // used in processLatex2devCore only
1419 // TBD: eliminate Converter again and replace by ConverterCategory
1420 // including also the rerun pattern.
1421 boolean needRun(boolean another, String cmdStr, File logAuxFile,
1422 String pattern) {
1423 // may log warning WFU03: cannot close
1424 FileMatch fileMatch = this.fileUtils.getMatchInFile(logAuxFile, pattern);
1425 if (fileMatch.isFileReadable()) {
1426 return fileMatch.doesExprMatch();
1427 }
1428 this.log
1429 .warn("WLP02: Cannot read "
1430 + TexFileUtils.getSuffix(logAuxFile, false)
1431 + " file '" + logAuxFile.getName() + "'; " + cmdStr
1432 + " may require " + (another ? "re" : "") + "run. ");
1433 return false;
1434 }
1435
1436 /**
1437 * Runs LaTeX, BibTeX, MakeIndex and MakeGlossaries
1438 * on the latex main file <code>texFile</code>
1439 * described by <code>desc</code>
1440 * repeatedly as described for
1441 * {@link #processLatex2devCore(LatexMainDesc,LatexDev)}
1442 * and issue a warning if the last LaTeX run issued a warning.
1443 * </ul>
1444 * <p>
1445 * Logging:
1446 * <ul>
1447 * <li>WFU03: cannot close
1448 * <li>WAP04: if <code>logFile</code> is not readable.
1449 * <li>WLP01: if another rerun is required
1450 * but the maximum number of runs is reached.
1451 * <li>WLP03: <code>command</code> created bad boxes
1452 * <li>WLP04: <code>command</code> emitted warnings
1453 * <li>WLP04: Cannot read idx file; skip creation of index
1454 * <li>WLP05: Use package 'splitidx' without option 'split'
1455 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1456 * {@link #processLatex2devCore(LatexMainDesc, LatexDev)}
1457 * if running an exernal command fails.
1458 * </ul>
1459 *
1460 * @param desc
1461 * the description of a latex main file <code>texFile</code>
1462 * to be processed.
1463 * @param dev
1464 * the device describing the output format which is either pdf or dvi.
1465 * See {@link LatexDev#getLatexOutputFormat()}.
1466 * @throws BuildFailureException
1467 * TEX01 as for
1468 * {@link #processLatex2devCore(LatexMainDesc, LatexDev)}.
1469 * @see #needRun(boolean, String, File, String)
1470 * @see Target#pdf
1471 */
1472 private void processLatex2dev(LatexMainDesc desc, LatexDev dev)
1473 throws BuildFailureException {
1474
1475 // invocation of latexmk here is suitable if used not only for pdf but also for dvi in future
1476 // // TBD: improve this in several respect.
1477 // if (dev == LatexDev.pdf && this.settings.getLatexmkUsage() != LatexmkUsage.NotAtAll) {
1478 // this.log.info("Running latexmk bypassing direct compilation. ");
1479 // runLatexmk(desc);
1480 // return;
1481 // }
1482 // this.log.info("No latexmk because target=" + dev + " and usage=" + this.settings.getLatexmkUsage());
1483
1484 // may throw BuildFailureException TEX01,
1485 // log warning EAP01, EAP02, WAP04, WLP02, WFU03, WLP04, WLP05,
1486 // EEX01, EEX02, EEX03, WEX04, WEX05
1487 processLatex2devCore(desc, dev);
1488
1489 // emit warnings (errors are emitted by runLatex2dev and that like.)
1490 // may log warnings WFU03, WAP04, WLP03, WLP04
1491 logWarns(desc.logFile, getLatex2pdfCommand());
1492 }
1493
1494 void processLatex2dvi(LatexMainDesc desc)
1495 throws BuildFailureException {
1496 this.log.info("Converting into dvi/xdv format. ");
1497 // may throw BuildFailureException TEX01,
1498 // log warning EEX01, EEX02, EEX03, WEX04, WEX05
1499 // WFU03, WAP04, WLP03, WLP04
1500 processLatex2dev(desc, LatexDev.dvips);
1501 }
1502
1503 void processLatex2pdf(LatexMainDesc desc)
1504 throws BuildFailureException {
1505 this.log.info("Converting into pdf format. ");
1506
1507 // TBD: improve this in several respect.
1508 if (isCompileWithLatexmk(desc)) {
1509 // CAUTION: timestamp is not used directly
1510 runLatexmk(desc);
1511 return;
1512 }
1513
1514 LatexDev dev = this.settings.getPdfViaDvi();
1515
1516 // may throw BuildFailureException TEX01,
1517 // log warning EEX01, EEX02, EEX03, WEX04, WEX05, WLP04, WLP05
1518 processLatex2dev(desc, dev);
1519 // FIXME: certain figures are invisible in the intermediate dvi file,
1520 // but converstion to pdf shows that the figures are present.
1521
1522 if (dev.isViaDvi()) {
1523 // may throw BuildFailureException TEX01,
1524 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
1525 runDvi2pdf(desc);
1526 }
1527 }
1528
1529 /**
1530 * Logs errors detected in the according log file:
1531 * The log file is by replacing the ending of <code>texFile</code>
1532 * by <code>log</code>.
1533 * If the log file exists, a <em>warning</em> is logged
1534 * if the error pattern given by {@link Settings#getPatternErrLatex()}
1535 * occurs in the log file.
1536 * If the log file does not exist, an <em>error</em> is logged.
1537 * In both cases, the message logged refers to the <code>command</code>
1538 * which failed.
1539 * <p>
1540 * Logging:
1541 * <ul>
1542 * <li>EAP01: Running <code>command</code> failed. For details...
1543 * <li>EAP02: Running <code>command</code> failed. No log file
1544 * <li>WAP04: if <code>logFile</code> is not readable.
1545 * <li>WFU03: cannot close
1546 * </ul>
1547 */
1548 private void logErrs(File logFile, String command) {
1549 // may log warnings WFU03, EAP01, EAP02, WAP04
1550 logErrs(logFile, command, this.settings.getPatternErrLatex());
1551 }
1552
1553 /**
1554 * Logs warnings detected in the according log-file <code>logFile</code>:
1555 * Before logging warnings,
1556 * errors are logged via {@link #logErrs(File, String)}.
1557 * So, if the log-file does not exist,
1558 * an error was already shown and so nothing is to be done here.
1559 * If the log-file exists, a <em>warning</em> is logged if
1560 * <ul>
1561 * <li>
1562 * another LaTeX rerun is required
1563 * beyond {@link Settings#maxNumReRunsLatex},
1564 * <li>
1565 * bad boxes occurred and shall be logged
1566 * according to {@link Settings#getDebugBadBoxes()}.
1567 * <li>
1568 * warnings occurred and shall be logged
1569 * according to {@link Settings#getDebugWarnings()}.
1570 * </ul>
1571 * Both criteria are based on pattern recognized in the log file:
1572 * {@link #PATTERN_OUFULL_HVBOX} for bad boxes is fixed,
1573 * whereas {@link Settings#getPatternWarnLatex()} is configurable.
1574 * The message logged refers to the <code>command</code> which failed.
1575 * <p>
1576 * Logging:
1577 * <ul>
1578 * <li>WFU03: cannot close
1579 * <li>WAP04: if <code>logFile</code> is not readable.
1580 * <li>WLP03: <code>command</code> created bad boxes
1581 * <li>WLP04: <code>command</code> emitted warnings
1582 * </ul>
1583 *
1584 * @param logFile
1585 * the log-file to detect warnings in.
1586 * @param command
1587 * the command which created <code>logFile</code>
1588 * and which maybe created warnings.
1589 */
1590 private void logWarns(File logFile, String command) {
1591 if (!logFile.exists()) {
1592 return;
1593 }
1594 // hasErrsWarns may log warnings WFU03 cannot close, WAP04 not readable
1595 if (this.settings.getDebugBadBoxes()
1596 && hasErrsWarns(logFile, PATTERN_OUFULL_HVBOX)) {
1597 this.log.warn("WLP03: Running " + command
1598 + " created bad boxes logged in '" + logFile.getName() + "'. ");
1599 }
1600
1601 if (this.settings.getDebugWarnings()
1602 && hasErrsWarns(logFile, this.settings.getPatternWarnLatex())) {
1603 // logs warning WAP03: emitted warnings
1604 logWarn(logFile, command);
1605 }
1606 }
1607
1608 /**
1609 * Runs conversion of <code>texFile</code> to html or xhtml
1610 * after processing latex to set up the references,
1611 * bibliography, index and that like.
1612 * <p>
1613 * Logging: FIXME: incomplete
1614 * <ul>
1615 * <li>EAP01: Running <code>command</code> failed. For details...
1616 * <li>EAP02: Running <code>command</code> failed. No log file
1617 * <li>WAP04: if <code>logFile</code> is not readable.
1618 * <li>WLP02: Cannot read blg file: BibTeX run required?
1619 * <li>WFU03: cannot close log file
1620 * <li>WLP04: Cannot read idx file; skip creation of index
1621 * <li>WLP05: Use package 'splitidx' without option 'split'
1622 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1623 * if running an exernal command fails.
1624 * </ul>
1625 *
1626 * @param desc
1627 * the tex file to be processed.
1628 * @throws BuildFailureException
1629 * TEX01 as for
1630 * {@link #preProcessLatex2dev(LatexMainDesc, LatexDev)}
1631 * but also as for
1632 * {@link #runLatex2html(LatexProcessor.LatexMainDesc)}.
1633 * @see #preProcessLatex2dev(LatexMainDesc, LatexDev)
1634 * @see #runLatex2html(LatexMainDesc)
1635 * @see Target#html
1636 */
1637 // TBD: check: is it really sensible to do preprocessing?
1638 // TBD: also inconsistency: preprocessing with lualatex, xelatex, pdflatex
1639 // whereas processing is done with htlatex and with xtlatex... no lualatex involved.
1640 void processLatex2html(LatexMainDesc desc)
1641 throws BuildFailureException {
1642 this.log.info("Converting into html format. ");
1643 // may throw BuildFailureException TEX01,
1644 // log warning EAP01, EAP02, WLP04, WLP05, WAP04, WLP02, WFU03,
1645 // EEX01, EEX02, EEX03, WEX04, WEX05
1646 preProcessLatex2dev(desc, LatexDev.devViaDvi(true));
1647 // may throw BuildFailureException TEX01,
1648 // log warning EEX01, EEX02, EEX03, WEX04, WEX05
1649 runLatex2html(desc);
1650 }
1651
1652 /**
1653 * Runs conversion of <code>texFile</code>
1654 * to odt or other open office formats
1655 * after processing latex to set up the references,
1656 * bibliography, index and that like.
1657 * <p>
1658 * Logging: FIXME: incomplete
1659 * <ul>
1660 * <li>EAP01: Running <code>command</code> failed. For details...
1661 * <li>EAP02: Running <code>command</code> failed. No log file
1662 * <li>WAP04: if <code>logFile</code> is not readable.
1663 * <li>WLP02: Cannot read blg file: BibTeX run required?
1664 * <li>WFU03: cannot close log file
1665 * <li>WLP04: Cannot read idx file; skip creation of index
1666 * <li>WLP05: Use package 'splitidx' without option 'split'
1667 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1668 * if running an exernal command fails.
1669 * </ul>
1670 *
1671 * @param desc
1672 * the tex file to be processed.
1673 * @throws BuildFailureException
1674 * TEX01 as for
1675 * {@link #preProcessLatex2dev(LatexMainDesc, LatexDev)}
1676 * but also as for
1677 * {@link #runLatex2odt(LatexProcessor.LatexMainDesc)}.
1678 * @see #preProcessLatex2dev(LatexMainDesc, LatexDev)
1679 * @see #runLatex2odt(LatexMainDesc)
1680 * @see Target#odt
1681 */
1682 void processLatex2odt(LatexMainDesc desc)
1683 throws BuildFailureException {
1684 this.log.info("Converting into odt format. ");
1685 // may throw BuildFailureException TEX01,
1686 // log warning EAP01, EAP02, WAP04, WLP02, WFU03, WLP04, WLP05
1687 // EEX01, EEX02, EEX03, WEX04, WEX05
1688 preProcessLatex2dev(desc, this.settings.getPdfViaDvi());
1689 // may throw BuildFailureException TEX01,
1690 // log warning EEX01, EEX02, EEX03, WEX04, WEX05
1691 runLatex2odt(desc);
1692 }
1693
1694 /**
1695 * Runs conversion of <code>texFile</code>
1696 * to docx or other MS word formats
1697 * after processing latex to set up the references,
1698 * bibliography, index and that like.
1699 * <p>
1700 * Logging: FIXME: incomplete
1701 * <ul>
1702 * <li>EAP01: Running <code>command</code> failed. For details...
1703 * <li>EAP02: Running <code>command</code> failed. No log file
1704 * <li>WAP04: if <code>logFile</code> is not readable.
1705 * <li>WLP02: Cannot read blg file: BibTeX run required?
1706 * <li>WFU03: cannot close log file
1707 * <li>WLP04: Cannot read idx file; skip creation of index
1708 * <li>WLP05: Use package 'splitidx' without option 'split'
1709 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1710 * if running an exernal command fails.
1711 * </ul>
1712 *
1713 * @param desc
1714 * the latex main file to be processed.
1715 * @throws BuildFailureException
1716 * TEX01 as for
1717 * {@link #preProcessLatex2dev(LatexMainDesc, LatexDev)}
1718 * but also as for
1719 * {@link #runLatex2odt(LatexProcessor.LatexMainDesc)}
1720 * and for {@link #runOdt2doc(LatexMainDesc)}.
1721 * @see #preProcessLatex2dev(LatexMainDesc, LatexDev)
1722 * @see #runLatex2odt(LatexMainDesc)
1723 * @see #runOdt2doc(LatexMainDesc)
1724 * @see Target#docx
1725 */
1726 void processLatex2docx(LatexMainDesc desc)
1727 throws BuildFailureException {
1728 this.log.info("Converting into doc(x) format. ");
1729 // may throw BuildFailureException TEX0,
1730 // log warning EAP01, EAP02, WAP04, WLP02, WFU03, WLP04, WLP05
1731 // EEX01, EEX02, EEX03, WEX04, WEX05
1732 preProcessLatex2dev(desc, this.settings.getPdfViaDvi());
1733 // may throw BuildFailureException TEX0,
1734 // log warning EEX01, EEX02, EEX03, WEX04, WEX05
1735 runLatex2odt(desc);
1736 // may throw BuildFailureException TEX01,
1737 // log warning EEX01, EEX02, EEX03, WEX04, WEX05
1738 runOdt2doc(desc);
1739 }
1740
1741 /**
1742 * Runs direct conversion of <code>texFile</code> to rtf format.
1743 * <p>
1744 * FIXME: Maybe prior invocation of LaTeX MakeIndex and BibTeX
1745 * after set up the references, bibliography, index and that like
1746 * would be better.
1747 * <p>
1748 * Logging: FIXME: incomplete
1749 * <ul>
1750 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1751 * if running an exernal command fails.
1752 * </ul>
1753 *
1754 * @param desc
1755 * the tex file to be processed.
1756 * @throws BuildFailureException
1757 * TEX01 if running the latex2rtf command
1758 * returned by
1759 * {@link Settings#getLatex2rtfCommand()} failed.
1760 * @see #runLatex2rtf(File)
1761 * @see Target#rtf
1762 */
1763 void processLatex2rtf(LatexMainDesc desc) throws BuildFailureException {
1764 this.log.info("Converting into rtf format. ");
1765 // may throw BuildFailureException TEX01,
1766 // log warning EEX01, EEX02, EEX03, WEX04, WEX05
1767 runLatex2rtf(desc.texFile);//, timestamp
1768 }
1769
1770 /**
1771 * Runs conversion of <code>texFile</code> to txt format via pdf.
1772 * <p>
1773 * Logging: FIXME: incomplete
1774 * <ul>
1775 * <li>WLP04: Cannot read idx file; skip creation of index
1776 * <li>WLP05: Use package 'splitidx' without option 'split'
1777 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1778 * if running an exernal command fails.
1779 * </ul>
1780 *
1781 * @param desc
1782 * the tex file to be processed.
1783 * @throws BuildFailureException
1784 * TEX01 as for
1785 * {@link #processLatex2devCore(LatexMainDesc, LatexDev)}
1786 * and for {@link #runPdf2txt(LatexMainDesc)}.
1787 * @see #processLatex2devCore(LatexMainDesc, LatexDev)
1788 * @see #runPdf2txt(LatexMainDesc)
1789 * @see Target#txt
1790 */
1791 void processLatex2txt(LatexMainDesc desc)
1792 throws BuildFailureException {
1793 this.log.info("Converting into txt format. ");
1794 LatexDev dev = this.settings.getPdfViaDvi();
1795
1796 // may throw BuildFailureException TEX01,
1797 // log warning EAP01, EAP02, WAP04, WLP02, WFU03, WLP04, WLP05,
1798 // EEX01, EEX02, EEX03, WEX04, WEX05
1799 processLatex2devCore(desc, dev);
1800 if (dev.isViaDvi()) {
1801 // may throw BuildFailureException TEX01,
1802 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
1803 runDvi2pdf(desc);
1804 }
1805
1806 // warnings emitted by LaTex are ignored
1807 // (errors are emitted by runLatex2dev and that like.)
1808 // may throw BuildFailureException TEX01,
1809 // log warning EEX01, EEX02, EEX03, WEX04, WEX05
1810 runPdf2txt(desc);
1811 }
1812
1813 /**
1814 * Prints meta information, mainly version information
1815 * on this software and on the converters used.
1816 * <p>
1817 * WMI01: If the version string of a converter cannot be read.
1818 * WMI02: If the version of a converter is not as expected.
1819 *
1820 * @param includeVersionInfo
1821 * whether to include plain version info; else warnings only.
1822 * @return
1823 * whether a warning has been issued.
1824 * @throws BuildFailureException
1825 * <ul>
1826 * <li>TMI01: if the stream to either the manifest file
1827 * or to a property file, either
1828 * {@LINK #VERSION_PROPS_FILE} or
1829 * {@link MetaInfo.GitProperties#GIT_PROPS_FILE}
1830 * could not be created.</li>
1831 * <li>TMI02: if the properties could not be read
1832 * from one of the two property files mentioned above.</li>
1833 * <li>TSS05: if converters are excluded in the
1834 * pom which are not known.</li>
1835 * </ul>
1836 */
1837 public boolean printMetaInfo(boolean includeVersionInfo)
1838 throws BuildFailureException {
1839 // may throw BuildFailureException TSS05
1840 SortedSet<Converter> convertersExcluded =
1841 this.settings.getConvertersExcluded();
1842 return this.metaInfo.printMetaInfo(includeVersionInfo, convertersExcluded);
1843 }
1844
1845 boolean runBibtex(LatexMainDesc desc) throws BuildFailureException {
1846 String command = this.settings.getCommand(ConverterCategory.BibTeX);
1847 this.log.debug("Running " + command + " on '" + desc.xxxFile.getName() + "'. ");
1848 String[] args = buildArguments(this.settings.getBibtexOptions(), desc.xxxFile);
1849 // may throw BuildFailureException TEX01,
1850 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
1851 this.executor.executeEnvR0(desc.parentDir, // workingDir
1852 this.settings.getTexPath(), command, args, desc.withSuffix(SUFFIX_BBL));
1853
1854 File logFile = desc.withSuffix(SUFFIX_BLG);
1855 // may log EAP01, EAP02, WAP04, WFU03
1856 logErrs(logFile, command, this.settings.getPatternErrBibtex());
1857 // may log warnings WFU03, WAP03, WAP04
1858 logWarns(logFile, command, this.settings.getPatternWarnBibtex());
1859 return true;
1860 }
1861
1862 /**
1863 * Runs the MakeIndex command
1864 * given by {@link Settings#getMakeIndexCommand()}
1865 * on the idx-file corresponding with <code>texFile</code> which is assumed to exist.
1866 * <p>
1867 * Note that {@link Settings#getMakeIndexCommand()}
1868 * is invoked either directly, or, in case of a multiple index,
1869 * via {@link Settings#getSplitIndexCommand()}.
1870 * To decide whether splitting is required,
1871 * the idx-file must be read.
1872 * If this is not possible, warning WLP04 is emitted
1873 * indicating that the index is not created.
1874 * This is the only case where the return value is <code>false</code>.
1875 * <p>
1876 * Logging:
1877 * <ul>
1878 * <li>WLP04: Cannot read idx file; skip creation of index
1879 * <li>WLP05: Use package 'splitidx' without option 'split'
1880 * <li>EAP01: Running <code>makeindex</code> failed. For details...
1881 * <li>EAP02: Running <code>makeindex</code> failed. No log file
1882 * <li>WAP03: Running <code>makeindex</code> emitted warnings.
1883 * <li>WAP04: .ilg-file is not readable.
1884 * <li>WFU03: cannot close .ilg-file
1885 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
1886 * if running the makeindex command failed.
1887 * </ul>
1888 *
1889 * @param desc
1890 * the description of a latex main file <code>dviFile</code>
1891 * including the idx-file MakeIndex is to be run on.
1892 * @return
1893 * whether MakeIndex had been run, possibly via splitindex.
1894 * This is done, except if the idx-file is not readable
1895 * and so it cannot be decided whether to invoke makeindex or splitindex.
1896 * In this case the warning WLP04 is emitted.
1897 * @throws BuildFailureException
1898 * TEX01 if invocation of the makeindex command
1899 * returned by
1900 * {@link Settings#getMakeIndexCommand()} failed.
1901 */
1902 boolean runMakeSplitIndex(LatexMainDesc desc)
1903 throws BuildFailureException {
1904
1905 // determine the explicit given identifiers of indices
1906 final Set<String> explIdxIdent =
1907 this.fileUtils.collectMatchesForIdx(desc.idxFile,
1908 Pattern.compile(this.settings.getPatternMultiIndex()),
1909 GRP_IDX_IDENT);
1910 if (explIdxIdent == null) {
1911 this.log.warn("WLP04: Cannot read idx file '" + desc.idxFile.getName()
1912 + "'; skip creation of index. ");
1913 return false;
1914 }
1915 assert explIdxIdent != null;
1916
1917 // Here, explIdxIdent contains the explicit identifiers of all indices
1918 // The identifier idx may be missing or not.
1919
1920 // package splitidx is used with option split
1921 // is in general not allowed. The criteria are:
1922 // - if \jobname-xxx.idx exists for some xxx whereas \jobname.idx does not:
1923 // This occurs only for option split
1924 // and does not allow applying splitindex
1925 // as it is intended in this software.
1926 // This would require applying makeindex separately
1927 // to all \jobname-xxx.idx
1928 // - if \jobname-xxx.idx exists for some xxx
1929 // and also \jobname.idx exists but has no entry
1930 // \indexentry[xxx]{...}{..}:
1931 // This occurs only for option split
1932 // and applying splitindex yields the wrong result.
1933 // This would require applying makeindex separately
1934 // to all \jobname-xxx.idx and to \jobname.idx
1935 // - if \jobname-xxx.idx does not exist for any xxx
1936 // then all is ok, whether \jobname.idx exists or not.
1937 // If it exists, even splitidx with option split is ok.
1938
1939 // so algorithm:
1940 // determine list of these xxx for which \jobname-xxx.idx exists
1941 // if (\jobname-xxx.idx exists for some xxx) {
1942 // if (!(\jobname.idx exists &&
1943 // \jobname.idx matches some \indexentry[xxx]{...}{.. )) {
1944 // log.error(cannot handle splitidx with option split)
1945 // return false;
1946 // }
1947 // // For second condition,
1948 // // determine list of all yyy matching \indexentry[yyy]{...}{..}
1949 // // and make sure that it is non-empty.
1950 // }
1951
1952 // filter for extended raw idx-files: \jobname-xxx.idx
1953 FileFilter filter =
1954 this.fileUtils.getFileFilterReplace(desc.idxFile, SEP_IDENT_IDX + ".+");
1955 // may cause WFU01: Cannot read directory
1956 File[] idxFilesExtInDir =
1957 this.fileUtils.listFilesOrWarn(desc.idxFile.getParentFile(), filter);
1958
1959 // If the directory compising idxFile is not readable,
1960 // idxFilesExtInDir == null
1961 // Then the check for option split cannot be done.
1962
1963 if (idxFilesExtInDir != null && idxFilesExtInDir.length > 0) {
1964 // Here, idxFilesExtInDir contains the idx-files \jobname-xxx.idx
1965 if (explIdxIdent.isEmpty()) {
1966 // Here, either \jobname.idx does not exist at all
1967 // or does not contain an entry \indexentry[yyy]{...}{..}
1968
1969 this.log.warn("WLP05: Use package 'splitidx' "
1970 + "without option 'split' in '" + desc.texFile.getName() + "'. ");
1971 // this.log.warn("WLP05: Found extended idx-file " +
1972 // " without (according entry in) '" +
1973 // desc.idxFile.getName() +
1974 // "': use package 'splitidx' " +
1975 // "without option 'split'. ");
1976 }
1977 }
1978
1979 // TBD: the following is misleading
1980 // if \sindex[idx]{...} is used with a single index name idx
1981 // This counts as a multi-index but is only one.
1982 // But then we need splitindex anyway.
1983
1984 // check whether more than one index has to be created
1985 if (explIdxIdent.isEmpty()) {
1986 // may throw BuildFailureException TEX01
1987 // may log warnings EEX01, EEX02, EEX03, WEX04, WEX05,
1988 // EAP01, EAP02, WAP03, WAP04, WFU03
1989 runMakeIndex(desc);
1990 } else {
1991 // may throw BuildFailureException TEX01
1992 // may log warnings EEX01, EEX02, EEX03, WEX04, WEX05,
1993 // EAP01, EAP02, WAP03, WAP04, WFU03
1994 runSplitIndex(desc, explIdxIdent);
1995 }
1996
1997 return true;
1998 }
1999
2000 /**
2001 * Runs the MakeIndex command
2002 * given by {@link Settings#getMakeIndexCommand()}.
2003 * Note that xindy is not supported in this context.
2004 * <p>
2005 * Logging:
2006 * <ul>
2007 * <li>EAP01: Running <code>makeindex</code> failed. For details...
2008 * <li>EAP02: Running <code>makeindex</code> failed. No log file
2009 * <li>WAP03: Running <code>makeindex</code> emitted warnings.
2010 * <li>WAP04 .ilg-file is not readable.
2011 * <li>WFU03: cannot close .ilg-file
2012 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2013 * if running the makeindex command failed.
2014 * </ul>
2015 *
2016 * @param desc
2017 * the description of a latex main file <code>texFile</code>
2018 * including the idx-file MakeIndex is to be run on.
2019 * @throws BuildFailureException
2020 * TEX01 if invocation of the MakeIndex command
2021 * returned by
2022 * {@link Settings#getMakeIndexCommand()} failed.
2023 */
2024 private void runMakeIndex(LatexMainDesc desc) throws BuildFailureException {
2025
2026 String command = this.settings.getCommand(ConverterCategory.MakeIndex);
2027 // File idxFile = desc.idxFile;
2028 // this.log.debug("Running " + command + " on '" + idxFile.getName() + "'. ");
2029 // String[] args =
2030 // buildArguments(this.settings.getMakeIndexOptions(), idxFile);
2031 File xxxFile = desc.xxxFile;
2032 this.log.debug("Running " + command + " on '" + xxxFile.getName() + "'. ");
2033 String[] args =
2034 buildArguments(this.settings.getMakeIndexOptions(), xxxFile); // may throw BuildFailureException TEX01,
2035 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2036 this.executor.executeEnvR0(desc.parentDir, // workingDir
2037 this.settings.getTexPath(), command, args, desc.indFile);
2038
2039 // detect errors and warnings makeindex wrote into xxx.ilg
2040 // may log EAP01, EAP02, WAP04, WFU03
2041 logErrs(desc.ilgFile, command, this.settings.getPatternErrMakeIndex());
2042 // may log warnings WFU03, WAP03, WAP04
2043 logWarns(desc.ilgFile, command, this.settings.getPatternWarnMakeIndex());
2044 }
2045
2046 /**
2047 * Combines an array of files from a file prefix <code>filePrefix</code>,
2048 * a collection of intermediate strings <code>variant</code>
2049 * and of the suffix <code>suffix</code>.
2050 *
2051 * @param filePrefix
2052 * prefix of file name; in practice of index file without suffix
2053 * @param variant
2054 * collection of strings; in practice set of identifiers of indices
2055 * @param suffix
2056 * the suffix of a file; in practice {@link #SUFFIX_IDX}.
2057 * @return
2058 * an array of file names of the form
2059 * <code><filePrefix><ident><suffix></code>,
2060 * where <code>ident</code> runs in <code>variant</code>.
2061 */
2062 private File[] files(String filePrefix, Collection<String> variant,
2063 String suffix) {
2064 File[] res = new File[variant.size()];
2065 int idx = 0;
2066 StringBuilder strb;
2067 for (String idxIdent : variant) {
2068 strb = new StringBuilder();
2069 strb.append(filePrefix);
2070 strb.append(idxIdent);
2071 strb.append(suffix);
2072
2073 res[idx++] = new File(strb.toString());
2074 }
2075 return res;
2076 }
2077
2078 /**
2079 * Runs the SplitIndex command
2080 * given by {@link Settings#getSplitIndexCommand()}.
2081 * <p>
2082 * Logging:
2083 * Note that <code>splitindex</code> neither writes a log file
2084 * nor may it fail in itself but invoking <code>makeindex</code>
2085 * or whatever program it uses.
2086 * <ul>
2087 * <li>EAP01: Running <code>splitindex</code> failed. For details...
2088 * <li>EAP02: Running <code>splitindex</code> failed. No log file
2089 * <li>WAP03: Running <code>splitindex</code> emitted warnings.
2090 * <li>WAP04 .ilg-file is not readable.
2091 * <li>WFU03: cannot close .ilg-file
2092 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2093 * if running the splitindex command failed.
2094 * </ul>
2095 *
2096 * @param desc
2097 * the description of a latex main file <code>texFile</code>
2098 * including the idx-file SplitIndex is to be run on.
2099 * @param explIdxIdent
2100 * the set of identifiers of indices,
2101 * whether explicitly given or not in the idx file.
2102 * @throws BuildFailureException
2103 * TEX01 if invocation of the SplitIndex command returned by
2104 * {@link Settings#getSplitIndexCommand()} failed.
2105 */
2106 private void runSplitIndex(LatexMainDesc desc, Collection<String> explIdxIdent)
2107 throws BuildFailureException {
2108
2109 String splitInxCmd = this.settings.getCommand(ConverterCategory.SplitIndex);
2110 // File idxFile = desc.idxFile;
2111 // this.log
2112 // .debug("Running " + splitInxCmd + " on '" + idxFile.getName() + "'. ");
2113 // // buildArguments(this.settings.getMakeIndexOptions(), idxFile);
2114 //File idxFile = desc.idxFile;
2115 this.log
2116 .debug("Running " + splitInxCmd + " on '" + desc.xxxFile.getName() + "'. ");
2117 // buildArguments(this.settings.getMakeIndexOptions(), idxFile);
2118 // NOTE: named groups are not compatible with the lua version of splitindex
2119
2120 // set the options of splitindex set internally and not passed to makeindex or that like
2121 String groupIdent = Settings.GRP_IDENT;
2122 String[] argsSplitIndexDefault = new String[] {
2123 "-m " + this.settings.getCommand(ConverterCategory.MakeIndex),
2124 // **** no splitindex.tlu
2125 // TBD: take over in .latexmkrc also: else latexmk cannot be used with splitindex.tlu.
2126 // This is hardcoded by splitidx when writing xxx.ind
2127 "-i " + this.settings.getPatternMultiIndex(),
2128 // This is hardcoded by makeindex when writing xxx.ind
2129 "-r "+groupIdent+GRP_IDX_IDXENTRY+groupIdent+GRP_IDX_KEYPAGE, // groups in IDX_EXPL: \indexentry{...}
2130 // -s -$2 is hardcoded by splitidx when reading in the -xxx.ind-files
2131 "-s " + SEP_IDENT_IDX + groupIdent + GRP_IDX_IDENT
2132 };
2133
2134 // add the single allowed option for splitindex given by the user
2135 String argSplitindexOption = this.settings.getSplitIndexOptions();
2136 assert(argSplitindexOption.indexOf(' ')) == -1;// contains no blank, is single option or empty
2137 // Arguments are default arguments, possibly option given explicitly and file name
2138 // This is true only if getMakeIndexOptions() is not empty
2139
2140 // arguments: default options, user defined option filename
2141 String[] args = new String[argsSplitIndexDefault.length + (argSplitindexOption.isEmpty() ? 0 : 1) + 1];
2142 System.arraycopy(argsSplitIndexDefault, 0, args, 0, argsSplitIndexDefault.length);
2143 if (!argSplitindexOption.isEmpty()) {
2144 args[args.length - 2] = argSplitindexOption;
2145 }
2146 args[args.length - 1] = desc.xxxFile.getName();
2147
2148 // Add the options to be passed to makeindex or that like
2149 String optionsMakeIndex = this.settings.getMakeIndexOptions();
2150 if (!optionsMakeIndex.isEmpty()) {
2151 // Here 'args' must be replaced by 'args -- optionsMakeIndex'
2152 String[] optionsMake_IndexArr = optionsMakeIndex.split(" ");
2153 String[] optionsSplitIndexArr = args;
2154 args = new String[optionsMake_IndexArr.length + 1
2155 + optionsSplitIndexArr.length];
2156 System.arraycopy(optionsSplitIndexArr, 0, args, 0,
2157 optionsSplitIndexArr.length);
2158 args[optionsSplitIndexArr.length] = "--";
2159 System.arraycopy(optionsMake_IndexArr, 0, args,
2160 optionsSplitIndexArr.length + 1, optionsMake_IndexArr.length);
2161 }
2162
2163 // determine the resulting ind-files
2164 //explIdxIdent.add(IMPL_IDENT_IDX);
2165 String filePrefix = desc.xxxFile.toString() + SEP_IDENT_IDX;
2166 File[] indFiles = files(filePrefix, explIdxIdent, SUFFIX_IND);
2167
2168 // may throw BuildFailureException TEX01,
2169 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2170 this.executor.executeEnvR0(desc.parentDir, // workingDir
2171 this.settings.getTexPath(), splitInxCmd, args, indFiles);
2172
2173 // detect errors and warnings splitindex,
2174 // aka makeindex wrote into xxx.ilg
2175 File[] ilgFiles = files(filePrefix, explIdxIdent, SUFFIX_ILG);
2176 splitInxCmd = this.settings.getCommand(ConverterCategory.MakeIndex);
2177 for (int idx = 0; idx < explIdxIdent.size(); idx++) {
2178 // may log EAP01, EAP02, WAP04, WFU03
2179 logErrs(ilgFiles[idx], splitInxCmd,
2180 this.settings.getPatternErrMakeIndex());
2181 // may log warnings WFU03, WAP03, WAP04
2182 logWarns(ilgFiles[idx], splitInxCmd,
2183 this.settings.getPatternWarnMakeIndex());
2184 }
2185 }
2186
2187 // return whether the processor has been invoked.
2188 // TBD: clarify whether success plays a role
2189 // parece que nao usa executeEnvR0 return value.
2190 // a valuable source of information is https://www.dickimaw-books.com/latex/auxglossaries/
2191 boolean runMakeGlossary(LatexMainDesc desc)
2192 throws BuildFailureException {
2193 // message emitted by makeglossaries: makeglossaries diagnostic messages
2194 // not used by makeglossaries-light
2195 // file name without ending: parameter for makeglossaries
2196 File xxxFile = desc.xxxFile;
2197 String command = this.settings.getCommand(ConverterCategory.MakeGlossaries);
2198 this.log.debug("Running " + command + " on '" + xxxFile.getName() + "'. ");
2199 String[] args =
2200 buildArguments(this.settings.getMakeGlossariesOptions(), xxxFile);
2201 // may throw BuildFailureException TEX01,
2202 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2203 this.executor.executeEnvR0(desc.parentDir, // workingDir
2204 this.settings.getTexPath(), command, args, desc.glsFile);
2205 // TBD: check whether more than one gls file is possible.
2206
2207 Boolean isMakeIndexOrLike = this.fileUtils.withMakindexLike(desc.auxFile);
2208 // assert isMakeIndexOrLike;
2209 String patternErr, patternWarn;
2210 if (isMakeIndexOrLike != null && isMakeIndexOrLike.booleanValue()) {
2211 patternErr = this.settings.getPatternErrMakeIndex();
2212 patternWarn = this.settings.getPatternWarnMakeIndex();
2213 } else {
2214 patternErr = this.settings.getPatternErrXindy();
2215 patternWarn = this.settings.getPatternWarnXindy();
2216 }
2217 Set<String> extLogFiles = this.fileUtils.collectLogsForGloss(desc.auxFile);
2218 if (isMakeIndexOrLike == null || extLogFiles == null) {
2219 this.log.warn("WLPXX: Cannot read AUX file; skipping evaluating log files for glossaries. ");
2220 return true;
2221 }
2222 for (String ext : extLogFiles) {
2223 // detect errors and warnings makeglossaries wrote into xxx.glg
2224 File logFile = desc.withSuffix("."+ext);
2225 // may log EAP01, EAP02, WAP04, WFU03
2226 logErrs(logFile, command, patternErr);
2227 // may log warnings WFU03, WAP03, WAP04
2228 logWarns(logFile, command, patternWarn);
2229 }
2230
2231 return true;
2232 } // runMakeGlossary
2233
2234 boolean runPythontex(LatexMainDesc desc)
2235 throws BuildFailureException {
2236 File xxxFile = desc.xxxFile;
2237 String command = this.settings.getCommand(ConverterCategory.Pythontex);
2238 this.log.debug("Running " + command + " on '" + xxxFile.getName() + "'. ");
2239 String[] args =
2240 buildArguments(this.settings.getPythontexOptions(), desc.xxxFile);
2241
2242 File outFolder = TexFileUtils
2243 .replacePrefix(this.settings.getPrefixPytexOutFolder(), desc.xxxFile);
2244 String repOutFileName = desc.xxxFile.getName() + SUFFIX_PYTXMCR;
2245 File repOutFile = new File(outFolder, repOutFileName);
2246 if (repOutFile.exists()) {
2247 // In the long run: eliminate options rerun and runall
2248 // from xxx.pytxcode; then rerun is controlled by command line options.
2249 boolean isDel = repOutFile.delete();
2250 if (!isDel) {
2251 this.log.warn("Preliminary warning: Could not delete '" + repOutFile
2252 + "'; this may cause further warnings/errors. ");
2253 }
2254 }
2255 // TBD: repOutFile is not the only result file.
2256 // one has to add pytxpyg, pkl and the stdout files.
2257 // The precise names of the latter must be read from the pytxcode file.
2258
2259 // may throw BuildFailureException TEX01,
2260 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2261 //CommandExecutor.CmdResult res =
2262 this.executor.executeEnvR0(desc.parentDir, // workingDir
2263 this.settings.getTexPath(), command,
2264 //true,
2265 // This may change later, when rerun=never is taken into account.
2266 args, repOutFile);
2267
2268 File logFile = desc.withSuffix(SUFFIX_PLG);
2269 // may log EAP01, EAP02, WAP04, WFU03
2270 logErrs(logFile, command, this.settings.getPatternErrPyTex());
2271 // may log warnings WFU03, WAP03, WAP04
2272 logWarns(logFile, command, this.settings.getPatternWarnPyTex());
2273 return true;
2274 } // runPythontex
2275
2276 /**
2277 * Runs the latexmk command given by {@link Settings#getLatexmkCommand()}
2278 * on the latex main file <code>texFile</code>
2279 * described by <code>desc</code>
2280 * in the directory containing <code>texFile</code> with arguments
2281 * <!--given by
2282 * {@link #buildLatexArguments(Settings, LatexDev, File, boolean)}-->.
2283 * The output format of the LaTeX run is given by <code>PDF</code> currently.
2284 * <p>
2285 * Logs a warning or an error if running latexmk failed.
2286 * <p>
2287 * Logging:
2288 * <ul>
2289 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2290 * if running the latexmk command failed.
2291 * </ul>
2292 *
2293 * @param desc
2294 * the description of a latex main file <code>texFile</code>
2295 * to be processed.
2296 * @throws BuildFailureException
2297 * TEX01 if invocation of the latexmk command returned by
2298 * {@link Settings#getLatexmkCommand()} failed.
2299 */
2300 private void runLatexmk(LatexMainDesc desc)
2301 throws BuildFailureException {
2302
2303 File texFile = desc.texFile;
2304 // FIXME: wrong name; better is latex2dev
2305
2306 String command = this.settings.getLatexmkCommand();
2307
2308 String[] args = buildLatexmkArguments(settings, desc);
2309 this.log.debug("Running " + command + " on '" + texFile.getName() + "'. ");
2310 // may throw BuildFailureException TEX01,
2311 // may log warning EEX01, EEX02, WEX05
2312 // special: PDF file must exist after but need not be updated
2313 this.executor.executeBuild(desc.parentDir, // workingDir
2314 this.settings.getTexPath(), command, args, desc.pdfFile);//
2315 // TBD: desc.withSuffix(SUFFIX_HTML): maybe depending on Target
2316 }
2317
2318 // also for tests
2319 // TBD: eliminate magic comments: occur in Settings and also in .latexmkrc
2320 protected static String[] buildLatexmkArguments(Settings settings,
2321 LatexMainDesc desc) throws BuildFailureException {
2322
2323 // running specific compiler given in magic comment
2324 // Optional<String> programMagic =
2325 // desc.groupMatch(LatexMainParameterNames.programMagic);
2326 List<String> addArgs = new ArrayList<String>();
2327 // if (programMagic.isPresent()) {
2328 // addArgs.add("-e");
2329 // addArgs.add("$programMagic=q/" + programMagic.get() + "/");
2330 // }
2331
2332 // running with specific time stamp if required in magic comment
2333 // Optional<String> chkDiffMagic =
2334 // desc.groupMatch(LatexMainParameterNames.chkDiffMagic);
2335 // if (chkDiffMagic.isPresent()) {
2336 // addArgs.add("-e");
2337 // // isChkDiff implements the same except for latexmk
2338 // Optional<String> chkDiffMagicValue =
2339 // desc.groupMatch(LatexMainParameterNames.chkDiffMagicVal);
2340 // String chkDiffMagicValueStr = chkDiffMagicValue.isPresent()
2341 // ? chkDiffMagic.get()
2342 // : Boolean.TRUE.toString();// default=true if value not present
2343 // addArgs.add("$chkDiffMagic=q/" + chkDiffMagicValueStr + "/");
2344 // }
2345
2346 // create arguments, both from settings and
2347 // the optional ones from magic comments
2348 return buildArguments(settings.getLatexmkOptions(), desc.texFile,
2349 addArgs.toArray(new String[addArgs.size()])
2350 );
2351 }
2352
2353 /**
2354 * Runs the LaTeX command given by {@link #getLatex2pdfCommand()}
2355 * on the latex main file <code>texFile</code>
2356 * described by <code>desc</code>
2357 * in the directory containing <code>texFile</code> with arguments
2358 * given by {@link #buildLatexArguments(Settings, LatexDev, File, boolean)}.
2359 * The output format of the LaTeX run is given by <code>dev</code>,
2360 * to be more precise by {@link LatexDev#getLatexOutputFormat()}.
2361 * <p>
2362 * Logs a warning or an error if the latex run failed
2363 * invoking {@link #logErrs(File, String)}
2364 * but not if bad boxes occurred or if warnings occurred.
2365 * This is done in
2366 * {@link #processLatex2dev(LatexMainDesc, LatexDev)}
2367 * after the last LaTeX run only.
2368 * <p>
2369 * Logging:
2370 * <ul>
2371 * <li>EAP01: Running <code>latex2pdf</code> failed. For details...
2372 * <li>EAP02: Running <code>latex2pdf</code> failed. No log file
2373 * <li>WAP04: .log-file is not readable.
2374 * <li>WFU03: cannot close .log-file
2375 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2376 * if running the latex2pdf command failed.
2377 * </ul>
2378 *
2379 * @param desc
2380 * the description of a latex main file <code>texFile</code>
2381 * to be processed.
2382 * @param dev
2383 * the device describing the output format
2384 * which is either pdf or dvi.
2385 * See {@link LatexDev#getLatexOutputFormat()}.
2386 * @throws BuildFailureException
2387 * TEX01 if invocation of the latex2pdf command returned by
2388 * {@link #getLatex2pdfCommand()} failed.
2389 */
2390 private void runLatex2dev(LatexMainDesc desc,
2391 LatexDev dev)
2392 throws BuildFailureException {
2393
2394 File texFile = desc.texFile;
2395 // FIXME: wrong name; better is latex2dev
2396 String command = getLatex2pdfCommand();
2397 this.log.debug("Running " + command + " on '" + texFile.getName() + "'. ");
2398 // TBD: this is not completely correct.
2399 // A wrapper of xelatex would not be recognized.
2400 // boolean isTypeXelatex =
2401 // Converter.XeLatex.getCommand().equals(settings.getLatex2pdfCommand());
2402 boolean isTypeXelatex =
2403 Converter.XeLatex.getCommand().equals(command);
2404 String[] args =
2405 buildLatexArguments(this.settings, dev, texFile, isTypeXelatex);
2406 File latexTargetFile = dev.latexTargetFile(desc, isTypeXelatex);
2407 // may throw BuildFailureException TEX01,
2408 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2409 // CAUTION: an error also occurs if running xelatex in conjunction with dvi mode
2410 // because this engine creates xdv instead of dvi
2411 this.executor.executeEnvR0(desc.parentDir, // workingDir
2412 this.settings.getTexPath(), command, args,
2413 latexTargetFile);
2414
2415 // logging errors (warnings are done in processLatex2pdf)
2416 // may log EAP01, EAP02, WAP04, WFU03
2417 logErrs(desc.logFile, command);
2418
2419 // FIXME: documentation that in the dvi file,
2420 // png, jpg and svg are not visible, but present.
2421 }
2422
2423 // also for tests
2424 protected static String[] buildLatexArguments(Settings settings, LatexDev dev,
2425 File texFile, boolean isTypeXelatex) throws BuildFailureException {
2426 // FIXME: here it should be taken xelatex into account, using different settings:
2427 // added is dev==pdf, then nothing, else dev==dvi it is "-no-pdf".
2428 String options = settings.getLatex2pdfOptions();
2429 if (dev.isDefault()) {
2430 return buildArguments(options, texFile);
2431 }
2432 // Here we shall create dvi or xdv file
2433 // FIXME: this shall be based on analysis of the options of the converter,
2434 // not on its name
2435 return buildArguments(options, texFile,
2436 isTypeXelatex ? "-no-pdf"
2437 : "-output-format=" + dev.getLatexOutputFormat());
2438 }
2439
2440 /**
2441 * Runs conversion from dvi or xdv to pdf-file
2442 * executing {@link #getDvi2pdfCommand()}
2443 * on a dvi/xdv-file covered by <code>desc</code> with arguments
2444 * given by {@link #buildLatexArguments(Settings, LatexDev, File, boolean)}.
2445 * Note that this works only because all converter of the according category
2446 * can convert both dvi and xdv
2447 * and apply also on the filename without extension.
2448 * If both files are present, the xdv file is processed.
2449 * <p>
2450 * Logging:
2451 * <ul>
2452 * WLP07: if both dvi and xdv files are present.
2453 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2454 * if running the dvi2pdf command failed.
2455 * </ul>
2456 *
2457 * @param desc
2458 * the description of a latex main file
2459 * <code>dviFile</code> or <code>xdvFile</code>
2460 * including the dvi-file dvi2pdf is to be run on.
2461 * @throws BuildFailureException
2462 * TEX01 if invocation of the dvi2pdf command returned by
2463 * {@link #getDvi2pdfCommand()} failed.
2464 */
2465 // used in processLatex2pdf(File) and processLatex2txt(File) only
2466 private void runDvi2pdf(LatexMainDesc desc)
2467 throws BuildFailureException {
2468 assert this.settings.getPdfViaDvi().isViaDvi();
2469
2470 String command = getDvi2pdfCommand();
2471 // use desc.xxxFile rather than desc.dviFile,
2472 // because we have to treat both, dvi and xdv
2473 // TBD: warning if both are present; then xdv is preferred.
2474 if (desc.dviFile.exists() && desc.xdvFile.exists()) {
2475 this.log.warn("WLP07: Found both '" + desc.dviFile + "' and '"
2476 + desc.xdvFile + "'; convert the latter. ");
2477 }
2478 this.log
2479 .debug("Running " + command + " on '" + desc.xxxFile.getName() + "'. ");
2480 String[] args =
2481 buildArguments(this.settings.getDvi2pdfOptions(), desc.xxxFile);
2482 // may throw BuildFailureException TEX01,
2483 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2484 this.executor.executeEnvR0(desc.parentDir, // workingDir
2485 this.settings.getTexPath(), command, args, desc.pdfFile);
2486 // FIXME: what about error logging?
2487 // Seems not to create a log-file.
2488 }
2489
2490 /**
2491 * Runs the tex4ht command given by {@link Settings#getTex4htCommand()}
2492 * on <code>texFile</code> described by <code>desc</code>
2493 * in the directory containing <code>texFile</code>
2494 * with arguments given by {@link #buildHtlatexArguments(Settings, File)}.
2495 * <p>
2496 * Logging:
2497 * <ul>
2498 * <li>EAP01: Running <code>htlatex</code> failed. For details...
2499 * <li>EAP02: Running <code>htlatex</code> failed. No log file
2500 * <li>WLP03: <code>htlatex</code> created bad boxes
2501 * <li>WLP04: <code>htlatex</code> emitted warnings
2502 * <li>WAP04: log file is not readable.
2503 * <li>WFU03: cannot close log file
2504 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2505 * if running the tex4ht command failed.
2506 * </ul>
2507 *
2508 * @param desc
2509 * the description of a latex main file <code>texFile</code>
2510 * to be processed.
2511 * @throws BuildFailureException
2512 * TEX01 if invocation of the tex4ht command
2513 * returned by {@link Settings#getTex4htCommand()} failed.
2514 */
2515 private void runLatex2html(LatexMainDesc desc) throws BuildFailureException {
2516
2517 File texFile = desc.texFile;
2518 String command = this.settings.getTex4htCommand();
2519 this.log.debug("Running " + command + " on '" + texFile.getName() + "'. ");
2520 String[] args = buildHtlatexArguments(this.settings, texFile);
2521 // may throw BuildFailureException TEX01,
2522 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2523 this.executor.executeEnvR0(desc.parentDir, // workingDir
2524 this.settings.getTexPath(), command, args,
2525 desc.withSuffix(SUFFIX_HTML));
2526
2527 // logging errors and warnings
2528 // may log EAP01, EAP02, WAP04, WFU03
2529 logErrs(desc.logFile, command);
2530 // may log warnings WFU03, WAP04, WLP03, WLP04
2531 logWarns(desc.logFile, command);
2532 }
2533
2534 protected static String[] buildHtlatexArguments(Settings settings,
2535 File texFile) {
2536 return new String[] {texFile.getName(),
2537 settings.getTex4htStyOptions(),
2538 settings.getTex4htOptions(),
2539 settings.getT4htOptions(),
2540 settings.getLatex2pdfOptions()};
2541 }
2542
2543 /**
2544 * Runs the latex2rtf command
2545 * given by {@link Settings#getLatex2rtfCommand()}
2546 * on <code>texFile</code>
2547 * in the directory containing <code>texFile</code>
2548 * with arguments given by {@link #buildArguments(String, File, String...)}.
2549 * <p>
2550 * Logging:
2551 * <ul>
2552 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2553 * if running the latex2rtf command failed.
2554 * </ul>
2555 *
2556 * @param texFile
2557 * the latex file to be processed.
2558 * @throws BuildFailureException
2559 * TEX01 if invocation of the latex2rtf command
2560 * returned by
2561 * {@link Settings#getLatex2rtfCommand()} failed.
2562 */
2563 private void runLatex2rtf(File texFile) throws BuildFailureException {
2564 String command = this.settings.getCommand(ConverterCategory.LaTeX2Rtf);
2565 this.log.debug("Running " + command + " on '" + texFile.getName() + "'. ");
2566 String[] args =
2567 buildArguments(this.settings.getLatex2rtfOptions(), texFile);
2568 // may throw BuildFailureException TEX01,
2569 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2570 this.executor.executeEnvR0(texFile.getParentFile(), // workingDir
2571 this.settings.getTexPath(), command, args,
2572 TexFileUtils.replaceSuffix(texFile, SUFFIX_RTF));
2573
2574 // FIXME: no check: just warning that no output has been created.
2575 // Warnings and error messages are output to stderr
2576 // and by default listed in the console window.
2577 // aThey can be redirected to a file “latex2rtf.log” by
2578 // appending 2>latex2rtf.log to the command line.
2579 }
2580
2581 /**
2582 * Runs conversion from latex to odt
2583 * executing {@link Settings#getTex4htCommand()}
2584 * on <code>texFile</code>
2585 * in the directory containing <code>texFile</code> with arguments
2586 * given by {@link #buildLatexArguments(Settings, LatexDev, File, boolean)}.
2587 * <p>
2588 * Logs a warning or an error if the latex run failed
2589 * invoking {@link #logErrs(File, String)}
2590 * but not if bad boxes ocurred or if warnings occurred.
2591 * This is done in
2592 * {@link #processLatex2dev(LatexMainDesc, LatexDev)}
2593 * after the last LaTeX run only.
2594 * <p>
2595 * Logging:
2596 * <ul>
2597 * <li>EAP01: Running <code>htlatex</code> failed. For details...
2598 * <li>EAP02: Running <code>htlatex</code> failed. No log file
2599 * <li>WLP03: <code>htlatex</code> created bad boxes
2600 * <li>WLP04: <code>htlatex</code> emitted warnings
2601 * <li>WAP04: log file is not readable.
2602 * <li>WFU03: cannot close log file
2603 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2604 * if running the tex4ht command failed.
2605 * </ul>
2606 *
2607 * @param desc
2608 * the descriptor of the latex main file to be processed.
2609 * @throws BuildFailureException
2610 * TEX01 if invocation of the tex4ht command
2611 * returned by {@link Settings#getTex4htCommand()} failed.
2612 */
2613 private void runLatex2odt(LatexMainDesc desc) throws BuildFailureException {
2614 File texFile = desc.texFile;
2615 String command = this.settings.getTex4htCommand();
2616 this.log.debug("Running " + command + " on '" + texFile.getName() + "'. ");
2617 String[] args = new String[] {texFile.getName(), "xhtml,ooffice", // there is no choice here
2618 "ooffice/! -cmozhtf", // ooffice/! represents a font direcory
2619 "-coo -cvalidate"// -coo is mandatory, -cvalidate is not
2620 };
2621 // may throw BuildFailureException TEX01,
2622 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2623 this.executor.executeEnvR0(desc.parentDir, this.settings.getTexPath(), command,
2624 args, desc.withSuffix(SUFFIX_ODT));
2625
2626 // FIXME: logging refers to latex only, not to tex4ht or t4ht script
2627 // may log EAP01, EAP02, WAP04, WFU03
2628 logErrs(desc.logFile, command);
2629 // may log warnings WFU03, WAP04, WLP03, WLP04
2630 logWarns(desc.logFile, command);
2631 }
2632
2633 // FIXME: missing options.
2634 // above all (input) doctype: -ddoc, -ddocx
2635 // and (output) doctype: -fdoc, -fdocx,
2636 // available: odt2doc --show.
2637 // among those also: latex and rtf !!!!!!
2638 // This is important to define the copy filter accordingly
2639 /**
2640 * Runs conversion from odt to doc or docx-file
2641 * executing {@link Settings#getOdt2docCommand()}
2642 * on an odt-file created from <code>texFile</code>
2643 * in the directory containing <code>texFile</code> with arguments
2644 * given by {@link #buildLatexArguments(Settings, LatexDev, File, boolean)}.
2645 * <p>
2646 * Logging:
2647 * <ul>
2648 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2649 * if running the odt2doc command failed.
2650 * </ul>
2651 *
2652 * @param desc
2653 * the description of a latex main file <code>texFile</code>
2654 * to be processed.
2655 * @throws BuildFailureException
2656 * TEX01 if invocation of the odt2doc command returned by
2657 * {@link Settings#getOdt2docCommand()} failed.
2658 */
2659 private void runOdt2doc(LatexMainDesc desc) throws BuildFailureException {
2660 File odtFile = desc.withSuffix(SUFFIX_ODT);
2661 String command = this.settings.getCommand(ConverterCategory.Odt2Doc);
2662 this.log.debug("Running " + command + " on '" + odtFile.getName() + "'. ");
2663 String[] args = buildArguments(this.settings.getOdt2docOptions(), odtFile);
2664 String suffix = null;
2665 for (int idx = 0; idx < args.length - 1; idx++) {
2666 // FIXME: -f is hardcoded
2667 if (args[idx].startsWith("-f")) {
2668 assert suffix == null;// -f comes once only
2669 // without leading '-f'
2670 suffix = args[idx].substring(2, args[idx].length());
2671 }
2672 }
2673 // FIXME: no validity check on suffix
2674 assert suffix != null;
2675 // may throw BuildFailureException TEX01,
2676 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2677 this.executor.executeEnvR0(desc.parentDir, this.settings.getTexPath(), command,
2678 args, desc.withSuffix(suffix));
2679 // FIXME: what about error logging?
2680 // Seems not to create a log-file.
2681 }
2682
2683 /**
2684 * Runs conversion from pdf to txt-file
2685 * executing {@link Settings#getPdf2txtCommand()}
2686 * on a pdf-file created from <code>texFile</code>
2687 * in the directory containing <code>texFile</code> with arguments
2688 * given by {@link #buildLatexArguments(Settings, LatexDev, File, boolean)}.
2689 * <p>
2690 * Logging:
2691 * <ul>
2692 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2693 * if running the pdf2txt command failed.
2694 * </ul>
2695 *
2696 * @param desc
2697 * the description of a latex main file <code>texFile</code>
2698 * to be processed.
2699 * @throws BuildFailureException
2700 * TEX01 if invocation of the pdf2txt command returned by
2701 * {@link Settings#getPdf2txtCommand()} failed.
2702 */
2703 private void runPdf2txt(LatexMainDesc desc) throws BuildFailureException {
2704 File pdfFile = desc.withSuffix(SUFFIX_PDF);
2705 String command = this.settings.getCommand(ConverterCategory.Pdf2Txt);
2706 this.log.debug("Running " + command + " on '" + pdfFile.getName() + "'. ");
2707 String[] args = buildArguments(this.settings.getPdf2txtOptions(), pdfFile);
2708 // may throw BuildFailureException TEX01,
2709 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2710 this.executor.executeEnvR0(desc.parentDir, this.settings.getTexPath(), command,
2711 args, desc.withSuffix(SUFFIX_TXT));
2712 // FIXME: what about error logging?
2713 // Seems not to create a log-file.
2714 }
2715
2716 void processCheck(LatexMainDesc desc) throws BuildFailureException {
2717 this.log.info("Checking source. ");
2718 runChktex(desc);
2719 }
2720
2721 /**
2722 * Runs the check command given by {@link Settings#getChkTexCommand()}
2723 * on the latex main file <code>texFile</code>
2724 * in the directory containing <code>texFile</code>
2725 * creating a log file with ending {@link #SUFFIX_CLG}
2726 * in that directory.
2727 * <p>
2728 * Logging:
2729 * <ul>
2730 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2731 * if running the ChkTeX command failed.
2732 * </ul>
2733 *
2734 * @param desc
2735 * the description of a latex main file <code>texFile</code>
2736 * to be processed.
2737 * @throws BuildFailureException
2738 * TEX01 if invocation of the check command
2739 * returned by {@link Settings#getChkTexCommand()} failed.
2740 */
2741 private void runChktex(LatexMainDesc desc) throws BuildFailureException {
2742 File texFile = desc.texFile;
2743 File clgFile = desc.withSuffix(SUFFIX_CLG);
2744 String command = this.settings.getCommand(ConverterCategory.LatexChk);
2745 this.log.debug("Running " + command + " on '" + texFile.getName() + "'. ");
2746 String[] args = buildChkTexArguments(this.settings.getChkTexOptions(),
2747 texFile, clgFile);
2748 // may throw BuildFailureException TEX01,
2749 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2750 CommandExecutor.CmdResult res = this.executor.executeEmptyEnv(
2751 texFile.getParentFile(), this.settings.getTexPath(), command,
2752 CommandExecutor.ReturnCodeChecker.IsOne, args, clgFile);
2753 switch (res.returnCode) {
2754 case 0: // all ok
2755 if (clgFile.exists() && clgFile.length() != 0) {
2756 this.log.info("Checker '" + command + "' logged a message in '"
2757 + clgFile.getName() + "'. ");
2758 }
2759 break;
2760 case 1: // execution error already treated in executor;
2761 // nothing to be done.
2762 // see CommandExecutor.ReturnCodeChecker.isOne.hasFailed
2763 break;
2764 case 3: // no execution error but check found error
2765 this.log.error("ELP02: Checker '" + command + "' logged an error in '"
2766 + clgFile.getName() + "'. ");
2767 break;
2768 case 2: // no execution error and no error but warning.
2769 this.log.warn("WLP08: Checker '" + command + "' logged a warning in '"
2770 + clgFile.getName() + "'. ");
2771 break;
2772 default:
2773 logErrUnexpectedReturnCode(command, res);
2774 }
2775 // Possibly, if not using the -q option
2776 // the status messages delivers even more pieces of information.
2777 // Maintain this, to indicate why WLP06 does not occur any more.
2778 // if (!clgFile.exists()) {
2779 // // Here, chktex could not perform the check
2780 // // but the failure is already logged.
2781 // return;
2782 // }
2783 // // assert !clgFile.isDirectory();
2784 // if (clgFile.length() != 0) {
2785 // this.log.warn("WLP06: Running " + command +
2786 // " found issues logged in '" +
2787 // texFile.getName() + "'. ");
2788 // }
2789 }
2790
2791 /**
2792 * Logs an error on command <code>command</code> if the result <code>command</code>
2793 * returned an unexpected return value.
2794 * This method shall be used for checker commands only.
2795 * The background is, that an error like that comes from a change in the application
2796 * which needs adaption of this software.
2797 * The error is the trigger for further development.
2798 *
2799 * @param command
2800 * the ckecker command which returned an unexpected return value.
2801 * @param res
2802 * the result with unexpected return value.
2803 */
2804 private void logErrUnexpectedReturnCode(String command, CommandExecutor.CmdResult res) {
2805 this.log.error("ELP01: For command '" + command
2806 + "' found unexpected return code " + res.returnCode + ". ");
2807 }
2808
2809 /**
2810 * Runs the validation command for PDF format given by {@link Settings#getcommand
2811 * @param desc
2812 * @throws BuildFailureException
2813 */
2814 private void runValidatePdf(LatexMainDesc desc) throws BuildFailureException {
2815 File pdfFile = desc.pdfFile;
2816 String command = this.settings.getCommand(ConverterCategory.StandardValidator);
2817 this.log.debug("Running " + command + " on '" + pdfFile.getName() + "'. ");
2818 String[] args = buildArguments(this.settings.getChkTexOptions(), pdfFile);
2819 // may throw BuildFailureException TEX01,
2820 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2821 CommandExecutor.CmdResult res = this.executor.executeEmptyEnv(
2822 pdfFile.getParentFile(), this.settings.getTexPath(), command,
2823 CommandExecutor.ReturnCodeChecker.IsNotZeroOrOne, args);
2824 // TBD: display the standards.
2825 // TBD: clarify how to use verapdf output
2826 switch (res.returnCode) {
2827 case 0: // check executed and passed
2828 this.log.info("Validation of '" + pdfFile.getName() + "' passed. ");
2829 break;
2830 case 1: // check executed and failed
2831 this.log.warn("XXX: Validation of '" + pdfFile.getName() + "' failed. ");
2832 break;
2833 case 2:
2834 case 3:
2835 case 4:
2836 // case 5 is missing
2837 case 6:
2838 case 7:
2839 case 8:
2840 case 9:
2841 case 10:
2842 case 11:
2843 case 12: // execution error already treated in executor;
2844 // nothing to be done.
2845 // see CommandExecutor.ReturnCodeChecker.IsNotZeroOrOne.hasFailed
2846 break;
2847 default:
2848 logErrUnexpectedReturnCode(command, res);
2849 }
2850 }
2851
2852
2853
2854 /**
2855 * Returns an array of strings,
2856 * each entry with a single option given by <code>options</code>
2857 * except the last three which represent <code>-o clgFile texFile</code>.
2858 *
2859 * @param options
2860 * the options string. The individual options
2861 * are expected to be separated by a single blank.
2862 * @param texFile
2863 * the latex main file to be checked.
2864 * @param clgFile
2865 * the log-file with the result of the check of
2866 * <code>texFile</code>.
2867 * @return
2868 * An array of strings:
2869 * If <code>options</code> is not empty,
2870 * the first entries are the options in <code>options</code>.
2871 * The last three entries are
2872 * <code>-o</code>, <code>clgFile</code> and <code>texFile</code>.
2873 */
2874 protected static String[] buildChkTexArguments(String options, File texFile,
2875 File clgFile) {
2876 return buildArguments(options, texFile, "-o", clgFile.getName());
2877 }
2878
2879 /**
2880 * Returns whether the given pdf files coincide
2881 * running the diff tool specified
2882 * by {@link Settings#getCommand(ConverterCategory)}
2883 * with {@link ConverterCategory#DiffPdf}.
2884 * For a checker method the return value is quite unusual:
2885 * It causes a break of the process.
2886 *
2887 * Logging:
2888 * <ul>
2889 * <li>EEX01, EEX02, EEX03, WEX04, WEX05:
2890 * if running the ChkTeX command failed.
2891 * </ul>
2892 *
2893 *
2894 * @param pdfFileCmp
2895 * the pdf file for comparison
2896 * @param pdfFileAct
2897 * the pdf file actually created.
2898 * @return
2899 * whether <code>pdfFileAct</code> could be checked to be coincideswith
2900 * <code>pdfFileCmp</code>.
2901 * This is false whether the check failed or the check could not be performed.
2902 * @throws BuildFailureException
2903 * TEX01 if invocation of the diff command failed.
2904 * @see #runChktex(LatexMainDesc)
2905 */
2906 private boolean runDiffPdf(File pdfFileCmp, File pdfFileAct)
2907 throws BuildFailureException {
2908 //
2909 // File clgFile = TexFileUtils.replaceSuffix(texFile, SUFFIX_CLG);
2910 String command = this.settings.getCommand(ConverterCategory.DiffPdf);
2911 this.log.debug("Running " + command + " diffing '" + pdfFileCmp.getName()
2912 + "' and '" + pdfFileAct.getName() + "'. ");
2913 // String[] args = buildChkTexArguments(this.settings.getChkTexOptions(),
2914 // texFile,
2915 // clgFile);
2916 String[] args = new String[] {pdfFileCmp.toString(), pdfFileAct.toString()};
2917
2918 // may throw BuildFailureException TEX01,
2919 // may log warning EEX01, EEX02, EEX03, WEX04, WEX05
2920 CmdResult res = this.executor.executeEmptyEnv(null, // texFile.getParentFile(),
2921 this.settings.getTexPath(), command,
2922 CommandExecutor.ReturnCodeChecker.IsNotZeroOrOne, args);
2923 // other value 2 caused an exception before
2924 //assert returnCode == 0 || returnCode == 1 : "diff unexpected return value "+returnCode;
2925 // 0 means that the files 'coincide',
2926 // where as 2 means that there is a significant difference
2927 int returnCode = res.returnCode;
2928 switch (returnCode) {
2929 case 0:
2930 // Here, files could be diffed and coincide.
2931 // treated outside via return value
2932 break;
2933 case 1:
2934 // Here, files could be diffed but do not coincide
2935 // treated outside via return value
2936 break;
2937 case 2:
2938 // Here, files could not be diffed because of an error in command
2939 // This is already treated in executor
2940 // see CommandExecutor.ReturnCodeChecker.IsNotZeroOrOne
2941 // Nevertheless, the return value is part of the treatment.
2942 break;
2943 default:
2944 logErrUnexpectedReturnCode(command, res);
2945 }
2946 return returnCode == 0;
2947 }
2948
2949 // run pdfinfo or that like
2950 // in the long should return a map for metdatata.
2951 // currently it is the create time as epoch
2952 /**
2953 * Returns the meta info <code>CreationDate</code> of <code>pdfFile</code>,
2954 * which is in fact date and time, as epoch time in seconds
2955 * by invoking a tool like <code>prefinfo</code>.
2956 * @param pdfFile
2957 * @return
2958 * <code>CreationDate</code> as epoch time.
2959 * @throws BuildFailureException
2960 */
2961 long runPdfInfo(File pdfFile) throws BuildFailureException {
2962 //System.out.println("pdfinfo on "+pdfFile);
2963 String command = this.settings.getCommand(ConverterCategory.MetaInfoPdf);
2964 this.log.debug("Running " + command + " extracting metainformation from '" + pdfFile.getName() + "''. ");
2965 // TBD: neither of this works really. first yields wrong pdf, second does not work with more than 1 option
2966 //String[] args = buildArguments(this.settings.getPdfMetainfoOptions(), pdfFile);
2967 String[] args = new String[] {this.settings.getPdfMetainfoXmpOptions(), pdfFile.toString()};
2968 CmdResult res = this.executor.executeEmptyEnv(null, // texFile.getParentFile(),
2969 this.settings.getTexPath(), command,
2970 CommandExecutor.ReturnCodeChecker.IsNonZero, args);
2971 //System.out.println("pdfinfo yields\n"+res.output);
2972 // TBD: eliminate literal
2973
2974 String patternString;
2975 if (res.output.isEmpty()) {
2976 // no XMP data, fallback
2977 System.out.println("no xmp");
2978 args = new String[] {this.settings.getPdfMetainfoOptions(), pdfFile.toString()};
2979 res = this.executor.executeEmptyEnv(null, // texFile.getParentFile(),
2980 this.settings.getTexPath(), command,
2981 CommandExecutor.ReturnCodeChecker.IsNonZero, args);
2982 patternString = "CreationDate:\\s*(?<creationDate>.*?)\\R";
2983 } else {
2984 // Here, XMP data is available and latex then writes date into it
2985 patternString = "<xmp:CreateDate>(?<creationDate>.*?)</xmp:CreateDate>\\R";
2986 }
2987 Pattern pattern = Pattern.compile(patternString);
2988 Matcher matcher = pattern.matcher(res.output);
2989
2990 if (!matcher.find()) {
2991 // TBD: rethink
2992 throw new RuntimeException("Found no creation date");
2993 }
2994 String creationDateIso8601 = matcher.group("creationDate");// TBD: eliminate literal
2995 long epochTimeSec = ZonedDateTime.parse(creationDateIso8601).toEpochSecond();
2996 //long epochTimeSec = Instant.parse(creationDateIso8601).getEpochSecond();
2997
2998 // String[] lines = res.output.split("\\R");
2999 // for (String line : lines) {
3000 // line
3001 // }
3002
3003 return epochTimeSec;
3004
3005 }
3006
3007 /**
3008 * The folder in the jar-file containing this application
3009 * containing the files of the {@link Injection}s.
3010 */
3011 private static final String FOLDER_INJ = "injections/";
3012
3013 /**
3014 * Processes injection of files
3015 * like <code>.latexmkrc</code> and <code>.chktexrc</code>
3016 * as described by <code>injections</code>, e.g.
3017 * <ul>
3018 * <li> performing filtering similar to the resources plugin, </li>
3019 * <li> turning the result into an executable file,
3020 * </ul>
3021 * Filtering differs from the mechanism of the resources plugin
3022 * in two respects:
3023 * <ul>
3024 * <li> The names are those of parameters in settings,
3025 * not properties in the pom. </li>
3026 * <li> The sources are loaded as a resource by the classloader
3027 * by name <code>.latexmkrc</code> and <code>.chktexrc</code>, respectively.
3028 * They are a kind of templates containing parameter names
3029 * and the target is a file in the folder
3030 * given by {@link Settings#getTexSrcDirectoryFile()}.
3031 * This file contains the values of the parameters, finally.
3032 * </ul>
3033 *
3034 * may log:
3035 * <ul>
3036 * <li> WFU10: if the file has not been created by this software
3037 * <li> WFU11: if it cannot be ensured that the file has been created
3038 * by this software </li>
3039 * or if the reader to read to decide cannot be closed. </li>
3040 * </ul>
3041 *
3042 * @param injections
3043 * define the files to be injected and how injection is done.
3044 * @throws BuildFailureException
3045 * <ul>
3046 * <li>
3047 * TLP03: Failure while writing file '...' or closing in-stream.
3048 * The file is created from a template replacing parameter names
3049 * by their actual values.
3050 * A reason may be that the template cannot be read
3051 * or its in-stream cannot be closed. </li>
3052 * <li> TMI01: if the stream to the template cannot be read. </li>
3053 * <li> TMI01: if the stream to the according properties file
3054 * could not be created </li>
3055 * <li> TMI02: if the properties could not be read. </li>
3056 * </ul>
3057 */
3058 //
3059 public void processFileInjections(Set<Injection> injections)
3060 throws BuildFailureException {
3061 //this.settings.getProperties();
3062 // TBD: centralize this because also needed for goal clr
3063
3064 for (Injection inj : injections) {
3065 String fileName = inj.getFileName();
3066 // may throw TMI01
3067 InputStream inStream = MetaInfo.getStream(FOLDER_INJ+fileName);
3068 File outFile = this.settings.rcResourceToFile(fileName);
3069
3070 try {
3071 // isCreatedByMyself may emit warnings WFU10, WFU11
3072 if (!outFile.exists()
3073 || this.fileUtils.isCreatedByMyself(outFile, inj)) {
3074 // Here, outFile does not exist or is a regular file.
3075
3076 // may throw FileNotFoundException and so IOException:
3077 // if does not exist
3078 // and a regular file of that name cannot be created for writing
3079 // or if it does exist and cannot be opened for writing,
3080 // or if some error occurs when creating or opening for writing
3081 PrintStream writer = new PrintStream(outFile);
3082 // may throw IOException: readline.
3083 // may throw TMI01, TMI02
3084 String version = this.metaInfo.getCoordinates().version;
3085 this.settings.filterInjection(inStream, writer, version, inj);
3086 }
3087 // may throw IOExeption
3088 inStream.close();
3089 } catch (IOException ioe) {
3090 // Here, the goal inj must be aborted, or inStream could not be closed.
3091 // THis is true not only when the Print stream cannot be created,
3092 // but also if the resource cannot be read in filterRcFile.
3093 // To be strict, one could go on even if inStream could not be closed.
3094 throw new BuildFailureException("TLP03 Failure while writing file '"
3095 + fileName + "' or closing in-stream. ", ioe);
3096 }
3097
3098 boolean success = outFile.setExecutable(inj.setExecutable(), false);
3099 assert success || !inj.setExecutable();
3100 }
3101 }
3102
3103 /**
3104 * Deletes injected files if written by this software if possible.
3105 *
3106 * WFU10, WFU11: if a config file is not written by this software
3107 * or it is not clear or the reader cannot close.
3108 * EFU05: if the config file shall be deleted but this coes not work.
3109 */
3110 private void clearInjFiles() {
3111 for (Injection inj : Injection.values()) {
3112 File outFile = this.settings.rcResourceToFile(inj.getFileName());
3113 // isCreatedByMyself may emit warnings WFU10, WFU11
3114 if (outFile.exists() && this.fileUtils.isCreatedByMyself(outFile, inj)) {
3115 // could emit EFU05
3116 this.fileUtils.deleteOrError(outFile, false);
3117 }
3118 }
3119 }
3120
3121 }