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 All rights reserved.
8 
9 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10 
11     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12     * 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.
13     * 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.
14 
15 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.
16 */
17 
18 #include <stdexcept>
19 #include "ParseTree.h"
20 
21 using namespace std;
22 
23 namespace blahtex
24 {
25 
26 // This is a list of delimiters which may appear after "\left", "\right"
27 // and "\big", and of which MathML characters they get mapped to.
28 
29 static pair<wstring, wstring> gDelimiterArray[] =
30 {
31     make_pair(L".",             L""),
32     make_pair(L"[",             L"["),
33     make_pair(L"]",             L"]"),
34     make_pair(L"\\lbrack",      L"["),
35     make_pair(L"\\rbrack",      L"]"),
36     make_pair(L"(",             L"("),
37     make_pair(L")",             L")"),
38     make_pair(L"<",             L"\U00002329"),
39     make_pair(L">",             L"\U0000232A"),
40     make_pair(L"\\langle",      L"\U00002329"),
41     make_pair(L"\\rangle",      L"\U0000232A"),
42     make_pair(L"/",             L"/"),
43     make_pair(L"\\backslash",   L"\U00002216"),
44     make_pair(L"\\{",           L"{"),
45     make_pair(L"\\}",           L"}"),
46     make_pair(L"\\lbrace",      L"{"),
47     make_pair(L"\\rbrace",      L"}"),
48     make_pair(L"|",             L"|"),
49     make_pair(L"\\vert",        L"|"),
50     make_pair(L"\\lvert",       L"|"),
51     make_pair(L"\\rvert",       L"|"),
52     make_pair(L"\\Vert",        L"\U00002225"),
53     make_pair(L"\\lVert",       L"\U00002225"),
54     make_pair(L"\\rVert",       L"\U00002225"),
55     make_pair(L"\\uparrow",     L"\U00002191"),
56     make_pair(L"\\downarrow",   L"\U00002193"),
57     make_pair(L"\\updownarrow", L"\U00002195"),
58     make_pair(L"\\Uparrow",     L"\U000021D1"),
59     make_pair(L"\\Downarrow",   L"\U000021D3"),
60     make_pair(L"\\Updownarrow", L"\U000021D5"),
61     make_pair(L"\\lfloor",      L"\U0000230A"),
62     make_pair(L"\\rfloor",      L"\U0000230B"),
63     make_pair(L"\\lceil",       L"\U00002308"),
64     make_pair(L"\\rceil",       L"\U00002309")
65 };
66 
67 wishful_hash_map<wstring, wstring> gDelimiterTable(
68     gDelimiterArray,
69     END_ARRAY(gDelimiterArray)
70 );
71 
72 
73 namespace ParseTree
74 {
75 
76 
BuildLayoutTree(const TexProcessingState & state) const77 auto_ptr<LayoutTree::Node> MathList::BuildLayoutTree(
78     const TexProcessingState& state
79 ) const
80 {
81     auto_ptr<LayoutTree::Row> output(
82         new LayoutTree::Row(state.mStyle, state.mColour)
83     );
84     list<LayoutTree::Node*>& targetList = output->mChildren;
85 
86 
87     // 1st pass: recursively build layout trees for all children in
88     // this row, and process state changes
89     TexProcessingState currentState = state;
90     for (vector<MathNode*>::const_iterator
91         node = mChildren.begin(); node != mChildren.end(); node++
92     )
93     {
94         MathStateChange* nodeAsStateChange =
95             dynamic_cast<MathStateChange*>(*node);
96 
97         if (nodeAsStateChange)
98             nodeAsStateChange->Apply(currentState);
99         else
100             targetList.push_back(
101                 (*node)->BuildLayoutTree(currentState).release()
102             );
103     }
104 
105 
106     // 2nd pass: modify atom flavours according to TeX's rules.
107     for (list<LayoutTree::Node*>::iterator
108         node = targetList.begin(); node != targetList.end(); node++
109     )
110     {
111         switch ((*node)->mFlavour)
112         {
113             case LayoutTree::Node::cFlavourBin:
114             {
115                 if (node == targetList.begin())
116                     (*node)->mFlavour = LayoutTree::Node::cFlavourOrd;
117                 else
118                 {
119                     list<LayoutTree::Node*>::iterator previous = node;
120                     previous--;
121                     switch ((*previous)->mFlavour)
122                     {
123                         case LayoutTree::Node::cFlavourBin:
124                         case LayoutTree::Node::cFlavourOp:
125                         case LayoutTree::Node::cFlavourRel:
126                         case LayoutTree::Node::cFlavourOpen:
127                         case LayoutTree::Node::cFlavourPunct:
128                             (*node)->mFlavour =
129                                 LayoutTree::Node::cFlavourOrd;
130                             break;
131                     }
132                 }
133                 break;
134             }
135 
136             case LayoutTree::Node::cFlavourRel:
137             case LayoutTree::Node::cFlavourClose:
138             case LayoutTree::Node::cFlavourPunct:
139             {
140                 if (node != targetList.begin())
141                 {
142                     list<LayoutTree::Node*>::iterator previous = node;
143                     previous--;
144                     if ((*previous)->mFlavour ==
145                             LayoutTree::Node::cFlavourBin
146                     )
147                         (*previous)->mFlavour =
148                             LayoutTree::Node::cFlavourOrd;
149                 }
150 
151                 break;
152             }
153         }
154     }
155     if (!targetList.empty() &&
156         targetList.back()->mFlavour == LayoutTree::Node::cFlavourBin
157     )
158         targetList.back()->mFlavour = LayoutTree::Node::cFlavourOrd;
159 
160 
161     // 3rd pass: insert inter-atomic spacing according to TeX's rules.
162 
163     // spaceTable[i][j] gives the amount of space that should be inserted
164     // between nodes of flavour i and flavour j.
165 
166     // ignoreSpaceTable[i][j] is nonzero whenever the space between i and j
167     // should be ignored while in script or scriptscript style.
168 
169     static int spaceTable[8][8] =
170     {
171     //                     RIGHT
172     // ord   op    bin   rel   open  close punct inner
173        {0,    3,    4,    5,    0,    0,    0,    3},    // ord
174        {3,    3,    0,    5,    0,    0,    0,    3},    // op
175        {4,    4,    0,    0,    4,    0,    0,    4},    // bin
176        {5,    5,    0,    0,    5,    0,    0,    5},    // rel
177        {0,    0,    0,    0,    0,    0,    0,    0},    // open     // LEFT
178        {0,    3,    4,    5,    0,    0,    0,    3},    // close
179        {3,    3,    0,    3,    3,    3,    3,    3},    // punct
180        {3,    3,    4,    5,    3,    0,    3,    3}     // inner
181     };
182 
183     static int ignoreSpaceTable[8][8] =
184     {
185     //                     RIGHT
186     // ord   op    bin   rel   open  close punct inner
187        {0,    0,    1,    1,    0,    0,    0,    1},    // ord
188        {0,    0,    0,    1,    0,    0,    0,    1},    // op
189        {1,    1,    0,    0,    1,    0,    0,    1},    // bin
190        {1,    1,    0,    0,    1,    0,    0,    1},    // rel
191        {0,    0,    0,    0,    0,    0,    0,    0},    // open     // LEFT
192        {0,    0,    1,    1,    0,    0,    0,    1},    // close
193        {1,    1,    0,    1,    1,    1,    1,    1},    // punct
194        {1,    0,    1,    1,    1,    0,    1,    1}     // inner
195     };
196 
197     list<LayoutTree::Node*>::iterator currentAtom = targetList.begin();
198     list<LayoutTree::Node*>::iterator previousAtom;
199     bool foundFirst = false;
200     while (true)
201     {
202         while (
203             currentAtom != targetList.end() &&
204             dynamic_cast<LayoutTree::Space*>(*currentAtom)
205         )
206             currentAtom++;
207 
208         if (currentAtom == targetList.end())
209             break;
210 
211         if (!foundFirst)
212             foundFirst = true;
213         else
214         {
215             LayoutTree::Node::Flavour leftFlavour =
216                 (*previousAtom)->mFlavour;
217             LayoutTree::Node::Flavour rightFlavour =
218                 (*currentAtom)->mFlavour;
219 
220             int width =
221             (
222                 ignoreSpaceTable[leftFlavour][rightFlavour] &&
223                     (
224                         state.mStyle ==
225                             LayoutTree::Node::cStyleScript
226                         ||
227                         state.mStyle ==
228                             LayoutTree::Node::cStyleScriptScript
229                     )
230             )
231                 ? 0 : spaceTable[leftFlavour][rightFlavour];
232 
233             targetList.insert(
234                 currentAtom,
235                 new LayoutTree::Space(
236                     width,
237                     false       // indicates non-user-specified space
238                 )
239             );
240         }
241 
242         previousAtom = currentAtom;
243         currentAtom++;
244     }
245 
246 
247     // 4th pass: splice any children Rows into this Row.
248     // The idea is that no Row node should have any Rows as children.
249     for (list<LayoutTree::Node*>::iterator
250         child = targetList.begin(); child != targetList.end(); child++
251     )
252     {
253         LayoutTree::Row* childAsRow
254             = dynamic_cast<LayoutTree::Row*>(*child);
255 
256         if (childAsRow)
257         {
258             targetList.splice(child, childAsRow->mChildren);
259             delete childAsRow;
260             child = targetList.erase(child);
261         }
262     }
263 
264     return static_cast< auto_ptr<LayoutTree::Node> >(output);
265 }
266 
267 
268 // Stores info about accent commands (like "\hat", "\overrightarrow", etc)
269 struct AccentInfo {
270     wstring mText;
271     bool mIsStretchy;
272 
AccentInfoblahtex::ParseTree::AccentInfo273     AccentInfo(
274         const wstring& text,
275         bool isStretchy
276     ) :
277         mText(text),
278         mIsStretchy(isStretchy)
279     { }
280 };
281 
282 
BuildLayoutTree(const TexProcessingState & state) const283 auto_ptr<LayoutTree::Node> MathCommand1Arg::BuildLayoutTree(
284     const TexProcessingState& state
285 ) const
286 {
287     if (mCommand == L"\\sqrt")
288         return auto_ptr<LayoutTree::Node>(
289             new LayoutTree::Sqrt(
290                 mChild->BuildLayoutTree(state),
291                 state.mColour
292             )
293         );
294 
295     if (mCommand == L"\\overbrace" || mCommand == L"\\underbrace")
296     {
297         auto_ptr<LayoutTree::Node> brace(
298             new LayoutTree::SymbolOperator(
299                 true,
300                 L"",
301                 false,
302                 mCommand == L"\\overbrace" ? L"\U0000FE37" : L"\U0000FE38",
303                 cMathmlFontNormal,
304                 LayoutTree::Node::cStyleScript,
305                 LayoutTree::Node::cFlavourOrd,
306                 LayoutTree::Node::cLimitsDisplayLimits,
307                 state.mColour
308             )
309         );
310 
311         TexProcessingState newState = state;
312         newState.mStyle =
313             (state.mStyle == LayoutTree::Node::cStyleDisplay)
314                 ? LayoutTree::Node::cStyleDisplay
315                 : LayoutTree::Node::cStyleText;
316 
317         auto_ptr<LayoutTree::Node> empty;
318 
319         return auto_ptr<LayoutTree::Node>(
320             new LayoutTree::Scripts(
321                 newState.mStyle,
322                 LayoutTree::Node::cFlavourOp,
323                 LayoutTree::Node::cLimitsLimits,
324                 state.mColour,
325                 false,
326                 mChild->BuildLayoutTree(newState),
327                 (mCommand == L"\\overbrace")  ? brace : empty,
328                 (mCommand == L"\\underbrace") ? brace : empty
329             )
330         );
331     }
332 
333     if (mCommand == L"\\pmod")
334     {
335         auto_ptr<LayoutTree::Row> row(
336             new LayoutTree::Row(state.mStyle, state.mColour)
337         );
338 
339         MathmlFont font =
340             state.mMathFont.mIsBoldsymbol
341                 ? cMathmlFontBold : cMathmlFontNormal;
342 
343         row->mChildren.push_back(new LayoutTree::Space(18, true));
344         row->mChildren.push_back(
345             new LayoutTree::SymbolOperator(
346                 false,
347                 L"",
348                 false,
349                 L"(",
350                 font,
351                 state.mStyle,
352                 LayoutTree::Node::cFlavourOpen,
353                 LayoutTree::Node::cLimitsDisplayLimits,
354                 state.mColour
355             )
356         );
357         row->mChildren.push_back(
358             new LayoutTree::SymbolOperator(
359                 false,
360                 L"",
361                 false,
362                 L"mod",
363                 font,
364                 state.mStyle,
365                 LayoutTree::Node::cFlavourOrd,
366                 LayoutTree::Node::cLimitsDisplayLimits,
367                 state.mColour
368             )
369         );
370         row->mChildren.push_back(new LayoutTree::Space(6, true));
371         row->mChildren.push_back(
372             mChild->BuildLayoutTree(state).release()
373         );
374         row->mChildren.push_back(
375             new LayoutTree::SymbolOperator(
376                 false,
377                 L"",
378                 false,
379                 L")",
380                 font,
381                 state.mStyle,
382                 LayoutTree::Node::cFlavourClose,
383                 LayoutTree::Node::cLimitsDisplayLimits,
384                 state.mColour
385             )
386         );
387 
388         return static_cast<auto_ptr<LayoutTree::Node> >(row);
389     }
390 
391     if (mCommand == L"\\operatorname" ||
392         mCommand == L"\\operatornamewithlimits"
393     )
394     {
395         // Essentially this just writes the argument in upright font and
396         // sets limits correctly. So initially it looks like
397         // <mi mathvariant="normal">s</mi>
398         // <mi mathvariant="normal">i</mi>
399         // <mi mathvariant="normal">n</mi>
400         // But then these get merged later on, to produce the more
401         // reasonable <mi>sin</mi>.
402 
403         TexProcessingState newState = state;
404         newState.mMathFont.mFamily = TexMathFont::cFamilyRm;
405         auto_ptr<LayoutTree::Node> node
406             = mChild->BuildLayoutTree(newState);
407         node->mFlavour = LayoutTree::Node::cFlavourOp;
408         node->mLimits =
409             (mCommand == L"\\operatorname")
410                 ? LayoutTree::Node::cLimitsNoLimits
411                 : LayoutTree::Node::cLimitsDisplayLimits;
412         return node;
413     }
414 
415 
416     static pair<wstring, LayoutTree::Node::Flavour> flavourCommandArray[] =
417     {
418         make_pair(L"\\mathop",        LayoutTree::Node::cFlavourOp),
419         make_pair(L"\\mathrel",       LayoutTree::Node::cFlavourRel),
420         make_pair(L"\\mathbin",       LayoutTree::Node::cFlavourBin),
421         make_pair(L"\\mathord",       LayoutTree::Node::cFlavourOrd),
422         make_pair(L"\\mathopen",      LayoutTree::Node::cFlavourOpen),
423         make_pair(L"\\mathclose",     LayoutTree::Node::cFlavourClose),
424         make_pair(L"\\mathpunct",     LayoutTree::Node::cFlavourPunct),
425         make_pair(L"\\mathinner",     LayoutTree::Node::cFlavourInner)
426     };
427     static wishful_hash_map<wstring, LayoutTree::Node::Flavour>
428         flavourCommandTable(
429             flavourCommandArray,
430             END_ARRAY(flavourCommandArray)
431         );
432 
433     wishful_hash_map<wstring, LayoutTree::Node::Flavour>::const_iterator
434         flavourCommand = flavourCommandTable.find(mCommand);
435     if (flavourCommand != flavourCommandTable.end())
436     {
437         auto_ptr<LayoutTree::Node> node
438             = mChild->BuildLayoutTree(state);
439         node->mFlavour = flavourCommand->second;
440         if (node->mFlavour == LayoutTree::Node::cFlavourOp)
441             node->mLimits = LayoutTree::Node::cLimitsDisplayLimits;
442         return node;
443     }
444 
445     static pair<wstring, TexMathFont::Family> fontCommandArray[] =
446     {
447         make_pair(L"\\mathbf",         TexMathFont::cFamilyBf),
448         make_pair(L"\\mathbb",         TexMathFont::cFamilyBb),
449         make_pair(L"\\mathit",         TexMathFont::cFamilyIt),
450         make_pair(L"\\mathrm",         TexMathFont::cFamilyRm),
451         make_pair(L"\\mathsf",         TexMathFont::cFamilySf),
452         make_pair(L"\\mathtt",         TexMathFont::cFamilyTt),
453         make_pair(L"\\mathcal",        TexMathFont::cFamilyCal),
454         make_pair(L"\\mathfrak",       TexMathFont::cFamilyFrak)
455     };
456     static wishful_hash_map<wstring, TexMathFont::Family> fontCommandTable(
457         fontCommandArray,
458         END_ARRAY(fontCommandArray)
459     );
460 
461     wishful_hash_map<wstring, TexMathFont::Family>::const_iterator
462         fontCommand = fontCommandTable.find(mCommand);
463     if (fontCommand != fontCommandTable.end())
464     {
465         TexProcessingState newState = state;
466         newState.mMathFont.mFamily = fontCommand->second;
467         return mChild->BuildLayoutTree(newState);
468     }
469 
470     if (mCommand == L"\\boldsymbol")
471     {
472         TexProcessingState newState = state;
473         newState.mMathFont.mIsBoldsymbol = true;
474         newState.mMathFont.mFamily = TexMathFont::cFamilyDefault;
475         return mChild->BuildLayoutTree(newState);
476     }
477 
478     // Here is a list of all the accent commands we know about.
479     static pair<wstring, AccentInfo> accentCommandArray[] =
480     {
481         // FIX: there's some funny inconsistency between the definition of
482         // &Hat; among MathML versions. I was originally using plain "^" for
483         // these accents, but Roger recommended using 0x302 instead.
484         make_pair(L"\\hat",                  AccentInfo(L"\U00000302", false)),
485         make_pair(L"\\widehat",              AccentInfo(L"\U00000302", true)),
486         make_pair(L"\\bar",                  AccentInfo(L"\U000000AF", false)),
487         make_pair(L"\\overline",             AccentInfo(L"\U000000AF", true)),
488         make_pair(L"\\underline",            AccentInfo(L"\U000000AF", true)),
489         make_pair(L"\\tilde",                AccentInfo(L"\U000002DC", false)),
490         make_pair(L"\\widetilde",            AccentInfo(L"\U000002DC", true)),
491         make_pair(L"\\overleftarrow",        AccentInfo(L"\U00002190", true)),
492         make_pair(L"\\vec",                  AccentInfo(L"\U000020D7", true)),
493         make_pair(L"\\overrightarrow",       AccentInfo(L"\U00002192", true)),
494         make_pair(L"\\overleftrightarrow",   AccentInfo(L"\U00002194", true)),
495         make_pair(L"\\dot",                  AccentInfo(L"\U000000B7", false)),
496         make_pair(L"\\ddot",                 AccentInfo(L"\U000000B7\U000000B7", false)),
497         make_pair(L"\\check",                AccentInfo(L"\U000002C7", false)),
498         make_pair(L"\\acute",                AccentInfo(L"\U000000B4", false)),
499         make_pair(L"\\grave",                AccentInfo(L"\U00000060", false)),
500         make_pair(L"\\breve",                AccentInfo(L"\U000002D8", false)
501         )
502     };
503     static wishful_hash_map<wstring, AccentInfo> accentCommandTable(
504         accentCommandArray,
505         END_ARRAY(accentCommandArray)
506     );
507 
508     wishful_hash_map<wstring, AccentInfo>::const_iterator
509         accentCommand = accentCommandTable.find(mCommand);
510     if (accentCommand != accentCommandTable.end())
511     {
512         auto_ptr<LayoutTree::Node> base
513             = mChild->BuildLayoutTree(state);
514         auto_ptr<LayoutTree::Node> lower, upper;
515 
516         auto_ptr<LayoutTree::Node> accent(
517             new LayoutTree::SymbolOperator(
518                 accentCommand->second.mIsStretchy,
519                 L"",
520                 true,       // is an accent
521                 accentCommand->second.mText,
522                 state.mMathFont.mIsBoldsymbol
523                     ? cMathmlFontBold : cMathmlFontNormal,
524                 // We don't need to decrement the style here, because
525                 // LayoutTree::SymbolOperator knows not to insert style
526                 // changes for accent operators
527                 state.mStyle,
528                 LayoutTree::Node::cFlavourOrd,
529                 LayoutTree::Node::cLimitsDisplayLimits,
530                 state.mColour
531             )
532         );
533 
534         if (mCommand == L"\\underline")
535             lower = accent;
536         else
537             upper = accent;
538 
539         return auto_ptr<LayoutTree::Node>(
540             new LayoutTree::Scripts(
541                 state.mStyle,
542                 LayoutTree::Node::cFlavourOrd,
543                 LayoutTree::Node::cLimitsDisplayLimits,
544                 state.mColour,
545                 false,      // not sideset
546                 base,
547                 upper,
548                 lower
549             )
550         );
551     }
552 
553     throw logic_error(
554         "Unexpected command in MathCommand1Arg::BuildLayoutTree"
555     );
556 }
557 
558 
BuildLayoutTree(const TexProcessingState & state) const559 auto_ptr<LayoutTree::Node> MathStateChange::BuildLayoutTree(
560     const TexProcessingState& state
561 ) const
562 {
563     // We should only arrive here if there was a state change command all
564     // by its lonesome self in its own math list, so we can safely ignore
565     // it.
566     return auto_ptr<LayoutTree::Node>(
567         new LayoutTree::Row(state.mStyle, state.mColour)
568     );
569 }
570 
BuildLayoutTree(const TexProcessingState & state) const571 auto_ptr<LayoutTree::Node> MathColour::BuildLayoutTree(
572     const TexProcessingState& state
573 ) const
574 {
575     // See above in MathStateChange::BuildLayoutTree
576     return auto_ptr<LayoutTree::Node>(
577         new LayoutTree::Row(state.mStyle, state.mColour)
578     );
579 }
580 
BuildLayoutTree(const TexProcessingState & state) const581 auto_ptr<LayoutTree::Node> TextStateChange::BuildLayoutTree(
582     const TexProcessingState& state
583 ) const
584 {
585     // See above in MathStateChange::BuildLayoutTree
586     return auto_ptr<LayoutTree::Node>(
587         new LayoutTree::Row(state.mStyle, state.mColour)
588     );
589 }
590 
BuildLayoutTree(const TexProcessingState & state) const591 auto_ptr<LayoutTree::Node> TextColour::BuildLayoutTree(
592     const TexProcessingState& state
593 ) const
594 {
595     // See above in MathStateChange::BuildLayoutTree
596     return auto_ptr<LayoutTree::Node>(
597         new LayoutTree::Row(state.mStyle, state.mColour)
598     );
599 }
600 
601 
BuildLayoutTree(const TexProcessingState & state) const602 auto_ptr<LayoutTree::Node> MathCommand2Args::BuildLayoutTree(
603     const TexProcessingState& state
604 ) const
605 {
606     bool isFractionCommand = false;
607     bool hasParentheses;
608     bool isLineVisible;
609 
610     if (mCommand == L"\\frac" || mCommand == L"\\over")
611     {
612         isFractionCommand = true;
613         isLineVisible = true;
614         hasParentheses = false;
615     }
616     else if (mCommand == L"\\atop")
617     {
618         isFractionCommand = true;
619         isLineVisible = false;
620         hasParentheses = false;
621     }
622     else if (mCommand == L"\\binom" || mCommand == L"\\choose")
623     {
624         isFractionCommand = true;
625         isLineVisible = false;
626         hasParentheses = true;
627     }
628 
629     if (isFractionCommand)
630     {
631         // Work out what style the numerator/denominator should be.
632         TexProcessingState newState = state;
633         switch (state.mStyle)
634         {
635             case LayoutTree::Node::cStyleDisplay:
636                 newState.mStyle = LayoutTree::Node::cStyleText;
637                 break;
638 
639             case LayoutTree::Node::cStyleText:
640                 newState.mStyle = LayoutTree::Node::cStyleScript;
641                 break;
642 
643             case LayoutTree::Node::cStyleScript:
644                 newState.mStyle = LayoutTree::Node::cStyleScriptScript;
645                 break;
646         }
647 
648         auto_ptr<LayoutTree::Node> inside(
649             new LayoutTree::Fraction(
650                 state.mStyle,
651                 state.mColour,
652                 mChild1->BuildLayoutTree(newState),
653                 mChild2->BuildLayoutTree(newState),
654                 isLineVisible
655             )
656         );
657 
658         if (hasParentheses)
659             return auto_ptr<LayoutTree::Node>(
660                 new LayoutTree::Fenced(
661                     state.mStyle,
662                     state.mColour,
663                     L"(", L")", inside
664                 )
665             );
666         else
667             return inside;
668     }
669 
670     if (mCommand == L"\\rootReserved")
671     {
672         TexProcessingState newState = state;
673         newState.mStyle = LayoutTree::Node::cStyleScriptScript;
674 
675         return auto_ptr<LayoutTree::Node>(
676             new LayoutTree::Root(
677                 mChild2->BuildLayoutTree(state),
678                 mChild1->BuildLayoutTree(newState),
679                 state.mColour
680             )
681         );
682     }
683 
684     if (mCommand == L"\\cfrac")
685     {
686         TexProcessingState newState = state;
687         newState.mStyle = LayoutTree::Node::cStyleText;
688 
689         return auto_ptr<LayoutTree::Node>(
690             new LayoutTree::Fraction(
691                 LayoutTree::Node::cStyleDisplay,
692                 state.mColour,
693                 mChild1->BuildLayoutTree(newState),
694                 mChild2->BuildLayoutTree(newState),
695                 true        // true = should be a visible fraction line
696             )
697         );
698     }
699 
700     if (mCommand == L"\\overset" || mCommand == L"\\underset")
701     {
702         // Work out what style the under/overset node should be.
703         TexProcessingState newState = state;
704         switch (state.mStyle)
705         {
706             case LayoutTree::Node::cStyleDisplay:
707             case LayoutTree::Node::cStyleText:
708                 newState.mStyle = LayoutTree::Node::cStyleScript;
709                 break;
710 
711             case LayoutTree::Node::cStyleScript:
712             case LayoutTree::Node::cStyleScriptScript:
713                 newState.mStyle = LayoutTree::Node::cStyleScriptScript;
714                 break;
715         }
716 
717         auto_ptr<LayoutTree::Node> upper, lower;
718         if (mCommand == L"\\overset")
719             upper = mChild1->BuildLayoutTree(newState);
720         else        // else underset
721             lower = mChild1->BuildLayoutTree(newState);
722 
723         auto_ptr<LayoutTree::Node> base =
724             mChild2->BuildLayoutTree(state);
725 
726         return auto_ptr<LayoutTree::Node>(
727             new LayoutTree::Scripts(
728                 state.mStyle,
729                 base->mFlavour,
730                 LayoutTree::Node::cLimitsNoLimits,
731                 state.mColour,
732                 false,      // false = NOT sideset
733                 base,
734                 upper,
735                 lower
736             )
737         );
738     }
739 
740     throw logic_error(
741         "Unexpected command in MathCommand2Args::BuildLayoutTree"
742     );
743 }
744 
745 
BuildLayoutTree(const TexProcessingState & state) const746 auto_ptr<LayoutTree::Node> MathScripts::BuildLayoutTree(
747     const TexProcessingState& state
748 ) const
749 {
750     auto_ptr<LayoutTree::Node> base, upper, lower;
751 
752     LayoutTree::Node::Flavour flavour = LayoutTree::Node::cFlavourOrd;
753     LayoutTree::Node::Limits limits =
754         LayoutTree::Node::cLimitsDisplayLimits;
755 
756     if (mBase.get())
757     {
758         // If the base is nonempty, we inherit its flavour and limits
759         // settings
760         base = mBase->BuildLayoutTree(state);
761         flavour = base->mFlavour;
762         limits = base->mLimits;
763     }
764 
765     // Work out the style for the super/subscripts
766     TexProcessingState newState = state;
767     switch (state.mStyle)
768     {
769         case LayoutTree::Node::cStyleDisplay:
770         case LayoutTree::Node::cStyleText:
771             newState.mStyle = LayoutTree::Node::cStyleScript;
772             break;
773 
774         case LayoutTree::Node::cStyleScript:
775         case LayoutTree::Node::cStyleScriptScript:
776             newState.mStyle = LayoutTree::Node::cStyleScriptScript;
777             break;
778     }
779 
780     if (mUpper.get())
781         upper = mUpper->BuildLayoutTree(newState);
782     if (mLower.get())
783         lower = mLower->BuildLayoutTree(newState);
784 
785     // Determine from the flavour and limits settings whether we should
786     // be putting limits above/below or to the side.
787     bool isSideset =
788         (flavour != LayoutTree::Node::cFlavourOp) ||
789         (
790             limits != LayoutTree::Node::cLimitsLimits &&
791             (
792                 limits != LayoutTree::Node::cLimitsDisplayLimits ||
793                 state.mStyle != LayoutTree::Node::cStyleDisplay
794             )
795         );
796 
797     return auto_ptr<LayoutTree::Node>(
798         new LayoutTree::Scripts(
799             state.mStyle,
800             flavour,
801             LayoutTree::Node::cLimitsDisplayLimits,
802             state.mColour,
803             isSideset,
804             base,
805             upper,
806             lower
807         )
808     );
809 }
810 
811 
BuildLayoutTree(const TexProcessingState & state) const812 auto_ptr<LayoutTree::Node> MathLimits::BuildLayoutTree(
813     const TexProcessingState& state
814 ) const
815 {
816     auto_ptr<LayoutTree::Node> node =
817         mChild->BuildLayoutTree(state);
818 
819     if (node->mFlavour != LayoutTree::Node::cFlavourOp)
820         throw Exception(L"MisplacedLimits", mCommand);
821 
822     if (mCommand == L"\\limits")
823         node->mLimits = LayoutTree::Node::cLimitsLimits;
824     else if (mCommand == L"\\nolimits")
825         node->mLimits = LayoutTree::Node::cLimitsNoLimits;
826     else if (mCommand == L"\\displaylimits")
827         node->mLimits = LayoutTree::Node::cLimitsDisplayLimits;
828     else
829         throw logic_error(
830             "Unexpected command in MathLimits::BuildLayoutTree."
831         );
832 
833     return node;
834 }
835 
BuildLayoutTree(const TexProcessingState & state) const836 auto_ptr<LayoutTree::Node> MathGroup::BuildLayoutTree(
837     const TexProcessingState& state
838 ) const
839 {
840     // TeX treates any group enclosed by curly braces as an "ordinary" atom.
841     // This is why e.g. "123{,}456" looks different to "123,456"
842     auto_ptr<LayoutTree::Node> node
843         = mChild->BuildLayoutTree(state);
844     node->mFlavour = LayoutTree::Node::cFlavourOrd;
845     return node;
846 }
847 
848 
BuildLayoutTree(const TexProcessingState & state) const849 auto_ptr<LayoutTree::Node> MathDelimited::BuildLayoutTree(
850     const TexProcessingState& state
851 ) const
852 {
853     return auto_ptr<LayoutTree::Node>(
854         new LayoutTree::Fenced(
855             state.mStyle,
856             state.mColour,
857             gDelimiterTable[mLeftDelimiter],
858             gDelimiterTable[mRightDelimiter],
859             mChild->BuildLayoutTree(state)
860         )
861     );
862 }
863 
864 
865 // Stores information about the various "\big..." commands.
866 struct BigInfo
867 {
868     LayoutTree::Node::Flavour mFlavour;
869     wstring mSize;
870 
BigInfoblahtex::ParseTree::BigInfo871     BigInfo(
872         LayoutTree::Node::Flavour flavour,
873         const wstring& size
874     ) :
875         mFlavour(flavour),
876         mSize(size)
877     { }
878 };
879 
880 
BuildLayoutTree(const TexProcessingState & state) const881 auto_ptr<LayoutTree::Node> MathBig::BuildLayoutTree(
882     const TexProcessingState& state
883 ) const
884 {
885     // Here's a list of all the "\big..." commands, how big the delimiter
886     // should become, and what flavour it should be, for each one.
887     static pair<wstring, BigInfo> bigCommandArray[] =
888     {
889         make_pair(L"\\big",   BigInfo(LayoutTree::Node::cFlavourOrd,   L"1.2em")),
890         make_pair(L"\\bigl",  BigInfo(LayoutTree::Node::cFlavourOpen,  L"1.2em")),
891         make_pair(L"\\bigr",  BigInfo(LayoutTree::Node::cFlavourClose, L"1.2em")),
892 
893         make_pair(L"\\Big",   BigInfo(LayoutTree::Node::cFlavourOrd,   L"1.8em")),
894         make_pair(L"\\Bigl",  BigInfo(LayoutTree::Node::cFlavourOpen,  L"1.8em")),
895         make_pair(L"\\Bigr",  BigInfo(LayoutTree::Node::cFlavourClose, L"1.8em")),
896 
897         make_pair(L"\\bigg",  BigInfo(LayoutTree::Node::cFlavourOrd,   L"2.4em")),
898         make_pair(L"\\biggl", BigInfo(LayoutTree::Node::cFlavourOpen,  L"2.4em")),
899         make_pair(L"\\biggr", BigInfo(LayoutTree::Node::cFlavourClose, L"2.4em")),
900 
901         make_pair(L"\\Bigg",  BigInfo(LayoutTree::Node::cFlavourOrd,   L"3em")),
902         make_pair(L"\\Biggl", BigInfo(LayoutTree::Node::cFlavourOpen,  L"3em")),
903         make_pair(L"\\Biggr", BigInfo(LayoutTree::Node::cFlavourClose, L"3em"))
904     };
905     static wishful_hash_map<wstring, BigInfo> bigCommandTable(
906         bigCommandArray,
907         END_ARRAY(bigCommandArray)
908     );
909 
910     wishful_hash_map<wstring, BigInfo>::const_iterator
911         bigCommand = bigCommandTable.find(mCommand);
912 
913     if (bigCommand != bigCommandTable.end())
914     {
915         LayoutTree::Node::Style newStyle = state.mStyle;
916         if (state.mStyle != LayoutTree::Node::cStyleDisplay &&
917             state.mStyle != LayoutTree::Node::cStyleText
918         )
919             newStyle = LayoutTree::Node::cStyleText;
920 
921         // FIX: TeX allows "\big."; do we?
922         return auto_ptr<LayoutTree::Node>(
923             new LayoutTree::SymbolOperator(
924                 true,       // indicates stretchy="true"
925                 bigCommand->second.mSize,
926                 false,      // not an accent
927                 gDelimiterTable[mDelimiter],
928                 cMathmlFontNormal,
929                 newStyle,
930                 bigCommand->second.mFlavour,
931                 LayoutTree::Node::cLimitsDisplayLimits,
932                 state.mColour
933             )
934         );
935     }
936 
937     throw logic_error("Unknown command in MathBig::BuildLayoutTree");
938 }
939 
940 
BuildLayoutTree(const TexProcessingState & state) const941 auto_ptr<LayoutTree::Node> MathTableRow::BuildLayoutTree(
942     const TexProcessingState& state
943 ) const
944 {
945     // We should never get here, because MathTable::BuildLayoutTree
946     // handles the whole table.
947     throw logic_error(
948         "Arrived unexpectedly in MathTableRow::BuildLayoutTree"
949     );
950 }
951 
952 
BuildLayoutTree(const TexProcessingState & state) const953 auto_ptr<LayoutTree::Node> MathTable::BuildLayoutTree(
954     const TexProcessingState& state
955 ) const
956 {
957     auto_ptr<LayoutTree::Table> table(
958         new LayoutTree::Table(state.mStyle, state.mColour)
959     );
960     table->mRows.reserve(mRows.size());
961 
962     // Walk the table, building the layout tree as we go.
963     for (vector<MathTableRow*>::const_iterator
964         inRow = mRows.begin();
965         inRow != mRows.end();
966         inRow++
967     )
968     {
969         table->mRows.push_back(vector<LayoutTree::Node*>());
970         vector<LayoutTree::Node*>& outRow = table->mRows.back();
971         for (vector<MathNode*>::const_iterator
972             entry = (*inRow)->mEntries.begin();
973             entry != (*inRow)->mEntries.end();
974             entry++
975         )
976             outRow.push_back(
977                 (*entry)->
978                     BuildLayoutTree(state).release()
979             );
980     }
981 
982     return static_cast<auto_ptr<LayoutTree::Node> >(table);
983 }
984 
985 
986 // Stores information about an environment.
987 struct EnvironmentInfo
988 {
989     wstring mLeftDelimiter, mRightDelimiter;
990 
EnvironmentInfoblahtex::ParseTree::EnvironmentInfo991     EnvironmentInfo(
992         const wstring& leftDelimiter,
993         const wstring& rightDelimiter
994     ) :
995         mLeftDelimiter(leftDelimiter),
996         mRightDelimiter(rightDelimiter)
997     { }
998 };
999 
1000 
BuildLayoutTree(const TexProcessingState & state) const1001 auto_ptr<LayoutTree::Node> MathEnvironment::BuildLayoutTree(
1002     const TexProcessingState& state
1003 ) const
1004 {
1005     // A list of all environments, and which delimiters appear on each
1006     // side of the corresponding table.
1007     // FIX: this is kind of stupid... almost every environment ends up
1008     // with its own special-case code!
1009     static pair<wstring, EnvironmentInfo> environmentArray[] =
1010     {
1011         make_pair(L"matrix",       EnvironmentInfo(L"",       L"")),
1012         make_pair(L"pmatrix",      EnvironmentInfo(L"(",      L")")),
1013         make_pair(L"bmatrix",      EnvironmentInfo(L"[",      L"]")),
1014         make_pair(L"Bmatrix",      EnvironmentInfo(L"{",      L"}")),
1015         make_pair(L"vmatrix",      EnvironmentInfo(L"|",      L"|")),
1016         // DoubleVerticalBar:
1017         make_pair(L"Vmatrix",      EnvironmentInfo(L"\U00002225", L"\U00002225")),
1018         make_pair(L"cases",        EnvironmentInfo(L"{",      L"")),
1019         make_pair(L"aligned",      EnvironmentInfo(L"",       L"")),
1020         make_pair(L"smallmatrix",  EnvironmentInfo(L"",       L"")),
1021         make_pair(L"substack",     EnvironmentInfo(L"",       L""))
1022     };
1023     static wishful_hash_map<wstring, EnvironmentInfo> environmentTable(
1024         environmentArray,
1025         END_ARRAY(environmentArray)
1026     );
1027 
1028     wishful_hash_map<wstring, EnvironmentInfo>::const_iterator
1029         environmentLookup = environmentTable.find(mName);
1030 
1031     if (environmentLookup == environmentTable.end())
1032         throw logic_error(
1033             "Unexpected environment name in "
1034             "MathEnvironment::BuildLayoutTree"
1035         );
1036 
1037     // For reasons I haven't investigated, the "boldsymbol" flag persists
1038     // into environments, but the math font doesn't.
1039     TexProcessingState newState = state;
1040     newState.mMathFont = TexMathFont();
1041     newState.mMathFont.mIsBoldsymbol = state.mMathFont.mIsBoldsymbol;
1042 
1043     LayoutTree::Node::Style fencedStyle;
1044     if (mName == L"smallmatrix" || mName == L"substack")
1045         newState.mStyle = LayoutTree::Node::cStyleScript;
1046     else if (mName == L"aligned")
1047         newState.mStyle = LayoutTree::Node::cStyleDisplay;
1048     else
1049     {
1050         newState.mStyle = LayoutTree::Node::cStyleText;
1051         fencedStyle =
1052             (state.mStyle == LayoutTree::Node::cStyleDisplay)
1053                 ? LayoutTree::Node::cStyleDisplay
1054                 : LayoutTree::Node::cStyleText;
1055     }
1056 
1057     auto_ptr<LayoutTree::Node> table = mTable->BuildLayoutTree(newState);
1058     LayoutTree::Table* tablePtr =
1059         dynamic_cast<LayoutTree::Table*>(table.get());
1060     if (!tablePtr)
1061         throw logic_error(
1062             "Unexpected node type in MathEnvironment::BuildLayoutTree"
1063         );
1064 
1065     if (mName == L"substack")
1066         tablePtr->mRowSpacing = LayoutTree::Table::cRowSpacingTight;
1067 
1068     if (mName == L"aligned")
1069         tablePtr->mAlign = LayoutTree::Table::cAlignRightLeft;
1070     else if (mName == L"cases")
1071         tablePtr->mAlign = LayoutTree::Table::cAlignLeft;
1072 
1073     if (environmentLookup->second.mLeftDelimiter.empty() &&
1074         environmentLookup->second.mRightDelimiter.empty()
1075     )
1076         return table;
1077 
1078     return auto_ptr<LayoutTree::Node>(
1079         new LayoutTree::Fenced(
1080             fencedStyle,
1081             state.mColour,
1082             environmentLookup->second.mLeftDelimiter,
1083             environmentLookup->second.mRightDelimiter,
1084             table
1085         )
1086     );
1087 }
1088 
1089 
BuildLayoutTree(const TexProcessingState & state) const1090 auto_ptr<LayoutTree::Node> EnterTextMode::BuildLayoutTree(
1091     const TexProcessingState& state
1092 ) const
1093 {
1094     // List of all commands that launch into text mode, and some information
1095     // about which font they select.
1096     static pair<wstring, TexTextFont> textCommandArray[] =
1097     {                                             // flags are:     bold?  italic?
1098         make_pair(L"\\mbox",    TexTextFont(TexTextFont::cFamilyRm, false, false)),
1099         make_pair(L"\\hbox",    TexTextFont(TexTextFont::cFamilyRm, false, false)),
1100         make_pair(L"\\text",    TexTextFont(TexTextFont::cFamilyRm, false, false)),
1101         make_pair(L"\\textrm",  TexTextFont(TexTextFont::cFamilyRm, false, false)),
1102         make_pair(L"\\textbf",  TexTextFont(TexTextFont::cFamilyRm, true,  false)),
1103         make_pair(L"\\emph",    TexTextFont(TexTextFont::cFamilyRm, false, true)),
1104         make_pair(L"\\textit",  TexTextFont(TexTextFont::cFamilyRm, false, true)),
1105         make_pair(L"\\textsf",  TexTextFont(TexTextFont::cFamilySf, false, false)),
1106         make_pair(L"\\texttt",  TexTextFont(TexTextFont::cFamilyTt, false, false)),
1107         make_pair(L"\\cyr",     TexTextFont(TexTextFont::cFamilyRm, false, false)),
1108         make_pair(L"\\jap",     TexTextFont(TexTextFont::cFamilyRm, false, false))
1109     };
1110     static wishful_hash_map<wstring, TexTextFont> textCommandTable(
1111         textCommandArray,
1112         END_ARRAY(textCommandArray)
1113     );
1114 
1115     wishful_hash_map<wstring, TexTextFont>::iterator
1116         textCommand = textCommandTable.find(mCommand);
1117 
1118     if (textCommand == textCommandTable.end())
1119         throw logic_error(
1120             "Unexpected command in EnterTextMode::BuildLayoutTree"
1121         );
1122 
1123     TexProcessingState newState = state;
1124     newState.mTextFont = textCommand->second;
1125 
1126     if (mCommand == L"\\hbox" || mCommand == L"\\mbox")
1127         newState.mStyle = LayoutTree::Node::cStyleText;
1128 
1129     return mChild->BuildLayoutTree(newState);
1130 }
1131 
1132 
BuildLayoutTree(const TexProcessingState & state) const1133 auto_ptr<LayoutTree::Node> TextList::BuildLayoutTree(
1134     const TexProcessingState& state
1135 ) const
1136 {
1137     auto_ptr<LayoutTree::Row> node(
1138         new LayoutTree::Row(state.mStyle, state.mColour)
1139     );
1140 
1141     // Recursively build layout trees for children, and merge Rows to obtain
1142     // a single Row, and apply state changes as appropriate.
1143     TexProcessingState currentState = state;
1144     for (vector<TextNode*>::const_iterator
1145         child = mChildren.begin();
1146         child != mChildren.end();
1147         child++
1148     )
1149     {
1150         TextStateChange* childAsStateChange =
1151             dynamic_cast<TextStateChange*>(*child);
1152 
1153         if (childAsStateChange)
1154             childAsStateChange->Apply(currentState);
1155         else
1156         {
1157             auto_ptr<LayoutTree::Node>
1158                 newNode = (*child)->BuildLayoutTree(currentState);
1159 
1160             LayoutTree::Row* isRow =
1161                 dynamic_cast<LayoutTree::Row*>(newNode.get());
1162 
1163             if (isRow)
1164                 node->mChildren.splice(
1165                     node->mChildren.end(),
1166                     isRow->mChildren
1167                 );
1168             else
1169                 node->mChildren.push_back(newNode.release());
1170         }
1171     }
1172 
1173     return static_cast<auto_ptr<LayoutTree::Node> >(node);
1174 }
1175 
1176 
BuildLayoutTree(const TexProcessingState & state) const1177 auto_ptr<LayoutTree::Node> TextSymbol::BuildLayoutTree(
1178     const TexProcessingState& state
1179 ) const
1180 {
1181     static pair<wstring, wstring> textCommandArray[] =
1182     {
1183         make_pair(L"\\!",      L""),
1184         make_pair(L" ",        L"\U000000A0"),     // NonBreakingSpace
1185         make_pair(L"~",        L"\U000000A0"),
1186         make_pair(L"\\,",      L"\U000000A0"),
1187         make_pair(L"\\ ",      L"\U000000A0"),
1188         make_pair(L"\\;",      L"\U000000A0"),
1189         make_pair(L"\\quad",   L"\U000000A0\U000000A0"),
1190         make_pair(L"\\qquad",  L"\U000000A0\U000000A0\U000000A0\U000000A0"),
1191 
1192         make_pair(L"\\&",                 L"&"),
1193         // FIX: why did I put in these next two lines again?
1194         // FIX: The character "<" and ">" actually do funny things in TeX...
1195         make_pair(L"<",                   L"<"),
1196         make_pair(L">",                   L">"),
1197         make_pair(L"\\_",                 L"_"),
1198         make_pair(L"\\$",                 L"$"),
1199         make_pair(L"\\#",                 L"#"),
1200         make_pair(L"\\%",                 L"%"),
1201         make_pair(L"\\{",                 L"{"),
1202         make_pair(L"\\}",                 L"}"),
1203         make_pair(L"\\textbackslash",     L"\\"),
1204         // FIX: for some reason in Firefox the caret is much lower
1205         // than it should be
1206         make_pair(L"\\textasciicircum",   L"^"),
1207         make_pair(L"\\textasciitilde",    L"~"),
1208         make_pair(L"\\textvisiblespace",  L"\U000023B5"),
1209         make_pair(L"\\O",                 L"\U000000D8"),
1210         make_pair(L"\\S",                 L"\U000000A7")
1211     };
1212     static wishful_hash_map<wstring, wstring> textCommandTable(
1213         textCommandArray,
1214         END_ARRAY(textCommandArray)
1215     );
1216 
1217     wishful_hash_map<wstring, wstring>::iterator
1218         textCommand = textCommandTable.find(mCommand);
1219 
1220     if (textCommand != textCommandTable.end())
1221         return auto_ptr<LayoutTree::Node>(
1222             new LayoutTree::SymbolText(
1223                 textCommand->second,
1224                 state.mTextFont.GetMathmlApproximation(),
1225                 state.mStyle,
1226                 state.mColour
1227             )
1228         );
1229 
1230     return auto_ptr<LayoutTree::Node>(
1231         new LayoutTree::SymbolText(
1232             mCommand,
1233             state.mTextFont.GetMathmlApproximation(),
1234             state.mStyle,
1235             state.mColour
1236         )
1237     );
1238 }
1239 
1240 
BuildLayoutTree(const TexProcessingState & state) const1241 auto_ptr<LayoutTree::Node> TextGroup::BuildLayoutTree(
1242     const TexProcessingState& state
1243 ) const
1244 {
1245     return mChild->BuildLayoutTree(state);
1246 }
1247 
1248 
BuildLayoutTree(const TexProcessingState & state) const1249 auto_ptr<LayoutTree::Node> TextCommand1Arg::BuildLayoutTree(
1250     const TexProcessingState& state
1251 ) const
1252 {
1253     TexProcessingState newState = state;
1254 
1255     if (mCommand == L"\\textrm")
1256         newState.mTextFont.mFamily = TexTextFont::cFamilyRm;
1257     else if (mCommand == L"\\texttt")
1258         newState.mTextFont.mFamily = TexTextFont::cFamilyTt;
1259     else if (mCommand == L"\\textsf")
1260         newState.mTextFont.mFamily = TexTextFont::cFamilySf;
1261     else if (mCommand == L"\\textit")
1262         newState.mTextFont.mIsItalic = true;
1263     else if (mCommand == L"\\emph")
1264         newState.mTextFont.mIsItalic = !newState.mTextFont.mIsItalic;
1265     else if (mCommand == L"\\textbf")
1266         newState.mTextFont.mIsBold = true;
1267     else if (
1268         mCommand == L"\\text" ||
1269         mCommand == L"\\hbox" ||
1270         mCommand == L"\\mbox" ||
1271         mCommand == L"\\cyr" ||
1272         mCommand == L"\\jap"
1273     )
1274         // do nothing!
1275         { }
1276     else
1277         throw logic_error(
1278             "Unexpected command in TextCommand1Arg::BuildLayoutTree"
1279         );
1280 
1281     return mChild->BuildLayoutTree(newState);
1282 }
1283 
1284 }
1285 }
1286 
1287 // end of file @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1288