CoverityFix-CID:1351023 Bad use of return value
[scilab.git] / scilab / modules / helptools / src / java / org / scilab / modules / helptools / CopyConvert.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2008 - INRIA - Hussein SHAFIE
4  * Copyright (C) 2010 - DIGITEO - Sylvestre LEDRU
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16 package org.scilab.modules.helptools;
17
18 import java.io.IOException;
19 import java.io.File;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.OutputStreamWriter;
25 import java.io.PrintWriter;
26 import java.net.URISyntaxException;
27 import java.net.MalformedURLException;
28 import java.net.URL;
29 import java.util.Map;
30 import java.util.Iterator;
31 import java.util.HashMap;
32 import java.util.ArrayList;
33 import java.util.zip.GZIPInputStream;
34 import javax.xml.parsers.SAXParser;
35 import javax.xml.parsers.SAXParserFactory;
36 import org.xml.sax.SAXParseException;
37 import org.xml.sax.SAXException;
38 import org.xml.sax.Locator;
39 import org.xml.sax.Attributes;
40 import org.xml.sax.helpers.DefaultHandler;
41
42 import org.apache.batik.transcoder.TranscoderException;
43 import org.apache.batik.transcoder.ErrorHandler;
44 import org.apache.batik.transcoder.TranscoderInput;
45 import org.apache.batik.transcoder.TranscoderOutput;
46 import org.apache.batik.transcoder.Transcoder;
47 import org.apache.batik.transcoder.XMLAbstractTranscoder;
48 import org.apache.batik.transcoder.image.ImageTranscoder;
49 import org.apache.batik.transcoder.image.PNGTranscoder;
50
51 import net.sourceforge.jeuclid.context.Parameter;
52 import net.sourceforge.jeuclid.MutableLayoutContext;
53 import net.sourceforge.jeuclid.context.LayoutContextImpl;
54 import net.sourceforge.jeuclid.converter.Converter;
55
56 import org.scilab.forge.jlatexmath.TeXConstants;
57 import org.scilab.forge.jlatexmath.TeXFormula;
58 import org.scilab.forge.jlatexmath.ParseException;
59 import java.awt.Color;
60
61 /**
62  * Preprocess before building the documentation
63  */
64 public class CopyConvert extends DefaultHandler implements ErrorHandler {
65
66     private static final String MATHML_NS =
67         "http://www.w3.org/1998/Math/MathML";
68
69     /**
70      * The tmp directory
71      */
72     public static final File TMPDIR = new File(System.getenv("TMPDIR"));
73
74     private boolean verbose;
75     private String printFormat;
76
77     private File outDir;
78     private PrintWriter out;
79     private SAXParser parser;
80     private Locator locator;
81
82     private ArrayList<String[]> inScopePrefixes;
83     private PrintWriter mainOut;
84     private int graphicsCounter;
85     private int embeddedGraphicsIsMathML;
86
87     private int initTeX;
88     private File latex;
89     private File dvips;
90     private File gs;
91     private File ps2pdf;
92
93     private LaTeXElement latexElem;
94     private boolean isLatexConverted = true;
95
96     /**
97      * Enables the verbose mode
98      *
99      * @param verbose true if enable
100      */
101     public void setVerbose(boolean verbose) {
102         this.verbose = verbose;
103     }
104
105     /**
106      * Set the print format
107      *
108      * @param printFormat  The print format
109      */
110     public void setPrintFormat(String printFormat) {
111         this.printFormat = printFormat;
112     }
113
114
115
116     /**
117      * Set if the LaTeX label are converted in PNG or not to use with
118      * the jlatexmath-fop plugin.
119      * @param isLatexConverted true if LaTeX label are converted in PNG
120      */
121     public void setLatexConverted(boolean isLatexConverted) {
122         this.isLatexConverted = isLatexConverted;
123     }
124
125     // -----------------------------------------------------------------------
126
127     /**
128      * Run the copy/convert process
129      *
130      * @param inFile Input file
131      * @param outputFile Output file
132      */
133     public void run(File inFile, File outputFile)
134     throws SAXParseException, SAXException, IOException {
135         File outFile = outputFile.getCanonicalFile();
136         outDir = outFile.getParentFile();
137         if (!outDir.isDirectory() && !outDir.mkdirs()) {
138             throw new IOException("Cannot create directory '" + outDir + "'");
139         }
140
141         try {
142             SAXParserFactory factory = SAXParserFactory.newInstance();
143             factory.setNamespaceAware(true);
144             // We need qNames and xmlns*.
145             // FIXME: xmlns:db prefix is not handled by the thirdparty's stylesheet (not the right version)
146             //            factory.setFeature(
147             //                "http://xml.org/sax/features/namespace-prefixes", true);
148             factory.setValidating(false);
149             //factory.setXIncludeAware(false);
150
151             parser = factory.newSAXParser();
152         } catch (Exception e) {
153             throw new SAXParseException(
154                 "Cannot create a properly configured SAX parser: " + Helpers.reason(e), locator);
155         }
156
157         inScopePrefixes = new ArrayList<String[]>();
158         mainOut = null;
159         graphicsCounter = 0;
160         embeddedGraphicsIsMathML = -1;
161         initTeX = -1;
162
163         out = new PrintWriter(
164             new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
165         try {
166             parser.parse(inFile, this);
167             out.flush();
168             if (out.checkError()) {
169                 throw new IOException("Error writing '" + outFile + "'");
170             }
171         } catch (SAXException e) {
172             if (locator != null) {
173                 throw new SAXParseException("Cannot parse " + inFile + " " + Helpers.reason(e), locator);
174             } else {
175                 throw new SAXException("Cannot parse " + inFile + " " + Helpers.reason(e));
176             }
177         } finally {
178             out.close();
179         }
180     }
181
182     // -----------------------------------------------------------------------
183     // ContentHandler
184     // -----------------------------------------------------------------------
185
186     /**
187      * Set the document Locator
188      *
189      * @param locator  The locator
190      */
191     public void setDocumentLocator(Locator locator) {
192         this.locator = locator;
193     }
194
195     public void startDocument() throws SAXException {
196         out.write("<?xml version='1.0' encoding='UTF-8'?>\n");
197     }
198
199     public void endDocument() throws SAXException {
200     }
201
202
203     public void startPrefixMapping(String prefix, String uri)
204     throws SAXException {
205         inScopePrefixes.add(new String[] {prefix, uri });
206     }
207
208     public void endPrefixMapping(String prefix)
209     throws SAXException {
210         int count = inScopePrefixes.size();
211         for (int i = count - 1; i >= 0; --i) {
212             String[] pair = inScopePrefixes.get(i);
213             if (pair[0].equals(prefix)) {
214                 inScopePrefixes.remove(i);
215                 break;
216             }
217         }
218     }
219
220     public void startElement(String uri, String localName, String qName,
221                              Attributes atts)
222     throws SAXException {
223         out.write('<');
224
225         if ("latex".equals(localName)) {
226             latexElem = new LaTeXElement(atts, isLatexConverted);
227             return;
228         }
229
230         out.write(qName);
231
232         boolean isImage = "imagedata".equals(localName);
233         boolean isGraphicsFile = false;
234
235         int count = atts.getLength();
236         for (int i = 0; i < count; ++i) {
237             String attValue = atts.getValue(i);
238
239             if (isImage && "fileref".equals(atts.getLocalName(i))) {
240                 isGraphicsFile = true;
241
242                 URL url = null;
243
244                 try {
245                     URL base = new URL(locator.getSystemId());
246                     url = new URL(base, attValue);
247                 } catch (MalformedURLException e) {
248                     reportError("Malformed URL '" + attValue + "'");
249                 }
250
251                 if (url != null) {
252                     File graphicsFile = null;
253
254                     try {
255                         graphicsFile = new File(url.toURI());
256                     } catch (URISyntaxException e) {
257                         reportError("Malformed URI '" + url + "'");
258                     } catch (IllegalArgumentException e) {
259                         reportError("'" + url + "', not a 'file:' URL");
260                     }
261
262                     if (graphicsFile != null) {
263                         String converted =
264                             copyConvertGraphics(graphicsFile);
265                         if (converted != null) {
266                             // Substitute the basename of the converted file
267                             // to attValue.
268                             attValue = converted;
269                         }
270                     }
271                 }
272             }
273
274             out.write(' ');
275             out.write(atts.getQName(i));
276             out.write("='");
277             Helpers.escapeXML(attValue, out);
278             out.write('\'');
279         }
280
281         if (mainOut != null) {
282             if (embeddedGraphicsIsMathML < 0) {
283                 // Is embedded graphics SVG or MathML?
284                 embeddedGraphicsIsMathML = MATHML_NS.equals(uri) ? 1 : 0;
285             }
286
287             // Declare namespace prefixes in the extracted file.
288             declarePrefixes(atts);
289         }
290
291         if (!isImage || isGraphicsFile) {
292             out.write('>');
293         } else {
294             File extractedFile =
295                 new File(outDir, "graphics-" + (++graphicsCounter) + ".tmp");
296             try {
297                 PrintWriter extracted = new PrintWriter(
298                     new OutputStreamWriter(new FileOutputStream(extractedFile),
299                                            "UTF-8"));
300
301                 out.flush();
302                 mainOut = out;
303
304                 out = extracted;
305                 out.write("<?xml version='1.0' encoding='UTF-8'?>\n");
306
307                 embeddedGraphicsIsMathML = -1;
308             } catch (IOException e) {
309                 reportError("Cannot create file '" + extractedFile + "': "
310                             + Helpers.reason(e));
311                 // Keep embedded graphics.
312                 out.write('>');
313             }
314         }
315     }
316
317     private void declarePrefixes(Attributes atts) {
318         HashMap<String, String> uniquePrefixes = new HashMap<String, String>();
319
320         int count = inScopePrefixes.size();
321         for (int i = 0; i < count; ++i) {
322             String[] pair = inScopePrefixes.get(i);
323
324             uniquePrefixes.put(pair[0], pair[1]);
325         }
326
327         Iterator<Map.Entry<String, String>> iter =
328             uniquePrefixes.entrySet().iterator();
329         while (iter.hasNext()) {
330             Map.Entry<String, String> e = iter.next();
331
332             String prefix = e.getKey();
333             String namespace = e.getValue();
334
335             String xmlns;
336             if (prefix.length() == 0) {
337                 xmlns = "xmlns";
338             } else {
339                 xmlns = "xmlns:" + prefix;
340             }
341
342             if (atts.getValue(xmlns) == null) { // If not already declared.
343                 out.write(' ');
344                 out.write(xmlns);
345                 out.write("='");
346                 Helpers.escapeXML(namespace, out);
347                 out.write('\'');
348             }
349         }
350     }
351
352     public void endElement(String uri, String localName, String qName)
353     throws SAXException {
354         if ("latex".equals(localName)) {
355             latexElem.generate(locator);
356             latexElem = null;
357             return;
358         }
359
360         if ("imagedata".equals(localName) && mainOut != null) {
361             String rootName = "graphics-" + graphicsCounter;
362             String baseName = rootName + ".tmp";
363             File extractedFile = new File(outDir, baseName);
364
365             out.flush();
366             if (out.checkError()) {
367                 reportError("Error writing '" + extractedFile + "'");
368                 out.close();
369                 extractedFile = null;
370             } else {
371                 out.close(); // Close now otherwise rename fails on Windows
372
373                 File extractedFile2 = new File(
374                     outDir,
375                     rootName + ((embeddedGraphicsIsMathML == 1) ? ".mml" : ".svg"));
376                 if (!extractedFile.renameTo(extractedFile2)) {
377                     reportError("Cannot rename '" + extractedFile + "' to '"
378                                 + extractedFile2 + "'");
379                     extractedFile = null;
380                 } else {
381                     extractedFile = extractedFile2;
382                 }
383             }
384
385             out = mainOut;
386             mainOut = null;
387
388             if (extractedFile != null) {
389                 reportInfo("Extracted embedded graphics from '" + extractedFile + "' to '" + extractedFile + "'.");
390
391                 String converted = copyConvertGraphics(extractedFile);
392                 if (converted != null) {
393                     baseName = converted;
394
395                     if (!extractedFile.delete()) {
396                         reportError("Cannot delete '" + extractedFile + "'");
397                     }
398                 }
399             }
400
401             out.write(" fileref='");
402             Helpers.escapeXML(baseName, out);
403             out.write("'/>");
404         } else {
405             out.write("</");
406             out.write(qName);
407             out.write('>');
408         }
409     }
410
411     public void characters(char[] ch, int start, int length)
412     throws SAXException {
413         if (latexElem != null) {
414             latexElem.setLaTeX(new String(ch, start, length));
415             return;
416         }
417
418         Helpers.escapeXML(ch, start, length, out);
419     }
420
421     public void processingInstruction(String target, String data)
422     throws SAXException {
423         // The DocBook XSL style sheets make use of processing-instructions.
424         out.write("<?");
425         out.write(target);
426         out.write(' ');
427         out.write(data);
428         out.write("?>");
429     }
430
431     public void ignorableWhitespace(char[] ch, int start, int length)
432     throws SAXException { }
433
434     public void skippedEntity(String name)
435     throws SAXException { }
436
437     // -----------------------------------------------------------------------
438     // Copy & convert graphics file
439     // -----------------------------------------------------------------------
440
441     private String copyConvertGraphics(File graphicsFile) {
442         if (!graphicsFile.isFile()) {
443             reportError("'" + graphicsFile + "' not found");
444             return null;
445         }
446
447         String baseName = graphicsFile.getName();
448         String rootName = Helpers.setFileExtension(baseName, null);
449         String ext = Helpers.getFileExtension(baseName);
450
451         if ("tex".equalsIgnoreCase(ext)) {
452             if ("ps".equalsIgnoreCase(printFormat)) {
453                 baseName = rootName + "_tex.eps";
454             } else if ("pdf".equalsIgnoreCase(printFormat)) {
455                 baseName = rootName + "_tex.pdf";
456             } else {
457                 baseName = rootName + "_tex.png";
458             }
459             File convertedFile = new File(outDir, baseName);
460
461             if (!convertedFile.exists() || convertedFile.lastModified() < graphicsFile.lastModified()) {
462                 reportInfo("Converting TeX '" + graphicsFile + "' to '"
463                            + convertedFile + "'...");
464
465                 if (!convertTeX(graphicsFile, convertedFile)) {
466                     baseName = null;
467                 }
468             }
469         } else if ("mml".equalsIgnoreCase(ext)) {
470             baseName = rootName + "_mml.png";
471             File convertedFile = new File(outDir, baseName);
472
473             if (!convertedFile.exists() || convertedFile.lastModified() < graphicsFile.lastModified()) {
474                 reportInfo("Converting MathML '" + graphicsFile + "' to '"
475                            + convertedFile + "'...");
476
477                 if (!convertMathML(graphicsFile, convertedFile)) {
478                     baseName = null;
479                 }
480             }
481         } else if ("svg".equalsIgnoreCase(ext) || "svgz".equalsIgnoreCase(ext)) {
482             baseName = rootName + "_svg.png";
483             File convertedFile = new File(outDir, baseName);
484
485             if (!convertedFile.exists() || convertedFile.lastModified() < graphicsFile.lastModified()) {
486                 reportInfo("Converting SVG '" + graphicsFile + "' to '"
487                            + convertedFile + "'...");
488
489                 if (!convertSVG("svgz".equalsIgnoreCase(ext), graphicsFile, convertedFile)) {
490                     baseName = null;
491                 }
492             }
493         } else {
494             // Just copy the file ---
495
496             File outFile = new File(outDir, baseName);
497             if (!outFile.exists() || outFile.lastModified() < graphicsFile.lastModified()) {
498                 reportInfo("Copying '" + graphicsFile + "' to '"
499                            + outFile + "'...");
500
501                 try {
502                     Helpers.copyFile(graphicsFile, outFile);
503                 } catch (IOException e) {
504                     reportError("Cannot copy '" + graphicsFile + "' to '"
505                                 + outFile + "': " + Helpers.reason(e));
506                 }
507             }
508         }
509
510         return baseName;
511     }
512
513     private boolean convertTeX(File inFile, File outFile) {
514         if (!initTeX()) {
515             return false;
516         }
517
518         try {
519             doConvertTeX(inFile, outFile);
520             return true;
521         } catch (Exception e) {
522             reportError("Cannot convert '" + inFile + "' to '"
523                         + outFile + "': " + Helpers.reason(e));
524             return false;
525         }
526     }
527
528     private boolean initTeX() {
529         if (initTeX == -1) {
530             latex = Helpers.findInPath("latex");
531             if (latex == null) {
532                 reportError("Don't find executable 'latex' in PATH");
533             }
534
535             dvips = Helpers.findInPath("dvips");
536             if (dvips == null) {
537                 reportError("Don't find executable 'dvips' in PATH");
538             }
539
540             String appName = Helpers.IS_WINDOWS ? "gswin32c" : "gs";
541             gs = Helpers.findInPath(appName);
542             if (gs == null) {
543                 reportError("Don't find executable '" + appName + "' in PATH");
544             }
545
546             ps2pdf = Helpers.findInPath("ps2pdf");
547             if (ps2pdf == null) {
548                 reportError("Don't find executable 'ps2pdf' in PATH");
549             }
550
551             if (latex == null || dvips == null || gs == null || ps2pdf == null) {
552                 initTeX = 0;
553             } else {
554                 initTeX = 1;
555             }
556         }
557         return (initTeX == 1);
558     }
559
560     private void doConvertTeX(File inFile, File outFile)
561     throws IOException, InterruptedException {
562         File latexFile = wrapTeX(inFile);
563
564         File tmpDir = latexFile.getParentFile();
565
566         String latexBaseName = latexFile.getName();
567         String latexRootName = Helpers.setFileExtension(latexBaseName, null);
568
569         File auxFile = new File(tmpDir, latexRootName + ".aux");
570         File logFile = new File(tmpDir, latexRootName + ".log");
571         File dviFile = new File(tmpDir, latexRootName + ".dvi");
572         File epsFile = new File(tmpDir, latexRootName + ".eps");
573
574         try {
575             StringBuilder cmd = new StringBuilder();
576             cmd.append('\"');
577             cmd.append(latex);
578             cmd.append("\" --interaction batchmode ");
579             cmd.append(latexBaseName);
580
581             shellExec(cmd.toString(), tmpDir);
582
583             if (!dviFile.isFile()) {
584                 throw new IOException("'" + inFile + "' has TeX errors");
585             }
586
587             cmd = new StringBuilder();
588             cmd.append('\"');
589             cmd.append(dvips);
590             cmd.append("\" -E -o \"");
591             cmd.append(epsFile);
592             cmd.append("\" \"");
593             cmd.append(dviFile);
594             cmd.append("\"");
595
596             shellExec(cmd.toString(), tmpDir);
597
598             if ("ps".equalsIgnoreCase(printFormat)) {
599                 Helpers.copyFile(epsFile, outFile);
600             } else if ("pdf".equalsIgnoreCase(printFormat)) {
601                 cmd = new StringBuilder();
602                 cmd.append('\"');
603                 cmd.append(ps2pdf);
604                 cmd.append("\" -dEPSCrop \"");
605                 cmd.append(epsFile);
606                 cmd.append("\" \"");
607                 cmd.append(outFile);
608                 cmd.append("\"");
609
610                 shellExec(cmd.toString(), tmpDir);
611             } else {
612                 cmd = new StringBuilder();
613                 cmd.append('\"');
614                 cmd.append(gs);
615                 cmd.append("\" -q -dBATCH -dNOPAUSE -sDEVICE=png16m");
616                 cmd.append(" -r96 -dTextAlphaBits=4 -dGraphicsAlphaBits=4");
617                 cmd.append(" -dEPSCrop \"-sOutputFile=");
618                 cmd.append(outFile);
619                 cmd.append("\" \"");
620                 cmd.append(epsFile);
621                 cmd.append("\"");
622
623                 shellExec(cmd.toString(), tmpDir);
624             }
625         } finally {
626             // Delete all temporary files ---
627             latexFile.delete();
628             if (auxFile.isFile()) {
629                 if (!auxFile.delete())
630                 {
631                     System.err.println("Cannot delete '" + auxFile + "'");
632                 }
633             }
634             if (logFile.isFile()) {
635                 if (!logFile.delete())
636                 {
637                     System.err.println("Cannot delete '" + logFile + "'"); 
638                 }
639             }
640             if (dviFile.isFile()) {
641                 if (!dviFile.delete())
642                 {
643                     System.err.println("Cannot delete '" + dviFile + "'");
644                 }
645             }
646             if (epsFile.isFile()) {
647                 if (!epsFile.delete())
648                 {
649                     System.err.println("Cannot delete '" + epsFile + "'");
650                 }
651             }
652         }
653     }
654
655     private File wrapTeX(File inFile)
656     throws IOException {
657         String tex = Helpers.loadString(inFile, "ISO-8859-1");
658
659         File latexFile = File.createTempFile("CopyConvert", ".tex", TMPDIR);
660
661         StringBuilder buffer = new StringBuilder();
662         buffer.append("\\documentclass[12pt]{article}\n");
663         buffer.append("\\usepackage[latin1]{inputenc}\n");
664         buffer.append("\\pagestyle{empty}\n");
665         buffer.append("\\begin{document}\n");
666         buffer.append(tex);
667         buffer.append('\n');
668         buffer.append("\\end{document}\n");
669
670         Helpers.saveString(buffer.toString(), latexFile, "ISO-8859-1");
671
672         return latexFile;
673     }
674
675     private void shellExec(String command, File workDir)
676     throws IOException, InterruptedException {
677         int status =
678             Helpers.shellExec(command, /*envp*/ null, workDir, verbose);
679         if (status != 0) {
680             throw new RuntimeException("command '" + command
681                                        + "' has exited with non-zero status "
682                                        + status);
683         }
684     }
685
686     private boolean convertMathML(File inFile, File outFile) {
687         MutableLayoutContext context = new LayoutContextImpl(LayoutContextImpl
688                 .getDefaultLayoutContext());
689         context.setParameter(Parameter.ANTIALIAS, "true");
690         // Workaround a XEP problem. FOP 1 is OK.
691         context.setParameter(Parameter.MATHBACKGROUND, "#FFFFFF");
692         context.setParameter(Parameter.MATHSIZE, "18");
693
694         try {
695             Converter.getInstance().convert(inFile, outFile, "image/png", context);
696
697             return true;
698         } catch (IOException e) {
699             reportError("Cannot convert '" + inFile + "' to '" + outFile + "': " + Helpers.reason(e));
700             return false;
701         }
702     }
703
704     private boolean convertSVG(boolean gunzip, File inFile, File outFile) {
705         Transcoder transcoder = new PNGTranscoder();
706         transcoder.addTranscodingHint(
707             ImageTranscoder.KEY_FORCE_TRANSPARENT_WHITE,
708             Boolean.TRUE);
709
710         try {
711             InputStream in = new FileInputStream(inFile);
712             if (gunzip) {
713                 in = new GZIPInputStream(in);
714             }
715
716             try {
717                 TranscoderInput input = new TranscoderInput(in);
718                 input.setURI(inFile.toURI().toASCIIString());
719
720                 // Failing to do so causes Batik to report this error: SAX2
721                 // driver class org.apache.xerces.parsers.SAXParser not found
722                 // which is normal because we don't want to bundle a copy of
723                 // Xerces.
724
725                 transcoder.addTranscodingHint(
726                     XMLAbstractTranscoder.KEY_XML_PARSER_CLASSNAME,
727                     parser.getXMLReader().getClass().getName());
728                 transcoder.addTranscodingHint(
729                     XMLAbstractTranscoder.KEY_XML_PARSER_VALIDATING,
730                     Boolean.FALSE);
731
732                 OutputStream outf = new FileOutputStream(outFile);
733
734                 try {
735                     TranscoderOutput output = new TranscoderOutput(outf);
736                     transcoder.transcode(input, output);
737                     outf.flush();
738                 } finally {
739                     outf.close();
740                 }
741             } finally {
742                 in.close();
743             }
744             return true;
745         } catch (Exception e) {
746             reportError("Cannot convert '" + inFile + "' to '"
747                         + outFile + "': " + Helpers.reason(e));
748             return false;
749         }
750     }
751
752     // -----------------------------------
753     // Batik's ErrorHandler
754     // -----------------------------------
755
756     public void warning(TranscoderException e)
757     throws TranscoderException {
758         String msg = "SVG transcoder warning: " + Helpers.reason(e);
759         reportWarning(msg);
760     }
761
762     public void error(TranscoderException e)
763     throws TranscoderException {
764         String msg = "SVG transcoder error: " + Helpers.reason(e);
765         reportError(msg);
766     }
767
768     public void fatalError(TranscoderException e)
769     throws TranscoderException {
770         String msg = "SVG transcoder fatal error: " + Helpers.reason(e);
771         reportError(msg);
772     }
773
774     // -----------------------------------------------------------------------
775     // Utilities
776     // -----------------------------------------------------------------------
777
778     private void reportInfo(String message) {
779         if (verbose) {
780             System.out.println(message);
781         }
782     }
783
784     private static void reportWarning(String message) {
785         System.err.println("warning: " + message);
786     }
787
788     private static void reportError(String message) {
789         System.err.println("*** error: " + message);
790     }
791
792     // -----------------------------------------------------------------------
793     // Main
794     // -----------------------------------------------------------------------
795
796     public static int main(String[] args) {
797         boolean verbose = false;
798         String printFormat = null;
799         boolean usage = false;
800         int l = 0;
801
802         for (; l < args.length; ++l) {
803             String arg = args[l];
804
805             if ("-v".equalsIgnoreCase(arg)) {
806                 verbose = true;
807             } else if ("-ps".equalsIgnoreCase(arg)) {
808                 printFormat = "ps";
809             } else if ("-pdf".equalsIgnoreCase(arg)) {
810                 printFormat = "pdf";
811             } else {
812                 if (arg.startsWith("-")) {
813                     usage = true;
814                 }
815                 break;
816             }
817         }
818
819         if (!usage && l + 2 != args.length) {
820             usage = true;
821         }
822
823         if (usage) {
824             System.err.println(
825                 "Usage: java org.scilab.modules.helptools.CopyConvert"
826                 + "  {-ps|-pdf}? {-v}? in_xml_file out_xml_file\n"
827                 + "Creates out_xml_file as a ``flattened'' copy of\n"
828                 + "in_xml_file.\n"
829                 + "All graphics files referenced by <imagedata fileref='XXX'/>\n"
830                 + "elements are copied, possibly after being converted to PNG,\n"
831                 + "to the directory containing in_xml_file.\n"
832                 + "(This directory is assumed to be a temporary \n"
833                 + "working directory.)\n"
834                 + "Options are:\n"
835                 + "  -ps  Target format is PostScript rather than HTML.\n"
836                 + "  -pdf Target format is PDF rather than HTML.\n"
837                 + "  -v   Verbose.");
838             return 1;
839         }
840
841         File inFile = new File(args[l]);
842         File outFile = new File(args[l + 1]);
843
844         CopyConvert copyConvert = new CopyConvert();
845         copyConvert.verbose = verbose;
846         copyConvert.printFormat = printFormat;
847
848         try {
849             copyConvert.run(inFile, outFile);
850         } catch (Exception e) {
851             reportError("Cannot copy/convert '" + inFile + "' to '" + outFile
852                         + "': " + Helpers.reason(e));
853             return 2;
854         }
855         return 0;
856     }
857
858     protected class LaTeXElement {
859         int size = 18;
860         Color fg = null, bg = null;
861         int disp = TeXConstants.STYLE_DISPLAY;
862         String code = "mediaobject><imageobject><imagedata";
863         String LaTeX = "";
864         boolean exported;
865         String attribs, align = "";
866
867         protected LaTeXElement(Attributes attrs, boolean exported) {
868             this.exported = exported;
869             int n = attrs.getLength();
870             String fgS = "", bgS = "";
871             String dispS = "display";
872
873             for (int i = 0; i < n; i++) {
874                 String attr = attrs.getValue(i);
875                 String name = attrs.getLocalName(i);
876                 if ("align".equals(name)) {
877                     code += " align=\'" + attr + "\'";
878                     align = attr;
879                 } else if ("style".equals(name)) {
880                     if ("text".equals(attr)) {
881                         disp = TeXConstants.STYLE_TEXT;
882                     } else if ("script".equals(attr)) {
883                         disp = TeXConstants.STYLE_SCRIPT;
884                     } else if ("script_script".equals(attr)) {
885                         disp = TeXConstants.STYLE_SCRIPT_SCRIPT;
886                     }
887                     dispS = attr;
888                 } else if ("size".equals(name)) {
889                     try {
890                         size = Integer.parseInt(attr);
891                     } catch (NumberFormatException e) {
892                         size = 16;
893                     }
894                 } else if ("fg".equals(name)) {
895                     fg = Color.decode(attr);
896                     fgS = attr;
897                 } else if ("bg".equals(name)) {
898                     bg = Color.decode(attr);
899                     bgS = attr;
900                 }
901             }
902
903             attribs = " align='" + align + "'";
904             attribs += " size='" + size + "'";
905             attribs += " style='" + dispS + "'";
906             if (bg != null) {
907                 attribs += " bg='" + bgS + "'";
908             }
909             if (fg != null) {
910                 attribs += " fg='" + fgS + "'";
911             }
912         }
913
914         protected void setLaTeX(String str) {
915             LaTeX += str;
916         }
917
918         protected void generate(Locator loc) throws SAXParseException {
919             if (exported) {
920                 generatePNG(loc);
921                 return;
922             }
923             out.write("latex" + attribs  + " xmlns=\"http://forge.scilab.org/p/jlatexmath\"><![CDATA[" + LaTeX + "]]></latex>");
924         }
925
926         protected void generatePNG(Locator loc) throws SAXParseException {
927             TeXFormula tf;
928             try {
929                 tf = new TeXFormula(LaTeX);
930             } catch (ParseException e) {
931                 throw new SAXParseException(
932                     "\nThere was a problem in parsing the LaTeX' formula : \n"
933                     + e.getMessage(), locator);
934             }
935             File f = new File(outDir, "graphics-" + (++graphicsCounter)
936                               + "_latex.png");
937             reportInfo("Converting LaTeX formula to " + f + "'...");
938             tf.createPNG(disp, size, f.getPath(), bg, fg);
939
940             String end = "";
941             if (align.length() == 0) {
942                 code = "inline" + code;
943                 end = "inline";
944             }
945
946             out.write(code + " fileref='graphics-" + graphicsCounter
947                       + "_latex.png'/>" + "</imageobject></" + end
948                       + "mediaobject>");
949         }
950     }
951
952 }