Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / starmath / source / node.cxx
blob226e813d0120492dd0570aed87e1eca8f7364a2c
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 <parse.hxx>
23 #include <rect.hxx>
24 #include <symbol.hxx>
25 #include <smmod.hxx>
26 #include "mathtype.hxx"
27 #include "tmpdevice.hxx"
28 #include <visitors.hxx>
30 #include <comphelper/string.hxx>
31 #include <tools/color.hxx>
32 #include <tools/fract.hxx>
33 #include <tools/gen.hxx>
34 #include <vcl/outdev.hxx>
36 #include <cassert>
37 #include <math.h>
38 #include <memory>
39 #include <float.h>
40 #include <vector>
42 namespace {
44 template<typename F>
45 void ForEachNonNull(SmNode *pNode, F && f)
47 size_t nSize = pNode->GetNumSubNodes();
48 for (size_t i = 0; i < nSize; ++i)
50 SmNode *pSubNode = pNode->GetSubNode(i);
51 if (pSubNode != nullptr)
52 f(pSubNode);
58 SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken)
59 : maNodeToken( rNodeToken )
60 , meType( eNodeType )
61 , meScaleMode( SmScaleMode::None )
62 , meRectHorAlign( RectHorAlign::Left )
63 , mnFlags( FontChangeMask::None )
64 , mnAttributes( FontAttribute::None )
65 , mbIsPhantom( false )
66 , mbIsSelected( false )
67 , mnAccIndex( -1 )
68 , mpParentNode( nullptr )
72 SmNode::~SmNode()
76 const SmNode * SmNode::GetLeftMost() const
77 // returns leftmost node of current subtree.
78 //! (this assumes the one with index 0 is always the leftmost subnode
79 //! for the current node).
81 const SmNode *pNode = GetNumSubNodes() > 0 ?
82 GetSubNode(0) : nullptr;
84 return pNode ? pNode->GetLeftMost() : this;
88 void SmNode::SetPhantom(bool bIsPhantomP)
90 if (! (Flags() & FontChangeMask::Phantom))
91 mbIsPhantom = bIsPhantomP;
93 bool b = mbIsPhantom;
94 ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
98 void SmNode::SetColor(const Color& rColor)
100 if (! (Flags() & FontChangeMask::Color))
101 GetFont().SetColor(rColor);
103 ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
107 void SmNode::SetAttribut(FontAttribute nAttrib)
109 if (
110 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
111 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
114 mnAttributes |= nAttrib;
117 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribut(nAttrib);});
121 void SmNode::ClearAttribut(FontAttribute nAttrib)
123 if (
124 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
125 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
128 mnAttributes &= ~nAttrib;
131 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribut(nAttrib);});
135 void SmNode::SetFont(const SmFace &rFace)
137 if (!(Flags() & FontChangeMask::Face))
138 GetFont() = rFace;
140 ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
144 void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
145 //! 'rSize' is in units of pts
147 Size aFntSize;
149 if (!(Flags() & FontChangeMask::Size))
151 Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()),
152 rSize.GetDenominator());
153 long nHeight = static_cast<long>(aVal);
155 aFntSize = GetFont().GetFontSize();
156 aFntSize.setWidth( 0 );
157 switch(nType)
159 case FontSizeType::ABSOLUT:
160 aFntSize.setHeight( nHeight );
161 break;
163 case FontSizeType::PLUS:
164 aFntSize.AdjustHeight(nHeight );
165 break;
167 case FontSizeType::MINUS:
168 aFntSize.AdjustHeight( -nHeight );
169 break;
171 case FontSizeType::MULTIPLY:
172 aFntSize.setHeight( static_cast<long>(Fraction(aFntSize.Height()) * rSize) );
173 break;
175 case FontSizeType::DIVIDE:
176 if (rSize != Fraction(0))
177 aFntSize.setHeight( static_cast<long>(Fraction(aFntSize.Height()) / rSize) );
178 break;
179 default:
180 break;
183 // check the requested size against maximum value
184 static int const nMaxVal = SmPtsTo100th_mm(128);
185 if (aFntSize.Height() > nMaxVal)
186 aFntSize.setHeight( nMaxVal );
188 GetFont().SetSize(aFntSize);
191 ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
195 void SmNode::SetSize(const Fraction &rSize)
197 GetFont() *= rSize;
199 ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
203 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
205 if (!(Flags() & FontChangeMask::HorAlign))
206 meRectHorAlign = eHorAlign;
208 if (bApplyToSubTree)
209 ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
213 void SmNode::PrepareAttributes()
215 GetFont().SetWeight((Attributes() & FontAttribute::Bold) ? WEIGHT_BOLD : WEIGHT_NORMAL);
216 GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
220 void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
222 if (nDepth > 1024)
223 throw std::range_error("parser depth limit");
225 mbIsPhantom = false;
226 mnFlags = FontChangeMask::None;
227 mnAttributes = FontAttribute::None;
229 switch (rFormat.GetHorAlign())
230 { case SmHorAlign::Left: meRectHorAlign = RectHorAlign::Left; break;
231 case SmHorAlign::Center: meRectHorAlign = RectHorAlign::Center; break;
232 case SmHorAlign::Right: 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, nDepth](SmNode *pNode){pNode->Prepare(rFormat, rDocShell, nDepth + 1);});
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);});
254 void SmNode::CreateTextFromNode(OUStringBuffer &rText)
256 auto nSize = GetNumSubNodes();
257 if (nSize > 1)
258 rText.append("{");
259 ForEachNonNull(this, [&rText](SmNode *pNode){pNode->CreateTextFromNode(rText);});
260 if (nSize > 1)
262 rText.stripEnd(' ');
263 rText.append("} ");
267 void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
272 void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
277 const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
278 // returns (first) ** visible ** (sub)node with the tokens text at
279 // position 'nRow', 'nCol'.
280 //! (there should be exactly one such node if any)
282 if ( IsVisible()
283 && nRow == GetToken().nRow
284 && nCol >= GetToken().nCol && nCol < GetToken().nCol + GetToken().aText.getLength())
285 return this;
286 else
288 size_t nNumSubNodes = GetNumSubNodes();
289 for (size_t i = 0; i < nNumSubNodes; ++i)
291 const SmNode *pNode = GetSubNode(i);
293 if (!pNode)
294 continue;
296 const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
297 if (pResult)
298 return pResult;
302 return nullptr;
306 const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
308 long nDist = LONG_MAX;
309 const SmNode *pResult = nullptr;
311 if (IsVisible())
312 pResult = this;
313 else
315 size_t nNumSubNodes = GetNumSubNodes();
316 for (size_t 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 size_t nNumSubNodes = GetNumSubNodes();
359 for (size_t 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, std::default_delete<SmNode>());
381 void SmStructureNode::ClearSubNodes()
383 maSubNodes.clear();
386 void SmStructureNode::SetSubNodes(SmNode *pFirst, SmNode *pSecond, SmNode *pThird)
388 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
389 maSubNodes.resize( nSize );
390 if (pFirst)
391 maSubNodes[0] = pFirst;
392 if (pSecond)
393 maSubNodes[1] = pSecond;
394 if (pThird)
395 maSubNodes[2] = pThird;
397 ClaimPaternity();
400 void SmStructureNode::SetSubNodes(SmNodeArray&& rNodeArray)
402 maSubNodes = std::move(rNodeArray);
403 ClaimPaternity();
406 bool SmStructureNode::IsVisible() const
408 return false;
411 size_t SmStructureNode::GetNumSubNodes() const
413 return maSubNodes.size();
416 SmNode* SmStructureNode::GetSubNode(size_t nIndex)
418 return maSubNodes[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 );
432 void SmStructureNode::ClaimPaternity()
434 ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
437 bool SmVisibleNode::IsVisible() const
439 return true;
442 size_t SmVisibleNode::GetNumSubNodes() const
444 return 0;
447 SmNode * SmVisibleNode::GetSubNode(size_t /*nIndex*/)
449 return nullptr;
452 void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
454 rText.append(GetToken().aText);
457 void SmExpressionNode::CreateTextFromNode(OUStringBuffer &rText)
459 size_t nSize = GetNumSubNodes();
460 if (nSize > 1)
461 rText.append("{");
462 for (size_t i = 0; i < nSize; ++i)
464 SmNode *pNode = GetSubNode(i);
465 if (pNode)
467 pNode->CreateTextFromNode(rText);
468 //Just a bit of foo to make unary +asd -asd +-asd -+asd look nice
469 if (pNode->GetType() == SmNodeType::Math)
470 if ((nSize != 2) || rText.isEmpty() ||
471 (rText[rText.getLength() - 1] != '+' && rText[rText.getLength() - 1] != '-') )
472 rText.append(" ");
476 if (nSize > 1)
478 rText.stripEnd(' ');
479 rText.append("} ");
483 void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
484 // arranges all subnodes in one column
486 SmNode *pNode;
487 size_t nSize = GetNumSubNodes();
489 // make distance depend on font size
490 long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
491 * GetFont().GetFontSize().Height()) / 100L;
493 if (nSize < 1)
494 return;
496 // arrange subnodes and get maximum width of them
497 long nMaxWidth = 0,
498 nTmp;
499 for (size_t i = 0; i < nSize; ++i)
501 if (nullptr != (pNode = GetSubNode(i)))
502 { pNode->Arrange(rDev, rFormat);
503 if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
504 nMaxWidth = nTmp;
508 Point aPos;
509 SmRect::operator = (SmRect(nMaxWidth, 1));
510 for (size_t i = 0; i < nSize; ++i)
512 if (nullptr != (pNode = GetSubNode(i)))
513 { const SmRect &rNodeRect = pNode->GetRect();
514 const SmNode *pCoNode = pNode->GetLeftMost();
515 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
517 aPos = rNodeRect.AlignTo(*this, RectPos::Bottom,
518 eHorAlign, RectVerAlign::Baseline);
519 if (i)
520 aPos.AdjustY(nDist );
521 pNode->MoveTo(aPos);
522 ExtendBy(rNodeRect, nSize > 1 ? RectCopyMBL::None : RectCopyMBL::Arg);
525 // #i972#
526 if (HasBaseline())
527 mnFormulaBaseline = GetBaseline();
528 else
530 SmTmpDevice aTmpDev (rDev, true);
531 aTmpDev.SetFont(GetFont());
533 SmRect aRect(aTmpDev, &rFormat, "a", GetFont().GetBorderWidth());
534 mnFormulaBaseline = GetAlignM();
535 // move from middle position by constant - distance
536 // between middle and baseline for single letter
537 mnFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
541 const SmNode * SmTableNode::GetLeftMost() const
543 return this;
547 long SmTableNode::GetFormulaBaseline() const
549 return mnFormulaBaseline;
553 /**************************************************************************/
556 void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
558 SmNode::Prepare(rFormat, rDocShell, nDepth);
560 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
561 // to the rest of the formula compared to the 'FNT_MATH' font.
562 GetFont() = rFormat.GetFont(FNT_VARIABLE);
563 Flags() |= FontChangeMask::Face;
567 /**************************************************************************/
570 void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
571 // arranges all subnodes in one row with some extra space between
573 SmNode *pNode;
574 size_t nSize = GetNumSubNodes();
575 for (size_t i = 0; i < nSize; ++i)
577 if (nullptr != (pNode = GetSubNode(i)))
578 pNode->Arrange(rDev, rFormat);
581 SmTmpDevice aTmpDev (rDev, true);
582 aTmpDev.SetFont(GetFont());
584 if (nSize < 1)
586 // provide an empty rectangle with alignment parameters for the "current"
587 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
588 // same sub-/supscript positions.)
589 //! be sure to use a character that has explicitly defined HiAttribut
590 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
591 //! 'vec {a}'.
592 SmRect::operator = (SmRect(aTmpDev, &rFormat, "a",
593 GetFont().GetBorderWidth()));
594 // make sure that the rectangle occupies (almost) no space
595 SetWidth(1);
596 SetItalicSpaces(0, 0);
597 return;
600 // make distance depend on font size
601 long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetFontSize().Height()) / 100L;
602 if (!IsUseExtraSpaces())
603 nDist = 0;
605 Point aPos;
606 // copy the first node into LineNode and extend by the others
607 if (nullptr != (pNode = GetSubNode(0)))
608 SmRect::operator = (pNode->GetRect());
610 for (size_t i = 1; i < nSize; ++i)
612 if (nullptr != (pNode = GetSubNode(i)))
614 aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
616 // add horizontal space to the left for each but the first sub node
617 aPos.AdjustX(nDist );
619 pNode->MoveTo(aPos);
620 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.AdjustX(nDist );
665 pNode1->MoveTo(aPos);
666 ExtendBy(*pNode1, RectCopyMBL::Xor);
670 /**************************************************************************/
672 namespace {
674 void lcl_GetHeightVerOffset(const SmRect &rRect,
675 long &rHeight, long &rVerOffset)
676 // calculate height and vertical offset of root sign suitable for 'rRect'
678 rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
679 rHeight = rRect.GetHeight() - rVerOffset;
681 OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
682 OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
686 Point lcl_GetExtraPos(const SmRect &rRootSymbol,
687 const SmRect &rExtra)
689 const Size &rSymSize = rRootSymbol.GetSize();
691 Point aPos = rRootSymbol.GetTopLeft()
692 + Point((rSymSize.Width() * 70) / 100,
693 (rSymSize.Height() * 52) / 100);
695 // from this calculate topleft edge of 'rExtra'
696 aPos.AdjustX( -(rExtra.GetWidth() + rExtra.GetItalicRightSpace()) );
697 aPos.AdjustY( -(rExtra.GetHeight()) );
698 // if there's enough space move a bit less to the right
699 // examples: "nroot i a", "nroot j a"
700 // (it looks better if we don't use italic-spaces here)
701 long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
702 if (aPos.X() > nX)
703 aPos.setX( nX );
705 return aPos;
710 void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
712 //! pExtra needs to have the smaller index than pRootSym in order to
713 //! not to get the root symbol but the pExtra when clicking on it in the
714 //! GraphicWindow. (That is because of the simplicity of the algorithm
715 //! that finds the node corresponding to a mouseclick in the window.)
716 SmNode *pExtra = GetSubNode(0),
717 *pRootSym = GetSubNode(1),
718 *pBody = GetSubNode(2);
719 assert(pRootSym);
720 assert(pBody);
722 pBody->Arrange(rDev, rFormat);
724 long nHeight,
725 nVerOffset;
726 lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset);
727 nHeight += rFormat.GetDistance(DIS_ROOT)
728 * GetFont().GetFontSize().Height() / 100L;
730 // font specialist advised to change the width first
731 pRootSym->AdaptToY(rDev, nHeight);
732 pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
734 pRootSym->Arrange(rDev, rFormat);
736 Point aPos = pRootSym->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, RectVerAlign::Baseline);
737 //! override calculated vertical position
738 aPos.setY( pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom() );
739 aPos.AdjustY( -nVerOffset );
740 pRootSym->MoveTo(aPos);
742 if (pExtra)
743 { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
744 pExtra->Arrange(rDev, rFormat);
746 aPos = lcl_GetExtraPos(*pRootSym, *pExtra);
747 pExtra->MoveTo(aPos);
750 SmRect::operator = (*pBody);
751 ExtendBy(*pRootSym, RectCopyMBL::This);
752 if (pExtra)
753 ExtendBy(*pExtra, RectCopyMBL::This, true);
757 void SmRootNode::CreateTextFromNode(OUStringBuffer &rText)
759 SmNode *pExtra = GetSubNode(0);
760 if (pExtra)
762 rText.append("nroot ");
763 pExtra->CreateTextFromNode(rText);
765 else
766 rText.append("sqrt ");
768 if (!pExtra && GetSubNode(2)->GetNumSubNodes() > 1)
769 rText.append("{ ");
771 GetSubNode(2)->CreateTextFromNode(rText);
773 if (!pExtra && GetSubNode(2)->GetNumSubNodes() > 1)
774 rText.append("} ");
777 /**************************************************************************/
780 void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
782 SmNode *pLeft = LeftOperand(),
783 *pOper = Symbol(),
784 *pRight = RightOperand();
785 assert(pLeft);
786 assert(pOper);
787 assert(pRight);
789 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
791 pLeft ->Arrange(rDev, rFormat);
792 pOper ->Arrange(rDev, rFormat);
793 pRight->Arrange(rDev, rFormat);
795 const SmRect &rOpRect = pOper->GetRect();
797 long nDist = (rOpRect.GetWidth() *
798 rFormat.GetDistance(DIS_HORIZONTAL)) / 100L;
800 SmRect::operator = (*pLeft);
802 Point aPos;
803 aPos = pOper->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
804 aPos.AdjustX(nDist );
805 pOper->MoveTo(aPos);
806 ExtendBy(*pOper, RectCopyMBL::Xor);
808 aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
809 aPos.AdjustX(nDist );
811 pRight->MoveTo(aPos);
812 ExtendBy(*pRight, RectCopyMBL::Xor);
816 /**************************************************************************/
819 void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
821 SmNode *pNum = GetSubNode(0),
822 *pLine = GetSubNode(1),
823 *pDenom = GetSubNode(2);
824 assert(pNum);
825 assert(pLine);
826 assert(pDenom);
828 bool bIsTextmode = rFormat.IsTextmode();
829 if (bIsTextmode)
831 Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
832 pNum ->SetSize(aFraction);
833 pLine ->SetSize(aFraction);
834 pDenom->SetSize(aFraction);
837 pNum ->Arrange(rDev, rFormat);
838 pDenom->Arrange(rDev, rFormat);
840 long nFontHeight = GetFont().GetFontSize().Height(),
841 nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100L,
842 nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L,
843 nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
844 nNumDist = bIsTextmode ? 0 :
845 nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100L,
846 nDenomDist = bIsTextmode ? 0 :
847 nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100L;
849 // font specialist advised to change the width first
850 pLine->AdaptToY(rDev, nThick);
851 pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
852 pLine->Arrange(rDev, rFormat);
854 // get horizontal alignment for numerator
855 const SmNode *pLM = pNum->GetLeftMost();
856 RectHorAlign eHorAlign = pLM->GetRectHorAlign();
858 // move numerator to its position
859 Point aPos = pNum->AlignTo(*pLine, RectPos::Top, eHorAlign, RectVerAlign::Baseline);
860 aPos.AdjustY( -nNumDist );
861 pNum->MoveTo(aPos);
863 // get horizontal alignment for denominator
864 pLM = pDenom->GetLeftMost();
865 eHorAlign = pLM->GetRectHorAlign();
867 // move denominator to its position
868 aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
869 aPos.AdjustY(nDenomDist );
870 pDenom->MoveTo(aPos);
872 SmRect::operator = (*pNum);
873 ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
876 void SmBinVerNode::CreateTextFromNode(OUStringBuffer &rText)
878 SmNode *pNum = GetSubNode(0),
879 *pDenom = GetSubNode(2);
880 pNum->CreateTextFromNode(rText);
881 rText.append("over ");
882 pDenom->CreateTextFromNode(rText);
885 const SmNode * SmBinVerNode::GetLeftMost() const
887 return this;
891 namespace {
893 /// @return value of the determinant formed by the two points
894 double Det(const Point &rHeading1, const Point &rHeading2)
896 return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
900 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
901 /// and has the direction vector 'rHeading2'
902 bool IsPointInLine(const Point &rPoint1,
903 const Point &rPoint2, const Point &rHeading2)
905 assert(rHeading2 != Point());
907 bool bRes = false;
908 static const double eps = 5.0 * DBL_EPSILON;
910 double fLambda;
911 if (labs(rHeading2.X()) > labs(rHeading2.Y()))
913 fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
914 bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
916 else
918 fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
919 bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
922 return bRes;
926 sal_uInt16 GetLineIntersectionPoint(Point &rResult,
927 const Point& rPoint1, const Point &rHeading1,
928 const Point& rPoint2, const Point &rHeading2)
930 assert(rHeading1 != Point());
931 assert(rHeading2 != Point());
933 sal_uInt16 nRes = 1;
934 static const double eps = 5.0 * DBL_EPSILON;
936 // are the direction vectors linearly dependent?
937 double fDet = Det(rHeading1, rHeading2);
938 if (fabs(fDet) < eps)
940 nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
941 rResult = nRes ? rPoint1 : Point();
943 else
945 // here we do not pay attention to the computational accuracy
946 // (that would be more complicated and is not really worth it in this case)
947 double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
948 - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
949 / fDet;
950 rResult = Point(rPoint1.X() + static_cast<long>(fLambda * rHeading1.X()),
951 rPoint1.Y() + static_cast<long>(fLambda * rHeading1.Y()));
954 return nRes;
960 /// @return position and size of the diagonal line
961 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
962 void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
963 const Point &rDiagPoint, double fAngleDeg) const
966 static const double fPi = 3.1415926535897932384626433;
967 double fAngleRad = fAngleDeg / 180.0 * fPi;
968 long nRectLeft = GetItalicLeft(),
969 nRectRight = GetItalicRight(),
970 nRectTop = GetTop(),
971 nRectBottom = GetBottom();
972 Point aRightHdg (100, 0),
973 aDownHdg (0, 100),
974 aDiagHdg ( static_cast<long>(100.0 * cos(fAngleRad)),
975 static_cast<long>(-100.0 * sin(fAngleRad)) );
977 long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
978 Point aPoint;
979 if (IsAscending())
981 // determine top right corner
982 GetLineIntersectionPoint(aPoint,
983 Point(nRectLeft, nRectTop), aRightHdg,
984 rDiagPoint, aDiagHdg);
985 // is there a point of intersection with the top border?
986 if (aPoint.X() <= nRectRight)
988 nRight = aPoint.X();
989 nTop = nRectTop;
991 else
993 // there has to be a point of intersection with the right border!
994 GetLineIntersectionPoint(aPoint,
995 Point(nRectRight, nRectTop), aDownHdg,
996 rDiagPoint, aDiagHdg);
998 nRight = nRectRight;
999 nTop = aPoint.Y();
1002 // determine bottom left corner
1003 GetLineIntersectionPoint(aPoint,
1004 Point(nRectLeft, nRectBottom), aRightHdg,
1005 rDiagPoint, aDiagHdg);
1006 // is there a point of intersection with the bottom border?
1007 if (aPoint.X() >= nRectLeft)
1009 nLeft = aPoint.X();
1010 nBottom = nRectBottom;
1012 else
1014 // there has to be a point of intersection with the left border!
1015 GetLineIntersectionPoint(aPoint,
1016 Point(nRectLeft, nRectTop), aDownHdg,
1017 rDiagPoint, aDiagHdg);
1019 nLeft = nRectLeft;
1020 nBottom = aPoint.Y();
1023 else
1025 // determine top left corner
1026 GetLineIntersectionPoint(aPoint,
1027 Point(nRectLeft, nRectTop), aRightHdg,
1028 rDiagPoint, aDiagHdg);
1029 // is there a point of intersection with the top border?
1030 if (aPoint.X() >= nRectLeft)
1032 nLeft = aPoint.X();
1033 nTop = nRectTop;
1035 else
1037 // there has to be a point of intersection with the left border!
1038 GetLineIntersectionPoint(aPoint,
1039 Point(nRectLeft, nRectTop), aDownHdg,
1040 rDiagPoint, aDiagHdg);
1042 nLeft = nRectLeft;
1043 nTop = aPoint.Y();
1046 // determine bottom right corner
1047 GetLineIntersectionPoint(aPoint,
1048 Point(nRectLeft, nRectBottom), aRightHdg,
1049 rDiagPoint, aDiagHdg);
1050 // is there a point of intersection with the bottom border?
1051 if (aPoint.X() <= nRectRight)
1053 nRight = aPoint.X();
1054 nBottom = nRectBottom;
1056 else
1058 // there has to be a point of intersection with the right border!
1059 GetLineIntersectionPoint(aPoint,
1060 Point(nRectRight, nRectTop), aDownHdg,
1061 rDiagPoint, aDiagHdg);
1063 nRight = nRectRight;
1064 nBottom = aPoint.Y();
1068 rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
1069 rPos.setX( nLeft );
1070 rPos.setY( nTop );
1074 void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1076 // Both arguments have to get into the SubNodes before the Operator so that clicking
1077 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1078 SmNode *pLeft = GetSubNode(0),
1079 *pRight = GetSubNode(1),
1080 *pLine = GetSubNode(2);
1081 assert(pLeft);
1082 assert(pRight);
1083 assert(pLine && pLine->GetType() == SmNodeType::PolyLine);
1085 SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
1086 assert(pOper);
1088 //! some routines being called extract some info from the OutputDevice's
1089 //! font (eg the space to be used for borders OR the font name(!!)).
1090 //! Thus the font should reflect the needs and has to be set!
1091 SmTmpDevice aTmpDev (rDev, true);
1092 aTmpDev.SetFont(GetFont());
1094 pLeft->Arrange(aTmpDev, rFormat);
1095 pRight->Arrange(aTmpDev, rFormat);
1097 // determine implicitly the values (incl. the margin) of the diagonal line
1098 pOper->Arrange(aTmpDev, rFormat);
1100 long nDelta = pOper->GetWidth() * 8 / 10;
1102 // determine TopLeft position from the right argument
1103 Point aPos;
1104 aPos.setX( pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace() );
1105 if (IsAscending())
1106 aPos.setY( pLeft->GetBottom() + nDelta );
1107 else
1108 aPos.setY( pLeft->GetTop() - nDelta - pRight->GetHeight() );
1110 pRight->MoveTo(aPos);
1112 // determine new baseline
1113 long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
1114 : (pLeft->GetTop() + pRight->GetBottom()) / 2;
1115 Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
1116 nTmpBaseline);
1118 SmRect::operator = (*pLeft);
1119 ExtendBy(*pRight, RectCopyMBL::None);
1122 // determine position and size of diagonal line
1123 Size aTmpSize;
1124 GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
1126 // font specialist advised to change the width first
1127 pOper->AdaptToY(aTmpDev, aTmpSize.Height());
1128 pOper->AdaptToX(aTmpDev, aTmpSize.Width());
1129 // and make it active
1130 pOper->Arrange(aTmpDev, rFormat);
1132 pOper->MoveTo(aPos);
1134 ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
1138 /**************************************************************************/
1141 void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1143 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
1144 "Sm: wrong number of subnodes");
1146 SmNode *pBody = GetBody();
1147 assert(pBody);
1149 long nOrigHeight = pBody->GetFont().GetFontSize().Height();
1151 pBody->Arrange(rDev, rFormat);
1153 const SmRect &rBodyRect = pBody->GetRect();
1154 SmRect::operator = (rBodyRect);
1156 // line that separates sub- and supscript rectangles
1157 long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1159 Point aPos;
1160 long nDelta, nDist;
1162 // iterate over all possible sub-/supscripts
1163 SmRect aTmpRect (rBodyRect);
1164 for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
1166 SmSubSup eSubSup = static_cast<SmSubSup>(i);
1167 SmNode *pSubSup = GetSubSup(eSubSup);
1169 if (!pSubSup)
1170 continue;
1172 // switch position of limits if we are in textmode
1173 if (rFormat.IsTextmode() && (GetToken().nGroup & TG::Limit))
1174 switch (eSubSup)
1175 { case CSUB: eSubSup = RSUB; break;
1176 case CSUP: eSubSup = RSUP; break;
1177 default:
1178 break;
1181 // prevent sub-/supscripts from diminishing in size
1182 // (as would be in "a_{1_{2_{3_4}}}")
1183 if (GetFont().GetFontSize().Height() > rFormat.GetBaseSize().Height() / 3)
1185 sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
1186 SIZ_LIMITS : SIZ_INDEX;
1187 Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
1188 pSubSup->SetSize(aFraction);
1191 pSubSup->Arrange(rDev, rFormat);
1193 bool bIsTextmode = rFormat.IsTextmode();
1194 nDist = 0;
1196 //! be sure that CSUB, CSUP are handled before the other cases!
1197 switch (eSubSup)
1198 { case RSUB :
1199 case LSUB :
1200 if (!bIsTextmode)
1201 nDist = nOrigHeight
1202 * rFormat.GetDistance(DIS_SUBSCRIPT) / 100L;
1203 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1204 eSubSup == LSUB ? RectPos::Left : RectPos::Right,
1205 RectHorAlign::Center, RectVerAlign::Bottom);
1206 aPos.AdjustY(nDist );
1207 nDelta = nDelimLine - aPos.Y();
1208 if (nDelta > 0)
1209 aPos.AdjustY(nDelta );
1210 break;
1211 case RSUP :
1212 case LSUP :
1213 if (!bIsTextmode)
1214 nDist = nOrigHeight
1215 * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100L;
1216 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1217 eSubSup == LSUP ? RectPos::Left : RectPos::Right,
1218 RectHorAlign::Center, RectVerAlign::Top);
1219 aPos.AdjustY( -nDist );
1220 nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
1221 if (nDelta > 0)
1222 aPos.AdjustY( -nDelta );
1223 break;
1224 case CSUB :
1225 if (!bIsTextmode)
1226 nDist = nOrigHeight
1227 * rFormat.GetDistance(DIS_LOWERLIMIT) / 100L;
1228 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Bottom,
1229 RectHorAlign::Center, RectVerAlign::Baseline);
1230 aPos.AdjustY(nDist );
1231 break;
1232 case CSUP :
1233 if (!bIsTextmode)
1234 nDist = nOrigHeight
1235 * rFormat.GetDistance(DIS_UPPERLIMIT) / 100L;
1236 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Top,
1237 RectHorAlign::Center, RectVerAlign::Baseline);
1238 aPos.AdjustY( -nDist );
1239 break;
1242 pSubSup->MoveTo(aPos);
1243 ExtendBy(*pSubSup, RectCopyMBL::This, true);
1245 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1246 // will be aligned to
1247 if (eSubSup == CSUB || eSubSup == CSUP)
1248 aTmpRect = *this;
1252 void SmSubSupNode::CreateTextFromNode(OUStringBuffer &rText)
1254 SmNode *pNode;
1255 GetSubNode(0)->CreateTextFromNode(rText);
1257 if (nullptr != (pNode = GetSubNode(LSUB+1)))
1259 rText.append("lsub ");
1260 pNode->CreateTextFromNode(rText);
1262 if (nullptr != (pNode = GetSubNode(LSUP+1)))
1264 rText.append("lsup ");
1265 pNode->CreateTextFromNode(rText);
1267 if (nullptr != (pNode = GetSubNode(CSUB+1)))
1269 rText.append("csub ");
1270 pNode->CreateTextFromNode(rText);
1272 if (nullptr != (pNode = GetSubNode(CSUP+1)))
1274 rText.append("csup ");
1275 pNode->CreateTextFromNode(rText);
1277 if (nullptr != (pNode = GetSubNode(RSUB+1)))
1279 rText.stripEnd(' ');
1280 rText.append("_");
1281 pNode->CreateTextFromNode(rText);
1283 if (nullptr != (pNode = GetSubNode(RSUP+1)))
1285 rText.stripEnd(' ');
1286 rText.append("^");
1287 pNode->CreateTextFromNode(rText);
1292 /**************************************************************************/
1294 void SmBraceNode::CreateTextFromNode(OUStringBuffer &rText)
1296 if (GetScaleMode() == SmScaleMode::Height)
1297 rText.append("left ");
1299 OUStringBuffer aStrBuf;
1300 OpeningBrace()->CreateTextFromNode(aStrBuf);
1301 OUString aStr = aStrBuf.makeStringAndClear();
1302 aStr = comphelper::string::strip(aStr, ' ');
1303 aStr = comphelper::string::stripStart(aStr, '\\');
1304 if (!aStr.isEmpty())
1306 if (aStr == "divides")
1307 rText.append("lline");
1308 else if (aStr == "parallel")
1309 rText.append("ldline");
1310 else if (aStr == "<")
1311 rText.append("langle");
1312 else
1313 rText.append(aStr);
1314 rText.append(" ");
1316 else
1317 rText.append("none ");
1319 Body()->CreateTextFromNode(rText);
1320 if (GetScaleMode() == SmScaleMode::Height)
1321 rText.append("right ");
1323 OUStringBuffer aStrBuf;
1324 ClosingBrace()->CreateTextFromNode(aStrBuf);
1325 OUString aStr = aStrBuf.makeStringAndClear();
1326 aStr = comphelper::string::strip(aStr, ' ');
1327 aStr = comphelper::string::stripStart(aStr, '\\');
1328 if (!aStr.isEmpty())
1330 if (aStr == "divides")
1331 rText.append("rline");
1332 else if (aStr == "parallel")
1333 rText.append("rdline");
1334 else if (aStr == ">")
1335 rText.append("rangle");
1336 else
1337 rText.append(aStr);
1338 rText.append(" ");
1340 else
1341 rText.append("none ");
1343 rText.append(" ");
1346 void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1348 SmNode *pLeft = OpeningBrace(),
1349 *pBody = Body(),
1350 *pRight = ClosingBrace();
1351 assert(pLeft);
1352 assert(pBody);
1353 assert(pRight);
1355 pBody->Arrange(rDev, rFormat);
1357 bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
1358 bScale = pBody->GetHeight() > 0 &&
1359 (GetScaleMode() == SmScaleMode::Height || bIsScaleNormal),
1360 bIsABS = GetToken().eType == TABS;
1362 long nFaceHeight = GetFont().GetFontSize().Height();
1364 // determine oversize in %
1365 sal_uInt16 nPerc = 0;
1366 if (!bIsABS && bScale)
1367 { // in case of oversize braces...
1368 sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
1369 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1370 nPerc = rFormat.GetDistance(nIndex);
1373 // determine the height for the braces
1374 long nBraceHeight;
1375 if (bScale)
1377 nBraceHeight = pBody->GetType() == SmNodeType::Bracebody ?
1378 static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
1379 : pBody->GetHeight();
1380 nBraceHeight += 2 * (nBraceHeight * nPerc / 100L);
1382 else
1383 nBraceHeight = nFaceHeight;
1385 // distance to the argument
1386 nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
1387 long nDist = nFaceHeight * nPerc / 100L;
1389 // if wanted, scale the braces to the wanted size
1390 if (bScale)
1392 Size aTmpSize (pLeft->GetFont().GetFontSize());
1393 OSL_ENSURE(pRight->GetFont().GetFontSize() == aTmpSize,
1394 "Sm : different font sizes");
1395 aTmpSize.setWidth( std::min(nBraceHeight * 60L / 100L,
1396 rFormat.GetBaseSize().Height() * 3L / 2L) );
1397 // correction factor since change from StarMath to OpenSymbol font
1398 // because of the different font width in the FontMetric
1399 aTmpSize.setWidth( aTmpSize.Width() * 182 );
1400 aTmpSize.setWidth( aTmpSize.Width() / 267 );
1402 sal_Unicode cChar = pLeft->GetToken().cMathChar;
1403 if (cChar != MS_LINE && cChar != MS_DLINE &&
1404 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1405 pLeft ->GetFont().SetSize(aTmpSize);
1407 cChar = pRight->GetToken().cMathChar;
1408 if (cChar != MS_LINE && cChar != MS_DLINE &&
1409 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1410 pRight->GetFont().SetSize(aTmpSize);
1412 pLeft ->AdaptToY(rDev, nBraceHeight);
1413 pRight->AdaptToY(rDev, nBraceHeight);
1416 pLeft ->Arrange(rDev, rFormat);
1417 pRight->Arrange(rDev, rFormat);
1419 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1420 RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1422 Point aPos;
1423 aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
1424 aPos.AdjustX( -nDist );
1425 pLeft->MoveTo(aPos);
1427 aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
1428 aPos.AdjustX(nDist );
1429 pRight->MoveTo(aPos);
1431 SmRect::operator = (*pBody);
1432 ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
1436 /**************************************************************************/
1439 void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1441 size_t nNumSubNodes = GetNumSubNodes();
1442 if (nNumSubNodes == 0)
1443 return;
1445 // arrange arguments
1446 for (size_t i = 0; i < nNumSubNodes; i += 2)
1447 GetSubNode(i)->Arrange(rDev, rFormat);
1449 // build reference rectangle with necessary info for vertical alignment
1450 SmRect aRefRect (*GetSubNode(0));
1451 for (size_t i = 0; i < nNumSubNodes; i += 2)
1453 SmRect aTmpRect (*GetSubNode(i));
1454 Point aPos = aTmpRect.AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
1455 aTmpRect.MoveTo(aPos);
1456 aRefRect.ExtendBy(aTmpRect, RectCopyMBL::Xor);
1459 mnBodyHeight = aRefRect.GetHeight();
1461 // scale separators to required height and arrange them
1462 bool bScale = GetScaleMode() == SmScaleMode::Height || rFormat.IsScaleNormalBrackets();
1463 long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetFontSize().Height();
1464 sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
1465 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1466 sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
1467 if (bScale)
1468 nHeight += 2 * (nHeight * nPerc / 100L);
1469 for (size_t i = 1; i < nNumSubNodes; i += 2)
1471 SmNode *pNode = GetSubNode(i);
1472 pNode->AdaptToY(rDev, nHeight);
1473 pNode->Arrange(rDev, rFormat);
1476 // horizontal distance between argument and brackets or separators
1477 long nDist = GetFont().GetFontSize().Height()
1478 * rFormat.GetDistance(DIS_BRACKETSPACE) / 100L;
1480 SmNode *pLeft = GetSubNode(0);
1481 SmRect::operator = (*pLeft);
1482 for (size_t i = 1; i < nNumSubNodes; ++i)
1484 bool bIsSeparator = i % 2 != 0;
1485 RectVerAlign eVerAlign = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1487 SmNode *pRight = GetSubNode(i);
1488 Point aPosX = pRight->AlignTo(*pLeft, RectPos::Right, RectHorAlign::Center, eVerAlign),
1489 aPosY = pRight->AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, eVerAlign);
1490 aPosX.AdjustX(nDist );
1492 pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
1493 ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
1495 pLeft = pRight;
1500 /**************************************************************************/
1503 void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1505 SmNode *pBody = Body(),
1506 *pBrace = Brace(),
1507 *pScript = Script();
1508 assert(pBody);
1509 assert(pBrace);
1510 assert(pScript);
1512 SmTmpDevice aTmpDev (rDev, true);
1513 aTmpDev.SetFont(GetFont());
1515 pBody->Arrange(aTmpDev, rFormat);
1517 // size is the same as for limits for this part
1518 pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
1519 // braces are a bit taller than usually
1520 pBrace ->SetSize( Fraction(3, 2) );
1522 long nItalicWidth = pBody->GetItalicWidth();
1523 if (nItalicWidth > 0)
1524 pBrace->AdaptToX(aTmpDev, nItalicWidth);
1526 pBrace ->Arrange(aTmpDev, rFormat);
1527 pScript->Arrange(aTmpDev, rFormat);
1529 // determine the relative position and the distances between each other
1530 RectPos eRectPos;
1531 long nFontHeight = pBody->GetFont().GetFontSize().Height();
1532 long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
1533 nDistScript = nFontHeight;
1534 if (GetToken().eType == TOVERBRACE)
1536 eRectPos = RectPos::Top;
1537 nDistBody = - nDistBody;
1538 nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
1540 else // TUNDERBRACE
1542 eRectPos = RectPos::Bottom;
1543 nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
1545 nDistBody /= 100;
1546 nDistScript /= 100;
1548 Point aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1549 aPos.AdjustY(nDistBody );
1550 pBrace->MoveTo(aPos);
1552 aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1553 aPos.AdjustY(nDistScript );
1554 pScript->MoveTo(aPos);
1556 SmRect::operator = (*pBody);
1557 ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
1561 /**************************************************************************/
1564 SmNode * SmOperNode::GetSymbol()
1566 SmNode *pNode = GetSubNode(0);
1567 assert(pNode);
1569 if (pNode->GetType() == SmNodeType::SubSup)
1570 pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
1572 OSL_ENSURE(pNode, "Sm: NULL pointer!");
1573 return pNode;
1577 long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
1578 const SmFormat &rFormat) const
1579 // returns the font height to be used for operator-symbol
1581 long nHeight = GetFont().GetFontSize().Height();
1583 SmTokenType eTmpType = GetToken().eType;
1584 if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
1585 return nHeight;
1587 if (!rFormat.IsTextmode())
1589 // set minimum size ()
1590 nHeight += (nHeight * 20L) / 100L;
1592 nHeight += nHeight
1593 * rFormat.GetDistance(DIS_OPERATORSIZE) / 100L;
1594 nHeight = nHeight * 686L / 845L;
1597 // correct user-defined symbols to match height of sum from used font
1598 if (rSymbol.GetToken().eType == TSPECIAL)
1599 nHeight = nHeight * 845L / 686L;
1601 return nHeight;
1605 void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1607 SmNode *pOper = GetSubNode(0);
1608 SmNode *pBody = GetSubNode(1);
1610 assert(pOper);
1611 assert(pBody);
1613 SmNode *pSymbol = GetSymbol();
1614 pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
1615 pSymbol->GetFont().GetFontSize().Height()));
1617 pBody->Arrange(rDev, rFormat);
1618 bool bDynamicallySized = false;
1619 if (pSymbol->GetToken().eType == TINTD)
1621 long nBodyHeight = pBody->GetHeight();
1622 long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
1623 if (nFontHeight < nBodyHeight)
1625 pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
1626 bDynamicallySized = true;
1629 pOper->Arrange(rDev, rFormat);
1631 long nOrigHeight = GetFont().GetFontSize().Height(),
1632 nDist = nOrigHeight
1633 * rFormat.GetDistance(DIS_OPERATORSPACE) / 100L;
1635 Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
1636 aPos.AdjustX( -nDist );
1637 pOper->MoveTo(aPos);
1639 SmRect::operator = (*pBody);
1640 ExtendBy(*pOper, RectCopyMBL::This);
1644 /**************************************************************************/
1647 void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1648 // set alignment within the entire subtree (including current node)
1650 assert(GetNumSubNodes() == 1);
1652 SmNode *pNode = GetSubNode(0);
1653 assert(pNode);
1655 RectHorAlign eHorAlign = RectHorAlign::Center;
1656 switch (GetToken().eType)
1658 case TALIGNL: eHorAlign = RectHorAlign::Left; break;
1659 case TALIGNC: eHorAlign = RectHorAlign::Center; break;
1660 case TALIGNR: eHorAlign = RectHorAlign::Right; break;
1661 default:
1662 break;
1664 SetRectHorAlign(eHorAlign);
1666 pNode->Arrange(rDev, rFormat);
1668 SmRect::operator = (pNode->GetRect());
1672 /**************************************************************************/
1675 void SmAttributNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1677 SmNode *pAttr = Attribute(),
1678 *pBody = Body();
1679 assert(pBody);
1680 assert(pAttr);
1682 pBody->Arrange(rDev, rFormat);
1684 if (GetScaleMode() == SmScaleMode::Width)
1685 pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
1686 pAttr->Arrange(rDev, rFormat);
1688 // get relative position of attribute
1689 RectVerAlign eVerAlign;
1690 long nDist = 0;
1691 switch (GetToken().eType)
1692 { case TUNDERLINE :
1693 eVerAlign = RectVerAlign::AttributeLo;
1694 break;
1695 case TOVERSTRIKE :
1696 eVerAlign = RectVerAlign::AttributeMid;
1697 break;
1698 default :
1699 eVerAlign = RectVerAlign::AttributeHi;
1700 if (pBody->GetType() == SmNodeType::Attribut)
1701 nDist = GetFont().GetFontSize().Height()
1702 * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100L;
1704 Point aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
1705 aPos.AdjustY( -nDist );
1706 pAttr->MoveTo(aPos);
1708 SmRect::operator = (*pBody);
1709 ExtendBy(*pAttr, RectCopyMBL::This, true);
1712 void SmFontNode::CreateTextFromNode(OUStringBuffer &rText)
1714 rText.append("{");
1716 switch (GetToken().eType)
1718 case TBOLD:
1719 rText.append("bold ");
1720 break;
1721 case TNBOLD:
1722 rText.append("nbold ");
1723 break;
1724 case TITALIC:
1725 rText.append("italic ");
1726 break;
1727 case TNITALIC:
1728 rText.append("nitalic ");
1729 break;
1730 case TPHANTOM:
1731 rText.append("phantom ");
1732 break;
1733 case TSIZE:
1735 rText.append("size ");
1736 switch (meSizeType)
1738 case FontSizeType::PLUS:
1739 rText.append("+");
1740 break;
1741 case FontSizeType::MINUS:
1742 rText.append("-");
1743 break;
1744 case FontSizeType::MULTIPLY:
1745 rText.append("*");
1746 break;
1747 case FontSizeType::DIVIDE:
1748 rText.append("/");
1749 break;
1750 case FontSizeType::ABSOLUT:
1751 default:
1752 break;
1754 rText.append(::rtl::math::doubleToUString(
1755 static_cast<double>(maFontSize),
1756 rtl_math_StringFormat_Automatic,
1757 rtl_math_DecimalPlaces_Max, '.', true));
1758 rText.append(" ");
1760 break;
1761 case TBLACK:
1762 rText.append("color black ");
1763 break;
1764 case TWHITE:
1765 rText.append("color white ");
1766 break;
1767 case TRED:
1768 rText.append("color red ");
1769 break;
1770 case TGREEN:
1771 rText.append("color green ");
1772 break;
1773 case TBLUE:
1774 rText.append("color blue ");
1775 break;
1776 case TCYAN:
1777 rText.append("color cyan ");
1778 break;
1779 case TMAGENTA:
1780 rText.append("color magenta ");
1781 break;
1782 case TYELLOW:
1783 rText.append("color yellow ");
1784 break;
1785 case TTEAL:
1786 rText.append("color teal ");
1787 break;
1788 case TSILVER:
1789 rText.append("color silver ");
1790 break;
1791 case TGRAY:
1792 rText.append("color gray ");
1793 break;
1794 case TMAROON:
1795 rText.append("color maroon ");
1796 break;
1797 case TPURPLE:
1798 rText.append("color purple ");
1799 break;
1800 case TLIME:
1801 rText.append("color lime ");
1802 break;
1803 case TOLIVE:
1804 rText.append("color olive ");
1805 break;
1806 case TNAVY:
1807 rText.append("color navy ");
1808 break;
1809 case TAQUA:
1810 rText.append("color aqua ");
1811 break;
1812 case TFUCHSIA:
1813 rText.append("color fuchsia ");
1814 break;
1815 case TSANS:
1816 rText.append("font sans ");
1817 break;
1818 case TSERIF:
1819 rText.append("font serif ");
1820 break;
1821 case TFIXED:
1822 rText.append("font fixed ");
1823 break;
1824 default:
1825 break;
1827 if (GetNumSubNodes() > 1)
1828 GetSubNode(1)->CreateTextFromNode(rText);
1830 rText.stripEnd(' ');
1831 rText.append("} ");
1834 void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1836 //! prepare subnodes first
1837 SmNode::Prepare(rFormat, rDocShell, nDepth);
1839 int nFnt = -1;
1840 switch (GetToken().eType)
1842 case TFIXED: nFnt = FNT_FIXED; break;
1843 case TSANS: nFnt = FNT_SANS; break;
1844 case TSERIF: nFnt = FNT_SERIF; break;
1845 default:
1846 break;
1848 if (nFnt != -1)
1849 { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
1850 SetFont(GetFont());
1853 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
1854 //! other font nodes (those with lower depth in the tree)
1855 Flags() |= FontChangeMask::Face;
1858 void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1860 SmNode *pNode = GetSubNode(1);
1861 assert(pNode);
1863 switch (GetToken().eType)
1864 { case TSIZE :
1865 pNode->SetFontSize(maFontSize, meSizeType);
1866 break;
1867 case TSANS :
1868 case TSERIF :
1869 case TFIXED :
1870 pNode->SetFont(GetFont());
1871 break;
1872 case TUNKNOWN : break; // no assertion on "font <?> <?>"
1874 case TPHANTOM : SetPhantom(true); break;
1875 case TBOLD : SetAttribut(FontAttribute::Bold); break;
1876 case TITALIC : SetAttribut(FontAttribute::Italic); break;
1877 case TNBOLD : ClearAttribut(FontAttribute::Bold); break;
1878 case TNITALIC : ClearAttribut(FontAttribute::Italic); break;
1880 case TBLACK : SetColor(COL_BLACK); break;
1881 case TWHITE : SetColor(COL_WHITE); break;
1882 case TRED : SetColor(COL_LIGHTRED); break;
1883 case TGREEN : SetColor(COL_GREEN); break;
1884 case TBLUE : SetColor(COL_LIGHTBLUE); break;
1885 case TCYAN : SetColor(COL_LIGHTCYAN); break; // as in Calc
1886 case TMAGENTA : SetColor(COL_LIGHTMAGENTA); break; // as in Calc
1887 case TYELLOW : SetColor(COL_YELLOW); break;
1888 case TTEAL : SetColor(COL_CYAN); break;
1889 case TSILVER : SetColor(COL_LIGHTGRAY); break;
1890 case TGRAY : SetColor(COL_GRAY); break;
1891 case TMAROON : SetColor(COL_RED); break;
1892 case TPURPLE : SetColor(COL_MAGENTA); break;
1893 case TLIME : SetColor(COL_LIGHTGREEN); break;
1894 case TOLIVE : SetColor(COL_BROWN); break;
1895 case TNAVY : SetColor(COL_BLUE); break;
1896 case TAQUA : SetColor(COL_LIGHTCYAN); break;
1897 case TFUCHSIA : SetColor(COL_LIGHTMAGENTA); break;
1899 default:
1900 SAL_WARN("starmath", "unknown case");
1903 pNode->Arrange(rDev, rFormat);
1905 SmRect::operator = (pNode->GetRect());
1909 void SmFontNode::SetSizeParameter(const Fraction& rValue, FontSizeType eType)
1911 meSizeType = eType;
1912 maFontSize = rValue;
1916 /**************************************************************************/
1919 SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
1920 : SmGraphicNode(SmNodeType::PolyLine, rNodeToken)
1921 , maPoly(2)
1922 , maToSize()
1923 , mnWidth(0)
1928 void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
1930 maToSize.setWidth( nNewWidth );
1934 void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
1936 GetFont().FreezeBorderWidth();
1937 maToSize.setHeight( 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 assert(maPoly.GetSize() == 2);
1953 Point aPointA, aPointB;
1954 if (GetToken().eType == TWIDESLASH)
1956 aPointA.setX( nBorderwidth );
1957 aPointA.setY( maToSize.Height() - nBorderwidth );
1958 aPointB.setX( maToSize.Width() - nBorderwidth );
1959 aPointB.setY( nBorderwidth );
1961 else
1963 OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
1964 aPointA.setX( nBorderwidth );
1965 aPointA.setY( nBorderwidth );
1966 aPointB.setX( maToSize.Width() - nBorderwidth );
1967 aPointB.setY( maToSize.Height() - nBorderwidth );
1969 maPoly.SetPoint(aPointA, 0);
1970 maPoly.SetPoint(aPointB, 1);
1972 long nThick = GetFont().GetFontSize().Height()
1973 * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L;
1974 mnWidth = nThick + 2 * nBorderwidth;
1976 SmRect::operator = (SmRect(maToSize.Width(), maToSize.Height()));
1980 /**************************************************************************/
1982 void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1984 mnBodyWidth = 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 maToSize.setWidth( nWidth );
2005 void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
2007 GetFont().FreezeBorderWidth();
2008 maToSize.setHeight( nHeight );
2012 void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
2014 long nFontHeight = GetFont().GetFontSize().Height();
2015 long nWidth = maToSize.Width(),
2016 nHeight = maToSize.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 , mnFontDesc(nFontDescP)
2041 , mnSelectionStart(0)
2042 , mnSelectionEnd(0)
2046 SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
2047 : SmVisibleNode(SmNodeType::Text, rNodeToken)
2048 , mnFontDesc(nFontDescP)
2049 , mnSelectionStart(0)
2050 , mnSelectionEnd(0)
2054 void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2056 SmNode::Prepare(rFormat, rDocShell, nDepth);
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 maText = 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 its 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, maText, GetFont().GetBorderWidth()));
2094 void SmTextNode::CreateTextFromNode(OUStringBuffer &rText)
2096 bool bQuoted=false;
2097 if (GetToken().eType == TTEXT)
2099 rText.append("\"");
2100 bQuoted=true;
2102 else
2104 SmParser aParseTest;
2105 auto pTable = aParseTest.Parse(GetToken().aText);
2106 assert(pTable->GetType() == SmNodeType::Table);
2107 bQuoted=true;
2108 if (pTable->GetNumSubNodes() == 1)
2110 SmNode *pResult = pTable->GetSubNode(0);
2111 if ( (pResult->GetType() == SmNodeType::Line) &&
2112 (pResult->GetNumSubNodes() == 1) )
2114 pResult = pResult->GetSubNode(0);
2115 if (pResult->GetType() == SmNodeType::Text)
2116 bQuoted=false;
2120 if ((GetToken().eType == TIDENT) && (GetFontDesc() == FNT_FUNCTION))
2122 //Search for existing functions and remove extraneous keyword
2123 rText.append("func ");
2125 else if (bQuoted)
2126 rText.append("italic ");
2128 if (bQuoted)
2129 rText.append("\"");
2133 rText.append(GetToken().aText);
2135 if (bQuoted)
2136 rText.append("\"");
2137 rText.append(" ");
2140 void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
2142 rText.append(maText);
2145 void SmTextNode::AdjustFontDesc()
2147 if (GetToken().eType == TTEXT)
2148 mnFontDesc = FNT_TEXT;
2149 else if(GetToken().eType == TFUNC)
2150 mnFontDesc = FNT_FUNCTION;
2151 else {
2152 SmTokenType nTok;
2153 const SmTokenTableEntry *pEntry = SmParser::GetTokenTableEntry( maText );
2154 if (pEntry && pEntry->nGroup == TG::Function) {
2155 nTok = pEntry->eType;
2156 mnFontDesc = FNT_FUNCTION;
2157 } else {
2158 sal_Unicode firstChar = maText[0];
2159 if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',') {
2160 mnFontDesc = FNT_NUMBER;
2161 nTok = TNUMBER;
2162 } else if (maText.getLength() > 1) {
2163 mnFontDesc = FNT_VARIABLE;
2164 nTok = TIDENT;
2165 } else {
2166 mnFontDesc = FNT_VARIABLE;
2167 nTok = TCHARACTER;
2170 SmToken tok = GetToken();
2171 tok.eType = nTok;
2172 SetToken(tok);
2176 sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
2178 //Find the best match in accepted unicode for our private area symbols
2179 static const sal_Unicode aStarMathPrivateToUnicode[] =
2181 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
2182 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
2183 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
2184 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
2185 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
2186 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
2187 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
2188 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
2189 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
2190 0xE0DA, 0x2190, 0x2191, 0x2193
2192 if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
2193 nIn = aStarMathPrivateToUnicode[nIn-0xE080];
2195 //For whatever unicode glyph that equation editor doesn't ship with that
2196 //we have a possible match we can munge it to.
2197 switch (nIn)
2199 case 0x2223:
2200 nIn = '|';
2201 break;
2202 default:
2203 break;
2206 return nIn;
2209 /**************************************************************************/
2211 void SmMatrixNode::CreateTextFromNode(OUStringBuffer &rText)
2213 rText.append("matrix {");
2214 for (size_t i = 0; i < mnNumRows; ++i)
2216 for (size_t j = 0; j < mnNumCols; ++j)
2218 SmNode *pNode = GetSubNode(i * mnNumCols + j);
2219 if (pNode)
2220 pNode->CreateTextFromNode(rText);
2221 if (j != mnNumCols - 1U)
2222 rText.append("# ");
2224 if (i != mnNumRows - 1U)
2225 rText.append("## ");
2227 rText.stripEnd(' ');
2228 rText.append("} ");
2231 void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2233 SmNode *pNode;
2235 // initialize array that is to hold the maximum widths of all
2236 // elements (subnodes) in that column.
2237 std::vector<long> aColWidth(mnNumCols);
2239 // arrange subnodes and calculate the above arrays contents
2240 size_t nNodes = GetNumSubNodes();
2241 for (size_t i = 0; i < nNodes; ++i)
2243 size_t nIdx = nNodes - 1 - i;
2244 if (nullptr != (pNode = GetSubNode(nIdx)))
2246 pNode->Arrange(rDev, rFormat);
2247 int nCol = nIdx % mnNumCols;
2248 aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
2252 // norm distance from which the following two are calculated
2253 const long nNormDist = 3 * GetFont().GetFontSize().Height();
2255 // define horizontal and vertical minimal distances that separate
2256 // the elements
2257 long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100L,
2258 nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100L;
2260 // build array that holds the leftmost position for each column
2261 std::vector<long> aColLeft(mnNumCols);
2262 long nX = 0;
2263 for (size_t j = 0; j < mnNumCols; ++j)
2265 aColLeft[j] = nX;
2266 nX += aColWidth[j] + nHorDist;
2269 SmRect::operator = (SmRect());
2270 for (size_t i = 0; i < mnNumRows; ++i)
2272 Point aPos;
2273 SmRect aLineRect;
2274 for (size_t j = 0; j < mnNumCols; ++j)
2276 SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
2277 assert(pTmpNode);
2279 const SmRect &rNodeRect = pTmpNode->GetRect();
2281 // align all baselines in that row if possible
2282 aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
2284 // get horizontal alignment
2285 const SmNode *pCoNode = pTmpNode->GetLeftMost();
2286 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
2288 // calculate horizontal position of element depending on column
2289 // and horizontal alignment
2290 switch (eHorAlign)
2291 { case RectHorAlign::Left:
2292 aPos.setX( aColLeft[j] );
2293 break;
2294 case RectHorAlign::Center:
2295 aPos.setX( rNodeRect.GetLeft() + aColLeft[j]
2296 + aColWidth[j] / 2
2297 - rNodeRect.GetItalicCenterX() );
2298 break;
2299 case RectHorAlign::Right:
2300 aPos.setX( aColLeft[j]
2301 + aColWidth[j] - rNodeRect.GetItalicWidth() );
2302 break;
2303 default:
2304 assert(false);
2307 pTmpNode->MoveTo(aPos);
2308 aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
2311 aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
2312 if (i > 0)
2313 aPos.AdjustY(nVerDist );
2315 // move 'aLineRect' and rectangles in that line to final position
2316 Point aDelta(0, // since horizontal alignment is already done
2317 aPos.Y() - aLineRect.GetTop());
2318 aLineRect.Move(aDelta);
2319 for (size_t j = 0; j < mnNumCols; ++j)
2321 if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
2322 pNode->Move(aDelta);
2325 ExtendBy(aLineRect, RectCopyMBL::None);
2330 void SmMatrixNode::SetRowCol(sal_uInt16 nMatrixRows, sal_uInt16 nMatrixCols)
2332 mnNumRows = nMatrixRows;
2333 mnNumCols = nMatrixCols;
2337 const SmNode * SmMatrixNode::GetLeftMost() const
2339 return this;
2343 /**************************************************************************/
2346 SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
2347 : SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH)
2349 sal_Unicode cChar = GetToken().cMathChar;
2350 if (u'\0' != cChar)
2351 SetText(OUString(cChar));
2354 void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
2356 // Since there is no function to do this, we try to approximate it:
2357 Size aFntSize (GetFont().GetFontSize());
2359 //! however the result is a bit better with 'nWidth' as initial font width
2360 aFntSize.setWidth( nWidth );
2361 GetFont().SetSize(aFntSize);
2363 SmTmpDevice aTmpDev (rDev, true);
2364 aTmpDev.SetFont(GetFont());
2366 // get denominator of error factor for width
2367 long nTmpBorderWidth = GetFont().GetBorderWidth();
2368 long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
2370 // scale fontwidth with this error factor
2371 aFntSize.setWidth( aFntSize.Width() * nWidth );
2372 aFntSize.setWidth( aFntSize.Width() / ( nDenom ? nDenom : 1) );
2374 GetFont().SetSize(aFntSize);
2377 void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
2379 GetFont().FreezeBorderWidth();
2380 Size aFntSize (GetFont().GetFontSize());
2382 // Since we only want to scale the height, we might have
2383 // to determine the font width in order to keep it
2384 if (aFntSize.Width() == 0)
2386 rDev.Push(PushFlags::FONT | PushFlags::MAPMODE);
2387 rDev.SetFont(GetFont());
2388 aFntSize.setWidth( rDev.GetFontMetric().GetFontSize().Width() );
2389 rDev.Pop();
2391 OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
2393 //! however the result is a bit better with 'nHeight' as initial
2394 //! font height
2395 aFntSize.setHeight( nHeight );
2396 GetFont().SetSize(aFntSize);
2398 SmTmpDevice aTmpDev (rDev, true);
2399 aTmpDev.SetFont(GetFont());
2401 // get denominator of error factor for height
2402 long nTmpBorderWidth = GetFont().GetBorderWidth();
2403 long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetHeight();
2405 // scale fontwidth with this error factor
2406 aFntSize.setHeight( aFntSize.Height() * nHeight );
2407 aFntSize.setHeight( aFntSize.Height() / ( nDenom ? nDenom : 1) );
2409 GetFont().SetSize(aFntSize);
2413 void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2415 SmNode::Prepare(rFormat, rDocShell, nDepth);
2417 GetFont() = rFormat.GetFont(GetFontDesc());
2418 // use same font size as is used for variables
2419 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2421 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
2422 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
2423 "wrong charset for character from StarMath/OpenSymbol font");
2425 Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
2429 void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2431 const OUString &rText = GetText();
2433 if (rText.isEmpty() || rText[0] == '\0')
2434 { SmRect::operator = (SmRect());
2435 return;
2438 PrepareAttributes();
2440 GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
2442 SmTmpDevice aTmpDev (rDev, true);
2443 aTmpDev.SetFont(GetFont());
2445 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2448 void SmMathSymbolNode::CreateTextFromNode(OUStringBuffer &rText)
2450 sal_Unicode cChar = GetToken().cMathChar;
2451 if (cChar == MS_INT && GetScaleMode() == SmScaleMode::Height)
2452 rText.append("intd ");
2453 else
2454 MathType::LookupChar(cChar, rText, 3);
2457 void SmRectangleNode::CreateTextFromNode(OUStringBuffer &rText)
2459 switch (GetToken().eType)
2461 case TUNDERLINE:
2462 rText.append("underline ");
2463 break;
2464 case TOVERLINE:
2465 rText.append("overline ");
2466 break;
2467 case TOVERSTRIKE:
2468 rText.append("overstrike ");
2469 break;
2470 default:
2471 break;
2475 void SmAttributNode::CreateTextFromNode(OUStringBuffer &rText)
2477 SmNode *pNode;
2478 assert(GetNumSubNodes() == 2);
2479 rText.append("{");
2480 sal_Unicode nLast=0;
2481 if (nullptr != (pNode = Attribute()))
2483 OUStringBuffer aStr;
2484 pNode->CreateTextFromNode(aStr);
2485 if (aStr.getLength() > 1)
2486 rText.append(aStr);
2487 else
2489 nLast = aStr[0];
2490 switch (nLast)
2492 case MS_BAR: // MACRON
2493 rText.append("overline ");
2494 break;
2495 case MS_DOT: // DOT ABOVE
2496 rText.append("dot ");
2497 break;
2498 case 0x2dc: // SMALL TILDE
2499 rText.append("widetilde ");
2500 break;
2501 case MS_DDOT: // DIAERESIS
2502 rText.append("ddot ");
2503 break;
2504 case 0xE082:
2505 break;
2506 case 0xE09B:
2507 case MS_DDDOT: // COMBINING THREE DOTS ABOVE
2508 rText.append("dddot ");
2509 break;
2510 case MS_ACUTE: // ACUTE ACCENT
2511 case MS_COMBACUTE: // COMBINING ACUTE ACCENT
2512 rText.append("acute ");
2513 break;
2514 case MS_GRAVE: // GRAVE ACCENT
2515 case MS_COMBGRAVE: // COMBINING GRAVE ACCENT
2516 rText.append("grave ");
2517 break;
2518 case MS_CHECK: // CARON
2519 case MS_COMBCHECK: // COMBINING CARON
2520 rText.append("check ");
2521 break;
2522 case MS_BREVE: // BREVE
2523 case MS_COMBBREVE: // COMBINING BREVE
2524 rText.append("breve ");
2525 break;
2526 case MS_CIRCLE: // RING ABOVE
2527 case MS_COMBCIRCLE: // COMBINING RING ABOVE
2528 rText.append("circle ");
2529 break;
2530 case MS_RIGHTARROW: // RIGHTWARDS ARROW
2531 case MS_VEC: // COMBINING RIGHT ARROW ABOVE
2532 rText.append("vec ");
2533 break;
2534 case MS_TILDE: // TILDE
2535 case MS_COMBTILDE: // COMBINING TILDE
2536 rText.append("tilde ");
2537 break;
2538 case MS_HAT: // CIRCUMFLEX ACCENT
2539 case MS_COMBHAT: // COMBINING CIRCUMFLEX ACCENT
2540 rText.append("hat ");
2541 break;
2542 case MS_COMBBAR: // COMBINING MACRON
2543 rText.append("bar ");
2544 break;
2545 default:
2546 rText.append(OUStringLiteral1(nLast));
2547 break;
2552 if (nullptr != (pNode = Body()))
2553 pNode->CreateTextFromNode(rText);
2555 rText.stripEnd(' ');
2557 if (nLast == 0xE082)
2558 rText.append(" overbrace {}");
2560 rText.append("} ");
2563 /**************************************************************************/
2565 static bool lcl_IsFromGreekSymbolSet( const OUString &rTokenText )
2567 bool bRes = false;
2569 // valid symbol name needs to have a '%' at pos 0 and at least an additional char
2570 if (rTokenText.getLength() > 2 && rTokenText[0] == u'%')
2572 OUString aName( rTokenText.copy(1) );
2573 SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName );
2574 if (pSymbol && SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
2575 bRes = true;
2578 return bRes;
2582 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
2583 : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
2584 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
2589 SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
2590 : SmTextNode(SmNodeType::Special, rNodeToken, FNT_MATH) // default Font isn't always correct!
2591 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
2596 void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2598 SmNode::Prepare(rFormat, rDocShell, nDepth);
2600 const SmSym *pSym;
2601 SmModule *pp = SM_MOD();
2603 OUString aName(GetToken().aText.copy(1));
2604 if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName( aName )))
2606 sal_UCS4 cChar = pSym->GetCharacter();
2607 OUString aTmp( &cChar, 1 );
2608 SetText( aTmp );
2609 GetFont() = pSym->GetFace();
2611 else
2613 SetText( GetToken().aText );
2614 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2616 // use same font size as is used for variables
2617 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2619 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2620 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2621 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2623 //! see also SmFontStyles::GetStyleName
2624 if (IsItalic( GetFont() ))
2625 SetAttribut(FontAttribute::Italic);
2626 if (IsBold( GetFont() ))
2627 SetAttribut(FontAttribute::Bold);
2629 Flags() |= FontChangeMask::Face;
2631 if (mbIsFromGreekSymbolSet)
2633 OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
2634 bool bItalic = false;
2635 sal_Int16 nStyle = rFormat.GetGreekCharStyle();
2636 OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
2637 if (nStyle == 1)
2638 bItalic = true;
2639 else if (nStyle == 2)
2641 const OUString& rTmp(GetText());
2642 if (!rTmp.isEmpty())
2644 static const sal_Unicode cUppercaseAlpha = 0x0391;
2645 static const sal_Unicode cUppercaseOmega = 0x03A9;
2646 sal_Unicode cChar = rTmp[0];
2647 // uppercase letters should be straight and lowercase letters italic
2648 bItalic = !(cUppercaseAlpha <= cChar && cChar <= cUppercaseOmega);
2652 if (bItalic)
2653 Attributes() |= FontAttribute::Italic;
2654 else
2655 Attributes() &= ~FontAttribute::Italic;
2660 void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2662 PrepareAttributes();
2664 SmTmpDevice aTmpDev (rDev, true);
2665 aTmpDev.SetFont(GetFont());
2667 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2670 /**************************************************************************/
2673 void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2675 PrepareAttributes();
2677 SmTmpDevice aTmpDev (rDev, true);
2678 aTmpDev.SetFont(GetFont());
2680 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
2681 GetFont().GetBorderWidth()).AsGlyphRect());
2685 /**************************************************************************/
2688 void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2690 SmNode::Prepare(rFormat, rDocShell, nDepth);
2692 GetFont().SetColor(COL_GRAY);
2693 Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
2697 void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2699 PrepareAttributes();
2701 SmTmpDevice aTmpDev (rDev, true);
2702 aTmpDev.SetFont(GetFont());
2704 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2708 /**************************************************************************/
2711 void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2713 SmNode::Prepare(rFormat, rDocShell, nDepth);
2715 GetFont().SetColor(COL_RED);
2716 Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
2717 | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
2721 void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2723 PrepareAttributes();
2725 SmTmpDevice aTmpDev (rDev, true);
2726 aTmpDev.SetFont(GetFont());
2728 const OUString &rText = GetText();
2729 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2732 /**************************************************************************/
2734 void SmBlankNode::IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy)
2736 switch(rToken.eType)
2738 case TBLANK: mnNum += (4 * nMultiplyBy); break;
2739 case TSBLANK: mnNum += (1 * nMultiplyBy); break;
2740 default:
2741 break;
2745 void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2747 SmNode::Prepare(rFormat, rDocShell, nDepth);
2749 // Here it need/should not be the StarMath font, so that for the character
2750 // used in Arrange a normal (non-clipped) rectangle is generated
2751 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2753 Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
2757 void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2759 SmTmpDevice aTmpDev (rDev, true);
2760 aTmpDev.SetFont(GetFont());
2762 // make distance depend on the font height
2763 // (so that it increases when scaling (e.g. size *2 {a ~ b})
2764 long nDist = GetFont().GetFontSize().Height() / 10L,
2765 nSpace = mnNum * nDist;
2767 // get a SmRect with Baseline and all the bells and whistles
2768 SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
2769 GetFont().GetBorderWidth()));
2771 // and resize it to the requested size
2772 SetItalicSpaces(0, 0);
2773 SetWidth(nSpace);
2776 void SmBlankNode::CreateTextFromNode(OUStringBuffer &rText)
2778 if (mnNum <= 0)
2779 return;
2780 sal_uInt16 nWide = mnNum / 4;
2781 sal_uInt16 nNarrow = mnNum % 4;
2782 for (sal_uInt16 i = 0; i < nWide; i++)
2783 rText.append("~");
2784 for (sal_uInt16 i = 0; i < nNarrow; i++)
2785 rText.append("`");
2786 rText.append(" ");
2790 /**************************************************************************/
2791 //Implementation of all accept methods for SmVisitor
2793 void SmTableNode::Accept(SmVisitor* pVisitor) {
2794 pVisitor->Visit(this);
2797 void SmBraceNode::Accept(SmVisitor* pVisitor) {
2798 pVisitor->Visit(this);
2801 void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
2802 pVisitor->Visit(this);
2805 void SmOperNode::Accept(SmVisitor* pVisitor) {
2806 pVisitor->Visit(this);
2809 void SmAlignNode::Accept(SmVisitor* pVisitor) {
2810 pVisitor->Visit(this);
2813 void SmAttributNode::Accept(SmVisitor* pVisitor) {
2814 pVisitor->Visit(this);
2817 void SmFontNode::Accept(SmVisitor* pVisitor) {
2818 pVisitor->Visit(this);
2821 void SmUnHorNode::Accept(SmVisitor* pVisitor) {
2822 pVisitor->Visit(this);
2825 void SmBinHorNode::Accept(SmVisitor* pVisitor) {
2826 pVisitor->Visit(this);
2829 void SmBinVerNode::Accept(SmVisitor* pVisitor) {
2830 pVisitor->Visit(this);
2833 void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
2834 pVisitor->Visit(this);
2837 void SmSubSupNode::Accept(SmVisitor* pVisitor) {
2838 pVisitor->Visit(this);
2841 void SmMatrixNode::Accept(SmVisitor* pVisitor) {
2842 pVisitor->Visit(this);
2845 void SmPlaceNode::Accept(SmVisitor* pVisitor) {
2846 pVisitor->Visit(this);
2849 void SmTextNode::Accept(SmVisitor* pVisitor) {
2850 pVisitor->Visit(this);
2853 void SmSpecialNode::Accept(SmVisitor* pVisitor) {
2854 pVisitor->Visit(this);
2857 void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
2858 pVisitor->Visit(this);
2861 void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
2862 pVisitor->Visit(this);
2865 void SmBlankNode::Accept(SmVisitor* pVisitor) {
2866 pVisitor->Visit(this);
2869 void SmErrorNode::Accept(SmVisitor* pVisitor) {
2870 pVisitor->Visit(this);
2873 void SmLineNode::Accept(SmVisitor* pVisitor) {
2874 pVisitor->Visit(this);
2877 void SmExpressionNode::Accept(SmVisitor* pVisitor) {
2878 pVisitor->Visit(this);
2881 void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
2882 pVisitor->Visit(this);
2885 void SmRootNode::Accept(SmVisitor* pVisitor) {
2886 pVisitor->Visit(this);
2889 void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
2890 pVisitor->Visit(this);
2893 void SmRectangleNode::Accept(SmVisitor* pVisitor) {
2894 pVisitor->Visit(this);
2897 void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
2898 pVisitor->Visit(this);
2901 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */