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