cURL: follow redirects
[LibreOffice.git] / starmath / source / node.cxx
blob71bcfe23f117f8d6b9e6f023897a9b05ce777cf7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "node.hxx"
22 #include "rect.hxx"
23 #include "symbol.hxx"
24 #include "smmod.hxx"
25 #include "document.hxx"
26 #include "view.hxx"
27 #include "mathtype.hxx"
28 #include "tmpdevice.hxx"
29 #include "visitors.hxx"
31 #include <comphelper/string.hxx>
32 #include <tools/color.hxx>
33 #include <tools/fract.hxx>
34 #include <tools/gen.hxx>
35 #include <vcl/outdev.hxx>
37 #include <cassert>
38 #include <math.h>
39 #include <memory>
40 #include <float.h>
41 #include <vector>
42 #include <boost/checked_delete.hpp>
44 namespace {
46 template<typename F>
47 void ForEachNonNull(SmNode *pNode, F && f)
49 sal_uInt16 nSize = pNode->GetNumSubNodes();
50 for (sal_uInt16 i = 0; i < nSize; i++) {
51 SmNode *pSubNode = pNode->GetSubNode(i);
52 if (pSubNode != nullptr)
53 f(pSubNode);
59 SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken)
60 : maNodeToken( rNodeToken )
61 , meType( eNodeType )
62 , meScaleMode( SCALE_NONE )
63 , meRectHorAlign( RectHorAlign::Left )
64 , mnFlags( FontChangeMask::None )
65 , mnAttributes( FontAttribute::None )
66 , mbIsPhantom( false )
67 , mbIsSelected( false )
68 , mnAccIndex( -1 )
69 , mpParentNode( nullptr )
74 SmNode::~SmNode()
79 const SmNode * SmNode::GetLeftMost() const
80 // returns leftmost node of current subtree.
81 //! (this assumes the one with index 0 is always the leftmost subnode
82 //! for the current node).
84 const SmNode *pNode = GetNumSubNodes() > 0 ?
85 GetSubNode(0) : nullptr;
87 return pNode ? pNode->GetLeftMost() : this;
91 void SmNode::SetPhantom(bool bIsPhantomP)
93 if (! (Flags() & FontChangeMask::Phantom))
94 mbIsPhantom = bIsPhantomP;
96 bool b = mbIsPhantom;
97 ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
101 void SmNode::SetColor(const Color& rColor)
103 if (! (Flags() & FontChangeMask::Color))
104 GetFont().SetColor(rColor);
106 ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
110 void SmNode::SetAttribut(FontAttribute nAttrib)
112 if (
113 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
114 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
117 mnAttributes |= nAttrib;
120 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribut(nAttrib);});
124 void SmNode::ClearAttribut(FontAttribute nAttrib)
126 if (
127 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
128 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
131 mnAttributes &= ~nAttrib;
134 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribut(nAttrib);});
138 void SmNode::SetFont(const SmFace &rFace)
140 if (!(Flags() & FontChangeMask::Face))
141 GetFont() = rFace;
143 ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
147 void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
148 //! 'rSize' is in units of pts
150 Size aFntSize;
152 if (!(Flags() & FontChangeMask::Size))
154 Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()),
155 rSize.GetDenominator());
156 long nHeight = static_cast<long>(aVal);
158 aFntSize = GetFont().GetFontSize();
159 aFntSize.Width() = 0;
160 switch(nType)
162 case FontSizeType::ABSOLUT:
163 aFntSize.Height() = nHeight;
164 break;
166 case FontSizeType::PLUS:
167 aFntSize.Height() += nHeight;
168 break;
170 case FontSizeType::MINUS:
171 aFntSize.Height() -= nHeight;
172 break;
174 case FontSizeType::MULTIPLY:
175 aFntSize.Height() = static_cast<long>(Fraction(aFntSize.Height()) * rSize);
176 break;
178 case FontSizeType::DIVIDE:
179 if (rSize != Fraction(0L))
180 aFntSize.Height() = static_cast<long>(Fraction(aFntSize.Height()) / rSize);
181 break;
182 default:
183 break;
186 // check the requested size against maximum value
187 static int const nMaxVal = SmPtsTo100th_mm(128);
188 if (aFntSize.Height() > nMaxVal)
189 aFntSize.Height() = nMaxVal;
191 GetFont().SetSize(aFntSize);
194 ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
198 void SmNode::SetSize(const Fraction &rSize)
200 GetFont() *= rSize;
202 ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
206 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
208 if (!(Flags() & FontChangeMask::HorAlign))
209 meRectHorAlign = eHorAlign;
211 if (bApplyToSubTree)
212 ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
216 void SmNode::PrepareAttributes()
218 GetFont().SetWeight((Attributes() & FontAttribute::Bold) ? WEIGHT_BOLD : WEIGHT_NORMAL);
219 GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
223 void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
225 mbIsPhantom = false;
226 mnFlags = FontChangeMask::None;
227 mnAttributes = FontAttribute::None;
229 switch (rFormat.GetHorAlign())
230 { case AlignLeft: meRectHorAlign = RectHorAlign::Left; break;
231 case AlignCenter: meRectHorAlign = RectHorAlign::Center; break;
232 case AlignRight: meRectHorAlign = RectHorAlign::Right; break;
235 GetFont() = rFormat.GetFont(FNT_MATH);
236 OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
237 "unexpected CharSet" );
238 GetFont().SetWeight(WEIGHT_NORMAL);
239 GetFont().SetItalic(ITALIC_NONE);
241 ForEachNonNull(this, [&rFormat, &rDocShell](SmNode *pNode){pNode->Prepare(rFormat, rDocShell);});
244 void SmNode::Move(const Point& rPosition)
246 if (rPosition.X() == 0 && rPosition.Y() == 0)
247 return;
249 SmRect::Move(rPosition);
251 ForEachNonNull(this, [&rPosition](SmNode *pNode){pNode->Move(rPosition);});
255 void SmNode::CreateTextFromNode(OUString &rText)
257 sal_uInt16 nSize = GetNumSubNodes();
258 if (nSize > 1)
259 rText += "{";
260 ForEachNonNull(this, [&rText](SmNode *pNode){pNode->CreateTextFromNode(rText);});
261 if (nSize > 1)
263 rText = comphelper::string::stripEnd(rText, ' ');
264 rText += "} ";
269 void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
274 void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
279 const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
280 // returns (first) ** visible ** (sub)node with the tokens text at
281 // position 'nRow', 'nCol'.
282 //! (there should be exactly one such node if any)
284 if ( IsVisible()
285 && nRow == GetToken().nRow
286 && nCol >= GetToken().nCol && nCol < GetToken().nCol + GetToken().aText.getLength())
287 return this;
288 else
290 sal_uInt16 nNumSubNodes = GetNumSubNodes();
291 for (sal_uInt16 i = 0; i < nNumSubNodes; i++)
292 { const SmNode *pNode = GetSubNode(i);
294 if (!pNode)
295 continue;
297 const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
298 if (pResult)
299 return pResult;
303 return nullptr;
307 const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
309 long nDist = LONG_MAX;
310 const SmNode *pResult = nullptr;
312 if (IsVisible())
313 pResult = this;
314 else
316 sal_uInt16 nNumSubNodes = GetNumSubNodes();
317 for (sal_uInt16 i = 0; i < nNumSubNodes; i++)
318 { const SmNode *pNode = GetSubNode(i);
320 if (!pNode)
321 continue;
323 long nTmp;
324 const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
325 if (pFound && (nTmp = pFound->OrientedDist(rPoint)) < nDist)
326 { nDist = nTmp;
327 pResult = pFound;
329 // quit immediately if 'rPoint' is inside the *should not
330 // overlap with other rectangles* part.
331 // This (partly) serves for getting the attributes in eg
332 // "bar overstrike a".
333 // ('nDist < 0' is used as *quick shot* to avoid evaluation of
334 // the following expression, where the result is already determined)
335 if (nDist < 0 && pFound->IsInsideRect(rPoint))
336 break;
341 return pResult;
344 const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
346 const SmNode *pResult = nullptr;
348 sal_Int32 nIdx = GetAccessibleIndex();
349 OUStringBuffer aTxt;
350 if (nIdx >= 0)
351 GetAccessibleText( aTxt ); // get text if used in following 'if' statement
353 if (nIdx >= 0
354 && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.getLength())
355 pResult = this;
356 else
358 sal_uInt16 nNumSubNodes = GetNumSubNodes();
359 for (sal_uInt16 i = 0; i < nNumSubNodes; i++)
361 const SmNode *pNode = GetSubNode(i);
362 if (!pNode)
363 continue;
365 pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
366 if (pResult)
367 return pResult;
371 return pResult;
375 SmStructureNode::~SmStructureNode()
377 ForEachNonNull(this, boost::checked_deleter<SmNode>());
381 void SmStructureNode::SetSubNodes(SmNode *pFirst, SmNode *pSecond, SmNode *pThird)
383 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
384 aSubNodes.resize( nSize );
385 if (pFirst)
386 aSubNodes[0] = pFirst;
387 if (pSecond)
388 aSubNodes[1] = pSecond;
389 if (pThird)
390 aSubNodes[2] = pThird;
392 ClaimPaternity();
396 void SmStructureNode::SetSubNodes(const SmNodeArray &rNodeArray)
398 aSubNodes = rNodeArray;
399 ClaimPaternity();
403 bool SmStructureNode::IsVisible() const
405 return false;
409 sal_uInt16 SmStructureNode::GetNumSubNodes() const
411 return sal::static_int_cast<sal_uInt16>(aSubNodes.size());
415 SmNode * SmStructureNode::GetSubNode(sal_uInt16 nIndex)
417 return aSubNodes[nIndex];
421 void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
423 ForEachNonNull(const_cast<SmStructureNode *>(this),
424 [&rText](SmNode *pNode)
426 if (pNode->IsVisible())
427 pNode->SetAccessibleIndex(rText.getLength());
428 pNode->GetAccessibleText( rText );
433 void SmStructureNode::ClaimPaternity()
435 ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
439 bool SmVisibleNode::IsVisible() const
441 return true;
445 sal_uInt16 SmVisibleNode::GetNumSubNodes() const
447 return 0;
451 SmNode * SmVisibleNode::GetSubNode(sal_uInt16 /*nIndex*/)
453 return nullptr;
456 void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
458 rText.append(GetToken().aText);
461 void SmExpressionNode::CreateTextFromNode(OUString &rText)
463 sal_uInt16 nSize = GetNumSubNodes();
464 if (nSize > 1)
465 rText += "{";
466 for (sal_uInt16 i = 0; i < nSize; i++)
468 SmNode *pNode = GetSubNode(i);
469 if (pNode)
471 pNode->CreateTextFromNode(rText);
472 //Just a bit of foo to make unary +asd -asd +-asd -+asd look nice
473 if (pNode->GetType() == NMATH)
474 if ((nSize != 2) ||
475 ( !rText.endsWith("+") && !rText.endsWith("-") ))
476 rText += " ";
480 if (nSize > 1)
482 rText = comphelper::string::stripEnd(rText, ' ');
483 rText += "} ";
487 void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
488 // arranges all subnodes in one column
490 SmNode *pNode;
491 sal_uInt16 nSize = GetNumSubNodes();
493 // make distance depend on font size
494 long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
495 * GetFont().GetFontSize().Height()) / 100L;
497 if (nSize < 1)
498 return;
500 // arrange subnodes and get maximum width of them
501 long nMaxWidth = 0,
502 nTmp;
503 sal_uInt16 i;
504 for (i = 0; i < nSize; i++)
505 if (nullptr != (pNode = GetSubNode(i)))
506 { pNode->Arrange(rDev, rFormat);
507 if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
508 nMaxWidth = nTmp;
511 Point aPos;
512 SmRect::operator = (SmRect(nMaxWidth, 1));
513 for (i = 0; i < nSize; i++)
514 { if (nullptr != (pNode = GetSubNode(i)))
515 { const SmRect &rNodeRect = pNode->GetRect();
516 const SmNode *pCoNode = pNode->GetLeftMost();
517 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
519 aPos = rNodeRect.AlignTo(*this, RectPos::Bottom,
520 eHorAlign, RectVerAlign::Baseline);
521 if (i)
522 aPos.Y() += nDist;
523 pNode->MoveTo(aPos);
524 ExtendBy(rNodeRect, nSize > 1 ? RectCopyMBL::None : RectCopyMBL::Arg);
527 // #i972#
528 if (HasBaseline())
529 nFormulaBaseline = GetBaseline();
530 else
532 SmTmpDevice aTmpDev (rDev, true);
533 aTmpDev.SetFont(GetFont());
535 SmRect aRect = (SmRect(aTmpDev, &rFormat, OUString("a"),
536 GetFont().GetBorderWidth()));
537 nFormulaBaseline = GetAlignM();
538 // move from middle position by constant - distance
539 // between middle and baseline for single letter
540 nFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
544 const SmNode * SmTableNode::GetLeftMost() const
546 return this;
550 long SmTableNode::GetFormulaBaseline() const
552 return nFormulaBaseline;
556 /**************************************************************************/
559 void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
561 SmNode::Prepare(rFormat, rDocShell);
563 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
564 // to the rest of the formula compared to the 'FNT_MATH' font.
565 GetFont() = rFormat.GetFont(FNT_VARIABLE);
566 Flags() |= FontChangeMask::Face;
570 /**************************************************************************/
573 void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
574 // arranges all subnodes in one row with some extra space between
576 SmNode *pNode;
577 sal_uInt16 nSize = GetNumSubNodes();
578 sal_uInt16 i;
579 for (i = 0; i < nSize; i++)
580 if (nullptr != (pNode = GetSubNode(i)))
581 pNode->Arrange(rDev, rFormat);
583 SmTmpDevice aTmpDev (rDev, true);
584 aTmpDev.SetFont(GetFont());
586 if (nSize < 1)
588 // provide an empty rectangle with alignment parameters for the "current"
589 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
590 // same sub-/supscript positions.)
591 //! be sure to use a character that has explicitly defined HiAttribut
592 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
593 //! 'vec {a}'.
594 SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString("a"),
595 GetFont().GetBorderWidth()));
596 // make sure that the rectangle occupies (almost) no space
597 SetWidth(1);
598 SetItalicSpaces(0, 0);
599 return;
602 // make distance depend on font size
603 long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetFontSize().Height()) / 100L;
604 if (!IsUseExtraSpaces())
605 nDist = 0;
607 Point aPos;
608 // copy the first node into LineNode and extend by the others
609 if (nullptr != (pNode = GetSubNode(0)))
610 SmRect::operator = (pNode->GetRect());
612 for (i = 1; i < nSize; i++)
613 if (nullptr != (pNode = GetSubNode(i)))
615 aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
617 // add horizontal space to the left for each but the first sub node
618 aPos.X() += nDist;
620 pNode->MoveTo(aPos);
621 ExtendBy( *pNode, RectCopyMBL::Xor );
626 /**************************************************************************/
629 void SmExpressionNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
630 // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
632 SmLineNode::Arrange(rDev, rFormat);
634 // copy alignment of leftmost subnode if any
635 const SmNode *pNode = GetLeftMost();
636 if (pNode)
637 SetRectHorAlign(pNode->GetRectHorAlign(), false);
641 /**************************************************************************/
644 void SmUnHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
646 bool bIsPostfix = GetToken().eType == TFACT;
648 SmNode *pNode0 = GetSubNode(0),
649 *pNode1 = GetSubNode(1);
650 SmNode *pOper = bIsPostfix ? pNode1 : pNode0,
651 *pBody = bIsPostfix ? pNode0 : pNode1;
652 assert(pOper);
653 assert(pBody);
655 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
656 pOper->Arrange(rDev, rFormat);
657 pBody->Arrange(rDev, rFormat);
659 long nDist = (pOper->GetRect().GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100L;
661 SmRect::operator = (*pNode0);
663 Point aPos = pNode1->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
664 aPos.X() += nDist;
665 pNode1->MoveTo(aPos);
666 ExtendBy(*pNode1, RectCopyMBL::Xor);
670 /**************************************************************************/
673 void SmRootNode::GetHeightVerOffset(const SmRect &rRect,
674 long &rHeight, long &rVerOffset)
675 // calculate height and vertical offset of root sign suitable for 'rRect'
677 rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
678 rHeight = rRect.GetHeight() - rVerOffset;
680 OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
681 OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
685 Point SmRootNode::GetExtraPos(const SmRect &rRootSymbol,
686 const SmRect &rExtra)
688 const Size &rSymSize = rRootSymbol.GetSize();
690 Point aPos = rRootSymbol.GetTopLeft()
691 + Point((rSymSize.Width() * 70) / 100,
692 (rSymSize.Height() * 52) / 100);
694 // from this calculate topleft edge of 'rExtra'
695 aPos.X() -= rExtra.GetWidth() + rExtra.GetItalicRightSpace();
696 aPos.Y() -= rExtra.GetHeight();
697 // if there's enough space move a bit less to the right
698 // examples: "nroot i a", "nroot j a"
699 // (it looks better if we don't use italic-spaces here)
700 long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
701 if (aPos.X() > nX)
702 aPos.X() = nX;
704 return aPos;
708 void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
710 //! pExtra needs to have the smaller index than pRootSym in order to
711 //! not to get the root symbol but the pExtra when clicking on it in the
712 //! GraphicWindow. (That is because of the simplicity of the algorithm
713 //! that finds the node corresponding to a mouseclick in the window.)
714 SmNode *pExtra = GetSubNode(0),
715 *pRootSym = GetSubNode(1),
716 *pBody = GetSubNode(2);
717 assert(pRootSym);
718 assert(pBody);
720 pBody->Arrange(rDev, rFormat);
722 long nHeight,
723 nVerOffset;
724 GetHeightVerOffset(*pBody, nHeight, nVerOffset);
725 nHeight += rFormat.GetDistance(DIS_ROOT)
726 * GetFont().GetFontSize().Height() / 100L;
728 // font specialist advised to change the width first
729 pRootSym->AdaptToY(rDev, nHeight);
730 pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
732 pRootSym->Arrange(rDev, rFormat);
734 Point aPos = pRootSym->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, RectVerAlign::Baseline);
735 //! override calculated vertical position
736 aPos.Y() = pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom();
737 aPos.Y() -= nVerOffset;
738 pRootSym->MoveTo(aPos);
740 if (pExtra)
741 { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
742 pExtra->Arrange(rDev, rFormat);
744 aPos = GetExtraPos(*pRootSym, *pExtra);
745 pExtra->MoveTo(aPos);
748 SmRect::operator = (*pBody);
749 ExtendBy(*pRootSym, RectCopyMBL::This);
750 if (pExtra)
751 ExtendBy(*pExtra, RectCopyMBL::This, true);
755 void SmRootNode::CreateTextFromNode(OUString &rText)
757 SmNode *pExtra = GetSubNode(0);
758 if (pExtra)
760 rText += "nroot ";
761 pExtra->CreateTextFromNode(rText);
763 else
764 rText += "sqrt ";
766 if (!pExtra && GetSubNode(2)->GetNumSubNodes() > 1)
767 rText += "{ ";
769 GetSubNode(2)->CreateTextFromNode(rText);
771 if (!pExtra && GetSubNode(2)->GetNumSubNodes() > 1)
772 rText += "} ";
775 /**************************************************************************/
778 void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
780 SmNode *pLeft = LeftOperand(),
781 *pOper = Symbol(),
782 *pRight = RightOperand();
783 assert(pLeft);
784 assert(pOper);
785 assert(pRight);
787 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
789 pLeft ->Arrange(rDev, rFormat);
790 pOper ->Arrange(rDev, rFormat);
791 pRight->Arrange(rDev, rFormat);
793 const SmRect &rOpRect = pOper->GetRect();
795 long nDist = (rOpRect.GetWidth() *
796 rFormat.GetDistance(DIS_HORIZONTAL)) / 100L;
798 SmRect::operator = (*pLeft);
800 Point aPos;
801 aPos = pOper->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
802 aPos.X() += nDist;
803 pOper->MoveTo(aPos);
804 ExtendBy(*pOper, RectCopyMBL::Xor);
806 aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
807 aPos.X() += nDist;
809 pRight->MoveTo(aPos);
810 ExtendBy(*pRight, RectCopyMBL::Xor);
814 /**************************************************************************/
817 void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
819 SmNode *pNum = GetSubNode(0),
820 *pLine = GetSubNode(1),
821 *pDenom = GetSubNode(2);
822 assert(pNum);
823 assert(pLine);
824 assert(pDenom);
826 bool bIsTextmode = rFormat.IsTextmode();
827 if (bIsTextmode)
829 Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
830 pNum ->SetSize(aFraction);
831 pLine ->SetSize(aFraction);
832 pDenom->SetSize(aFraction);
835 pNum ->Arrange(rDev, rFormat);
836 pDenom->Arrange(rDev, rFormat);
838 long nFontHeight = GetFont().GetFontSize().Height(),
839 nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100L,
840 nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L,
841 nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
842 nNumDist = bIsTextmode ? 0 :
843 nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100L,
844 nDenomDist = bIsTextmode ? 0 :
845 nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100L;
847 // font specialist advised to change the width first
848 pLine->AdaptToY(rDev, nThick);
849 pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
850 pLine->Arrange(rDev, rFormat);
852 // get horizontal alignment for numerator
853 const SmNode *pLM = pNum->GetLeftMost();
854 RectHorAlign eHorAlign = pLM->GetRectHorAlign();
856 // move numerator to its position
857 Point aPos = pNum->AlignTo(*pLine, RectPos::Top, eHorAlign, RectVerAlign::Baseline);
858 aPos.Y() -= nNumDist;
859 pNum->MoveTo(aPos);
861 // get horizontal alignment for denominator
862 pLM = pDenom->GetLeftMost();
863 eHorAlign = pLM->GetRectHorAlign();
865 // move denominator to its position
866 aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
867 aPos.Y() += nDenomDist;
868 pDenom->MoveTo(aPos);
870 SmRect::operator = (*pNum);
871 ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
874 void SmBinVerNode::CreateTextFromNode(OUString &rText)
876 SmNode *pNum = GetSubNode(0),
877 *pDenom = GetSubNode(2);
878 pNum->CreateTextFromNode(rText);
879 rText += "over ";
880 pDenom->CreateTextFromNode(rText);
884 const SmNode * SmBinVerNode::GetLeftMost() const
886 return this;
890 namespace {
892 /// @return value of the determinant formed by the two points
893 double Det(const Point &rHeading1, const Point &rHeading2)
895 return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
899 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
900 /// and has the direction vector 'rHeading2'
901 bool IsPointInLine(const Point &rPoint1,
902 const Point &rPoint2, const Point &rHeading2)
904 OSL_ENSURE(rHeading2 != Point(), "Sm : 0 vector");
906 bool bRes = false;
907 static const double eps = 5.0 * DBL_EPSILON;
909 double fLambda;
910 if (labs(rHeading2.X()) > labs(rHeading2.Y()))
912 fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
913 bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
915 else
917 fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
918 bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
921 return bRes;
925 sal_uInt16 GetLineIntersectionPoint(Point &rResult,
926 const Point& rPoint1, const Point &rHeading1,
927 const Point& rPoint2, const Point &rHeading2)
929 OSL_ENSURE(rHeading1 != Point(), "Sm : 0 vector");
930 OSL_ENSURE(rHeading2 != Point(), "Sm : 0 vector");
932 sal_uInt16 nRes = 1;
933 static const double eps = 5.0 * DBL_EPSILON;
935 // are the direction vectors linearly dependent?
936 double fDet = Det(rHeading1, rHeading2);
937 if (fabs(fDet) < eps)
939 nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
940 rResult = nRes ? rPoint1 : Point();
942 else
944 // here we do not pay attention to the computational accuracy
945 // (that would be more complicated and is not really worth it in this case)
946 double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
947 - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
948 / fDet;
949 rResult = Point(rPoint1.X() + static_cast<long>(fLambda * rHeading1.X()),
950 rPoint1.Y() + static_cast<long>(fLambda * rHeading1.Y()));
953 return nRes;
959 SmBinDiagonalNode::SmBinDiagonalNode(const SmToken &rNodeToken)
960 : SmStructureNode(NBINDIAGONAL, rNodeToken, 3)
962 bAscending = false;
966 /// @return position and size of the diagonal line
967 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
968 void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
969 const Point &rDiagPoint, double fAngleDeg) const
972 static const double fPi = 3.1415926535897932384626433;
973 double fAngleRad = fAngleDeg / 180.0 * fPi;
974 long nRectLeft = GetItalicLeft(),
975 nRectRight = GetItalicRight(),
976 nRectTop = GetTop(),
977 nRectBottom = GetBottom();
978 Point aRightHdg (100, 0),
979 aDownHdg (0, 100),
980 aDiagHdg ( static_cast<long>(100.0 * cos(fAngleRad)),
981 static_cast<long>(-100.0 * sin(fAngleRad)) );
983 long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
984 Point aPoint;
985 if (IsAscending())
987 // determine top right corner
988 GetLineIntersectionPoint(aPoint,
989 Point(nRectLeft, nRectTop), aRightHdg,
990 rDiagPoint, aDiagHdg);
991 // is there a point of intersection with the top border?
992 if (aPoint.X() <= nRectRight)
994 nRight = aPoint.X();
995 nTop = nRectTop;
997 else
999 // there has to be a point of intersection with the right border!
1000 GetLineIntersectionPoint(aPoint,
1001 Point(nRectRight, nRectTop), aDownHdg,
1002 rDiagPoint, aDiagHdg);
1004 nRight = nRectRight;
1005 nTop = aPoint.Y();
1008 // determine bottom left corner
1009 GetLineIntersectionPoint(aPoint,
1010 Point(nRectLeft, nRectBottom), aRightHdg,
1011 rDiagPoint, aDiagHdg);
1012 // is there a point of intersection with the bottom border?
1013 if (aPoint.X() >= nRectLeft)
1015 nLeft = aPoint.X();
1016 nBottom = nRectBottom;
1018 else
1020 // there has to be a point of intersection with the left border!
1021 GetLineIntersectionPoint(aPoint,
1022 Point(nRectLeft, nRectTop), aDownHdg,
1023 rDiagPoint, aDiagHdg);
1025 nLeft = nRectLeft;
1026 nBottom = aPoint.Y();
1029 else
1031 // determine top left corner
1032 GetLineIntersectionPoint(aPoint,
1033 Point(nRectLeft, nRectTop), aRightHdg,
1034 rDiagPoint, aDiagHdg);
1035 // is there a point of intersection with the top border?
1036 if (aPoint.X() >= nRectLeft)
1038 nLeft = aPoint.X();
1039 nTop = nRectTop;
1041 else
1043 // there has to be a point of intersection with the left border!
1044 GetLineIntersectionPoint(aPoint,
1045 Point(nRectLeft, nRectTop), aDownHdg,
1046 rDiagPoint, aDiagHdg);
1048 nLeft = nRectLeft;
1049 nTop = aPoint.Y();
1052 // determine bottom right corner
1053 GetLineIntersectionPoint(aPoint,
1054 Point(nRectLeft, nRectBottom), aRightHdg,
1055 rDiagPoint, aDiagHdg);
1056 // is there a point of intersection with the bottom border?
1057 if (aPoint.X() <= nRectRight)
1059 nRight = aPoint.X();
1060 nBottom = nRectBottom;
1062 else
1064 // there has to be a point of intersection with the right border!
1065 GetLineIntersectionPoint(aPoint,
1066 Point(nRectRight, nRectTop), aDownHdg,
1067 rDiagPoint, aDiagHdg);
1069 nRight = nRectRight;
1070 nBottom = aPoint.Y();
1074 rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
1075 rPos.X() = nLeft;
1076 rPos.Y() = nTop;
1080 void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1082 // Both arguments have to get into the SubNodes before the Operator so that clicking
1083 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1084 SmNode *pLeft = GetSubNode(0),
1085 *pRight = GetSubNode(1),
1086 *pLine = GetSubNode(2);
1087 assert(pLeft);
1088 assert(pRight);
1089 assert(pLine && pLine->GetType() == NPOLYLINE);
1091 SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
1092 assert(pOper);
1094 //! some routines being called extract some info from the OutputDevice's
1095 //! font (eg the space to be used for borders OR the font name(!!)).
1096 //! Thus the font should reflect the needs and has to be set!
1097 SmTmpDevice aTmpDev (rDev, true);
1098 aTmpDev.SetFont(GetFont());
1100 pLeft->Arrange(aTmpDev, rFormat);
1101 pRight->Arrange(aTmpDev, rFormat);
1103 // determine implicitly the values (incl. the margin) of the diagonal line
1104 pOper->Arrange(aTmpDev, rFormat);
1106 long nDelta = pOper->GetWidth() * 8 / 10;
1108 // determine TopLeft position from the right argument
1109 Point aPos;
1110 aPos.X() = pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace();
1111 if (IsAscending())
1112 aPos.Y() = pLeft->GetBottom() + nDelta;
1113 else
1114 aPos.Y() = pLeft->GetTop() - nDelta - pRight->GetHeight();
1116 pRight->MoveTo(aPos);
1118 // determine new baseline
1119 long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
1120 : (pLeft->GetTop() + pRight->GetBottom()) / 2;
1121 Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
1122 nTmpBaseline);
1124 SmRect::operator = (*pLeft);
1125 ExtendBy(*pRight, RectCopyMBL::None);
1128 // determine position and size of diagonal line
1129 Size aTmpSize;
1130 GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
1132 // font specialist advised to change the width first
1133 pOper->AdaptToY(aTmpDev, aTmpSize.Height());
1134 pOper->AdaptToX(aTmpDev, aTmpSize.Width());
1135 // and make it active
1136 pOper->Arrange(aTmpDev, rFormat);
1138 pOper->MoveTo(aPos);
1140 ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
1144 /**************************************************************************/
1147 void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1149 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
1150 "Sm: wrong number of subnodes");
1152 SmNode *pBody = GetBody();
1153 assert(pBody);
1155 long nOrigHeight = pBody->GetFont().GetFontSize().Height();
1157 pBody->Arrange(rDev, rFormat);
1159 const SmRect &rBodyRect = pBody->GetRect();
1160 SmRect::operator = (rBodyRect);
1162 // line that separates sub- and supscript rectangles
1163 long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1165 Point aPos;
1166 long nDelta, nDist;
1168 // iterate over all possible sub-/supscripts
1169 SmRect aTmpRect (rBodyRect);
1170 for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
1172 SmSubSup eSubSup = static_cast<SmSubSup>(i);
1173 SmNode *pSubSup = GetSubSup(eSubSup);
1175 if (!pSubSup)
1176 continue;
1178 // switch position of limits if we are in textmode
1179 if (rFormat.IsTextmode() && (GetToken().nGroup & TG::Limit))
1180 switch (eSubSup)
1181 { case CSUB: eSubSup = RSUB; break;
1182 case CSUP: eSubSup = RSUP; break;
1183 default:
1184 break;
1187 // prevent sub-/supscripts from diminishing in size
1188 // (as would be in "a_{1_{2_{3_4}}}")
1189 if (GetFont().GetFontSize().Height() > rFormat.GetBaseSize().Height() / 3)
1191 sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
1192 SIZ_LIMITS : SIZ_INDEX;
1193 Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
1194 pSubSup->SetSize(aFraction);
1197 pSubSup->Arrange(rDev, rFormat);
1199 bool bIsTextmode = rFormat.IsTextmode();
1200 nDist = 0;
1202 //! be sure that CSUB, CSUP are handled before the other cases!
1203 switch (eSubSup)
1204 { case RSUB :
1205 case LSUB :
1206 if (!bIsTextmode)
1207 nDist = nOrigHeight
1208 * rFormat.GetDistance(DIS_SUBSCRIPT) / 100L;
1209 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1210 eSubSup == LSUB ? RectPos::Left : RectPos::Right,
1211 RectHorAlign::Center, RectVerAlign::Bottom);
1212 aPos.Y() += nDist;
1213 nDelta = nDelimLine - aPos.Y();
1214 if (nDelta > 0)
1215 aPos.Y() += nDelta;
1216 break;
1217 case RSUP :
1218 case LSUP :
1219 if (!bIsTextmode)
1220 nDist = nOrigHeight
1221 * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100L;
1222 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1223 eSubSup == LSUP ? RectPos::Left : RectPos::Right,
1224 RectHorAlign::Center, RectVerAlign::Top);
1225 aPos.Y() -= nDist;
1226 nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
1227 if (nDelta > 0)
1228 aPos.Y() -= nDelta;
1229 break;
1230 case CSUB :
1231 if (!bIsTextmode)
1232 nDist = nOrigHeight
1233 * rFormat.GetDistance(DIS_LOWERLIMIT) / 100L;
1234 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Bottom,
1235 RectHorAlign::Center, RectVerAlign::Baseline);
1236 aPos.Y() += nDist;
1237 break;
1238 case CSUP :
1239 if (!bIsTextmode)
1240 nDist = nOrigHeight
1241 * rFormat.GetDistance(DIS_UPPERLIMIT) / 100L;
1242 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Top,
1243 RectHorAlign::Center, RectVerAlign::Baseline);
1244 aPos.Y() -= nDist;
1245 break;
1248 pSubSup->MoveTo(aPos);
1249 ExtendBy(*pSubSup, RectCopyMBL::This, true);
1251 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1252 // will be aligned to
1253 if (eSubSup == CSUB || eSubSup == CSUP)
1254 aTmpRect = *this;
1258 void SmSubSupNode::CreateTextFromNode(OUString &rText)
1260 SmNode *pNode;
1261 GetSubNode(0)->CreateTextFromNode(rText);
1263 if (nullptr != (pNode = GetSubNode(LSUB+1)))
1265 rText += "lsub ";
1266 pNode->CreateTextFromNode(rText);
1268 if (nullptr != (pNode = GetSubNode(LSUP+1)))
1270 rText += "lsup ";
1271 pNode->CreateTextFromNode(rText);
1273 if (nullptr != (pNode = GetSubNode(CSUB+1)))
1275 rText += "csub ";
1276 pNode->CreateTextFromNode(rText);
1278 if (nullptr != (pNode = GetSubNode(CSUP+1)))
1280 rText += "csup ";
1281 pNode->CreateTextFromNode(rText);
1283 if (nullptr != (pNode = GetSubNode(RSUB+1)))
1285 rText = comphelper::string::stripEnd(rText, ' ');
1286 rText += "_";
1287 pNode->CreateTextFromNode(rText);
1289 if (nullptr != (pNode = GetSubNode(RSUP+1)))
1291 rText = comphelper::string::stripEnd(rText, ' ');
1292 rText += "^";
1293 pNode->CreateTextFromNode(rText);
1298 /**************************************************************************/
1300 void SmBraceNode::CreateTextFromNode(OUString &rText)
1302 if (GetScaleMode() == SCALE_HEIGHT)
1303 rText += "left ";
1305 OUString aStr;
1306 OpeningBrace()->CreateTextFromNode(aStr);
1307 aStr = comphelper::string::strip(aStr, ' ');
1308 aStr = comphelper::string::stripStart(aStr, '\\');
1309 if (!aStr.isEmpty())
1311 if (aStr == "divides")
1312 rText += "lline";
1313 else if (aStr == "parallel")
1314 rText += "ldline";
1315 else if (aStr == "<")
1316 rText += "langle";
1317 else
1318 rText += aStr;
1319 rText += " ";
1321 else
1322 rText += "none ";
1324 Body()->CreateTextFromNode(rText);
1325 if (GetScaleMode() == SCALE_HEIGHT)
1326 rText += "right ";
1328 OUString aStr;
1329 ClosingBrace()->CreateTextFromNode(aStr);
1330 aStr = comphelper::string::strip(aStr, ' ');
1331 aStr = comphelper::string::stripStart(aStr, '\\');
1332 if (!aStr.isEmpty())
1334 if (aStr == "divides")
1335 rText += "rline";
1336 else if (aStr == "parallel")
1337 rText += "rdline";
1338 else if (aStr == ">")
1339 rText += "rangle";
1340 else
1341 rText += aStr;
1342 rText += " ";
1344 else
1345 rText += "none ";
1347 rText += " ";
1351 void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1353 SmNode *pLeft = OpeningBrace(),
1354 *pBody = Body(),
1355 *pRight = ClosingBrace();
1356 assert(pLeft);
1357 assert(pBody);
1358 assert(pRight);
1360 pBody->Arrange(rDev, rFormat);
1362 bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
1363 bScale = pBody->GetHeight() > 0 &&
1364 (GetScaleMode() == SCALE_HEIGHT || bIsScaleNormal),
1365 bIsABS = GetToken().eType == TABS;
1367 long nFaceHeight = GetFont().GetFontSize().Height();
1369 // determine oversize in %
1370 sal_uInt16 nPerc = 0;
1371 if (!bIsABS && bScale)
1372 { // in case of oversize braces...
1373 sal_uInt16 nIndex = GetScaleMode() == SCALE_HEIGHT ?
1374 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1375 nPerc = rFormat.GetDistance(nIndex);
1378 // determine the height for the braces
1379 long nBraceHeight;
1380 if (bScale)
1382 nBraceHeight = pBody->GetType() == NBRACEBODY ?
1383 static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
1384 : pBody->GetHeight();
1385 nBraceHeight += 2 * (nBraceHeight * nPerc / 100L);
1387 else
1388 nBraceHeight = nFaceHeight;
1390 // distance to the argument
1391 nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
1392 long nDist = nFaceHeight * nPerc / 100L;
1394 // if wanted, scale the braces to the wanted size
1395 if (bScale)
1397 Size aTmpSize (pLeft->GetFont().GetFontSize());
1398 OSL_ENSURE(pRight->GetFont().GetFontSize() == aTmpSize,
1399 "Sm : different font sizes");
1400 aTmpSize.Width() = std::min(nBraceHeight * 60L / 100L,
1401 rFormat.GetBaseSize().Height() * 3L / 2L);
1402 // correction factor since change from StarMath to OpenSymbol font
1403 // because of the different font width in the FontMetric
1404 aTmpSize.Width() *= 182;
1405 aTmpSize.Width() /= 267;
1407 sal_Unicode cChar = pLeft->GetToken().cMathChar;
1408 if (cChar != MS_LINE && cChar != MS_DLINE &&
1409 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1410 pLeft ->GetFont().SetSize(aTmpSize);
1412 cChar = pRight->GetToken().cMathChar;
1413 if (cChar != MS_LINE && cChar != MS_DLINE &&
1414 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1415 pRight->GetFont().SetSize(aTmpSize);
1417 pLeft ->AdaptToY(rDev, nBraceHeight);
1418 pRight->AdaptToY(rDev, nBraceHeight);
1421 pLeft ->Arrange(rDev, rFormat);
1422 pRight->Arrange(rDev, rFormat);
1424 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1425 RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1427 Point aPos;
1428 aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
1429 aPos.X() -= nDist;
1430 pLeft->MoveTo(aPos);
1432 aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
1433 aPos.X() += nDist;
1434 pRight->MoveTo(aPos);
1436 SmRect::operator = (*pBody);
1437 ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
1441 /**************************************************************************/
1444 void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1446 sal_uInt16 nNumSubNodes = GetNumSubNodes();
1447 if (nNumSubNodes == 0)
1448 return;
1450 // arrange arguments
1451 sal_uInt16 i;
1452 for (i = 0; i < nNumSubNodes; i += 2)
1453 GetSubNode(i)->Arrange(rDev, rFormat);
1455 // build reference rectangle with necessary info for vertical alignment
1456 SmRect aRefRect (*GetSubNode(0));
1457 for (i = 0; i < nNumSubNodes; i += 2)
1459 SmRect aTmpRect (*GetSubNode(i));
1460 Point aPos = aTmpRect.AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
1461 aTmpRect.MoveTo(aPos);
1462 aRefRect.ExtendBy(aTmpRect, RectCopyMBL::Xor);
1465 nBodyHeight = aRefRect.GetHeight();
1467 // scale separators to required height and arrange them
1468 bool bScale = GetScaleMode() == SCALE_HEIGHT || rFormat.IsScaleNormalBrackets();
1469 long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetFontSize().Height();
1470 sal_uInt16 nIndex = GetScaleMode() == SCALE_HEIGHT ?
1471 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1472 sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
1473 if (bScale)
1474 nHeight += 2 * (nHeight * nPerc / 100L);
1475 for (i = 1; i < nNumSubNodes; i += 2)
1477 SmNode *pNode = GetSubNode(i);
1478 pNode->AdaptToY(rDev, nHeight);
1479 pNode->Arrange(rDev, rFormat);
1482 // horizontal distance between argument and brackets or separators
1483 long nDist = GetFont().GetFontSize().Height()
1484 * rFormat.GetDistance(DIS_BRACKETSPACE) / 100L;
1486 SmNode *pLeft = GetSubNode(0);
1487 SmRect::operator = (*pLeft);
1488 for (i = 1; i < nNumSubNodes; i++)
1490 bool bIsSeparator = i % 2 != 0;
1491 RectVerAlign eVerAlign = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1493 SmNode *pRight = GetSubNode(i);
1494 Point aPosX = pRight->AlignTo(*pLeft, RectPos::Right, RectHorAlign::Center, eVerAlign),
1495 aPosY = pRight->AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, eVerAlign);
1496 aPosX.X() += nDist;
1498 pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
1499 ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
1501 pLeft = pRight;
1506 /**************************************************************************/
1509 void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1511 SmNode *pBody = Body(),
1512 *pBrace = Brace(),
1513 *pScript = Script();
1514 assert(pBody);
1515 assert(pBrace);
1516 assert(pScript);
1518 SmTmpDevice aTmpDev (rDev, true);
1519 aTmpDev.SetFont(GetFont());
1521 pBody->Arrange(aTmpDev, rFormat);
1523 // size is the same as for limits for this part
1524 pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
1525 // braces are a bit taller than usually
1526 pBrace ->SetSize( Fraction(3, 2) );
1528 long nItalicWidth = pBody->GetItalicWidth();
1529 if (nItalicWidth > 0)
1530 pBrace->AdaptToX(aTmpDev, nItalicWidth);
1532 pBrace ->Arrange(aTmpDev, rFormat);
1533 pScript->Arrange(aTmpDev, rFormat);
1535 // determine the relative position and the distances between each other
1536 RectPos eRectPos;
1537 long nFontHeight = pBody->GetFont().GetFontSize().Height();
1538 long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
1539 nDistScript = nFontHeight;
1540 if (GetToken().eType == TOVERBRACE)
1542 eRectPos = RectPos::Top;
1543 nDistBody = - nDistBody;
1544 nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
1546 else // TUNDERBRACE
1548 eRectPos = RectPos::Bottom;
1549 nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
1551 nDistBody /= 100;
1552 nDistScript /= 100;
1554 Point aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1555 aPos.Y() += nDistBody;
1556 pBrace->MoveTo(aPos);
1558 aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1559 aPos.Y() += nDistScript;
1560 pScript->MoveTo(aPos);
1562 SmRect::operator = (*pBody);
1563 ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
1567 /**************************************************************************/
1570 SmNode * SmOperNode::GetSymbol()
1572 SmNode *pNode = GetSubNode(0);
1573 assert(pNode);
1575 if (pNode->GetType() == NSUBSUP)
1576 pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
1578 OSL_ENSURE(pNode, "Sm: NULL pointer!");
1579 return pNode;
1583 long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
1584 const SmFormat &rFormat) const
1585 // returns the font height to be used for operator-symbol
1587 long nHeight = GetFont().GetFontSize().Height();
1589 SmTokenType eTmpType = GetToken().eType;
1590 if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
1591 return nHeight;
1593 if (!rFormat.IsTextmode())
1595 // set minimum size ()
1596 nHeight += (nHeight * 20L) / 100L;
1598 nHeight += nHeight
1599 * rFormat.GetDistance(DIS_OPERATORSIZE) / 100L;
1600 nHeight = nHeight * 686L / 845L;
1603 // correct user-defined symbols to match height of sum from used font
1604 if (rSymbol.GetToken().eType == TSPECIAL)
1605 nHeight = nHeight * 845L / 686L;
1607 return nHeight;
1611 void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1613 SmNode *pOper = GetSubNode(0);
1614 SmNode *pBody = GetSubNode(1);
1616 assert(pOper);
1617 assert(pBody);
1619 SmNode *pSymbol = GetSymbol();
1620 pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
1621 pSymbol->GetFont().GetFontSize().Height()));
1623 pBody->Arrange(rDev, rFormat);
1624 bool bDynamicallySized = false;
1625 if (pSymbol->GetToken().eType == TINTD)
1627 long nBodyHeight = pBody->GetHeight();
1628 long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
1629 if (nFontHeight < nBodyHeight)
1631 pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
1632 bDynamicallySized = true;
1635 pOper->Arrange(rDev, rFormat);
1637 long nOrigHeight = GetFont().GetFontSize().Height(),
1638 nDist = nOrigHeight
1639 * rFormat.GetDistance(DIS_OPERATORSPACE) / 100L;
1641 Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
1642 aPos.X() -= nDist;
1643 pOper->MoveTo(aPos);
1645 SmRect::operator = (*pBody);
1646 ExtendBy(*pOper, RectCopyMBL::This);
1650 /**************************************************************************/
1653 void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1654 // set alignment within the entire subtree (including current node)
1656 assert(GetNumSubNodes() == 1);
1658 SmNode *pNode = GetSubNode(0);
1659 assert(pNode);
1661 RectHorAlign eHorAlign = RectHorAlign::Center;
1662 switch (GetToken().eType)
1664 case TALIGNL: eHorAlign = RectHorAlign::Left; break;
1665 case TALIGNC: eHorAlign = RectHorAlign::Center; break;
1666 case TALIGNR: eHorAlign = RectHorAlign::Right; break;
1667 default:
1668 break;
1670 SetRectHorAlign(eHorAlign);
1672 pNode->Arrange(rDev, rFormat);
1674 SmRect::operator = (pNode->GetRect());
1678 /**************************************************************************/
1681 void SmAttributNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1683 SmNode *pAttr = Attribute(),
1684 *pBody = Body();
1685 assert(pBody);
1686 assert(pAttr);
1688 pBody->Arrange(rDev, rFormat);
1690 if (GetScaleMode() == SCALE_WIDTH)
1691 pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
1692 pAttr->Arrange(rDev, rFormat);
1694 // get relative position of attribute
1695 RectVerAlign eVerAlign;
1696 long nDist = 0;
1697 switch (GetToken().eType)
1698 { case TUNDERLINE :
1699 eVerAlign = RectVerAlign::AttributeLo;
1700 break;
1701 case TOVERSTRIKE :
1702 eVerAlign = RectVerAlign::AttributeMid;
1703 break;
1704 default :
1705 eVerAlign = RectVerAlign::AttributeHi;
1706 if (pBody->GetType() == NATTRIBUT)
1707 nDist = GetFont().GetFontSize().Height()
1708 * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100L;
1710 Point aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
1711 aPos.Y() -= nDist;
1712 pAttr->MoveTo(aPos);
1714 SmRect::operator = (*pBody);
1715 ExtendBy(*pAttr, RectCopyMBL::This, true);
1718 void SmFontNode::CreateTextFromNode(OUString &rText)
1720 switch (GetToken().eType)
1722 case TBOLD:
1723 rText += "bold ";
1724 break;
1725 case TNBOLD:
1726 rText += "nbold ";
1727 break;
1728 case TITALIC:
1729 rText += "italic ";
1730 break;
1731 case TNITALIC:
1732 rText += "nitalic ";
1733 break;
1734 case TPHANTOM:
1735 rText += "phantom ";
1736 break;
1737 case TSIZE:
1739 rText += "size ";
1740 switch (nSizeType)
1742 case FontSizeType::PLUS:
1743 rText += "+";
1744 break;
1745 case FontSizeType::MINUS:
1746 rText += "-";
1747 break;
1748 case FontSizeType::MULTIPLY:
1749 rText += "*";
1750 break;
1751 case FontSizeType::DIVIDE:
1752 rText += "/";
1753 break;
1754 case FontSizeType::ABSOLUT:
1755 default:
1756 break;
1758 rText += ::rtl::math::doubleToUString(
1759 static_cast<double>(aFontSize),
1760 rtl_math_StringFormat_Automatic,
1761 rtl_math_DecimalPlaces_Max, '.', true);
1762 rText += " ";
1764 break;
1765 case TBLACK:
1766 rText += "color black ";
1767 break;
1768 case TWHITE:
1769 rText += "color white ";
1770 break;
1771 case TRED:
1772 rText += "color red ";
1773 break;
1774 case TGREEN:
1775 rText += "color green ";
1776 break;
1777 case TBLUE:
1778 rText += "color blue ";
1779 break;
1780 case TCYAN:
1781 rText += "color cyan ";
1782 break;
1783 case TMAGENTA:
1784 rText += "color magenta ";
1785 break;
1786 case TYELLOW:
1787 rText += "color yellow ";
1788 break;
1789 case TTEAL:
1790 rText += "color teal ";
1791 break;
1792 case TSILVER:
1793 rText += "color silver ";
1794 break;
1795 case TGRAY:
1796 rText += "color gray ";
1797 break;
1798 case TMAROON:
1799 rText += "color maroon ";
1800 break;
1801 case TPURPLE:
1802 rText += "color purple ";
1803 break;
1804 case TLIME:
1805 rText += "color lime ";
1806 break;
1807 case TOLIVE:
1808 rText += "color olive ";
1809 break;
1810 case TNAVY:
1811 rText += "color navy ";
1812 break;
1813 case TAQUA:
1814 rText += "color aqua ";
1815 break;
1816 case TFUCHSIA:
1817 rText += "color fuchsia ";
1818 break;
1819 case TSANS:
1820 rText += "font sans ";
1821 break;
1822 case TSERIF:
1823 rText += "font serif ";
1824 break;
1825 case TFIXED:
1826 rText += "font fixed ";
1827 break;
1828 default:
1829 break;
1831 if(GetNumSubNodes() > 1)
1832 GetSubNode(1)->CreateTextFromNode(rText);
1835 void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
1837 //! prepare subnodes first
1838 SmNode::Prepare(rFormat, rDocShell);
1840 int nFnt = -1;
1841 switch (GetToken().eType)
1843 case TFIXED: nFnt = FNT_FIXED; break;
1844 case TSANS: nFnt = FNT_SANS; break;
1845 case TSERIF: nFnt = FNT_SERIF; break;
1846 default:
1847 break;
1849 if (nFnt != -1)
1850 { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
1851 SetFont(GetFont());
1854 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
1855 //! other font nodes (those with lower depth in the tree)
1856 Flags() |= FontChangeMask::Face;
1859 void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1861 SmNode *pNode = GetSubNode(1);
1862 assert(pNode);
1864 switch (GetToken().eType)
1865 { case TSIZE :
1866 pNode->SetFontSize(aFontSize, nSizeType);
1867 break;
1868 case TSANS :
1869 case TSERIF :
1870 case TFIXED :
1871 pNode->SetFont(GetFont());
1872 break;
1873 case TUNKNOWN : break; // no assertion on "font <?> <?>"
1875 case TPHANTOM : SetPhantom(true); break;
1876 case TBOLD : SetAttribut(FontAttribute::Bold); break;
1877 case TITALIC : SetAttribut(FontAttribute::Italic); break;
1878 case TNBOLD : ClearAttribut(FontAttribute::Bold); break;
1879 case TNITALIC : ClearAttribut(FontAttribute::Italic); break;
1881 case TBLACK : SetColor(Color(COL_BLACK)); break;
1882 case TWHITE : SetColor(Color(COL_WHITE)); break;
1883 case TRED : SetColor(Color(COL_LIGHTRED)); break;
1884 case TGREEN : SetColor(Color(COL_GREEN)); break;
1885 case TBLUE : SetColor(Color(COL_LIGHTBLUE)); break;
1886 case TCYAN : SetColor(Color(COL_LIGHTCYAN)); break; // as in Calc
1887 case TMAGENTA : SetColor(Color(COL_LIGHTMAGENTA)); break; // as in Calc
1888 case TYELLOW : SetColor(Color(COL_YELLOW)); break;
1889 case TTEAL : SetColor(Color(COL_CYAN)); break;
1890 case TSILVER : SetColor(Color(COL_LIGHTGRAY)); break;
1891 case TGRAY : SetColor(Color(COL_GRAY)); break;
1892 case TMAROON : SetColor(Color(COL_RED)); break;
1893 case TPURPLE : SetColor(Color(COL_MAGENTA)); break;
1894 case TLIME : SetColor(Color(COL_LIGHTGREEN)); break;
1895 case TOLIVE : SetColor(Color(COL_BROWN)); break;
1896 case TNAVY : SetColor(Color(COL_BLUE)); break;
1897 case TAQUA : SetColor(Color(COL_LIGHTCYAN)); break;
1898 case TFUCHSIA : SetColor(Color(COL_LIGHTMAGENTA)); break;
1900 default:
1901 SAL_WARN("starmath", "unknown case");
1904 pNode->Arrange(rDev, rFormat);
1906 SmRect::operator = (pNode->GetRect());
1910 void SmFontNode::SetSizeParameter(const Fraction& rValue, FontSizeType Type)
1912 nSizeType = Type;
1913 aFontSize = rValue;
1917 /**************************************************************************/
1920 SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
1921 : SmGraphicNode(NPOLYLINE, rNodeToken)
1923 aPoly.SetSize(2);
1924 nWidth = 0;
1928 void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
1930 aToSize.Width() = nNewWidth;
1934 void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
1936 GetFont().FreezeBorderWidth();
1937 aToSize.Height() = nNewHeight;
1941 void SmPolyLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1943 //! some routines being called extract some info from the OutputDevice's
1944 //! font (eg the space to be used for borders OR the font name(!!)).
1945 //! Thus the font should reflect the needs and has to be set!
1946 SmTmpDevice aTmpDev (rDev, true);
1947 aTmpDev.SetFont(GetFont());
1949 long nBorderwidth = GetFont().GetBorderWidth();
1951 // create polygon using both endpoints
1952 OSL_ENSURE(aPoly.GetSize() == 2, "Sm : wrong number of points");
1953 Point aPointA, aPointB;
1954 if (GetToken().eType == TWIDESLASH)
1956 aPointA.X() = nBorderwidth;
1957 aPointA.Y() = aToSize.Height() - nBorderwidth;
1958 aPointB.X() = aToSize.Width() - nBorderwidth;
1959 aPointB.Y() = nBorderwidth;
1961 else
1963 OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
1964 aPointA.X() =
1965 aPointA.Y() = nBorderwidth;
1966 aPointB.X() = aToSize.Width() - nBorderwidth;
1967 aPointB.Y() = aToSize.Height() - nBorderwidth;
1969 aPoly.SetPoint(aPointA, 0);
1970 aPoly.SetPoint(aPointB, 1);
1972 long nThick = GetFont().GetFontSize().Height()
1973 * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L;
1974 nWidth = nThick + 2 * nBorderwidth;
1976 SmRect::operator = (SmRect(aToSize.Width(), aToSize.Height()));
1980 /**************************************************************************/
1982 void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1984 nBodyWidth = nWidth;
1988 void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
1990 // some additional length so that the horizontal
1991 // bar will be positioned above the argument
1992 SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10L);
1996 /**************************************************************************/
1999 void SmRectangleNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
2001 aToSize.Width() = nWidth;
2005 void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
2007 GetFont().FreezeBorderWidth();
2008 aToSize.Height() = nHeight;
2012 void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
2014 long nFontHeight = GetFont().GetFontSize().Height();
2015 long nWidth = aToSize.Width(),
2016 nHeight = aToSize.Height();
2017 if (nHeight == 0)
2018 nHeight = nFontHeight / 30;
2019 if (nWidth == 0)
2020 nWidth = nFontHeight / 3;
2022 SmTmpDevice aTmpDev (rDev, true);
2023 aTmpDev.SetFont(GetFont());
2025 // add some borderspace
2026 sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth();
2027 nHeight += 2 * nTmpBorderWidth;
2029 //! use this method in order to have 'SmRect::HasAlignInfo() == true'
2030 //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
2031 SmRect::operator = (SmRect(nWidth, nHeight));
2035 /**************************************************************************/
2038 SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
2039 : SmVisibleNode(eNodeType, rNodeToken)
2040 , nFontDesc(nFontDescP)
2041 , nSelectionStart(0)
2042 , nSelectionEnd(0)
2046 SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
2047 : SmVisibleNode(NTEXT, rNodeToken)
2048 , nFontDesc(nFontDescP)
2049 , nSelectionStart(0)
2050 , nSelectionEnd(0)
2054 void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2056 SmNode::Prepare(rFormat, rDocShell);
2058 // default setting for horizontal alignment of nodes with TTEXT
2059 // content is as alignl (cannot be done in Arrange since it would
2060 // override the settings made by an SmAlignNode before)
2061 if (TTEXT == GetToken().eType)
2062 SetRectHorAlign( RectHorAlign::Left );
2064 aText = GetToken().aText;
2065 GetFont() = rFormat.GetFont(GetFontDesc());
2067 if (IsItalic( GetFont() ))
2068 Attributes() |= FontAttribute::Italic;
2069 if (IsBold( GetFont() ))
2070 Attributes() |= FontAttribute::Bold;
2072 // special handling for ':' where it is a token on it's own and is likely
2073 // to be used for mathematical notations. (E.g. a:b = 2:3)
2074 // In that case it should not be displayed in italic.
2075 if (GetToken().aText.getLength() == 1 && GetToken().aText[0] == ':')
2076 Attributes() &= ~FontAttribute::Italic;
2080 void SmTextNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2082 PrepareAttributes();
2084 sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
2085 SIZ_FUNCTION : SIZ_TEXT;
2086 GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
2088 SmTmpDevice aTmpDev (rDev, true);
2089 aTmpDev.SetFont(GetFont());
2091 SmRect::operator = (SmRect(aTmpDev, &rFormat, aText, GetFont().GetBorderWidth()));
2094 void SmTextNode::CreateTextFromNode(OUString &rText)
2096 bool bQuoted=false;
2097 if (GetToken().eType == TTEXT)
2099 rText += "\"";
2100 bQuoted=true;
2102 else
2104 SmParser aParseTest;
2105 std::unique_ptr<SmTableNode> pTable(aParseTest.Parse(GetToken().aText));
2106 assert(pTable->GetType() == NTABLE);
2107 bQuoted=true;
2108 if (pTable->GetNumSubNodes() == 1)
2110 SmNode *pResult = pTable->GetSubNode(0);
2111 if ( (pResult->GetType() == NLINE) &&
2112 (pResult->GetNumSubNodes() == 1) )
2114 pResult = pResult->GetSubNode(0);
2115 if (pResult->GetType() == NTEXT)
2116 bQuoted=false;
2120 if ((GetToken().eType == TIDENT) && (GetFontDesc() == FNT_FUNCTION))
2122 //Search for existing functions and remove extraneous keyword
2123 rText += "func ";
2125 else if (bQuoted)
2126 rText += "italic ";
2128 if (bQuoted)
2129 rText += "\"";
2133 rText += GetToken().aText;
2135 if (bQuoted)
2136 rText += "\"";
2137 rText += " ";
2141 void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
2143 rText.append(aText);
2146 void SmTextNode::AdjustFontDesc()
2148 if (GetToken().eType == TTEXT)
2149 nFontDesc = FNT_TEXT;
2150 else if(GetToken().eType == TFUNC)
2151 nFontDesc = FNT_FUNCTION;
2152 else {
2153 SmTokenType nTok;
2154 const SmTokenTableEntry *pEntry = SmParser::GetTokenTableEntry( aText );
2155 if (pEntry && pEntry->nGroup == TG::Function) {
2156 nTok = pEntry->eType;
2157 nFontDesc = FNT_FUNCTION;
2158 } else {
2159 sal_Unicode firstChar = aText[0];
2160 if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',') {
2161 nFontDesc = FNT_NUMBER;
2162 nTok = TNUMBER;
2163 } else if (aText.getLength() > 1) {
2164 nFontDesc = FNT_VARIABLE;
2165 nTok = TIDENT;
2166 } else {
2167 nFontDesc = FNT_VARIABLE;
2168 nTok = TCHARACTER;
2171 SmToken tok = GetToken();
2172 tok.eType = nTok;
2173 SetToken(tok);
2177 sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
2179 //Find the best match in accepted unicode for our private area symbols
2180 static const sal_Unicode aStarMathPrivateToUnicode[] =
2182 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
2183 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
2184 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
2185 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
2186 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
2187 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
2188 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
2189 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
2190 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
2191 0xE0DA, 0x2190, 0x2191, 0x2193
2193 if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
2194 nIn = aStarMathPrivateToUnicode[nIn-0xE080];
2196 //For whatever unicode glyph that equation editor doesn't ship with that
2197 //we have a possible match we can munge it to.
2198 switch (nIn)
2200 case 0x2223:
2201 nIn = '|';
2202 break;
2203 default:
2204 break;
2207 return nIn;
2210 /**************************************************************************/
2212 void SmMatrixNode::CreateTextFromNode(OUString &rText)
2214 rText += "matrix {";
2215 for (sal_uInt16 i = 0; i < mnNumRows; i++)
2217 for (sal_uInt16 j = 0; j < mnNumCols; j++)
2219 SmNode *pNode = GetSubNode(i * mnNumCols + j);
2220 if (pNode)
2221 pNode->CreateTextFromNode(rText);
2222 if (j != mnNumCols-1)
2223 rText += "# ";
2225 if (i != mnNumRows-1)
2226 rText += "## ";
2228 rText = comphelper::string::stripEnd(rText, ' ');
2229 rText += "} ";
2233 void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2235 SmNode *pNode;
2236 sal_uInt16 i, j;
2238 // initialize array that is to hold the maximum widths of all
2239 // elements (subnodes) in that column.
2240 std::vector<long> aColWidth(mnNumCols);
2242 // arrange subnodes and calculate the above arrays contents
2243 sal_uInt16 nNodes = GetNumSubNodes();
2244 for (i = 0; i < nNodes; i++)
2246 sal_uInt16 nIdx = nNodes - 1 - i;
2247 if (nullptr != (pNode = GetSubNode(nIdx)))
2249 pNode->Arrange(rDev, rFormat);
2250 int nCol = nIdx % mnNumCols;
2251 aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
2255 // norm distance from which the following two are calculated
2256 const long nNormDist = 3 * GetFont().GetFontSize().Height();
2258 // define horizontal and vertical minimal distances that separate
2259 // the elements
2260 long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100L,
2261 nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100L;
2263 // build array that holds the leftmost position for each column
2264 std::vector<long> aColLeft(mnNumCols);
2265 long nX = 0;
2266 for (j = 0; j < mnNumCols; j++)
2268 aColLeft[j] = nX;
2269 nX += aColWidth[j] + nHorDist;
2272 SmRect::operator = (SmRect());
2273 for (i = 0; i < mnNumRows; i++)
2275 Point aPos;
2276 SmRect aLineRect;
2277 for (j = 0; j < mnNumCols; j++)
2279 SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
2280 assert(pTmpNode);
2282 const SmRect &rNodeRect = pTmpNode->GetRect();
2284 // align all baselines in that row if possible
2285 aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
2287 // get horizontal alignment
2288 const SmNode *pCoNode = pTmpNode->GetLeftMost();
2289 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
2291 // calculate horizontal position of element depending on column
2292 // and horizontal alignment
2293 switch (eHorAlign)
2294 { case RectHorAlign::Left:
2295 aPos.X() = aColLeft[j];
2296 break;
2297 case RectHorAlign::Center:
2298 aPos.X() = rNodeRect.GetLeft() + aColLeft[j]
2299 + aColWidth[j] / 2
2300 - rNodeRect.GetItalicCenterX();
2301 break;
2302 case RectHorAlign::Right:
2303 aPos.X() = aColLeft[j]
2304 + aColWidth[j] - rNodeRect.GetItalicWidth();
2305 break;
2306 default:
2307 assert(false);
2310 pTmpNode->MoveTo(aPos);
2311 aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
2314 aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
2315 if (i > 0)
2316 aPos.Y() += nVerDist;
2318 // move 'aLineRect' and rectangles in that line to final position
2319 Point aDelta(0, // since horizontal alignment is already done
2320 aPos.Y() - aLineRect.GetTop());
2321 aLineRect.Move(aDelta);
2322 for (j = 0; j < mnNumCols; j++)
2323 if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
2324 pNode->Move(aDelta);
2326 ExtendBy(aLineRect, RectCopyMBL::None);
2331 void SmMatrixNode::SetRowCol(sal_uInt16 nMatrixRows, sal_uInt16 nMatrixCols)
2333 mnNumRows = nMatrixRows;
2334 mnNumCols = nMatrixCols;
2338 const SmNode * SmMatrixNode::GetLeftMost() const
2340 return this;
2344 /**************************************************************************/
2347 SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
2348 : SmSpecialNode(NMATH, rNodeToken, FNT_MATH)
2350 sal_Unicode cChar = GetToken().cMathChar;
2351 if (sal_Unicode('\0') != cChar)
2352 SetText(OUString(cChar));
2355 void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
2357 // Since there is no function to do this, we try to approximate it:
2358 Size aFntSize (GetFont().GetFontSize());
2360 //! however the result is a bit better with 'nWidth' as initial font width
2361 aFntSize.Width() = nWidth;
2362 GetFont().SetSize(aFntSize);
2364 SmTmpDevice aTmpDev (rDev, true);
2365 aTmpDev.SetFont(GetFont());
2367 // get denominator of error factor for width
2368 long nTmpBorderWidth = GetFont().GetBorderWidth();
2369 long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
2371 // scale fontwidth with this error factor
2372 aFntSize.Width() *= nWidth;
2373 aFntSize.Width() /= nDenom ? nDenom : 1;
2375 GetFont().SetSize(aFntSize);
2378 void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
2380 GetFont().FreezeBorderWidth();
2381 Size aFntSize (GetFont().GetFontSize());
2383 // Since we only want to scale the height, we might have
2384 // to determine the font width in order to keep it
2385 if (aFntSize.Width() == 0)
2387 rDev.Push(PushFlags::FONT | PushFlags::MAPMODE);
2388 rDev.SetFont(GetFont());
2389 aFntSize.Width() = rDev.GetFontMetric().GetFontSize().Width();
2390 rDev.Pop();
2392 OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
2394 //! however the result is a bit better with 'nHeight' as initial
2395 //! font height
2396 aFntSize.Height() = nHeight;
2397 GetFont().SetSize(aFntSize);
2399 SmTmpDevice aTmpDev (rDev, true);
2400 aTmpDev.SetFont(GetFont());
2402 // get denominator of error factor for height
2403 long nTmpBorderWidth = GetFont().GetBorderWidth();
2404 long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetHeight();
2406 // scale fontwidth with this error factor
2407 aFntSize.Height() *= nHeight;
2408 aFntSize.Height() /= nDenom ? nDenom : 1;
2410 GetFont().SetSize(aFntSize);
2414 void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2416 SmNode::Prepare(rFormat, rDocShell);
2418 GetFont() = rFormat.GetFont(GetFontDesc());
2419 // use same font size as is used for variables
2420 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2422 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
2423 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
2424 "wrong charset for character from StarMath/OpenSymbol font");
2426 Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
2430 void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2432 const OUString &rText = GetText();
2434 if (rText.isEmpty() || rText[0] == '\0')
2435 { SmRect::operator = (SmRect());
2436 return;
2439 PrepareAttributes();
2441 GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
2443 SmTmpDevice aTmpDev (rDev, true);
2444 aTmpDev.SetFont(GetFont());
2446 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2449 void SmMathSymbolNode::CreateTextFromNode(OUString &rText)
2451 OUString sStr;
2452 sal_Unicode cChar = GetToken().cMathChar;
2453 if (cChar == MS_INT && GetScaleMode() == SCALE_HEIGHT)
2454 sStr = "intd ";
2455 else
2456 MathType::LookupChar(cChar, sStr, 3);
2457 rText += sStr;
2460 void SmRectangleNode::CreateTextFromNode(OUString &rText)
2462 switch (GetToken().eType)
2464 case TUNDERLINE:
2465 rText += "underline ";
2466 break;
2467 case TOVERLINE:
2468 rText += "overline ";
2469 break;
2470 case TOVERSTRIKE:
2471 rText += "overstrike ";
2472 break;
2473 default:
2474 break;
2478 void SmAttributNode::CreateTextFromNode(OUString &rText)
2480 SmNode *pNode;
2481 assert(GetNumSubNodes() == 2);
2482 rText += "{";
2483 sal_Unicode nLast=0;
2484 if (nullptr != (pNode = Attribute()))
2486 OUString aStr;
2487 pNode->CreateTextFromNode(aStr);
2488 if (aStr.getLength() > 1)
2489 rText += aStr;
2490 else
2492 nLast = aStr[0];
2493 switch (nLast)
2495 case MS_BAR: // MACRON
2496 rText += "overline ";
2497 break;
2498 case MS_DOT: // DOT ABOVE
2499 rText += "dot ";
2500 break;
2501 case 0x2dc: // SMALL TILDE
2502 rText += "widetilde ";
2503 break;
2504 case MS_DDOT: // DIAERESIS
2505 rText += "ddot ";
2506 break;
2507 case 0xE082:
2508 break;
2509 case 0xE09B:
2510 case MS_DDDOT: // COMBINING THREE DOTS ABOVE
2511 rText += "dddot ";
2512 break;
2513 case MS_ACUTE: // ACUTE ACCENT
2514 case MS_COMBACUTE: // COMBINING ACUTE ACCENT
2515 rText += "acute ";
2516 break;
2517 case MS_GRAVE: // GRAVE ACCENT
2518 case MS_COMBGRAVE: // COMBINING GRAVE ACCENT
2519 rText += "grave ";
2520 break;
2521 case MS_CHECK: // CARON
2522 case MS_COMBCHECK: // COMBINING CARON
2523 rText += "check ";
2524 break;
2525 case MS_BREVE: // BREVE
2526 case MS_COMBBREVE: // COMBINING BREVE
2527 rText += "breve ";
2528 break;
2529 case MS_CIRCLE: // RING ABOVE
2530 case MS_COMBCIRCLE: // COMBINING RING ABOVE
2531 rText += "circle ";
2532 break;
2533 case MS_RIGHTARROW: // RIGHTWARDS ARROW
2534 case MS_VEC: // COMBINING RIGHT ARROW ABOVE
2535 rText += "vec ";
2536 break;
2537 case MS_TILDE: // TILDE
2538 case MS_COMBTILDE: // COMBINING TILDE
2539 rText += "tilde ";
2540 break;
2541 case MS_HAT: // CIRCUMFLEX ACCENT
2542 case MS_COMBHAT: // COMBINING CIRCUMFLEX ACCENT
2543 rText += "hat ";
2544 break;
2545 case MS_COMBBAR: // COMBINING MACRON
2546 rText += "bar ";
2547 break;
2548 default:
2549 rText += OUStringLiteral1( nLast );
2550 break;
2555 if (nullptr != (pNode = Body()))
2556 pNode->CreateTextFromNode(rText);
2558 rText = comphelper::string::stripEnd(rText, ' ');
2560 if (nLast == 0xE082)
2561 rText += " overbrace {}";
2563 rText += "} ";
2566 /**************************************************************************/
2568 static bool lcl_IsFromGreekSymbolSet( const OUString &rTokenText )
2570 bool bRes = false;
2572 // valid symbol name needs to have a '%' at pos 0 and at least an additional char
2573 if (rTokenText.getLength() > 2 && rTokenText[0] == sal_Unicode('%'))
2575 OUString aName( rTokenText.copy(1) );
2576 SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName );
2577 if (pSymbol && SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
2578 bRes = true;
2581 return bRes;
2585 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc) :
2586 SmTextNode(eNodeType, rNodeToken, _nFontDesc)
2588 bIsFromGreekSymbolSet = lcl_IsFromGreekSymbolSet( rNodeToken.aText );
2592 SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken) :
2593 SmTextNode(NSPECIAL, rNodeToken, FNT_MATH) // default Font isn't always correct!
2595 bIsFromGreekSymbolSet = lcl_IsFromGreekSymbolSet( rNodeToken.aText );
2599 void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2601 SmNode::Prepare(rFormat, rDocShell);
2603 const SmSym *pSym;
2604 SmModule *pp = SM_MOD();
2606 OUString aName(GetToken().aText.copy(1));
2607 if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName( aName )))
2609 sal_UCS4 cChar = pSym->GetCharacter();
2610 OUString aTmp( &cChar, 1 );
2611 SetText( aTmp );
2612 GetFont() = pSym->GetFace();
2614 else
2616 SetText( GetToken().aText );
2617 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2619 // use same font size as is used for variables
2620 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2622 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2623 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2624 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2626 //! see also SmFontStyles::GetStyleName
2627 if (IsItalic( GetFont() ))
2628 SetAttribut(FontAttribute::Italic);
2629 if (IsBold( GetFont() ))
2630 SetAttribut(FontAttribute::Bold);
2632 Flags() |= FontChangeMask::Face;
2634 if (bIsFromGreekSymbolSet)
2636 OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
2637 bool bItalic = false;
2638 sal_Int16 nStyle = rFormat.GetGreekCharStyle();
2639 OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
2640 if (nStyle == 1)
2641 bItalic = true;
2642 else if (nStyle == 2)
2644 const OUString& rTmp(GetText());
2645 if (!rTmp.isEmpty())
2647 static const sal_Unicode cUppercaseAlpha = 0x0391;
2648 static const sal_Unicode cUppercaseOmega = 0x03A9;
2649 sal_Unicode cChar = rTmp[0];
2650 // uppercase letters should be straight and lowercase letters italic
2651 bItalic = !(cUppercaseAlpha <= cChar && cChar <= cUppercaseOmega);
2655 if (bItalic)
2656 Attributes() |= FontAttribute::Italic;
2657 else
2658 Attributes() &= ~FontAttribute::Italic;
2663 void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2665 PrepareAttributes();
2667 SmTmpDevice aTmpDev (rDev, true);
2668 aTmpDev.SetFont(GetFont());
2670 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2673 /**************************************************************************/
2676 void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2678 PrepareAttributes();
2680 SmTmpDevice aTmpDev (rDev, true);
2681 aTmpDev.SetFont(GetFont());
2683 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
2684 GetFont().GetBorderWidth()).AsGlyphRect());
2688 /**************************************************************************/
2691 void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2693 SmNode::Prepare(rFormat, rDocShell);
2695 GetFont().SetColor(COL_GRAY);
2696 Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
2700 void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2702 PrepareAttributes();
2704 SmTmpDevice aTmpDev (rDev, true);
2705 aTmpDev.SetFont(GetFont());
2707 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2711 /**************************************************************************/
2714 void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2716 SmNode::Prepare(rFormat, rDocShell);
2718 GetFont().SetColor(COL_RED);
2719 Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
2720 | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
2724 void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2726 PrepareAttributes();
2728 SmTmpDevice aTmpDev (rDev, true);
2729 aTmpDev.SetFont(GetFont());
2731 const OUString &rText = GetText();
2732 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2736 /**************************************************************************/
2739 void SmBlankNode::IncreaseBy(const SmToken &rToken)
2741 switch(rToken.eType)
2743 case TBLANK: mnNum += 4; break;
2744 case TSBLANK: mnNum += 1; break;
2745 default:
2746 break;
2751 void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2753 SmNode::Prepare(rFormat, rDocShell);
2755 // Here it need/should not be the StarMath font, so that for the character
2756 // used in Arrange a normal (non-clipped) rectangle is generated
2757 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2759 Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
2763 void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2765 SmTmpDevice aTmpDev (rDev, true);
2766 aTmpDev.SetFont(GetFont());
2768 // make distance depend on the font height
2769 // (so that it increases when scaling (e.g. size *2 {a ~ b})
2770 long nDist = GetFont().GetFontSize().Height() / 10L,
2771 nSpace = mnNum * nDist;
2773 // get a SmRect with Baseline and all the bells and whistles
2774 SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
2775 GetFont().GetBorderWidth()));
2777 // and resize it to the requested size
2778 SetItalicSpaces(0, 0);
2779 SetWidth(nSpace);
2782 void SmBlankNode::CreateTextFromNode(OUString &rText)
2784 if (mnNum <= 0)
2785 return;
2786 sal_uInt16 nWide = mnNum / 4;
2787 sal_uInt16 nNarrow = mnNum % 4;
2788 for (sal_uInt16 i = 0; i < nWide; i++)
2789 rText += "~";
2790 for (sal_uInt16 i = 0; i < nNarrow; i++)
2791 rText += "`";
2792 rText += " ";
2796 /**************************************************************************/
2797 //Implementation of all accept methods for SmVisitor
2799 void SmTableNode::Accept(SmVisitor* pVisitor) {
2800 pVisitor->Visit(this);
2803 void SmBraceNode::Accept(SmVisitor* pVisitor) {
2804 pVisitor->Visit(this);
2807 void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
2808 pVisitor->Visit(this);
2811 void SmOperNode::Accept(SmVisitor* pVisitor) {
2812 pVisitor->Visit(this);
2815 void SmAlignNode::Accept(SmVisitor* pVisitor) {
2816 pVisitor->Visit(this);
2819 void SmAttributNode::Accept(SmVisitor* pVisitor) {
2820 pVisitor->Visit(this);
2823 void SmFontNode::Accept(SmVisitor* pVisitor) {
2824 pVisitor->Visit(this);
2827 void SmUnHorNode::Accept(SmVisitor* pVisitor) {
2828 pVisitor->Visit(this);
2831 void SmBinHorNode::Accept(SmVisitor* pVisitor) {
2832 pVisitor->Visit(this);
2835 void SmBinVerNode::Accept(SmVisitor* pVisitor) {
2836 pVisitor->Visit(this);
2839 void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
2840 pVisitor->Visit(this);
2843 void SmSubSupNode::Accept(SmVisitor* pVisitor) {
2844 pVisitor->Visit(this);
2847 void SmMatrixNode::Accept(SmVisitor* pVisitor) {
2848 pVisitor->Visit(this);
2851 void SmPlaceNode::Accept(SmVisitor* pVisitor) {
2852 pVisitor->Visit(this);
2855 void SmTextNode::Accept(SmVisitor* pVisitor) {
2856 pVisitor->Visit(this);
2859 void SmSpecialNode::Accept(SmVisitor* pVisitor) {
2860 pVisitor->Visit(this);
2863 void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
2864 pVisitor->Visit(this);
2867 void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
2868 pVisitor->Visit(this);
2871 void SmBlankNode::Accept(SmVisitor* pVisitor) {
2872 pVisitor->Visit(this);
2875 void SmErrorNode::Accept(SmVisitor* pVisitor) {
2876 pVisitor->Visit(this);
2879 void SmLineNode::Accept(SmVisitor* pVisitor) {
2880 pVisitor->Visit(this);
2883 void SmExpressionNode::Accept(SmVisitor* pVisitor) {
2884 pVisitor->Visit(this);
2887 void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
2888 pVisitor->Visit(this);
2891 void SmRootNode::Accept(SmVisitor* pVisitor) {
2892 pVisitor->Visit(this);
2895 void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
2896 pVisitor->Visit(this);
2899 void SmRectangleNode::Accept(SmVisitor* pVisitor) {
2900 pVisitor->Visit(this);
2903 void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
2904 pVisitor->Visit(this);
2907 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */