1 /*
2 blahtex: a TeX to MathML converter designed with MediaWiki in mind
3 blahtexml: an extension of blahtex with XML processing in mind
4 http://gva.noekeon.org/blahtexml
5 
6 Copyright (c) 2006, David Harvey
7 Copyright (c) 2010, Gilles Van Assche
8 All rights reserved.
9 
10 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
11 
12     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
13     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
14     * Neither the names of the authors nor the names of their affiliation may be used to endorse or promote products derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17 */
18 
19 #include "BlahtexCore/Interface.h"
20 #include "UnicodeConverter.h"
21 #include "mainPng.h"
22 #include <iostream>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sstream>
26 #include <stdexcept>
27 #include <iostream>
28 #include <fstream>
29 
30 #include <unistd.h>
31 
32 using namespace std;
33 using namespace blahtex;
34 
35 #ifdef BLAHTEXML_USING_XERCES
36 #include <string.h>
37 #include <xercesc/framework/StdInInputSource.hpp>
38 #include <xercesc/framework/XMLFormatter.hpp>
39 #include <xercesc/parsers/SAX2XMLFilterImpl.hpp>
40 #include <xercesc/sax2/Attributes.hpp>
41 #include <xercesc/sax2/SAX2XMLReader.hpp>
42 #include <xercesc/sax2/XMLReaderFactory.hpp>
43 #include <xercesc/util/OutOfMemoryException.hpp>
44 #include <xercesc/util/PlatformUtils.hpp>
45 #include <xercesc/util/TransService.hpp>
46 #include <xercesc/util/XMLString.hpp>
47 #include <xercesc/util/XMLUniDefs.hpp>
48 #include "BlahtexXMLin/BlahtexFilter.h"
49 #include "BlahtexXMLin/SAX2Output.h"
50 #endif
51 
52 string gBlahtexVersion = "0.9";
53 
54 // A single global instance of UnicodeConverter.
55 UnicodeConverter gUnicodeConverter;
56 
57 // Imported from Messages.cpp:
58 extern wstring GetErrorMessage(const blahtex::Exception& e);
59 extern wstring GetErrorMessages();
60 
61 // FormatError() converts a blahtex Exception object into a string like
62 // "<error><id>...</id><arg>...</arg><arg>...</arg> ...
63 // <message>...</message></error".
FormatError(const blahtex::Exception & e,const EncodingOptions & options)64 wstring FormatError(
65     const blahtex::Exception& e,
66     const EncodingOptions& options
67 )
68 {
69     wstring output = L"<error><id>" + e.GetCode() + L"</id>";
70     for (vector<wstring>::const_iterator
71         arg = e.GetArgs().begin(); arg != e.GetArgs().end(); arg++
72     )
73         output += L"<arg>" + XmlEncode(*arg, options) + L"</arg>";
74 
75     output += L"<message>";
76     output += XmlEncode(GetErrorMessage(e), options);
77     output += L"</message>";
78 
79     output += L"</error>";
80     return output;
81 }
82 
ULLToWstring(unsigned long long number)83 wstring ULLToWstring(unsigned long long number)
84 {
85     wstring wstr;
86 
87     stringstream ss;
88     ss << number;
89 
90     string str = ss.str();
91 
92     return wstr.assign(str.begin(), str.end());
93 }
94 
FormatTokenError(const blahtex::TokenException & e,const EncodingOptions & options)95 wstring FormatTokenError(const blahtex::TokenException & e, const EncodingOptions & options)
96 {
97     wstring output = L"<error><id>" + e.GetCode() + L"</id>";
98 
99     for (vector<wstring>::const_iterator arg = e.GetArgs().begin(); arg != e.GetArgs().end(); arg++)
100         output += L"<arg>" + XmlEncode(*arg, options) + L"</arg>";
101 
102     output += L"<message>";
103     output += XmlEncode(GetErrorMessage(e), options);
104     output += L"</message>";
105 
106     output += L"<startPos>";
107     output += ULLToWstring(e.getToken().getStartPos());
108     output += L"</startPos>";
109 
110     output += L"<length>";
111     output += ULLToWstring(e.getToken().getLength());
112     output += L"</length>";
113 
114     output += L"</error>";
115 
116     return output;
117 }
118 
119 // ShowUsage() prints a help screen.
ShowUsage()120 void ShowUsage()
121 {
122     cout << "\n"
123 #ifdef BLAHTEXML_USING_XERCES
124 "Blahtexml version " << gBlahtexVersion << "\n"
125 #else
126 "Blahtex version " << gBlahtexVersion << "\n"
127 #endif
128 "Copyright (C) 2006, David Harvey\n"
129 "Copyright (C) 2007-2010, Gilles Van Assche\n"
130 "\n"
131 "This is free software; see the source "
132 "for copying conditions. There is NO\n"
133 "warranty; not even for MERCHANTABILITY "
134 "or FITNESS FOR A PARTICULAR PURPOSE.\n"
135 "\n"
136 #ifdef BLAHTEXML_USING_XERCES
137 "Normal mode:    blahtexml [ options ] < inputfile > outputfile\n"
138 "XML input mode: blahtexml --xmlin [ options ] < inputfile > outputfile\n"
139 #else
140 "Usage: blahtex [ options ] < inputfile > outputfile\n"
141 #endif
142 "\n"
143 "SUMMARY OF OPTIONS (see manual for details)\n"
144 "\n"
145 " --texvc-compatible-commands\n"
146 "\n"
147 " --mathml\n"
148 " --displaymath\n"
149 " --indented\n"
150 " --spacing { strict | moderate | relaxed }\n"
151 " --mathml-version-1-fonts\n"
152 " --disallow-plane-1\n"
153 " --mathml-encoding { raw | numeric | short | long }\n"
154 " --other-encoding { raw | numeric }\n"
155 "\n"
156 " --png\n"
157 " --displaymath\n"
158 " --use-ucs-package\n"
159 " --use-cjk-package\n"
160 " --use-preview-package\n"
161 " --japanese-font  fontname\n"
162 " --shell-latex  command\n"
163 " --shell-dvipng  command\n"
164 " --temp-directory  directory\n"
165 " --png-directory  directory\n"
166 " --png-latex-preamble content\n"
167 " --png-latex-before-math content\n"
168 "\n"
169 " --debug { parse | layout | purified }\n"
170 " --keep-temp-files\n"
171 " --throw-logic-error\n"
172 " --print-error-messages\n"
173 "\n"
174 #ifdef BLAHTEXML_USING_XERCES
175 " --doctype-system DTD\n"
176 " --doctype-public PublicID DTD\n"
177 " --doctype-xhtml+mathml\n"
178 " --mathml-nsprefix-auto\n"
179 " --mathml-nsprefix-none\n"
180 " --mathml-nsprefix prefix\n"
181 " --annotate-PNG\n"
182 " --annotate-TeX\n"
183 "\n"
184 "\n"
185 #endif
186 "More information available at http://gva.noekeon.org/blahtexml/\n"
187 "\n";
188 
189     // FIX: need command line option to select output DPI
190 
191     exit(0);
192 }
193 
194 // CommandLineException is used for reporting incorrect command line
195 // syntax.
196 struct CommandLineException
197 {
198     string mMessage;
199 
CommandLineExceptionCommandLineException200     CommandLineException(
201         const string& message
202     ) :
203         mMessage(message)
204     { }
205 };
206 
207 // Adds a trailing slash to the string, if it's not already there.
AddTrailingSlash(string & s)208 void AddTrailingSlash(string& s)
209 {
210     if (!s.empty() && s[s.size() - 1] != '/')
211         s += '/';
212 }
213 
214 PngParams pngParams;
215 #ifdef BLAHTEXML_USING_XERCES
216 SAX2Output::Doctype outputDoctype = SAX2Output::DoctypeNone;
217 string outputPublicID;
218 string outputDTD;
219 BlahtexFilter::PrefixType MathMLPrefixType = BlahtexFilter::PrefixAuto;
220 string MathMLPrefix;
221 bool annotatePNG, annotateTeX;
batchXMLConversion(blahtex::Interface & interface)222 int batchXMLConversion(blahtex::Interface& interface)
223 {
224     cerr << "\n"
225         "Blahtexml version " << gBlahtexVersion << "\n"
226         "Copyright (C) 2006, David Harvey\n"
227         "Copyright (C) 2007-2010, Gilles Van Assche\n"
228         "\n"
229         "This is free software; see the source "
230         "for copying conditions. There is NO\n"
231         "warranty; not even for MERCHANTABILITY "
232         "or FITNESS FOR A PARTICULAR PURPOSE.\n";
233     cerr << endl;
234     try {
235          XMLPlatformUtils::Initialize();
236     }
237     catch (const XMLException& toCatch) {
238          XERCES_STD_QUALIFIER cerr << "Error during initialization! :\n" << XMLString::transcode(toCatch.getMessage()) << endl;
239          return 1;
240     }
241     SAX2XMLReader* reader = XMLReaderFactory::createXMLReader();
242     BlahtexFilter* parser = new BlahtexFilter(reader, interface);
243 
244     parser->setFeature(XMLUni::fgSAX2CoreValidation, false);
245     parser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
246     parser->setFeature(XMLUni::fgXercesSchema, false);
247     parser->setFeature(XMLUni::fgXercesSchemaFullChecking, false);
248     parser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, false);
249 
250     XercesString _MathMLPrefix(MathMLPrefix.c_str());
251     wstring __MathMLPrefix = _MathMLPrefix.convertTowstring();
252     parser->setDesiredMathMLPrefixType(MathMLPrefixType, __MathMLPrefix);
253     parser->setAnnotatePNG(annotatePNG);
254     parser->setAnnotateTeX(annotateTeX);
255     parser->setPngParams(pngParams);
256 
257     int parserErrors = 0;
258     int result = 0;
259     {
260         StdInInputSource stdIn;
261         try {
262             XercesString publicID(outputPublicID.c_str());
263             XercesString DTD(outputDTD.c_str());
264             SAX2Output output(cout, "UTF-8", XMLFormatter::UnRep_CharRef, outputDoctype, publicID, DTD);
265             parser->setContentHandler(&output);
266             parser->setErrorHandler(&output);
267             parser->parse(stdIn);
268             parserErrors = parser->getErrorCount();
269         }
270         catch (const OutOfMemoryException&) {
271             cerr << endl;
272             cerr << "Out of memory exception" << endl;
273             result = 1;
274         }
275         catch (const XMLException& e) {
276             char *errorString = XMLString::transcode(e.getMessage());
277             cerr << endl;
278             cerr << "Error: " << errorString << endl;
279             XMLString::release(&errorString);
280             result = 1;
281         }
282     }
283     if (parserErrors > 0) {
284         cerr << "During the XML input parsing, ";
285         if (parserErrors == 1) cerr << "an error";
286         else cerr << parserErrors << " errors";
287         cerr << " occurred." << endl;
288     }
289     int blahtexErrors = parser->getNumberOfErrors();
290     if (blahtexErrors > 0) {
291         cerr << "During the blahtex conversion, ";
292         if (blahtexErrors == 1) cerr << "an error";
293         else cerr << blahtexErrors << " errors";
294         cerr << " occurred.\nSee the output file for more information." << endl;
295         result = 1;
296     }
297     delete parser;
298     delete reader;
299     XMLPlatformUtils::Terminate();
300     return result;
301 }
302 #endif
303 
main(int argc,char * const argv[])304 int main (int argc, char* const argv[]) {
305     // This outermost try block catches std::runtime_error
306     // and CommandLineException.
307     try
308     {
309         gUnicodeConverter.Open();
310 
311         blahtex::Interface interface;
312 
313         bool doPng    = false;
314         bool doMathml = false;
315 #ifdef BLAHTEXML_USING_XERCES
316         bool doXMLinput = false;
317         annotatePNG = false;
318         annotateTeX = false;
319 #endif
320 
321         bool debugLayoutTree  = false;
322         bool debugParseTree   = false;
323         bool debugPurifiedTex = false;
324 
325         pngParams.deleteTempFiles  = true;
326         pngParams.shellLatex    = "latex";
327         pngParams.shellDvipng   = "dvipng";
328         pngParams.tempDirectory = "./";
329         pngParams.pngDirectory  = "./";
330 
331         bool displayStyle = false;
332 
333         const char *inputFilePath = NULL;
334 
335 
336         // Process command line arguments
337         for (int i = 1; i < argc; i++)
338         {
339             string arg(argv[i]);
340 
341             if (arg == "--help")
342                 ShowUsage();
343 
344             else if (arg == "--input-file")
345             {
346                 if (++i == argc)
347                     throw CommandLineException("Missing file path after \"--input-file\"");
348 
349                 inputFilePath = argv[i];
350             }
351 
352             else if (arg == "--print-error-messages")
353             {
354                 cout << gUnicodeConverter.ConvertOut(GetErrorMessages())
355                     << endl;
356                 return 0;
357             }
358 
359             else if (arg == "--throw-logic-error")
360                 throw logic_error("Aaarrrgggghhhh!");
361 
362             else if (arg == "--shell-latex")
363             {
364                 if (++i == argc)
365                     throw CommandLineException(
366                         "Missing string after \"--shell-latex\""
367                     );
368                 pngParams.shellLatex = string(argv[i]);
369             }
370 
371             else if (arg == "--shell-dvipng")
372             {
373                 if (++i == argc)
374                     throw CommandLineException(
375                         "Missing string after \"--shell-dvipng\""
376                     );
377                 pngParams.shellDvipng = string(argv[i]);
378             }
379 
380             else if (arg == "--temp-directory")
381             {
382                 if (++i == argc)
383                     throw CommandLineException(
384                         "Missing string after \"--temp-directory\""
385                     );
386                 pngParams.tempDirectory = string(argv[i]);
387                 AddTrailingSlash(pngParams.tempDirectory);
388             }
389 
390             else if (arg == "--png-directory")
391             {
392                 if (++i == argc)
393                     throw CommandLineException(
394                         "Missing string after \"--png-directory\""
395                     );
396                 pngParams.pngDirectory = string(argv[i]);
397                 AddTrailingSlash(pngParams.pngDirectory);
398             }
399 
400             else if (arg == "--displaymath") {
401                 interface.mPurifiedTexOptions.mDisplayMath = true;
402                 displayStyle = true;
403             }
404 
405             else if (arg == "--use-ucs-package")
406                 interface.mPurifiedTexOptions.mAllowUcs = true;
407 
408             else if (arg == "--use-cjk-package")
409                 interface.mPurifiedTexOptions.mAllowCJK = true;
410 
411             else if (arg == "--use-preview-package")
412                 interface.mPurifiedTexOptions.mAllowPreview = true;
413 
414             else if (arg == "--japanese-font")
415             {
416                 if (++i == argc)
417                     throw CommandLineException(
418                         "Missing string after \"--japanese-font\""
419                     );
420                 interface.mPurifiedTexOptions.mJapaneseFont =
421                     gUnicodeConverter.ConvertIn(string(argv[i]));
422             }
423 
424             else if (arg == "--indented")
425                 interface.mIndented = true;
426 
427             else if (arg == "--spacing")
428             {
429                 if (++i == argc)
430                     throw CommandLineException(
431                         "Missing string after \"--spacing\""
432                     );
433                 arg = string(argv[i]);
434 
435                 if (arg == "strict")
436                     interface.mMathmlOptions.mSpacingControl
437                         = MathmlOptions::cSpacingControlStrict;
438 
439                 else if (arg == "moderate")
440                     interface.mMathmlOptions.mSpacingControl
441                         = MathmlOptions::cSpacingControlModerate;
442 
443                 else if (arg == "relaxed")
444                     interface.mMathmlOptions.mSpacingControl
445                         = MathmlOptions::cSpacingControlRelaxed;
446 
447                 else
448                     throw CommandLineException(
449                         "Illegal string after \"--spacing\""
450                     );
451             }
452 
453             else if (arg == "--mathml-version-1-fonts")
454                 interface.mMathmlOptions.mUseVersion1FontAttributes = true;
455 
456             else if (arg == "--texvc-compatible-commands")
457                 interface.mTexvcCompatibility = true;
458 
459             else if (arg == "--png")
460                 doPng = true;
461 
462             else if (arg == "--mathml")
463                 doMathml = true;
464 
465             else if (arg == "--mathml-encoding")
466             {
467                 if (++i == argc)
468                     throw CommandLineException(
469                         "Missing string after \"--mathml-encoding\""
470                     );
471                 arg = string(argv[i]);
472 
473                 if (arg == "raw")
474                     interface.mEncodingOptions.mMathmlEncoding
475                         = EncodingOptions::cMathmlEncodingRaw;
476 
477                 else if (arg == "numeric")
478                     interface.mEncodingOptions.mMathmlEncoding
479                         = EncodingOptions::cMathmlEncodingNumeric;
480 
481                 else if (arg == "short")
482                     interface.mEncodingOptions.mMathmlEncoding
483                         = EncodingOptions::cMathmlEncodingShort;
484 
485                 else if (arg == "long")
486                     interface.mEncodingOptions.mMathmlEncoding
487                         = EncodingOptions::cMathmlEncodingLong;
488 
489                 else
490                     throw CommandLineException(
491                         "Illegal string after \"--mathml-encoding\""
492                     );
493             }
494 
495             else if (arg == "--disallow-plane-1")
496             {
497                 interface.mMathmlOptions  .mAllowPlane1 = false;
498                 interface.mEncodingOptions.mAllowPlane1 = false;
499             }
500 
501             else if (arg == "--other-encoding")
502             {
503                 if (++i == argc)
504                     throw CommandLineException(
505                         "Missing string after \"--other-encoding\""
506                     );
507                 arg = string(argv[i]);
508                 if (arg == "raw")
509                     interface.mEncodingOptions.mOtherEncodingRaw = true;
510                 else if (arg == "numeric")
511                     interface.mEncodingOptions.mOtherEncodingRaw = false;
512                 else
513                     throw CommandLineException(
514                         "Illegal string after \"--other-encoding\""
515                     );
516             }
517 
518             else if (arg == "--debug")
519             {
520                 if (++i == argc)
521                     throw CommandLineException(
522                         "Missing string after \"--debug\""
523                     );
524                 arg = string(argv[i]);
525                 if (arg == "layout")
526                     debugLayoutTree = true;
527                 else if (arg == "parse")
528                     debugParseTree = true;
529                 else if (arg == "purified")
530                     debugPurifiedTex = true;
531                 else
532                     throw CommandLineException(
533                         "Illegal string after \"--debug\""
534                     );
535             }
536 
537             else if (arg == "--keep-temp-files")
538                 pngParams.deleteTempFiles = false;
539 #ifdef BLAHTEXML_USING_XERCES
540             else if (arg == "--xmlin")
541                 doXMLinput = true;
542             else if (arg == "--doctype-system") {
543                 outputDoctype = SAX2Output::DoctypeSystem;
544                 if (++i == argc) throw CommandLineException("Missing string after \"--doctype-system\"");
545                 outputDTD = argv[i];
546             }
547             else if (arg == "--doctype-public") {
548                 outputDoctype = SAX2Output::DoctypePublic;
549                 if (++i == argc) throw CommandLineException("Missing two strings after \"--doctype-public\"");
550                 outputPublicID = argv[i];
551                 if (++i == argc) throw CommandLineException("Missing one string after \"--doctype-public\"");
552                 outputDTD = argv[i];
553             }
554             else if (arg == "--doctype-xhtml+mathml") {
555                 outputDoctype = SAX2Output::DoctypePublic;
556                 outputPublicID = "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN";
557                 outputDTD = "http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd";
558                 MathMLPrefixType = BlahtexFilter::PrefixNone;
559             }
560             else if (arg == "--mathml-nsprefix-auto")
561                 MathMLPrefixType = BlahtexFilter::PrefixAuto;
562             else if (arg == "--mathml-nsprefix-none")
563                 MathMLPrefixType = BlahtexFilter::PrefixNone;
564             else if (arg == "--mathml-nsprefix") {
565                 MathMLPrefixType = BlahtexFilter::PrefixAdd;
566                 if (++i == argc) throw CommandLineException("Missing string after \"--mathml-nsprefix\"");
567                 MathMLPrefix = argv[i];
568             }
569             else if (arg == "--annotate-PNG")
570                 annotatePNG = true;
571             else if (arg == "--annotate-TeX")
572                 annotateTeX = true;
573 #endif
574             else if (arg == "--png-latex-preamble") {
575                 if (++i == argc) {
576                     throw CommandLineException(
577                         "Missing string after \"--png-latex-preamble\""
578                     );
579                 }
580                 interface.mPurifiedTexOptions.mLaTeXPreamble = gUnicodeConverter.ConvertIn(string(argv[i]));
581             }
582             else if (arg == "--png-latex-before-math") {
583                 if (++i == argc) {
584                     throw CommandLineException(
585                         "Missing string after \"--png-latex-before-math\""
586                     );
587                 }
588                 interface.mPurifiedTexOptions.mLaTeXBeforeMath = gUnicodeConverter.ConvertIn(string(argv[i]));
589             }
590             else
591                 throw CommandLineException(
592                     "Unrecognised command line option \"" + arg + "\""
593                 );
594         }
595 
596         // Finished processing command line, now process the input
597 
598 #ifdef BLAHTEXML_USING_XERCES
599         if (doXMLinput)
600             return batchXMLConversion(interface);
601 #endif
602         if (isatty(0) && !inputFilePath)
603             ShowUsage();
604 
605         wostringstream mainOutput;
606 
607         try
608         {
609             wstring input;
610 
611             // Read input file
612             string inputUtf8;
613             {
614                 if (inputFilePath)
615                 {
616                     ifstream inputFile (inputFilePath, ifstream::in);
617 
618                     if (inputFile.is_open())
619                     {
620                         char c;
621                         while (inputFile.get(c))
622                             inputUtf8 += c;
623                         inputFile.close();
624                     }
625 
626                     else
627 					{
628 						throw CommandLineException("Could not open the input file!");
629 					}
630                 }
631 
632                 else
633                 {
634                     char c;
635                     while (cin.get(c))
636                         inputUtf8 += c;
637 
638                 }
639             }
640 
641             // This try block converts UnicodeConverter::Exception into an
642             // input syntax error, i.e. if the user supplies invalid UTF-8.
643             // (Later we treat such exceptions as debug assertions.)
644             try
645             {
646                 input = gUnicodeConverter.ConvertIn(inputUtf8);
647             }
648             catch (UnicodeConverter::Exception& e)
649             {
650                 throw blahtex::Exception(L"InvalidUtf8Input");
651             }
652 
653             // Build the parse and layout trees.
654             interface.ProcessInput(input, displayStyle);
655 
656             if (debugParseTree)
657             {
658                 mainOutput << L"\n=== BEGIN PARSE TREE ===\n\n";
659                 interface.GetManager()->GetParseTree()->Print(mainOutput);
660                 mainOutput << L"\n=== END PARSE TREE ===\n\n";
661             }
662 
663             if (debugLayoutTree)
664             {
665                 mainOutput << L"\n=== BEGIN LAYOUT TREE ===\n\n";
666                 wostringstream temp;
667                 interface.GetManager()->GetLayoutTree()->Print(temp);
668                 mainOutput << XmlEncode(temp.str(), EncodingOptions());
669                 mainOutput << L"\n=== END LAYOUT TREE ===\n\n";
670             }
671 
672             // Generate purified TeX if required.
673             if (doPng || debugPurifiedTex)
674             {
675                 // This stream is where we build the PNG output block:
676                 wostringstream pngOutput;
677 
678                 try
679                 {
680                     wstring purifiedTex = interface.GetPurifiedTex();
681 
682                     if (debugPurifiedTex)
683                     {
684                         pngOutput << L"\n=== BEGIN PURIFIED TEX ===\n\n";
685                         pngOutput << purifiedTex;
686                         pngOutput << L"\n=== END PURIFIED TEX ===\n\n";
687                     }
688 
689                     // Make the system calls to generate the PNG image
690                     // if requested.
691                     if (doPng)
692                     {
693                         PngInfo info = MakePngFile(purifiedTex, "", pngParams);
694 
695                         // The height and depth measurements are only
696                         // valid if the "preview" package is used:
697                         if (interface.mPurifiedTexOptions.mAllowPreview
698                             && info.mDimensionsValid
699                         )
700                         {
701                             pngOutput << L"<height>"
702                                 << info.mHeight << L"</height>\n";
703                             pngOutput << L"<depth>"
704                                 << info.mDepth << L"</depth>\n";
705                         }
706 
707                         pngOutput << L"<md5>"
708                             << gUnicodeConverter.ConvertIn(info.mMd5)
709                             << L"</md5>\n";
710                     }
711                 }
712 
713                 // Catching errors that occurred during PNG generation:
714                 catch (blahtex::Exception& e)
715                 {
716                     pngOutput.str(L"");
717                     pngOutput << FormatError(e, interface.mEncodingOptions)
718                         << endl;
719                 }
720 
721                 mainOutput << L"<png>\n" << pngOutput.str() << L"</png>\n";
722             }
723 
724             // This block generates MathML output if requested.
725             if (doMathml)
726             {
727                 // This stream is where we build the MathML output block:
728                 wostringstream mathmlOutput;
729 
730                 try
731                 {
732                     mathmlOutput << L"<markup>\n";
733                     mathmlOutput << interface.GetMathml();
734                     if (!interface.mIndented)
735                         mathmlOutput << L"\n";
736                     mathmlOutput << L"</markup>\n";
737                 }
738 
739                 // Catch errors in generating the MathML:
740                 catch (blahtex::Exception& e)
741                 {
742                     mathmlOutput.str(L"");
743                     mathmlOutput
744                         << FormatError(e, interface.mEncodingOptions)
745                         << endl;
746                 }
747 
748                 mainOutput << L"<mathml>\n" << mathmlOutput.str()
749                     << L"</mathml>\n";
750             }
751         }
752 
753         catch (blahtex::TokenException& e)
754         {
755             mainOutput.str(L"");
756             mainOutput << FormatTokenError(e, interface.mEncodingOptions) << endl;
757         }
758 
759         // This catches input syntax errors.
760         catch (blahtex::Exception& e)
761         {
762             mainOutput.str(L"");
763             mainOutput << FormatError(e, interface.mEncodingOptions) << endl;
764         }
765 
766         cout << "<blahtex>\n"
767             << gUnicodeConverter.ConvertOut(mainOutput.str())
768             << "</blahtex>\n";
769     }
770 
771     // The following errors might occur if there's a bug in blahtex that
772     // some assertion condition picked up. We still want to report these
773     // nicely to the user so that they can notify the developers.
774     catch (std::logic_error& e)
775     {
776         // WARNING: this doesn't XML-encode the message
777         // (We don't expect to the message to contain the characters &<>)
778         cout << "<blahtex>\n<logicError>" << e.what()
779             << "</logicError>\n</blahtex>\n";
780     }
781 
782     // These indicate incorrect command line syntax:
783     catch (CommandLineException& e)
784     {
785         cerr << "blahtex: " << e.mMessage
786 #ifdef BLAHTEXML_USING_XERCES
787             << " (try \"blahtexml --help\")\n";
788 #else
789             << " (try \"blahtex --help\")\n";
790 #endif
791     }
792 
793     // These kind of errors should only occur if the program has been
794     // installed incorrectly.
795     catch (std::runtime_error& e)
796     {
797         cerr << "blahtex runtime error: " << e.what() << endl;
798     }
799 
800     return 0;
801 }
802 
803 // end of file @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
804