Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / starmath / source / node.cxx
blob7a9d42bd4fe1658324a7ae237bdb83a91453524c
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 <symbol.hxx>
22 #include <smmod.hxx>
23 #include "tmpdevice.hxx"
24 #include <utility>
25 #include <visitors.hxx>
26 #include <tools/UnitConversion.hxx>
27 #include <vcl/metric.hxx>
28 #include <o3tl/safeint.hxx>
29 #include <osl/diagnose.h>
30 #include <basegfx/numeric/ftools.hxx>
32 namespace {
34 template<typename F>
35 void ForEachNonNull(SmNode *pNode, F && f)
37 size_t nSize = pNode->GetNumSubNodes();
38 for (size_t i = 0; i < nSize; ++i)
40 SmNode *pSubNode = pNode->GetSubNode(i);
41 if (pSubNode != nullptr)
42 f(pSubNode);
48 SmNode::SmNode(SmNodeType eNodeType, SmToken aNodeToken)
49 : maNodeToken(std::move( aNodeToken ))
50 , meType( eNodeType )
51 , meScaleMode( SmScaleMode::None )
52 , meRectHorAlign( RectHorAlign::Left )
53 , mnFlags( FontChangeMask::None )
54 , mnAttributes( FontAttribute::None )
55 , mbIsPhantom( false )
56 , mbIsSelected( false )
57 , mnAccIndex( -1 )
58 , mpParentNode( nullptr )
62 SmNode::~SmNode()
66 const SmNode * SmNode::GetLeftMost() const
67 // returns leftmost node of current subtree.
68 //! (this assumes the one with index 0 is always the leftmost subnode
69 //! for the current node).
71 const SmNode *pNode = GetNumSubNodes() > 0 ?
72 GetSubNode(0) : nullptr;
74 return pNode ? pNode->GetLeftMost() : this;
78 void SmNode::SetPhantom(bool bIsPhantomP)
80 if (! (Flags() & FontChangeMask::Phantom))
81 mbIsPhantom = bIsPhantomP;
83 bool b = mbIsPhantom;
84 ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
88 void SmNode::SetColor(const Color& rColor)
90 if (! (Flags() & FontChangeMask::Color))
91 GetFont().SetColor(rColor);
93 ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
97 void SmNode::SetAttribute(FontAttribute nAttrib)
99 if (
100 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
101 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
104 mnAttributes |= nAttrib;
107 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribute(nAttrib);});
111 void SmNode::ClearAttribute(FontAttribute nAttrib)
113 if (
114 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
115 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
118 mnAttributes &= ~nAttrib;
121 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribute(nAttrib);});
125 void SmNode::SetFont(const SmFace &rFace)
127 if (!(Flags() & FontChangeMask::Face))
128 GetFont() = rFace;
129 ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
133 void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
134 //! 'rSize' is in units of pts
136 Size aFntSize;
138 if (!(Flags() & FontChangeMask::Size))
140 Fraction aVal(conversionFract(o3tl::Length::pt, SmO3tlLengthUnit()) * rSize);
141 tools::Long nHeight = static_cast<tools::Long>(aVal);
143 aFntSize = GetFont().GetFontSize();
144 aFntSize.setWidth( 0 );
145 switch(nType)
147 case FontSizeType::ABSOLUT:
148 aFntSize.setHeight( nHeight );
149 break;
151 case FontSizeType::PLUS:
152 aFntSize.AdjustHeight(nHeight );
153 break;
155 case FontSizeType::MINUS:
156 aFntSize.AdjustHeight( -nHeight );
157 break;
159 case FontSizeType::MULTIPLY:
160 aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) * rSize) );
161 break;
163 case FontSizeType::DIVIDE:
164 if (rSize != Fraction(0))
165 aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) / rSize) );
166 break;
167 default:
168 break;
171 // check the requested size against maximum value
172 const int nMaxVal = o3tl::convert(128, o3tl::Length::pt, SmO3tlLengthUnit());
173 if (aFntSize.Height() > nMaxVal)
174 aFntSize.setHeight( nMaxVal );
176 GetFont().SetSize(aFntSize);
179 ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
183 void SmNode::SetSize(const Fraction &rSize)
185 GetFont() *= rSize;
187 ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
191 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
193 meRectHorAlign = eHorAlign;
195 if (bApplyToSubTree)
196 ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
200 void SmNode::PrepareAttributes()
202 GetFont().SetWeight((Attributes() & FontAttribute::Bold) ? WEIGHT_BOLD : WEIGHT_NORMAL);
203 GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
207 void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
209 if (nDepth > 1024)
210 throw std::range_error("parser depth limit");
212 mbIsPhantom = false;
213 mnFlags = FontChangeMask::None;
214 mnAttributes = FontAttribute::None;
216 switch (rFormat.GetHorAlign())
217 { case SmHorAlign::Left: meRectHorAlign = RectHorAlign::Left; break;
218 case SmHorAlign::Center: meRectHorAlign = RectHorAlign::Center; break;
219 case SmHorAlign::Right: meRectHorAlign = RectHorAlign::Right; break;
222 GetFont() = rFormat.GetFont(FNT_MATH);
223 OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
224 "unexpected CharSet" );
225 GetFont().SetWeight(WEIGHT_NORMAL);
226 GetFont().SetItalic(ITALIC_NONE);
228 ForEachNonNull(this, [&rFormat, &rDocShell, nDepth](SmNode *pNode){pNode->Prepare(rFormat, rDocShell, nDepth + 1);});
231 void SmNode::Move(const Point& rVector)
233 if (rVector.X() == 0 && rVector.Y() == 0)
234 return;
236 SmRect::Move(rVector);
238 ForEachNonNull(this, [&rVector](SmNode *pNode){pNode->Move(rVector);});
241 void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
246 void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
251 const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
252 // returns (first) ** visible ** (sub)node with the tokens text at
253 // position 'nRow', 'nCol'.
254 //! (there should be exactly one such node if any)
256 if ( IsVisible()
257 && nRow == GetSelection().nStartPara
258 && nCol >= GetSelection().nStartPos && nCol <= GetSelection().nEndPos )
259 return this;
260 else
262 size_t nNumSubNodes = GetNumSubNodes();
263 for (size_t i = 0; i < nNumSubNodes; ++i)
265 const SmNode *pNode = GetSubNode(i);
267 if (!pNode)
268 continue;
270 const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
271 if (pResult)
272 return pResult;
276 return nullptr;
280 const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
282 tools::Long nDist = LONG_MAX;
283 const SmNode *pResult = nullptr;
285 if (IsVisible())
286 pResult = this;
287 else
289 size_t nNumSubNodes = GetNumSubNodes();
290 for (size_t i = 0; i < nNumSubNodes; ++i)
292 const SmNode *pNode = GetSubNode(i);
294 if (!pNode)
295 continue;
297 const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
298 if (pFound)
300 tools::Long nTmp = pFound->OrientedDist(rPoint);
301 if (nTmp < nDist)
303 nDist = nTmp;
304 pResult = pFound;
306 // quit immediately if 'rPoint' is inside the *should not
307 // overlap with other rectangles* part.
308 // This (partly) serves for getting the attributes in eg
309 // "bar overstrike a".
310 // ('nDist < 0' is used as *quick shot* to avoid evaluation of
311 // the following expression, where the result is already determined)
312 if (nDist < 0 && pFound->IsInsideRect(rPoint))
313 break;
319 return pResult;
322 const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
324 const SmNode *pResult = nullptr;
326 sal_Int32 nIdx = GetAccessibleIndex();
327 OUStringBuffer aTxt;
328 if (nIdx >= 0)
329 GetAccessibleText( aTxt ); // get text if used in following 'if' statement
331 if (nIdx >= 0
332 && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.getLength())
333 pResult = this;
334 else
336 size_t nNumSubNodes = GetNumSubNodes();
337 for (size_t i = 0; i < nNumSubNodes; ++i)
339 const SmNode *pNode = GetSubNode(i);
340 if (!pNode)
341 continue;
343 pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
344 if (pResult)
345 return pResult;
349 return pResult;
353 SmStructureNode::~SmStructureNode()
355 ForEachNonNull(this, std::default_delete<SmNode>());
359 void SmStructureNode::ClearSubNodes()
361 maSubNodes.clear();
364 void SmStructureNode::SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
366 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
367 maSubNodes.resize( nSize );
368 if (pFirst)
369 maSubNodes[0] = pFirst.release();
370 if (pSecond)
371 maSubNodes[1] = pSecond.release();
372 if (pThird)
373 maSubNodes[2] = pThird.release();
375 ClaimPaternity();
378 void SmStructureNode::SetSubNodes(SmNode* pFirst, SmNode* pSecond, SmNode* pThird)
380 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
381 maSubNodes.resize( nSize );
382 if (pFirst)
383 maSubNodes[0] = pFirst;
384 if (pSecond)
385 maSubNodes[1] = pSecond;
386 if (pThird)
387 maSubNodes[2] = pThird;
389 ClaimPaternity();
392 void SmStructureNode::SetSubNodesBinMo(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
394 if(GetType()==SmNodeType::BinDiagonal)
396 size_t nSize = pSecond ? 3 : (pThird ? 2 : (pFirst ? 1 : 0));
397 maSubNodes.resize( nSize );
398 if (pFirst)
399 maSubNodes[0] = pFirst.release();
400 if (pSecond)
401 maSubNodes[2] = pSecond.release();
402 if (pThird)
403 maSubNodes[1] = pThird.release();
405 else
407 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
408 maSubNodes.resize( nSize );
409 if (pFirst)
410 maSubNodes[0] = pFirst.release();
411 if (pSecond)
412 maSubNodes[1] = pSecond.release();
413 if (pThird)
414 maSubNodes[2] = pThird.release();
416 ClaimPaternity();
419 void SmStructureNode::SetSubNodes(SmNodeArray&& rNodeArray)
421 maSubNodes = std::move(rNodeArray);
422 ClaimPaternity();
425 bool SmStructureNode::IsVisible() const
427 return false;
430 size_t SmStructureNode::GetNumSubNodes() const
432 return maSubNodes.size();
435 SmNode* SmStructureNode::GetSubNode(size_t nIndex)
437 return maSubNodes[nIndex];
440 SmNode* SmStructureNode::GetSubNodeBinMo(size_t nIndex) const
442 if(GetType()==SmNodeType::BinDiagonal)
444 if (nIndex==1)
445 nIndex = 2;
446 else if (nIndex==2)
447 nIndex = 1;
449 return maSubNodes[nIndex];
452 void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
454 ForEachNonNull(const_cast<SmStructureNode *>(this),
455 [&rText](SmNode *pNode)
457 if (pNode->IsVisible())
458 pNode->SetAccessibleIndex(rText.getLength());
459 pNode->GetAccessibleText( rText );
463 void SmStructureNode::ClaimPaternity()
465 ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
468 int SmStructureNode::IndexOfSubNode(SmNode const * pSubNode)
470 size_t nSize = GetNumSubNodes();
471 for (size_t i = 0; i < nSize; i++)
472 if (pSubNode == GetSubNode(i))
473 return i;
474 return -1;
477 void SmStructureNode::SetSubNode(size_t nIndex, SmNode* pNode)
479 size_t size = maSubNodes.size();
480 if (size <= nIndex)
482 //Resize subnodes array
483 maSubNodes.resize(nIndex + 1);
484 //Set new slots to NULL except at nIndex
485 for (size_t i = size; i < nIndex; i++)
486 maSubNodes[i] = nullptr;
488 maSubNodes[nIndex] = pNode;
489 if (pNode)
490 pNode->SetParent(this);
493 bool SmVisibleNode::IsVisible() const
495 return true;
498 size_t SmVisibleNode::GetNumSubNodes() const
500 return 0;
503 SmNode * SmVisibleNode::GetSubNode(size_t /*nIndex*/)
505 return nullptr;
508 void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
510 rText.append(GetToken().aText);
513 void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
514 // arranges all subnodes in one column
516 SmNode *pNode;
517 size_t nSize = GetNumSubNodes();
519 // make distance depend on font size
520 tools::Long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
521 * GetFont().GetFontSize().Height()) / 100;
523 if (nSize < 1)
524 return;
526 // arrange subnodes and get maximum width of them
527 tools::Long nMaxWidth = 0,
528 nTmp;
529 for (size_t i = 0; i < nSize; ++i)
531 if (nullptr != (pNode = GetSubNode(i)))
532 { pNode->Arrange(rDev, rFormat);
533 if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
534 nMaxWidth = nTmp;
538 Point aPos;
539 SmRect::operator = (SmRect(nMaxWidth, 1));
540 for (size_t i = 0; i < nSize; ++i)
542 if (nullptr != (pNode = GetSubNode(i)))
543 { const SmRect &rNodeRect = pNode->GetRect();
544 const SmNode *pCoNode = pNode->GetLeftMost();
545 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
547 aPos = rNodeRect.AlignTo(*this, RectPos::Bottom,
548 eHorAlign, RectVerAlign::Baseline);
549 if (i)
550 aPos.AdjustY(nDist );
551 pNode->MoveTo(aPos);
552 ExtendBy(rNodeRect, nSize > 1 ? RectCopyMBL::None : RectCopyMBL::Arg);
555 // #i972#
556 if (HasBaseline())
557 mnFormulaBaseline = GetBaseline();
558 else
560 SmTmpDevice aTmpDev (rDev, true);
561 aTmpDev.SetFont(GetFont());
563 SmRect aRect(aTmpDev, &rFormat, "a", GetFont().GetBorderWidth());
564 mnFormulaBaseline = GetAlignM();
565 // move from middle position by constant - distance
566 // between middle and baseline for single letter
567 mnFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
571 const SmNode * SmTableNode::GetLeftMost() const
573 return this;
577 tools::Long SmTableNode::GetFormulaBaseline() const
579 return mnFormulaBaseline;
583 /**************************************************************************/
586 void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
588 SmNode::Prepare(rFormat, rDocShell, nDepth);
590 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
591 // to the rest of the formula compared to the 'FNT_MATH' font.
592 GetFont() = rFormat.GetFont(FNT_VARIABLE);
593 Flags() |= FontChangeMask::Face;
597 /**************************************************************************/
600 void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
601 // arranges all subnodes in one row with some extra space between
603 SmNode *pNode;
604 size_t nSize = GetNumSubNodes();
605 for (size_t i = 0; i < nSize; ++i)
607 if (nullptr != (pNode = GetSubNode(i)))
608 pNode->Arrange(rDev, rFormat);
611 SmTmpDevice aTmpDev (rDev, true);
612 aTmpDev.SetFont(GetFont());
614 if (nSize < 1)
616 // provide an empty rectangle with alignment parameters for the "current"
617 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
618 // same sub-/supscript positions.)
619 //! be sure to use a character that has explicitly defined HiAttribut
620 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
621 //! 'vec {a}'.
622 SmRect::operator = (SmRect(aTmpDev, &rFormat, "a",
623 GetFont().GetBorderWidth()));
624 // make sure that the rectangle occupies (almost) no space
625 SetWidth(1);
626 SetItalicSpaces(0, 0);
627 return;
630 // make distance depend on font size
631 tools::Long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetFontSize().Height()) / 100;
632 if (!IsUseExtraSpaces())
633 nDist = 0;
635 Point aPos;
636 // copy the first node into LineNode and extend by the others
637 if (nullptr != (pNode = GetSubNode(0)))
638 SmRect::operator = (pNode->GetRect());
640 for (size_t i = 1; i < nSize; ++i)
642 if (nullptr != (pNode = GetSubNode(i)))
644 aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
646 // add horizontal space to the left for each but the first sub node
647 aPos.AdjustX(nDist );
649 pNode->MoveTo(aPos);
650 ExtendBy( *pNode, RectCopyMBL::Xor );
656 /**************************************************************************/
659 void SmExpressionNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
660 // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
662 SmLineNode::Arrange(rDev, rFormat);
664 // copy alignment of leftmost subnode if any
665 const SmNode *pNode = GetLeftMost();
666 if (pNode)
667 SetRectHorAlign(pNode->GetRectHorAlign(), false);
671 /**************************************************************************/
674 void SmUnHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
676 bool bIsPostfix = GetToken().eType == TFACT;
678 SmNode *pNode0 = GetSubNode(0),
679 *pNode1 = GetSubNode(1);
680 SmNode *pOper = bIsPostfix ? pNode1 : pNode0,
681 *pBody = bIsPostfix ? pNode0 : pNode1;
682 assert(pOper);
683 assert(pBody);
685 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
686 pOper->Arrange(rDev, rFormat);
687 pBody->Arrange(rDev, rFormat);
689 tools::Long nDist = (pOper->GetRect().GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
691 SmRect::operator = (*pNode0);
693 Point aPos = pNode1->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
694 aPos.AdjustX(nDist );
695 pNode1->MoveTo(aPos);
696 ExtendBy(*pNode1, RectCopyMBL::Xor);
700 /**************************************************************************/
702 namespace {
704 void lcl_GetHeightVerOffset(const SmRect &rRect,
705 tools::Long &rHeight, tools::Long &rVerOffset)
706 // calculate height and vertical offset of root sign suitable for 'rRect'
708 rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
709 rHeight = rRect.GetHeight() - rVerOffset;
711 OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
712 OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
716 Point lcl_GetExtraPos(const SmRect &rRootSymbol,
717 const SmRect &rExtra)
719 const Size &rSymSize = rRootSymbol.GetSize();
721 Point aPos = rRootSymbol.GetTopLeft()
722 + Point((rSymSize.Width() * 70) / 100,
723 (rSymSize.Height() * 52) / 100);
725 // from this calculate topleft edge of 'rExtra'
726 aPos.AdjustX( -(rExtra.GetWidth() + rExtra.GetItalicRightSpace()) );
727 aPos.AdjustY( -(rExtra.GetHeight()) );
728 // if there's enough space move a bit less to the right
729 // examples: "nroot i a", "nroot j a"
730 // (it looks better if we don't use italic-spaces here)
731 tools::Long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
732 if (aPos.X() > nX)
733 aPos.setX( nX );
735 return aPos;
740 void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
742 //! pExtra needs to have the smaller index than pRootSym in order to
743 //! not to get the root symbol but the pExtra when clicking on it in the
744 //! GraphicWindow. (That is because of the simplicity of the algorithm
745 //! that finds the node corresponding to a mouseclick in the window.)
746 SmNode *pExtra = GetSubNode(0),
747 *pRootSym = GetSubNode(1),
748 *pBody = GetSubNode(2);
749 assert(pRootSym);
750 assert(pBody);
752 pBody->Arrange(rDev, rFormat);
754 tools::Long nHeight,
755 nVerOffset;
756 lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset);
757 nHeight += rFormat.GetDistance(DIS_ROOT)
758 * GetFont().GetFontSize().Height() / 100;
760 if (nHeight < 0)
762 SAL_WARN("starmath", "negative height");
763 nHeight = 0;
766 // font specialist advised to change the width first
767 pRootSym->AdaptToY(rDev, nHeight);
768 pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
770 pRootSym->Arrange(rDev, rFormat);
772 Point aPos = pRootSym->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, RectVerAlign::Baseline);
773 //! override calculated vertical position
774 aPos.setY( pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom() );
775 aPos.AdjustY( -nVerOffset );
776 pRootSym->MoveTo(aPos);
778 if (pExtra)
779 { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
780 pExtra->Arrange(rDev, rFormat);
782 aPos = lcl_GetExtraPos(*pRootSym, *pExtra);
783 pExtra->MoveTo(aPos);
786 SmRect::operator = (*pBody);
787 ExtendBy(*pRootSym, RectCopyMBL::This);
788 if (pExtra)
789 ExtendBy(*pExtra, RectCopyMBL::This, true);
792 /**************************************************************************/
795 void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
797 SmNode *pLeft = LeftOperand(),
798 *pOper = Symbol(),
799 *pRight = RightOperand();
800 assert(pLeft);
801 assert(pOper);
802 assert(pRight);
804 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
806 pLeft ->Arrange(rDev, rFormat);
807 pOper ->Arrange(rDev, rFormat);
808 pRight->Arrange(rDev, rFormat);
810 const SmRect &rOpRect = pOper->GetRect();
812 tools::Long nMul;
813 if (o3tl::checked_multiply<tools::Long>(rOpRect.GetWidth(), rFormat.GetDistance(DIS_HORIZONTAL), nMul))
815 SAL_WARN("starmath", "integer overflow");
816 return;
819 tools::Long nDist = nMul / 100;
821 SmRect::operator = (*pLeft);
823 Point aPos;
824 aPos = pOper->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
825 aPos.AdjustX(nDist );
826 pOper->MoveTo(aPos);
827 ExtendBy(*pOper, RectCopyMBL::Xor);
829 aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
830 aPos.AdjustX(nDist );
832 pRight->MoveTo(aPos);
833 ExtendBy(*pRight, RectCopyMBL::Xor);
837 /**************************************************************************/
840 void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
842 SmNode *pNum = GetSubNode(0),
843 *pLine = GetSubNode(1),
844 *pDenom = GetSubNode(2);
845 assert(pNum);
846 assert(pLine);
847 assert(pDenom);
849 bool bIsTextmode = rFormat.IsTextmode();
850 if (bIsTextmode)
852 Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
853 pNum ->SetSize(aFraction);
854 pLine ->SetSize(aFraction);
855 pDenom->SetSize(aFraction);
858 pNum ->Arrange(rDev, rFormat);
859 pDenom->Arrange(rDev, rFormat);
861 tools::Long nFontHeight = GetFont().GetFontSize().Height(),
862 nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100,
863 nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100,
864 nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
865 nNumDist = bIsTextmode ? 0 :
866 nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100,
867 nDenomDist = bIsTextmode ? 0 :
868 nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100;
870 // font specialist advised to change the width first
871 pLine->AdaptToY(rDev, nThick);
872 pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
873 pLine->Arrange(rDev, rFormat);
875 // get horizontal alignment for numerator
876 const SmNode *pLM = pNum->GetLeftMost();
877 RectHorAlign eHorAlign = pLM->GetRectHorAlign();
879 // move numerator to its position
880 Point aPos = pNum->AlignTo(*pLine, RectPos::Top, eHorAlign, RectVerAlign::Baseline);
881 aPos.AdjustY( -nNumDist );
882 pNum->MoveTo(aPos);
884 // get horizontal alignment for denominator
885 pLM = pDenom->GetLeftMost();
886 eHorAlign = pLM->GetRectHorAlign();
888 // move denominator to its position
889 aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
890 aPos.AdjustY(nDenomDist );
891 pDenom->MoveTo(aPos);
893 SmRect::operator = (*pNum);
894 ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
897 const SmNode * SmBinVerNode::GetLeftMost() const
899 return this;
903 namespace {
905 /// @return value of the determinant formed by the two points
906 double Det(const Point &rHeading1, const Point &rHeading2)
908 return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
912 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
913 /// and has the direction vector 'rHeading2'
914 bool IsPointInLine(const Point &rPoint1,
915 const Point &rPoint2, const Point &rHeading2)
917 assert(rHeading2 != Point());
919 bool bRes = false;
920 static const double eps = 5.0 * DBL_EPSILON;
922 double fLambda;
923 if (std::abs(rHeading2.X()) > std::abs(rHeading2.Y()))
925 fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
926 bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
928 else
930 fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
931 bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
934 return bRes;
938 sal_uInt16 GetLineIntersectionPoint(Point &rResult,
939 const Point& rPoint1, const Point &rHeading1,
940 const Point& rPoint2, const Point &rHeading2)
942 assert(rHeading1 != Point());
943 assert(rHeading2 != Point());
945 sal_uInt16 nRes = 1;
946 static const double eps = 5.0 * DBL_EPSILON;
948 // are the direction vectors linearly dependent?
949 double fDet = Det(rHeading1, rHeading2);
950 if (fabs(fDet) < eps)
952 nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
953 rResult = nRes ? rPoint1 : Point();
955 else
957 // here we do not pay attention to the computational accuracy
958 // (that would be more complicated and is not really worth it in this case)
959 double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
960 - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
961 / fDet;
962 rResult = Point(rPoint1.X() + static_cast<tools::Long>(fLambda * rHeading1.X()),
963 rPoint1.Y() + static_cast<tools::Long>(fLambda * rHeading1.Y()));
966 return nRes;
972 /// @return position and size of the diagonal line
973 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
974 void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
975 const Point &rDiagPoint, double fAngleDeg) const
978 double fAngleRad = basegfx::deg2rad(fAngleDeg);
979 tools::Long nRectLeft = GetItalicLeft(),
980 nRectRight = GetItalicRight(),
981 nRectTop = GetTop(),
982 nRectBottom = GetBottom();
983 Point aRightHdg (100, 0),
984 aDownHdg (0, 100),
985 aDiagHdg ( static_cast<tools::Long>(100.0 * cos(fAngleRad)),
986 static_cast<tools::Long>(-100.0 * sin(fAngleRad)) );
988 tools::Long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
989 Point aPoint;
990 if (IsAscending())
992 // determine top right corner
993 GetLineIntersectionPoint(aPoint,
994 Point(nRectLeft, nRectTop), aRightHdg,
995 rDiagPoint, aDiagHdg);
996 // is there a point of intersection with the top border?
997 if (aPoint.X() <= nRectRight)
999 nRight = aPoint.X();
1000 nTop = nRectTop;
1002 else
1004 // there has to be a point of intersection with the right border!
1005 GetLineIntersectionPoint(aPoint,
1006 Point(nRectRight, nRectTop), aDownHdg,
1007 rDiagPoint, aDiagHdg);
1009 nRight = nRectRight;
1010 nTop = aPoint.Y();
1013 // determine bottom left corner
1014 GetLineIntersectionPoint(aPoint,
1015 Point(nRectLeft, nRectBottom), aRightHdg,
1016 rDiagPoint, aDiagHdg);
1017 // is there a point of intersection with the bottom border?
1018 if (aPoint.X() >= nRectLeft)
1020 nLeft = aPoint.X();
1021 nBottom = nRectBottom;
1023 else
1025 // there has to be a point of intersection with the left border!
1026 GetLineIntersectionPoint(aPoint,
1027 Point(nRectLeft, nRectTop), aDownHdg,
1028 rDiagPoint, aDiagHdg);
1030 nLeft = nRectLeft;
1031 nBottom = aPoint.Y();
1034 else
1036 // determine top left corner
1037 GetLineIntersectionPoint(aPoint,
1038 Point(nRectLeft, nRectTop), aRightHdg,
1039 rDiagPoint, aDiagHdg);
1040 // is there a point of intersection with the top border?
1041 if (aPoint.X() >= nRectLeft)
1043 nLeft = aPoint.X();
1044 nTop = nRectTop;
1046 else
1048 // there has to be a point of intersection with the left border!
1049 GetLineIntersectionPoint(aPoint,
1050 Point(nRectLeft, nRectTop), aDownHdg,
1051 rDiagPoint, aDiagHdg);
1053 nLeft = nRectLeft;
1054 nTop = aPoint.Y();
1057 // determine bottom right corner
1058 GetLineIntersectionPoint(aPoint,
1059 Point(nRectLeft, nRectBottom), aRightHdg,
1060 rDiagPoint, aDiagHdg);
1061 // is there a point of intersection with the bottom border?
1062 if (aPoint.X() <= nRectRight)
1064 nRight = aPoint.X();
1065 nBottom = nRectBottom;
1067 else
1069 // there has to be a point of intersection with the right border!
1070 GetLineIntersectionPoint(aPoint,
1071 Point(nRectRight, nRectTop), aDownHdg,
1072 rDiagPoint, aDiagHdg);
1074 nRight = nRectRight;
1075 nBottom = aPoint.Y();
1079 rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
1080 rPos.setX( nLeft );
1081 rPos.setY( nTop );
1085 void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1087 // Both arguments have to get into the SubNodes before the Operator so that clicking
1088 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1089 SmNode *pLeft = GetSubNode(0),
1090 *pRight = GetSubNode(1),
1091 *pLine = GetSubNode(2);
1092 assert(pLeft);
1093 assert(pRight);
1094 assert(pLine && pLine->GetType() == SmNodeType::PolyLine);
1096 SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
1097 assert(pOper);
1099 //! some routines being called extract some info from the OutputDevice's
1100 //! font (eg the space to be used for borders OR the font name(!!)).
1101 //! Thus the font should reflect the needs and has to be set!
1102 SmTmpDevice aTmpDev (rDev, true);
1103 aTmpDev.SetFont(GetFont());
1105 pLeft->Arrange(aTmpDev, rFormat);
1106 pRight->Arrange(aTmpDev, rFormat);
1108 // determine implicitly the values (incl. the margin) of the diagonal line
1109 pOper->Arrange(aTmpDev, rFormat);
1111 tools::Long nDelta = pOper->GetWidth() * 8 / 10;
1113 // determine TopLeft position from the right argument
1114 Point aPos;
1115 aPos.setX( pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace() );
1116 if (IsAscending())
1117 aPos.setY( pLeft->GetBottom() + nDelta );
1118 else
1119 aPos.setY( pLeft->GetTop() - nDelta - pRight->GetHeight() );
1121 pRight->MoveTo(aPos);
1123 // determine new baseline
1124 tools::Long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
1125 : (pLeft->GetTop() + pRight->GetBottom()) / 2;
1126 Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
1127 nTmpBaseline);
1129 SmRect::operator = (*pLeft);
1130 ExtendBy(*pRight, RectCopyMBL::None);
1133 // determine position and size of diagonal line
1134 Size aTmpSize;
1135 GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
1137 // font specialist advised to change the width first
1138 pOper->AdaptToY(aTmpDev, aTmpSize.Height());
1139 pOper->AdaptToX(aTmpDev, aTmpSize.Width());
1140 // and make it active
1141 pOper->Arrange(aTmpDev, rFormat);
1143 pOper->MoveTo(aPos);
1145 ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
1149 /**************************************************************************/
1152 void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1154 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
1155 "Sm: wrong number of subnodes");
1157 SmNode *pBody = GetBody();
1158 assert(pBody);
1160 tools::Long nOrigHeight = pBody->GetFont().GetFontSize().Height();
1162 pBody->Arrange(rDev, rFormat);
1164 const SmRect &rBodyRect = pBody->GetRect();
1165 SmRect::operator = (rBodyRect);
1167 // line that separates sub- and supscript rectangles
1168 tools::Long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1170 Point aPos;
1171 tools::Long nDelta, nDist;
1173 // iterate over all possible sub-/supscripts
1174 SmRect aTmpRect (rBodyRect);
1175 for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
1177 SmSubSup eSubSup = static_cast<SmSubSup>(i);
1178 SmNode *pSubSup = GetSubSup(eSubSup);
1180 if (!pSubSup)
1181 continue;
1183 // switch position of limits if we are in textmode
1184 if (rFormat.IsTextmode() && (GetToken().nGroup & TG::Limit))
1185 switch (eSubSup)
1186 { case CSUB: eSubSup = RSUB; break;
1187 case CSUP: eSubSup = RSUP; break;
1188 default:
1189 break;
1192 // prevent sub-/supscripts from diminishing in size
1193 // (as would be in "a_{1_{2_{3_4}}}")
1194 if (GetFont().GetFontSize().Height() > rFormat.GetBaseSize().Height() / 3)
1196 sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
1197 SIZ_LIMITS : SIZ_INDEX;
1198 Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
1199 pSubSup->SetSize(aFraction);
1202 pSubSup->Arrange(rDev, rFormat);
1204 bool bIsTextmode = rFormat.IsTextmode();
1205 nDist = 0;
1207 //! be sure that CSUB, CSUP are handled before the other cases!
1208 switch (eSubSup)
1209 { case RSUB :
1210 case LSUB :
1211 if (!bIsTextmode)
1212 nDist = nOrigHeight
1213 * rFormat.GetDistance(DIS_SUBSCRIPT) / 100;
1214 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1215 eSubSup == LSUB ? RectPos::Left : RectPos::Right,
1216 RectHorAlign::Center, RectVerAlign::Bottom);
1217 aPos.AdjustY(nDist );
1218 nDelta = nDelimLine - aPos.Y();
1219 if (nDelta > 0)
1220 aPos.AdjustY(nDelta );
1221 break;
1222 case RSUP :
1223 case LSUP :
1224 if (!bIsTextmode)
1225 nDist = nOrigHeight
1226 * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100;
1227 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1228 eSubSup == LSUP ? RectPos::Left : RectPos::Right,
1229 RectHorAlign::Center, RectVerAlign::Top);
1230 aPos.AdjustY( -nDist );
1231 nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
1232 if (nDelta > 0)
1233 aPos.AdjustY( -nDelta );
1234 break;
1235 case CSUB :
1236 if (!bIsTextmode)
1237 nDist = nOrigHeight
1238 * rFormat.GetDistance(DIS_LOWERLIMIT) / 100;
1239 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Bottom,
1240 RectHorAlign::Center, RectVerAlign::Baseline);
1241 aPos.AdjustY(nDist );
1242 break;
1243 case CSUP :
1244 if (!bIsTextmode)
1245 nDist = nOrigHeight
1246 * rFormat.GetDistance(DIS_UPPERLIMIT) / 100;
1247 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Top,
1248 RectHorAlign::Center, RectVerAlign::Baseline);
1249 aPos.AdjustY( -nDist );
1250 break;
1253 pSubSup->MoveTo(aPos);
1254 ExtendBy(*pSubSup, RectCopyMBL::This, true);
1256 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1257 // will be aligned to
1258 if (eSubSup == CSUB || eSubSup == CSUP)
1259 aTmpRect = *this;
1263 /**************************************************************************/
1265 void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1267 SmNode *pLeft = OpeningBrace(),
1268 *pBody = Body(),
1269 *pRight = ClosingBrace();
1270 assert(pLeft);
1271 assert(pBody);
1272 assert(pRight);
1274 pBody->Arrange(rDev, rFormat);
1276 bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
1277 bScale = pBody->GetHeight() > 0 &&
1278 (GetScaleMode() == SmScaleMode::Height || bIsScaleNormal),
1279 bIsABS = GetToken().eType == TABS;
1281 tools::Long nFaceHeight = GetFont().GetFontSize().Height();
1283 // determine oversize in %
1284 sal_uInt16 nPerc = 0;
1285 if (!bIsABS && bScale)
1286 { // in case of oversize braces...
1287 sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
1288 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1289 nPerc = rFormat.GetDistance(nIndex);
1292 // determine the height for the braces
1293 tools::Long nBraceHeight;
1294 if (bScale)
1296 nBraceHeight = pBody->GetType() == SmNodeType::Bracebody ?
1297 static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
1298 : pBody->GetHeight();
1299 nBraceHeight += 2 * (nBraceHeight * nPerc / 100);
1301 else
1302 nBraceHeight = nFaceHeight;
1304 // distance to the argument
1305 nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
1306 tools::Long nDist = nFaceHeight * nPerc / 100;
1308 // if wanted, scale the braces to the wanted size
1309 if (bScale)
1311 Size aTmpSize (pLeft->GetFont().GetFontSize());
1312 OSL_ENSURE(pRight->GetFont().GetFontSize() == aTmpSize,
1313 "Sm : different font sizes");
1314 aTmpSize.setWidth( std::min(nBraceHeight * 60 / 100,
1315 rFormat.GetBaseSize().Height() * 3 / 2) );
1316 // correction factor since change from StarMath to OpenSymbol font
1317 // because of the different font width in the FontMetric
1318 aTmpSize.setWidth( aTmpSize.Width() * 182 );
1319 aTmpSize.setWidth( aTmpSize.Width() / 267 );
1321 sal_Unicode cChar = pLeft->GetToken().cMathChar[0];
1322 if (cChar != MS_LINE && cChar != MS_DLINE &&
1323 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1324 pLeft ->GetFont().SetSize(aTmpSize);
1326 cChar = pRight->GetToken().cMathChar[0];
1327 if (cChar != MS_LINE && cChar != MS_DLINE &&
1328 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1329 pRight->GetFont().SetSize(aTmpSize);
1331 pLeft ->AdaptToY(rDev, nBraceHeight);
1332 pRight->AdaptToY(rDev, nBraceHeight);
1335 pLeft ->Arrange(rDev, rFormat);
1336 pRight->Arrange(rDev, rFormat);
1338 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1339 RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1341 Point aPos;
1342 aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
1343 aPos.AdjustX( -nDist );
1344 pLeft->MoveTo(aPos);
1346 aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
1347 aPos.AdjustX(nDist );
1348 pRight->MoveTo(aPos);
1350 SmRect::operator = (*pBody);
1351 ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
1355 /**************************************************************************/
1358 void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1360 size_t nNumSubNodes = GetNumSubNodes();
1361 if (nNumSubNodes == 0)
1362 return;
1364 // arrange arguments
1365 for (size_t i = 0; i < nNumSubNodes; i += 2)
1366 GetSubNode(i)->Arrange(rDev, rFormat);
1368 // build reference rectangle with necessary info for vertical alignment
1369 SmRect aRefRect (*GetSubNode(0));
1370 for (size_t i = 0; i < nNumSubNodes; i += 2)
1372 SmRect aTmpRect (*GetSubNode(i));
1373 Point aPos = aTmpRect.AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
1374 aTmpRect.MoveTo(aPos);
1375 aRefRect.ExtendBy(aTmpRect, RectCopyMBL::Xor);
1378 mnBodyHeight = aRefRect.GetHeight();
1380 // scale separators to required height and arrange them
1381 bool bScale = GetScaleMode() == SmScaleMode::Height || rFormat.IsScaleNormalBrackets();
1382 tools::Long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetFontSize().Height();
1383 sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
1384 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1385 sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
1386 if (bScale)
1387 nHeight += 2 * (nHeight * nPerc / 100);
1388 for (size_t i = 1; i < nNumSubNodes; i += 2)
1390 SmNode *pNode = GetSubNode(i);
1391 pNode->AdaptToY(rDev, nHeight);
1392 pNode->Arrange(rDev, rFormat);
1395 // horizontal distance between argument and brackets or separators
1396 tools::Long nDist = GetFont().GetFontSize().Height()
1397 * rFormat.GetDistance(DIS_BRACKETSPACE) / 100;
1399 SmNode *pLeft = GetSubNode(0);
1400 SmRect::operator = (*pLeft);
1401 for (size_t i = 1; i < nNumSubNodes; ++i)
1403 bool bIsSeparator = i % 2 != 0;
1404 RectVerAlign eVerAlign = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1406 SmNode *pRight = GetSubNode(i);
1407 Point aPosX = pRight->AlignTo(*pLeft, RectPos::Right, RectHorAlign::Center, eVerAlign),
1408 aPosY = pRight->AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, eVerAlign);
1409 aPosX.AdjustX(nDist );
1411 pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
1412 ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
1414 pLeft = pRight;
1419 /**************************************************************************/
1422 void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1424 SmNode *pBody = Body(),
1425 *pBrace = Brace(),
1426 *pScript = Script();
1427 assert(pBody);
1428 assert(pBrace);
1429 assert(pScript);
1431 SmTmpDevice aTmpDev (rDev, true);
1432 aTmpDev.SetFont(GetFont());
1434 pBody->Arrange(aTmpDev, rFormat);
1436 // size is the same as for limits for this part
1437 pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
1438 // braces are a bit taller than usually
1439 pBrace ->SetSize( Fraction(3, 2) );
1441 tools::Long nItalicWidth = pBody->GetItalicWidth();
1442 if (nItalicWidth > 0)
1443 pBrace->AdaptToX(aTmpDev, nItalicWidth);
1445 pBrace ->Arrange(aTmpDev, rFormat);
1446 pScript->Arrange(aTmpDev, rFormat);
1448 // determine the relative position and the distances between each other
1449 RectPos eRectPos;
1450 tools::Long nFontHeight = pBody->GetFont().GetFontSize().Height();
1451 tools::Long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
1452 nDistScript = nFontHeight;
1453 if (GetToken().eType == TOVERBRACE)
1455 eRectPos = RectPos::Top;
1456 nDistBody = - nDistBody;
1457 nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
1459 else // TUNDERBRACE
1461 eRectPos = RectPos::Bottom;
1462 nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
1464 nDistBody /= 100;
1465 nDistScript /= 100;
1467 Point aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1468 aPos.AdjustY(nDistBody );
1469 pBrace->MoveTo(aPos);
1471 aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1472 aPos.AdjustY(nDistScript );
1473 pScript->MoveTo(aPos);
1475 SmRect::operator = (*pBody);
1476 ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
1480 /**************************************************************************/
1483 SmNode * SmOperNode::GetSymbol()
1485 SmNode *pNode = GetSubNode(0);
1486 assert(pNode);
1488 if (pNode->GetType() == SmNodeType::SubSup)
1489 pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
1491 OSL_ENSURE(pNode, "Sm: NULL pointer!");
1492 return pNode;
1496 tools::Long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
1497 const SmFormat &rFormat) const
1498 // returns the font height to be used for operator-symbol
1500 tools::Long nHeight = GetFont().GetFontSize().Height();
1502 SmTokenType eTmpType = GetToken().eType;
1503 if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
1504 return nHeight;
1506 if (!rFormat.IsTextmode())
1508 // set minimum size ()
1509 nHeight += (nHeight * 20) / 100;
1511 nHeight += nHeight
1512 * rFormat.GetDistance(DIS_OPERATORSIZE) / 100;
1513 nHeight = nHeight * 686 / 845;
1516 // correct user-defined symbols to match height of sum from used font
1517 if (rSymbol.GetToken().eType == TSPECIAL)
1518 nHeight = nHeight * 845 / 686;
1520 return nHeight;
1524 void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1526 SmNode *pOper = GetSubNode(0);
1527 SmNode *pBody = GetSubNode(1);
1529 assert(pOper);
1530 assert(pBody);
1532 SmNode *pSymbol = GetSymbol();
1533 pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
1534 pSymbol->GetFont().GetFontSize().Height()));
1536 pBody->Arrange(rDev, rFormat);
1537 bool bDynamicallySized = false;
1538 if (pSymbol->GetToken().eType == TINTD)
1540 tools::Long nBodyHeight = pBody->GetHeight();
1541 tools::Long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
1542 if (nFontHeight < nBodyHeight)
1544 pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
1545 bDynamicallySized = true;
1548 pOper->Arrange(rDev, rFormat);
1550 tools::Long nOrigHeight = GetFont().GetFontSize().Height(),
1551 nDist = nOrigHeight
1552 * rFormat.GetDistance(DIS_OPERATORSPACE) / 100;
1554 Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
1555 aPos.AdjustX( -nDist );
1556 pOper->MoveTo(aPos);
1558 SmRect::operator = (*pBody);
1559 ExtendBy(*pOper, RectCopyMBL::This);
1563 /**************************************************************************/
1566 void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1567 // set alignment within the entire subtree (including current node)
1569 assert(GetNumSubNodes() == 1);
1571 SmNode *pNode = GetSubNode(0);
1572 assert(pNode);
1574 RectHorAlign eHorAlign = RectHorAlign::Center;
1575 switch (GetToken().eType)
1577 case TALIGNL: eHorAlign = RectHorAlign::Left; break;
1578 case TALIGNC: eHorAlign = RectHorAlign::Center; break;
1579 case TALIGNR: eHorAlign = RectHorAlign::Right; break;
1580 default:
1581 break;
1583 SetRectHorAlign(eHorAlign);
1585 pNode->Arrange(rDev, rFormat);
1587 SmRect::operator = (pNode->GetRect());
1591 /**************************************************************************/
1594 void SmAttributeNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1596 SmNode *pAttr = Attribute(),
1597 *pBody = Body();
1598 assert(pBody);
1599 assert(pAttr);
1601 pBody->Arrange(rDev, rFormat);
1603 if (GetScaleMode() == SmScaleMode::Width)
1604 pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
1605 pAttr->Arrange(rDev, rFormat);
1607 // get relative position of attribute
1608 RectVerAlign eVerAlign;
1609 tools::Long nDist = 0;
1610 switch (GetToken().eType)
1611 { case TUNDERLINE :
1612 eVerAlign = RectVerAlign::AttributeLo;
1613 break;
1614 case TOVERSTRIKE :
1615 eVerAlign = RectVerAlign::AttributeMid;
1616 break;
1617 default :
1618 eVerAlign = RectVerAlign::AttributeHi;
1619 if (pBody->GetType() == SmNodeType::Attribute)
1620 nDist = GetFont().GetFontSize().Height()
1621 * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100;
1623 Point aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
1624 aPos.AdjustY( -nDist );
1625 pAttr->MoveTo(aPos);
1627 SmRect::operator = (*pBody);
1628 ExtendBy(*pAttr, RectCopyMBL::This, true);
1631 void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1633 //! prepare subnodes first
1634 SmNode::Prepare(rFormat, rDocShell, nDepth);
1636 int nFnt = -1;
1637 switch (GetToken().eType)
1639 case TFIXED: nFnt = FNT_FIXED; break;
1640 case TSANS: nFnt = FNT_SANS; break;
1641 case TSERIF: nFnt = FNT_SERIF; break;
1642 default:
1643 break;
1645 if (nFnt != -1)
1646 { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
1647 SetFont(GetFont());
1650 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
1651 //! other font nodes (those with lower depth in the tree)
1652 Flags() |= FontChangeMask::Face;
1655 void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1657 SmNode *pNode = GetSubNode(1);
1658 assert(pNode);
1659 sal_uInt32 nc;
1661 switch (GetToken().eType)
1662 { case TSIZE :
1663 pNode->SetFontSize(maFontSize, meSizeType);
1664 break;
1665 case TSANS :
1666 case TSERIF :
1667 case TFIXED :
1668 pNode->SetFont(GetFont());
1669 break;
1670 case TUNKNOWN : break; // no assertion on "font <?> <?>"
1672 case TPHANTOM : SetPhantom(true); break;
1673 case TBOLD : SetAttribute(FontAttribute::Bold); break;
1674 case TITALIC : SetAttribute(FontAttribute::Italic); break;
1675 case TNBOLD : ClearAttribute(FontAttribute::Bold); break;
1676 case TNITALIC : ClearAttribute(FontAttribute::Italic); break;
1678 // Using HTML CSS Level 1 standard
1679 case TRGB :
1680 case TRGBA :
1681 case THTMLCOL :
1682 case TMATHMLCOL :
1683 case TDVIPSNAMESCOL:
1684 case TICONICCOL :
1685 case THEX :
1686 nc = GetToken().cMathChar.toUInt32(16);
1687 SetColor(Color(ColorTransparency, nc));
1688 break;
1690 default:
1691 SAL_WARN("starmath", "unknown case");
1694 pNode->Arrange(rDev, rFormat);
1696 SmRect::operator = (pNode->GetRect());
1699 /**************************************************************************/
1702 SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
1703 : SmGraphicNode(SmNodeType::PolyLine, rNodeToken)
1704 , maPoly(2)
1705 , mnWidth(0)
1710 void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
1712 maToSize.setWidth( nNewWidth );
1716 void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
1718 GetFont().FreezeBorderWidth();
1719 maToSize.setHeight( nNewHeight );
1723 void SmPolyLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1725 //! some routines being called extract some info from the OutputDevice's
1726 //! font (eg the space to be used for borders OR the font name(!!)).
1727 //! Thus the font should reflect the needs and has to be set!
1728 SmTmpDevice aTmpDev (rDev, true);
1729 aTmpDev.SetFont(GetFont());
1731 tools::Long nBorderwidth = GetFont().GetBorderWidth();
1733 // create polygon using both endpoints
1734 assert(maPoly.GetSize() == 2);
1735 Point aPointA, aPointB;
1736 if (GetToken().eType == TWIDESLASH)
1738 aPointA.setX( nBorderwidth );
1739 aPointA.setY( maToSize.Height() - nBorderwidth );
1740 aPointB.setX( maToSize.Width() - nBorderwidth );
1741 aPointB.setY( nBorderwidth );
1743 else
1745 OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
1746 aPointA.setX( nBorderwidth );
1747 aPointA.setY( nBorderwidth );
1748 aPointB.setX( maToSize.Width() - nBorderwidth );
1749 aPointB.setY( maToSize.Height() - nBorderwidth );
1751 maPoly.SetPoint(aPointA, 0);
1752 maPoly.SetPoint(aPointB, 1);
1754 tools::Long nThick = GetFont().GetFontSize().Height()
1755 * rFormat.GetDistance(DIS_STROKEWIDTH) / 100;
1756 mnWidth = nThick + 2 * nBorderwidth;
1758 SmRect::operator = (SmRect(maToSize.Width(), maToSize.Height()));
1762 /**************************************************************************/
1764 void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1766 mnBodyWidth = nWidth;
1770 void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
1772 // some additional length so that the horizontal
1773 // bar will be positioned above the argument
1774 SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10);
1778 /**************************************************************************/
1781 void SmRectangleNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1783 maToSize.setWidth( nWidth );
1787 void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
1789 GetFont().FreezeBorderWidth();
1790 maToSize.setHeight( nHeight );
1794 void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
1796 tools::Long nFontHeight = GetFont().GetFontSize().Height();
1797 tools::Long nWidth = maToSize.Width(),
1798 nHeight = maToSize.Height();
1799 if (nHeight == 0)
1800 nHeight = nFontHeight / 30;
1801 if (nWidth == 0)
1802 nWidth = nFontHeight / 3;
1804 SmTmpDevice aTmpDev (rDev, true);
1805 aTmpDev.SetFont(GetFont());
1807 // add some borderspace
1808 sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth();
1809 nHeight += 2 * nTmpBorderWidth;
1811 //! use this method in order to have 'SmRect::HasAlignInfo() == true'
1812 //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
1813 SmRect::operator = (SmRect(nWidth, nHeight));
1817 /**************************************************************************/
1820 SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
1821 : SmVisibleNode(eNodeType, rNodeToken)
1822 , mnFontDesc(nFontDescP)
1823 , mnSelectionStart(0)
1824 , mnSelectionEnd(0)
1828 SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
1829 : SmVisibleNode(SmNodeType::Text, rNodeToken)
1830 , mnFontDesc(nFontDescP)
1831 , mnSelectionStart(0)
1832 , mnSelectionEnd(0)
1836 void SmTextNode::ChangeText(const OUString &rText) {
1837 maText = rText;
1838 GetToken().aText = rText;
1839 AdjustFontDesc();
1842 void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1844 SmNode::Prepare(rFormat, rDocShell, nDepth);
1846 // default setting for horizontal alignment of nodes with TTEXT
1847 // content is as alignl (cannot be done in Arrange since it would
1848 // override the settings made by an SmAlignNode before)
1849 if (TTEXT == GetToken().eType)
1850 SetRectHorAlign( RectHorAlign::Left );
1852 maText = GetToken().aText;
1853 GetFont() = rFormat.GetFont(GetFontDesc());
1855 if (IsItalic( GetFont() ))
1856 Attributes() |= FontAttribute::Italic;
1857 if (IsBold( GetFont() ))
1858 Attributes() |= FontAttribute::Bold;
1860 // special handling for ':' where it is a token on its own and is likely
1861 // to be used for mathematical notations. (E.g. a:b = 2:3)
1862 // In that case it should not be displayed in italic.
1863 if (GetToken().aText.getLength() == 1 && GetToken().aText[0] == ':')
1864 Attributes() &= ~FontAttribute::Italic;
1868 void SmTextNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1870 PrepareAttributes();
1872 sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
1873 SIZ_FUNCTION : SIZ_TEXT;
1874 GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
1876 SmTmpDevice aTmpDev (rDev, true);
1877 aTmpDev.SetFont(GetFont());
1879 SmRect::operator = (SmRect(aTmpDev, &rFormat, maText, GetFont().GetBorderWidth()));
1882 void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
1884 rText.append(maText);
1887 void SmTextNode::AdjustFontDesc()
1889 if (GetToken().nGroup == TG::Function) mnFontDesc = FNT_FUNCTION;
1890 else if (GetToken().eType == TTEXT) mnFontDesc = FNT_TEXT;
1891 else {
1892 sal_Unicode firstChar = maText[0];
1893 if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',')
1894 mnFontDesc = FNT_NUMBER;
1895 else mnFontDesc = FNT_VARIABLE;
1899 sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
1901 //Find the best match in accepted unicode for our private area symbols
1902 static const sal_Unicode aStarMathPrivateToUnicode[] =
1904 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
1905 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
1906 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
1907 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
1908 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
1909 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
1910 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
1911 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
1912 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
1913 0xE0DA, 0x2190, 0x2191, 0x2193
1915 if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
1916 nIn = aStarMathPrivateToUnicode[nIn-0xE080];
1918 //For whatever unicode glyph that equation editor doesn't ship with that
1919 //we have a possible match we can munge it to.
1920 switch (nIn)
1922 case 0x2223:
1923 nIn = '|';
1924 break;
1925 default:
1926 break;
1929 return nIn;
1932 /**************************************************************************/
1934 void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1936 SmNode *pNode;
1938 // initialize array that is to hold the maximum widths of all
1939 // elements (subnodes) in that column.
1940 std::vector<tools::Long> aColWidth(mnNumCols);
1942 // arrange subnodes and calculate the above arrays contents
1943 size_t nNodes = GetNumSubNodes();
1944 for (size_t i = 0; i < nNodes; ++i)
1946 size_t nIdx = nNodes - 1 - i;
1947 if (nullptr != (pNode = GetSubNode(nIdx)))
1949 pNode->Arrange(rDev, rFormat);
1950 int nCol = nIdx % mnNumCols;
1951 aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
1955 // norm distance from which the following two are calculated
1956 const tools::Long nNormDist = 3 * GetFont().GetFontSize().Height();
1958 // define horizontal and vertical minimal distances that separate
1959 // the elements
1960 tools::Long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100,
1961 nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100;
1963 // build array that holds the leftmost position for each column
1964 std::vector<tools::Long> aColLeft(mnNumCols);
1965 tools::Long nX = 0;
1966 for (size_t j = 0; j < mnNumCols; ++j)
1968 aColLeft[j] = nX;
1969 nX += aColWidth[j] + nHorDist;
1972 SmRect::operator = (SmRect());
1973 for (size_t i = 0; i < mnNumRows; ++i)
1975 Point aPos;
1976 SmRect aLineRect;
1977 for (size_t j = 0; j < mnNumCols; ++j)
1979 SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
1980 assert(pTmpNode);
1982 const SmRect &rNodeRect = pTmpNode->GetRect();
1984 // align all baselines in that row if possible
1985 aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
1987 // get horizontal alignment
1988 const SmNode *pCoNode = pTmpNode->GetLeftMost();
1989 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
1991 // calculate horizontal position of element depending on column
1992 // and horizontal alignment
1993 switch (eHorAlign)
1994 { case RectHorAlign::Left:
1995 aPos.setX( aColLeft[j] );
1996 break;
1997 case RectHorAlign::Center:
1998 aPos.setX( rNodeRect.GetLeft() + aColLeft[j]
1999 + aColWidth[j] / 2
2000 - rNodeRect.GetItalicCenterX() );
2001 break;
2002 case RectHorAlign::Right:
2003 aPos.setX( aColLeft[j]
2004 + aColWidth[j] - rNodeRect.GetItalicWidth() );
2005 break;
2006 default:
2007 assert(false);
2010 pTmpNode->MoveTo(aPos);
2011 aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
2014 aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
2015 if (i > 0)
2016 aPos.AdjustY(nVerDist );
2018 // move 'aLineRect' and rectangles in that line to final position
2019 Point aDelta(0, // since horizontal alignment is already done
2020 aPos.Y() - aLineRect.GetTop());
2021 aLineRect.Move(aDelta);
2022 for (size_t j = 0; j < mnNumCols; ++j)
2024 if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
2025 pNode->Move(aDelta);
2028 ExtendBy(aLineRect, RectCopyMBL::None);
2032 const SmNode * SmMatrixNode::GetLeftMost() const
2034 return this;
2038 /**************************************************************************/
2041 SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
2042 : SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH)
2044 SetText(GetToken().cMathChar);
2047 void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
2049 // Since there is no function to do this, we try to approximate it:
2050 Size aFntSize (GetFont().GetFontSize());
2052 //! however the result is a bit better with 'nWidth' as initial font width
2053 aFntSize.setWidth( nWidth );
2054 GetFont().SetSize(aFntSize);
2056 SmTmpDevice aTmpDev (rDev, true);
2057 aTmpDev.SetFont(GetFont());
2059 // get denominator of error factor for width
2060 tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
2061 tools::Long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
2063 // scale fontwidth with this error factor
2064 aFntSize.setWidth( aFntSize.Width() * nWidth );
2065 aFntSize.setWidth( aFntSize.Width() / ( nDenom ? nDenom : 1) );
2067 GetFont().SetSize(aFntSize);
2070 void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
2072 GetFont().FreezeBorderWidth();
2073 Size aFntSize (GetFont().GetFontSize());
2075 // Since we only want to scale the height, we might have
2076 // to determine the font width in order to keep it
2077 if (aFntSize.Width() == 0)
2079 rDev.Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE);
2080 rDev.SetFont(GetFont());
2081 aFntSize.setWidth( rDev.GetFontMetric().GetFontSize().Width() );
2082 rDev.Pop();
2084 OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
2086 //! however the result is a bit better with 'nHeight' as initial
2087 //! font height
2088 aFntSize.setHeight( nHeight );
2089 GetFont().SetSize(aFntSize);
2091 SmTmpDevice aTmpDev (rDev, true);
2092 aTmpDev.SetFont(GetFont());
2094 // get denominator of error factor for height
2095 tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
2096 tools::Long nDenom = 0;
2097 if (!GetText().isEmpty())
2098 nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetHeight();
2100 // scale fontwidth with this error factor
2101 aFntSize.setHeight( aFntSize.Height() * nHeight );
2102 aFntSize.setHeight( aFntSize.Height() / ( nDenom ? nDenom : 1) );
2104 GetFont().SetSize(aFntSize);
2108 void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2110 SmNode::Prepare(rFormat, rDocShell, nDepth);
2112 GetFont() = rFormat.GetFont(GetFontDesc());
2113 // use same font size as is used for variables
2114 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2116 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
2117 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
2118 "wrong charset for character from StarMath/OpenSymbol font");
2120 Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
2124 void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2126 const OUString &rText = GetText();
2128 if (rText.isEmpty() || rText[0] == '\0')
2129 { SmRect::operator = (SmRect());
2130 return;
2133 PrepareAttributes();
2135 GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
2137 SmTmpDevice aTmpDev (rDev, true);
2138 aTmpDev.SetFont(GetFont());
2140 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2143 /**************************************************************************/
2145 static bool lcl_IsFromGreekSymbolSet( std::u16string_view aTokenText )
2147 bool bRes = false;
2149 // valid symbol name needs to have a '%' at pos 0 and at least an additional char
2150 if (aTokenText.size() > 2 && aTokenText[0] == u'%')
2152 OUString aName( aTokenText.substr(1) );
2153 SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName );
2154 if (pSymbol && SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
2155 bRes = true;
2158 return bRes;
2162 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
2163 : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
2164 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
2169 SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
2170 : SmTextNode(SmNodeType::Special, rNodeToken, FNT_MATH) // default Font isn't always correct!
2171 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
2176 void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2178 SmNode::Prepare(rFormat, rDocShell, nDepth);
2180 const SmSym *pSym;
2181 SmModule *pp = SM_MOD();
2183 OUString aName(GetToken().aText.copy(1));
2184 if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName( aName )))
2186 sal_UCS4 cChar = pSym->GetCharacter();
2187 OUString aTmp( &cChar, 1 );
2188 SetText( aTmp );
2189 GetFont() = pSym->GetFace();
2191 else
2193 SetText( GetToken().aText );
2194 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2196 // use same font size as is used for variables
2197 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2199 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2200 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2201 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2203 //! see also SmFontStyles::GetStyleName
2204 if (IsItalic( GetFont() ))
2205 SetAttribute(FontAttribute::Italic);
2206 if (IsBold( GetFont() ))
2207 SetAttribute(FontAttribute::Bold);
2209 Flags() |= FontChangeMask::Face;
2211 if (!mbIsFromGreekSymbolSet)
2212 return;
2214 OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
2215 bool bItalic = false;
2216 sal_Int16 nStyle = rFormat.GetGreekCharStyle();
2217 OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
2218 if (nStyle == 1)
2219 bItalic = true;
2220 else if (nStyle == 2)
2222 const OUString& rTmp(GetText());
2223 if (!rTmp.isEmpty())
2225 static const sal_Unicode cUppercaseAlpha = 0x0391;
2226 static const sal_Unicode cUppercaseOmega = 0x03A9;
2227 sal_Unicode cChar = rTmp[0];
2228 // uppercase letters should be straight and lowercase letters italic
2229 bItalic = cUppercaseAlpha > cChar || cChar > cUppercaseOmega;
2233 if (bItalic)
2234 Attributes() |= FontAttribute::Italic;
2235 else
2236 Attributes() &= ~FontAttribute::Italic;
2240 void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2242 PrepareAttributes();
2244 SmTmpDevice aTmpDev (rDev, true);
2245 aTmpDev.SetFont(GetFont());
2247 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2250 /**************************************************************************/
2253 void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2255 PrepareAttributes();
2257 SmTmpDevice aTmpDev (rDev, true);
2258 aTmpDev.SetFont(GetFont());
2260 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
2261 GetFont().GetBorderWidth()).AsGlyphRect());
2265 /**************************************************************************/
2268 void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2270 SmNode::Prepare(rFormat, rDocShell, nDepth);
2272 GetFont().SetColor(COL_GRAY);
2273 Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
2277 void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2279 PrepareAttributes();
2281 SmTmpDevice aTmpDev (rDev, true);
2282 aTmpDev.SetFont(GetFont());
2284 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2288 /**************************************************************************/
2291 void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2293 SmNode::Prepare(rFormat, rDocShell, nDepth);
2295 GetFont().SetColor(COL_RED);
2296 Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
2297 | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
2301 void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2303 PrepareAttributes();
2305 SmTmpDevice aTmpDev (rDev, true);
2306 aTmpDev.SetFont(GetFont());
2308 const OUString &rText = GetText();
2309 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2312 /**************************************************************************/
2314 void SmBlankNode::IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy)
2316 switch(rToken.eType)
2318 case TBLANK: mnNum += (4 * nMultiplyBy); break;
2319 case TSBLANK: mnNum += (1 * nMultiplyBy); break;
2320 default:
2321 break;
2325 void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2327 SmNode::Prepare(rFormat, rDocShell, nDepth);
2329 // Here it need/should not be the StarMath font, so that for the character
2330 // used in Arrange a normal (non-clipped) rectangle is generated
2331 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2333 Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
2337 void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2339 SmTmpDevice aTmpDev (rDev, true);
2340 aTmpDev.SetFont(GetFont());
2342 // make distance depend on the font height
2343 // (so that it increases when scaling (e.g. size *2 {a ~ b})
2344 tools::Long nDist = GetFont().GetFontSize().Height() / 10,
2345 nSpace = mnNum * nDist;
2347 // get a SmRect with Baseline and all the bells and whistles
2348 SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
2349 GetFont().GetBorderWidth()));
2351 // and resize it to the requested size
2352 SetItalicSpaces(0, 0);
2353 SetWidth(nSpace);
2356 /**************************************************************************/
2357 //Implementation of all accept methods for SmVisitor
2359 void SmTableNode::Accept(SmVisitor* pVisitor) {
2360 pVisitor->Visit(this);
2363 void SmBraceNode::Accept(SmVisitor* pVisitor) {
2364 pVisitor->Visit(this);
2367 void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
2368 pVisitor->Visit(this);
2371 void SmOperNode::Accept(SmVisitor* pVisitor) {
2372 pVisitor->Visit(this);
2375 void SmAlignNode::Accept(SmVisitor* pVisitor) {
2376 pVisitor->Visit(this);
2379 void SmAttributeNode::Accept(SmVisitor* pVisitor) {
2380 pVisitor->Visit(this);
2383 void SmFontNode::Accept(SmVisitor* pVisitor) {
2384 pVisitor->Visit(this);
2387 void SmUnHorNode::Accept(SmVisitor* pVisitor) {
2388 pVisitor->Visit(this);
2391 void SmBinHorNode::Accept(SmVisitor* pVisitor) {
2392 pVisitor->Visit(this);
2395 void SmBinVerNode::Accept(SmVisitor* pVisitor) {
2396 pVisitor->Visit(this);
2399 void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
2400 pVisitor->Visit(this);
2403 void SmSubSupNode::Accept(SmVisitor* pVisitor) {
2404 pVisitor->Visit(this);
2407 void SmMatrixNode::Accept(SmVisitor* pVisitor) {
2408 pVisitor->Visit(this);
2411 void SmPlaceNode::Accept(SmVisitor* pVisitor) {
2412 pVisitor->Visit(this);
2415 void SmTextNode::Accept(SmVisitor* pVisitor) {
2416 pVisitor->Visit(this);
2419 void SmSpecialNode::Accept(SmVisitor* pVisitor) {
2420 pVisitor->Visit(this);
2423 void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
2424 pVisitor->Visit(this);
2427 void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
2428 pVisitor->Visit(this);
2431 void SmBlankNode::Accept(SmVisitor* pVisitor) {
2432 pVisitor->Visit(this);
2435 void SmErrorNode::Accept(SmVisitor* pVisitor) {
2436 pVisitor->Visit(this);
2439 void SmLineNode::Accept(SmVisitor* pVisitor) {
2440 pVisitor->Visit(this);
2443 void SmExpressionNode::Accept(SmVisitor* pVisitor) {
2444 pVisitor->Visit(this);
2447 void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
2448 pVisitor->Visit(this);
2451 void SmRootNode::Accept(SmVisitor* pVisitor) {
2452 pVisitor->Visit(this);
2455 void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
2456 pVisitor->Visit(this);
2459 void SmRectangleNode::Accept(SmVisitor* pVisitor) {
2460 pVisitor->Visit(this);
2463 void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
2464 pVisitor->Visit(this);
2467 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */