Bump version to 21.06.18.1
[LibreOffice.git] / starmath / source / node.cxx
blob9b3b3b64288e40325a2826c296cac41be0650c96
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>
29 #include <starmathdatabase.hxx>
31 #include <comphelper/string.hxx>
32 #include <tools/color.hxx>
33 #include <tools/fract.hxx>
34 #include <tools/gen.hxx>
35 #include <vcl/metric.hxx>
36 #include <vcl/outdev.hxx>
37 #include <sal/log.hxx>
38 #include <osl/diagnose.h>
39 #include <rtl/math.hxx>
41 #include <cassert>
42 #include <cstdlib>
43 #include <math.h>
44 #include <memory>
45 #include <float.h>
46 #include <vector>
48 namespace {
50 template<typename F>
51 void ForEachNonNull(SmNode *pNode, F && f)
53 size_t nSize = pNode->GetNumSubNodes();
54 for (size_t i = 0; i < nSize; ++i)
56 SmNode *pSubNode = pNode->GetSubNode(i);
57 if (pSubNode != nullptr)
58 f(pSubNode);
64 SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken)
65 : maNodeToken( rNodeToken )
66 , meType( eNodeType )
67 , meScaleMode( SmScaleMode::None )
68 , meRectHorAlign( RectHorAlign::Left )
69 , mnFlags( FontChangeMask::None )
70 , mnAttributes( FontAttribute::None )
71 , mbIsPhantom( false )
72 , mbIsSelected( false )
73 , mnAccIndex( -1 )
74 , mpParentNode( nullptr )
78 SmNode::~SmNode()
82 const SmNode * SmNode::GetLeftMost() const
83 // returns leftmost node of current subtree.
84 //! (this assumes the one with index 0 is always the leftmost subnode
85 //! for the current node).
87 const SmNode *pNode = GetNumSubNodes() > 0 ?
88 GetSubNode(0) : nullptr;
90 return pNode ? pNode->GetLeftMost() : this;
94 void SmNode::SetPhantom(bool bIsPhantomP)
96 if (! (Flags() & FontChangeMask::Phantom))
97 mbIsPhantom = bIsPhantomP;
99 bool b = mbIsPhantom;
100 ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
104 void SmNode::SetColor(const Color& rColor)
106 if (! (Flags() & FontChangeMask::Color))
107 GetFont().SetColor(rColor);
109 ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
113 void SmNode::SetAttribut(FontAttribute nAttrib)
115 if (
116 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
117 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
120 mnAttributes |= nAttrib;
123 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribut(nAttrib);});
127 void SmNode::ClearAttribut(FontAttribute nAttrib)
129 if (
130 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
131 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
134 mnAttributes &= ~nAttrib;
137 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribut(nAttrib);});
141 void SmNode::SetFont(const SmFace &rFace)
143 if (!(Flags() & FontChangeMask::Face))
144 GetFont() = rFace;
145 ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
149 void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
150 //! 'rSize' is in units of pts
152 Size aFntSize;
154 if (!(Flags() & FontChangeMask::Size))
156 Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()),
157 rSize.GetDenominator());
158 tools::Long nHeight = static_cast<tools::Long>(aVal);
160 aFntSize = GetFont().GetFontSize();
161 aFntSize.setWidth( 0 );
162 switch(nType)
164 case FontSizeType::ABSOLUT:
165 aFntSize.setHeight( nHeight );
166 break;
168 case FontSizeType::PLUS:
169 aFntSize.AdjustHeight(nHeight );
170 break;
172 case FontSizeType::MINUS:
173 aFntSize.AdjustHeight( -nHeight );
174 break;
176 case FontSizeType::MULTIPLY:
177 aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) * rSize) );
178 break;
180 case FontSizeType::DIVIDE:
181 if (rSize != Fraction(0))
182 aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) / rSize) );
183 break;
184 default:
185 break;
188 // check the requested size against maximum value
189 static int const nMaxVal = SmPtsTo100th_mm(128);
190 if (aFntSize.Height() > nMaxVal)
191 aFntSize.setHeight( nMaxVal );
193 GetFont().SetSize(aFntSize);
196 ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
200 void SmNode::SetSize(const Fraction &rSize)
202 GetFont() *= rSize;
204 ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
208 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
210 meRectHorAlign = eHorAlign;
212 if (bApplyToSubTree)
213 ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
217 void SmNode::PrepareAttributes()
219 GetFont().SetWeight((Attributes() & FontAttribute::Bold) ? WEIGHT_BOLD : WEIGHT_NORMAL);
220 GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
224 void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
226 if (nDepth > 1024)
227 throw std::range_error("parser depth limit");
229 mbIsPhantom = false;
230 mnFlags = FontChangeMask::None;
231 mnAttributes = FontAttribute::None;
233 switch (rFormat.GetHorAlign())
234 { case SmHorAlign::Left: meRectHorAlign = RectHorAlign::Left; break;
235 case SmHorAlign::Center: meRectHorAlign = RectHorAlign::Center; break;
236 case SmHorAlign::Right: meRectHorAlign = RectHorAlign::Right; break;
239 GetFont() = rFormat.GetFont(FNT_MATH);
240 OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
241 "unexpected CharSet" );
242 GetFont().SetWeight(WEIGHT_NORMAL);
243 GetFont().SetItalic(ITALIC_NONE);
245 ForEachNonNull(this, [&rFormat, &rDocShell, nDepth](SmNode *pNode){pNode->Prepare(rFormat, rDocShell, nDepth + 1);});
248 void SmNode::Move(const Point& rVector)
250 if (rVector.X() == 0 && rVector.Y() == 0)
251 return;
253 SmRect::Move(rVector);
255 ForEachNonNull(this, [&rVector](SmNode *pNode){pNode->Move(rVector);});
258 void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
263 void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
268 const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
269 // returns (first) ** visible ** (sub)node with the tokens text at
270 // position 'nRow', 'nCol'.
271 //! (there should be exactly one such node if any)
273 if ( IsVisible()
274 && nRow == GetToken().nRow
275 && nCol >= GetToken().nCol && nCol < GetToken().nCol + GetToken().aText.getLength())
276 return this;
277 else
279 size_t nNumSubNodes = GetNumSubNodes();
280 for (size_t i = 0; i < nNumSubNodes; ++i)
282 const SmNode *pNode = GetSubNode(i);
284 if (!pNode)
285 continue;
287 const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
288 if (pResult)
289 return pResult;
293 return nullptr;
297 const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
299 tools::Long nDist = LONG_MAX;
300 const SmNode *pResult = nullptr;
302 if (IsVisible())
303 pResult = this;
304 else
306 size_t nNumSubNodes = GetNumSubNodes();
307 for (size_t i = 0; i < nNumSubNodes; ++i)
309 const SmNode *pNode = GetSubNode(i);
311 if (!pNode)
312 continue;
314 const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
315 if (pFound)
317 tools::Long nTmp = pFound->OrientedDist(rPoint);
318 if (nTmp < nDist)
320 nDist = nTmp;
321 pResult = pFound;
323 // quit immediately if 'rPoint' is inside the *should not
324 // overlap with other rectangles* part.
325 // This (partly) serves for getting the attributes in eg
326 // "bar overstrike a".
327 // ('nDist < 0' is used as *quick shot* to avoid evaluation of
328 // the following expression, where the result is already determined)
329 if (nDist < 0 && pFound->IsInsideRect(rPoint))
330 break;
336 return pResult;
339 const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
341 const SmNode *pResult = nullptr;
343 sal_Int32 nIdx = GetAccessibleIndex();
344 OUStringBuffer aTxt;
345 if (nIdx >= 0)
346 GetAccessibleText( aTxt ); // get text if used in following 'if' statement
348 if (nIdx >= 0
349 && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.getLength())
350 pResult = this;
351 else
353 size_t nNumSubNodes = GetNumSubNodes();
354 for (size_t i = 0; i < nNumSubNodes; ++i)
356 const SmNode *pNode = GetSubNode(i);
357 if (!pNode)
358 continue;
360 pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
361 if (pResult)
362 return pResult;
366 return pResult;
370 SmStructureNode::~SmStructureNode()
372 ForEachNonNull(this, std::default_delete<SmNode>());
376 void SmStructureNode::ClearSubNodes()
378 maSubNodes.clear();
381 void SmStructureNode::SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
383 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
384 maSubNodes.resize( nSize );
385 if (pFirst)
386 maSubNodes[0] = pFirst.release();
387 if (pSecond)
388 maSubNodes[1] = pSecond.release();
389 if (pThird)
390 maSubNodes[2] = pThird.release();
392 ClaimPaternity();
395 void SmStructureNode::SetSubNodes(SmNodeArray&& rNodeArray)
397 maSubNodes = std::move(rNodeArray);
398 ClaimPaternity();
401 bool SmStructureNode::IsVisible() const
403 return false;
406 size_t SmStructureNode::GetNumSubNodes() const
408 return maSubNodes.size();
411 SmNode* SmStructureNode::GetSubNode(size_t nIndex)
413 return maSubNodes[nIndex];
416 void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
418 ForEachNonNull(const_cast<SmStructureNode *>(this),
419 [&rText](SmNode *pNode)
421 if (pNode->IsVisible())
422 pNode->SetAccessibleIndex(rText.getLength());
423 pNode->GetAccessibleText( rText );
427 void SmStructureNode::ClaimPaternity()
429 ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
432 int SmStructureNode::IndexOfSubNode(SmNode const * pSubNode)
434 size_t nSize = GetNumSubNodes();
435 for (size_t i = 0; i < nSize; i++)
436 if (pSubNode == GetSubNode(i))
437 return i;
438 return -1;
441 void SmStructureNode::SetSubNode(size_t nIndex, SmNode* pNode)
443 size_t size = maSubNodes.size();
444 if (size <= nIndex)
446 //Resize subnodes array
447 maSubNodes.resize(nIndex + 1);
448 //Set new slots to NULL except at nIndex
449 for (size_t i = size; i < nIndex; i++)
450 maSubNodes[i] = nullptr;
452 maSubNodes[nIndex] = pNode;
453 if (pNode)
454 pNode->SetParent(this);
457 bool SmVisibleNode::IsVisible() const
459 return true;
462 size_t SmVisibleNode::GetNumSubNodes() const
464 return 0;
467 SmNode * SmVisibleNode::GetSubNode(size_t /*nIndex*/)
469 return nullptr;
472 void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
474 rText.append(GetToken().aText);
477 void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
478 // arranges all subnodes in one column
480 SmNode *pNode;
481 size_t nSize = GetNumSubNodes();
483 // make distance depend on font size
484 tools::Long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
485 * GetFont().GetFontSize().Height()) / 100;
487 if (nSize < 1)
488 return;
490 // arrange subnodes and get maximum width of them
491 tools::Long nMaxWidth = 0,
492 nTmp;
493 for (size_t i = 0; i < nSize; ++i)
495 if (nullptr != (pNode = GetSubNode(i)))
496 { pNode->Arrange(rDev, rFormat);
497 if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
498 nMaxWidth = nTmp;
502 Point aPos;
503 SmRect::operator = (SmRect(nMaxWidth, 1));
504 for (size_t i = 0; i < nSize; ++i)
506 if (nullptr != (pNode = GetSubNode(i)))
507 { const SmRect &rNodeRect = pNode->GetRect();
508 const SmNode *pCoNode = pNode->GetLeftMost();
509 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
511 aPos = rNodeRect.AlignTo(*this, RectPos::Bottom,
512 eHorAlign, RectVerAlign::Baseline);
513 if (i)
514 aPos.AdjustY(nDist );
515 pNode->MoveTo(aPos);
516 ExtendBy(rNodeRect, nSize > 1 ? RectCopyMBL::None : RectCopyMBL::Arg);
519 // #i972#
520 if (HasBaseline())
521 mnFormulaBaseline = GetBaseline();
522 else
524 SmTmpDevice aTmpDev (rDev, true);
525 aTmpDev.SetFont(GetFont());
527 SmRect aRect(aTmpDev, &rFormat, "a", GetFont().GetBorderWidth());
528 mnFormulaBaseline = GetAlignM();
529 // move from middle position by constant - distance
530 // between middle and baseline for single letter
531 mnFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
535 const SmNode * SmTableNode::GetLeftMost() const
537 return this;
541 tools::Long SmTableNode::GetFormulaBaseline() const
543 return mnFormulaBaseline;
547 /**************************************************************************/
550 void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
552 SmNode::Prepare(rFormat, rDocShell, nDepth);
554 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
555 // to the rest of the formula compared to the 'FNT_MATH' font.
556 GetFont() = rFormat.GetFont(FNT_VARIABLE);
557 Flags() |= FontChangeMask::Face;
561 /**************************************************************************/
564 void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
565 // arranges all subnodes in one row with some extra space between
567 SmNode *pNode;
568 size_t nSize = GetNumSubNodes();
569 for (size_t i = 0; i < nSize; ++i)
571 if (nullptr != (pNode = GetSubNode(i)))
572 pNode->Arrange(rDev, rFormat);
575 SmTmpDevice aTmpDev (rDev, true);
576 aTmpDev.SetFont(GetFont());
578 if (nSize < 1)
580 // provide an empty rectangle with alignment parameters for the "current"
581 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
582 // same sub-/supscript positions.)
583 //! be sure to use a character that has explicitly defined HiAttribut
584 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
585 //! 'vec {a}'.
586 SmRect::operator = (SmRect(aTmpDev, &rFormat, "a",
587 GetFont().GetBorderWidth()));
588 // make sure that the rectangle occupies (almost) no space
589 SetWidth(1);
590 SetItalicSpaces(0, 0);
591 return;
594 // make distance depend on font size
595 tools::Long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetFontSize().Height()) / 100;
596 if (!IsUseExtraSpaces())
597 nDist = 0;
599 Point aPos;
600 // copy the first node into LineNode and extend by the others
601 if (nullptr != (pNode = GetSubNode(0)))
602 SmRect::operator = (pNode->GetRect());
604 for (size_t i = 1; i < nSize; ++i)
606 if (nullptr != (pNode = GetSubNode(i)))
608 aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
610 // add horizontal space to the left for each but the first sub node
611 aPos.AdjustX(nDist );
613 pNode->MoveTo(aPos);
614 ExtendBy( *pNode, RectCopyMBL::Xor );
620 /**************************************************************************/
623 void SmExpressionNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
624 // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
626 SmLineNode::Arrange(rDev, rFormat);
628 // copy alignment of leftmost subnode if any
629 const SmNode *pNode = GetLeftMost();
630 if (pNode)
631 SetRectHorAlign(pNode->GetRectHorAlign(), false);
635 /**************************************************************************/
638 void SmUnHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
640 bool bIsPostfix = GetToken().eType == TFACT;
642 SmNode *pNode0 = GetSubNode(0),
643 *pNode1 = GetSubNode(1);
644 SmNode *pOper = bIsPostfix ? pNode1 : pNode0,
645 *pBody = bIsPostfix ? pNode0 : pNode1;
646 assert(pOper);
647 assert(pBody);
649 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
650 pOper->Arrange(rDev, rFormat);
651 pBody->Arrange(rDev, rFormat);
653 tools::Long nDist = (pOper->GetRect().GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
655 SmRect::operator = (*pNode0);
657 Point aPos = pNode1->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
658 aPos.AdjustX(nDist );
659 pNode1->MoveTo(aPos);
660 ExtendBy(*pNode1, RectCopyMBL::Xor);
664 /**************************************************************************/
666 namespace {
668 void lcl_GetHeightVerOffset(const SmRect &rRect,
669 tools::Long &rHeight, tools::Long &rVerOffset)
670 // calculate height and vertical offset of root sign suitable for 'rRect'
672 rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
673 rHeight = rRect.GetHeight() - rVerOffset;
675 OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
676 OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
680 Point lcl_GetExtraPos(const SmRect &rRootSymbol,
681 const SmRect &rExtra)
683 const Size &rSymSize = rRootSymbol.GetSize();
685 Point aPos = rRootSymbol.GetTopLeft()
686 + Point((rSymSize.Width() * 70) / 100,
687 (rSymSize.Height() * 52) / 100);
689 // from this calculate topleft edge of 'rExtra'
690 aPos.AdjustX( -(rExtra.GetWidth() + rExtra.GetItalicRightSpace()) );
691 aPos.AdjustY( -(rExtra.GetHeight()) );
692 // if there's enough space move a bit less to the right
693 // examples: "nroot i a", "nroot j a"
694 // (it looks better if we don't use italic-spaces here)
695 tools::Long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
696 if (aPos.X() > nX)
697 aPos.setX( nX );
699 return aPos;
704 void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
706 //! pExtra needs to have the smaller index than pRootSym in order to
707 //! not to get the root symbol but the pExtra when clicking on it in the
708 //! GraphicWindow. (That is because of the simplicity of the algorithm
709 //! that finds the node corresponding to a mouseclick in the window.)
710 SmNode *pExtra = GetSubNode(0),
711 *pRootSym = GetSubNode(1),
712 *pBody = GetSubNode(2);
713 assert(pRootSym);
714 assert(pBody);
716 pBody->Arrange(rDev, rFormat);
718 tools::Long nHeight,
719 nVerOffset;
720 lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset);
721 nHeight += rFormat.GetDistance(DIS_ROOT)
722 * GetFont().GetFontSize().Height() / 100;
724 // font specialist advised to change the width first
725 pRootSym->AdaptToY(rDev, nHeight);
726 pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
728 pRootSym->Arrange(rDev, rFormat);
730 Point aPos = pRootSym->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, RectVerAlign::Baseline);
731 //! override calculated vertical position
732 aPos.setY( pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom() );
733 aPos.AdjustY( -nVerOffset );
734 pRootSym->MoveTo(aPos);
736 if (pExtra)
737 { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
738 pExtra->Arrange(rDev, rFormat);
740 aPos = lcl_GetExtraPos(*pRootSym, *pExtra);
741 pExtra->MoveTo(aPos);
744 SmRect::operator = (*pBody);
745 ExtendBy(*pRootSym, RectCopyMBL::This);
746 if (pExtra)
747 ExtendBy(*pExtra, RectCopyMBL::This, true);
750 /**************************************************************************/
753 void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
755 SmNode *pLeft = LeftOperand(),
756 *pOper = Symbol(),
757 *pRight = RightOperand();
758 assert(pLeft);
759 assert(pOper);
760 assert(pRight);
762 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
764 pLeft ->Arrange(rDev, rFormat);
765 pOper ->Arrange(rDev, rFormat);
766 pRight->Arrange(rDev, rFormat);
768 const SmRect &rOpRect = pOper->GetRect();
770 tools::Long nDist = (rOpRect.GetWidth() *
771 rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
773 SmRect::operator = (*pLeft);
775 Point aPos;
776 aPos = pOper->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
777 aPos.AdjustX(nDist );
778 pOper->MoveTo(aPos);
779 ExtendBy(*pOper, RectCopyMBL::Xor);
781 aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
782 aPos.AdjustX(nDist );
784 pRight->MoveTo(aPos);
785 ExtendBy(*pRight, RectCopyMBL::Xor);
789 /**************************************************************************/
792 void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
794 SmNode *pNum = GetSubNode(0),
795 *pLine = GetSubNode(1),
796 *pDenom = GetSubNode(2);
797 assert(pNum);
798 assert(pLine);
799 assert(pDenom);
801 bool bIsTextmode = rFormat.IsTextmode();
802 if (bIsTextmode)
804 Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
805 pNum ->SetSize(aFraction);
806 pLine ->SetSize(aFraction);
807 pDenom->SetSize(aFraction);
810 pNum ->Arrange(rDev, rFormat);
811 pDenom->Arrange(rDev, rFormat);
813 tools::Long nFontHeight = GetFont().GetFontSize().Height(),
814 nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100,
815 nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100,
816 nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
817 nNumDist = bIsTextmode ? 0 :
818 nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100,
819 nDenomDist = bIsTextmode ? 0 :
820 nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100;
822 // font specialist advised to change the width first
823 pLine->AdaptToY(rDev, nThick);
824 pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
825 pLine->Arrange(rDev, rFormat);
827 // get horizontal alignment for numerator
828 const SmNode *pLM = pNum->GetLeftMost();
829 RectHorAlign eHorAlign = pLM->GetRectHorAlign();
831 // move numerator to its position
832 Point aPos = pNum->AlignTo(*pLine, RectPos::Top, eHorAlign, RectVerAlign::Baseline);
833 aPos.AdjustY( -nNumDist );
834 pNum->MoveTo(aPos);
836 // get horizontal alignment for denominator
837 pLM = pDenom->GetLeftMost();
838 eHorAlign = pLM->GetRectHorAlign();
840 // move denominator to its position
841 aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
842 aPos.AdjustY(nDenomDist );
843 pDenom->MoveTo(aPos);
845 SmRect::operator = (*pNum);
846 ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
849 const SmNode * SmBinVerNode::GetLeftMost() const
851 return this;
855 namespace {
857 /// @return value of the determinant formed by the two points
858 double Det(const Point &rHeading1, const Point &rHeading2)
860 return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
864 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
865 /// and has the direction vector 'rHeading2'
866 bool IsPointInLine(const Point &rPoint1,
867 const Point &rPoint2, const Point &rHeading2)
869 assert(rHeading2 != Point());
871 bool bRes = false;
872 static const double eps = 5.0 * DBL_EPSILON;
874 double fLambda;
875 if (std::abs(rHeading2.X()) > std::abs(rHeading2.Y()))
877 fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
878 bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
880 else
882 fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
883 bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
886 return bRes;
890 sal_uInt16 GetLineIntersectionPoint(Point &rResult,
891 const Point& rPoint1, const Point &rHeading1,
892 const Point& rPoint2, const Point &rHeading2)
894 assert(rHeading1 != Point());
895 assert(rHeading2 != Point());
897 sal_uInt16 nRes = 1;
898 static const double eps = 5.0 * DBL_EPSILON;
900 // are the direction vectors linearly dependent?
901 double fDet = Det(rHeading1, rHeading2);
902 if (fabs(fDet) < eps)
904 nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
905 rResult = nRes ? rPoint1 : Point();
907 else
909 // here we do not pay attention to the computational accuracy
910 // (that would be more complicated and is not really worth it in this case)
911 double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
912 - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
913 / fDet;
914 rResult = Point(rPoint1.X() + static_cast<tools::Long>(fLambda * rHeading1.X()),
915 rPoint1.Y() + static_cast<tools::Long>(fLambda * rHeading1.Y()));
918 return nRes;
924 /// @return position and size of the diagonal line
925 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
926 void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
927 const Point &rDiagPoint, double fAngleDeg) const
930 static const double fPi = 3.1415926535897932384626433;
931 double fAngleRad = fAngleDeg / 180.0 * fPi;
932 tools::Long nRectLeft = GetItalicLeft(),
933 nRectRight = GetItalicRight(),
934 nRectTop = GetTop(),
935 nRectBottom = GetBottom();
936 Point aRightHdg (100, 0),
937 aDownHdg (0, 100),
938 aDiagHdg ( static_cast<tools::Long>(100.0 * cos(fAngleRad)),
939 static_cast<tools::Long>(-100.0 * sin(fAngleRad)) );
941 tools::Long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
942 Point aPoint;
943 if (IsAscending())
945 // determine top right corner
946 GetLineIntersectionPoint(aPoint,
947 Point(nRectLeft, nRectTop), aRightHdg,
948 rDiagPoint, aDiagHdg);
949 // is there a point of intersection with the top border?
950 if (aPoint.X() <= nRectRight)
952 nRight = aPoint.X();
953 nTop = nRectTop;
955 else
957 // there has to be a point of intersection with the right border!
958 GetLineIntersectionPoint(aPoint,
959 Point(nRectRight, nRectTop), aDownHdg,
960 rDiagPoint, aDiagHdg);
962 nRight = nRectRight;
963 nTop = aPoint.Y();
966 // determine bottom left corner
967 GetLineIntersectionPoint(aPoint,
968 Point(nRectLeft, nRectBottom), aRightHdg,
969 rDiagPoint, aDiagHdg);
970 // is there a point of intersection with the bottom border?
971 if (aPoint.X() >= nRectLeft)
973 nLeft = aPoint.X();
974 nBottom = nRectBottom;
976 else
978 // there has to be a point of intersection with the left border!
979 GetLineIntersectionPoint(aPoint,
980 Point(nRectLeft, nRectTop), aDownHdg,
981 rDiagPoint, aDiagHdg);
983 nLeft = nRectLeft;
984 nBottom = aPoint.Y();
987 else
989 // determine top left corner
990 GetLineIntersectionPoint(aPoint,
991 Point(nRectLeft, nRectTop), aRightHdg,
992 rDiagPoint, aDiagHdg);
993 // is there a point of intersection with the top border?
994 if (aPoint.X() >= nRectLeft)
996 nLeft = aPoint.X();
997 nTop = nRectTop;
999 else
1001 // there has to be a point of intersection with the left border!
1002 GetLineIntersectionPoint(aPoint,
1003 Point(nRectLeft, nRectTop), aDownHdg,
1004 rDiagPoint, aDiagHdg);
1006 nLeft = nRectLeft;
1007 nTop = aPoint.Y();
1010 // determine bottom right corner
1011 GetLineIntersectionPoint(aPoint,
1012 Point(nRectLeft, nRectBottom), aRightHdg,
1013 rDiagPoint, aDiagHdg);
1014 // is there a point of intersection with the bottom border?
1015 if (aPoint.X() <= nRectRight)
1017 nRight = aPoint.X();
1018 nBottom = nRectBottom;
1020 else
1022 // there has to be a point of intersection with the right border!
1023 GetLineIntersectionPoint(aPoint,
1024 Point(nRectRight, nRectTop), aDownHdg,
1025 rDiagPoint, aDiagHdg);
1027 nRight = nRectRight;
1028 nBottom = aPoint.Y();
1032 rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
1033 rPos.setX( nLeft );
1034 rPos.setY( nTop );
1038 void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1040 // Both arguments have to get into the SubNodes before the Operator so that clicking
1041 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1042 SmNode *pLeft = GetSubNode(0),
1043 *pRight = GetSubNode(1),
1044 *pLine = GetSubNode(2);
1045 assert(pLeft);
1046 assert(pRight);
1047 assert(pLine && pLine->GetType() == SmNodeType::PolyLine);
1049 SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
1050 assert(pOper);
1052 //! some routines being called extract some info from the OutputDevice's
1053 //! font (eg the space to be used for borders OR the font name(!!)).
1054 //! Thus the font should reflect the needs and has to be set!
1055 SmTmpDevice aTmpDev (rDev, true);
1056 aTmpDev.SetFont(GetFont());
1058 pLeft->Arrange(aTmpDev, rFormat);
1059 pRight->Arrange(aTmpDev, rFormat);
1061 // determine implicitly the values (incl. the margin) of the diagonal line
1062 pOper->Arrange(aTmpDev, rFormat);
1064 tools::Long nDelta = pOper->GetWidth() * 8 / 10;
1066 // determine TopLeft position from the right argument
1067 Point aPos;
1068 aPos.setX( pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace() );
1069 if (IsAscending())
1070 aPos.setY( pLeft->GetBottom() + nDelta );
1071 else
1072 aPos.setY( pLeft->GetTop() - nDelta - pRight->GetHeight() );
1074 pRight->MoveTo(aPos);
1076 // determine new baseline
1077 tools::Long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
1078 : (pLeft->GetTop() + pRight->GetBottom()) / 2;
1079 Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
1080 nTmpBaseline);
1082 SmRect::operator = (*pLeft);
1083 ExtendBy(*pRight, RectCopyMBL::None);
1086 // determine position and size of diagonal line
1087 Size aTmpSize;
1088 GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
1090 // font specialist advised to change the width first
1091 pOper->AdaptToY(aTmpDev, aTmpSize.Height());
1092 pOper->AdaptToX(aTmpDev, aTmpSize.Width());
1093 // and make it active
1094 pOper->Arrange(aTmpDev, rFormat);
1096 pOper->MoveTo(aPos);
1098 ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
1102 /**************************************************************************/
1105 void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1107 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
1108 "Sm: wrong number of subnodes");
1110 SmNode *pBody = GetBody();
1111 assert(pBody);
1113 tools::Long nOrigHeight = pBody->GetFont().GetFontSize().Height();
1115 pBody->Arrange(rDev, rFormat);
1117 const SmRect &rBodyRect = pBody->GetRect();
1118 SmRect::operator = (rBodyRect);
1120 // line that separates sub- and supscript rectangles
1121 tools::Long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1123 Point aPos;
1124 tools::Long nDelta, nDist;
1126 // iterate over all possible sub-/supscripts
1127 SmRect aTmpRect (rBodyRect);
1128 for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
1130 SmSubSup eSubSup = static_cast<SmSubSup>(i);
1131 SmNode *pSubSup = GetSubSup(eSubSup);
1133 if (!pSubSup)
1134 continue;
1136 // switch position of limits if we are in textmode
1137 if (rFormat.IsTextmode() && (GetToken().nGroup & TG::Limit))
1138 switch (eSubSup)
1139 { case CSUB: eSubSup = RSUB; break;
1140 case CSUP: eSubSup = RSUP; break;
1141 default:
1142 break;
1145 // prevent sub-/supscripts from diminishing in size
1146 // (as would be in "a_{1_{2_{3_4}}}")
1147 if (GetFont().GetFontSize().Height() > rFormat.GetBaseSize().Height() / 3)
1149 sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
1150 SIZ_LIMITS : SIZ_INDEX;
1151 Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
1152 pSubSup->SetSize(aFraction);
1155 pSubSup->Arrange(rDev, rFormat);
1157 bool bIsTextmode = rFormat.IsTextmode();
1158 nDist = 0;
1160 //! be sure that CSUB, CSUP are handled before the other cases!
1161 switch (eSubSup)
1162 { case RSUB :
1163 case LSUB :
1164 if (!bIsTextmode)
1165 nDist = nOrigHeight
1166 * rFormat.GetDistance(DIS_SUBSCRIPT) / 100;
1167 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1168 eSubSup == LSUB ? RectPos::Left : RectPos::Right,
1169 RectHorAlign::Center, RectVerAlign::Bottom);
1170 aPos.AdjustY(nDist );
1171 nDelta = nDelimLine - aPos.Y();
1172 if (nDelta > 0)
1173 aPos.AdjustY(nDelta );
1174 break;
1175 case RSUP :
1176 case LSUP :
1177 if (!bIsTextmode)
1178 nDist = nOrigHeight
1179 * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100;
1180 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1181 eSubSup == LSUP ? RectPos::Left : RectPos::Right,
1182 RectHorAlign::Center, RectVerAlign::Top);
1183 aPos.AdjustY( -nDist );
1184 nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
1185 if (nDelta > 0)
1186 aPos.AdjustY( -nDelta );
1187 break;
1188 case CSUB :
1189 if (!bIsTextmode)
1190 nDist = nOrigHeight
1191 * rFormat.GetDistance(DIS_LOWERLIMIT) / 100;
1192 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Bottom,
1193 RectHorAlign::Center, RectVerAlign::Baseline);
1194 aPos.AdjustY(nDist );
1195 break;
1196 case CSUP :
1197 if (!bIsTextmode)
1198 nDist = nOrigHeight
1199 * rFormat.GetDistance(DIS_UPPERLIMIT) / 100;
1200 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Top,
1201 RectHorAlign::Center, RectVerAlign::Baseline);
1202 aPos.AdjustY( -nDist );
1203 break;
1206 pSubSup->MoveTo(aPos);
1207 ExtendBy(*pSubSup, RectCopyMBL::This, true);
1209 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1210 // will be aligned to
1211 if (eSubSup == CSUB || eSubSup == CSUP)
1212 aTmpRect = *this;
1216 /**************************************************************************/
1218 void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1220 SmNode *pLeft = OpeningBrace(),
1221 *pBody = Body(),
1222 *pRight = ClosingBrace();
1223 assert(pLeft);
1224 assert(pBody);
1225 assert(pRight);
1227 pBody->Arrange(rDev, rFormat);
1229 bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
1230 bScale = pBody->GetHeight() > 0 &&
1231 (GetScaleMode() == SmScaleMode::Height || bIsScaleNormal),
1232 bIsABS = GetToken().eType == TABS;
1234 tools::Long nFaceHeight = GetFont().GetFontSize().Height();
1236 // determine oversize in %
1237 sal_uInt16 nPerc = 0;
1238 if (!bIsABS && bScale)
1239 { // in case of oversize braces...
1240 sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
1241 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1242 nPerc = rFormat.GetDistance(nIndex);
1245 // determine the height for the braces
1246 tools::Long nBraceHeight;
1247 if (bScale)
1249 nBraceHeight = pBody->GetType() == SmNodeType::Bracebody ?
1250 static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
1251 : pBody->GetHeight();
1252 nBraceHeight += 2 * (nBraceHeight * nPerc / 100);
1254 else
1255 nBraceHeight = nFaceHeight;
1257 // distance to the argument
1258 nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
1259 tools::Long nDist = nFaceHeight * nPerc / 100;
1261 // if wanted, scale the braces to the wanted size
1262 if (bScale)
1264 Size aTmpSize (pLeft->GetFont().GetFontSize());
1265 OSL_ENSURE(pRight->GetFont().GetFontSize() == aTmpSize,
1266 "Sm : different font sizes");
1267 aTmpSize.setWidth( std::min(nBraceHeight * 60 / 100,
1268 rFormat.GetBaseSize().Height() * 3 / 2) );
1269 // correction factor since change from StarMath to OpenSymbol font
1270 // because of the different font width in the FontMetric
1271 aTmpSize.setWidth( aTmpSize.Width() * 182 );
1272 aTmpSize.setWidth( aTmpSize.Width() / 267 );
1274 sal_Unicode cChar = pLeft->GetToken().cMathChar;
1275 if (cChar != MS_LINE && cChar != MS_DLINE &&
1276 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1277 pLeft ->GetFont().SetSize(aTmpSize);
1279 cChar = pRight->GetToken().cMathChar;
1280 if (cChar != MS_LINE && cChar != MS_DLINE &&
1281 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1282 pRight->GetFont().SetSize(aTmpSize);
1284 pLeft ->AdaptToY(rDev, nBraceHeight);
1285 pRight->AdaptToY(rDev, nBraceHeight);
1288 pLeft ->Arrange(rDev, rFormat);
1289 pRight->Arrange(rDev, rFormat);
1291 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1292 RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1294 Point aPos;
1295 aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
1296 aPos.AdjustX( -nDist );
1297 pLeft->MoveTo(aPos);
1299 aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
1300 aPos.AdjustX(nDist );
1301 pRight->MoveTo(aPos);
1303 SmRect::operator = (*pBody);
1304 ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
1308 /**************************************************************************/
1311 void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1313 size_t nNumSubNodes = GetNumSubNodes();
1314 if (nNumSubNodes == 0)
1315 return;
1317 // arrange arguments
1318 for (size_t i = 0; i < nNumSubNodes; i += 2)
1319 GetSubNode(i)->Arrange(rDev, rFormat);
1321 // build reference rectangle with necessary info for vertical alignment
1322 SmRect aRefRect (*GetSubNode(0));
1323 for (size_t i = 0; i < nNumSubNodes; i += 2)
1325 SmRect aTmpRect (*GetSubNode(i));
1326 Point aPos = aTmpRect.AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
1327 aTmpRect.MoveTo(aPos);
1328 aRefRect.ExtendBy(aTmpRect, RectCopyMBL::Xor);
1331 mnBodyHeight = aRefRect.GetHeight();
1333 // scale separators to required height and arrange them
1334 bool bScale = GetScaleMode() == SmScaleMode::Height || rFormat.IsScaleNormalBrackets();
1335 tools::Long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetFontSize().Height();
1336 sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
1337 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1338 sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
1339 if (bScale)
1340 nHeight += 2 * (nHeight * nPerc / 100);
1341 for (size_t i = 1; i < nNumSubNodes; i += 2)
1343 SmNode *pNode = GetSubNode(i);
1344 pNode->AdaptToY(rDev, nHeight);
1345 pNode->Arrange(rDev, rFormat);
1348 // horizontal distance between argument and brackets or separators
1349 tools::Long nDist = GetFont().GetFontSize().Height()
1350 * rFormat.GetDistance(DIS_BRACKETSPACE) / 100;
1352 SmNode *pLeft = GetSubNode(0);
1353 SmRect::operator = (*pLeft);
1354 for (size_t i = 1; i < nNumSubNodes; ++i)
1356 bool bIsSeparator = i % 2 != 0;
1357 RectVerAlign eVerAlign = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1359 SmNode *pRight = GetSubNode(i);
1360 Point aPosX = pRight->AlignTo(*pLeft, RectPos::Right, RectHorAlign::Center, eVerAlign),
1361 aPosY = pRight->AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, eVerAlign);
1362 aPosX.AdjustX(nDist );
1364 pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
1365 ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
1367 pLeft = pRight;
1372 /**************************************************************************/
1375 void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1377 SmNode *pBody = Body(),
1378 *pBrace = Brace(),
1379 *pScript = Script();
1380 assert(pBody);
1381 assert(pBrace);
1382 assert(pScript);
1384 SmTmpDevice aTmpDev (rDev, true);
1385 aTmpDev.SetFont(GetFont());
1387 pBody->Arrange(aTmpDev, rFormat);
1389 // size is the same as for limits for this part
1390 pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
1391 // braces are a bit taller than usually
1392 pBrace ->SetSize( Fraction(3, 2) );
1394 tools::Long nItalicWidth = pBody->GetItalicWidth();
1395 if (nItalicWidth > 0)
1396 pBrace->AdaptToX(aTmpDev, nItalicWidth);
1398 pBrace ->Arrange(aTmpDev, rFormat);
1399 pScript->Arrange(aTmpDev, rFormat);
1401 // determine the relative position and the distances between each other
1402 RectPos eRectPos;
1403 tools::Long nFontHeight = pBody->GetFont().GetFontSize().Height();
1404 tools::Long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
1405 nDistScript = nFontHeight;
1406 if (GetToken().eType == TOVERBRACE)
1408 eRectPos = RectPos::Top;
1409 nDistBody = - nDistBody;
1410 nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
1412 else // TUNDERBRACE
1414 eRectPos = RectPos::Bottom;
1415 nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
1417 nDistBody /= 100;
1418 nDistScript /= 100;
1420 Point aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1421 aPos.AdjustY(nDistBody );
1422 pBrace->MoveTo(aPos);
1424 aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1425 aPos.AdjustY(nDistScript );
1426 pScript->MoveTo(aPos);
1428 SmRect::operator = (*pBody);
1429 ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
1433 /**************************************************************************/
1436 SmNode * SmOperNode::GetSymbol()
1438 SmNode *pNode = GetSubNode(0);
1439 assert(pNode);
1441 if (pNode->GetType() == SmNodeType::SubSup)
1442 pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
1444 OSL_ENSURE(pNode, "Sm: NULL pointer!");
1445 return pNode;
1449 tools::Long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
1450 const SmFormat &rFormat) const
1451 // returns the font height to be used for operator-symbol
1453 tools::Long nHeight = GetFont().GetFontSize().Height();
1455 SmTokenType eTmpType = GetToken().eType;
1456 if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
1457 return nHeight;
1459 if (!rFormat.IsTextmode())
1461 // set minimum size ()
1462 nHeight += (nHeight * 20) / 100;
1464 nHeight += nHeight
1465 * rFormat.GetDistance(DIS_OPERATORSIZE) / 100;
1466 nHeight = nHeight * 686 / 845;
1469 // correct user-defined symbols to match height of sum from used font
1470 if (rSymbol.GetToken().eType == TSPECIAL)
1471 nHeight = nHeight * 845 / 686;
1473 return nHeight;
1477 void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1479 SmNode *pOper = GetSubNode(0);
1480 SmNode *pBody = GetSubNode(1);
1482 assert(pOper);
1483 assert(pBody);
1485 SmNode *pSymbol = GetSymbol();
1486 pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
1487 pSymbol->GetFont().GetFontSize().Height()));
1489 pBody->Arrange(rDev, rFormat);
1490 bool bDynamicallySized = false;
1491 if (pSymbol->GetToken().eType == TINTD)
1493 tools::Long nBodyHeight = pBody->GetHeight();
1494 tools::Long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
1495 if (nFontHeight < nBodyHeight)
1497 pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
1498 bDynamicallySized = true;
1501 pOper->Arrange(rDev, rFormat);
1503 tools::Long nOrigHeight = GetFont().GetFontSize().Height(),
1504 nDist = nOrigHeight
1505 * rFormat.GetDistance(DIS_OPERATORSPACE) / 100;
1507 Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
1508 aPos.AdjustX( -nDist );
1509 pOper->MoveTo(aPos);
1511 SmRect::operator = (*pBody);
1512 ExtendBy(*pOper, RectCopyMBL::This);
1516 /**************************************************************************/
1519 void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1520 // set alignment within the entire subtree (including current node)
1522 assert(GetNumSubNodes() == 1);
1524 SmNode *pNode = GetSubNode(0);
1525 assert(pNode);
1527 RectHorAlign eHorAlign = RectHorAlign::Center;
1528 switch (GetToken().eType)
1530 case TALIGNL: eHorAlign = RectHorAlign::Left; break;
1531 case TALIGNC: eHorAlign = RectHorAlign::Center; break;
1532 case TALIGNR: eHorAlign = RectHorAlign::Right; break;
1533 default:
1534 break;
1536 SetRectHorAlign(eHorAlign);
1538 pNode->Arrange(rDev, rFormat);
1540 SmRect::operator = (pNode->GetRect());
1544 /**************************************************************************/
1547 void SmAttributNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1549 SmNode *pAttr = Attribute(),
1550 *pBody = Body();
1551 assert(pBody);
1552 assert(pAttr);
1554 pBody->Arrange(rDev, rFormat);
1556 if (GetScaleMode() == SmScaleMode::Width)
1557 pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
1558 pAttr->Arrange(rDev, rFormat);
1560 // get relative position of attribute
1561 RectVerAlign eVerAlign;
1562 tools::Long nDist = 0;
1563 switch (GetToken().eType)
1564 { case TUNDERLINE :
1565 eVerAlign = RectVerAlign::AttributeLo;
1566 break;
1567 case TOVERSTRIKE :
1568 eVerAlign = RectVerAlign::AttributeMid;
1569 break;
1570 default :
1571 eVerAlign = RectVerAlign::AttributeHi;
1572 if (pBody->GetType() == SmNodeType::Attribut)
1573 nDist = GetFont().GetFontSize().Height()
1574 * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100;
1576 Point aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
1577 aPos.AdjustY( -nDist );
1578 pAttr->MoveTo(aPos);
1580 SmRect::operator = (*pBody);
1581 ExtendBy(*pAttr, RectCopyMBL::This, true);
1584 void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1586 //! prepare subnodes first
1587 SmNode::Prepare(rFormat, rDocShell, nDepth);
1589 int nFnt = -1;
1590 switch (GetToken().eType)
1592 case TFIXED: nFnt = FNT_FIXED; break;
1593 case TSANS: nFnt = FNT_SANS; break;
1594 case TSERIF: nFnt = FNT_SERIF; break;
1595 default:
1596 break;
1598 if (nFnt != -1)
1599 { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
1600 SetFont(GetFont());
1603 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
1604 //! other font nodes (those with lower depth in the tree)
1605 Flags() |= FontChangeMask::Face;
1608 void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1610 SmNode *pNode = GetSubNode(1);
1611 assert(pNode);
1612 sal_uInt32 nc;
1614 switch (GetToken().eType)
1615 { case TSIZE :
1616 pNode->SetFontSize(maFontSize, meSizeType);
1617 break;
1618 case TSANS :
1619 case TSERIF :
1620 case TFIXED :
1621 pNode->SetFont(GetFont());
1622 break;
1623 case TUNKNOWN : break; // no assertion on "font <?> <?>"
1625 case TPHANTOM : SetPhantom(true); break;
1626 case TBOLD : SetAttribut(FontAttribute::Bold); break;
1627 case TITALIC : SetAttribut(FontAttribute::Italic); break;
1628 case TNBOLD : ClearAttribut(FontAttribute::Bold); break;
1629 case TNITALIC : ClearAttribut(FontAttribute::Italic); break;
1631 // Using HTML CSS Level 1 standard
1632 case TRGB :
1633 case TRGBA :
1634 case THTMLCOL :
1635 case TMATHMLCOL :
1636 case TDVIPSNAMESCOL:
1637 case TICONICCOL :
1638 case THEX :
1639 nc = GetToken().aText.toUInt32(16);
1640 SetColor(Color(nc));
1641 break;
1643 default:
1644 SAL_WARN("starmath", "unknown case");
1647 pNode->Arrange(rDev, rFormat);
1649 SmRect::operator = (pNode->GetRect());
1652 /**************************************************************************/
1655 SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
1656 : SmGraphicNode(SmNodeType::PolyLine, rNodeToken)
1657 , maPoly(2)
1658 , maToSize()
1659 , mnWidth(0)
1664 void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
1666 maToSize.setWidth( nNewWidth );
1670 void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
1672 GetFont().FreezeBorderWidth();
1673 maToSize.setHeight( nNewHeight );
1677 void SmPolyLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1679 //! some routines being called extract some info from the OutputDevice's
1680 //! font (eg the space to be used for borders OR the font name(!!)).
1681 //! Thus the font should reflect the needs and has to be set!
1682 SmTmpDevice aTmpDev (rDev, true);
1683 aTmpDev.SetFont(GetFont());
1685 tools::Long nBorderwidth = GetFont().GetBorderWidth();
1687 // create polygon using both endpoints
1688 assert(maPoly.GetSize() == 2);
1689 Point aPointA, aPointB;
1690 if (GetToken().eType == TWIDESLASH)
1692 aPointA.setX( nBorderwidth );
1693 aPointA.setY( maToSize.Height() - nBorderwidth );
1694 aPointB.setX( maToSize.Width() - nBorderwidth );
1695 aPointB.setY( nBorderwidth );
1697 else
1699 OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
1700 aPointA.setX( nBorderwidth );
1701 aPointA.setY( nBorderwidth );
1702 aPointB.setX( maToSize.Width() - nBorderwidth );
1703 aPointB.setY( maToSize.Height() - nBorderwidth );
1705 maPoly.SetPoint(aPointA, 0);
1706 maPoly.SetPoint(aPointB, 1);
1708 tools::Long nThick = GetFont().GetFontSize().Height()
1709 * rFormat.GetDistance(DIS_STROKEWIDTH) / 100;
1710 mnWidth = nThick + 2 * nBorderwidth;
1712 SmRect::operator = (SmRect(maToSize.Width(), maToSize.Height()));
1716 /**************************************************************************/
1718 void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1720 mnBodyWidth = nWidth;
1724 void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
1726 // some additional length so that the horizontal
1727 // bar will be positioned above the argument
1728 SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10);
1732 /**************************************************************************/
1735 void SmRectangleNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1737 maToSize.setWidth( nWidth );
1741 void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
1743 GetFont().FreezeBorderWidth();
1744 maToSize.setHeight( nHeight );
1748 void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
1750 tools::Long nFontHeight = GetFont().GetFontSize().Height();
1751 tools::Long nWidth = maToSize.Width(),
1752 nHeight = maToSize.Height();
1753 if (nHeight == 0)
1754 nHeight = nFontHeight / 30;
1755 if (nWidth == 0)
1756 nWidth = nFontHeight / 3;
1758 SmTmpDevice aTmpDev (rDev, true);
1759 aTmpDev.SetFont(GetFont());
1761 // add some borderspace
1762 sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth();
1763 nHeight += 2 * nTmpBorderWidth;
1765 //! use this method in order to have 'SmRect::HasAlignInfo() == true'
1766 //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
1767 SmRect::operator = (SmRect(nWidth, nHeight));
1771 /**************************************************************************/
1774 SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
1775 : SmVisibleNode(eNodeType, rNodeToken)
1776 , mnFontDesc(nFontDescP)
1777 , mnSelectionStart(0)
1778 , mnSelectionEnd(0)
1782 SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
1783 : SmVisibleNode(SmNodeType::Text, rNodeToken)
1784 , mnFontDesc(nFontDescP)
1785 , mnSelectionStart(0)
1786 , mnSelectionEnd(0)
1790 void SmTextNode::ChangeText(const OUString &rText) {
1791 maText = rText;
1792 GetToken().aText = rText;
1793 AdjustFontDesc();
1796 void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1798 SmNode::Prepare(rFormat, rDocShell, nDepth);
1800 // default setting for horizontal alignment of nodes with TTEXT
1801 // content is as alignl (cannot be done in Arrange since it would
1802 // override the settings made by an SmAlignNode before)
1803 if (TTEXT == GetToken().eType)
1804 SetRectHorAlign( RectHorAlign::Left );
1806 maText = GetToken().aText;
1807 GetFont() = rFormat.GetFont(GetFontDesc());
1809 if (IsItalic( GetFont() ))
1810 Attributes() |= FontAttribute::Italic;
1811 if (IsBold( GetFont() ))
1812 Attributes() |= FontAttribute::Bold;
1814 // special handling for ':' where it is a token on its own and is likely
1815 // to be used for mathematical notations. (E.g. a:b = 2:3)
1816 // In that case it should not be displayed in italic.
1817 if (GetToken().aText.getLength() == 1 && GetToken().aText[0] == ':')
1818 Attributes() &= ~FontAttribute::Italic;
1822 void SmTextNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1824 PrepareAttributes();
1826 sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
1827 SIZ_FUNCTION : SIZ_TEXT;
1828 GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
1830 SmTmpDevice aTmpDev (rDev, true);
1831 aTmpDev.SetFont(GetFont());
1833 SmRect::operator = (SmRect(aTmpDev, &rFormat, maText, GetFont().GetBorderWidth()));
1836 void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
1838 rText.append(maText);
1841 void SmTextNode::AdjustFontDesc()
1843 if (GetToken().nGroup == TG::Function) mnFontDesc = FNT_FUNCTION;
1844 else if (GetToken().eType == TTEXT) mnFontDesc = FNT_TEXT;
1845 else {
1846 sal_Unicode firstChar = maText[0];
1847 if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',')
1848 mnFontDesc = FNT_NUMBER;
1849 else mnFontDesc = FNT_VARIABLE;
1853 sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
1855 //Find the best match in accepted unicode for our private area symbols
1856 static const sal_Unicode aStarMathPrivateToUnicode[] =
1858 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
1859 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
1860 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
1861 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
1862 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
1863 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
1864 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
1865 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
1866 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
1867 0xE0DA, 0x2190, 0x2191, 0x2193
1869 if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
1870 nIn = aStarMathPrivateToUnicode[nIn-0xE080];
1872 //For whatever unicode glyph that equation editor doesn't ship with that
1873 //we have a possible match we can munge it to.
1874 switch (nIn)
1876 case 0x2223:
1877 nIn = '|';
1878 break;
1879 default:
1880 break;
1883 return nIn;
1886 /**************************************************************************/
1888 void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1890 SmNode *pNode;
1892 // initialize array that is to hold the maximum widths of all
1893 // elements (subnodes) in that column.
1894 std::vector<tools::Long> aColWidth(mnNumCols);
1896 // arrange subnodes and calculate the above arrays contents
1897 size_t nNodes = GetNumSubNodes();
1898 for (size_t i = 0; i < nNodes; ++i)
1900 size_t nIdx = nNodes - 1 - i;
1901 if (nullptr != (pNode = GetSubNode(nIdx)))
1903 pNode->Arrange(rDev, rFormat);
1904 int nCol = nIdx % mnNumCols;
1905 aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
1909 // norm distance from which the following two are calculated
1910 const tools::Long nNormDist = 3 * GetFont().GetFontSize().Height();
1912 // define horizontal and vertical minimal distances that separate
1913 // the elements
1914 tools::Long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100,
1915 nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100;
1917 // build array that holds the leftmost position for each column
1918 std::vector<tools::Long> aColLeft(mnNumCols);
1919 tools::Long nX = 0;
1920 for (size_t j = 0; j < mnNumCols; ++j)
1922 aColLeft[j] = nX;
1923 nX += aColWidth[j] + nHorDist;
1926 SmRect::operator = (SmRect());
1927 for (size_t i = 0; i < mnNumRows; ++i)
1929 Point aPos;
1930 SmRect aLineRect;
1931 for (size_t j = 0; j < mnNumCols; ++j)
1933 SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
1934 assert(pTmpNode);
1936 const SmRect &rNodeRect = pTmpNode->GetRect();
1938 // align all baselines in that row if possible
1939 aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
1941 // get horizontal alignment
1942 const SmNode *pCoNode = pTmpNode->GetLeftMost();
1943 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
1945 // calculate horizontal position of element depending on column
1946 // and horizontal alignment
1947 switch (eHorAlign)
1948 { case RectHorAlign::Left:
1949 aPos.setX( aColLeft[j] );
1950 break;
1951 case RectHorAlign::Center:
1952 aPos.setX( rNodeRect.GetLeft() + aColLeft[j]
1953 + aColWidth[j] / 2
1954 - rNodeRect.GetItalicCenterX() );
1955 break;
1956 case RectHorAlign::Right:
1957 aPos.setX( aColLeft[j]
1958 + aColWidth[j] - rNodeRect.GetItalicWidth() );
1959 break;
1960 default:
1961 assert(false);
1964 pTmpNode->MoveTo(aPos);
1965 aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
1968 aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
1969 if (i > 0)
1970 aPos.AdjustY(nVerDist );
1972 // move 'aLineRect' and rectangles in that line to final position
1973 Point aDelta(0, // since horizontal alignment is already done
1974 aPos.Y() - aLineRect.GetTop());
1975 aLineRect.Move(aDelta);
1976 for (size_t j = 0; j < mnNumCols; ++j)
1978 if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
1979 pNode->Move(aDelta);
1982 ExtendBy(aLineRect, RectCopyMBL::None);
1986 const SmNode * SmMatrixNode::GetLeftMost() const
1988 return this;
1992 /**************************************************************************/
1995 SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
1996 : SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH)
1998 sal_Unicode cChar = GetToken().cMathChar;
1999 if (u'\0' != cChar)
2000 SetText(OUString(cChar));
2003 void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
2005 // Since there is no function to do this, we try to approximate it:
2006 Size aFntSize (GetFont().GetFontSize());
2008 //! however the result is a bit better with 'nWidth' as initial font width
2009 aFntSize.setWidth( nWidth );
2010 GetFont().SetSize(aFntSize);
2012 SmTmpDevice aTmpDev (rDev, true);
2013 aTmpDev.SetFont(GetFont());
2015 // get denominator of error factor for width
2016 tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
2017 tools::Long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
2019 // scale fontwidth with this error factor
2020 aFntSize.setWidth( aFntSize.Width() * nWidth );
2021 aFntSize.setWidth( aFntSize.Width() / ( nDenom ? nDenom : 1) );
2023 GetFont().SetSize(aFntSize);
2026 void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
2028 GetFont().FreezeBorderWidth();
2029 Size aFntSize (GetFont().GetFontSize());
2031 // Since we only want to scale the height, we might have
2032 // to determine the font width in order to keep it
2033 if (aFntSize.Width() == 0)
2035 rDev.Push(PushFlags::FONT | PushFlags::MAPMODE);
2036 rDev.SetFont(GetFont());
2037 aFntSize.setWidth( rDev.GetFontMetric().GetFontSize().Width() );
2038 rDev.Pop();
2040 OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
2042 //! however the result is a bit better with 'nHeight' as initial
2043 //! font height
2044 aFntSize.setHeight( nHeight );
2045 GetFont().SetSize(aFntSize);
2047 SmTmpDevice aTmpDev (rDev, true);
2048 aTmpDev.SetFont(GetFont());
2050 // get denominator of error factor for height
2051 tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
2052 tools::Long nDenom = 0;
2053 if (!GetText().isEmpty())
2054 nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetHeight();
2056 // scale fontwidth with this error factor
2057 aFntSize.setHeight( aFntSize.Height() * nHeight );
2058 aFntSize.setHeight( aFntSize.Height() / ( nDenom ? nDenom : 1) );
2060 GetFont().SetSize(aFntSize);
2064 void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2066 SmNode::Prepare(rFormat, rDocShell, nDepth);
2068 GetFont() = rFormat.GetFont(GetFontDesc());
2069 // use same font size as is used for variables
2070 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2072 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
2073 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
2074 "wrong charset for character from StarMath/OpenSymbol font");
2076 Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
2080 void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2082 const OUString &rText = GetText();
2084 if (rText.isEmpty() || rText[0] == '\0')
2085 { SmRect::operator = (SmRect());
2086 return;
2089 PrepareAttributes();
2091 GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
2093 SmTmpDevice aTmpDev (rDev, true);
2094 aTmpDev.SetFont(GetFont());
2096 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2099 /**************************************************************************/
2101 static bool lcl_IsFromGreekSymbolSet( const OUString &rTokenText )
2103 bool bRes = false;
2105 // valid symbol name needs to have a '%' at pos 0 and at least an additional char
2106 if (rTokenText.getLength() > 2 && rTokenText[0] == u'%')
2108 OUString aName( rTokenText.copy(1) );
2109 SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName );
2110 if (pSymbol && SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
2111 bRes = true;
2114 return bRes;
2118 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
2119 : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
2120 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
2125 SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
2126 : SmTextNode(SmNodeType::Special, rNodeToken, FNT_MATH) // default Font isn't always correct!
2127 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
2132 void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2134 SmNode::Prepare(rFormat, rDocShell, nDepth);
2136 const SmSym *pSym;
2137 SmModule *pp = SM_MOD();
2139 OUString aName(GetToken().aText.copy(1));
2140 if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName( aName )))
2142 sal_UCS4 cChar = pSym->GetCharacter();
2143 OUString aTmp( &cChar, 1 );
2144 SetText( aTmp );
2145 GetFont() = pSym->GetFace();
2147 else
2149 SetText( GetToken().aText );
2150 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2152 // use same font size as is used for variables
2153 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2155 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2156 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2157 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2159 //! see also SmFontStyles::GetStyleName
2160 if (IsItalic( GetFont() ))
2161 SetAttribut(FontAttribute::Italic);
2162 if (IsBold( GetFont() ))
2163 SetAttribut(FontAttribute::Bold);
2165 Flags() |= FontChangeMask::Face;
2167 if (!mbIsFromGreekSymbolSet)
2168 return;
2170 OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
2171 bool bItalic = false;
2172 sal_Int16 nStyle = rFormat.GetGreekCharStyle();
2173 OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
2174 if (nStyle == 1)
2175 bItalic = true;
2176 else if (nStyle == 2)
2178 const OUString& rTmp(GetText());
2179 if (!rTmp.isEmpty())
2181 static const sal_Unicode cUppercaseAlpha = 0x0391;
2182 static const sal_Unicode cUppercaseOmega = 0x03A9;
2183 sal_Unicode cChar = rTmp[0];
2184 // uppercase letters should be straight and lowercase letters italic
2185 bItalic = cUppercaseAlpha > cChar || cChar > cUppercaseOmega;
2189 if (bItalic)
2190 Attributes() |= FontAttribute::Italic;
2191 else
2192 Attributes() &= ~FontAttribute::Italic;
2196 void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2198 PrepareAttributes();
2200 SmTmpDevice aTmpDev (rDev, true);
2201 aTmpDev.SetFont(GetFont());
2203 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2206 /**************************************************************************/
2209 void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2211 PrepareAttributes();
2213 SmTmpDevice aTmpDev (rDev, true);
2214 aTmpDev.SetFont(GetFont());
2216 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
2217 GetFont().GetBorderWidth()).AsGlyphRect());
2221 /**************************************************************************/
2224 void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2226 SmNode::Prepare(rFormat, rDocShell, nDepth);
2228 GetFont().SetColor(COL_GRAY);
2229 Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
2233 void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2235 PrepareAttributes();
2237 SmTmpDevice aTmpDev (rDev, true);
2238 aTmpDev.SetFont(GetFont());
2240 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2244 /**************************************************************************/
2247 void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2249 SmNode::Prepare(rFormat, rDocShell, nDepth);
2251 GetFont().SetColor(COL_RED);
2252 Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
2253 | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
2257 void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2259 PrepareAttributes();
2261 SmTmpDevice aTmpDev (rDev, true);
2262 aTmpDev.SetFont(GetFont());
2264 const OUString &rText = GetText();
2265 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2268 /**************************************************************************/
2270 void SmBlankNode::IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy)
2272 switch(rToken.eType)
2274 case TBLANK: mnNum += (4 * nMultiplyBy); break;
2275 case TSBLANK: mnNum += (1 * nMultiplyBy); break;
2276 default:
2277 break;
2281 void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2283 SmNode::Prepare(rFormat, rDocShell, nDepth);
2285 // Here it need/should not be the StarMath font, so that for the character
2286 // used in Arrange a normal (non-clipped) rectangle is generated
2287 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2289 Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
2293 void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2295 SmTmpDevice aTmpDev (rDev, true);
2296 aTmpDev.SetFont(GetFont());
2298 // make distance depend on the font height
2299 // (so that it increases when scaling (e.g. size *2 {a ~ b})
2300 tools::Long nDist = GetFont().GetFontSize().Height() / 10,
2301 nSpace = mnNum * nDist;
2303 // get a SmRect with Baseline and all the bells and whistles
2304 SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
2305 GetFont().GetBorderWidth()));
2307 // and resize it to the requested size
2308 SetItalicSpaces(0, 0);
2309 SetWidth(nSpace);
2312 /**************************************************************************/
2313 //Implementation of all accept methods for SmVisitor
2315 void SmTableNode::Accept(SmVisitor* pVisitor) {
2316 pVisitor->Visit(this);
2319 void SmBraceNode::Accept(SmVisitor* pVisitor) {
2320 pVisitor->Visit(this);
2323 void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
2324 pVisitor->Visit(this);
2327 void SmOperNode::Accept(SmVisitor* pVisitor) {
2328 pVisitor->Visit(this);
2331 void SmAlignNode::Accept(SmVisitor* pVisitor) {
2332 pVisitor->Visit(this);
2335 void SmAttributNode::Accept(SmVisitor* pVisitor) {
2336 pVisitor->Visit(this);
2339 void SmFontNode::Accept(SmVisitor* pVisitor) {
2340 pVisitor->Visit(this);
2343 void SmUnHorNode::Accept(SmVisitor* pVisitor) {
2344 pVisitor->Visit(this);
2347 void SmBinHorNode::Accept(SmVisitor* pVisitor) {
2348 pVisitor->Visit(this);
2351 void SmBinVerNode::Accept(SmVisitor* pVisitor) {
2352 pVisitor->Visit(this);
2355 void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
2356 pVisitor->Visit(this);
2359 void SmSubSupNode::Accept(SmVisitor* pVisitor) {
2360 pVisitor->Visit(this);
2363 void SmMatrixNode::Accept(SmVisitor* pVisitor) {
2364 pVisitor->Visit(this);
2367 void SmPlaceNode::Accept(SmVisitor* pVisitor) {
2368 pVisitor->Visit(this);
2371 void SmTextNode::Accept(SmVisitor* pVisitor) {
2372 pVisitor->Visit(this);
2375 void SmSpecialNode::Accept(SmVisitor* pVisitor) {
2376 pVisitor->Visit(this);
2379 void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
2380 pVisitor->Visit(this);
2383 void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
2384 pVisitor->Visit(this);
2387 void SmBlankNode::Accept(SmVisitor* pVisitor) {
2388 pVisitor->Visit(this);
2391 void SmErrorNode::Accept(SmVisitor* pVisitor) {
2392 pVisitor->Visit(this);
2395 void SmLineNode::Accept(SmVisitor* pVisitor) {
2396 pVisitor->Visit(this);
2399 void SmExpressionNode::Accept(SmVisitor* pVisitor) {
2400 pVisitor->Visit(this);
2403 void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
2404 pVisitor->Visit(this);
2407 void SmRootNode::Accept(SmVisitor* pVisitor) {
2408 pVisitor->Visit(this);
2411 void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
2412 pVisitor->Visit(this);
2415 void SmRectangleNode::Accept(SmVisitor* pVisitor) {
2416 pVisitor->Visit(this);
2419 void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
2420 pVisitor->Visit(this);
2423 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */