Clover Coverage Report
Coverage timestamp: Fri May 24 2013 13:47:27 UTC
../../../../../../img/srcFileCovDistChart9.png 45% of files have more coverage
619   1,062   259   21.34
384   814   0.42   29
29     8.93  
1    
 
  Latex2UnicodeParser       Line # 33 619 259 84.1% 0.84108526
 
  (27)
 
1    /* This file is part of the project "Hilbert II" - http://www.qedeq.org
2    *
3    * Copyright 2000-2013, Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10    * This program is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13    * GNU General Public License for more details.
14    */
15   
16    package org.qedeq.kernel.bo.service.unicode;
17   
18    import java.util.Stack;
19   
20    import org.qedeq.base.io.AbstractOutput;
21    import org.qedeq.base.io.SourcePosition;
22    import org.qedeq.base.io.StringOutput;
23    import org.qedeq.base.io.SubTextInput;
24    import org.qedeq.base.io.TextInput;
25    import org.qedeq.base.trace.Trace;
26    import org.qedeq.kernel.bo.service.latex.LatexErrorCodes;
27   
28    /**
29    * Transform LaTeX into Unicode format.
30    *
31    * @author Michael Meyling
32    */
 
33    public final class Latex2UnicodeParser {
34   
35    /** This class. */
36    private static final Class CLASS = Latex2UnicodeParser.class;
37   
38    /** These characters get a special treatment in LaTeX. */
39    private static final String SPECIALCHARACTERS = "(),{}\\~%$&\'`^_-";
40   
41    /** Herein goes our output. */
42    private final AbstractOutput output;
43   
44    /** Resolver for references. */
45    private final ReferenceFinder finder;
46   
47    /** This is our current input stream .*/
48    private SubTextInput input;
49   
50    /** Math mode on? */
51    private boolean mathMode = false;
52   
53    /** Mathfrak mode on? */
54    private boolean mathfrak = false;
55   
56    /** Emphasize on? */
57    private boolean emph = false;
58   
59    /** Bold on? */
60    private boolean bold = false;
61   
62    /** Mathbb on? */
63    private boolean mathbb = false;
64   
65    /** Stack for input parser. */
66    private Stack inputStack = new Stack();
67   
68    /** Stack for math mode. */
69    private Stack mathModeStack = new Stack();
70   
71    /** Stack for mathfrak mode. */
72    private Stack mathfrakStack = new Stack();
73   
74    /** Stack for emphasize mode. */
75    private Stack emphStack = new Stack();
76   
77    /** Stack for bold mode. */
78    private Stack boldStack = new Stack();
79   
80    /** Stack for mathbb mode. */
81    private Stack mathbbStack = new Stack();
82   
83    /** Stack for skipWhitspace mode. */
84    private Stack skipWhitespaceStack = new Stack();
85   
86    /** Should I skip whitespace before printing the next token. */
87    private boolean skipWhitespace;
88   
89    /** Here the last read token begins. This is an absolute position. */
90    private int tokenBegin;
91   
92    /** Here the last read token ends. This is an absolute position. */
93    private int tokenEnd;
94   
95    /** Current item number. */
96    private int itemNumber;
97   
98    /**
99    * Parse LaTeX text into QEDEQ module string.
100    *
101    * @param finder Finder for references.
102    * @param input Parse this input.
103    * @param columns Maximum column number. Break (if possible) before.
104    * @return QEDEQ module string.
105    */
 
106  3896 toggle public static final String transform(final ReferenceFinder finder, final String input,
107    final int columns) {
108  3896 final Latex2UnicodeParser parser = new Latex2UnicodeParser(finder);
109  3896 parser.output.setColumns(columns);
110  3896 return parser.getUtf8(input);
111    }
112   
113    /**
114    * Constructor.
115    *
116    * @param finder Finder for references.
117    */
 
118  3896 toggle private Latex2UnicodeParser(final ReferenceFinder finder) {
119    // use dummy implementation if finder is null
120  3896 if (finder == null) {
121  1381 this.finder = new ReferenceFinder() {
 
122  0 toggle public String getReferenceLink(final String reference,
123    final SourcePosition startDelta, final SourcePosition endDelta) {
124  0 return "[" + reference + "]";
125    }
126   
 
127  0 toggle public void addWarning(final int code, final String msg,
128    final SourcePosition startDelta, final SourcePosition endDelta) {
129    // nothing to do
130    }
131    };
132    } else {
133  2515 this.finder = finder;
134    }
135  3896 this.output = new StringOutput();
136    }
137   
138    /**
139    * Get UTF-8 String out of LaTeX text.
140    *
141    * @param text LaTeX.
142    * @return UTF-8.
143    */
 
144  3896 toggle private String getUtf8(final String text) {
145  3896 skipWhitespace = true;
146  3896 this.input = new SubTextInput(text);
147  3896 parseAndPrint(this.input);
148  3896 return output.toString();
149    }
150   
151    /**
152    * Do parsing and print result.
153    *
154    * @param input Parse this LaTeX text and print UTF-8 into output.
155    */
 
156  7518 toggle private void parseAndPrint(final SubTextInput input) {
157    // remember old:
158  7518 inputStack.push(this.input);
159  7518 mathModeStack.push(Boolean.valueOf(mathMode));
160  7518 mathfrakStack.push(Boolean.valueOf(mathfrak));
161  7518 emphStack.push(Boolean.valueOf(emph));
162  7518 boldStack.push(Boolean.valueOf(bold));
163  7518 mathbbStack.push(Boolean.valueOf(mathbb));
164  7518 skipWhitespaceStack.push(Boolean.valueOf(skipWhitespace));
165  7518 try {
166  7518 this.input = input;
167  7518 boolean whitespace = false;
168  226862 while (!eof()) {
169  219344 String token = readToken();
170  219344 if (!token.startsWith("\\")) {
171  192498 token = token.trim();
172    }
173  219344 if (token.length() == 0) {
174  127951 whitespace = true;
175  127951 continue;
176    }
177  91393 if (whitespace && !"\\par".equals(token)) {
178  40411 print(" ");
179  40411 whitespace = false;
180    }
181  91393 if ("\\begin".equals(token)) {
182  132 parseBegin();
183  91261 } else if ("\\footnote".equals(token)) {
184  99 parseFootnote();
185  91162 } else if ("\\qref".equals(token)) {
186  238 parseQref();
187  90924 } else if ("$$".equals(token)) {
188  13 mathMode = true;
189  13 final SubTextInput content = readTilToken(token);
190  13 println();
191  13 parseAndPrint(content);
192  13 println();
193  13 mathMode = false;
194  90911 } else if ("$".equals(token)) {
195  1885 mathMode = true;
196  1885 final SubTextInput content = readTilToken(token);
197  1885 parseAndPrint(content);
198  1885 mathMode = false;
199  89026 } else if ("\\mathfrak".equals(token)) {
200  506 if ('{' == getChar()) {
201  506 mathfrak = true;
202  506 final SubTextInput content = readCurlyBraceContents();
203  506 parseAndPrint(content);
204  506 mathfrak = false;
205    } else {
206  0 mathfrak = true;
207    }
208  88520 } else if ("\\mathbb".equals(token)) {
209  2 if ('{' == getChar()) {
210  2 mathbb = true;
211  2 final SubTextInput content = readCurlyBraceContents();
212  2 parseAndPrint(content);
213  2 mathbb = false;
214    } else {
215  0 mathbb = true;
216    }
217  88518 } else if ("\\emph".equals(token)) {
218  551 if ('{' == getChar()) {
219  550 emph = true;
220  550 final SubTextInput content = readCurlyBraceContents();
221  550 parseAndPrint(content);
222    // output.addWs("\u2006");
223  550 output.addWs(" ");
224  550 emph = false;
225    } else {
226  1 emph = true;
227    }
228  87967 } else if ("\\textbf".equals(token)) {
229  58 if ('{' == getChar()) {
230  58 bold = true;
231  58 final SubTextInput content = readCurlyBraceContents();
232  58 parseAndPrint(content);
233  58 bold = false;
234    } else {
235  0 bold = true;
236    }
237  87909 } else if ("\\cite".equals(token)) {
238  22 if ('{' == getChar()) {
239  22 final SubTextInput content = readCurlyBraceContents();
240  22 output.addToken("[" + content.asString() + "]");
241    }
242  87887 } else if ("\\tag".equals(token)) {
243  10 if ('{' == getChar()) {
244  10 final SubTextInput content = readCurlyBraceContents();
245  10 output.addToken("(" + content.asString() + ")");
246    }
247  87877 } else if ("\\mbox".equals(token)) {
248  108 if ('{' == getChar()) {
249  108 final SubTextInput content = readCurlyBraceContents();
250  108 parseAndPrint(content);
251    }
252  87769 } else if ("\\cline".equals(token)) {
253  10 if ('{' == getChar()) {
254  10 readCurlyBraceContents();
255    // ignore
256    }
257  10 output.addToken("_______________________________________");
258  10 println();
259  87759 } else if ("\\item".equals(token)) {
260  96 output.popLevel(3);
261  96 itemNumber++;
262  96 output.println();
263  96 output.addToken(itemNumber + ".");
264  96 output.addWs("");
265  96 output.pushLevel(" ");
266  96 output.setTabLevel();
267  87663 } else if ("{".equals(token)) {
268  273 input.readInverse();
269  273 final SubTextInput content = readCurlyBraceContents();
270  273 parseAndPrint(content);
271  87390 } else if ("\\url".equals(token)) {
272  64 final SubTextInput content = readCurlyBraceContents();
273  64 output.addToken(" " + content.asString() + " ");
274  87326 } else if ('{' == getChar() && ("\\index".equals(token) || "\\label".equals(token)
275    || token.equals("\\vspace") || token.equals("\\hspace")
276    || token.equals("\\vspace*") || token.equals("\\hspace*"))) {
277    // ignore content
278  466 readCurlyBraceContents();
279  86860 } else if ("_".equals(token) || "^".equals(token)) {
280  788 if (mathMode) {
281  786 String content;
282  786 if ('{' == getChar()) {
283  8 content = readCurlyBraceContents().asString();
284    } else {
285  778 content = readToken();
286    }
287  786 if ("_".equals(token)) {
288  706 printSubscript(content);
289    } else {
290  80 printSuperscript(content);
291    }
292    } else {
293  2 print(token);
294    }
295    } else {
296  86072 print(token);
297    }
298    }
299    } finally {
300  7518 this.input = (SubTextInput) inputStack.pop();
301  7518 mathMode = ((Boolean) mathModeStack.pop()).booleanValue();
302  7518 mathfrak = ((Boolean) mathfrakStack.pop()).booleanValue();
303  7518 emph = ((Boolean) emphStack.pop()).booleanValue();
304  7518 bold = ((Boolean) boldStack.pop()).booleanValue();
305  7518 skipWhitespace = ((Boolean) skipWhitespaceStack.pop()).booleanValue();
306  7518 output.flush();
307    }
308    }
309   
310    /**
311    * Parse after \footnote.
312    */
 
313  99 toggle private void parseFootnote() {
314  99 if ('{' == getChar()) {
315  99 final SubTextInput content = readCurlyBraceContents();
316  99 println();
317  99 output.printWithoutSplit(" \u250C");
318  99 output.pushLevel();
319  99 output.pushLevel();
320  99 output.pushLevel();
321  99 output.pushLevel();
322  99 output.pushLevel();
323  99 output.pushLevel("\u2502 ");
324  99 println();
325  99 parseAndPrint(content);
326  99 output.popLevel();
327  99 output.popLevel();
328  99 output.popLevel();
329  99 output.popLevel();
330  99 output.popLevel();
331  99 output.popLevel();
332  99 println();
333  99 output.printWithoutSplit(" \u2514");
334  99 println();
335    }
336    }
337   
338    /**
339    * Transform <code>\qref{key}</code> entries into common LaTeX code.
340    *
341    * @param text Work on this text.
342    * @return Result of transforming \qref into text.
343    */
344    /**
345    * Parse after \footnote.
346    */
 
347  238 toggle private void parseQref() {
348  238 final String method = "parseQref()";
349  238 final int localStart1 = input.getAbsolutePosition();
350  238 if ('{' == getChar()) {
351  238 final SubTextInput content = readCurlyBraceContents();
352  238 String ref = content.asString().trim();
353  238 Trace.param(CLASS, this, method, "ref", ref);
354  238 if (ref.length() == 0) {
355  1 addWarning(LatexErrorCodes.QREF_EMPTY_CODE, LatexErrorCodes.QREF_EMPTY_TEXT,
356    localStart1, input.getAbsolutePosition());
357  1 return;
358    }
359  237 if (ref.length() > 1024) {
360  0 addWarning(LatexErrorCodes.QREF_END_NOT_FOUND_CODE,
361    LatexErrorCodes.QREF_END_NOT_FOUND_TEXT,
362    localStart1, input.getAbsolutePosition());
363  0 return;
364    }
365  237 if (ref.indexOf("{") >= 0) {
366  1 addWarning(LatexErrorCodes.QREF_END_NOT_FOUND_CODE,
367    LatexErrorCodes.QREF_END_NOT_FOUND_TEXT,
368    localStart1, input.getAbsolutePosition());
369  1 input.setAbsolutePosition(localStart1);
370  1 return;
371    }
372   
373  236 String display = finder.getReferenceLink(ref, getAbsoluteSourcePosition(localStart1),
374    getAbsoluteSourcePosition(input.getAbsolutePosition()));
375  236 output.addToken(display);
376    }
377    }
378   
379   
380    /**
381    * Parse after \begin.
382    */
 
383  132 toggle private void parseBegin() {
384  132 final String kind = readCurlyBraceContents().asString(); // ignore
385  132 final SubTextInput content = readSection(kind);
386  132 if ("eqnarray".equals(kind)
387    || "eqnarray*".equals(kind)
388    || "equation*".equals(kind)) {
389  10 mathMode = true;
390  10 skipWhitespace = false;
391  10 parseAndPrint(content);
392  10 println();
393  10 mathMode = false;
394  122 } else if ("quote".equals(kind)) {
395  9 output.pushLevel();
396  9 output.pushLevel();
397  9 output.pushLevel();
398  9 println();
399  9 parseAndPrint(content);
400  9 println();
401  9 output.popLevel();
402  9 output.popLevel();
403  9 output.popLevel();
404  113 } else if ("tabularx".equals(kind)) {
405  65 skipWhitespace = false;
406  65 parseAndPrint(content);
407  48 } else if ("enumerate".equals(kind)) {
408  8 itemNumber = 0;
409  8 output.pushLevel(" ");
410  8 parseAndPrint(content);
411  8 output.popLevel(3);
412  40 } else if ("verbatim".equals(kind)) {
413  4 final String level = output.getLevel();
414  4 output.setLevel("");
415  4 print(content.asString());
416  4 output.setLevel(level);
417    } else {
418  36 parseAndPrint(content);
419    }
420    }
421   
 
422  706 toggle private void printSubscript(final String content) {
423  706 output.addToken(Latex2UnicodeSpecials.transform2Subscript(content));
424    }
425   
 
426  80 toggle private void printSuperscript(final String content) {
427  80 output.addToken(Latex2UnicodeSpecials.transform2Superscript(content));
428    }
429   
430    /**
431    * Read until section ends with \{kind}.
432    *
433    * @param kind Look for the end of this.
434    * @return Read text.
435    */
 
436  132 toggle private SubTextInput readSection(final String kind) {
437  132 if ('{' == getChar()) { // skip content
438  73 readCurlyBraceContents();
439    }
440  132 if ('{' == getChar()) { // skip content
441  65 readCurlyBraceContents();
442    }
443  132 final int localStart = input.getAbsolutePosition();
444  132 int current = localStart;
445  132 do {
446  45051 current = input.getAbsolutePosition();
447  45051 final String item = readToken();
448  45051 if (item == null) {
449  0 Trace.fatal(CLASS, this, "readSection", "not found: " + "\\end{" + kind + "}",
450    new IllegalArgumentException("from " + localStart + " to " + input.getAbsolutePosition()
451    + input.getPosition()));
452  0 break;
453    }
454  45051 if ("\\end".equals(item)) {
455  137 final String curly2 = readCurlyBraceContents().asString();
456  137 if (kind.equals(curly2)) {
457  132 break;
458    }
459    }
460    } while (true);
461  132 return input.getSubTextInput(localStart, current);
462    }
463   
464    /**
465    * Get text till <code>token</code> occurs.
466    *
467    * @param token Terminator token.
468    * @return Read text before token.
469    */
 
470  1898 toggle private SubTextInput readTilToken(final String token) {
471  1898 final int localStart = input.getAbsolutePosition();
472  1898 final StringBuffer buffer = new StringBuffer();
473  1898 int current = localStart;
474  1898 do {
475  16701 current = input.getAbsolutePosition();
476  16701 final String item = readToken();
477  16701 if (item == null) {
478  0 Trace.fatal(CLASS, this, "readSection", "not found: " + token,
479    new IllegalArgumentException("from " + localStart + " to " + current
480    + input.getAbsolutePosition()));
481  0 break;
482    }
483  16701 if (token.equals(item)) {
484  1898 break;
485    }
486  14803 buffer.append(item);
487    } while (true);
488  1898 return input.getSubTextInput(localStart, current);
489    }
490   
491    /**
492    * Read next token from input stream.
493    *
494    * @return Read token.
495    */
 
496  303419 toggle protected final String readToken() {
497  303419 final String method = "readToken()";
498  303419 Trace.begin(CLASS, this, method);
499  303419 tokenBegin = input.getAbsolutePosition();
500  303419 StringBuffer token = new StringBuffer();
501  303419 try {
502  303419 do {
503  492000 if (eof()) {
504  0 if (token.length() <= 0) {
505  0 token = null;
506    }
507  0 break;
508    }
509  492000 final char c = (char) getChar();
510  492000 if (Character.isDigit(c)) {
511  2302 token.append((char) read());
512  2302 if (Character.isDigit((char) getChar())) {
513  695 continue;
514    }
515  1607 break;
516    }
517  489698 if (Character.isLetter(c)) {
518  248076 token.append((char) read());
519  248076 if (Character.isLetter((char) getChar())) {
520  187759 continue;
521    }
522  60317 break;
523    }
524  241622 if (SPECIALCHARACTERS.indexOf(c) >= 0) {
525  69831 switch (c) {
526  1558 case '&':
527  4144 case '{':
528  3869 case '}':
529  730 case '~':
530  1829 case '_':
531  198 case '^':
532  12328 token.append((char) read());
533  12328 break;
534  5858 case '$':
535  395 case '\'':
536  250 case '`':
537  258 case '-':
538  6761 token.append((char) read());
539  6761 if (c == getChar()) {
540  117 continue;
541    }
542  6644 break;
543  10 case '%':
544  10 token.append((char) read());
545  10 if (c == getChar()) {
546    // we must skip till end of line
547  10 token.append(readln());
548    // System.out.println("skipping comment:");
549    // System.out.println(token);
550  10 token.setLength(0);
551  10 continue;
552    }
553  0 break;
554  33901 case '\\':
555  33901 if (' ' == getChar()) {
556  0 token.append("\\");
557  0 token.append((char) read());
558  0 break;
559    }
560  33901 final String t = readBackslashToken();
561  33901 token.append(t);
562  33901 break;
563  16831 default:
564  16831 read();
565  16831 token.append(c);
566    }
567  69704 break;
568    }
569  171791 token.append((char) read());
570  171791 if ('_' == getChar() || '^' == getChar()) {
571  0 token.append((char) read());
572  0 continue;
573    }
574  171791 break;
575  188581 } while (!eof());
576  303419 Trace.param(CLASS, this, method, "Read token", token);
577    // System.out.println("< " + token);
578  303419 tokenEnd = input.getAbsolutePosition();
579  303419 return (token != null ? token.toString() : null);
580    } finally {
581  303419 Trace.end(CLASS, this, method);
582    }
583    }
584   
585    /**
586    * Get token that starts with a backlash.
587    *
588    * @return Token with backslash.
589    */
 
590  33901 toggle private String readBackslashToken() {
591  33901 final String method = "readBackslashToken()";
592  33901 Trace.begin(CLASS, this, method);
593  33901 if (getChar() != '\\') {
594  0 throw new IllegalArgumentException("\\ expected");
595    }
596  33901 read(); // read \
597  33901 if (eof()) {
598  0 Trace.param(CLASS, this, method, "return", null);
599  0 Trace.end(CLASS, this, method);
600  0 return null;
601    }
602  33901 if (!Character.isLetter((char) getChar())) {
603  15360 Trace.param(CLASS, this, method, "return", (char) getChar());
604  15360 Trace.end(CLASS, this, method);
605  15360 return "\\" + ((char) read());
606    }
607  18541 final StringBuffer buffer = new StringBuffer("\\");
608  18541 do {
609  108887 buffer.append((char) read());
610  108887 } while (!eof() && (Character.isLetter((char) getChar()) || '*' == (char) getChar()));
611  18541 Trace.param(CLASS, this, method, "return", buffer.toString());
612  18541 Trace.end(CLASS, this, method);
613  18541 return buffer.toString();
614    }
615   
616    /**
617    * Read contents that is within { .. }.
618    *
619    * @return Contents.
620    */
 
621  2821 toggle private SubTextInput readCurlyBraceContents() {
622  2821 final int localStart = input.getAbsolutePosition();
623  2821 final String first = readToken();
624  2821 if (!"{".equals(first)) {
625  0 addWarning(LatexErrorCodes.BRACKET_START_NOT_FOUND_CODE,
626    LatexErrorCodes.BRACKET_START_NOT_FOUND_TEXT,
627    localStart, input.getAbsolutePosition());
628  0 throw new IllegalArgumentException("\"{\" expected, but was: \"" + first + "\"");
629    }
630  2821 final int curlyStart = input.getAbsolutePosition();
631  2821 int curlyEnd = curlyStart;
632  2821 final StringBuffer buffer = new StringBuffer();
633  2821 String next = "";
634  2821 int level = 1;
635  18726 while (level > 0 && getChar() != TextInput.EOF) {
636  18724 next = readToken();
637  18724 if ("{".equals(next)) {
638  175 level++;
639  18549 } else if ("}".equals(next)) {
640  2994 level--;
641    }
642  18724 if (level <= 0) {
643  2819 break;
644    }
645  15905 buffer.append(next);
646  15905 curlyEnd = input.getAbsolutePosition();
647    }
648  2821 if (!"}".equals(next)) {
649  2 addWarning(LatexErrorCodes.BRACKET_END_NOT_FOUND_CODE,
650    LatexErrorCodes.BRACKET_END_NOT_FOUND_TEXT,
651    localStart, input.getAbsolutePosition());
652  2 buffer.setLength(0);
653  2 input.setAbsolutePosition(curlyStart);
654  2 curlyEnd = curlyStart;
655    }
656  2821 return input.getSubTextInput(curlyStart, curlyEnd);
657    }
658   
659    /**
660    * Print <code>token</code> to output stream.
661    *
662    * @param token Print this for UTF-8.
663    */
 
664  126489 toggle private final void print(final String token) {
665    // System.out.println("> " + token);
666  126489 if (token.trim().length() == 0) {
667  40411 if (skipWhitespace) {
668  1441 return;
669    }
670    }
671  125048 skipWhitespace = false;
672  125048 if (token.equals("\\par")) {
673  343 println();
674  343 println();
675  343 skipWhitespace = true;
676  124705 } else if (token.equals("\\\\")) {
677  315 println();
678  124390 } else if (token.equals("&")) {
679  755 output.addWs(" ");
680  123635 } else if (token.equals("\\-")) {
681    // ignore
682  123633 } else if (token.equals("--")) {
683  3 output.addToken("\u2012");
684  123630 } else if (token.equals("`")) {
685  112 output.addWs("\u2018");
686  123518 } else if (token.equals("'")) {
687  174 output.addToken("\u2019");
688  123344 } else if (token.equals("\\neq")) {
689  56 output.addToken("\u2260");
690  123288 } else if (token.equals("\\in")) {
691  490 output.addToken("\u2208");
692  122798 } else if (token.equals("\\forall")) {
693  472 output.addToken("\u2200");
694  122326 } else if (token.equals("\\exists")) {
695  248 output.addToken("\u2203");
696  122078 } else if (token.equals("\\emptyset")) {
697  87 output.addToken("\u2205");
698  121991 } else if (token.equals("\\rightarrow")) {
699  3248 output.addToken("\u2192");
700  118743 } else if (token.equals("\\Rightarrow")) {
701  6 output.addToken("\u21D2");
702  118737 } else if (token.equals("\\leftrightarrow")) {
703  474 output.addToken("\u2194");
704  118263 } else if (token.equals("\\Leftarrow")) {
705  4 output.addToken("\u21D0");
706  118259 } else if (token.equals("\\Leftrightarrow")) {
707  0 output.addToken("\u21D4");
708  118259 } else if (token.equals("\\langle")) {
709  22 output.addToken("\u2329");
710  118237 } else if (token.equals("\\rangle")) {
711  22 output.addToken("\u232A");
712  118215 } else if (token.equals("\\land") || token.equals("\\vee")) {
713  915 output.addToken("\u2227");
714  117300 } else if (token.equals("\\lor") || token.equals("\\wedge")) {
715  1522 output.addToken("\u2228");
716  115778 } else if (token.equals("\\bar")) {
717  7 output.addToken("\u203E");
718  115771 } else if (token.equals("\\bigcap")) {
719  30 output.addToken("\u22C2");
720  115741 } else if (token.equals("\\cap")) {
721  88 output.addToken("\u2229");
722  115653 } else if (token.equals("\\bigcup")) {
723  44 output.addToken("\u22C3");
724  115609 } else if (token.equals("\\cup")) {
725  86 output.addToken("\u222A");
726  115523 } else if (token.equals("\\in")) {
727  0 output.addToken("\u2208");
728  115523 } else if (token.equals("\\notin")) {
729  50 output.addToken("\u2209");
730  115473 } else if (token.equals("\\Alpha")) {
731  0 output.addToken("\u0391");
732  115473 } else if (token.equals("\\alpha")) {
733  320 output.addToken("\u03B1");
734  115153 } else if (token.equals("\\Beta")) {
735  0 output.addToken("\u0392");
736  115153 } else if (token.equals("\\beta")) {
737  209 output.addToken("\u03B2");
738  114944 } else if (token.equals("\\Gamma")) {
739  6 output.addToken("\u0393");
740  114938 } else if (token.equals("\\gamma")) {
741  13 output.addToken("\u03B3");
742  114925 } else if (token.equals("\\Delta")) {
743  0 output.addToken("\u0394");
744  114925 } else if (token.equals("\\delta")) {
745  6 output.addToken("\u03B4");
746  114919 } else if (token.equals("\\Epslilon")) {
747  0 output.addToken("\u0395");
748  114919 } else if (token.equals("\\epsilon")) {
749  0 output.addToken("\u03B5");
750  114919 } else if (token.equals("\\Zeta")) {
751  0 output.addToken("\u0396");
752  114919 } else if (token.equals("\\zeta")) {
753  0 output.addToken("\u03B6");
754  114919 } else if (token.equals("\\Eta")) {
755  0 output.addToken("\u0397");
756  114919 } else if (token.equals("\\eta")) {
757  0 output.addToken("\u03B7");
758  114919 } else if (token.equals("\\Theta")) {
759  0 output.addToken("\u0398");
760  114919 } else if (token.equals("\\theta")) {
761  0 output.addToken("\u03B8");
762  114919 } else if (token.equals("\\Iota")) {
763  0 output.addToken("\u0399");
764  114919 } else if (token.equals("\\iota")) {
765  0 output.addToken("\u03B9");
766  114919 } else if (token.equals("\\Kappa")) {
767  0 output.addToken("\u039A");
768  114919 } else if (token.equals("\\kappa")) {
769  0 output.addToken("\u03BA");
770  114919 } else if (token.equals("\\Lamda")) {
771  0 output.addToken("\u039B");
772  114919 } else if (token.equals("\\lamda")) {
773  0 output.addToken("\u03BB");
774  114919 } else if (token.equals("\\Mu")) {
775  0 output.addToken("\u039C");
776  114919 } else if (token.equals("\\mu")) {
777  0 output.addToken("\u03BC");
778  114919 } else if (token.equals("\\Nu")) {
779  0 output.addToken("\u039D");
780  114919 } else if (token.equals("\\nu")) {
781  0 output.addToken("\u03BD");
782  114919 } else if (token.equals("\\Xi")) {
783  0 output.addToken("\u039E");
784  114919 } else if (token.equals("\\xi")) {
785  0 output.addToken("\u03BE");
786  114919 } else if (token.equals("\\Omikron")) {
787  0 output.addToken("\u039F");
788  114919 } else if (token.equals("\\omikron")) {
789  0 output.addToken("\u03BF");
790  114919 } else if (token.equals("\\Pi")) {
791  0 output.addToken("\u03A0");
792  114919 } else if (token.equals("\\pi")) {
793  0 output.addToken("\u03C0");
794  114919 } else if (token.equals("\\Rho")) {
795  0 output.addToken("\u03A1");
796  114919 } else if (token.equals("\\rho")) {
797  0 output.addToken("\u03C1");
798  114919 } else if (token.equals("\\Sigma")) {
799  0 output.addToken("\u03A3");
800  114919 } else if (token.equals("\\sigma")) {
801  23 output.addToken("\u03C3");
802  114896 } else if (token.equals("\\Tau")) {
803  0 output.addToken("\u03A4");
804  114896 } else if (token.equals("\\tau")) {
805  37 output.addToken("\u03C4");
806  114859 } else if (token.equals("\\Upsilon")) {
807  0 output.addToken("\u03A5");
808  114859 } else if (token.equals("\\upsilon")) {
809  0 output.addToken("\u03C5");
810  114859 } else if (token.equals("\\Phi")) {
811  0 output.addToken("\u03A6");
812  114859 } else if (token.equals("\\phi")) {
813  557 output.addToken("\u03C6");
814  114302 } else if (token.equals("\\Chi")) {
815  0 output.addToken("\u03A6");
816  114302 } else if (token.equals("\\chi")) {
817  0 output.addToken("\u03C7");
818  114302 } else if (token.equals("\\Psi")) {
819  0 output.addToken("\u03A8");
820  114302 } else if (token.equals("\\psi")) {
821  86 output.addToken("\u03C8");
822  114216 } else if (token.equals("\\Omega")) {
823  0 output.addToken("\u03A9");
824  114216 } else if (token.equals("\\omega")) {
825  36 output.addToken("\u03C9");
826  114180 } else if (token.equals("\\subset")) {
827  0 output.addToken("\u2282");
828  114180 } else if (token.equals("\\supset")) {
829  4 output.addToken("\u2283");
830  114176 } else if (token.equals("\\subseteq")) {
831  111 output.addToken("\u2286");
832  114065 } else if (token.equals("\\supseteq")) {
833  0 output.addToken("\u2287");
834  114065 } else if (token.equals("\\{")) {
835  281 output.addToken("{");
836  113784 } else if (token.equals("\\}")) {
837  281 output.addToken("}");
838  113503 } else if (token.equals("\\&")) {
839  2 output.addToken("&");
840  113501 } else if (token.equals("\\ ")) {
841  12873 output.addWs(" ");
842  100628 } else if (token.equals("\\S")) {
843  6 output.addToken("\u00A7");
844  100622 } else if (token.equals("\\tt")) {
845    // ignore
846  100579 } else if (token.equals("\\tiny")) {
847    // ignore
848  100569 } else if (token.equals("\\nonumber")) {
849    // ignore
850  100567 } else if (token.equals("\\LaTeX")) {
851  43 output.addToken("LaTeX");
852  100524 } else if (token.equals("\\vdash")) {
853  6 output.addToken("\u22A2");
854  100518 } else if (token.equals("\\dashv")) {
855  0 output.addToken("\u22A3");
856  100518 } else if (token.equals("\\times")) {
857  4 output.addToken("\u00D7");
858  100514 } else if (token.equals("~")) {
859  356 output.addToken("\u00A0");
860  100158 } else if (token.equals("\\quad")) {
861    // output.addWs("\u2000");
862  3 output.addWs(" ");
863  100155 } else if (token.equals("\\qquad")) {
864    // output.addWs("\u2000\u2000");
865  10 output.addWs(" ");
866  100145 } else if (token.equals("\\,")) {
867    // output.addWs("\u2009");
868  4 output.addWs(" ");
869  100141 } else if (token.equals("\\neg") || token.equals("\\not")) {
870  489 output.addToken("\u00AC");
871  99652 } else if (token.equals("\\bot")) {
872  22 output.addToken("\u22A5");
873  99630 } else if (token.equals("\\top")) {
874  44 output.addToken("\u22A4");
875  99586 } else if (token.equals("''") || token.equals("\\grqq")) {
876  63 output.addToken("\u201D");
877  99523 } else if (token.equals("``") || token.equals("\\glqq")) {
878  65 skipWhitespace = true;
879  65 output.addToken("\u201E");
880  99458 } else if (token.equals("\\ldots")) {
881  238 output.addToken("...");
882  99220 } else if (token.equals("\\overline")) { // TODO 20101018 m31: we assume set complement
883  76 output.addToken("\u2201");
884  99144 } else if (token.startsWith("\\")) {
885  30 addWarning(LatexErrorCodes.COMMAND_NOT_SUPPORTED_CODE,
886    LatexErrorCodes.COMMAND_NOT_SUPPORTED_TEXT + token, tokenBegin, tokenEnd);
887    } else {
888  99114 if (mathfrak) {
889  506 mathfrak(token);
890  98608 } else if (mathbb) {
891  2 mathbb(token);
892  98606 } else if (emph) {
893  1336 emph(token);
894  97270 } else if (bold) {
895  124 bold(token);
896    } else {
897  97146 if (isWs(token)) {
898  38701 output.addWs(token);
899    } else {
900  58445 output.addToken(token);
901    }
902    }
903    }
904    }
905   
906    /**
907    * Write token chars in mathbb mode.
908    *
909    * @param token Chars to write.
910    */
 
911  1336 toggle private void emph(final String token) {
912  1336 if (isWs(token)) {
913  257 output.addWs(Latex2UnicodeSpecials.transform2Emph(token));
914    } else {
915  1079 output.addToken(Latex2UnicodeSpecials.transform2Emph(token));
916    }
917    }
918   
919    /**
920    * Write token chars in mathbb mode.
921    *
922    * @param token Chars to write.
923    */
 
924  2 toggle private void mathbb(final String token) {
925  4 for (int i = 0; i < token.length(); i++) {
926  2 final char c = token.charAt(i);
927  2 switch (c) {
928  0 case 'C': output.addToken("\u2102");
929  0 break;
930  0 case 'H': output.addToken("\u210D");
931  0 break;
932  2 case 'N': output.addToken("\u2115");
933  2 break;
934  0 case 'P': output.addToken("\u2119");
935  0 break;
936  0 case 'Q': output.addToken("\u211A");
937  0 break;
938  0 case 'R': output.addToken("\u211D");
939  0 break;
940  0 case 'Z': output.addToken("\u2124");
941  0 break;
942  0 default:
943  0 if (Character.isWhitespace(c)) {
944  0 output.addWs("" + c);
945    } else {
946  0 output.addToken("" + c);
947    }
948    }
949    }
950    }
951   
 
952  99112 toggle private boolean isWs(final String token) {
953  99112 return token == null || token.trim().length() == 0;
954    }
955   
956    /**
957    * Write token chars in mathfrak mode.
958    *
959    * @param token Chars to write.
960    */
 
961  506 toggle private void mathfrak(final String token) {
962  506 if (isWs(token)) {
963  0 output.addWs(Latex2UnicodeSpecials.transform2Mathfrak(token));
964    } else {
965  506 output.addToken(Latex2UnicodeSpecials.transform2Mathfrak(token));
966    }
967    }
968   
969    /**
970    * Write token in bold mode.
971    *
972    * @param token Chars to write.
973    */
 
974  124 toggle private void bold(final String token) {
975  124 if (isWs(token)) {
976  12 output.addWs(Latex2UnicodeSpecials.transform2Bold(token));
977    } else {
978  112 output.addToken(Latex2UnicodeSpecials.transform2Bold(token));
979    }
980    }
981   
982    /**
983    * Print end of line.
984    */
 
985  1461 toggle private final void println() {
986  1461 output.println();
987    }
988   
989    /**
990    * Reads a single character and does not change the reading
991    * position.
992    *
993    * @return character read, if there are no more chars
994    * <code>-1</code> is returned
995    */
 
996  1444596 toggle protected final int getChar() {
997  1444596 return input.getChar();
998    }
999   
1000    /**
1001    * Reads a single character and increments the reading position
1002    * by one.
1003    *
1004    * @return character read, if there are no more chars
1005    * <code>-1</code> is returned
1006    */
 
1007  616801 toggle protected final int read() {
1008  616801 return input.read();
1009    }
1010   
1011    /**
1012    * Read until end of line.
1013    *
1014    * @return Characters read.
1015    */
 
1016  10 toggle protected final String readln() {
1017  10 StringBuffer result = new StringBuffer();
1018  10 int c;
1019  ? while (TextInput.EOF != (c = read())) {
1020  554 if (c == '\n') {
1021  10 break;
1022    }
1023  544 result.append((char) c);
1024    }
1025  10 return result.toString();
1026    }
1027   
1028    /**
1029    * Are there still any characters to read?
1030    *
1031    * @return Anything left for reading further?
1032    */
 
1033  1050231 toggle public final boolean eof() {
1034  1050231 return input.isEmpty();
1035    }
1036   
1037    /**
1038    * Convert character position into row and column information.
1039    *
1040    * @param absolutePosition Find this character position.
1041    * @return Row and column information.
1042    */
 
1043  540 toggle public SourcePosition getAbsoluteSourcePosition(final int absolutePosition) {
1044  540 return ((SubTextInput) inputStack.get(0)).getPosition(absolutePosition);
1045    }
1046   
1047    /**
1048    * Add warning message.
1049    *
1050    * @param code Message code.
1051    * @param message Message.
1052    * @param from Absolute character position of problem start.
1053    * @param to Absolute character position of problem end.
1054    */
 
1055  34 toggle private void addWarning(final int code, final String message, final int from, final int to) {
1056  34 finder.addWarning(code, message, getAbsoluteSourcePosition(from),
1057    getAbsoluteSourcePosition(to));
1058    }
1059   
1060   
1061   
1062    }