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 // ^ 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