1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
23 #include "tmpdevice.hxx"
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>
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)
48 SmNode::SmNode(SmNodeType eNodeType
, SmToken aNodeToken
)
49 : maNodeToken(std::move( aNodeToken
))
51 , meScaleMode( SmScaleMode::None
)
52 , meRectHorAlign( RectHorAlign::Left
)
53 , mnFlags( FontChangeMask::None
)
54 , mnAttributes( FontAttribute::None
)
55 , mbIsPhantom( false )
56 , mbIsSelected( false )
58 , mpParentNode( nullptr )
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
;
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
)
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
)
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
))
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
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 );
147 case FontSizeType::ABSOLUT
:
148 aFntSize
.setHeight( nHeight
);
151 case FontSizeType::PLUS
:
152 aFntSize
.AdjustHeight(nHeight
);
155 case FontSizeType::MINUS
:
156 aFntSize
.AdjustHeight( -nHeight
);
159 case FontSizeType::MULTIPLY
:
160 aFntSize
.setHeight( static_cast<tools::Long
>(Fraction(aFntSize
.Height()) * rSize
) );
163 case FontSizeType::DIVIDE
:
164 if (rSize
!= Fraction(0))
165 aFntSize
.setHeight( static_cast<tools::Long
>(Fraction(aFntSize
.Height()) / rSize
) );
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
)
187 ForEachNonNull(this, [&rSize
](SmNode
*pNode
){pNode
->SetSize(rSize
);});
191 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign
, bool bApplyToSubTree
)
193 meRectHorAlign
= eHorAlign
;
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
)
210 throw std::range_error("parser depth limit");
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)
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)
257 && nRow
== GetSelection().nStartPara
258 && nCol
>= GetSelection().nStartPos
&& nCol
<= GetSelection().nEndPos
)
262 size_t nNumSubNodes
= GetNumSubNodes();
263 for (size_t i
= 0; i
< nNumSubNodes
; ++i
)
265 const SmNode
*pNode
= GetSubNode(i
);
270 const SmNode
*pResult
= pNode
->FindTokenAt(nRow
, nCol
);
280 const SmNode
* SmNode::FindRectClosestTo(const Point
&rPoint
) const
282 tools::Long nDist
= LONG_MAX
;
283 const SmNode
*pResult
= nullptr;
289 size_t nNumSubNodes
= GetNumSubNodes();
290 for (size_t i
= 0; i
< nNumSubNodes
; ++i
)
292 const SmNode
*pNode
= GetSubNode(i
);
297 const SmNode
*pFound
= pNode
->FindRectClosestTo(rPoint
);
300 tools::Long nTmp
= pFound
->OrientedDist(rPoint
);
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
))
322 const SmNode
* SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx
) const
324 const SmNode
*pResult
= nullptr;
326 sal_Int32 nIdx
= GetAccessibleIndex();
329 GetAccessibleText( aTxt
); // get text if used in following 'if' statement
332 && nIdx
<= nAccIdx
&& nAccIdx
< nIdx
+ aTxt
.getLength())
336 size_t nNumSubNodes
= GetNumSubNodes();
337 for (size_t i
= 0; i
< nNumSubNodes
; ++i
)
339 const SmNode
*pNode
= GetSubNode(i
);
343 pResult
= pNode
->FindNodeWithAccessibleIndex(nAccIdx
);
353 SmStructureNode::~SmStructureNode()
355 ForEachNonNull(this, std::default_delete
<SmNode
>());
359 void SmStructureNode::ClearSubNodes()
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
);
369 maSubNodes
[0] = pFirst
.release();
371 maSubNodes
[1] = pSecond
.release();
373 maSubNodes
[2] = pThird
.release();
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
);
383 maSubNodes
[0] = pFirst
;
385 maSubNodes
[1] = pSecond
;
387 maSubNodes
[2] = pThird
;
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
);
399 maSubNodes
[0] = pFirst
.release();
401 maSubNodes
[2] = pSecond
.release();
403 maSubNodes
[1] = pThird
.release();
407 size_t nSize
= pThird
? 3 : (pSecond
? 2 : (pFirst
? 1 : 0));
408 maSubNodes
.resize( nSize
);
410 maSubNodes
[0] = pFirst
.release();
412 maSubNodes
[1] = pSecond
.release();
414 maSubNodes
[2] = pThird
.release();
419 void SmStructureNode::SetSubNodes(SmNodeArray
&& rNodeArray
)
421 maSubNodes
= std::move(rNodeArray
);
425 bool SmStructureNode::IsVisible() const
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
)
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
))
477 void SmStructureNode::SetSubNode(size_t nIndex
, SmNode
* pNode
)
479 size_t size
= maSubNodes
.size();
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
;
490 pNode
->SetParent(this);
493 bool SmVisibleNode::IsVisible() const
498 size_t SmVisibleNode::GetNumSubNodes() const
503 SmNode
* SmVisibleNode::GetSubNode(size_t /*nIndex*/)
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
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;
526 // arrange subnodes and get maximum width of them
527 tools::Long nMaxWidth
= 0,
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
)
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
);
550 aPos
.AdjustY(nDist
);
552 ExtendBy(rNodeRect
, nSize
> 1 ? RectCopyMBL::None
: RectCopyMBL::Arg
);
557 mnFormulaBaseline
= GetBaseline();
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
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
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());
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
622 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, "a",
623 GetFont().GetBorderWidth()));
624 // make sure that the rectangle occupies (almost) no space
626 SetItalicSpaces(0, 0);
630 // make distance depend on font size
631 tools::Long nDist
= (rFormat
.GetDistance(DIS_HORIZONTAL
) * GetFont().GetFontSize().Height()) / 100;
632 if (!IsUseExtraSpaces())
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
);
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();
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
;
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 /**************************************************************************/
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;
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);
752 pBody
->Arrange(rDev
, rFormat
);
756 lcl_GetHeightVerOffset(*pBody
, nHeight
, nVerOffset
);
757 nHeight
+= rFormat
.GetDistance(DIS_ROOT
)
758 * GetFont().GetFontSize().Height() / 100;
762 SAL_WARN("starmath", "negative height");
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
);
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
);
789 ExtendBy(*pExtra
, RectCopyMBL::This
, true);
792 /**************************************************************************/
795 void SmBinHorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
797 SmNode
*pLeft
= LeftOperand(),
799 *pRight
= RightOperand();
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();
813 if (o3tl::checked_multiply
<tools::Long
>(rOpRect
.GetWidth(), rFormat
.GetDistance(DIS_HORIZONTAL
), nMul
))
815 SAL_WARN("starmath", "integer overflow");
819 tools::Long nDist
= nMul
/ 100;
821 SmRect::operator = (*pLeft
);
824 aPos
= pOper
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
825 aPos
.AdjustX(nDist
);
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);
849 bool bIsTextmode
= rFormat
.IsTextmode();
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
);
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
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());
920 static const double eps
= 5.0 * DBL_EPSILON
;
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
;
930 fLambda
= (rPoint1
.Y() - rPoint2
.Y()) / static_cast<double>(rHeading2
.Y());
931 bRes
= fabs(rPoint1
.X() - (rPoint2
.X() + fLambda
* rHeading2
.X())) < eps
;
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());
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();
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())
962 rResult
= Point(rPoint1
.X() + static_cast<tools::Long
>(fLambda
* rHeading1
.X()),
963 rPoint1
.Y() + static_cast<tools::Long
>(fLambda
* rHeading1
.Y()));
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(),
982 nRectBottom
= GetBottom();
983 Point
aRightHdg (100, 0),
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
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
)
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
;
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
)
1021 nBottom
= nRectBottom
;
1025 // there has to be a point of intersection with the left border!
1026 GetLineIntersectionPoint(aPoint
,
1027 Point(nRectLeft
, nRectTop
), aDownHdg
,
1028 rDiagPoint
, aDiagHdg
);
1031 nBottom
= aPoint
.Y();
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
)
1048 // there has to be a point of intersection with the left border!
1049 GetLineIntersectionPoint(aPoint
,
1050 Point(nRectLeft
, nRectTop
), aDownHdg
,
1051 rDiagPoint
, aDiagHdg
);
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
;
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);
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);
1094 assert(pLine
&& pLine
->GetType() == SmNodeType::PolyLine
);
1096 SmPolyLineNode
*pOper
= static_cast<SmPolyLineNode
*>(pLine
);
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
1115 aPos
.setX( pLeft
->GetItalicRight() + nDelta
+ pRight
->GetItalicLeftSpace() );
1117 aPos
.setY( pLeft
->GetBottom() + nDelta
);
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,
1129 SmRect::operator = (*pLeft
);
1130 ExtendBy(*pRight
, RectCopyMBL::None
);
1133 // determine position and size of diagonal line
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();
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);
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
);
1183 // switch position of limits if we are in textmode
1184 if (rFormat
.IsTextmode() && (GetToken().nGroup
& TG::Limit
))
1186 { case CSUB
: eSubSup
= RSUB
; break;
1187 case CSUP
: eSubSup
= RSUP
; 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();
1207 //! be sure that CSUB, CSUP are handled before the other cases!
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();
1220 aPos
.AdjustY(nDelta
);
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
;
1233 aPos
.AdjustY( -nDelta
);
1238 * rFormat
.GetDistance(DIS_LOWERLIMIT
) / 100;
1239 aPos
= pSubSup
->GetRect().AlignTo(rBodyRect
, RectPos::Bottom
,
1240 RectHorAlign::Center
, RectVerAlign::Baseline
);
1241 aPos
.AdjustY(nDist
);
1246 * rFormat
.GetDistance(DIS_UPPERLIMIT
) / 100;
1247 aPos
= pSubSup
->GetRect().AlignTo(rBodyRect
, RectPos::Top
,
1248 RectHorAlign::Center
, RectVerAlign::Baseline
);
1249 aPos
.AdjustY( -nDist
);
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
)
1263 /**************************************************************************/
1265 void SmBraceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1267 SmNode
*pLeft
= OpeningBrace(),
1269 *pRight
= ClosingBrace();
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
;
1296 nBraceHeight
= pBody
->GetType() == SmNodeType::Bracebody
?
1297 static_cast<SmBracebodyNode
*>(pBody
)->GetBodyHeight()
1298 : pBody
->GetHeight();
1299 nBraceHeight
+= 2 * (nBraceHeight
* nPerc
/ 100);
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
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
;
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)
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
);
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
);
1419 /**************************************************************************/
1422 void SmVerticalBraceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1424 SmNode
*pBody
= Body(),
1426 *pScript
= Script();
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
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
);
1461 eRectPos
= RectPos::Bottom
;
1462 nDistScript
*= + rFormat
.GetDistance(DIS_LOWERLIMIT
);
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);
1488 if (pNode
->GetType() == SmNodeType::SubSup
)
1489 pNode
= static_cast<SmSubSupNode
*>(pNode
)->GetBody();
1491 OSL_ENSURE(pNode
, "Sm: NULL pointer!");
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
)
1506 if (!rFormat
.IsTextmode())
1508 // set minimum size ()
1509 nHeight
+= (nHeight
* 20) / 100;
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;
1524 void SmOperNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1526 SmNode
*pOper
= GetSubNode(0);
1527 SmNode
*pBody
= GetSubNode(1);
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(),
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);
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;
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(),
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
)
1612 eVerAlign
= RectVerAlign::AttributeLo
;
1615 eVerAlign
= RectVerAlign::AttributeMid
;
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
);
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;
1646 { GetFont() = rFormat
.GetFont( sal::static_int_cast
< sal_uInt16
>(nFnt
) );
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);
1661 switch (GetToken().eType
)
1663 pNode
->SetFontSize(maFontSize
, meSizeType
);
1668 pNode
->SetFont(GetFont());
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
1683 case TDVIPSNAMESCOL
:
1686 nc
= GetToken().cMathChar
.toUInt32(16);
1687 SetColor(Color(ColorTransparency
, nc
));
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
)
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
);
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();
1800 nHeight
= nFontHeight
/ 30;
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)
1828 SmTextNode::SmTextNode( const SmToken
&rNodeToken
, sal_uInt16 nFontDescP
)
1829 : SmVisibleNode(SmNodeType::Text
, rNodeToken
)
1830 , mnFontDesc(nFontDescP
)
1831 , mnSelectionStart(0)
1836 void SmTextNode::ChangeText(const OUString
&rText
) {
1838 GetToken().aText
= rText
;
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
;
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.
1932 /**************************************************************************/
1934 void SmMatrixNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
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
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
);
1966 for (size_t j
= 0; j
< mnNumCols
; ++j
)
1969 nX
+= aColWidth
[j
] + nHorDist
;
1972 SmRect::operator = (SmRect());
1973 for (size_t i
= 0; i
< mnNumRows
; ++i
)
1977 for (size_t j
= 0; j
< mnNumCols
; ++j
)
1979 SmNode
*pTmpNode
= GetSubNode(i
* mnNumCols
+ j
);
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
1994 { case RectHorAlign::Left
:
1995 aPos
.setX( aColLeft
[j
] );
1997 case RectHorAlign::Center
:
1998 aPos
.setX( rNodeRect
.GetLeft() + aColLeft
[j
]
2000 - rNodeRect
.GetItalicCenterX() );
2002 case RectHorAlign::Right
:
2003 aPos
.setX( aColLeft
[j
]
2004 + aColWidth
[j
] - rNodeRect
.GetItalicWidth() );
2010 pTmpNode
->MoveTo(aPos
);
2011 aLineRect
.ExtendBy(rNodeRect
, RectCopyMBL::Xor
);
2014 aPos
= aLineRect
.AlignTo(*this, RectPos::Bottom
, RectHorAlign::Center
, RectVerAlign::Baseline
);
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
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() );
2084 OSL_ENSURE(aFntSize
.Width() != 0, "Sm: ");
2086 //! however the result is a bit better with 'nHeight' as initial
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());
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
)
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")
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
);
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 );
2189 GetFont() = pSym
->GetFace();
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
)
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" );
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
;
2234 Attributes() |= FontAttribute::Italic
;
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;
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);
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: */