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