1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 #include "tmpdevice.hxx"
25 #include <visitors.hxx>
26 #include <tools/UnitConversion.hxx>
27 #include <vcl/metric.hxx>
28 #include <o3tl/safeint.hxx>
29 #include <osl/diagnose.h>
30 #include <basegfx/numeric/ftools.hxx>
31 #include <unicode/uchar.h>
32 #include <unicode/uscript.h>
37 void ForEachNonNull(SmNode
*pNode
, F
&& f
)
39 size_t nSize
= pNode
->GetNumSubNodes();
40 for (size_t i
= 0; i
< nSize
; ++i
)
42 SmNode
*pSubNode
= pNode
->GetSubNode(i
);
43 if (pSubNode
!= nullptr)
50 SmNode::SmNode(SmNodeType eNodeType
, SmToken aNodeToken
)
51 : maNodeToken(std::move( aNodeToken
))
53 , meScaleMode( SmScaleMode::None
)
54 , meRectHorAlign( RectHorAlign::Left
)
55 , mnFlags( FontChangeMask::None
)
56 , mnAttributes( FontAttribute::None
)
57 , mbIsPhantom( false )
58 , mbIsSelected( false )
60 , mpParentNode( nullptr )
68 const SmNode
* SmNode::GetLeftMost() const
69 // returns leftmost node of current subtree.
70 //! (this assumes the one with index 0 is always the leftmost subnode
71 //! for the current node).
73 const SmNode
*pNode
= GetNumSubNodes() > 0 ?
74 GetSubNode(0) : nullptr;
76 return pNode
? pNode
->GetLeftMost() : this;
80 void SmNode::SetPhantom(bool bIsPhantomP
)
82 if (! (Flags() & FontChangeMask::Phantom
))
83 mbIsPhantom
= bIsPhantomP
;
86 ForEachNonNull(this, [b
](SmNode
*pNode
){pNode
->SetPhantom(b
);});
90 void SmNode::SetColor(const Color
& rColor
)
92 if (! (Flags() & FontChangeMask::Color
))
93 GetFont().SetColor(rColor
);
95 ForEachNonNull(this, [&rColor
](SmNode
*pNode
){pNode
->SetColor(rColor
);});
99 void SmNode::SetAttribute(FontAttribute nAttrib
)
102 (nAttrib
== FontAttribute::Bold
&& !(Flags() & FontChangeMask::Bold
)) ||
103 (nAttrib
== FontAttribute::Italic
&& !(Flags() & FontChangeMask::Italic
))
106 mnAttributes
|= nAttrib
;
109 ForEachNonNull(this, [nAttrib
](SmNode
*pNode
){pNode
->SetAttribute(nAttrib
);});
113 void SmNode::ClearAttribute(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
->ClearAttribute(nAttrib
);});
127 void SmNode::SetFont(const SmFace
&rFace
)
129 if (!(Flags() & FontChangeMask::Face
))
131 ForEachNonNull(this, [&rFace
](SmNode
*pNode
){pNode
->SetFont(rFace
);});
135 void SmNode::SetFontSize(const Fraction
&rSize
, FontSizeType nType
)
136 //! 'rSize' is in units of pts
140 if (!(Flags() & FontChangeMask::Size
))
142 Fraction
aVal(conversionFract(o3tl::Length::pt
, SmO3tlLengthUnit()) * rSize
);
143 tools::Long nHeight
= static_cast<tools::Long
>(aVal
);
145 aFntSize
= GetFont().GetFontSize();
146 aFntSize
.setWidth( 0 );
149 case FontSizeType::ABSOLUT
:
150 aFntSize
.setHeight( nHeight
);
153 case FontSizeType::PLUS
:
154 aFntSize
.AdjustHeight(nHeight
);
157 case FontSizeType::MINUS
:
158 aFntSize
.AdjustHeight( -nHeight
);
161 case FontSizeType::MULTIPLY
:
162 aFntSize
.setHeight( static_cast<tools::Long
>(Fraction(aFntSize
.Height()) * rSize
) );
165 case FontSizeType::DIVIDE
:
166 if (rSize
!= Fraction(0))
167 aFntSize
.setHeight( static_cast<tools::Long
>(Fraction(aFntSize
.Height()) / rSize
) );
173 // check the requested size against maximum value
174 const int nMaxVal
= o3tl::convert(128, o3tl::Length::pt
, SmO3tlLengthUnit());
175 if (aFntSize
.Height() > nMaxVal
)
176 aFntSize
.setHeight( nMaxVal
);
178 GetFont().SetSize(aFntSize
);
181 ForEachNonNull(this, [&rSize
, &nType
](SmNode
*pNode
){pNode
->SetFontSize(rSize
, nType
);});
185 void SmNode::SetSize(const Fraction
&rSize
)
189 ForEachNonNull(this, [&rSize
](SmNode
*pNode
){pNode
->SetSize(rSize
);});
193 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign
, bool bApplyToSubTree
)
195 meRectHorAlign
= eHorAlign
;
198 ForEachNonNull(this, [eHorAlign
](SmNode
*pNode
){pNode
->SetRectHorAlign(eHorAlign
);});
202 void SmNode::PrepareAttributes()
204 GetFont().SetWeight((Attributes() & FontAttribute::Bold
) ? WEIGHT_BOLD
: WEIGHT_NORMAL
);
205 GetFont().SetItalic((Attributes() & FontAttribute::Italic
) ? ITALIC_NORMAL
: ITALIC_NONE
);
209 void SmNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
212 throw std::range_error("parser depth limit");
215 mnFlags
= FontChangeMask::None
;
216 mnAttributes
= FontAttribute::None
;
218 switch (rFormat
.GetHorAlign())
219 { case SmHorAlign::Left
: meRectHorAlign
= RectHorAlign::Left
; break;
220 case SmHorAlign::Center
: meRectHorAlign
= RectHorAlign::Center
; break;
221 case SmHorAlign::Right
: meRectHorAlign
= RectHorAlign::Right
; break;
224 GetFont() = rFormat
.GetFont(FNT_MATH
);
225 OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE
,
226 "unexpected CharSet" );
227 GetFont().SetWeight(WEIGHT_NORMAL
);
228 GetFont().SetItalic(ITALIC_NONE
);
230 ForEachNonNull(this, [&rFormat
, &rDocShell
, nDepth
](SmNode
*pNode
){pNode
->Prepare(rFormat
, rDocShell
, nDepth
+ 1);});
233 void SmNode::Move(const Point
& rVector
)
235 if (rVector
.X() == 0 && rVector
.Y() == 0)
238 SmRect::Move(rVector
);
240 ForEachNonNull(this, [&rVector
](SmNode
*pNode
){pNode
->Move(rVector
);});
243 void SmNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong
/*nWidth*/)
248 void SmNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong
/*nHeight*/)
253 const SmNode
* SmNode::FindTokenAt(sal_uInt16 nRow
, sal_uInt16 nCol
) const
254 // returns (first) ** visible ** (sub)node with the tokens text at
255 // position 'nRow', 'nCol'.
256 //! (there should be exactly one such node if any)
259 && nRow
== GetSelection().nStartPara
260 && nCol
>= GetSelection().nStartPos
&& nCol
<= GetSelection().nEndPos
)
264 size_t nNumSubNodes
= GetNumSubNodes();
265 for (size_t i
= 0; i
< nNumSubNodes
; ++i
)
267 const SmNode
*pNode
= GetSubNode(i
);
272 const SmNode
*pResult
= pNode
->FindTokenAt(nRow
, nCol
);
282 const SmNode
* SmNode::FindRectClosestTo(const Point
&rPoint
) const
284 tools::Long nDist
= LONG_MAX
;
285 const SmNode
*pResult
= nullptr;
291 size_t nNumSubNodes
= GetNumSubNodes();
292 for (size_t i
= 0; i
< nNumSubNodes
; ++i
)
294 const SmNode
*pNode
= GetSubNode(i
);
299 const SmNode
*pFound
= pNode
->FindRectClosestTo(rPoint
);
302 tools::Long nTmp
= pFound
->OrientedDist(rPoint
);
308 // quit immediately if 'rPoint' is inside the *should not
309 // overlap with other rectangles* part.
310 // This (partly) serves for getting the attributes in eg
311 // "bar overstrike a".
312 // ('nDist < 0' is used as *quick shot* to avoid evaluation of
313 // the following expression, where the result is already determined)
314 if (nDist
< 0 && pFound
->IsInsideRect(rPoint
))
324 const SmNode
* SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx
) const
326 const SmNode
*pResult
= nullptr;
328 sal_Int32 nIdx
= GetAccessibleIndex();
331 GetAccessibleText( aTxt
); // get text if used in following 'if' statement
334 && nIdx
<= nAccIdx
&& nAccIdx
< nIdx
+ aTxt
.getLength())
338 size_t nNumSubNodes
= GetNumSubNodes();
339 for (size_t i
= 0; i
< nNumSubNodes
; ++i
)
341 const SmNode
*pNode
= GetSubNode(i
);
345 pResult
= pNode
->FindNodeWithAccessibleIndex(nAccIdx
);
355 SmStructureNode::~SmStructureNode()
357 ForEachNonNull(this, std::default_delete
<SmNode
>());
361 void SmStructureNode::ClearSubNodes()
366 void SmStructureNode::SetSubNodes(std::unique_ptr
<SmNode
> pFirst
, std::unique_ptr
<SmNode
> pSecond
, std::unique_ptr
<SmNode
> pThird
)
368 size_t nSize
= pThird
? 3 : (pSecond
? 2 : (pFirst
? 1 : 0));
369 maSubNodes
.resize( nSize
);
371 maSubNodes
[0] = pFirst
.release();
373 maSubNodes
[1] = pSecond
.release();
375 maSubNodes
[2] = pThird
.release();
380 void SmStructureNode::SetSubNodes(SmNode
* pFirst
, SmNode
* pSecond
, SmNode
* pThird
)
382 size_t nSize
= pThird
? 3 : (pSecond
? 2 : (pFirst
? 1 : 0));
383 maSubNodes
.resize( nSize
);
385 maSubNodes
[0] = pFirst
;
387 maSubNodes
[1] = pSecond
;
389 maSubNodes
[2] = pThird
;
394 void SmStructureNode::SetSubNodesBinMo(std::unique_ptr
<SmNode
> pFirst
, std::unique_ptr
<SmNode
> pSecond
, std::unique_ptr
<SmNode
> pThird
)
396 if(GetType()==SmNodeType::BinDiagonal
)
398 size_t nSize
= pSecond
? 3 : (pThird
? 2 : (pFirst
? 1 : 0));
399 maSubNodes
.resize( nSize
);
401 maSubNodes
[0] = pFirst
.release();
403 maSubNodes
[2] = pSecond
.release();
405 maSubNodes
[1] = pThird
.release();
409 size_t nSize
= pThird
? 3 : (pSecond
? 2 : (pFirst
? 1 : 0));
410 maSubNodes
.resize( nSize
);
412 maSubNodes
[0] = pFirst
.release();
414 maSubNodes
[1] = pSecond
.release();
416 maSubNodes
[2] = pThird
.release();
421 void SmStructureNode::SetSubNodes(SmNodeArray
&& rNodeArray
)
423 maSubNodes
= std::move(rNodeArray
);
427 bool SmStructureNode::IsVisible() const
432 size_t SmStructureNode::GetNumSubNodes() const
434 return maSubNodes
.size();
437 SmNode
* SmStructureNode::GetSubNode(size_t nIndex
)
439 return maSubNodes
[nIndex
];
442 SmNode
* SmStructureNode::GetSubNodeBinMo(size_t nIndex
) const
444 if(GetType()==SmNodeType::BinDiagonal
)
451 return maSubNodes
[nIndex
];
454 void SmStructureNode::GetAccessibleText( OUStringBuffer
&rText
) const
456 ForEachNonNull(const_cast<SmStructureNode
*>(this),
457 [&rText
](SmNode
*pNode
)
459 if (pNode
->IsVisible())
460 pNode
->SetAccessibleIndex(rText
.getLength());
461 pNode
->GetAccessibleText( rText
);
465 void SmStructureNode::ClaimPaternity()
467 ForEachNonNull(this, [this](SmNode
*pNode
){pNode
->SetParent(this);});
470 int SmStructureNode::IndexOfSubNode(SmNode
const * pSubNode
)
472 size_t nSize
= GetNumSubNodes();
473 for (size_t i
= 0; i
< nSize
; i
++)
474 if (pSubNode
== GetSubNode(i
))
479 void SmStructureNode::SetSubNode(size_t nIndex
, SmNode
* pNode
)
481 size_t size
= maSubNodes
.size();
484 //Resize subnodes array
485 maSubNodes
.resize(nIndex
+ 1);
486 //Set new slots to NULL except at nIndex
487 for (size_t i
= size
; i
< nIndex
; i
++)
488 maSubNodes
[i
] = nullptr;
490 maSubNodes
[nIndex
] = pNode
;
492 pNode
->SetParent(this);
495 bool SmVisibleNode::IsVisible() const
500 size_t SmVisibleNode::GetNumSubNodes() const
505 SmNode
* SmVisibleNode::GetSubNode(size_t /*nIndex*/)
510 void SmGraphicNode::GetAccessibleText( OUStringBuffer
&rText
) const
512 rText
.append(GetToken().aText
);
515 void SmTableNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
516 // arranges all subnodes in one column
519 size_t nSize
= GetNumSubNodes();
521 // make distance depend on font size
522 tools::Long nDist
= +(rFormat
.GetDistance(DIS_VERTICAL
)
523 * GetFont().GetFontSize().Height()) / 100;
528 // arrange subnodes and get maximum width of them
529 tools::Long nMaxWidth
= 0,
531 for (size_t i
= 0; i
< nSize
; ++i
)
533 if (nullptr != (pNode
= GetSubNode(i
)))
534 { pNode
->Arrange(rDev
, rFormat
);
535 if ((nTmp
= pNode
->GetItalicWidth()) > nMaxWidth
)
541 SmRect::operator = (SmRect(nMaxWidth
, 1));
542 for (size_t i
= 0; i
< nSize
; ++i
)
544 if (nullptr != (pNode
= GetSubNode(i
)))
545 { const SmRect
&rNodeRect
= pNode
->GetRect();
546 const SmNode
*pCoNode
= pNode
->GetLeftMost();
547 RectHorAlign eHorAlign
= pCoNode
->GetRectHorAlign();
549 aPos
= rNodeRect
.AlignTo(*this, RectPos::Bottom
,
550 eHorAlign
, RectVerAlign::Baseline
);
552 aPos
.AdjustY(nDist
);
554 ExtendBy(rNodeRect
, nSize
> 1 ? RectCopyMBL::None
: RectCopyMBL::Arg
);
559 mnFormulaBaseline
= GetBaseline();
562 SmTmpDevice
aTmpDev (rDev
, true);
563 aTmpDev
.SetFont(GetFont());
565 SmRect
aRect(aTmpDev
, &rFormat
, u
"a"_ustr
, GetFont().GetBorderWidth());
566 mnFormulaBaseline
= GetAlignM();
567 // move from middle position by constant - distance
568 // between middle and baseline for single letter
569 mnFormulaBaseline
+= aRect
.GetBaseline() - aRect
.GetAlignM();
573 const SmNode
* SmTableNode::GetLeftMost() const
579 tools::Long
SmTableNode::GetFormulaBaseline() const
581 return mnFormulaBaseline
;
585 /**************************************************************************/
588 void SmLineNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
590 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
592 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
593 // to the rest of the formula compared to the 'FNT_MATH' font.
594 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
595 Flags() |= FontChangeMask::Face
;
599 /**************************************************************************/
602 void SmLineNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
603 // arranges all subnodes in one row with some extra space between
606 size_t nSize
= GetNumSubNodes();
607 for (size_t i
= 0; i
< nSize
; ++i
)
609 if (nullptr != (pNode
= GetSubNode(i
)))
610 pNode
->Arrange(rDev
, rFormat
);
613 SmTmpDevice
aTmpDev (rDev
, true);
614 aTmpDev
.SetFont(GetFont());
618 // provide an empty rectangle with alignment parameters for the "current"
619 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
620 // same sub-/supscript positions.)
621 //! be sure to use a character that has explicitly defined HiAttribut
622 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
624 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, u
"a"_ustr
,
625 GetFont().GetBorderWidth()));
626 // make sure that the rectangle occupies (almost) no space
628 SetItalicSpaces(0, 0);
632 // make distance depend on font size
633 tools::Long nDist
= (rFormat
.GetDistance(DIS_HORIZONTAL
) * GetFont().GetFontSize().Height()) / 100;
634 if (!IsUseExtraSpaces())
638 // copy the first node into LineNode and extend by the others
639 if (nullptr != (pNode
= GetSubNode(0)))
640 SmRect::operator = (pNode
->GetRect());
642 for (size_t i
= 1; i
< nSize
; ++i
)
644 if (nullptr != (pNode
= GetSubNode(i
)))
646 aPos
= pNode
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
648 // add horizontal space to the left for each but the first sub node
649 aPos
.AdjustX(nDist
);
652 ExtendBy( *pNode
, RectCopyMBL::Xor
);
658 /**************************************************************************/
661 void SmExpressionNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
662 // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
664 SmLineNode::Arrange(rDev
, rFormat
);
666 // copy alignment of leftmost subnode if any
667 const SmNode
*pNode
= GetLeftMost();
669 SetRectHorAlign(pNode
->GetRectHorAlign(), false);
673 /**************************************************************************/
676 void SmUnHorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
678 bool bIsPostfix
= GetToken().eType
== TFACT
;
680 SmNode
*pNode0
= GetSubNode(0),
681 *pNode1
= GetSubNode(1);
682 SmNode
*pOper
= bIsPostfix
? pNode1
: pNode0
,
683 *pBody
= bIsPostfix
? pNode0
: pNode1
;
687 pOper
->SetSize(Fraction (rFormat
.GetRelSize(SIZ_OPERATOR
), 100));
688 pOper
->Arrange(rDev
, rFormat
);
689 pBody
->Arrange(rDev
, rFormat
);
691 tools::Long nDist
= (pOper
->GetRect().GetWidth() * rFormat
.GetDistance(DIS_HORIZONTAL
)) / 100;
693 SmRect::operator = (*pNode0
);
695 Point aPos
= pNode1
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
696 aPos
.AdjustX(nDist
);
697 pNode1
->MoveTo(aPos
);
698 ExtendBy(*pNode1
, RectCopyMBL::Xor
);
702 /**************************************************************************/
706 void lcl_GetHeightVerOffset(const SmRect
&rRect
,
707 tools::Long
&rHeight
, tools::Long
&rVerOffset
)
708 // calculate height and vertical offset of root sign suitable for 'rRect'
710 rVerOffset
= (rRect
.GetBottom() - rRect
.GetAlignB()) / 2;
711 rHeight
= rRect
.GetHeight() - rVerOffset
;
713 OSL_ENSURE(rHeight
>= 0, "Sm : Ooops...");
714 OSL_ENSURE(rVerOffset
>= 0, "Sm : Ooops...");
718 Point
lcl_GetExtraPos(const SmRect
&rRootSymbol
,
719 const SmRect
&rExtra
)
721 const Size
&rSymSize
= rRootSymbol
.GetSize();
723 Point aPos
= rRootSymbol
.GetTopLeft()
724 + Point((rSymSize
.Width() * 70) / 100,
725 (rSymSize
.Height() * 52) / 100);
727 // from this calculate topleft edge of 'rExtra'
728 aPos
.AdjustX( -(rExtra
.GetWidth() + rExtra
.GetItalicRightSpace()) );
729 aPos
.AdjustY( -(rExtra
.GetHeight()) );
730 // if there's enough space move a bit less to the right
731 // examples: "nroot i a", "nroot j a"
732 // (it looks better if we don't use italic-spaces here)
733 tools::Long nX
= rRootSymbol
.GetLeft() + (rSymSize
.Width() * 30) / 100;
742 void SmRootNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
744 //! pExtra needs to have the smaller index than pRootSym in order to
745 //! not to get the root symbol but the pExtra when clicking on it in the
746 //! GraphicWindow. (That is because of the simplicity of the algorithm
747 //! that finds the node corresponding to a mouseclick in the window.)
748 SmNode
*pExtra
= GetSubNode(0),
749 *pRootSym
= GetSubNode(1),
750 *pBody
= GetSubNode(2);
754 pBody
->Arrange(rDev
, rFormat
);
758 lcl_GetHeightVerOffset(*pBody
, nHeight
, nVerOffset
);
759 nHeight
+= rFormat
.GetDistance(DIS_ROOT
)
760 * GetFont().GetFontSize().Height() / 100;
764 SAL_WARN("starmath", "negative height");
768 // font specialist advised to change the width first
769 pRootSym
->AdaptToY(rDev
, nHeight
);
770 pRootSym
->AdaptToX(rDev
, pBody
->GetItalicWidth());
772 pRootSym
->Arrange(rDev
, rFormat
);
774 // Set the top and bottom of the root symbol to the top and bottom of its glyph bounding rect,
775 // to get accurate position of the root symbol.
776 SmRect rRootSymRect
= pRootSym
->AsGlyphRect();
777 pRootSym
->SetTop(rRootSymRect
.GetTop());
778 pRootSym
->SetBottom(rRootSymRect
.GetBottom());
780 Point aPos
= pRootSym
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, RectVerAlign::Baseline
);
781 //! override calculated vertical position
782 aPos
.setY( pRootSym
->GetTop() + pBody
->GetBottom() - pRootSym
->GetBottom() );
783 aPos
.AdjustY( -nVerOffset
);
784 pRootSym
->MoveTo(aPos
);
787 { pExtra
->SetSize(Fraction(rFormat
.GetRelSize(SIZ_INDEX
), 100));
788 pExtra
->Arrange(rDev
, rFormat
);
790 aPos
= lcl_GetExtraPos(*pRootSym
, *pExtra
);
791 pExtra
->MoveTo(aPos
);
794 SmRect::operator = (*pBody
);
795 ExtendBy(*pRootSym
, RectCopyMBL::This
);
797 ExtendBy(*pExtra
, RectCopyMBL::This
, true);
800 /**************************************************************************/
803 void SmBinHorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
805 SmNode
*pLeft
= LeftOperand(),
807 *pRight
= RightOperand();
812 pOper
->SetSize(Fraction (rFormat
.GetRelSize(SIZ_OPERATOR
), 100));
814 pLeft
->Arrange(rDev
, rFormat
);
815 pOper
->Arrange(rDev
, rFormat
);
816 pRight
->Arrange(rDev
, rFormat
);
818 const SmRect
&rOpRect
= pOper
->GetRect();
821 if (o3tl::checked_multiply
<tools::Long
>(rOpRect
.GetWidth(), rFormat
.GetDistance(DIS_HORIZONTAL
), nMul
))
823 SAL_WARN("starmath", "integer overflow");
827 tools::Long nDist
= nMul
/ 100;
829 SmRect::operator = (*pLeft
);
832 aPos
= pOper
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
833 aPos
.AdjustX(nDist
);
835 ExtendBy(*pOper
, RectCopyMBL::Xor
);
837 aPos
= pRight
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
838 aPos
.AdjustX(nDist
);
840 pRight
->MoveTo(aPos
);
841 ExtendBy(*pRight
, RectCopyMBL::Xor
);
845 /**************************************************************************/
848 void SmBinVerNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
850 SmNode
*pNum
= GetSubNode(0),
851 *pLine
= GetSubNode(1),
852 *pDenom
= GetSubNode(2);
857 bool bIsTextmode
= rFormat
.IsTextmode();
860 Fraction
aFraction(rFormat
.GetRelSize(SIZ_INDEX
), 100);
861 pNum
->SetSize(aFraction
);
862 pLine
->SetSize(aFraction
);
863 pDenom
->SetSize(aFraction
);
866 pNum
->Arrange(rDev
, rFormat
);
867 pDenom
->Arrange(rDev
, rFormat
);
869 tools::Long nFontHeight
= GetFont().GetFontSize().Height(),
870 nExtLen
= nFontHeight
* rFormat
.GetDistance(DIS_FRACTION
) / 100,
871 nThick
= nFontHeight
* rFormat
.GetDistance(DIS_STROKEWIDTH
) / 100,
872 nWidth
= std::max(pNum
->GetItalicWidth(), pDenom
->GetItalicWidth()),
873 nNumDist
= bIsTextmode
? 0 :
874 nFontHeight
* rFormat
.GetDistance(DIS_NUMERATOR
) / 100,
875 nDenomDist
= bIsTextmode
? 0 :
876 nFontHeight
* rFormat
.GetDistance(DIS_DENOMINATOR
) / 100;
878 // font specialist advised to change the width first
879 pLine
->AdaptToY(rDev
, nThick
);
880 pLine
->AdaptToX(rDev
, nWidth
+ 2 * nExtLen
);
881 pLine
->Arrange(rDev
, rFormat
);
883 // get horizontal alignment for numerator
884 const SmNode
*pLM
= pNum
->GetLeftMost();
885 RectHorAlign eHorAlign
= pLM
->GetRectHorAlign();
887 // move numerator to its position
888 Point aPos
= pNum
->AlignTo(*pLine
, RectPos::Top
, eHorAlign
, RectVerAlign::Baseline
);
889 aPos
.AdjustY( -nNumDist
);
892 // get horizontal alignment for denominator
893 pLM
= pDenom
->GetLeftMost();
894 eHorAlign
= pLM
->GetRectHorAlign();
896 // move denominator to its position
897 aPos
= pDenom
->AlignTo(*pLine
, RectPos::Bottom
, eHorAlign
, RectVerAlign::Baseline
);
898 aPos
.AdjustY(nDenomDist
);
899 pDenom
->MoveTo(aPos
);
901 SmRect::operator = (*pNum
);
902 ExtendBy(*pDenom
, RectCopyMBL::None
).ExtendBy(*pLine
, RectCopyMBL::None
, pLine
->GetCenterY());
905 const SmNode
* SmBinVerNode::GetLeftMost() const
913 /// @return value of the determinant formed by the two points
914 double Det(const Point
&rHeading1
, const Point
&rHeading2
)
916 return rHeading1
.X() * rHeading2
.Y() - rHeading1
.Y() * rHeading2
.X();
920 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
921 /// and has the direction vector 'rHeading2'
922 bool IsPointInLine(const Point
&rPoint1
,
923 const Point
&rPoint2
, const Point
&rHeading2
)
925 assert(rHeading2
!= Point());
928 static const double eps
= 5.0 * DBL_EPSILON
;
931 if (std::abs(rHeading2
.X()) > std::abs(rHeading2
.Y()))
933 fLambda
= (rPoint1
.X() - rPoint2
.X()) / static_cast<double>(rHeading2
.X());
934 bRes
= fabs(rPoint1
.Y() - (rPoint2
.Y() + fLambda
* rHeading2
.Y())) < eps
;
938 fLambda
= (rPoint1
.Y() - rPoint2
.Y()) / static_cast<double>(rHeading2
.Y());
939 bRes
= fabs(rPoint1
.X() - (rPoint2
.X() + fLambda
* rHeading2
.X())) < eps
;
946 sal_uInt16
GetLineIntersectionPoint(Point
&rResult
,
947 const Point
& rPoint1
, const Point
&rHeading1
,
948 const Point
& rPoint2
, const Point
&rHeading2
)
950 assert(rHeading1
!= Point());
951 assert(rHeading2
!= Point());
954 static const double eps
= 5.0 * DBL_EPSILON
;
956 // are the direction vectors linearly dependent?
957 double fDet
= Det(rHeading1
, rHeading2
);
958 if (fabs(fDet
) < eps
)
960 nRes
= IsPointInLine(rPoint1
, rPoint2
, rHeading2
) ? USHRT_MAX
: 0;
961 rResult
= nRes
? rPoint1
: Point();
965 // here we do not pay attention to the computational accuracy
966 // (that would be more complicated and is not really worth it in this case)
967 double fLambda
= ( (rPoint1
.Y() - rPoint2
.Y()) * rHeading2
.X()
968 - (rPoint1
.X() - rPoint2
.X()) * rHeading2
.Y())
970 rResult
= Point(rPoint1
.X() + static_cast<tools::Long
>(fLambda
* rHeading1
.X()),
971 rPoint1
.Y() + static_cast<tools::Long
>(fLambda
* rHeading1
.Y()));
980 /// @return position and size of the diagonal line
981 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
982 void SmBinDiagonalNode::GetOperPosSize(Point
&rPos
, Size
&rSize
,
983 const Point
&rDiagPoint
, double fAngleDeg
) const
986 double fAngleRad
= basegfx::deg2rad(fAngleDeg
);
987 tools::Long nRectLeft
= GetItalicLeft(),
988 nRectRight
= GetItalicRight(),
990 nRectBottom
= GetBottom();
991 Point
aRightHdg (100, 0),
993 aDiagHdg ( static_cast<tools::Long
>(100.0 * cos(fAngleRad
)),
994 static_cast<tools::Long
>(-100.0 * sin(fAngleRad
)) );
996 tools::Long nLeft
, nRight
, nTop
, nBottom
; // margins of the rectangle for the diagonal
1000 // determine top right corner
1001 GetLineIntersectionPoint(aPoint
,
1002 Point(nRectLeft
, nRectTop
), aRightHdg
,
1003 rDiagPoint
, aDiagHdg
);
1004 // is there a point of intersection with the top border?
1005 if (aPoint
.X() <= nRectRight
)
1007 nRight
= aPoint
.X();
1012 // there has to be a point of intersection with the right border!
1013 GetLineIntersectionPoint(aPoint
,
1014 Point(nRectRight
, nRectTop
), aDownHdg
,
1015 rDiagPoint
, aDiagHdg
);
1017 nRight
= nRectRight
;
1021 // determine bottom left corner
1022 GetLineIntersectionPoint(aPoint
,
1023 Point(nRectLeft
, nRectBottom
), aRightHdg
,
1024 rDiagPoint
, aDiagHdg
);
1025 // is there a point of intersection with the bottom border?
1026 if (aPoint
.X() >= nRectLeft
)
1029 nBottom
= nRectBottom
;
1033 // there has to be a point of intersection with the left border!
1034 GetLineIntersectionPoint(aPoint
,
1035 Point(nRectLeft
, nRectTop
), aDownHdg
,
1036 rDiagPoint
, aDiagHdg
);
1039 nBottom
= aPoint
.Y();
1044 // determine top left corner
1045 GetLineIntersectionPoint(aPoint
,
1046 Point(nRectLeft
, nRectTop
), aRightHdg
,
1047 rDiagPoint
, aDiagHdg
);
1048 // is there a point of intersection with the top border?
1049 if (aPoint
.X() >= nRectLeft
)
1056 // there has to be a point of intersection with the left border!
1057 GetLineIntersectionPoint(aPoint
,
1058 Point(nRectLeft
, nRectTop
), aDownHdg
,
1059 rDiagPoint
, aDiagHdg
);
1065 // determine bottom right corner
1066 GetLineIntersectionPoint(aPoint
,
1067 Point(nRectLeft
, nRectBottom
), aRightHdg
,
1068 rDiagPoint
, aDiagHdg
);
1069 // is there a point of intersection with the bottom border?
1070 if (aPoint
.X() <= nRectRight
)
1072 nRight
= aPoint
.X();
1073 nBottom
= nRectBottom
;
1077 // there has to be a point of intersection with the right border!
1078 GetLineIntersectionPoint(aPoint
,
1079 Point(nRectRight
, nRectTop
), aDownHdg
,
1080 rDiagPoint
, aDiagHdg
);
1082 nRight
= nRectRight
;
1083 nBottom
= aPoint
.Y();
1087 rSize
= Size(nRight
- nLeft
+ 1, nBottom
- nTop
+ 1);
1093 void SmBinDiagonalNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1095 // Both arguments have to get into the SubNodes before the Operator so that clicking
1096 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1097 SmNode
*pLeft
= GetSubNode(0),
1098 *pRight
= GetSubNode(1),
1099 *pLine
= GetSubNode(2);
1102 assert(pLine
&& pLine
->GetType() == SmNodeType::PolyLine
);
1104 SmPolyLineNode
*pOper
= static_cast<SmPolyLineNode
*>(pLine
);
1107 //! some routines being called extract some info from the OutputDevice's
1108 //! font (eg the space to be used for borders OR the font name(!!)).
1109 //! Thus the font should reflect the needs and has to be set!
1110 SmTmpDevice
aTmpDev (rDev
, true);
1111 aTmpDev
.SetFont(GetFont());
1113 pLeft
->Arrange(aTmpDev
, rFormat
);
1114 pRight
->Arrange(aTmpDev
, rFormat
);
1116 // determine implicitly the values (incl. the margin) of the diagonal line
1117 pOper
->Arrange(aTmpDev
, rFormat
);
1119 tools::Long nDelta
= pOper
->GetWidth() * 8 / 10;
1121 // determine TopLeft position from the right argument
1123 aPos
.setX( pLeft
->GetItalicRight() + nDelta
+ pRight
->GetItalicLeftSpace() );
1125 aPos
.setY( pLeft
->GetBottom() + nDelta
);
1127 aPos
.setY( pLeft
->GetTop() - nDelta
- pRight
->GetHeight() );
1129 pRight
->MoveTo(aPos
);
1131 // determine new baseline
1132 tools::Long nTmpBaseline
= IsAscending() ? (pLeft
->GetBottom() + pRight
->GetTop()) / 2
1133 : (pLeft
->GetTop() + pRight
->GetBottom()) / 2;
1134 Point
aLogCenter ((pLeft
->GetItalicRight() + pRight
->GetItalicLeft()) / 2,
1137 SmRect::operator = (*pLeft
);
1138 ExtendBy(*pRight
, RectCopyMBL::None
);
1141 // determine position and size of diagonal line
1143 GetOperPosSize(aPos
, aTmpSize
, aLogCenter
, IsAscending() ? 60.0 : -60.0);
1145 // font specialist advised to change the width first
1146 pOper
->AdaptToY(aTmpDev
, aTmpSize
.Height());
1147 pOper
->AdaptToX(aTmpDev
, aTmpSize
.Width());
1148 // and make it active
1149 pOper
->Arrange(aTmpDev
, rFormat
);
1151 pOper
->MoveTo(aPos
);
1153 ExtendBy(*pOper
, RectCopyMBL::None
, nTmpBaseline
);
1157 /**************************************************************************/
1160 void SmSubSupNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1162 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES
,
1163 "Sm: wrong number of subnodes");
1165 SmNode
*pBody
= GetBody();
1168 tools::Long nOrigHeight
= pBody
->GetFont().GetFontSize().Height();
1170 pBody
->Arrange(rDev
, rFormat
);
1172 const SmRect
&rBodyRect
= pBody
->GetRect();
1173 SmRect::operator = (rBodyRect
);
1175 // line that separates sub- and supscript rectangles
1176 tools::Long nDelimLine
= SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1179 tools::Long nDelta
, nDist
;
1181 // iterate over all possible sub-/supscripts
1182 SmRect
aTmpRect (rBodyRect
);
1183 for (int i
= 0; i
< SUBSUP_NUM_ENTRIES
; i
++)
1185 SmSubSup eSubSup
= static_cast<SmSubSup
>(i
);
1186 SmNode
*pSubSup
= GetSubSup(eSubSup
);
1191 // switch position of limits if we are in textmode
1192 if (rFormat
.IsTextmode() && (GetToken().nGroup
& TG::Limit
))
1194 { case CSUB
: eSubSup
= RSUB
; break;
1195 case CSUP
: eSubSup
= RSUP
; break;
1200 // prevent sub-/supscripts from diminishing in size
1201 // (as would be in "a_{1_{2_{3_4}}}")
1202 if (GetFont().GetFontSize().Height() > rFormat
.GetBaseSize().Height() / 3)
1204 sal_uInt16 nIndex
= (eSubSup
== CSUB
|| eSubSup
== CSUP
) ?
1205 SIZ_LIMITS
: SIZ_INDEX
;
1206 Fraction
aFraction ( rFormat
.GetRelSize(nIndex
), 100 );
1207 pSubSup
->SetSize(aFraction
);
1210 pSubSup
->Arrange(rDev
, rFormat
);
1212 bool bIsTextmode
= rFormat
.IsTextmode();
1215 //! be sure that CSUB, CSUP are handled before the other cases!
1221 * rFormat
.GetDistance(DIS_SUBSCRIPT
) / 100;
1222 aPos
= pSubSup
->GetRect().AlignTo(aTmpRect
,
1223 eSubSup
== LSUB
? RectPos::Left
: RectPos::Right
,
1224 RectHorAlign::Center
, RectVerAlign::Bottom
);
1225 aPos
.AdjustY(nDist
);
1226 nDelta
= nDelimLine
- aPos
.Y();
1228 aPos
.AdjustY(nDelta
);
1234 * rFormat
.GetDistance(DIS_SUPERSCRIPT
) / 100;
1235 aPos
= pSubSup
->GetRect().AlignTo(aTmpRect
,
1236 eSubSup
== LSUP
? RectPos::Left
: RectPos::Right
,
1237 RectHorAlign::Center
, RectVerAlign::Top
);
1238 aPos
.AdjustY( -nDist
);
1239 nDelta
= aPos
.Y() + pSubSup
->GetHeight() - nDelimLine
;
1241 aPos
.AdjustY( -nDelta
);
1246 * rFormat
.GetDistance(DIS_LOWERLIMIT
) / 100;
1247 aPos
= pSubSup
->GetRect().AlignTo(rBodyRect
, RectPos::Bottom
,
1248 RectHorAlign::Center
, RectVerAlign::Baseline
);
1249 aPos
.AdjustY(nDist
);
1254 * rFormat
.GetDistance(DIS_UPPERLIMIT
) / 100;
1255 aPos
= pSubSup
->GetRect().AlignTo(rBodyRect
, RectPos::Top
,
1256 RectHorAlign::Center
, RectVerAlign::Baseline
);
1257 aPos
.AdjustY( -nDist
);
1261 pSubSup
->MoveTo(aPos
);
1262 ExtendBy(*pSubSup
, RectCopyMBL::This
, true);
1264 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1265 // will be aligned to
1266 if (eSubSup
== CSUB
|| eSubSup
== CSUP
)
1271 /**************************************************************************/
1273 void SmBraceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1275 SmNode
*pLeft
= OpeningBrace(),
1277 *pRight
= ClosingBrace();
1282 pBody
->Arrange(rDev
, rFormat
);
1284 bool bIsScaleNormal
= rFormat
.IsScaleNormalBrackets(),
1285 bScale
= pBody
->GetHeight() > 0 &&
1286 (GetScaleMode() == SmScaleMode::Height
|| bIsScaleNormal
),
1287 bIsABS
= GetToken().eType
== TABS
;
1289 tools::Long nFaceHeight
= GetFont().GetFontSize().Height();
1291 // determine oversize in %
1292 sal_uInt16 nPerc
= 0;
1293 if (!bIsABS
&& bScale
)
1294 { // in case of oversize braces...
1295 sal_uInt16 nIndex
= GetScaleMode() == SmScaleMode::Height
?
1296 DIS_BRACKETSIZE
: DIS_NORMALBRACKETSIZE
;
1297 nPerc
= rFormat
.GetDistance(nIndex
);
1300 // determine the height for the braces
1301 tools::Long nBraceHeight
;
1304 nBraceHeight
= pBody
->GetType() == SmNodeType::Bracebody
?
1305 static_cast<SmBracebodyNode
*>(pBody
)->GetBodyHeight()
1306 : pBody
->GetHeight();
1307 nBraceHeight
+= 2 * (nBraceHeight
* nPerc
/ 100);
1310 nBraceHeight
= nFaceHeight
;
1312 // distance to the argument
1313 nPerc
= bIsABS
? 0 : rFormat
.GetDistance(DIS_BRACKETSPACE
);
1314 tools::Long nDist
= nFaceHeight
* nPerc
/ 100;
1316 // if wanted, scale the braces to the wanted size
1319 Size
aTmpSize (pLeft
->GetFont().GetFontSize());
1320 OSL_ENSURE(pRight
->GetFont().GetFontSize() == aTmpSize
,
1321 "Sm : different font sizes");
1322 aTmpSize
.setWidth( std::min(nBraceHeight
* 60 / 100,
1323 rFormat
.GetBaseSize().Height() * 3 / 2) );
1324 // correction factor since change from StarMath to OpenSymbol font
1325 // because of the different font width in the FontMetric
1326 aTmpSize
.setWidth( aTmpSize
.Width() * 182 );
1327 aTmpSize
.setWidth( aTmpSize
.Width() / 267 );
1329 sal_Unicode cChar
= pLeft
->GetToken().cMathChar
[0];
1330 if (cChar
!= MS_LINE
&& cChar
!= MS_DLINE
&&
1331 cChar
!= MS_VERTLINE
&& cChar
!= MS_DVERTLINE
)
1332 pLeft
->GetFont().SetSize(aTmpSize
);
1334 cChar
= pRight
->GetToken().cMathChar
[0];
1335 if (cChar
!= MS_LINE
&& cChar
!= MS_DLINE
&&
1336 cChar
!= MS_VERTLINE
&& cChar
!= MS_DVERTLINE
)
1337 pRight
->GetFont().SetSize(aTmpSize
);
1339 pLeft
->AdaptToY(rDev
, nBraceHeight
);
1340 pRight
->AdaptToY(rDev
, nBraceHeight
);
1343 pLeft
->Arrange(rDev
, rFormat
);
1344 pRight
->Arrange(rDev
, rFormat
);
1346 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1347 RectVerAlign eVerAlign
= bScale
? RectVerAlign::CenterY
: RectVerAlign::Baseline
;
1350 aPos
= pLeft
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, eVerAlign
);
1351 aPos
.AdjustX( -nDist
);
1352 pLeft
->MoveTo(aPos
);
1354 aPos
= pRight
->AlignTo(*pBody
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
);
1355 aPos
.AdjustX(nDist
);
1356 pRight
->MoveTo(aPos
);
1358 SmRect::operator = (*pBody
);
1359 ExtendBy(*pLeft
, RectCopyMBL::This
).ExtendBy(*pRight
, RectCopyMBL::This
);
1363 /**************************************************************************/
1366 void SmBracebodyNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1368 size_t nNumSubNodes
= GetNumSubNodes();
1369 if (nNumSubNodes
== 0)
1372 // arrange arguments
1373 for (size_t i
= 0; i
< nNumSubNodes
; i
+= 2)
1374 GetSubNode(i
)->Arrange(rDev
, rFormat
);
1376 // build reference rectangle with necessary info for vertical alignment
1377 SmRect
aRefRect (*GetSubNode(0));
1378 for (size_t i
= 0; i
< nNumSubNodes
; i
+= 2)
1380 SmRect
aTmpRect (*GetSubNode(i
));
1381 Point aPos
= aTmpRect
.AlignTo(aRefRect
, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1382 aTmpRect
.MoveTo(aPos
);
1383 aRefRect
.ExtendBy(aTmpRect
, RectCopyMBL::Xor
);
1386 mnBodyHeight
= aRefRect
.GetHeight();
1388 // scale separators to required height and arrange them
1389 bool bScale
= GetScaleMode() == SmScaleMode::Height
|| rFormat
.IsScaleNormalBrackets();
1390 tools::Long nHeight
= bScale
? aRefRect
.GetHeight() : GetFont().GetFontSize().Height();
1391 sal_uInt16 nIndex
= GetScaleMode() == SmScaleMode::Height
?
1392 DIS_BRACKETSIZE
: DIS_NORMALBRACKETSIZE
;
1393 sal_uInt16 nPerc
= rFormat
.GetDistance(nIndex
);
1395 nHeight
+= 2 * (nHeight
* nPerc
/ 100);
1396 for (size_t i
= 1; i
< nNumSubNodes
; i
+= 2)
1398 SmNode
*pNode
= GetSubNode(i
);
1399 pNode
->AdaptToY(rDev
, nHeight
);
1400 pNode
->Arrange(rDev
, rFormat
);
1403 // horizontal distance between argument and brackets or separators
1404 tools::Long nDist
= GetFont().GetFontSize().Height()
1405 * rFormat
.GetDistance(DIS_BRACKETSPACE
) / 100;
1407 SmNode
*pLeft
= GetSubNode(0);
1408 SmRect::operator = (*pLeft
);
1409 for (size_t i
= 1; i
< nNumSubNodes
; ++i
)
1411 bool bIsSeparator
= i
% 2 != 0;
1412 RectVerAlign eVerAlign
= bIsSeparator
? RectVerAlign::CenterY
: RectVerAlign::Baseline
;
1414 SmNode
*pRight
= GetSubNode(i
);
1415 Point aPosX
= pRight
->AlignTo(*pLeft
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
),
1416 aPosY
= pRight
->AlignTo(aRefRect
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
);
1417 aPosX
.AdjustX(nDist
);
1419 pRight
->MoveTo(Point(aPosX
.X(), aPosY
.Y()));
1420 ExtendBy(*pRight
, bIsSeparator
? RectCopyMBL::This
: RectCopyMBL::Xor
);
1427 /**************************************************************************/
1430 void SmVerticalBraceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1432 SmNode
*pBody
= Body(),
1434 *pScript
= Script();
1439 SmTmpDevice
aTmpDev (rDev
, true);
1440 aTmpDev
.SetFont(GetFont());
1442 pBody
->Arrange(aTmpDev
, rFormat
);
1444 // size is the same as for limits for this part
1445 pScript
->SetSize( Fraction( rFormat
.GetRelSize(SIZ_LIMITS
), 100 ) );
1446 // braces are a bit taller than usually
1447 pBrace
->SetSize( Fraction(3, 2) );
1449 tools::Long nItalicWidth
= pBody
->GetItalicWidth();
1450 if (nItalicWidth
> 0)
1451 pBrace
->AdaptToX(aTmpDev
, nItalicWidth
);
1453 pBrace
->Arrange(aTmpDev
, rFormat
);
1454 pScript
->Arrange(aTmpDev
, rFormat
);
1456 // determine the relative position and the distances between each other
1458 tools::Long nFontHeight
= pBody
->GetFont().GetFontSize().Height();
1459 tools::Long nDistBody
= nFontHeight
* rFormat
.GetDistance(DIS_ORNAMENTSIZE
),
1460 nDistScript
= nFontHeight
;
1461 if (GetToken().eType
== TOVERBRACE
)
1463 eRectPos
= RectPos::Top
;
1464 nDistBody
= - nDistBody
;
1465 nDistScript
*= - rFormat
.GetDistance(DIS_UPPERLIMIT
);
1469 eRectPos
= RectPos::Bottom
;
1470 nDistScript
*= + rFormat
.GetDistance(DIS_LOWERLIMIT
);
1475 Point aPos
= pBrace
->AlignTo(*pBody
, eRectPos
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1476 aPos
.AdjustY(nDistBody
);
1477 pBrace
->MoveTo(aPos
);
1479 aPos
= pScript
->AlignTo(*pBrace
, eRectPos
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1480 aPos
.AdjustY(nDistScript
);
1481 pScript
->MoveTo(aPos
);
1483 SmRect::operator = (*pBody
);
1484 ExtendBy(*pBrace
, RectCopyMBL::This
).ExtendBy(*pScript
, RectCopyMBL::This
);
1488 /**************************************************************************/
1491 SmNode
* SmOperNode::GetSymbol()
1493 SmNode
*pNode
= GetSubNode(0);
1496 if (pNode
->GetType() == SmNodeType::SubSup
)
1497 pNode
= static_cast<SmSubSupNode
*>(pNode
)->GetBody();
1499 OSL_ENSURE(pNode
, "Sm: NULL pointer!");
1504 tools::Long
SmOperNode::CalcSymbolHeight(const SmNode
&rSymbol
,
1505 const SmFormat
&rFormat
) const
1506 // returns the font height to be used for operator-symbol
1508 tools::Long nHeight
= GetFont().GetFontSize().Height();
1510 SmTokenType eTmpType
= GetToken().eType
;
1511 if (eTmpType
== TLIM
|| eTmpType
== TLIMINF
|| eTmpType
== TLIMSUP
)
1514 if (!rFormat
.IsTextmode())
1516 // set minimum size ()
1517 nHeight
+= (nHeight
* 20) / 100;
1520 * rFormat
.GetDistance(DIS_OPERATORSIZE
) / 100;
1521 nHeight
= nHeight
* 686 / 845;
1524 // correct user-defined symbols to match height of sum from used font
1525 if (rSymbol
.GetToken().eType
== TSPECIAL
)
1526 nHeight
= nHeight
* 845 / 686;
1532 void SmOperNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1534 SmNode
*pOper
= GetSubNode(0);
1535 SmNode
*pBody
= GetSubNode(1);
1540 SmNode
*pSymbol
= GetSymbol();
1541 pSymbol
->SetSize(Fraction(CalcSymbolHeight(*pSymbol
, rFormat
),
1542 pSymbol
->GetFont().GetFontSize().Height()));
1544 pBody
->Arrange(rDev
, rFormat
);
1545 bool bDynamicallySized
= false;
1546 if (pSymbol
->GetToken().eType
== TINTD
)
1548 tools::Long nBodyHeight
= pBody
->GetHeight();
1549 tools::Long nFontHeight
= pSymbol
->GetFont().GetFontSize().Height();
1550 if (nFontHeight
< nBodyHeight
)
1552 pSymbol
->SetSize(Fraction(nBodyHeight
, nFontHeight
));
1553 bDynamicallySized
= true;
1556 pOper
->Arrange(rDev
, rFormat
);
1558 tools::Long nOrigHeight
= GetFont().GetFontSize().Height(),
1560 * rFormat
.GetDistance(DIS_OPERATORSPACE
) / 100;
1562 Point aPos
= pOper
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, bDynamicallySized
? RectVerAlign::CenterY
: RectVerAlign::Mid
);
1563 aPos
.AdjustX( -nDist
);
1564 pOper
->MoveTo(aPos
);
1566 SmRect::operator = (*pBody
);
1567 ExtendBy(*pOper
, RectCopyMBL::This
);
1571 /**************************************************************************/
1574 void SmAlignNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1575 // set alignment within the entire subtree (including current node)
1577 assert(GetNumSubNodes() == 1);
1579 SmNode
*pNode
= GetSubNode(0);
1582 RectHorAlign eHorAlign
= RectHorAlign::Center
;
1583 switch (GetToken().eType
)
1585 case TALIGNL
: eHorAlign
= RectHorAlign::Left
; break;
1586 case TALIGNC
: eHorAlign
= RectHorAlign::Center
; break;
1587 case TALIGNR
: eHorAlign
= RectHorAlign::Right
; break;
1591 SetRectHorAlign(eHorAlign
);
1593 pNode
->Arrange(rDev
, rFormat
);
1595 SmRect::operator = (pNode
->GetRect());
1599 /**************************************************************************/
1602 void SmAttributeNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1604 SmNode
*pAttr
= Attribute(),
1609 pBody
->Arrange(rDev
, rFormat
);
1611 if (GetScaleMode() == SmScaleMode::Width
)
1612 pAttr
->AdaptToX(rDev
, pBody
->GetItalicWidth());
1613 pAttr
->Arrange(rDev
, rFormat
);
1615 // get relative position of attribute
1616 RectVerAlign eVerAlign
;
1617 tools::Long nDist
= 0;
1618 switch (GetToken().eType
)
1620 eVerAlign
= RectVerAlign::AttributeLo
;
1623 eVerAlign
= RectVerAlign::AttributeMid
;
1626 eVerAlign
= RectVerAlign::AttributeHi
;
1627 if (pBody
->GetType() == SmNodeType::Attribute
)
1628 nDist
= GetFont().GetFontSize().Height()
1629 * rFormat
.GetDistance(DIS_ORNAMENTSPACE
) / 100;
1631 Point aPos
= pAttr
->AlignTo(*pBody
, RectPos::Attribute
, RectHorAlign::Center
, eVerAlign
);
1632 aPos
.AdjustY( -nDist
);
1633 pAttr
->MoveTo(aPos
);
1635 SmRect::operator = (*pBody
);
1636 ExtendBy(*pAttr
, RectCopyMBL::This
, true);
1639 void SmFontNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
1641 //! prepare subnodes first
1642 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
1645 switch (GetToken().eType
)
1647 case TFIXED
: nFnt
= FNT_FIXED
; break;
1648 case TSANS
: nFnt
= FNT_SANS
; break;
1649 case TSERIF
: nFnt
= FNT_SERIF
; break;
1654 { GetFont() = rFormat
.GetFont( sal::static_int_cast
< sal_uInt16
>(nFnt
) );
1658 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
1659 //! other font nodes (those with lower depth in the tree)
1660 Flags() |= FontChangeMask::Face
;
1663 void SmFontNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1665 SmNode
*pNode
= GetSubNode(1);
1669 switch (GetToken().eType
)
1671 pNode
->SetFontSize(maFontSize
, meSizeType
);
1676 pNode
->SetFont(GetFont());
1678 case TUNKNOWN
: break; // no assertion on "font <?> <?>"
1680 case TPHANTOM
: SetPhantom(true); break;
1681 case TBOLD
: SetAttribute(FontAttribute::Bold
); break;
1682 case TITALIC
: SetAttribute(FontAttribute::Italic
); break;
1683 case TNBOLD
: ClearAttribute(FontAttribute::Bold
); break;
1684 case TNITALIC
: ClearAttribute(FontAttribute::Italic
); break;
1686 // Using HTML CSS Level 1 standard
1691 case TDVIPSNAMESCOL
:
1694 nc
= GetToken().cMathChar
.toUInt32(16);
1695 SetColor(Color(ColorTransparency
, nc
));
1699 SAL_WARN("starmath", "unknown case");
1702 pNode
->Arrange(rDev
, rFormat
);
1704 SmRect::operator = (pNode
->GetRect());
1707 /**************************************************************************/
1710 SmPolyLineNode::SmPolyLineNode(const SmToken
&rNodeToken
)
1711 : SmGraphicNode(SmNodeType::PolyLine
, rNodeToken
)
1718 void SmPolyLineNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong nNewWidth
)
1720 maToSize
.setWidth( nNewWidth
);
1724 void SmPolyLineNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong nNewHeight
)
1726 GetFont().FreezeBorderWidth();
1727 maToSize
.setHeight( nNewHeight
);
1731 void SmPolyLineNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1733 //! some routines being called extract some info from the OutputDevice's
1734 //! font (eg the space to be used for borders OR the font name(!!)).
1735 //! Thus the font should reflect the needs and has to be set!
1736 SmTmpDevice
aTmpDev (rDev
, true);
1737 aTmpDev
.SetFont(GetFont());
1739 tools::Long nBorderwidth
= GetFont().GetBorderWidth();
1741 // create polygon using both endpoints
1742 assert(maPoly
.GetSize() == 2);
1743 Point aPointA
, aPointB
;
1744 if (GetToken().eType
== TWIDESLASH
)
1746 aPointA
.setX( nBorderwidth
);
1747 aPointA
.setY( maToSize
.Height() - nBorderwidth
);
1748 aPointB
.setX( maToSize
.Width() - nBorderwidth
);
1749 aPointB
.setY( nBorderwidth
);
1753 OSL_ENSURE(GetToken().eType
== TWIDEBACKSLASH
, "Sm : unexpected token");
1754 aPointA
.setX( nBorderwidth
);
1755 aPointA
.setY( nBorderwidth
);
1756 aPointB
.setX( maToSize
.Width() - nBorderwidth
);
1757 aPointB
.setY( maToSize
.Height() - nBorderwidth
);
1759 maPoly
.SetPoint(aPointA
, 0);
1760 maPoly
.SetPoint(aPointB
, 1);
1762 tools::Long nThick
= GetFont().GetFontSize().Height()
1763 * rFormat
.GetDistance(DIS_STROKEWIDTH
) / 100;
1764 mnWidth
= nThick
+ 2 * nBorderwidth
;
1766 SmRect::operator = (SmRect(maToSize
.Width(), maToSize
.Height()));
1770 /**************************************************************************/
1772 void SmRootSymbolNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong nWidth
)
1774 mnBodyWidth
= nWidth
;
1778 void SmRootSymbolNode::AdaptToY(OutputDevice
&rDev
, sal_uLong nHeight
)
1780 // some additional length so that the horizontal
1781 // bar will be positioned above the argument
1782 SmMathSymbolNode::AdaptToY(rDev
, nHeight
+ nHeight
/ 10);
1786 /**************************************************************************/
1789 void SmRectangleNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong nWidth
)
1791 maToSize
.setWidth( nWidth
);
1795 void SmRectangleNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong nHeight
)
1797 GetFont().FreezeBorderWidth();
1798 maToSize
.setHeight( nHeight
);
1802 void SmRectangleNode::Arrange(OutputDevice
&rDev
, const SmFormat
&/*rFormat*/)
1804 tools::Long nFontHeight
= GetFont().GetFontSize().Height();
1805 tools::Long nWidth
= maToSize
.Width(),
1806 nHeight
= maToSize
.Height();
1808 nHeight
= nFontHeight
/ 30;
1810 nWidth
= nFontHeight
/ 3;
1812 SmTmpDevice
aTmpDev (rDev
, true);
1813 aTmpDev
.SetFont(GetFont());
1815 // add some borderspace
1816 sal_uLong nTmpBorderWidth
= GetFont().GetBorderWidth();
1817 nHeight
+= 2 * nTmpBorderWidth
;
1819 //! use this method in order to have 'SmRect::HasAlignInfo() == true'
1820 //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
1821 SmRect::operator = (SmRect(nWidth
, nHeight
));
1825 /**************************************************************************/
1828 SmTextNode::SmTextNode( SmNodeType eNodeType
, const SmToken
&rNodeToken
, sal_uInt16 nFontDescP
)
1829 : SmVisibleNode(eNodeType
, rNodeToken
)
1830 , mnFontDesc(nFontDescP
)
1831 , mnSelectionStart(0)
1836 SmTextNode::SmTextNode( const SmToken
&rNodeToken
, sal_uInt16 nFontDescP
)
1837 : SmVisibleNode(SmNodeType::Text
, rNodeToken
)
1838 , mnFontDesc(nFontDescP
)
1839 , mnSelectionStart(0)
1844 void SmTextNode::ChangeText(const OUString
&rText
) {
1846 GetToken().aText
= rText
;
1850 void SmTextNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
1852 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
1854 // default setting for horizontal alignment of nodes with TTEXT
1855 // content is as alignl (cannot be done in Arrange since it would
1856 // override the settings made by an SmAlignNode before)
1857 if (TTEXT
== GetToken().eType
)
1858 SetRectHorAlign( RectHorAlign::Left
);
1860 maText
= GetToken().aText
;
1861 GetFont() = rFormat
.GetFont(GetFontDesc());
1863 if (IsItalic( GetFont() ))
1864 Attributes() |= FontAttribute::Italic
;
1865 if (IsBold( GetFont() ))
1866 Attributes() |= FontAttribute::Bold
;
1868 // special handling for ':' where it is a token on its own and is likely
1869 // to be used for mathematical notations. (E.g. a:b = 2:3)
1870 // In that case it should not be displayed in italic.
1871 if (maText
.getLength() == 1 && GetToken().aText
[0] == ':')
1872 Attributes() &= ~FontAttribute::Italic
;
1874 // Arabic text should not be italic, so we check for any character in Arabic script and
1875 // remove italic attribute.
1876 if (!maText
.isEmpty())
1878 sal_Int32 nIndex
= 0;
1879 while (nIndex
< maText
.getLength())
1881 sal_uInt32 cChar
= maText
.iterateCodePoints(&nIndex
);
1882 if (u_getIntPropertyValue(cChar
, UCHAR_SCRIPT
) == USCRIPT_ARABIC
)
1884 Attributes() &= ~FontAttribute::Italic
;
1892 void SmTextNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1894 PrepareAttributes();
1896 sal_uInt16 nSizeDesc
= GetFontDesc() == FNT_FUNCTION
?
1897 SIZ_FUNCTION
: SIZ_TEXT
;
1898 GetFont() *= Fraction (rFormat
.GetRelSize(nSizeDesc
), 100);
1900 SmTmpDevice
aTmpDev (rDev
, true);
1901 aTmpDev
.SetFont(GetFont());
1903 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, maText
, GetFont().GetBorderWidth()));
1906 void SmTextNode::GetAccessibleText( OUStringBuffer
&rText
) const
1908 rText
.append(maText
);
1911 void SmTextNode::AdjustFontDesc()
1913 if (GetToken().nGroup
== TG::Function
) mnFontDesc
= FNT_FUNCTION
;
1914 else if (GetToken().eType
== TTEXT
) mnFontDesc
= FNT_TEXT
;
1916 sal_Unicode firstChar
= maText
[0];
1917 if( ('0' <= firstChar
&& firstChar
<= '9') || firstChar
== '.' || firstChar
== ',')
1918 mnFontDesc
= FNT_NUMBER
;
1919 else mnFontDesc
= FNT_VARIABLE
;
1923 sal_Unicode
SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn
)
1925 //Find the best match in accepted unicode for our private area symbols
1926 static const sal_Unicode aStarMathPrivateToUnicode
[] =
1928 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
1929 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
1930 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
1931 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
1932 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
1933 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
1934 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
1935 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
1936 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
1937 0xE0DA, 0x2190, 0x2191, 0x2193
1939 if ((nIn
>= 0xE080) && (nIn
<= 0xE0DD))
1940 nIn
= aStarMathPrivateToUnicode
[nIn
-0xE080];
1942 //For whatever unicode glyph that equation editor doesn't ship with that
1943 //we have a possible match we can munge it to.
1956 /**************************************************************************/
1958 void SmMatrixNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1962 // initialize array that is to hold the maximum widths of all
1963 // elements (subnodes) in that column.
1964 std::vector
<tools::Long
> aColWidth(mnNumCols
);
1966 // arrange subnodes and calculate the above arrays contents
1967 size_t nNodes
= GetNumSubNodes();
1968 for (size_t i
= 0; i
< nNodes
; ++i
)
1970 size_t nIdx
= nNodes
- 1 - i
;
1971 if (nullptr != (pNode
= GetSubNode(nIdx
)))
1973 pNode
->Arrange(rDev
, rFormat
);
1974 int nCol
= nIdx
% mnNumCols
;
1975 aColWidth
[nCol
] = std::max(aColWidth
[nCol
], pNode
->GetItalicWidth());
1979 // norm distance from which the following two are calculated
1980 const tools::Long nNormDist
= 3 * GetFont().GetFontSize().Height();
1982 // define horizontal and vertical minimal distances that separate
1984 tools::Long nHorDist
= nNormDist
* rFormat
.GetDistance(DIS_MATRIXCOL
) / 100,
1985 nVerDist
= nNormDist
* rFormat
.GetDistance(DIS_MATRIXROW
) / 100;
1987 // build array that holds the leftmost position for each column
1988 std::vector
<tools::Long
> aColLeft(mnNumCols
);
1990 for (size_t j
= 0; j
< mnNumCols
; ++j
)
1993 nX
+= aColWidth
[j
] + nHorDist
;
1996 SmRect::operator = (SmRect());
1997 for (size_t i
= 0; i
< mnNumRows
; ++i
)
2001 for (size_t j
= 0; j
< mnNumCols
; ++j
)
2003 SmNode
*pTmpNode
= GetSubNode(i
* mnNumCols
+ j
);
2006 const SmRect
&rNodeRect
= pTmpNode
->GetRect();
2008 // align all baselines in that row if possible
2009 aPos
= rNodeRect
.AlignTo(aLineRect
, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
2011 // get horizontal alignment
2012 const SmNode
*pCoNode
= pTmpNode
->GetLeftMost();
2013 RectHorAlign eHorAlign
= pCoNode
->GetRectHorAlign();
2015 // calculate horizontal position of element depending on column
2016 // and horizontal alignment
2018 { case RectHorAlign::Left
:
2019 aPos
.setX( aColLeft
[j
] );
2021 case RectHorAlign::Center
:
2022 aPos
.setX( rNodeRect
.GetLeft() + aColLeft
[j
]
2024 - rNodeRect
.GetItalicCenterX() );
2026 case RectHorAlign::Right
:
2027 aPos
.setX( aColLeft
[j
]
2028 + aColWidth
[j
] - rNodeRect
.GetItalicWidth() );
2034 pTmpNode
->MoveTo(aPos
);
2035 aLineRect
.ExtendBy(rNodeRect
, RectCopyMBL::Xor
);
2038 aPos
= aLineRect
.AlignTo(*this, RectPos::Bottom
, RectHorAlign::Center
, RectVerAlign::Baseline
);
2040 aPos
.AdjustY(nVerDist
);
2042 // move 'aLineRect' and rectangles in that line to final position
2043 Point
aDelta(0, // since horizontal alignment is already done
2044 aPos
.Y() - aLineRect
.GetTop());
2045 aLineRect
.Move(aDelta
);
2046 for (size_t j
= 0; j
< mnNumCols
; ++j
)
2048 if (nullptr != (pNode
= GetSubNode(i
* mnNumCols
+ j
)))
2049 pNode
->Move(aDelta
);
2052 ExtendBy(aLineRect
, RectCopyMBL::None
);
2056 const SmNode
* SmMatrixNode::GetLeftMost() const
2062 /**************************************************************************/
2065 SmMathSymbolNode::SmMathSymbolNode(const SmToken
&rNodeToken
)
2066 : SmSpecialNode(SmNodeType::Math
, rNodeToken
, FNT_MATH
)
2068 SetText(GetToken().cMathChar
);
2071 void SmMathSymbolNode::AdaptToX(OutputDevice
&rDev
, sal_uLong nWidth
)
2073 // Since there is no function to do this, we try to approximate it:
2074 Size
aFntSize (GetFont().GetFontSize());
2076 //! however the result is a bit better with 'nWidth' as initial font width
2077 aFntSize
.setWidth( nWidth
);
2078 GetFont().SetSize(aFntSize
);
2080 SmTmpDevice
aTmpDev (rDev
, true);
2081 aTmpDev
.SetFont(GetFont());
2083 // get denominator of error factor for width
2084 tools::Long nTmpBorderWidth
= GetFont().GetBorderWidth();
2085 tools::Long nDenom
= SmRect(aTmpDev
, nullptr, GetText(), nTmpBorderWidth
).GetItalicWidth();
2087 // scale fontwidth with this error factor
2088 aFntSize
.setWidth( aFntSize
.Width() * nWidth
);
2089 aFntSize
.setWidth( aFntSize
.Width() / ( nDenom
? nDenom
: 1) );
2091 GetFont().SetSize(aFntSize
);
2094 void SmMathSymbolNode::AdaptToY(OutputDevice
&rDev
, sal_uLong nHeight
)
2096 GetFont().FreezeBorderWidth();
2097 Size
aFntSize (GetFont().GetFontSize());
2099 // Since we only want to scale the height, we might have
2100 // to determine the font width in order to keep it
2101 if (aFntSize
.Width() == 0)
2103 rDev
.Push(vcl::PushFlags::FONT
| vcl::PushFlags::MAPMODE
);
2104 rDev
.SetFont(GetFont());
2105 aFntSize
.setWidth( rDev
.GetFontMetric().GetFontSize().Width() );
2108 OSL_ENSURE(aFntSize
.Width() != 0, "Sm: ");
2110 //! however the result is a bit better with 'nHeight' as initial
2112 aFntSize
.setHeight( nHeight
);
2113 GetFont().SetSize(aFntSize
);
2115 SmTmpDevice
aTmpDev (rDev
, true);
2116 aTmpDev
.SetFont(GetFont());
2118 // get denominator of error factor for height
2119 tools::Long nTmpBorderWidth
= GetFont().GetBorderWidth();
2120 tools::Long nDenom
= 0;
2121 if (!GetText().isEmpty())
2122 nDenom
= SmRect(aTmpDev
, nullptr, GetText(), nTmpBorderWidth
).GetHeight();
2124 // scale fontwidth with this error factor
2125 aFntSize
.setHeight( aFntSize
.Height() * nHeight
);
2126 aFntSize
.setHeight( aFntSize
.Height() / ( nDenom
? nDenom
: 1) );
2128 GetFont().SetSize(aFntSize
);
2132 void SmMathSymbolNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2134 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2136 GetFont() = rFormat
.GetFont(GetFontDesc());
2137 // use same font size as is used for variables
2138 GetFont().SetSize( rFormat
.GetFont( FNT_VARIABLE
).GetFontSize() );
2140 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL
||
2141 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE
,
2142 "wrong charset for character from StarMath/OpenSymbol font");
2144 Flags() |= FontChangeMask::Face
| FontChangeMask::Italic
;
2148 void SmMathSymbolNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2150 const OUString
&rText
= GetText();
2152 if (rText
.isEmpty() || rText
[0] == '\0')
2153 { SmRect::operator = (SmRect());
2157 PrepareAttributes();
2159 GetFont() *= Fraction (rFormat
.GetRelSize(SIZ_TEXT
), 100);
2161 SmTmpDevice
aTmpDev (rDev
, true);
2162 aTmpDev
.SetFont(GetFont());
2164 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, rText
, GetFont().GetBorderWidth()));
2167 /**************************************************************************/
2169 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType
, const SmToken
&rNodeToken
, sal_uInt16 _nFontDesc
)
2170 : SmTextNode(eNodeType
, rNodeToken
, _nFontDesc
)
2175 SmSpecialNode::SmSpecialNode(const SmToken
&rNodeToken
)
2176 : SmTextNode(SmNodeType::Special
, rNodeToken
, FNT_VARIABLE
) // default Font isn't always correct!
2181 void SmSpecialNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2183 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2186 SmModule
*pp
= SM_MOD();
2188 bool bIsGreekSymbol
= false;
2189 bool bIsSpecialSymbol
= false;
2190 bool bIsArabic
= false;
2192 if ((!GetToken().aText
.isEmpty())
2194 != (pSym
= pp
->GetSymbolManager().GetSymbolByName(GetToken().aText
.subView(1)))))
2196 sal_UCS4 cChar
= pSym
->GetCharacter();
2197 OUString
aTmp( &cChar
, 1 );
2199 GetFont() = SmFace(pSym
->GetFace(&rFormat
));
2201 OUString aSymbolSetName
= SmLocalizedSymbolData::GetExportSymbolSetName(pSym
->GetSymbolSetName());
2202 if (aSymbolSetName
== "Greek")
2203 bIsGreekSymbol
= true;
2204 else if (aSymbolSetName
== "Special")
2205 bIsSpecialSymbol
= true;
2206 else if (aSymbolSetName
== "Arabic")
2211 SetText( GetToken().aText
);
2212 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
2214 // use same font size as is used for variables
2215 GetFont().SetSize( rFormat
.GetFont( FNT_VARIABLE
).GetFontSize() );
2217 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2218 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2219 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2221 //! see also SmFontStyles::GetStyleName
2222 if (IsItalic( GetFont() ))
2223 SetAttribute(FontAttribute::Italic
);
2224 if (IsBold( GetFont() ))
2225 SetAttribute(FontAttribute::Bold
);
2227 Flags() |= FontChangeMask::Face
;
2229 sal_uInt32 cChar
= 0;
2230 if (!GetText().isEmpty())
2232 sal_Int32 nIndex
= 0;
2233 cChar
= GetText().iterateCodePoints(&nIndex
);
2235 bIsArabic
= u_getIntPropertyValue(cChar
, UCHAR_SCRIPT
) == USCRIPT_ARABIC
;
2238 if (!bIsGreekSymbol
&& !bIsSpecialSymbol
&& !bIsArabic
)
2241 // Arabic and special symbols should not be italic,
2242 // Greek is italic only in some cases.
2243 bool bItalic
= false;
2246 sal_Int16 nStyle
= rFormat
.GetGreekCharStyle();
2247 OSL_ENSURE( nStyle
>= 0 && nStyle
<= 2, "unexpected value for GreekCharStyle" );
2250 else if (nStyle
== 2)
2252 static const sal_Unicode cUppercaseAlpha
= 0x0391;
2253 static const sal_Unicode cUppercaseOmega
= 0x03A9;
2254 // uppercase letters should be straight and lowercase letters italic
2255 bItalic
= cUppercaseAlpha
> cChar
|| cChar
> cUppercaseOmega
;
2260 Attributes() |= FontAttribute::Italic
;
2262 Attributes() &= ~FontAttribute::Italic
;
2266 void SmSpecialNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2268 PrepareAttributes();
2270 SmTmpDevice
aTmpDev (rDev
, true);
2271 aTmpDev
.SetFont(GetFont());
2273 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(), GetFont().GetBorderWidth()));
2276 /**************************************************************************/
2279 void SmGlyphSpecialNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2281 PrepareAttributes();
2283 SmTmpDevice
aTmpDev (rDev
, true);
2284 aTmpDev
.SetFont(GetFont());
2286 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(),
2287 GetFont().GetBorderWidth()).AsGlyphRect());
2291 /**************************************************************************/
2294 void SmPlaceNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2296 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2298 GetFont().SetColor(COL_GRAY
);
2299 Flags() |= FontChangeMask::Color
| FontChangeMask::Face
| FontChangeMask::Italic
;
2303 void SmPlaceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2305 PrepareAttributes();
2307 SmTmpDevice
aTmpDev (rDev
, true);
2308 aTmpDev
.SetFont(GetFont());
2310 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(), GetFont().GetBorderWidth()));
2314 /**************************************************************************/
2317 void SmErrorNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2319 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2321 GetFont().SetColor(COL_RED
);
2322 Flags() |= FontChangeMask::Phantom
| FontChangeMask::Bold
| FontChangeMask::Italic
2323 | FontChangeMask::Color
| FontChangeMask::Face
| FontChangeMask::Size
;
2327 void SmErrorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2329 PrepareAttributes();
2331 SmTmpDevice
aTmpDev (rDev
, true);
2332 aTmpDev
.SetFont(GetFont());
2334 const OUString
&rText
= GetText();
2335 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, rText
, GetFont().GetBorderWidth()));
2338 /**************************************************************************/
2340 void SmBlankNode::IncreaseBy(const SmToken
&rToken
, sal_uInt32 nMultiplyBy
)
2342 switch(rToken
.eType
)
2344 case TBLANK
: mnNum
+= (4 * nMultiplyBy
); break;
2345 case TSBLANK
: mnNum
+= (1 * nMultiplyBy
); break;
2351 void SmBlankNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2353 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2355 // Here it need/should not be the StarMath font, so that for the character
2356 // used in Arrange a normal (non-clipped) rectangle is generated
2357 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
2359 Flags() |= FontChangeMask::Face
| FontChangeMask::Bold
| FontChangeMask::Italic
;
2363 void SmBlankNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2365 SmTmpDevice
aTmpDev (rDev
, true);
2366 aTmpDev
.SetFont(GetFont());
2368 // make distance depend on the font height
2369 // (so that it increases when scaling (e.g. size *2 {a ~ b})
2370 tools::Long nDist
= GetFont().GetFontSize().Height() / 10,
2371 nSpace
= mnNum
* nDist
;
2373 // get a SmRect with Baseline and all the bells and whistles
2374 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, OUString(' '),
2375 GetFont().GetBorderWidth()));
2377 // and resize it to the requested size
2378 SetItalicSpaces(0, 0);
2382 /**************************************************************************/
2383 //Implementation of all accept methods for SmVisitor
2385 void SmTableNode::Accept(SmVisitor
* pVisitor
) {
2386 pVisitor
->Visit(this);
2389 void SmBraceNode::Accept(SmVisitor
* pVisitor
) {
2390 pVisitor
->Visit(this);
2393 void SmBracebodyNode::Accept(SmVisitor
* pVisitor
) {
2394 pVisitor
->Visit(this);
2397 void SmOperNode::Accept(SmVisitor
* pVisitor
) {
2398 pVisitor
->Visit(this);
2401 void SmAlignNode::Accept(SmVisitor
* pVisitor
) {
2402 pVisitor
->Visit(this);
2405 void SmAttributeNode::Accept(SmVisitor
* pVisitor
) {
2406 pVisitor
->Visit(this);
2409 void SmFontNode::Accept(SmVisitor
* pVisitor
) {
2410 pVisitor
->Visit(this);
2413 void SmUnHorNode::Accept(SmVisitor
* pVisitor
) {
2414 pVisitor
->Visit(this);
2417 void SmBinHorNode::Accept(SmVisitor
* pVisitor
) {
2418 pVisitor
->Visit(this);
2421 void SmBinVerNode::Accept(SmVisitor
* pVisitor
) {
2422 pVisitor
->Visit(this);
2425 void SmBinDiagonalNode::Accept(SmVisitor
* pVisitor
) {
2426 pVisitor
->Visit(this);
2429 void SmSubSupNode::Accept(SmVisitor
* pVisitor
) {
2430 pVisitor
->Visit(this);
2433 void SmMatrixNode::Accept(SmVisitor
* pVisitor
) {
2434 pVisitor
->Visit(this);
2437 void SmPlaceNode::Accept(SmVisitor
* pVisitor
) {
2438 pVisitor
->Visit(this);
2441 void SmTextNode::Accept(SmVisitor
* pVisitor
) {
2442 pVisitor
->Visit(this);
2445 void SmSpecialNode::Accept(SmVisitor
* pVisitor
) {
2446 pVisitor
->Visit(this);
2449 void SmGlyphSpecialNode::Accept(SmVisitor
* pVisitor
) {
2450 pVisitor
->Visit(this);
2453 void SmMathSymbolNode::Accept(SmVisitor
* pVisitor
) {
2454 pVisitor
->Visit(this);
2457 void SmBlankNode::Accept(SmVisitor
* pVisitor
) {
2458 pVisitor
->Visit(this);
2461 void SmErrorNode::Accept(SmVisitor
* pVisitor
) {
2462 pVisitor
->Visit(this);
2465 void SmLineNode::Accept(SmVisitor
* pVisitor
) {
2466 pVisitor
->Visit(this);
2469 void SmExpressionNode::Accept(SmVisitor
* pVisitor
) {
2470 pVisitor
->Visit(this);
2473 void SmPolyLineNode::Accept(SmVisitor
* pVisitor
) {
2474 pVisitor
->Visit(this);
2477 void SmRootNode::Accept(SmVisitor
* pVisitor
) {
2478 pVisitor
->Visit(this);
2481 void SmRootSymbolNode::Accept(SmVisitor
* pVisitor
) {
2482 pVisitor
->Visit(this);
2485 void SmRectangleNode::Accept(SmVisitor
* pVisitor
) {
2486 pVisitor
->Visit(this);
2489 void SmVerticalBraceNode::Accept(SmVisitor
* pVisitor
) {
2490 pVisitor
->Visit(this);
2493 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */