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 .
25 #include "document.hxx"
27 #include "mathtype.hxx"
28 #include "tmpdevice.hxx"
29 #include "visitors.hxx"
31 #include <comphelper/string.hxx>
32 #include <tools/color.hxx>
33 #include <tools/fract.hxx>
34 #include <tools/gen.hxx>
35 #include <vcl/outdev.hxx>
42 #include <boost/checked_delete.hpp>
47 void ForEachNonNull(SmNode
*pNode
, F
&& f
)
49 sal_uInt16 nSize
= pNode
->GetNumSubNodes();
50 for (sal_uInt16 i
= 0; i
< nSize
; i
++) {
51 SmNode
*pSubNode
= pNode
->GetSubNode(i
);
52 if (pSubNode
!= nullptr)
59 SmNode::SmNode(SmNodeType eNodeType
, const SmToken
&rNodeToken
)
60 : maNodeToken( rNodeToken
)
62 , meScaleMode( SCALE_NONE
)
63 , meRectHorAlign( RectHorAlign::Left
)
64 , mnFlags( FontChangeMask::None
)
65 , mnAttributes( FontAttribute::None
)
66 , mbIsPhantom( false )
67 , mbIsSelected( false )
69 , mpParentNode( nullptr )
79 const SmNode
* SmNode::GetLeftMost() const
80 // returns leftmost node of current subtree.
81 //! (this assumes the one with index 0 is always the leftmost subnode
82 //! for the current node).
84 const SmNode
*pNode
= GetNumSubNodes() > 0 ?
85 GetSubNode(0) : nullptr;
87 return pNode
? pNode
->GetLeftMost() : this;
91 void SmNode::SetPhantom(bool bIsPhantomP
)
93 if (! (Flags() & FontChangeMask::Phantom
))
94 mbIsPhantom
= bIsPhantomP
;
97 ForEachNonNull(this, [b
](SmNode
*pNode
){pNode
->SetPhantom(b
);});
101 void SmNode::SetColor(const Color
& rColor
)
103 if (! (Flags() & FontChangeMask::Color
))
104 GetFont().SetColor(rColor
);
106 ForEachNonNull(this, [&rColor
](SmNode
*pNode
){pNode
->SetColor(rColor
);});
110 void SmNode::SetAttribut(FontAttribute nAttrib
)
113 (nAttrib
== FontAttribute::Bold
&& !(Flags() & FontChangeMask::Bold
)) ||
114 (nAttrib
== FontAttribute::Italic
&& !(Flags() & FontChangeMask::Italic
))
117 mnAttributes
|= nAttrib
;
120 ForEachNonNull(this, [nAttrib
](SmNode
*pNode
){pNode
->SetAttribut(nAttrib
);});
124 void SmNode::ClearAttribut(FontAttribute nAttrib
)
127 (nAttrib
== FontAttribute::Bold
&& !(Flags() & FontChangeMask::Bold
)) ||
128 (nAttrib
== FontAttribute::Italic
&& !(Flags() & FontChangeMask::Italic
))
131 mnAttributes
&= ~nAttrib
;
134 ForEachNonNull(this, [nAttrib
](SmNode
*pNode
){pNode
->ClearAttribut(nAttrib
);});
138 void SmNode::SetFont(const SmFace
&rFace
)
140 if (!(Flags() & FontChangeMask::Face
))
143 ForEachNonNull(this, [&rFace
](SmNode
*pNode
){pNode
->SetFont(rFace
);});
147 void SmNode::SetFontSize(const Fraction
&rSize
, FontSizeType nType
)
148 //! 'rSize' is in units of pts
152 if (!(Flags() & FontChangeMask::Size
))
154 Fraction
aVal (SmPtsTo100th_mm(rSize
.GetNumerator()),
155 rSize
.GetDenominator());
156 long nHeight
= static_cast<long>(aVal
);
158 aFntSize
= GetFont().GetFontSize();
159 aFntSize
.Width() = 0;
162 case FontSizeType::ABSOLUT
:
163 aFntSize
.Height() = nHeight
;
166 case FontSizeType::PLUS
:
167 aFntSize
.Height() += nHeight
;
170 case FontSizeType::MINUS
:
171 aFntSize
.Height() -= nHeight
;
174 case FontSizeType::MULTIPLY
:
175 aFntSize
.Height() = static_cast<long>(Fraction(aFntSize
.Height()) * rSize
);
178 case FontSizeType::DIVIDE
:
179 if (rSize
!= Fraction(0L))
180 aFntSize
.Height() = static_cast<long>(Fraction(aFntSize
.Height()) / rSize
);
186 // check the requested size against maximum value
187 static int const nMaxVal
= SmPtsTo100th_mm(128);
188 if (aFntSize
.Height() > nMaxVal
)
189 aFntSize
.Height() = nMaxVal
;
191 GetFont().SetSize(aFntSize
);
194 ForEachNonNull(this, [&rSize
, &nType
](SmNode
*pNode
){pNode
->SetFontSize(rSize
, nType
);});
198 void SmNode::SetSize(const Fraction
&rSize
)
202 ForEachNonNull(this, [&rSize
](SmNode
*pNode
){pNode
->SetSize(rSize
);});
206 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign
, bool bApplyToSubTree
)
208 if (!(Flags() & FontChangeMask::HorAlign
))
209 meRectHorAlign
= eHorAlign
;
212 ForEachNonNull(this, [eHorAlign
](SmNode
*pNode
){pNode
->SetRectHorAlign(eHorAlign
);});
216 void SmNode::PrepareAttributes()
218 GetFont().SetWeight((Attributes() & FontAttribute::Bold
) ? WEIGHT_BOLD
: WEIGHT_NORMAL
);
219 GetFont().SetItalic((Attributes() & FontAttribute::Italic
) ? ITALIC_NORMAL
: ITALIC_NONE
);
223 void SmNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
226 mnFlags
= FontChangeMask::None
;
227 mnAttributes
= FontAttribute::None
;
229 switch (rFormat
.GetHorAlign())
230 { case AlignLeft
: meRectHorAlign
= RectHorAlign::Left
; break;
231 case AlignCenter
: meRectHorAlign
= RectHorAlign::Center
; break;
232 case AlignRight
: meRectHorAlign
= RectHorAlign::Right
; break;
235 GetFont() = rFormat
.GetFont(FNT_MATH
);
236 OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE
,
237 "unexpected CharSet" );
238 GetFont().SetWeight(WEIGHT_NORMAL
);
239 GetFont().SetItalic(ITALIC_NONE
);
241 ForEachNonNull(this, [&rFormat
, &rDocShell
](SmNode
*pNode
){pNode
->Prepare(rFormat
, rDocShell
);});
244 void SmNode::Move(const Point
& rPosition
)
246 if (rPosition
.X() == 0 && rPosition
.Y() == 0)
249 SmRect::Move(rPosition
);
251 ForEachNonNull(this, [&rPosition
](SmNode
*pNode
){pNode
->Move(rPosition
);});
255 void SmNode::CreateTextFromNode(OUString
&rText
)
257 sal_uInt16 nSize
= GetNumSubNodes();
260 ForEachNonNull(this, [&rText
](SmNode
*pNode
){pNode
->CreateTextFromNode(rText
);});
263 rText
= comphelper::string::stripEnd(rText
, ' ');
269 void SmNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong
/*nWidth*/)
274 void SmNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong
/*nHeight*/)
279 const SmNode
* SmNode::FindTokenAt(sal_uInt16 nRow
, sal_uInt16 nCol
) const
280 // returns (first) ** visible ** (sub)node with the tokens text at
281 // position 'nRow', 'nCol'.
282 //! (there should be exactly one such node if any)
285 && nRow
== GetToken().nRow
286 && nCol
>= GetToken().nCol
&& nCol
< GetToken().nCol
+ GetToken().aText
.getLength())
290 sal_uInt16 nNumSubNodes
= GetNumSubNodes();
291 for (sal_uInt16 i
= 0; i
< nNumSubNodes
; i
++)
292 { const SmNode
*pNode
= GetSubNode(i
);
297 const SmNode
*pResult
= pNode
->FindTokenAt(nRow
, nCol
);
307 const SmNode
* SmNode::FindRectClosestTo(const Point
&rPoint
) const
309 long nDist
= LONG_MAX
;
310 const SmNode
*pResult
= nullptr;
316 sal_uInt16 nNumSubNodes
= GetNumSubNodes();
317 for (sal_uInt16 i
= 0; i
< nNumSubNodes
; i
++)
318 { const SmNode
*pNode
= GetSubNode(i
);
324 const SmNode
*pFound
= pNode
->FindRectClosestTo(rPoint
);
325 if (pFound
&& (nTmp
= pFound
->OrientedDist(rPoint
)) < nDist
)
329 // quit immediately if 'rPoint' is inside the *should not
330 // overlap with other rectangles* part.
331 // This (partly) serves for getting the attributes in eg
332 // "bar overstrike a".
333 // ('nDist < 0' is used as *quick shot* to avoid evaluation of
334 // the following expression, where the result is already determined)
335 if (nDist
< 0 && pFound
->IsInsideRect(rPoint
))
344 const SmNode
* SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx
) const
346 const SmNode
*pResult
= nullptr;
348 sal_Int32 nIdx
= GetAccessibleIndex();
351 GetAccessibleText( aTxt
); // get text if used in following 'if' statement
354 && nIdx
<= nAccIdx
&& nAccIdx
< nIdx
+ aTxt
.getLength())
358 sal_uInt16 nNumSubNodes
= GetNumSubNodes();
359 for (sal_uInt16 i
= 0; i
< nNumSubNodes
; i
++)
361 const SmNode
*pNode
= GetSubNode(i
);
365 pResult
= pNode
->FindNodeWithAccessibleIndex(nAccIdx
);
375 SmStructureNode::~SmStructureNode()
377 ForEachNonNull(this, boost::checked_deleter
<SmNode
>());
381 void SmStructureNode::SetSubNodes(SmNode
*pFirst
, SmNode
*pSecond
, SmNode
*pThird
)
383 size_t nSize
= pThird
? 3 : (pSecond
? 2 : (pFirst
? 1 : 0));
384 aSubNodes
.resize( nSize
);
386 aSubNodes
[0] = pFirst
;
388 aSubNodes
[1] = pSecond
;
390 aSubNodes
[2] = pThird
;
396 void SmStructureNode::SetSubNodes(const SmNodeArray
&rNodeArray
)
398 aSubNodes
= rNodeArray
;
403 bool SmStructureNode::IsVisible() const
409 sal_uInt16
SmStructureNode::GetNumSubNodes() const
411 return sal::static_int_cast
<sal_uInt16
>(aSubNodes
.size());
415 SmNode
* SmStructureNode::GetSubNode(sal_uInt16 nIndex
)
417 return aSubNodes
[nIndex
];
421 void SmStructureNode::GetAccessibleText( OUStringBuffer
&rText
) const
423 ForEachNonNull(const_cast<SmStructureNode
*>(this),
424 [&rText
](SmNode
*pNode
)
426 if (pNode
->IsVisible())
427 pNode
->SetAccessibleIndex(rText
.getLength());
428 pNode
->GetAccessibleText( rText
);
433 void SmStructureNode::ClaimPaternity()
435 ForEachNonNull(this, [this](SmNode
*pNode
){pNode
->SetParent(this);});
439 bool SmVisibleNode::IsVisible() const
445 sal_uInt16
SmVisibleNode::GetNumSubNodes() const
451 SmNode
* SmVisibleNode::GetSubNode(sal_uInt16
/*nIndex*/)
456 void SmGraphicNode::GetAccessibleText( OUStringBuffer
&rText
) const
458 rText
.append(GetToken().aText
);
461 void SmExpressionNode::CreateTextFromNode(OUString
&rText
)
463 sal_uInt16 nSize
= GetNumSubNodes();
466 for (sal_uInt16 i
= 0; i
< nSize
; i
++)
468 SmNode
*pNode
= GetSubNode(i
);
471 pNode
->CreateTextFromNode(rText
);
472 //Just a bit of foo to make unary +asd -asd +-asd -+asd look nice
473 if (pNode
->GetType() == NMATH
)
475 ( !rText
.endsWith("+") && !rText
.endsWith("-") ))
482 rText
= comphelper::string::stripEnd(rText
, ' ');
487 void SmTableNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
488 // arranges all subnodes in one column
491 sal_uInt16 nSize
= GetNumSubNodes();
493 // make distance depend on font size
494 long nDist
= +(rFormat
.GetDistance(DIS_VERTICAL
)
495 * GetFont().GetFontSize().Height()) / 100L;
500 // arrange subnodes and get maximum width of them
504 for (i
= 0; i
< nSize
; i
++)
505 if (nullptr != (pNode
= GetSubNode(i
)))
506 { pNode
->Arrange(rDev
, rFormat
);
507 if ((nTmp
= pNode
->GetItalicWidth()) > nMaxWidth
)
512 SmRect::operator = (SmRect(nMaxWidth
, 1));
513 for (i
= 0; i
< nSize
; i
++)
514 { if (nullptr != (pNode
= GetSubNode(i
)))
515 { const SmRect
&rNodeRect
= pNode
->GetRect();
516 const SmNode
*pCoNode
= pNode
->GetLeftMost();
517 RectHorAlign eHorAlign
= pCoNode
->GetRectHorAlign();
519 aPos
= rNodeRect
.AlignTo(*this, RectPos::Bottom
,
520 eHorAlign
, RectVerAlign::Baseline
);
524 ExtendBy(rNodeRect
, nSize
> 1 ? RectCopyMBL::None
: RectCopyMBL::Arg
);
529 nFormulaBaseline
= GetBaseline();
532 SmTmpDevice
aTmpDev (rDev
, true);
533 aTmpDev
.SetFont(GetFont());
535 SmRect aRect
= (SmRect(aTmpDev
, &rFormat
, OUString("a"),
536 GetFont().GetBorderWidth()));
537 nFormulaBaseline
= GetAlignM();
538 // move from middle position by constant - distance
539 // between middle and baseline for single letter
540 nFormulaBaseline
+= aRect
.GetBaseline() - aRect
.GetAlignM();
544 const SmNode
* SmTableNode::GetLeftMost() const
550 long SmTableNode::GetFormulaBaseline() const
552 return nFormulaBaseline
;
556 /**************************************************************************/
559 void SmLineNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
561 SmNode::Prepare(rFormat
, rDocShell
);
563 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
564 // to the rest of the formula compared to the 'FNT_MATH' font.
565 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
566 Flags() |= FontChangeMask::Face
;
570 /**************************************************************************/
573 void SmLineNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
574 // arranges all subnodes in one row with some extra space between
577 sal_uInt16 nSize
= GetNumSubNodes();
579 for (i
= 0; i
< nSize
; i
++)
580 if (nullptr != (pNode
= GetSubNode(i
)))
581 pNode
->Arrange(rDev
, rFormat
);
583 SmTmpDevice
aTmpDev (rDev
, true);
584 aTmpDev
.SetFont(GetFont());
588 // provide an empty rectangle with alignment parameters for the "current"
589 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
590 // same sub-/supscript positions.)
591 //! be sure to use a character that has explicitly defined HiAttribut
592 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
594 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, OUString("a"),
595 GetFont().GetBorderWidth()));
596 // make sure that the rectangle occupies (almost) no space
598 SetItalicSpaces(0, 0);
602 // make distance depend on font size
603 long nDist
= (rFormat
.GetDistance(DIS_HORIZONTAL
) * GetFont().GetFontSize().Height()) / 100L;
604 if (!IsUseExtraSpaces())
608 // copy the first node into LineNode and extend by the others
609 if (nullptr != (pNode
= GetSubNode(0)))
610 SmRect::operator = (pNode
->GetRect());
612 for (i
= 1; i
< nSize
; i
++)
613 if (nullptr != (pNode
= GetSubNode(i
)))
615 aPos
= pNode
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
617 // add horizontal space to the left for each but the first sub node
621 ExtendBy( *pNode
, RectCopyMBL::Xor
);
626 /**************************************************************************/
629 void SmExpressionNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
630 // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
632 SmLineNode::Arrange(rDev
, rFormat
);
634 // copy alignment of leftmost subnode if any
635 const SmNode
*pNode
= GetLeftMost();
637 SetRectHorAlign(pNode
->GetRectHorAlign(), false);
641 /**************************************************************************/
644 void SmUnHorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
646 bool bIsPostfix
= GetToken().eType
== TFACT
;
648 SmNode
*pNode0
= GetSubNode(0),
649 *pNode1
= GetSubNode(1);
650 SmNode
*pOper
= bIsPostfix
? pNode1
: pNode0
,
651 *pBody
= bIsPostfix
? pNode0
: pNode1
;
655 pOper
->SetSize(Fraction (rFormat
.GetRelSize(SIZ_OPERATOR
), 100));
656 pOper
->Arrange(rDev
, rFormat
);
657 pBody
->Arrange(rDev
, rFormat
);
659 long nDist
= (pOper
->GetRect().GetWidth() * rFormat
.GetDistance(DIS_HORIZONTAL
)) / 100L;
661 SmRect::operator = (*pNode0
);
663 Point aPos
= pNode1
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
665 pNode1
->MoveTo(aPos
);
666 ExtendBy(*pNode1
, RectCopyMBL::Xor
);
670 /**************************************************************************/
673 void SmRootNode::GetHeightVerOffset(const SmRect
&rRect
,
674 long &rHeight
, long &rVerOffset
)
675 // calculate height and vertical offset of root sign suitable for 'rRect'
677 rVerOffset
= (rRect
.GetBottom() - rRect
.GetAlignB()) / 2;
678 rHeight
= rRect
.GetHeight() - rVerOffset
;
680 OSL_ENSURE(rHeight
>= 0, "Sm : Ooops...");
681 OSL_ENSURE(rVerOffset
>= 0, "Sm : Ooops...");
685 Point
SmRootNode::GetExtraPos(const SmRect
&rRootSymbol
,
686 const SmRect
&rExtra
)
688 const Size
&rSymSize
= rRootSymbol
.GetSize();
690 Point aPos
= rRootSymbol
.GetTopLeft()
691 + Point((rSymSize
.Width() * 70) / 100,
692 (rSymSize
.Height() * 52) / 100);
694 // from this calculate topleft edge of 'rExtra'
695 aPos
.X() -= rExtra
.GetWidth() + rExtra
.GetItalicRightSpace();
696 aPos
.Y() -= rExtra
.GetHeight();
697 // if there's enough space move a bit less to the right
698 // examples: "nroot i a", "nroot j a"
699 // (it looks better if we don't use italic-spaces here)
700 long nX
= rRootSymbol
.GetLeft() + (rSymSize
.Width() * 30) / 100;
708 void SmRootNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
710 //! pExtra needs to have the smaller index than pRootSym in order to
711 //! not to get the root symbol but the pExtra when clicking on it in the
712 //! GraphicWindow. (That is because of the simplicity of the algorithm
713 //! that finds the node corresponding to a mouseclick in the window.)
714 SmNode
*pExtra
= GetSubNode(0),
715 *pRootSym
= GetSubNode(1),
716 *pBody
= GetSubNode(2);
720 pBody
->Arrange(rDev
, rFormat
);
724 GetHeightVerOffset(*pBody
, nHeight
, nVerOffset
);
725 nHeight
+= rFormat
.GetDistance(DIS_ROOT
)
726 * GetFont().GetFontSize().Height() / 100L;
728 // font specialist advised to change the width first
729 pRootSym
->AdaptToY(rDev
, nHeight
);
730 pRootSym
->AdaptToX(rDev
, pBody
->GetItalicWidth());
732 pRootSym
->Arrange(rDev
, rFormat
);
734 Point aPos
= pRootSym
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, RectVerAlign::Baseline
);
735 //! override calculated vertical position
736 aPos
.Y() = pRootSym
->GetTop() + pBody
->GetBottom() - pRootSym
->GetBottom();
737 aPos
.Y() -= nVerOffset
;
738 pRootSym
->MoveTo(aPos
);
741 { pExtra
->SetSize(Fraction(rFormat
.GetRelSize(SIZ_INDEX
), 100));
742 pExtra
->Arrange(rDev
, rFormat
);
744 aPos
= GetExtraPos(*pRootSym
, *pExtra
);
745 pExtra
->MoveTo(aPos
);
748 SmRect::operator = (*pBody
);
749 ExtendBy(*pRootSym
, RectCopyMBL::This
);
751 ExtendBy(*pExtra
, RectCopyMBL::This
, true);
755 void SmRootNode::CreateTextFromNode(OUString
&rText
)
757 SmNode
*pExtra
= GetSubNode(0);
761 pExtra
->CreateTextFromNode(rText
);
766 if (!pExtra
&& GetSubNode(2)->GetNumSubNodes() > 1)
769 GetSubNode(2)->CreateTextFromNode(rText
);
771 if (!pExtra
&& GetSubNode(2)->GetNumSubNodes() > 1)
775 /**************************************************************************/
778 void SmBinHorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
780 SmNode
*pLeft
= LeftOperand(),
782 *pRight
= RightOperand();
787 pOper
->SetSize(Fraction (rFormat
.GetRelSize(SIZ_OPERATOR
), 100));
789 pLeft
->Arrange(rDev
, rFormat
);
790 pOper
->Arrange(rDev
, rFormat
);
791 pRight
->Arrange(rDev
, rFormat
);
793 const SmRect
&rOpRect
= pOper
->GetRect();
795 long nDist
= (rOpRect
.GetWidth() *
796 rFormat
.GetDistance(DIS_HORIZONTAL
)) / 100L;
798 SmRect::operator = (*pLeft
);
801 aPos
= pOper
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
804 ExtendBy(*pOper
, RectCopyMBL::Xor
);
806 aPos
= pRight
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
809 pRight
->MoveTo(aPos
);
810 ExtendBy(*pRight
, RectCopyMBL::Xor
);
814 /**************************************************************************/
817 void SmBinVerNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
819 SmNode
*pNum
= GetSubNode(0),
820 *pLine
= GetSubNode(1),
821 *pDenom
= GetSubNode(2);
826 bool bIsTextmode
= rFormat
.IsTextmode();
829 Fraction
aFraction(rFormat
.GetRelSize(SIZ_INDEX
), 100);
830 pNum
->SetSize(aFraction
);
831 pLine
->SetSize(aFraction
);
832 pDenom
->SetSize(aFraction
);
835 pNum
->Arrange(rDev
, rFormat
);
836 pDenom
->Arrange(rDev
, rFormat
);
838 long nFontHeight
= GetFont().GetFontSize().Height(),
839 nExtLen
= nFontHeight
* rFormat
.GetDistance(DIS_FRACTION
) / 100L,
840 nThick
= nFontHeight
* rFormat
.GetDistance(DIS_STROKEWIDTH
) / 100L,
841 nWidth
= std::max(pNum
->GetItalicWidth(), pDenom
->GetItalicWidth()),
842 nNumDist
= bIsTextmode
? 0 :
843 nFontHeight
* rFormat
.GetDistance(DIS_NUMERATOR
) / 100L,
844 nDenomDist
= bIsTextmode
? 0 :
845 nFontHeight
* rFormat
.GetDistance(DIS_DENOMINATOR
) / 100L;
847 // font specialist advised to change the width first
848 pLine
->AdaptToY(rDev
, nThick
);
849 pLine
->AdaptToX(rDev
, nWidth
+ 2 * nExtLen
);
850 pLine
->Arrange(rDev
, rFormat
);
852 // get horizontal alignment for numerator
853 const SmNode
*pLM
= pNum
->GetLeftMost();
854 RectHorAlign eHorAlign
= pLM
->GetRectHorAlign();
856 // move numerator to its position
857 Point aPos
= pNum
->AlignTo(*pLine
, RectPos::Top
, eHorAlign
, RectVerAlign::Baseline
);
858 aPos
.Y() -= nNumDist
;
861 // get horizontal alignment for denominator
862 pLM
= pDenom
->GetLeftMost();
863 eHorAlign
= pLM
->GetRectHorAlign();
865 // move denominator to its position
866 aPos
= pDenom
->AlignTo(*pLine
, RectPos::Bottom
, eHorAlign
, RectVerAlign::Baseline
);
867 aPos
.Y() += nDenomDist
;
868 pDenom
->MoveTo(aPos
);
870 SmRect::operator = (*pNum
);
871 ExtendBy(*pDenom
, RectCopyMBL::None
).ExtendBy(*pLine
, RectCopyMBL::None
, pLine
->GetCenterY());
874 void SmBinVerNode::CreateTextFromNode(OUString
&rText
)
876 SmNode
*pNum
= GetSubNode(0),
877 *pDenom
= GetSubNode(2);
878 pNum
->CreateTextFromNode(rText
);
880 pDenom
->CreateTextFromNode(rText
);
884 const SmNode
* SmBinVerNode::GetLeftMost() const
892 /// @return value of the determinant formed by the two points
893 double Det(const Point
&rHeading1
, const Point
&rHeading2
)
895 return rHeading1
.X() * rHeading2
.Y() - rHeading1
.Y() * rHeading2
.X();
899 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
900 /// and has the direction vector 'rHeading2'
901 bool IsPointInLine(const Point
&rPoint1
,
902 const Point
&rPoint2
, const Point
&rHeading2
)
904 OSL_ENSURE(rHeading2
!= Point(), "Sm : 0 vector");
907 static const double eps
= 5.0 * DBL_EPSILON
;
910 if (labs(rHeading2
.X()) > labs(rHeading2
.Y()))
912 fLambda
= (rPoint1
.X() - rPoint2
.X()) / static_cast<double>(rHeading2
.X());
913 bRes
= fabs(rPoint1
.Y() - (rPoint2
.Y() + fLambda
* rHeading2
.Y())) < eps
;
917 fLambda
= (rPoint1
.Y() - rPoint2
.Y()) / static_cast<double>(rHeading2
.Y());
918 bRes
= fabs(rPoint1
.X() - (rPoint2
.X() + fLambda
* rHeading2
.X())) < eps
;
925 sal_uInt16
GetLineIntersectionPoint(Point
&rResult
,
926 const Point
& rPoint1
, const Point
&rHeading1
,
927 const Point
& rPoint2
, const Point
&rHeading2
)
929 OSL_ENSURE(rHeading1
!= Point(), "Sm : 0 vector");
930 OSL_ENSURE(rHeading2
!= Point(), "Sm : 0 vector");
933 static const double eps
= 5.0 * DBL_EPSILON
;
935 // are the direction vectors linearly dependent?
936 double fDet
= Det(rHeading1
, rHeading2
);
937 if (fabs(fDet
) < eps
)
939 nRes
= IsPointInLine(rPoint1
, rPoint2
, rHeading2
) ? USHRT_MAX
: 0;
940 rResult
= nRes
? rPoint1
: Point();
944 // here we do not pay attention to the computational accuracy
945 // (that would be more complicated and is not really worth it in this case)
946 double fLambda
= ( (rPoint1
.Y() - rPoint2
.Y()) * rHeading2
.X()
947 - (rPoint1
.X() - rPoint2
.X()) * rHeading2
.Y())
949 rResult
= Point(rPoint1
.X() + static_cast<long>(fLambda
* rHeading1
.X()),
950 rPoint1
.Y() + static_cast<long>(fLambda
* rHeading1
.Y()));
959 SmBinDiagonalNode::SmBinDiagonalNode(const SmToken
&rNodeToken
)
960 : SmStructureNode(NBINDIAGONAL
, rNodeToken
, 3)
966 /// @return position and size of the diagonal line
967 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
968 void SmBinDiagonalNode::GetOperPosSize(Point
&rPos
, Size
&rSize
,
969 const Point
&rDiagPoint
, double fAngleDeg
) const
972 static const double fPi
= 3.1415926535897932384626433;
973 double fAngleRad
= fAngleDeg
/ 180.0 * fPi
;
974 long nRectLeft
= GetItalicLeft(),
975 nRectRight
= GetItalicRight(),
977 nRectBottom
= GetBottom();
978 Point
aRightHdg (100, 0),
980 aDiagHdg ( static_cast<long>(100.0 * cos(fAngleRad
)),
981 static_cast<long>(-100.0 * sin(fAngleRad
)) );
983 long nLeft
, nRight
, nTop
, nBottom
; // margins of the rectangle for the diagonal
987 // determine top right corner
988 GetLineIntersectionPoint(aPoint
,
989 Point(nRectLeft
, nRectTop
), aRightHdg
,
990 rDiagPoint
, aDiagHdg
);
991 // is there a point of intersection with the top border?
992 if (aPoint
.X() <= nRectRight
)
999 // there has to be a point of intersection with the right border!
1000 GetLineIntersectionPoint(aPoint
,
1001 Point(nRectRight
, nRectTop
), aDownHdg
,
1002 rDiagPoint
, aDiagHdg
);
1004 nRight
= nRectRight
;
1008 // determine bottom left corner
1009 GetLineIntersectionPoint(aPoint
,
1010 Point(nRectLeft
, nRectBottom
), aRightHdg
,
1011 rDiagPoint
, aDiagHdg
);
1012 // is there a point of intersection with the bottom border?
1013 if (aPoint
.X() >= nRectLeft
)
1016 nBottom
= nRectBottom
;
1020 // there has to be a point of intersection with the left border!
1021 GetLineIntersectionPoint(aPoint
,
1022 Point(nRectLeft
, nRectTop
), aDownHdg
,
1023 rDiagPoint
, aDiagHdg
);
1026 nBottom
= aPoint
.Y();
1031 // determine top left corner
1032 GetLineIntersectionPoint(aPoint
,
1033 Point(nRectLeft
, nRectTop
), aRightHdg
,
1034 rDiagPoint
, aDiagHdg
);
1035 // is there a point of intersection with the top border?
1036 if (aPoint
.X() >= nRectLeft
)
1043 // there has to be a point of intersection with the left border!
1044 GetLineIntersectionPoint(aPoint
,
1045 Point(nRectLeft
, nRectTop
), aDownHdg
,
1046 rDiagPoint
, aDiagHdg
);
1052 // determine bottom right corner
1053 GetLineIntersectionPoint(aPoint
,
1054 Point(nRectLeft
, nRectBottom
), aRightHdg
,
1055 rDiagPoint
, aDiagHdg
);
1056 // is there a point of intersection with the bottom border?
1057 if (aPoint
.X() <= nRectRight
)
1059 nRight
= aPoint
.X();
1060 nBottom
= nRectBottom
;
1064 // there has to be a point of intersection with the right border!
1065 GetLineIntersectionPoint(aPoint
,
1066 Point(nRectRight
, nRectTop
), aDownHdg
,
1067 rDiagPoint
, aDiagHdg
);
1069 nRight
= nRectRight
;
1070 nBottom
= aPoint
.Y();
1074 rSize
= Size(nRight
- nLeft
+ 1, nBottom
- nTop
+ 1);
1080 void SmBinDiagonalNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1082 // Both arguments have to get into the SubNodes before the Operator so that clicking
1083 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1084 SmNode
*pLeft
= GetSubNode(0),
1085 *pRight
= GetSubNode(1),
1086 *pLine
= GetSubNode(2);
1089 assert(pLine
&& pLine
->GetType() == NPOLYLINE
);
1091 SmPolyLineNode
*pOper
= static_cast<SmPolyLineNode
*>(pLine
);
1094 //! some routines being called extract some info from the OutputDevice's
1095 //! font (eg the space to be used for borders OR the font name(!!)).
1096 //! Thus the font should reflect the needs and has to be set!
1097 SmTmpDevice
aTmpDev (rDev
, true);
1098 aTmpDev
.SetFont(GetFont());
1100 pLeft
->Arrange(aTmpDev
, rFormat
);
1101 pRight
->Arrange(aTmpDev
, rFormat
);
1103 // determine implicitly the values (incl. the margin) of the diagonal line
1104 pOper
->Arrange(aTmpDev
, rFormat
);
1106 long nDelta
= pOper
->GetWidth() * 8 / 10;
1108 // determine TopLeft position from the right argument
1110 aPos
.X() = pLeft
->GetItalicRight() + nDelta
+ pRight
->GetItalicLeftSpace();
1112 aPos
.Y() = pLeft
->GetBottom() + nDelta
;
1114 aPos
.Y() = pLeft
->GetTop() - nDelta
- pRight
->GetHeight();
1116 pRight
->MoveTo(aPos
);
1118 // determine new baseline
1119 long nTmpBaseline
= IsAscending() ? (pLeft
->GetBottom() + pRight
->GetTop()) / 2
1120 : (pLeft
->GetTop() + pRight
->GetBottom()) / 2;
1121 Point
aLogCenter ((pLeft
->GetItalicRight() + pRight
->GetItalicLeft()) / 2,
1124 SmRect::operator = (*pLeft
);
1125 ExtendBy(*pRight
, RectCopyMBL::None
);
1128 // determine position and size of diagonal line
1130 GetOperPosSize(aPos
, aTmpSize
, aLogCenter
, IsAscending() ? 60.0 : -60.0);
1132 // font specialist advised to change the width first
1133 pOper
->AdaptToY(aTmpDev
, aTmpSize
.Height());
1134 pOper
->AdaptToX(aTmpDev
, aTmpSize
.Width());
1135 // and make it active
1136 pOper
->Arrange(aTmpDev
, rFormat
);
1138 pOper
->MoveTo(aPos
);
1140 ExtendBy(*pOper
, RectCopyMBL::None
, nTmpBaseline
);
1144 /**************************************************************************/
1147 void SmSubSupNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1149 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES
,
1150 "Sm: wrong number of subnodes");
1152 SmNode
*pBody
= GetBody();
1155 long nOrigHeight
= pBody
->GetFont().GetFontSize().Height();
1157 pBody
->Arrange(rDev
, rFormat
);
1159 const SmRect
&rBodyRect
= pBody
->GetRect();
1160 SmRect::operator = (rBodyRect
);
1162 // line that separates sub- and supscript rectangles
1163 long nDelimLine
= SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1168 // iterate over all possible sub-/supscripts
1169 SmRect
aTmpRect (rBodyRect
);
1170 for (int i
= 0; i
< SUBSUP_NUM_ENTRIES
; i
++)
1172 SmSubSup eSubSup
= static_cast<SmSubSup
>(i
);
1173 SmNode
*pSubSup
= GetSubSup(eSubSup
);
1178 // switch position of limits if we are in textmode
1179 if (rFormat
.IsTextmode() && (GetToken().nGroup
& TG::Limit
))
1181 { case CSUB
: eSubSup
= RSUB
; break;
1182 case CSUP
: eSubSup
= RSUP
; break;
1187 // prevent sub-/supscripts from diminishing in size
1188 // (as would be in "a_{1_{2_{3_4}}}")
1189 if (GetFont().GetFontSize().Height() > rFormat
.GetBaseSize().Height() / 3)
1191 sal_uInt16 nIndex
= (eSubSup
== CSUB
|| eSubSup
== CSUP
) ?
1192 SIZ_LIMITS
: SIZ_INDEX
;
1193 Fraction
aFraction ( rFormat
.GetRelSize(nIndex
), 100 );
1194 pSubSup
->SetSize(aFraction
);
1197 pSubSup
->Arrange(rDev
, rFormat
);
1199 bool bIsTextmode
= rFormat
.IsTextmode();
1202 //! be sure that CSUB, CSUP are handled before the other cases!
1208 * rFormat
.GetDistance(DIS_SUBSCRIPT
) / 100L;
1209 aPos
= pSubSup
->GetRect().AlignTo(aTmpRect
,
1210 eSubSup
== LSUB
? RectPos::Left
: RectPos::Right
,
1211 RectHorAlign::Center
, RectVerAlign::Bottom
);
1213 nDelta
= nDelimLine
- aPos
.Y();
1221 * rFormat
.GetDistance(DIS_SUPERSCRIPT
) / 100L;
1222 aPos
= pSubSup
->GetRect().AlignTo(aTmpRect
,
1223 eSubSup
== LSUP
? RectPos::Left
: RectPos::Right
,
1224 RectHorAlign::Center
, RectVerAlign::Top
);
1226 nDelta
= aPos
.Y() + pSubSup
->GetHeight() - nDelimLine
;
1233 * rFormat
.GetDistance(DIS_LOWERLIMIT
) / 100L;
1234 aPos
= pSubSup
->GetRect().AlignTo(rBodyRect
, RectPos::Bottom
,
1235 RectHorAlign::Center
, RectVerAlign::Baseline
);
1241 * rFormat
.GetDistance(DIS_UPPERLIMIT
) / 100L;
1242 aPos
= pSubSup
->GetRect().AlignTo(rBodyRect
, RectPos::Top
,
1243 RectHorAlign::Center
, RectVerAlign::Baseline
);
1248 pSubSup
->MoveTo(aPos
);
1249 ExtendBy(*pSubSup
, RectCopyMBL::This
, true);
1251 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1252 // will be aligned to
1253 if (eSubSup
== CSUB
|| eSubSup
== CSUP
)
1258 void SmSubSupNode::CreateTextFromNode(OUString
&rText
)
1261 GetSubNode(0)->CreateTextFromNode(rText
);
1263 if (nullptr != (pNode
= GetSubNode(LSUB
+1)))
1266 pNode
->CreateTextFromNode(rText
);
1268 if (nullptr != (pNode
= GetSubNode(LSUP
+1)))
1271 pNode
->CreateTextFromNode(rText
);
1273 if (nullptr != (pNode
= GetSubNode(CSUB
+1)))
1276 pNode
->CreateTextFromNode(rText
);
1278 if (nullptr != (pNode
= GetSubNode(CSUP
+1)))
1281 pNode
->CreateTextFromNode(rText
);
1283 if (nullptr != (pNode
= GetSubNode(RSUB
+1)))
1285 rText
= comphelper::string::stripEnd(rText
, ' ');
1287 pNode
->CreateTextFromNode(rText
);
1289 if (nullptr != (pNode
= GetSubNode(RSUP
+1)))
1291 rText
= comphelper::string::stripEnd(rText
, ' ');
1293 pNode
->CreateTextFromNode(rText
);
1298 /**************************************************************************/
1300 void SmBraceNode::CreateTextFromNode(OUString
&rText
)
1302 if (GetScaleMode() == SCALE_HEIGHT
)
1306 OpeningBrace()->CreateTextFromNode(aStr
);
1307 aStr
= comphelper::string::strip(aStr
, ' ');
1308 aStr
= comphelper::string::stripStart(aStr
, '\\');
1309 if (!aStr
.isEmpty())
1311 if (aStr
== "divides")
1313 else if (aStr
== "parallel")
1315 else if (aStr
== "<")
1324 Body()->CreateTextFromNode(rText
);
1325 if (GetScaleMode() == SCALE_HEIGHT
)
1329 ClosingBrace()->CreateTextFromNode(aStr
);
1330 aStr
= comphelper::string::strip(aStr
, ' ');
1331 aStr
= comphelper::string::stripStart(aStr
, '\\');
1332 if (!aStr
.isEmpty())
1334 if (aStr
== "divides")
1336 else if (aStr
== "parallel")
1338 else if (aStr
== ">")
1351 void SmBraceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1353 SmNode
*pLeft
= OpeningBrace(),
1355 *pRight
= ClosingBrace();
1360 pBody
->Arrange(rDev
, rFormat
);
1362 bool bIsScaleNormal
= rFormat
.IsScaleNormalBrackets(),
1363 bScale
= pBody
->GetHeight() > 0 &&
1364 (GetScaleMode() == SCALE_HEIGHT
|| bIsScaleNormal
),
1365 bIsABS
= GetToken().eType
== TABS
;
1367 long nFaceHeight
= GetFont().GetFontSize().Height();
1369 // determine oversize in %
1370 sal_uInt16 nPerc
= 0;
1371 if (!bIsABS
&& bScale
)
1372 { // in case of oversize braces...
1373 sal_uInt16 nIndex
= GetScaleMode() == SCALE_HEIGHT
?
1374 DIS_BRACKETSIZE
: DIS_NORMALBRACKETSIZE
;
1375 nPerc
= rFormat
.GetDistance(nIndex
);
1378 // determine the height for the braces
1382 nBraceHeight
= pBody
->GetType() == NBRACEBODY
?
1383 static_cast<SmBracebodyNode
*>(pBody
)->GetBodyHeight()
1384 : pBody
->GetHeight();
1385 nBraceHeight
+= 2 * (nBraceHeight
* nPerc
/ 100L);
1388 nBraceHeight
= nFaceHeight
;
1390 // distance to the argument
1391 nPerc
= bIsABS
? 0 : rFormat
.GetDistance(DIS_BRACKETSPACE
);
1392 long nDist
= nFaceHeight
* nPerc
/ 100L;
1394 // if wanted, scale the braces to the wanted size
1397 Size
aTmpSize (pLeft
->GetFont().GetFontSize());
1398 OSL_ENSURE(pRight
->GetFont().GetFontSize() == aTmpSize
,
1399 "Sm : different font sizes");
1400 aTmpSize
.Width() = std::min(nBraceHeight
* 60L / 100L,
1401 rFormat
.GetBaseSize().Height() * 3L / 2L);
1402 // correction factor since change from StarMath to OpenSymbol font
1403 // because of the different font width in the FontMetric
1404 aTmpSize
.Width() *= 182;
1405 aTmpSize
.Width() /= 267;
1407 sal_Unicode cChar
= pLeft
->GetToken().cMathChar
;
1408 if (cChar
!= MS_LINE
&& cChar
!= MS_DLINE
&&
1409 cChar
!= MS_VERTLINE
&& cChar
!= MS_DVERTLINE
)
1410 pLeft
->GetFont().SetSize(aTmpSize
);
1412 cChar
= pRight
->GetToken().cMathChar
;
1413 if (cChar
!= MS_LINE
&& cChar
!= MS_DLINE
&&
1414 cChar
!= MS_VERTLINE
&& cChar
!= MS_DVERTLINE
)
1415 pRight
->GetFont().SetSize(aTmpSize
);
1417 pLeft
->AdaptToY(rDev
, nBraceHeight
);
1418 pRight
->AdaptToY(rDev
, nBraceHeight
);
1421 pLeft
->Arrange(rDev
, rFormat
);
1422 pRight
->Arrange(rDev
, rFormat
);
1424 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1425 RectVerAlign eVerAlign
= bScale
? RectVerAlign::CenterY
: RectVerAlign::Baseline
;
1428 aPos
= pLeft
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, eVerAlign
);
1430 pLeft
->MoveTo(aPos
);
1432 aPos
= pRight
->AlignTo(*pBody
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
);
1434 pRight
->MoveTo(aPos
);
1436 SmRect::operator = (*pBody
);
1437 ExtendBy(*pLeft
, RectCopyMBL::This
).ExtendBy(*pRight
, RectCopyMBL::This
);
1441 /**************************************************************************/
1444 void SmBracebodyNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1446 sal_uInt16 nNumSubNodes
= GetNumSubNodes();
1447 if (nNumSubNodes
== 0)
1450 // arrange arguments
1452 for (i
= 0; i
< nNumSubNodes
; i
+= 2)
1453 GetSubNode(i
)->Arrange(rDev
, rFormat
);
1455 // build reference rectangle with necessary info for vertical alignment
1456 SmRect
aRefRect (*GetSubNode(0));
1457 for (i
= 0; i
< nNumSubNodes
; i
+= 2)
1459 SmRect
aTmpRect (*GetSubNode(i
));
1460 Point aPos
= aTmpRect
.AlignTo(aRefRect
, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1461 aTmpRect
.MoveTo(aPos
);
1462 aRefRect
.ExtendBy(aTmpRect
, RectCopyMBL::Xor
);
1465 nBodyHeight
= aRefRect
.GetHeight();
1467 // scale separators to required height and arrange them
1468 bool bScale
= GetScaleMode() == SCALE_HEIGHT
|| rFormat
.IsScaleNormalBrackets();
1469 long nHeight
= bScale
? aRefRect
.GetHeight() : GetFont().GetFontSize().Height();
1470 sal_uInt16 nIndex
= GetScaleMode() == SCALE_HEIGHT
?
1471 DIS_BRACKETSIZE
: DIS_NORMALBRACKETSIZE
;
1472 sal_uInt16 nPerc
= rFormat
.GetDistance(nIndex
);
1474 nHeight
+= 2 * (nHeight
* nPerc
/ 100L);
1475 for (i
= 1; i
< nNumSubNodes
; i
+= 2)
1477 SmNode
*pNode
= GetSubNode(i
);
1478 pNode
->AdaptToY(rDev
, nHeight
);
1479 pNode
->Arrange(rDev
, rFormat
);
1482 // horizontal distance between argument and brackets or separators
1483 long nDist
= GetFont().GetFontSize().Height()
1484 * rFormat
.GetDistance(DIS_BRACKETSPACE
) / 100L;
1486 SmNode
*pLeft
= GetSubNode(0);
1487 SmRect::operator = (*pLeft
);
1488 for (i
= 1; i
< nNumSubNodes
; i
++)
1490 bool bIsSeparator
= i
% 2 != 0;
1491 RectVerAlign eVerAlign
= bIsSeparator
? RectVerAlign::CenterY
: RectVerAlign::Baseline
;
1493 SmNode
*pRight
= GetSubNode(i
);
1494 Point aPosX
= pRight
->AlignTo(*pLeft
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
),
1495 aPosY
= pRight
->AlignTo(aRefRect
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
);
1498 pRight
->MoveTo(Point(aPosX
.X(), aPosY
.Y()));
1499 ExtendBy(*pRight
, bIsSeparator
? RectCopyMBL::This
: RectCopyMBL::Xor
);
1506 /**************************************************************************/
1509 void SmVerticalBraceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1511 SmNode
*pBody
= Body(),
1513 *pScript
= Script();
1518 SmTmpDevice
aTmpDev (rDev
, true);
1519 aTmpDev
.SetFont(GetFont());
1521 pBody
->Arrange(aTmpDev
, rFormat
);
1523 // size is the same as for limits for this part
1524 pScript
->SetSize( Fraction( rFormat
.GetRelSize(SIZ_LIMITS
), 100 ) );
1525 // braces are a bit taller than usually
1526 pBrace
->SetSize( Fraction(3, 2) );
1528 long nItalicWidth
= pBody
->GetItalicWidth();
1529 if (nItalicWidth
> 0)
1530 pBrace
->AdaptToX(aTmpDev
, nItalicWidth
);
1532 pBrace
->Arrange(aTmpDev
, rFormat
);
1533 pScript
->Arrange(aTmpDev
, rFormat
);
1535 // determine the relative position and the distances between each other
1537 long nFontHeight
= pBody
->GetFont().GetFontSize().Height();
1538 long nDistBody
= nFontHeight
* rFormat
.GetDistance(DIS_ORNAMENTSIZE
),
1539 nDistScript
= nFontHeight
;
1540 if (GetToken().eType
== TOVERBRACE
)
1542 eRectPos
= RectPos::Top
;
1543 nDistBody
= - nDistBody
;
1544 nDistScript
*= - rFormat
.GetDistance(DIS_UPPERLIMIT
);
1548 eRectPos
= RectPos::Bottom
;
1549 nDistScript
*= + rFormat
.GetDistance(DIS_LOWERLIMIT
);
1554 Point aPos
= pBrace
->AlignTo(*pBody
, eRectPos
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1555 aPos
.Y() += nDistBody
;
1556 pBrace
->MoveTo(aPos
);
1558 aPos
= pScript
->AlignTo(*pBrace
, eRectPos
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1559 aPos
.Y() += nDistScript
;
1560 pScript
->MoveTo(aPos
);
1562 SmRect::operator = (*pBody
);
1563 ExtendBy(*pBrace
, RectCopyMBL::This
).ExtendBy(*pScript
, RectCopyMBL::This
);
1567 /**************************************************************************/
1570 SmNode
* SmOperNode::GetSymbol()
1572 SmNode
*pNode
= GetSubNode(0);
1575 if (pNode
->GetType() == NSUBSUP
)
1576 pNode
= static_cast<SmSubSupNode
*>(pNode
)->GetBody();
1578 OSL_ENSURE(pNode
, "Sm: NULL pointer!");
1583 long SmOperNode::CalcSymbolHeight(const SmNode
&rSymbol
,
1584 const SmFormat
&rFormat
) const
1585 // returns the font height to be used for operator-symbol
1587 long nHeight
= GetFont().GetFontSize().Height();
1589 SmTokenType eTmpType
= GetToken().eType
;
1590 if (eTmpType
== TLIM
|| eTmpType
== TLIMINF
|| eTmpType
== TLIMSUP
)
1593 if (!rFormat
.IsTextmode())
1595 // set minimum size ()
1596 nHeight
+= (nHeight
* 20L) / 100L;
1599 * rFormat
.GetDistance(DIS_OPERATORSIZE
) / 100L;
1600 nHeight
= nHeight
* 686L / 845L;
1603 // correct user-defined symbols to match height of sum from used font
1604 if (rSymbol
.GetToken().eType
== TSPECIAL
)
1605 nHeight
= nHeight
* 845L / 686L;
1611 void SmOperNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1613 SmNode
*pOper
= GetSubNode(0);
1614 SmNode
*pBody
= GetSubNode(1);
1619 SmNode
*pSymbol
= GetSymbol();
1620 pSymbol
->SetSize(Fraction(CalcSymbolHeight(*pSymbol
, rFormat
),
1621 pSymbol
->GetFont().GetFontSize().Height()));
1623 pBody
->Arrange(rDev
, rFormat
);
1624 bool bDynamicallySized
= false;
1625 if (pSymbol
->GetToken().eType
== TINTD
)
1627 long nBodyHeight
= pBody
->GetHeight();
1628 long nFontHeight
= pSymbol
->GetFont().GetFontSize().Height();
1629 if (nFontHeight
< nBodyHeight
)
1631 pSymbol
->SetSize(Fraction(nBodyHeight
, nFontHeight
));
1632 bDynamicallySized
= true;
1635 pOper
->Arrange(rDev
, rFormat
);
1637 long nOrigHeight
= GetFont().GetFontSize().Height(),
1639 * rFormat
.GetDistance(DIS_OPERATORSPACE
) / 100L;
1641 Point aPos
= pOper
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, bDynamicallySized
? RectVerAlign::CenterY
: RectVerAlign::Mid
);
1643 pOper
->MoveTo(aPos
);
1645 SmRect::operator = (*pBody
);
1646 ExtendBy(*pOper
, RectCopyMBL::This
);
1650 /**************************************************************************/
1653 void SmAlignNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1654 // set alignment within the entire subtree (including current node)
1656 assert(GetNumSubNodes() == 1);
1658 SmNode
*pNode
= GetSubNode(0);
1661 RectHorAlign eHorAlign
= RectHorAlign::Center
;
1662 switch (GetToken().eType
)
1664 case TALIGNL
: eHorAlign
= RectHorAlign::Left
; break;
1665 case TALIGNC
: eHorAlign
= RectHorAlign::Center
; break;
1666 case TALIGNR
: eHorAlign
= RectHorAlign::Right
; break;
1670 SetRectHorAlign(eHorAlign
);
1672 pNode
->Arrange(rDev
, rFormat
);
1674 SmRect::operator = (pNode
->GetRect());
1678 /**************************************************************************/
1681 void SmAttributNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1683 SmNode
*pAttr
= Attribute(),
1688 pBody
->Arrange(rDev
, rFormat
);
1690 if (GetScaleMode() == SCALE_WIDTH
)
1691 pAttr
->AdaptToX(rDev
, pBody
->GetItalicWidth());
1692 pAttr
->Arrange(rDev
, rFormat
);
1694 // get relative position of attribute
1695 RectVerAlign eVerAlign
;
1697 switch (GetToken().eType
)
1699 eVerAlign
= RectVerAlign::AttributeLo
;
1702 eVerAlign
= RectVerAlign::AttributeMid
;
1705 eVerAlign
= RectVerAlign::AttributeHi
;
1706 if (pBody
->GetType() == NATTRIBUT
)
1707 nDist
= GetFont().GetFontSize().Height()
1708 * rFormat
.GetDistance(DIS_ORNAMENTSPACE
) / 100L;
1710 Point aPos
= pAttr
->AlignTo(*pBody
, RectPos::Attribute
, RectHorAlign::Center
, eVerAlign
);
1712 pAttr
->MoveTo(aPos
);
1714 SmRect::operator = (*pBody
);
1715 ExtendBy(*pAttr
, RectCopyMBL::This
, true);
1718 void SmFontNode::CreateTextFromNode(OUString
&rText
)
1720 switch (GetToken().eType
)
1732 rText
+= "nitalic ";
1735 rText
+= "phantom ";
1742 case FontSizeType::PLUS
:
1745 case FontSizeType::MINUS
:
1748 case FontSizeType::MULTIPLY
:
1751 case FontSizeType::DIVIDE
:
1754 case FontSizeType::ABSOLUT
:
1758 rText
+= ::rtl::math::doubleToUString(
1759 static_cast<double>(aFontSize
),
1760 rtl_math_StringFormat_Automatic
,
1761 rtl_math_DecimalPlaces_Max
, '.', true);
1766 rText
+= "color black ";
1769 rText
+= "color white ";
1772 rText
+= "color red ";
1775 rText
+= "color green ";
1778 rText
+= "color blue ";
1781 rText
+= "color cyan ";
1784 rText
+= "color magenta ";
1787 rText
+= "color yellow ";
1790 rText
+= "color teal ";
1793 rText
+= "color silver ";
1796 rText
+= "color gray ";
1799 rText
+= "color maroon ";
1802 rText
+= "color purple ";
1805 rText
+= "color lime ";
1808 rText
+= "color olive ";
1811 rText
+= "color navy ";
1814 rText
+= "color aqua ";
1817 rText
+= "color fuchsia ";
1820 rText
+= "font sans ";
1823 rText
+= "font serif ";
1826 rText
+= "font fixed ";
1831 if(GetNumSubNodes() > 1)
1832 GetSubNode(1)->CreateTextFromNode(rText
);
1835 void SmFontNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
1837 //! prepare subnodes first
1838 SmNode::Prepare(rFormat
, rDocShell
);
1841 switch (GetToken().eType
)
1843 case TFIXED
: nFnt
= FNT_FIXED
; break;
1844 case TSANS
: nFnt
= FNT_SANS
; break;
1845 case TSERIF
: nFnt
= FNT_SERIF
; break;
1850 { GetFont() = rFormat
.GetFont( sal::static_int_cast
< sal_uInt16
>(nFnt
) );
1854 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
1855 //! other font nodes (those with lower depth in the tree)
1856 Flags() |= FontChangeMask::Face
;
1859 void SmFontNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1861 SmNode
*pNode
= GetSubNode(1);
1864 switch (GetToken().eType
)
1866 pNode
->SetFontSize(aFontSize
, nSizeType
);
1871 pNode
->SetFont(GetFont());
1873 case TUNKNOWN
: break; // no assertion on "font <?> <?>"
1875 case TPHANTOM
: SetPhantom(true); break;
1876 case TBOLD
: SetAttribut(FontAttribute::Bold
); break;
1877 case TITALIC
: SetAttribut(FontAttribute::Italic
); break;
1878 case TNBOLD
: ClearAttribut(FontAttribute::Bold
); break;
1879 case TNITALIC
: ClearAttribut(FontAttribute::Italic
); break;
1881 case TBLACK
: SetColor(Color(COL_BLACK
)); break;
1882 case TWHITE
: SetColor(Color(COL_WHITE
)); break;
1883 case TRED
: SetColor(Color(COL_LIGHTRED
)); break;
1884 case TGREEN
: SetColor(Color(COL_GREEN
)); break;
1885 case TBLUE
: SetColor(Color(COL_LIGHTBLUE
)); break;
1886 case TCYAN
: SetColor(Color(COL_LIGHTCYAN
)); break; // as in Calc
1887 case TMAGENTA
: SetColor(Color(COL_LIGHTMAGENTA
)); break; // as in Calc
1888 case TYELLOW
: SetColor(Color(COL_YELLOW
)); break;
1889 case TTEAL
: SetColor(Color(COL_CYAN
)); break;
1890 case TSILVER
: SetColor(Color(COL_LIGHTGRAY
)); break;
1891 case TGRAY
: SetColor(Color(COL_GRAY
)); break;
1892 case TMAROON
: SetColor(Color(COL_RED
)); break;
1893 case TPURPLE
: SetColor(Color(COL_MAGENTA
)); break;
1894 case TLIME
: SetColor(Color(COL_LIGHTGREEN
)); break;
1895 case TOLIVE
: SetColor(Color(COL_BROWN
)); break;
1896 case TNAVY
: SetColor(Color(COL_BLUE
)); break;
1897 case TAQUA
: SetColor(Color(COL_LIGHTCYAN
)); break;
1898 case TFUCHSIA
: SetColor(Color(COL_LIGHTMAGENTA
)); break;
1901 SAL_WARN("starmath", "unknown case");
1904 pNode
->Arrange(rDev
, rFormat
);
1906 SmRect::operator = (pNode
->GetRect());
1910 void SmFontNode::SetSizeParameter(const Fraction
& rValue
, FontSizeType Type
)
1917 /**************************************************************************/
1920 SmPolyLineNode::SmPolyLineNode(const SmToken
&rNodeToken
)
1921 : SmGraphicNode(NPOLYLINE
, rNodeToken
)
1928 void SmPolyLineNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong nNewWidth
)
1930 aToSize
.Width() = nNewWidth
;
1934 void SmPolyLineNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong nNewHeight
)
1936 GetFont().FreezeBorderWidth();
1937 aToSize
.Height() = nNewHeight
;
1941 void SmPolyLineNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1943 //! some routines being called extract some info from the OutputDevice's
1944 //! font (eg the space to be used for borders OR the font name(!!)).
1945 //! Thus the font should reflect the needs and has to be set!
1946 SmTmpDevice
aTmpDev (rDev
, true);
1947 aTmpDev
.SetFont(GetFont());
1949 long nBorderwidth
= GetFont().GetBorderWidth();
1951 // create polygon using both endpoints
1952 OSL_ENSURE(aPoly
.GetSize() == 2, "Sm : wrong number of points");
1953 Point aPointA
, aPointB
;
1954 if (GetToken().eType
== TWIDESLASH
)
1956 aPointA
.X() = nBorderwidth
;
1957 aPointA
.Y() = aToSize
.Height() - nBorderwidth
;
1958 aPointB
.X() = aToSize
.Width() - nBorderwidth
;
1959 aPointB
.Y() = nBorderwidth
;
1963 OSL_ENSURE(GetToken().eType
== TWIDEBACKSLASH
, "Sm : unexpected token");
1965 aPointA
.Y() = nBorderwidth
;
1966 aPointB
.X() = aToSize
.Width() - nBorderwidth
;
1967 aPointB
.Y() = aToSize
.Height() - nBorderwidth
;
1969 aPoly
.SetPoint(aPointA
, 0);
1970 aPoly
.SetPoint(aPointB
, 1);
1972 long nThick
= GetFont().GetFontSize().Height()
1973 * rFormat
.GetDistance(DIS_STROKEWIDTH
) / 100L;
1974 nWidth
= nThick
+ 2 * nBorderwidth
;
1976 SmRect::operator = (SmRect(aToSize
.Width(), aToSize
.Height()));
1980 /**************************************************************************/
1982 void SmRootSymbolNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong nWidth
)
1984 nBodyWidth
= nWidth
;
1988 void SmRootSymbolNode::AdaptToY(OutputDevice
&rDev
, sal_uLong nHeight
)
1990 // some additional length so that the horizontal
1991 // bar will be positioned above the argument
1992 SmMathSymbolNode::AdaptToY(rDev
, nHeight
+ nHeight
/ 10L);
1996 /**************************************************************************/
1999 void SmRectangleNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong nWidth
)
2001 aToSize
.Width() = nWidth
;
2005 void SmRectangleNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong nHeight
)
2007 GetFont().FreezeBorderWidth();
2008 aToSize
.Height() = nHeight
;
2012 void SmRectangleNode::Arrange(OutputDevice
&rDev
, const SmFormat
&/*rFormat*/)
2014 long nFontHeight
= GetFont().GetFontSize().Height();
2015 long nWidth
= aToSize
.Width(),
2016 nHeight
= aToSize
.Height();
2018 nHeight
= nFontHeight
/ 30;
2020 nWidth
= nFontHeight
/ 3;
2022 SmTmpDevice
aTmpDev (rDev
, true);
2023 aTmpDev
.SetFont(GetFont());
2025 // add some borderspace
2026 sal_uLong nTmpBorderWidth
= GetFont().GetBorderWidth();
2027 nHeight
+= 2 * nTmpBorderWidth
;
2029 //! use this method in order to have 'SmRect::HasAlignInfo() == true'
2030 //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
2031 SmRect::operator = (SmRect(nWidth
, nHeight
));
2035 /**************************************************************************/
2038 SmTextNode::SmTextNode( SmNodeType eNodeType
, const SmToken
&rNodeToken
, sal_uInt16 nFontDescP
)
2039 : SmVisibleNode(eNodeType
, rNodeToken
)
2040 , nFontDesc(nFontDescP
)
2041 , nSelectionStart(0)
2046 SmTextNode::SmTextNode( const SmToken
&rNodeToken
, sal_uInt16 nFontDescP
)
2047 : SmVisibleNode(NTEXT
, rNodeToken
)
2048 , nFontDesc(nFontDescP
)
2049 , nSelectionStart(0)
2054 void SmTextNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
2056 SmNode::Prepare(rFormat
, rDocShell
);
2058 // default setting for horizontal alignment of nodes with TTEXT
2059 // content is as alignl (cannot be done in Arrange since it would
2060 // override the settings made by an SmAlignNode before)
2061 if (TTEXT
== GetToken().eType
)
2062 SetRectHorAlign( RectHorAlign::Left
);
2064 aText
= GetToken().aText
;
2065 GetFont() = rFormat
.GetFont(GetFontDesc());
2067 if (IsItalic( GetFont() ))
2068 Attributes() |= FontAttribute::Italic
;
2069 if (IsBold( GetFont() ))
2070 Attributes() |= FontAttribute::Bold
;
2072 // special handling for ':' where it is a token on it's own and is likely
2073 // to be used for mathematical notations. (E.g. a:b = 2:3)
2074 // In that case it should not be displayed in italic.
2075 if (GetToken().aText
.getLength() == 1 && GetToken().aText
[0] == ':')
2076 Attributes() &= ~FontAttribute::Italic
;
2080 void SmTextNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2082 PrepareAttributes();
2084 sal_uInt16 nSizeDesc
= GetFontDesc() == FNT_FUNCTION
?
2085 SIZ_FUNCTION
: SIZ_TEXT
;
2086 GetFont() *= Fraction (rFormat
.GetRelSize(nSizeDesc
), 100);
2088 SmTmpDevice
aTmpDev (rDev
, true);
2089 aTmpDev
.SetFont(GetFont());
2091 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, aText
, GetFont().GetBorderWidth()));
2094 void SmTextNode::CreateTextFromNode(OUString
&rText
)
2097 if (GetToken().eType
== TTEXT
)
2104 SmParser aParseTest
;
2105 std::unique_ptr
<SmTableNode
> pTable(aParseTest
.Parse(GetToken().aText
));
2106 assert(pTable
->GetType() == NTABLE
);
2108 if (pTable
->GetNumSubNodes() == 1)
2110 SmNode
*pResult
= pTable
->GetSubNode(0);
2111 if ( (pResult
->GetType() == NLINE
) &&
2112 (pResult
->GetNumSubNodes() == 1) )
2114 pResult
= pResult
->GetSubNode(0);
2115 if (pResult
->GetType() == NTEXT
)
2120 if ((GetToken().eType
== TIDENT
) && (GetFontDesc() == FNT_FUNCTION
))
2122 //Search for existing functions and remove extraneous keyword
2133 rText
+= GetToken().aText
;
2141 void SmTextNode::GetAccessibleText( OUStringBuffer
&rText
) const
2143 rText
.append(aText
);
2146 void SmTextNode::AdjustFontDesc()
2148 if (GetToken().eType
== TTEXT
)
2149 nFontDesc
= FNT_TEXT
;
2150 else if(GetToken().eType
== TFUNC
)
2151 nFontDesc
= FNT_FUNCTION
;
2154 const SmTokenTableEntry
*pEntry
= SmParser::GetTokenTableEntry( aText
);
2155 if (pEntry
&& pEntry
->nGroup
== TG::Function
) {
2156 nTok
= pEntry
->eType
;
2157 nFontDesc
= FNT_FUNCTION
;
2159 sal_Unicode firstChar
= aText
[0];
2160 if( ('0' <= firstChar
&& firstChar
<= '9') || firstChar
== '.' || firstChar
== ',') {
2161 nFontDesc
= FNT_NUMBER
;
2163 } else if (aText
.getLength() > 1) {
2164 nFontDesc
= FNT_VARIABLE
;
2167 nFontDesc
= FNT_VARIABLE
;
2171 SmToken tok
= GetToken();
2177 sal_Unicode
SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn
)
2179 //Find the best match in accepted unicode for our private area symbols
2180 static const sal_Unicode aStarMathPrivateToUnicode
[] =
2182 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
2183 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
2184 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
2185 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
2186 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
2187 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
2188 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
2189 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
2190 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
2191 0xE0DA, 0x2190, 0x2191, 0x2193
2193 if ((nIn
>= 0xE080) && (nIn
<= 0xE0DD))
2194 nIn
= aStarMathPrivateToUnicode
[nIn
-0xE080];
2196 //For whatever unicode glyph that equation editor doesn't ship with that
2197 //we have a possible match we can munge it to.
2210 /**************************************************************************/
2212 void SmMatrixNode::CreateTextFromNode(OUString
&rText
)
2214 rText
+= "matrix {";
2215 for (sal_uInt16 i
= 0; i
< mnNumRows
; i
++)
2217 for (sal_uInt16 j
= 0; j
< mnNumCols
; j
++)
2219 SmNode
*pNode
= GetSubNode(i
* mnNumCols
+ j
);
2221 pNode
->CreateTextFromNode(rText
);
2222 if (j
!= mnNumCols
-1)
2225 if (i
!= mnNumRows
-1)
2228 rText
= comphelper::string::stripEnd(rText
, ' ');
2233 void SmMatrixNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2238 // initialize array that is to hold the maximum widths of all
2239 // elements (subnodes) in that column.
2240 std::vector
<long> aColWidth(mnNumCols
);
2242 // arrange subnodes and calculate the above arrays contents
2243 sal_uInt16 nNodes
= GetNumSubNodes();
2244 for (i
= 0; i
< nNodes
; i
++)
2246 sal_uInt16 nIdx
= nNodes
- 1 - i
;
2247 if (nullptr != (pNode
= GetSubNode(nIdx
)))
2249 pNode
->Arrange(rDev
, rFormat
);
2250 int nCol
= nIdx
% mnNumCols
;
2251 aColWidth
[nCol
] = std::max(aColWidth
[nCol
], pNode
->GetItalicWidth());
2255 // norm distance from which the following two are calculated
2256 const long nNormDist
= 3 * GetFont().GetFontSize().Height();
2258 // define horizontal and vertical minimal distances that separate
2260 long nHorDist
= nNormDist
* rFormat
.GetDistance(DIS_MATRIXCOL
) / 100L,
2261 nVerDist
= nNormDist
* rFormat
.GetDistance(DIS_MATRIXROW
) / 100L;
2263 // build array that holds the leftmost position for each column
2264 std::vector
<long> aColLeft(mnNumCols
);
2266 for (j
= 0; j
< mnNumCols
; j
++)
2269 nX
+= aColWidth
[j
] + nHorDist
;
2272 SmRect::operator = (SmRect());
2273 for (i
= 0; i
< mnNumRows
; i
++)
2277 for (j
= 0; j
< mnNumCols
; j
++)
2279 SmNode
*pTmpNode
= GetSubNode(i
* mnNumCols
+ j
);
2282 const SmRect
&rNodeRect
= pTmpNode
->GetRect();
2284 // align all baselines in that row if possible
2285 aPos
= rNodeRect
.AlignTo(aLineRect
, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
2287 // get horizontal alignment
2288 const SmNode
*pCoNode
= pTmpNode
->GetLeftMost();
2289 RectHorAlign eHorAlign
= pCoNode
->GetRectHorAlign();
2291 // calculate horizontal position of element depending on column
2292 // and horizontal alignment
2294 { case RectHorAlign::Left
:
2295 aPos
.X() = aColLeft
[j
];
2297 case RectHorAlign::Center
:
2298 aPos
.X() = rNodeRect
.GetLeft() + aColLeft
[j
]
2300 - rNodeRect
.GetItalicCenterX();
2302 case RectHorAlign::Right
:
2303 aPos
.X() = aColLeft
[j
]
2304 + aColWidth
[j
] - rNodeRect
.GetItalicWidth();
2310 pTmpNode
->MoveTo(aPos
);
2311 aLineRect
.ExtendBy(rNodeRect
, RectCopyMBL::Xor
);
2314 aPos
= aLineRect
.AlignTo(*this, RectPos::Bottom
, RectHorAlign::Center
, RectVerAlign::Baseline
);
2316 aPos
.Y() += nVerDist
;
2318 // move 'aLineRect' and rectangles in that line to final position
2319 Point
aDelta(0, // since horizontal alignment is already done
2320 aPos
.Y() - aLineRect
.GetTop());
2321 aLineRect
.Move(aDelta
);
2322 for (j
= 0; j
< mnNumCols
; j
++)
2323 if (nullptr != (pNode
= GetSubNode(i
* mnNumCols
+ j
)))
2324 pNode
->Move(aDelta
);
2326 ExtendBy(aLineRect
, RectCopyMBL::None
);
2331 void SmMatrixNode::SetRowCol(sal_uInt16 nMatrixRows
, sal_uInt16 nMatrixCols
)
2333 mnNumRows
= nMatrixRows
;
2334 mnNumCols
= nMatrixCols
;
2338 const SmNode
* SmMatrixNode::GetLeftMost() const
2344 /**************************************************************************/
2347 SmMathSymbolNode::SmMathSymbolNode(const SmToken
&rNodeToken
)
2348 : SmSpecialNode(NMATH
, rNodeToken
, FNT_MATH
)
2350 sal_Unicode cChar
= GetToken().cMathChar
;
2351 if (sal_Unicode('\0') != cChar
)
2352 SetText(OUString(cChar
));
2355 void SmMathSymbolNode::AdaptToX(OutputDevice
&rDev
, sal_uLong nWidth
)
2357 // Since there is no function to do this, we try to approximate it:
2358 Size
aFntSize (GetFont().GetFontSize());
2360 //! however the result is a bit better with 'nWidth' as initial font width
2361 aFntSize
.Width() = nWidth
;
2362 GetFont().SetSize(aFntSize
);
2364 SmTmpDevice
aTmpDev (rDev
, true);
2365 aTmpDev
.SetFont(GetFont());
2367 // get denominator of error factor for width
2368 long nTmpBorderWidth
= GetFont().GetBorderWidth();
2369 long nDenom
= SmRect(aTmpDev
, nullptr, GetText(), nTmpBorderWidth
).GetItalicWidth();
2371 // scale fontwidth with this error factor
2372 aFntSize
.Width() *= nWidth
;
2373 aFntSize
.Width() /= nDenom
? nDenom
: 1;
2375 GetFont().SetSize(aFntSize
);
2378 void SmMathSymbolNode::AdaptToY(OutputDevice
&rDev
, sal_uLong nHeight
)
2380 GetFont().FreezeBorderWidth();
2381 Size
aFntSize (GetFont().GetFontSize());
2383 // Since we only want to scale the height, we might have
2384 // to determine the font width in order to keep it
2385 if (aFntSize
.Width() == 0)
2387 rDev
.Push(PushFlags::FONT
| PushFlags::MAPMODE
);
2388 rDev
.SetFont(GetFont());
2389 aFntSize
.Width() = rDev
.GetFontMetric().GetFontSize().Width();
2392 OSL_ENSURE(aFntSize
.Width() != 0, "Sm: ");
2394 //! however the result is a bit better with 'nHeight' as initial
2396 aFntSize
.Height() = nHeight
;
2397 GetFont().SetSize(aFntSize
);
2399 SmTmpDevice
aTmpDev (rDev
, true);
2400 aTmpDev
.SetFont(GetFont());
2402 // get denominator of error factor for height
2403 long nTmpBorderWidth
= GetFont().GetBorderWidth();
2404 long nDenom
= SmRect(aTmpDev
, nullptr, GetText(), nTmpBorderWidth
).GetHeight();
2406 // scale fontwidth with this error factor
2407 aFntSize
.Height() *= nHeight
;
2408 aFntSize
.Height() /= nDenom
? nDenom
: 1;
2410 GetFont().SetSize(aFntSize
);
2414 void SmMathSymbolNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
2416 SmNode::Prepare(rFormat
, rDocShell
);
2418 GetFont() = rFormat
.GetFont(GetFontDesc());
2419 // use same font size as is used for variables
2420 GetFont().SetSize( rFormat
.GetFont( FNT_VARIABLE
).GetFontSize() );
2422 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL
||
2423 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE
,
2424 "wrong charset for character from StarMath/OpenSymbol font");
2426 Flags() |= FontChangeMask::Face
| FontChangeMask::Italic
;
2430 void SmMathSymbolNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2432 const OUString
&rText
= GetText();
2434 if (rText
.isEmpty() || rText
[0] == '\0')
2435 { SmRect::operator = (SmRect());
2439 PrepareAttributes();
2441 GetFont() *= Fraction (rFormat
.GetRelSize(SIZ_TEXT
), 100);
2443 SmTmpDevice
aTmpDev (rDev
, true);
2444 aTmpDev
.SetFont(GetFont());
2446 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, rText
, GetFont().GetBorderWidth()));
2449 void SmMathSymbolNode::CreateTextFromNode(OUString
&rText
)
2452 sal_Unicode cChar
= GetToken().cMathChar
;
2453 if (cChar
== MS_INT
&& GetScaleMode() == SCALE_HEIGHT
)
2456 MathType::LookupChar(cChar
, sStr
, 3);
2460 void SmRectangleNode::CreateTextFromNode(OUString
&rText
)
2462 switch (GetToken().eType
)
2465 rText
+= "underline ";
2468 rText
+= "overline ";
2471 rText
+= "overstrike ";
2478 void SmAttributNode::CreateTextFromNode(OUString
&rText
)
2481 assert(GetNumSubNodes() == 2);
2483 sal_Unicode nLast
=0;
2484 if (nullptr != (pNode
= Attribute()))
2487 pNode
->CreateTextFromNode(aStr
);
2488 if (aStr
.getLength() > 1)
2495 case MS_BAR
: // MACRON
2496 rText
+= "overline ";
2498 case MS_DOT
: // DOT ABOVE
2501 case 0x2dc: // SMALL TILDE
2502 rText
+= "widetilde ";
2504 case MS_DDOT
: // DIAERESIS
2510 case MS_DDDOT
: // COMBINING THREE DOTS ABOVE
2513 case MS_ACUTE
: // ACUTE ACCENT
2514 case MS_COMBACUTE
: // COMBINING ACUTE ACCENT
2517 case MS_GRAVE
: // GRAVE ACCENT
2518 case MS_COMBGRAVE
: // COMBINING GRAVE ACCENT
2521 case MS_CHECK
: // CARON
2522 case MS_COMBCHECK
: // COMBINING CARON
2525 case MS_BREVE
: // BREVE
2526 case MS_COMBBREVE
: // COMBINING BREVE
2529 case MS_CIRCLE
: // RING ABOVE
2530 case MS_COMBCIRCLE
: // COMBINING RING ABOVE
2533 case MS_RIGHTARROW
: // RIGHTWARDS ARROW
2534 case MS_VEC
: // COMBINING RIGHT ARROW ABOVE
2537 case MS_TILDE
: // TILDE
2538 case MS_COMBTILDE
: // COMBINING TILDE
2541 case MS_HAT
: // CIRCUMFLEX ACCENT
2542 case MS_COMBHAT
: // COMBINING CIRCUMFLEX ACCENT
2545 case MS_COMBBAR
: // COMBINING MACRON
2549 rText
+= OUStringLiteral1( nLast
);
2555 if (nullptr != (pNode
= Body()))
2556 pNode
->CreateTextFromNode(rText
);
2558 rText
= comphelper::string::stripEnd(rText
, ' ');
2560 if (nLast
== 0xE082)
2561 rText
+= " overbrace {}";
2566 /**************************************************************************/
2568 static bool lcl_IsFromGreekSymbolSet( const OUString
&rTokenText
)
2572 // valid symbol name needs to have a '%' at pos 0 and at least an additional char
2573 if (rTokenText
.getLength() > 2 && rTokenText
[0] == sal_Unicode('%'))
2575 OUString
aName( rTokenText
.copy(1) );
2576 SmSym
*pSymbol
= SM_MOD()->GetSymbolManager().GetSymbolByName( aName
);
2577 if (pSymbol
&& SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol
->GetSymbolSetName()) == "Greek")
2585 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType
, const SmToken
&rNodeToken
, sal_uInt16 _nFontDesc
) :
2586 SmTextNode(eNodeType
, rNodeToken
, _nFontDesc
)
2588 bIsFromGreekSymbolSet
= lcl_IsFromGreekSymbolSet( rNodeToken
.aText
);
2592 SmSpecialNode::SmSpecialNode(const SmToken
&rNodeToken
) :
2593 SmTextNode(NSPECIAL
, rNodeToken
, FNT_MATH
) // default Font isn't always correct!
2595 bIsFromGreekSymbolSet
= lcl_IsFromGreekSymbolSet( rNodeToken
.aText
);
2599 void SmSpecialNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
2601 SmNode::Prepare(rFormat
, rDocShell
);
2604 SmModule
*pp
= SM_MOD();
2606 OUString
aName(GetToken().aText
.copy(1));
2607 if (nullptr != (pSym
= pp
->GetSymbolManager().GetSymbolByName( aName
)))
2609 sal_UCS4 cChar
= pSym
->GetCharacter();
2610 OUString
aTmp( &cChar
, 1 );
2612 GetFont() = pSym
->GetFace();
2616 SetText( GetToken().aText
);
2617 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
2619 // use same font size as is used for variables
2620 GetFont().SetSize( rFormat
.GetFont( FNT_VARIABLE
).GetFontSize() );
2622 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2623 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2624 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2626 //! see also SmFontStyles::GetStyleName
2627 if (IsItalic( GetFont() ))
2628 SetAttribut(FontAttribute::Italic
);
2629 if (IsBold( GetFont() ))
2630 SetAttribut(FontAttribute::Bold
);
2632 Flags() |= FontChangeMask::Face
;
2634 if (bIsFromGreekSymbolSet
)
2636 OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
2637 bool bItalic
= false;
2638 sal_Int16 nStyle
= rFormat
.GetGreekCharStyle();
2639 OSL_ENSURE( nStyle
>= 0 && nStyle
<= 2, "unexpected value for GreekCharStyle" );
2642 else if (nStyle
== 2)
2644 const OUString
& rTmp(GetText());
2645 if (!rTmp
.isEmpty())
2647 static const sal_Unicode cUppercaseAlpha
= 0x0391;
2648 static const sal_Unicode cUppercaseOmega
= 0x03A9;
2649 sal_Unicode cChar
= rTmp
[0];
2650 // uppercase letters should be straight and lowercase letters italic
2651 bItalic
= !(cUppercaseAlpha
<= cChar
&& cChar
<= cUppercaseOmega
);
2656 Attributes() |= FontAttribute::Italic
;
2658 Attributes() &= ~FontAttribute::Italic
;
2663 void SmSpecialNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2665 PrepareAttributes();
2667 SmTmpDevice
aTmpDev (rDev
, true);
2668 aTmpDev
.SetFont(GetFont());
2670 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(), GetFont().GetBorderWidth()));
2673 /**************************************************************************/
2676 void SmGlyphSpecialNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2678 PrepareAttributes();
2680 SmTmpDevice
aTmpDev (rDev
, true);
2681 aTmpDev
.SetFont(GetFont());
2683 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(),
2684 GetFont().GetBorderWidth()).AsGlyphRect());
2688 /**************************************************************************/
2691 void SmPlaceNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
2693 SmNode::Prepare(rFormat
, rDocShell
);
2695 GetFont().SetColor(COL_GRAY
);
2696 Flags() |= FontChangeMask::Color
| FontChangeMask::Face
| FontChangeMask::Italic
;
2700 void SmPlaceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2702 PrepareAttributes();
2704 SmTmpDevice
aTmpDev (rDev
, true);
2705 aTmpDev
.SetFont(GetFont());
2707 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(), GetFont().GetBorderWidth()));
2711 /**************************************************************************/
2714 void SmErrorNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
2716 SmNode::Prepare(rFormat
, rDocShell
);
2718 GetFont().SetColor(COL_RED
);
2719 Flags() |= FontChangeMask::Phantom
| FontChangeMask::Bold
| FontChangeMask::Italic
2720 | FontChangeMask::Color
| FontChangeMask::Face
| FontChangeMask::Size
;
2724 void SmErrorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2726 PrepareAttributes();
2728 SmTmpDevice
aTmpDev (rDev
, true);
2729 aTmpDev
.SetFont(GetFont());
2731 const OUString
&rText
= GetText();
2732 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, rText
, GetFont().GetBorderWidth()));
2736 /**************************************************************************/
2739 void SmBlankNode::IncreaseBy(const SmToken
&rToken
)
2741 switch(rToken
.eType
)
2743 case TBLANK
: mnNum
+= 4; break;
2744 case TSBLANK
: mnNum
+= 1; break;
2751 void SmBlankNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
)
2753 SmNode::Prepare(rFormat
, rDocShell
);
2755 // Here it need/should not be the StarMath font, so that for the character
2756 // used in Arrange a normal (non-clipped) rectangle is generated
2757 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
2759 Flags() |= FontChangeMask::Face
| FontChangeMask::Bold
| FontChangeMask::Italic
;
2763 void SmBlankNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2765 SmTmpDevice
aTmpDev (rDev
, true);
2766 aTmpDev
.SetFont(GetFont());
2768 // make distance depend on the font height
2769 // (so that it increases when scaling (e.g. size *2 {a ~ b})
2770 long nDist
= GetFont().GetFontSize().Height() / 10L,
2771 nSpace
= mnNum
* nDist
;
2773 // get a SmRect with Baseline and all the bells and whistles
2774 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, OUString(' '),
2775 GetFont().GetBorderWidth()));
2777 // and resize it to the requested size
2778 SetItalicSpaces(0, 0);
2782 void SmBlankNode::CreateTextFromNode(OUString
&rText
)
2786 sal_uInt16 nWide
= mnNum
/ 4;
2787 sal_uInt16 nNarrow
= mnNum
% 4;
2788 for (sal_uInt16 i
= 0; i
< nWide
; i
++)
2790 for (sal_uInt16 i
= 0; i
< nNarrow
; i
++)
2796 /**************************************************************************/
2797 //Implementation of all accept methods for SmVisitor
2799 void SmTableNode::Accept(SmVisitor
* pVisitor
) {
2800 pVisitor
->Visit(this);
2803 void SmBraceNode::Accept(SmVisitor
* pVisitor
) {
2804 pVisitor
->Visit(this);
2807 void SmBracebodyNode::Accept(SmVisitor
* pVisitor
) {
2808 pVisitor
->Visit(this);
2811 void SmOperNode::Accept(SmVisitor
* pVisitor
) {
2812 pVisitor
->Visit(this);
2815 void SmAlignNode::Accept(SmVisitor
* pVisitor
) {
2816 pVisitor
->Visit(this);
2819 void SmAttributNode::Accept(SmVisitor
* pVisitor
) {
2820 pVisitor
->Visit(this);
2823 void SmFontNode::Accept(SmVisitor
* pVisitor
) {
2824 pVisitor
->Visit(this);
2827 void SmUnHorNode::Accept(SmVisitor
* pVisitor
) {
2828 pVisitor
->Visit(this);
2831 void SmBinHorNode::Accept(SmVisitor
* pVisitor
) {
2832 pVisitor
->Visit(this);
2835 void SmBinVerNode::Accept(SmVisitor
* pVisitor
) {
2836 pVisitor
->Visit(this);
2839 void SmBinDiagonalNode::Accept(SmVisitor
* pVisitor
) {
2840 pVisitor
->Visit(this);
2843 void SmSubSupNode::Accept(SmVisitor
* pVisitor
) {
2844 pVisitor
->Visit(this);
2847 void SmMatrixNode::Accept(SmVisitor
* pVisitor
) {
2848 pVisitor
->Visit(this);
2851 void SmPlaceNode::Accept(SmVisitor
* pVisitor
) {
2852 pVisitor
->Visit(this);
2855 void SmTextNode::Accept(SmVisitor
* pVisitor
) {
2856 pVisitor
->Visit(this);
2859 void SmSpecialNode::Accept(SmVisitor
* pVisitor
) {
2860 pVisitor
->Visit(this);
2863 void SmGlyphSpecialNode::Accept(SmVisitor
* pVisitor
) {
2864 pVisitor
->Visit(this);
2867 void SmMathSymbolNode::Accept(SmVisitor
* pVisitor
) {
2868 pVisitor
->Visit(this);
2871 void SmBlankNode::Accept(SmVisitor
* pVisitor
) {
2872 pVisitor
->Visit(this);
2875 void SmErrorNode::Accept(SmVisitor
* pVisitor
) {
2876 pVisitor
->Visit(this);
2879 void SmLineNode::Accept(SmVisitor
* pVisitor
) {
2880 pVisitor
->Visit(this);
2883 void SmExpressionNode::Accept(SmVisitor
* pVisitor
) {
2884 pVisitor
->Visit(this);
2887 void SmPolyLineNode::Accept(SmVisitor
* pVisitor
) {
2888 pVisitor
->Visit(this);
2891 void SmRootNode::Accept(SmVisitor
* pVisitor
) {
2892 pVisitor
->Visit(this);
2895 void SmRootSymbolNode::Accept(SmVisitor
* pVisitor
) {
2896 pVisitor
->Visit(this);
2899 void SmRectangleNode::Accept(SmVisitor
* pVisitor
) {
2900 pVisitor
->Visit(this);
2903 void SmVerticalBraceNode::Accept(SmVisitor
* pVisitor
) {
2904 pVisitor
->Visit(this);
2907 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */