use "Rubik" from more_fonts instead of "System" for consistent layout
[LibreOffice.git] / starmath / source / node.cxx
blob008ea0d3cc7da3c2c21325f2f2d69baff65629c1
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 <vcl/metric.hxx>
27 #include <osl/diagnose.h>
28 #include <basegfx/numeric/ftools.hxx>
30 namespace {
32 template<typename F>
33 void ForEachNonNull(SmNode *pNode, F && f)
35 size_t nSize = pNode->GetNumSubNodes();
36 for (size_t i = 0; i < nSize; ++i)
38 SmNode *pSubNode = pNode->GetSubNode(i);
39 if (pSubNode != nullptr)
40 f(pSubNode);
46 SmNode::SmNode(SmNodeType eNodeType, SmToken aNodeToken)
47 : maNodeToken(std::move( aNodeToken ))
48 , meType( eNodeType )
49 , meScaleMode( SmScaleMode::None )
50 , meRectHorAlign( RectHorAlign::Left )
51 , mnFlags( FontChangeMask::None )
52 , mnAttributes( FontAttribute::None )
53 , mbIsPhantom( false )
54 , mbIsSelected( false )
55 , mnAccIndex( -1 )
56 , mpParentNode( nullptr )
60 SmNode::~SmNode()
64 const SmNode * SmNode::GetLeftMost() const
65 // returns leftmost node of current subtree.
66 //! (this assumes the one with index 0 is always the leftmost subnode
67 //! for the current node).
69 const SmNode *pNode = GetNumSubNodes() > 0 ?
70 GetSubNode(0) : nullptr;
72 return pNode ? pNode->GetLeftMost() : this;
76 void SmNode::SetPhantom(bool bIsPhantomP)
78 if (! (Flags() & FontChangeMask::Phantom))
79 mbIsPhantom = bIsPhantomP;
81 bool b = mbIsPhantom;
82 ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
86 void SmNode::SetColor(const Color& rColor)
88 if (! (Flags() & FontChangeMask::Color))
89 GetFont().SetColor(rColor);
91 ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
95 void SmNode::SetAttribute(FontAttribute nAttrib)
97 if (
98 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
99 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
102 mnAttributes |= nAttrib;
105 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribute(nAttrib);});
109 void SmNode::ClearAttribute(FontAttribute nAttrib)
111 if (
112 (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
113 (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
116 mnAttributes &= ~nAttrib;
119 ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribute(nAttrib);});
123 void SmNode::SetFont(const SmFace &rFace)
125 if (!(Flags() & FontChangeMask::Face))
126 GetFont() = rFace;
127 ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
131 void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
132 //! 'rSize' is in units of pts
134 Size aFntSize;
136 if (!(Flags() & FontChangeMask::Size))
138 Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()),
139 rSize.GetDenominator());
140 tools::Long nHeight = static_cast<tools::Long>(aVal);
142 aFntSize = GetFont().GetFontSize();
143 aFntSize.setWidth( 0 );
144 switch(nType)
146 case FontSizeType::ABSOLUT:
147 aFntSize.setHeight( nHeight );
148 break;
150 case FontSizeType::PLUS:
151 aFntSize.AdjustHeight(nHeight );
152 break;
154 case FontSizeType::MINUS:
155 aFntSize.AdjustHeight( -nHeight );
156 break;
158 case FontSizeType::MULTIPLY:
159 aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) * rSize) );
160 break;
162 case FontSizeType::DIVIDE:
163 if (rSize != Fraction(0))
164 aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) / rSize) );
165 break;
166 default:
167 break;
170 // check the requested size against maximum value
171 static int const nMaxVal = SmPtsTo100th_mm(128);
172 if (aFntSize.Height() > nMaxVal)
173 aFntSize.setHeight( nMaxVal );
175 GetFont().SetSize(aFntSize);
178 ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
182 void SmNode::SetSize(const Fraction &rSize)
184 GetFont() *= rSize;
186 ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
190 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
192 meRectHorAlign = eHorAlign;
194 if (bApplyToSubTree)
195 ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
199 void SmNode::PrepareAttributes()
201 GetFont().SetWeight((Attributes() & FontAttribute::Bold) ? WEIGHT_BOLD : WEIGHT_NORMAL);
202 GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
206 void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
208 if (nDepth > 1024)
209 throw std::range_error("parser depth limit");
211 mbIsPhantom = false;
212 mnFlags = FontChangeMask::None;
213 mnAttributes = FontAttribute::None;
215 switch (rFormat.GetHorAlign())
216 { case SmHorAlign::Left: meRectHorAlign = RectHorAlign::Left; break;
217 case SmHorAlign::Center: meRectHorAlign = RectHorAlign::Center; break;
218 case SmHorAlign::Right: meRectHorAlign = RectHorAlign::Right; break;
221 GetFont() = rFormat.GetFont(FNT_MATH);
222 OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
223 "unexpected CharSet" );
224 GetFont().SetWeight(WEIGHT_NORMAL);
225 GetFont().SetItalic(ITALIC_NONE);
227 ForEachNonNull(this, [&rFormat, &rDocShell, nDepth](SmNode *pNode){pNode->Prepare(rFormat, rDocShell, nDepth + 1);});
230 void SmNode::Move(const Point& rVector)
232 if (rVector.X() == 0 && rVector.Y() == 0)
233 return;
235 SmRect::Move(rVector);
237 ForEachNonNull(this, [&rVector](SmNode *pNode){pNode->Move(rVector);});
240 void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
245 void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
250 const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
251 // returns (first) ** visible ** (sub)node with the tokens text at
252 // position 'nRow', 'nCol'.
253 //! (there should be exactly one such node if any)
255 if ( IsVisible()
256 && nRow == GetSelection().nStartPara
257 && nCol >= GetSelection().nStartPos && nCol <= GetSelection().nEndPos )
258 return this;
259 else
261 size_t nNumSubNodes = GetNumSubNodes();
262 for (size_t i = 0; i < nNumSubNodes; ++i)
264 const SmNode *pNode = GetSubNode(i);
266 if (!pNode)
267 continue;
269 const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
270 if (pResult)
271 return pResult;
275 return nullptr;
279 const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
281 tools::Long nDist = LONG_MAX;
282 const SmNode *pResult = nullptr;
284 if (IsVisible())
285 pResult = this;
286 else
288 size_t nNumSubNodes = GetNumSubNodes();
289 for (size_t i = 0; i < nNumSubNodes; ++i)
291 const SmNode *pNode = GetSubNode(i);
293 if (!pNode)
294 continue;
296 const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
297 if (pFound)
299 tools::Long nTmp = pFound->OrientedDist(rPoint);
300 if (nTmp < nDist)
302 nDist = nTmp;
303 pResult = pFound;
305 // quit immediately if 'rPoint' is inside the *should not
306 // overlap with other rectangles* part.
307 // This (partly) serves for getting the attributes in eg
308 // "bar overstrike a".
309 // ('nDist < 0' is used as *quick shot* to avoid evaluation of
310 // the following expression, where the result is already determined)
311 if (nDist < 0 && pFound->IsInsideRect(rPoint))
312 break;
318 return pResult;
321 const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
323 const SmNode *pResult = nullptr;
325 sal_Int32 nIdx = GetAccessibleIndex();
326 OUStringBuffer aTxt;
327 if (nIdx >= 0)
328 GetAccessibleText( aTxt ); // get text if used in following 'if' statement
330 if (nIdx >= 0
331 && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.getLength())
332 pResult = this;
333 else
335 size_t nNumSubNodes = GetNumSubNodes();
336 for (size_t i = 0; i < nNumSubNodes; ++i)
338 const SmNode *pNode = GetSubNode(i);
339 if (!pNode)
340 continue;
342 pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
343 if (pResult)
344 return pResult;
348 return pResult;
352 SmStructureNode::~SmStructureNode()
354 ForEachNonNull(this, std::default_delete<SmNode>());
358 void SmStructureNode::ClearSubNodes()
360 maSubNodes.clear();
363 void SmStructureNode::SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
365 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
366 maSubNodes.resize( nSize );
367 if (pFirst)
368 maSubNodes[0] = pFirst.release();
369 if (pSecond)
370 maSubNodes[1] = pSecond.release();
371 if (pThird)
372 maSubNodes[2] = pThird.release();
374 ClaimPaternity();
377 void SmStructureNode::SetSubNodes(SmNode* pFirst, SmNode* pSecond, SmNode* pThird)
379 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
380 maSubNodes.resize( nSize );
381 if (pFirst)
382 maSubNodes[0] = pFirst;
383 if (pSecond)
384 maSubNodes[1] = pSecond;
385 if (pThird)
386 maSubNodes[2] = pThird;
388 ClaimPaternity();
391 void SmStructureNode::SetSubNodesBinMo(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
393 if(GetType()==SmNodeType::BinDiagonal)
395 size_t nSize = pSecond ? 3 : (pThird ? 2 : (pFirst ? 1 : 0));
396 maSubNodes.resize( nSize );
397 if (pFirst)
398 maSubNodes[0] = pFirst.release();
399 if (pSecond)
400 maSubNodes[2] = pSecond.release();
401 if (pThird)
402 maSubNodes[1] = pThird.release();
404 else
406 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
407 maSubNodes.resize( nSize );
408 if (pFirst)
409 maSubNodes[0] = pFirst.release();
410 if (pSecond)
411 maSubNodes[1] = pSecond.release();
412 if (pThird)
413 maSubNodes[2] = pThird.release();
415 ClaimPaternity();
418 void SmStructureNode::SetSubNodesBinMo(SmNode* pFirst, SmNode* pSecond, SmNode* pThird)
420 if(GetType()==SmNodeType::BinDiagonal)
422 size_t nSize = pSecond ? 3 : (pThird ? 2 : (pFirst ? 1 : 0));
423 maSubNodes.resize( nSize );
424 if (pFirst)
425 maSubNodes[0] = pFirst;
426 if (pSecond)
427 maSubNodes[2] = pSecond;
428 if (pThird)
429 maSubNodes[1] = pThird;
431 else
433 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
434 maSubNodes.resize( nSize );
435 if (pFirst)
436 maSubNodes[0] = pFirst;
437 if (pSecond)
438 maSubNodes[1] = pSecond;
439 if (pThird)
440 maSubNodes[2] = pThird;
442 ClaimPaternity();
445 void SmStructureNode::SetSubNodes(SmNodeArray&& rNodeArray)
447 maSubNodes = std::move(rNodeArray);
448 ClaimPaternity();
451 bool SmStructureNode::IsVisible() const
453 return false;
456 size_t SmStructureNode::GetNumSubNodes() const
458 return maSubNodes.size();
461 SmNode* SmStructureNode::GetSubNode(size_t nIndex)
463 return maSubNodes[nIndex];
466 SmNode* SmStructureNode::GetSubNodeBinMo(size_t nIndex) const
468 if(GetType()==SmNodeType::BinDiagonal)
470 if (nIndex==1)
471 nIndex = 2;
472 else if (nIndex==2)
473 nIndex = 1;
475 return maSubNodes[nIndex];
478 void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
480 ForEachNonNull(const_cast<SmStructureNode *>(this),
481 [&rText](SmNode *pNode)
483 if (pNode->IsVisible())
484 pNode->SetAccessibleIndex(rText.getLength());
485 pNode->GetAccessibleText( rText );
489 void SmStructureNode::ClaimPaternity()
491 ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
494 int SmStructureNode::IndexOfSubNode(SmNode const * pSubNode)
496 size_t nSize = GetNumSubNodes();
497 for (size_t i = 0; i < nSize; i++)
498 if (pSubNode == GetSubNode(i))
499 return i;
500 return -1;
503 void SmStructureNode::SetSubNode(size_t nIndex, SmNode* pNode)
505 size_t size = maSubNodes.size();
506 if (size <= nIndex)
508 //Resize subnodes array
509 maSubNodes.resize(nIndex + 1);
510 //Set new slots to NULL except at nIndex
511 for (size_t i = size; i < nIndex; i++)
512 maSubNodes[i] = nullptr;
514 maSubNodes[nIndex] = pNode;
515 if (pNode)
516 pNode->SetParent(this);
519 bool SmVisibleNode::IsVisible() const
521 return true;
524 size_t SmVisibleNode::GetNumSubNodes() const
526 return 0;
529 SmNode * SmVisibleNode::GetSubNode(size_t /*nIndex*/)
531 return nullptr;
534 void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
536 rText.append(GetToken().aText);
539 void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
540 // arranges all subnodes in one column
542 SmNode *pNode;
543 size_t nSize = GetNumSubNodes();
545 // make distance depend on font size
546 tools::Long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
547 * GetFont().GetFontSize().Height()) / 100;
549 if (nSize < 1)
550 return;
552 // arrange subnodes and get maximum width of them
553 tools::Long nMaxWidth = 0,
554 nTmp;
555 for (size_t i = 0; i < nSize; ++i)
557 if (nullptr != (pNode = GetSubNode(i)))
558 { pNode->Arrange(rDev, rFormat);
559 if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
560 nMaxWidth = nTmp;
564 Point aPos;
565 SmRect::operator = (SmRect(nMaxWidth, 1));
566 for (size_t i = 0; i < nSize; ++i)
568 if (nullptr != (pNode = GetSubNode(i)))
569 { const SmRect &rNodeRect = pNode->GetRect();
570 const SmNode *pCoNode = pNode->GetLeftMost();
571 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
573 aPos = rNodeRect.AlignTo(*this, RectPos::Bottom,
574 eHorAlign, RectVerAlign::Baseline);
575 if (i)
576 aPos.AdjustY(nDist );
577 pNode->MoveTo(aPos);
578 ExtendBy(rNodeRect, nSize > 1 ? RectCopyMBL::None : RectCopyMBL::Arg);
581 // #i972#
582 if (HasBaseline())
583 mnFormulaBaseline = GetBaseline();
584 else
586 SmTmpDevice aTmpDev (rDev, true);
587 aTmpDev.SetFont(GetFont());
589 SmRect aRect(aTmpDev, &rFormat, "a", GetFont().GetBorderWidth());
590 mnFormulaBaseline = GetAlignM();
591 // move from middle position by constant - distance
592 // between middle and baseline for single letter
593 mnFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
597 const SmNode * SmTableNode::GetLeftMost() const
599 return this;
603 tools::Long SmTableNode::GetFormulaBaseline() const
605 return mnFormulaBaseline;
609 /**************************************************************************/
612 void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
614 SmNode::Prepare(rFormat, rDocShell, nDepth);
616 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
617 // to the rest of the formula compared to the 'FNT_MATH' font.
618 GetFont() = rFormat.GetFont(FNT_VARIABLE);
619 Flags() |= FontChangeMask::Face;
623 /**************************************************************************/
626 void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
627 // arranges all subnodes in one row with some extra space between
629 SmNode *pNode;
630 size_t nSize = GetNumSubNodes();
631 for (size_t i = 0; i < nSize; ++i)
633 if (nullptr != (pNode = GetSubNode(i)))
634 pNode->Arrange(rDev, rFormat);
637 SmTmpDevice aTmpDev (rDev, true);
638 aTmpDev.SetFont(GetFont());
640 if (nSize < 1)
642 // provide an empty rectangle with alignment parameters for the "current"
643 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
644 // same sub-/supscript positions.)
645 //! be sure to use a character that has explicitly defined HiAttribut
646 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
647 //! 'vec {a}'.
648 SmRect::operator = (SmRect(aTmpDev, &rFormat, "a",
649 GetFont().GetBorderWidth()));
650 // make sure that the rectangle occupies (almost) no space
651 SetWidth(1);
652 SetItalicSpaces(0, 0);
653 return;
656 // make distance depend on font size
657 tools::Long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetFontSize().Height()) / 100;
658 if (!IsUseExtraSpaces())
659 nDist = 0;
661 Point aPos;
662 // copy the first node into LineNode and extend by the others
663 if (nullptr != (pNode = GetSubNode(0)))
664 SmRect::operator = (pNode->GetRect());
666 for (size_t i = 1; i < nSize; ++i)
668 if (nullptr != (pNode = GetSubNode(i)))
670 aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
672 // add horizontal space to the left for each but the first sub node
673 aPos.AdjustX(nDist );
675 pNode->MoveTo(aPos);
676 ExtendBy( *pNode, RectCopyMBL::Xor );
682 /**************************************************************************/
685 void SmExpressionNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
686 // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
688 SmLineNode::Arrange(rDev, rFormat);
690 // copy alignment of leftmost subnode if any
691 const SmNode *pNode = GetLeftMost();
692 if (pNode)
693 SetRectHorAlign(pNode->GetRectHorAlign(), false);
697 /**************************************************************************/
700 void SmUnHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
702 bool bIsPostfix = GetToken().eType == TFACT;
704 SmNode *pNode0 = GetSubNode(0),
705 *pNode1 = GetSubNode(1);
706 SmNode *pOper = bIsPostfix ? pNode1 : pNode0,
707 *pBody = bIsPostfix ? pNode0 : pNode1;
708 assert(pOper);
709 assert(pBody);
711 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
712 pOper->Arrange(rDev, rFormat);
713 pBody->Arrange(rDev, rFormat);
715 tools::Long nDist = (pOper->GetRect().GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
717 SmRect::operator = (*pNode0);
719 Point aPos = pNode1->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
720 aPos.AdjustX(nDist );
721 pNode1->MoveTo(aPos);
722 ExtendBy(*pNode1, RectCopyMBL::Xor);
726 /**************************************************************************/
728 namespace {
730 void lcl_GetHeightVerOffset(const SmRect &rRect,
731 tools::Long &rHeight, tools::Long &rVerOffset)
732 // calculate height and vertical offset of root sign suitable for 'rRect'
734 rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
735 rHeight = rRect.GetHeight() - rVerOffset;
737 OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
738 OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
742 Point lcl_GetExtraPos(const SmRect &rRootSymbol,
743 const SmRect &rExtra)
745 const Size &rSymSize = rRootSymbol.GetSize();
747 Point aPos = rRootSymbol.GetTopLeft()
748 + Point((rSymSize.Width() * 70) / 100,
749 (rSymSize.Height() * 52) / 100);
751 // from this calculate topleft edge of 'rExtra'
752 aPos.AdjustX( -(rExtra.GetWidth() + rExtra.GetItalicRightSpace()) );
753 aPos.AdjustY( -(rExtra.GetHeight()) );
754 // if there's enough space move a bit less to the right
755 // examples: "nroot i a", "nroot j a"
756 // (it looks better if we don't use italic-spaces here)
757 tools::Long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
758 if (aPos.X() > nX)
759 aPos.setX( nX );
761 return aPos;
766 void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
768 //! pExtra needs to have the smaller index than pRootSym in order to
769 //! not to get the root symbol but the pExtra when clicking on it in the
770 //! GraphicWindow. (That is because of the simplicity of the algorithm
771 //! that finds the node corresponding to a mouseclick in the window.)
772 SmNode *pExtra = GetSubNode(0),
773 *pRootSym = GetSubNode(1),
774 *pBody = GetSubNode(2);
775 assert(pRootSym);
776 assert(pBody);
778 pBody->Arrange(rDev, rFormat);
780 tools::Long nHeight,
781 nVerOffset;
782 lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset);
783 nHeight += rFormat.GetDistance(DIS_ROOT)
784 * GetFont().GetFontSize().Height() / 100;
786 if (nHeight < 0)
788 SAL_WARN("starmath", "negative height");
789 nHeight = 0;
792 // font specialist advised to change the width first
793 pRootSym->AdaptToY(rDev, nHeight);
794 pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
796 pRootSym->Arrange(rDev, rFormat);
798 Point aPos = pRootSym->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, RectVerAlign::Baseline);
799 //! override calculated vertical position
800 aPos.setY( pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom() );
801 aPos.AdjustY( -nVerOffset );
802 pRootSym->MoveTo(aPos);
804 if (pExtra)
805 { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
806 pExtra->Arrange(rDev, rFormat);
808 aPos = lcl_GetExtraPos(*pRootSym, *pExtra);
809 pExtra->MoveTo(aPos);
812 SmRect::operator = (*pBody);
813 ExtendBy(*pRootSym, RectCopyMBL::This);
814 if (pExtra)
815 ExtendBy(*pExtra, RectCopyMBL::This, true);
818 /**************************************************************************/
821 void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
823 SmNode *pLeft = LeftOperand(),
824 *pOper = Symbol(),
825 *pRight = RightOperand();
826 assert(pLeft);
827 assert(pOper);
828 assert(pRight);
830 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
832 pLeft ->Arrange(rDev, rFormat);
833 pOper ->Arrange(rDev, rFormat);
834 pRight->Arrange(rDev, rFormat);
836 const SmRect &rOpRect = pOper->GetRect();
838 tools::Long nDist = (rOpRect.GetWidth() *
839 rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
841 SmRect::operator = (*pLeft);
843 Point aPos;
844 aPos = pOper->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
845 aPos.AdjustX(nDist );
846 pOper->MoveTo(aPos);
847 ExtendBy(*pOper, RectCopyMBL::Xor);
849 aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
850 aPos.AdjustX(nDist );
852 pRight->MoveTo(aPos);
853 ExtendBy(*pRight, RectCopyMBL::Xor);
857 /**************************************************************************/
860 void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
862 SmNode *pNum = GetSubNode(0),
863 *pLine = GetSubNode(1),
864 *pDenom = GetSubNode(2);
865 assert(pNum);
866 assert(pLine);
867 assert(pDenom);
869 bool bIsTextmode = rFormat.IsTextmode();
870 if (bIsTextmode)
872 Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
873 pNum ->SetSize(aFraction);
874 pLine ->SetSize(aFraction);
875 pDenom->SetSize(aFraction);
878 pNum ->Arrange(rDev, rFormat);
879 pDenom->Arrange(rDev, rFormat);
881 tools::Long nFontHeight = GetFont().GetFontSize().Height(),
882 nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100,
883 nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100,
884 nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
885 nNumDist = bIsTextmode ? 0 :
886 nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100,
887 nDenomDist = bIsTextmode ? 0 :
888 nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100;
890 // font specialist advised to change the width first
891 pLine->AdaptToY(rDev, nThick);
892 pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
893 pLine->Arrange(rDev, rFormat);
895 // get horizontal alignment for numerator
896 const SmNode *pLM = pNum->GetLeftMost();
897 RectHorAlign eHorAlign = pLM->GetRectHorAlign();
899 // move numerator to its position
900 Point aPos = pNum->AlignTo(*pLine, RectPos::Top, eHorAlign, RectVerAlign::Baseline);
901 aPos.AdjustY( -nNumDist );
902 pNum->MoveTo(aPos);
904 // get horizontal alignment for denominator
905 pLM = pDenom->GetLeftMost();
906 eHorAlign = pLM->GetRectHorAlign();
908 // move denominator to its position
909 aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
910 aPos.AdjustY(nDenomDist );
911 pDenom->MoveTo(aPos);
913 SmRect::operator = (*pNum);
914 ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
917 const SmNode * SmBinVerNode::GetLeftMost() const
919 return this;
923 namespace {
925 /// @return value of the determinant formed by the two points
926 double Det(const Point &rHeading1, const Point &rHeading2)
928 return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
932 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
933 /// and has the direction vector 'rHeading2'
934 bool IsPointInLine(const Point &rPoint1,
935 const Point &rPoint2, const Point &rHeading2)
937 assert(rHeading2 != Point());
939 bool bRes = false;
940 static const double eps = 5.0 * DBL_EPSILON;
942 double fLambda;
943 if (std::abs(rHeading2.X()) > std::abs(rHeading2.Y()))
945 fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
946 bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
948 else
950 fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
951 bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
954 return bRes;
958 sal_uInt16 GetLineIntersectionPoint(Point &rResult,
959 const Point& rPoint1, const Point &rHeading1,
960 const Point& rPoint2, const Point &rHeading2)
962 assert(rHeading1 != Point());
963 assert(rHeading2 != Point());
965 sal_uInt16 nRes = 1;
966 static const double eps = 5.0 * DBL_EPSILON;
968 // are the direction vectors linearly dependent?
969 double fDet = Det(rHeading1, rHeading2);
970 if (fabs(fDet) < eps)
972 nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
973 rResult = nRes ? rPoint1 : Point();
975 else
977 // here we do not pay attention to the computational accuracy
978 // (that would be more complicated and is not really worth it in this case)
979 double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
980 - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
981 / fDet;
982 rResult = Point(rPoint1.X() + static_cast<tools::Long>(fLambda * rHeading1.X()),
983 rPoint1.Y() + static_cast<tools::Long>(fLambda * rHeading1.Y()));
986 return nRes;
992 /// @return position and size of the diagonal line
993 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
994 void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
995 const Point &rDiagPoint, double fAngleDeg) const
998 double fAngleRad = basegfx::deg2rad(fAngleDeg);
999 tools::Long nRectLeft = GetItalicLeft(),
1000 nRectRight = GetItalicRight(),
1001 nRectTop = GetTop(),
1002 nRectBottom = GetBottom();
1003 Point aRightHdg (100, 0),
1004 aDownHdg (0, 100),
1005 aDiagHdg ( static_cast<tools::Long>(100.0 * cos(fAngleRad)),
1006 static_cast<tools::Long>(-100.0 * sin(fAngleRad)) );
1008 tools::Long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
1009 Point aPoint;
1010 if (IsAscending())
1012 // determine top right corner
1013 GetLineIntersectionPoint(aPoint,
1014 Point(nRectLeft, nRectTop), aRightHdg,
1015 rDiagPoint, aDiagHdg);
1016 // is there a point of intersection with the top border?
1017 if (aPoint.X() <= nRectRight)
1019 nRight = aPoint.X();
1020 nTop = nRectTop;
1022 else
1024 // there has to be a point of intersection with the right border!
1025 GetLineIntersectionPoint(aPoint,
1026 Point(nRectRight, nRectTop), aDownHdg,
1027 rDiagPoint, aDiagHdg);
1029 nRight = nRectRight;
1030 nTop = aPoint.Y();
1033 // determine bottom left corner
1034 GetLineIntersectionPoint(aPoint,
1035 Point(nRectLeft, nRectBottom), aRightHdg,
1036 rDiagPoint, aDiagHdg);
1037 // is there a point of intersection with the bottom border?
1038 if (aPoint.X() >= nRectLeft)
1040 nLeft = aPoint.X();
1041 nBottom = nRectBottom;
1043 else
1045 // there has to be a point of intersection with the left border!
1046 GetLineIntersectionPoint(aPoint,
1047 Point(nRectLeft, nRectTop), aDownHdg,
1048 rDiagPoint, aDiagHdg);
1050 nLeft = nRectLeft;
1051 nBottom = aPoint.Y();
1054 else
1056 // determine top left corner
1057 GetLineIntersectionPoint(aPoint,
1058 Point(nRectLeft, nRectTop), aRightHdg,
1059 rDiagPoint, aDiagHdg);
1060 // is there a point of intersection with the top border?
1061 if (aPoint.X() >= nRectLeft)
1063 nLeft = aPoint.X();
1064 nTop = nRectTop;
1066 else
1068 // there has to be a point of intersection with the left border!
1069 GetLineIntersectionPoint(aPoint,
1070 Point(nRectLeft, nRectTop), aDownHdg,
1071 rDiagPoint, aDiagHdg);
1073 nLeft = nRectLeft;
1074 nTop = aPoint.Y();
1077 // determine bottom right corner
1078 GetLineIntersectionPoint(aPoint,
1079 Point(nRectLeft, nRectBottom), aRightHdg,
1080 rDiagPoint, aDiagHdg);
1081 // is there a point of intersection with the bottom border?
1082 if (aPoint.X() <= nRectRight)
1084 nRight = aPoint.X();
1085 nBottom = nRectBottom;
1087 else
1089 // there has to be a point of intersection with the right border!
1090 GetLineIntersectionPoint(aPoint,
1091 Point(nRectRight, nRectTop), aDownHdg,
1092 rDiagPoint, aDiagHdg);
1094 nRight = nRectRight;
1095 nBottom = aPoint.Y();
1099 rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
1100 rPos.setX( nLeft );
1101 rPos.setY( nTop );
1105 void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1107 // Both arguments have to get into the SubNodes before the Operator so that clicking
1108 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1109 SmNode *pLeft = GetSubNode(0),
1110 *pRight = GetSubNode(1),
1111 *pLine = GetSubNode(2);
1112 assert(pLeft);
1113 assert(pRight);
1114 assert(pLine && pLine->GetType() == SmNodeType::PolyLine);
1116 SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
1117 assert(pOper);
1119 //! some routines being called extract some info from the OutputDevice's
1120 //! font (eg the space to be used for borders OR the font name(!!)).
1121 //! Thus the font should reflect the needs and has to be set!
1122 SmTmpDevice aTmpDev (rDev, true);
1123 aTmpDev.SetFont(GetFont());
1125 pLeft->Arrange(aTmpDev, rFormat);
1126 pRight->Arrange(aTmpDev, rFormat);
1128 // determine implicitly the values (incl. the margin) of the diagonal line
1129 pOper->Arrange(aTmpDev, rFormat);
1131 tools::Long nDelta = pOper->GetWidth() * 8 / 10;
1133 // determine TopLeft position from the right argument
1134 Point aPos;
1135 aPos.setX( pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace() );
1136 if (IsAscending())
1137 aPos.setY( pLeft->GetBottom() + nDelta );
1138 else
1139 aPos.setY( pLeft->GetTop() - nDelta - pRight->GetHeight() );
1141 pRight->MoveTo(aPos);
1143 // determine new baseline
1144 tools::Long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
1145 : (pLeft->GetTop() + pRight->GetBottom()) / 2;
1146 Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
1147 nTmpBaseline);
1149 SmRect::operator = (*pLeft);
1150 ExtendBy(*pRight, RectCopyMBL::None);
1153 // determine position and size of diagonal line
1154 Size aTmpSize;
1155 GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
1157 // font specialist advised to change the width first
1158 pOper->AdaptToY(aTmpDev, aTmpSize.Height());
1159 pOper->AdaptToX(aTmpDev, aTmpSize.Width());
1160 // and make it active
1161 pOper->Arrange(aTmpDev, rFormat);
1163 pOper->MoveTo(aPos);
1165 ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
1169 /**************************************************************************/
1172 void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1174 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
1175 "Sm: wrong number of subnodes");
1177 SmNode *pBody = GetBody();
1178 assert(pBody);
1180 tools::Long nOrigHeight = pBody->GetFont().GetFontSize().Height();
1182 pBody->Arrange(rDev, rFormat);
1184 const SmRect &rBodyRect = pBody->GetRect();
1185 SmRect::operator = (rBodyRect);
1187 // line that separates sub- and supscript rectangles
1188 tools::Long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1190 Point aPos;
1191 tools::Long nDelta, nDist;
1193 // iterate over all possible sub-/supscripts
1194 SmRect aTmpRect (rBodyRect);
1195 for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
1197 SmSubSup eSubSup = static_cast<SmSubSup>(i);
1198 SmNode *pSubSup = GetSubSup(eSubSup);
1200 if (!pSubSup)
1201 continue;
1203 // switch position of limits if we are in textmode
1204 if (rFormat.IsTextmode() && (GetToken().nGroup & TG::Limit))
1205 switch (eSubSup)
1206 { case CSUB: eSubSup = RSUB; break;
1207 case CSUP: eSubSup = RSUP; break;
1208 default:
1209 break;
1212 // prevent sub-/supscripts from diminishing in size
1213 // (as would be in "a_{1_{2_{3_4}}}")
1214 if (GetFont().GetFontSize().Height() > rFormat.GetBaseSize().Height() / 3)
1216 sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
1217 SIZ_LIMITS : SIZ_INDEX;
1218 Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
1219 pSubSup->SetSize(aFraction);
1222 pSubSup->Arrange(rDev, rFormat);
1224 bool bIsTextmode = rFormat.IsTextmode();
1225 nDist = 0;
1227 //! be sure that CSUB, CSUP are handled before the other cases!
1228 switch (eSubSup)
1229 { case RSUB :
1230 case LSUB :
1231 if (!bIsTextmode)
1232 nDist = nOrigHeight
1233 * rFormat.GetDistance(DIS_SUBSCRIPT) / 100;
1234 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1235 eSubSup == LSUB ? RectPos::Left : RectPos::Right,
1236 RectHorAlign::Center, RectVerAlign::Bottom);
1237 aPos.AdjustY(nDist );
1238 nDelta = nDelimLine - aPos.Y();
1239 if (nDelta > 0)
1240 aPos.AdjustY(nDelta );
1241 break;
1242 case RSUP :
1243 case LSUP :
1244 if (!bIsTextmode)
1245 nDist = nOrigHeight
1246 * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100;
1247 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1248 eSubSup == LSUP ? RectPos::Left : RectPos::Right,
1249 RectHorAlign::Center, RectVerAlign::Top);
1250 aPos.AdjustY( -nDist );
1251 nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
1252 if (nDelta > 0)
1253 aPos.AdjustY( -nDelta );
1254 break;
1255 case CSUB :
1256 if (!bIsTextmode)
1257 nDist = nOrigHeight
1258 * rFormat.GetDistance(DIS_LOWERLIMIT) / 100;
1259 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Bottom,
1260 RectHorAlign::Center, RectVerAlign::Baseline);
1261 aPos.AdjustY(nDist );
1262 break;
1263 case CSUP :
1264 if (!bIsTextmode)
1265 nDist = nOrigHeight
1266 * rFormat.GetDistance(DIS_UPPERLIMIT) / 100;
1267 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Top,
1268 RectHorAlign::Center, RectVerAlign::Baseline);
1269 aPos.AdjustY( -nDist );
1270 break;
1273 pSubSup->MoveTo(aPos);
1274 ExtendBy(*pSubSup, RectCopyMBL::This, true);
1276 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1277 // will be aligned to
1278 if (eSubSup == CSUB || eSubSup == CSUP)
1279 aTmpRect = *this;
1283 /**************************************************************************/
1285 void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1287 SmNode *pLeft = OpeningBrace(),
1288 *pBody = Body(),
1289 *pRight = ClosingBrace();
1290 assert(pLeft);
1291 assert(pBody);
1292 assert(pRight);
1294 pBody->Arrange(rDev, rFormat);
1296 bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
1297 bScale = pBody->GetHeight() > 0 &&
1298 (GetScaleMode() == SmScaleMode::Height || bIsScaleNormal),
1299 bIsABS = GetToken().eType == TABS;
1301 tools::Long nFaceHeight = GetFont().GetFontSize().Height();
1303 // determine oversize in %
1304 sal_uInt16 nPerc = 0;
1305 if (!bIsABS && bScale)
1306 { // in case of oversize braces...
1307 sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
1308 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1309 nPerc = rFormat.GetDistance(nIndex);
1312 // determine the height for the braces
1313 tools::Long nBraceHeight;
1314 if (bScale)
1316 nBraceHeight = pBody->GetType() == SmNodeType::Bracebody ?
1317 static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
1318 : pBody->GetHeight();
1319 nBraceHeight += 2 * (nBraceHeight * nPerc / 100);
1321 else
1322 nBraceHeight = nFaceHeight;
1324 // distance to the argument
1325 nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
1326 tools::Long nDist = nFaceHeight * nPerc / 100;
1328 // if wanted, scale the braces to the wanted size
1329 if (bScale)
1331 Size aTmpSize (pLeft->GetFont().GetFontSize());
1332 OSL_ENSURE(pRight->GetFont().GetFontSize() == aTmpSize,
1333 "Sm : different font sizes");
1334 aTmpSize.setWidth( std::min(nBraceHeight * 60 / 100,
1335 rFormat.GetBaseSize().Height() * 3 / 2) );
1336 // correction factor since change from StarMath to OpenSymbol font
1337 // because of the different font width in the FontMetric
1338 aTmpSize.setWidth( aTmpSize.Width() * 182 );
1339 aTmpSize.setWidth( aTmpSize.Width() / 267 );
1341 sal_Unicode cChar = pLeft->GetToken().cMathChar[0];
1342 if (cChar != MS_LINE && cChar != MS_DLINE &&
1343 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1344 pLeft ->GetFont().SetSize(aTmpSize);
1346 cChar = pRight->GetToken().cMathChar[0];
1347 if (cChar != MS_LINE && cChar != MS_DLINE &&
1348 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1349 pRight->GetFont().SetSize(aTmpSize);
1351 pLeft ->AdaptToY(rDev, nBraceHeight);
1352 pRight->AdaptToY(rDev, nBraceHeight);
1355 pLeft ->Arrange(rDev, rFormat);
1356 pRight->Arrange(rDev, rFormat);
1358 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1359 RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1361 Point aPos;
1362 aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
1363 aPos.AdjustX( -nDist );
1364 pLeft->MoveTo(aPos);
1366 aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
1367 aPos.AdjustX(nDist );
1368 pRight->MoveTo(aPos);
1370 SmRect::operator = (*pBody);
1371 ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
1375 /**************************************************************************/
1378 void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1380 size_t nNumSubNodes = GetNumSubNodes();
1381 if (nNumSubNodes == 0)
1382 return;
1384 // arrange arguments
1385 for (size_t i = 0; i < nNumSubNodes; i += 2)
1386 GetSubNode(i)->Arrange(rDev, rFormat);
1388 // build reference rectangle with necessary info for vertical alignment
1389 SmRect aRefRect (*GetSubNode(0));
1390 for (size_t i = 0; i < nNumSubNodes; i += 2)
1392 SmRect aTmpRect (*GetSubNode(i));
1393 Point aPos = aTmpRect.AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
1394 aTmpRect.MoveTo(aPos);
1395 aRefRect.ExtendBy(aTmpRect, RectCopyMBL::Xor);
1398 mnBodyHeight = aRefRect.GetHeight();
1400 // scale separators to required height and arrange them
1401 bool bScale = GetScaleMode() == SmScaleMode::Height || rFormat.IsScaleNormalBrackets();
1402 tools::Long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetFontSize().Height();
1403 sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
1404 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1405 sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
1406 if (bScale)
1407 nHeight += 2 * (nHeight * nPerc / 100);
1408 for (size_t i = 1; i < nNumSubNodes; i += 2)
1410 SmNode *pNode = GetSubNode(i);
1411 pNode->AdaptToY(rDev, nHeight);
1412 pNode->Arrange(rDev, rFormat);
1415 // horizontal distance between argument and brackets or separators
1416 tools::Long nDist = GetFont().GetFontSize().Height()
1417 * rFormat.GetDistance(DIS_BRACKETSPACE) / 100;
1419 SmNode *pLeft = GetSubNode(0);
1420 SmRect::operator = (*pLeft);
1421 for (size_t i = 1; i < nNumSubNodes; ++i)
1423 bool bIsSeparator = i % 2 != 0;
1424 RectVerAlign eVerAlign = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1426 SmNode *pRight = GetSubNode(i);
1427 Point aPosX = pRight->AlignTo(*pLeft, RectPos::Right, RectHorAlign::Center, eVerAlign),
1428 aPosY = pRight->AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, eVerAlign);
1429 aPosX.AdjustX(nDist );
1431 pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
1432 ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
1434 pLeft = pRight;
1439 /**************************************************************************/
1442 void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1444 SmNode *pBody = Body(),
1445 *pBrace = Brace(),
1446 *pScript = Script();
1447 assert(pBody);
1448 assert(pBrace);
1449 assert(pScript);
1451 SmTmpDevice aTmpDev (rDev, true);
1452 aTmpDev.SetFont(GetFont());
1454 pBody->Arrange(aTmpDev, rFormat);
1456 // size is the same as for limits for this part
1457 pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
1458 // braces are a bit taller than usually
1459 pBrace ->SetSize( Fraction(3, 2) );
1461 tools::Long nItalicWidth = pBody->GetItalicWidth();
1462 if (nItalicWidth > 0)
1463 pBrace->AdaptToX(aTmpDev, nItalicWidth);
1465 pBrace ->Arrange(aTmpDev, rFormat);
1466 pScript->Arrange(aTmpDev, rFormat);
1468 // determine the relative position and the distances between each other
1469 RectPos eRectPos;
1470 tools::Long nFontHeight = pBody->GetFont().GetFontSize().Height();
1471 tools::Long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
1472 nDistScript = nFontHeight;
1473 if (GetToken().eType == TOVERBRACE)
1475 eRectPos = RectPos::Top;
1476 nDistBody = - nDistBody;
1477 nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
1479 else // TUNDERBRACE
1481 eRectPos = RectPos::Bottom;
1482 nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
1484 nDistBody /= 100;
1485 nDistScript /= 100;
1487 Point aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1488 aPos.AdjustY(nDistBody );
1489 pBrace->MoveTo(aPos);
1491 aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1492 aPos.AdjustY(nDistScript );
1493 pScript->MoveTo(aPos);
1495 SmRect::operator = (*pBody);
1496 ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
1500 /**************************************************************************/
1503 SmNode * SmOperNode::GetSymbol()
1505 SmNode *pNode = GetSubNode(0);
1506 assert(pNode);
1508 if (pNode->GetType() == SmNodeType::SubSup)
1509 pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
1511 OSL_ENSURE(pNode, "Sm: NULL pointer!");
1512 return pNode;
1516 tools::Long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
1517 const SmFormat &rFormat) const
1518 // returns the font height to be used for operator-symbol
1520 tools::Long nHeight = GetFont().GetFontSize().Height();
1522 SmTokenType eTmpType = GetToken().eType;
1523 if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
1524 return nHeight;
1526 if (!rFormat.IsTextmode())
1528 // set minimum size ()
1529 nHeight += (nHeight * 20) / 100;
1531 nHeight += nHeight
1532 * rFormat.GetDistance(DIS_OPERATORSIZE) / 100;
1533 nHeight = nHeight * 686 / 845;
1536 // correct user-defined symbols to match height of sum from used font
1537 if (rSymbol.GetToken().eType == TSPECIAL)
1538 nHeight = nHeight * 845 / 686;
1540 return nHeight;
1544 void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1546 SmNode *pOper = GetSubNode(0);
1547 SmNode *pBody = GetSubNode(1);
1549 assert(pOper);
1550 assert(pBody);
1552 SmNode *pSymbol = GetSymbol();
1553 pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
1554 pSymbol->GetFont().GetFontSize().Height()));
1556 pBody->Arrange(rDev, rFormat);
1557 bool bDynamicallySized = false;
1558 if (pSymbol->GetToken().eType == TINTD)
1560 tools::Long nBodyHeight = pBody->GetHeight();
1561 tools::Long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
1562 if (nFontHeight < nBodyHeight)
1564 pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
1565 bDynamicallySized = true;
1568 pOper->Arrange(rDev, rFormat);
1570 tools::Long nOrigHeight = GetFont().GetFontSize().Height(),
1571 nDist = nOrigHeight
1572 * rFormat.GetDistance(DIS_OPERATORSPACE) / 100;
1574 Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
1575 aPos.AdjustX( -nDist );
1576 pOper->MoveTo(aPos);
1578 SmRect::operator = (*pBody);
1579 ExtendBy(*pOper, RectCopyMBL::This);
1583 /**************************************************************************/
1586 void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1587 // set alignment within the entire subtree (including current node)
1589 assert(GetNumSubNodes() == 1);
1591 SmNode *pNode = GetSubNode(0);
1592 assert(pNode);
1594 RectHorAlign eHorAlign = RectHorAlign::Center;
1595 switch (GetToken().eType)
1597 case TALIGNL: eHorAlign = RectHorAlign::Left; break;
1598 case TALIGNC: eHorAlign = RectHorAlign::Center; break;
1599 case TALIGNR: eHorAlign = RectHorAlign::Right; break;
1600 default:
1601 break;
1603 SetRectHorAlign(eHorAlign);
1605 pNode->Arrange(rDev, rFormat);
1607 SmRect::operator = (pNode->GetRect());
1611 /**************************************************************************/
1614 void SmAttributeNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1616 SmNode *pAttr = Attribute(),
1617 *pBody = Body();
1618 assert(pBody);
1619 assert(pAttr);
1621 pBody->Arrange(rDev, rFormat);
1623 if (GetScaleMode() == SmScaleMode::Width)
1624 pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
1625 pAttr->Arrange(rDev, rFormat);
1627 // get relative position of attribute
1628 RectVerAlign eVerAlign;
1629 tools::Long nDist = 0;
1630 switch (GetToken().eType)
1631 { case TUNDERLINE :
1632 eVerAlign = RectVerAlign::AttributeLo;
1633 break;
1634 case TOVERSTRIKE :
1635 eVerAlign = RectVerAlign::AttributeMid;
1636 break;
1637 default :
1638 eVerAlign = RectVerAlign::AttributeHi;
1639 if (pBody->GetType() == SmNodeType::Attribute)
1640 nDist = GetFont().GetFontSize().Height()
1641 * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100;
1643 Point aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
1644 aPos.AdjustY( -nDist );
1645 pAttr->MoveTo(aPos);
1647 SmRect::operator = (*pBody);
1648 ExtendBy(*pAttr, RectCopyMBL::This, true);
1651 void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1653 //! prepare subnodes first
1654 SmNode::Prepare(rFormat, rDocShell, nDepth);
1656 int nFnt = -1;
1657 switch (GetToken().eType)
1659 case TFIXED: nFnt = FNT_FIXED; break;
1660 case TSANS: nFnt = FNT_SANS; break;
1661 case TSERIF: nFnt = FNT_SERIF; break;
1662 default:
1663 break;
1665 if (nFnt != -1)
1666 { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
1667 SetFont(GetFont());
1670 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
1671 //! other font nodes (those with lower depth in the tree)
1672 Flags() |= FontChangeMask::Face;
1675 void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1677 SmNode *pNode = GetSubNode(1);
1678 assert(pNode);
1679 sal_uInt32 nc;
1681 switch (GetToken().eType)
1682 { case TSIZE :
1683 pNode->SetFontSize(maFontSize, meSizeType);
1684 break;
1685 case TSANS :
1686 case TSERIF :
1687 case TFIXED :
1688 pNode->SetFont(GetFont());
1689 break;
1690 case TUNKNOWN : break; // no assertion on "font <?> <?>"
1692 case TPHANTOM : SetPhantom(true); break;
1693 case TBOLD : SetAttribute(FontAttribute::Bold); break;
1694 case TITALIC : SetAttribute(FontAttribute::Italic); break;
1695 case TNBOLD : ClearAttribute(FontAttribute::Bold); break;
1696 case TNITALIC : ClearAttribute(FontAttribute::Italic); break;
1698 // Using HTML CSS Level 1 standard
1699 case TRGB :
1700 case TRGBA :
1701 case THTMLCOL :
1702 case TMATHMLCOL :
1703 case TDVIPSNAMESCOL:
1704 case TICONICCOL :
1705 case THEX :
1706 nc = GetToken().cMathChar.toUInt32(16);
1707 SetColor(Color(ColorTransparency, nc));
1708 break;
1710 default:
1711 SAL_WARN("starmath", "unknown case");
1714 pNode->Arrange(rDev, rFormat);
1716 SmRect::operator = (pNode->GetRect());
1719 /**************************************************************************/
1722 SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
1723 : SmGraphicNode(SmNodeType::PolyLine, rNodeToken)
1724 , maPoly(2)
1725 , mnWidth(0)
1730 void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
1732 maToSize.setWidth( nNewWidth );
1736 void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
1738 GetFont().FreezeBorderWidth();
1739 maToSize.setHeight( nNewHeight );
1743 void SmPolyLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1745 //! some routines being called extract some info from the OutputDevice's
1746 //! font (eg the space to be used for borders OR the font name(!!)).
1747 //! Thus the font should reflect the needs and has to be set!
1748 SmTmpDevice aTmpDev (rDev, true);
1749 aTmpDev.SetFont(GetFont());
1751 tools::Long nBorderwidth = GetFont().GetBorderWidth();
1753 // create polygon using both endpoints
1754 assert(maPoly.GetSize() == 2);
1755 Point aPointA, aPointB;
1756 if (GetToken().eType == TWIDESLASH)
1758 aPointA.setX( nBorderwidth );
1759 aPointA.setY( maToSize.Height() - nBorderwidth );
1760 aPointB.setX( maToSize.Width() - nBorderwidth );
1761 aPointB.setY( nBorderwidth );
1763 else
1765 OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
1766 aPointA.setX( nBorderwidth );
1767 aPointA.setY( nBorderwidth );
1768 aPointB.setX( maToSize.Width() - nBorderwidth );
1769 aPointB.setY( maToSize.Height() - nBorderwidth );
1771 maPoly.SetPoint(aPointA, 0);
1772 maPoly.SetPoint(aPointB, 1);
1774 tools::Long nThick = GetFont().GetFontSize().Height()
1775 * rFormat.GetDistance(DIS_STROKEWIDTH) / 100;
1776 mnWidth = nThick + 2 * nBorderwidth;
1778 SmRect::operator = (SmRect(maToSize.Width(), maToSize.Height()));
1782 /**************************************************************************/
1784 void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1786 mnBodyWidth = nWidth;
1790 void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
1792 // some additional length so that the horizontal
1793 // bar will be positioned above the argument
1794 SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10);
1798 /**************************************************************************/
1801 void SmRectangleNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1803 maToSize.setWidth( nWidth );
1807 void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
1809 GetFont().FreezeBorderWidth();
1810 maToSize.setHeight( nHeight );
1814 void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
1816 tools::Long nFontHeight = GetFont().GetFontSize().Height();
1817 tools::Long nWidth = maToSize.Width(),
1818 nHeight = maToSize.Height();
1819 if (nHeight == 0)
1820 nHeight = nFontHeight / 30;
1821 if (nWidth == 0)
1822 nWidth = nFontHeight / 3;
1824 SmTmpDevice aTmpDev (rDev, true);
1825 aTmpDev.SetFont(GetFont());
1827 // add some borderspace
1828 sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth();
1829 nHeight += 2 * nTmpBorderWidth;
1831 //! use this method in order to have 'SmRect::HasAlignInfo() == true'
1832 //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
1833 SmRect::operator = (SmRect(nWidth, nHeight));
1837 /**************************************************************************/
1840 SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
1841 : SmVisibleNode(eNodeType, rNodeToken)
1842 , mnFontDesc(nFontDescP)
1843 , mnSelectionStart(0)
1844 , mnSelectionEnd(0)
1848 SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
1849 : SmVisibleNode(SmNodeType::Text, rNodeToken)
1850 , mnFontDesc(nFontDescP)
1851 , mnSelectionStart(0)
1852 , mnSelectionEnd(0)
1856 void SmTextNode::ChangeText(const OUString &rText) {
1857 maText = rText;
1858 GetToken().aText = rText;
1859 AdjustFontDesc();
1862 void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1864 SmNode::Prepare(rFormat, rDocShell, nDepth);
1866 // default setting for horizontal alignment of nodes with TTEXT
1867 // content is as alignl (cannot be done in Arrange since it would
1868 // override the settings made by an SmAlignNode before)
1869 if (TTEXT == GetToken().eType)
1870 SetRectHorAlign( RectHorAlign::Left );
1872 maText = GetToken().aText;
1873 GetFont() = rFormat.GetFont(GetFontDesc());
1875 if (IsItalic( GetFont() ))
1876 Attributes() |= FontAttribute::Italic;
1877 if (IsBold( GetFont() ))
1878 Attributes() |= FontAttribute::Bold;
1880 // special handling for ':' where it is a token on its own and is likely
1881 // to be used for mathematical notations. (E.g. a:b = 2:3)
1882 // In that case it should not be displayed in italic.
1883 if (GetToken().aText.getLength() == 1 && GetToken().aText[0] == ':')
1884 Attributes() &= ~FontAttribute::Italic;
1888 void SmTextNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1890 PrepareAttributes();
1892 sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
1893 SIZ_FUNCTION : SIZ_TEXT;
1894 GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
1896 SmTmpDevice aTmpDev (rDev, true);
1897 aTmpDev.SetFont(GetFont());
1899 SmRect::operator = (SmRect(aTmpDev, &rFormat, maText, GetFont().GetBorderWidth()));
1902 void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
1904 rText.append(maText);
1907 void SmTextNode::AdjustFontDesc()
1909 if (GetToken().nGroup == TG::Function) mnFontDesc = FNT_FUNCTION;
1910 else if (GetToken().eType == TTEXT) mnFontDesc = FNT_TEXT;
1911 else {
1912 sal_Unicode firstChar = maText[0];
1913 if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',')
1914 mnFontDesc = FNT_NUMBER;
1915 else mnFontDesc = FNT_VARIABLE;
1919 sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
1921 //Find the best match in accepted unicode for our private area symbols
1922 static const sal_Unicode aStarMathPrivateToUnicode[] =
1924 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
1925 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
1926 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
1927 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
1928 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
1929 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
1930 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
1931 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
1932 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
1933 0xE0DA, 0x2190, 0x2191, 0x2193
1935 if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
1936 nIn = aStarMathPrivateToUnicode[nIn-0xE080];
1938 //For whatever unicode glyph that equation editor doesn't ship with that
1939 //we have a possible match we can munge it to.
1940 switch (nIn)
1942 case 0x2223:
1943 nIn = '|';
1944 break;
1945 default:
1946 break;
1949 return nIn;
1952 /**************************************************************************/
1954 void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1956 SmNode *pNode;
1958 // initialize array that is to hold the maximum widths of all
1959 // elements (subnodes) in that column.
1960 std::vector<tools::Long> aColWidth(mnNumCols);
1962 // arrange subnodes and calculate the above arrays contents
1963 size_t nNodes = GetNumSubNodes();
1964 for (size_t i = 0; i < nNodes; ++i)
1966 size_t nIdx = nNodes - 1 - i;
1967 if (nullptr != (pNode = GetSubNode(nIdx)))
1969 pNode->Arrange(rDev, rFormat);
1970 int nCol = nIdx % mnNumCols;
1971 aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
1975 // norm distance from which the following two are calculated
1976 const tools::Long nNormDist = 3 * GetFont().GetFontSize().Height();
1978 // define horizontal and vertical minimal distances that separate
1979 // the elements
1980 tools::Long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100,
1981 nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100;
1983 // build array that holds the leftmost position for each column
1984 std::vector<tools::Long> aColLeft(mnNumCols);
1985 tools::Long nX = 0;
1986 for (size_t j = 0; j < mnNumCols; ++j)
1988 aColLeft[j] = nX;
1989 nX += aColWidth[j] + nHorDist;
1992 SmRect::operator = (SmRect());
1993 for (size_t i = 0; i < mnNumRows; ++i)
1995 Point aPos;
1996 SmRect aLineRect;
1997 for (size_t j = 0; j < mnNumCols; ++j)
1999 SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
2000 assert(pTmpNode);
2002 const SmRect &rNodeRect = pTmpNode->GetRect();
2004 // align all baselines in that row if possible
2005 aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
2007 // get horizontal alignment
2008 const SmNode *pCoNode = pTmpNode->GetLeftMost();
2009 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
2011 // calculate horizontal position of element depending on column
2012 // and horizontal alignment
2013 switch (eHorAlign)
2014 { case RectHorAlign::Left:
2015 aPos.setX( aColLeft[j] );
2016 break;
2017 case RectHorAlign::Center:
2018 aPos.setX( rNodeRect.GetLeft() + aColLeft[j]
2019 + aColWidth[j] / 2
2020 - rNodeRect.GetItalicCenterX() );
2021 break;
2022 case RectHorAlign::Right:
2023 aPos.setX( aColLeft[j]
2024 + aColWidth[j] - rNodeRect.GetItalicWidth() );
2025 break;
2026 default:
2027 assert(false);
2030 pTmpNode->MoveTo(aPos);
2031 aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
2034 aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
2035 if (i > 0)
2036 aPos.AdjustY(nVerDist );
2038 // move 'aLineRect' and rectangles in that line to final position
2039 Point aDelta(0, // since horizontal alignment is already done
2040 aPos.Y() - aLineRect.GetTop());
2041 aLineRect.Move(aDelta);
2042 for (size_t j = 0; j < mnNumCols; ++j)
2044 if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
2045 pNode->Move(aDelta);
2048 ExtendBy(aLineRect, RectCopyMBL::None);
2052 const SmNode * SmMatrixNode::GetLeftMost() const
2054 return this;
2058 /**************************************************************************/
2061 SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
2062 : SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH)
2064 SetText(GetToken().cMathChar);
2067 void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
2069 // Since there is no function to do this, we try to approximate it:
2070 Size aFntSize (GetFont().GetFontSize());
2072 //! however the result is a bit better with 'nWidth' as initial font width
2073 aFntSize.setWidth( nWidth );
2074 GetFont().SetSize(aFntSize);
2076 SmTmpDevice aTmpDev (rDev, true);
2077 aTmpDev.SetFont(GetFont());
2079 // get denominator of error factor for width
2080 tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
2081 tools::Long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
2083 // scale fontwidth with this error factor
2084 aFntSize.setWidth( aFntSize.Width() * nWidth );
2085 aFntSize.setWidth( aFntSize.Width() / ( nDenom ? nDenom : 1) );
2087 GetFont().SetSize(aFntSize);
2090 void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
2092 GetFont().FreezeBorderWidth();
2093 Size aFntSize (GetFont().GetFontSize());
2095 // Since we only want to scale the height, we might have
2096 // to determine the font width in order to keep it
2097 if (aFntSize.Width() == 0)
2099 rDev.Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE);
2100 rDev.SetFont(GetFont());
2101 aFntSize.setWidth( rDev.GetFontMetric().GetFontSize().Width() );
2102 rDev.Pop();
2104 OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
2106 //! however the result is a bit better with 'nHeight' as initial
2107 //! font height
2108 aFntSize.setHeight( nHeight );
2109 GetFont().SetSize(aFntSize);
2111 SmTmpDevice aTmpDev (rDev, true);
2112 aTmpDev.SetFont(GetFont());
2114 // get denominator of error factor for height
2115 tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
2116 tools::Long nDenom = 0;
2117 if (!GetText().isEmpty())
2118 nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetHeight();
2120 // scale fontwidth with this error factor
2121 aFntSize.setHeight( aFntSize.Height() * nHeight );
2122 aFntSize.setHeight( aFntSize.Height() / ( nDenom ? nDenom : 1) );
2124 GetFont().SetSize(aFntSize);
2128 void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2130 SmNode::Prepare(rFormat, rDocShell, nDepth);
2132 GetFont() = rFormat.GetFont(GetFontDesc());
2133 // use same font size as is used for variables
2134 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2136 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
2137 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
2138 "wrong charset for character from StarMath/OpenSymbol font");
2140 Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
2144 void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2146 const OUString &rText = GetText();
2148 if (rText.isEmpty() || rText[0] == '\0')
2149 { SmRect::operator = (SmRect());
2150 return;
2153 PrepareAttributes();
2155 GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
2157 SmTmpDevice aTmpDev (rDev, true);
2158 aTmpDev.SetFont(GetFont());
2160 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2163 /**************************************************************************/
2165 static bool lcl_IsFromGreekSymbolSet( const OUString &rTokenText )
2167 bool bRes = false;
2169 // valid symbol name needs to have a '%' at pos 0 and at least an additional char
2170 if (rTokenText.getLength() > 2 && rTokenText[0] == u'%')
2172 OUString aName( rTokenText.copy(1) );
2173 SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName );
2174 if (pSymbol && SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
2175 bRes = true;
2178 return bRes;
2182 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
2183 : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
2184 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
2189 SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
2190 : SmTextNode(SmNodeType::Special, rNodeToken, FNT_MATH) // default Font isn't always correct!
2191 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
2196 void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2198 SmNode::Prepare(rFormat, rDocShell, nDepth);
2200 const SmSym *pSym;
2201 SmModule *pp = SM_MOD();
2203 OUString aName(GetToken().aText.copy(1));
2204 if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName( aName )))
2206 sal_UCS4 cChar = pSym->GetCharacter();
2207 OUString aTmp( &cChar, 1 );
2208 SetText( aTmp );
2209 GetFont() = pSym->GetFace();
2211 else
2213 SetText( GetToken().aText );
2214 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2216 // use same font size as is used for variables
2217 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2219 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2220 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2221 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2223 //! see also SmFontStyles::GetStyleName
2224 if (IsItalic( GetFont() ))
2225 SetAttribute(FontAttribute::Italic);
2226 if (IsBold( GetFont() ))
2227 SetAttribute(FontAttribute::Bold);
2229 Flags() |= FontChangeMask::Face;
2231 if (!mbIsFromGreekSymbolSet)
2232 return;
2234 OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
2235 bool bItalic = false;
2236 sal_Int16 nStyle = rFormat.GetGreekCharStyle();
2237 OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
2238 if (nStyle == 1)
2239 bItalic = true;
2240 else if (nStyle == 2)
2242 const OUString& rTmp(GetText());
2243 if (!rTmp.isEmpty())
2245 static const sal_Unicode cUppercaseAlpha = 0x0391;
2246 static const sal_Unicode cUppercaseOmega = 0x03A9;
2247 sal_Unicode cChar = rTmp[0];
2248 // uppercase letters should be straight and lowercase letters italic
2249 bItalic = cUppercaseAlpha > cChar || cChar > cUppercaseOmega;
2253 if (bItalic)
2254 Attributes() |= FontAttribute::Italic;
2255 else
2256 Attributes() &= ~FontAttribute::Italic;
2260 void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2262 PrepareAttributes();
2264 SmTmpDevice aTmpDev (rDev, true);
2265 aTmpDev.SetFont(GetFont());
2267 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2270 /**************************************************************************/
2273 void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2275 PrepareAttributes();
2277 SmTmpDevice aTmpDev (rDev, true);
2278 aTmpDev.SetFont(GetFont());
2280 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
2281 GetFont().GetBorderWidth()).AsGlyphRect());
2285 /**************************************************************************/
2288 void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2290 SmNode::Prepare(rFormat, rDocShell, nDepth);
2292 GetFont().SetColor(COL_GRAY);
2293 Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
2297 void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2299 PrepareAttributes();
2301 SmTmpDevice aTmpDev (rDev, true);
2302 aTmpDev.SetFont(GetFont());
2304 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2308 /**************************************************************************/
2311 void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2313 SmNode::Prepare(rFormat, rDocShell, nDepth);
2315 GetFont().SetColor(COL_RED);
2316 Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
2317 | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
2321 void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2323 PrepareAttributes();
2325 SmTmpDevice aTmpDev (rDev, true);
2326 aTmpDev.SetFont(GetFont());
2328 const OUString &rText = GetText();
2329 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2332 /**************************************************************************/
2334 void SmBlankNode::IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy)
2336 switch(rToken.eType)
2338 case TBLANK: mnNum += (4 * nMultiplyBy); break;
2339 case TSBLANK: mnNum += (1 * nMultiplyBy); break;
2340 default:
2341 break;
2345 void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2347 SmNode::Prepare(rFormat, rDocShell, nDepth);
2349 // Here it need/should not be the StarMath font, so that for the character
2350 // used in Arrange a normal (non-clipped) rectangle is generated
2351 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2353 Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
2357 void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2359 SmTmpDevice aTmpDev (rDev, true);
2360 aTmpDev.SetFont(GetFont());
2362 // make distance depend on the font height
2363 // (so that it increases when scaling (e.g. size *2 {a ~ b})
2364 tools::Long nDist = GetFont().GetFontSize().Height() / 10,
2365 nSpace = mnNum * nDist;
2367 // get a SmRect with Baseline and all the bells and whistles
2368 SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
2369 GetFont().GetBorderWidth()));
2371 // and resize it to the requested size
2372 SetItalicSpaces(0, 0);
2373 SetWidth(nSpace);
2376 /**************************************************************************/
2377 //Implementation of all accept methods for SmVisitor
2379 void SmTableNode::Accept(SmVisitor* pVisitor) {
2380 pVisitor->Visit(this);
2383 void SmBraceNode::Accept(SmVisitor* pVisitor) {
2384 pVisitor->Visit(this);
2387 void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
2388 pVisitor->Visit(this);
2391 void SmOperNode::Accept(SmVisitor* pVisitor) {
2392 pVisitor->Visit(this);
2395 void SmAlignNode::Accept(SmVisitor* pVisitor) {
2396 pVisitor->Visit(this);
2399 void SmAttributeNode::Accept(SmVisitor* pVisitor) {
2400 pVisitor->Visit(this);
2403 void SmFontNode::Accept(SmVisitor* pVisitor) {
2404 pVisitor->Visit(this);
2407 void SmUnHorNode::Accept(SmVisitor* pVisitor) {
2408 pVisitor->Visit(this);
2411 void SmBinHorNode::Accept(SmVisitor* pVisitor) {
2412 pVisitor->Visit(this);
2415 void SmBinVerNode::Accept(SmVisitor* pVisitor) {
2416 pVisitor->Visit(this);
2419 void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
2420 pVisitor->Visit(this);
2423 void SmSubSupNode::Accept(SmVisitor* pVisitor) {
2424 pVisitor->Visit(this);
2427 void SmMatrixNode::Accept(SmVisitor* pVisitor) {
2428 pVisitor->Visit(this);
2431 void SmPlaceNode::Accept(SmVisitor* pVisitor) {
2432 pVisitor->Visit(this);
2435 void SmTextNode::Accept(SmVisitor* pVisitor) {
2436 pVisitor->Visit(this);
2439 void SmSpecialNode::Accept(SmVisitor* pVisitor) {
2440 pVisitor->Visit(this);
2443 void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
2444 pVisitor->Visit(this);
2447 void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
2448 pVisitor->Visit(this);
2451 void SmBlankNode::Accept(SmVisitor* pVisitor) {
2452 pVisitor->Visit(this);
2455 void SmErrorNode::Accept(SmVisitor* pVisitor) {
2456 pVisitor->Visit(this);
2459 void SmLineNode::Accept(SmVisitor* pVisitor) {
2460 pVisitor->Visit(this);
2463 void SmExpressionNode::Accept(SmVisitor* pVisitor) {
2464 pVisitor->Visit(this);
2467 void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
2468 pVisitor->Visit(this);
2471 void SmRootNode::Accept(SmVisitor* pVisitor) {
2472 pVisitor->Visit(this);
2475 void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
2476 pVisitor->Visit(this);
2479 void SmRectangleNode::Accept(SmVisitor* pVisitor) {
2480 pVisitor->Visit(this);
2483 void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
2484 pVisitor->Visit(this);
2487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */