1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
26 #include "mathtype.hxx"
27 #include "tmpdevice.hxx"
28 #include <visitors.hxx>
30 #include <comphelper/string.hxx>
31 #include <tools/color.hxx>
32 #include <tools/fract.hxx>
33 #include <tools/gen.hxx>
34 #include <vcl/outdev.hxx>
45 void ForEachNonNull(SmNode
*pNode
, F
&& f
)
47 size_t nSize
= pNode
->GetNumSubNodes();
48 for (size_t i
= 0; i
< nSize
; ++i
)
50 SmNode
*pSubNode
= pNode
->GetSubNode(i
);
51 if (pSubNode
!= nullptr)
58 SmNode::SmNode(SmNodeType eNodeType
, const SmToken
&rNodeToken
)
59 : maNodeToken( rNodeToken
)
61 , meScaleMode( SmScaleMode::None
)
62 , meRectHorAlign( RectHorAlign::Left
)
63 , mnFlags( FontChangeMask::None
)
64 , mnAttributes( FontAttribute::None
)
65 , mbIsPhantom( false )
66 , mbIsSelected( false )
68 , mpParentNode( nullptr )
76 const SmNode
* SmNode::GetLeftMost() const
77 // returns leftmost node of current subtree.
78 //! (this assumes the one with index 0 is always the leftmost subnode
79 //! for the current node).
81 const SmNode
*pNode
= GetNumSubNodes() > 0 ?
82 GetSubNode(0) : nullptr;
84 return pNode
? pNode
->GetLeftMost() : this;
88 void SmNode::SetPhantom(bool bIsPhantomP
)
90 if (! (Flags() & FontChangeMask::Phantom
))
91 mbIsPhantom
= bIsPhantomP
;
94 ForEachNonNull(this, [b
](SmNode
*pNode
){pNode
->SetPhantom(b
);});
98 void SmNode::SetColor(const Color
& rColor
)
100 if (! (Flags() & FontChangeMask::Color
))
101 GetFont().SetColor(rColor
);
103 ForEachNonNull(this, [&rColor
](SmNode
*pNode
){pNode
->SetColor(rColor
);});
107 void SmNode::SetAttribut(FontAttribute nAttrib
)
110 (nAttrib
== FontAttribute::Bold
&& !(Flags() & FontChangeMask::Bold
)) ||
111 (nAttrib
== FontAttribute::Italic
&& !(Flags() & FontChangeMask::Italic
))
114 mnAttributes
|= nAttrib
;
117 ForEachNonNull(this, [nAttrib
](SmNode
*pNode
){pNode
->SetAttribut(nAttrib
);});
121 void SmNode::ClearAttribut(FontAttribute nAttrib
)
124 (nAttrib
== FontAttribute::Bold
&& !(Flags() & FontChangeMask::Bold
)) ||
125 (nAttrib
== FontAttribute::Italic
&& !(Flags() & FontChangeMask::Italic
))
128 mnAttributes
&= ~nAttrib
;
131 ForEachNonNull(this, [nAttrib
](SmNode
*pNode
){pNode
->ClearAttribut(nAttrib
);});
135 void SmNode::SetFont(const SmFace
&rFace
)
137 if (!(Flags() & FontChangeMask::Face
))
140 ForEachNonNull(this, [&rFace
](SmNode
*pNode
){pNode
->SetFont(rFace
);});
144 void SmNode::SetFontSize(const Fraction
&rSize
, FontSizeType nType
)
145 //! 'rSize' is in units of pts
149 if (!(Flags() & FontChangeMask::Size
))
151 Fraction
aVal (SmPtsTo100th_mm(rSize
.GetNumerator()),
152 rSize
.GetDenominator());
153 long nHeight
= static_cast<long>(aVal
);
155 aFntSize
= GetFont().GetFontSize();
156 aFntSize
.setWidth( 0 );
159 case FontSizeType::ABSOLUT
:
160 aFntSize
.setHeight( nHeight
);
163 case FontSizeType::PLUS
:
164 aFntSize
.AdjustHeight(nHeight
);
167 case FontSizeType::MINUS
:
168 aFntSize
.AdjustHeight( -nHeight
);
171 case FontSizeType::MULTIPLY
:
172 aFntSize
.setHeight( static_cast<long>(Fraction(aFntSize
.Height()) * rSize
) );
175 case FontSizeType::DIVIDE
:
176 if (rSize
!= Fraction(0))
177 aFntSize
.setHeight( static_cast<long>(Fraction(aFntSize
.Height()) / rSize
) );
183 // check the requested size against maximum value
184 static int const nMaxVal
= SmPtsTo100th_mm(128);
185 if (aFntSize
.Height() > nMaxVal
)
186 aFntSize
.setHeight( nMaxVal
);
188 GetFont().SetSize(aFntSize
);
191 ForEachNonNull(this, [&rSize
, &nType
](SmNode
*pNode
){pNode
->SetFontSize(rSize
, nType
);});
195 void SmNode::SetSize(const Fraction
&rSize
)
199 ForEachNonNull(this, [&rSize
](SmNode
*pNode
){pNode
->SetSize(rSize
);});
203 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign
, bool bApplyToSubTree
)
205 if (!(Flags() & FontChangeMask::HorAlign
))
206 meRectHorAlign
= eHorAlign
;
209 ForEachNonNull(this, [eHorAlign
](SmNode
*pNode
){pNode
->SetRectHorAlign(eHorAlign
);});
213 void SmNode::PrepareAttributes()
215 GetFont().SetWeight((Attributes() & FontAttribute::Bold
) ? WEIGHT_BOLD
: WEIGHT_NORMAL
);
216 GetFont().SetItalic((Attributes() & FontAttribute::Italic
) ? ITALIC_NORMAL
: ITALIC_NONE
);
220 void SmNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
223 throw std::range_error("parser depth limit");
226 mnFlags
= FontChangeMask::None
;
227 mnAttributes
= FontAttribute::None
;
229 switch (rFormat
.GetHorAlign())
230 { case SmHorAlign::Left
: meRectHorAlign
= RectHorAlign::Left
; break;
231 case SmHorAlign::Center
: meRectHorAlign
= RectHorAlign::Center
; break;
232 case SmHorAlign::Right
: 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
, nDepth
](SmNode
*pNode
){pNode
->Prepare(rFormat
, rDocShell
, nDepth
+ 1);});
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
);});
254 void SmNode::CreateTextFromNode(OUStringBuffer
&rText
)
256 auto nSize
= GetNumSubNodes();
259 ForEachNonNull(this, [&rText
](SmNode
*pNode
){pNode
->CreateTextFromNode(rText
);});
267 void SmNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong
/*nWidth*/)
272 void SmNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong
/*nHeight*/)
277 const SmNode
* SmNode::FindTokenAt(sal_uInt16 nRow
, sal_uInt16 nCol
) const
278 // returns (first) ** visible ** (sub)node with the tokens text at
279 // position 'nRow', 'nCol'.
280 //! (there should be exactly one such node if any)
283 && nRow
== GetToken().nRow
284 && nCol
>= GetToken().nCol
&& nCol
< GetToken().nCol
+ GetToken().aText
.getLength())
288 size_t nNumSubNodes
= GetNumSubNodes();
289 for (size_t i
= 0; i
< nNumSubNodes
; ++i
)
291 const SmNode
*pNode
= GetSubNode(i
);
296 const SmNode
*pResult
= pNode
->FindTokenAt(nRow
, nCol
);
306 const SmNode
* SmNode::FindRectClosestTo(const Point
&rPoint
) const
308 long nDist
= LONG_MAX
;
309 const SmNode
*pResult
= nullptr;
315 size_t nNumSubNodes
= GetNumSubNodes();
316 for (size_t 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 size_t nNumSubNodes
= GetNumSubNodes();
359 for (size_t i
= 0; i
< nNumSubNodes
; ++i
)
361 const SmNode
*pNode
= GetSubNode(i
);
365 pResult
= pNode
->FindNodeWithAccessibleIndex(nAccIdx
);
375 SmStructureNode::~SmStructureNode()
377 ForEachNonNull(this, std::default_delete
<SmNode
>());
381 void SmStructureNode::ClearSubNodes()
386 void SmStructureNode::SetSubNodes(SmNode
*pFirst
, SmNode
*pSecond
, SmNode
*pThird
)
388 size_t nSize
= pThird
? 3 : (pSecond
? 2 : (pFirst
? 1 : 0));
389 maSubNodes
.resize( nSize
);
391 maSubNodes
[0] = pFirst
;
393 maSubNodes
[1] = pSecond
;
395 maSubNodes
[2] = pThird
;
400 void SmStructureNode::SetSubNodes(SmNodeArray
&& rNodeArray
)
402 maSubNodes
= std::move(rNodeArray
);
406 bool SmStructureNode::IsVisible() const
411 size_t SmStructureNode::GetNumSubNodes() const
413 return maSubNodes
.size();
416 SmNode
* SmStructureNode::GetSubNode(size_t nIndex
)
418 return maSubNodes
[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
);
432 void SmStructureNode::ClaimPaternity()
434 ForEachNonNull(this, [this](SmNode
*pNode
){pNode
->SetParent(this);});
437 bool SmVisibleNode::IsVisible() const
442 size_t SmVisibleNode::GetNumSubNodes() const
447 SmNode
* SmVisibleNode::GetSubNode(size_t /*nIndex*/)
452 void SmGraphicNode::GetAccessibleText( OUStringBuffer
&rText
) const
454 rText
.append(GetToken().aText
);
457 void SmExpressionNode::CreateTextFromNode(OUStringBuffer
&rText
)
459 size_t nSize
= GetNumSubNodes();
462 for (size_t i
= 0; i
< nSize
; ++i
)
464 SmNode
*pNode
= GetSubNode(i
);
467 pNode
->CreateTextFromNode(rText
);
468 //Just a bit of foo to make unary +asd -asd +-asd -+asd look nice
469 if (pNode
->GetType() == SmNodeType::Math
)
470 if ((nSize
!= 2) || rText
.isEmpty() ||
471 (rText
[rText
.getLength() - 1] != '+' && rText
[rText
.getLength() - 1] != '-') )
483 void SmTableNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
484 // arranges all subnodes in one column
487 size_t nSize
= GetNumSubNodes();
489 // make distance depend on font size
490 long nDist
= +(rFormat
.GetDistance(DIS_VERTICAL
)
491 * GetFont().GetFontSize().Height()) / 100L;
496 // arrange subnodes and get maximum width of them
499 for (size_t i
= 0; i
< nSize
; ++i
)
501 if (nullptr != (pNode
= GetSubNode(i
)))
502 { pNode
->Arrange(rDev
, rFormat
);
503 if ((nTmp
= pNode
->GetItalicWidth()) > nMaxWidth
)
509 SmRect::operator = (SmRect(nMaxWidth
, 1));
510 for (size_t i
= 0; i
< nSize
; ++i
)
512 if (nullptr != (pNode
= GetSubNode(i
)))
513 { const SmRect
&rNodeRect
= pNode
->GetRect();
514 const SmNode
*pCoNode
= pNode
->GetLeftMost();
515 RectHorAlign eHorAlign
= pCoNode
->GetRectHorAlign();
517 aPos
= rNodeRect
.AlignTo(*this, RectPos::Bottom
,
518 eHorAlign
, RectVerAlign::Baseline
);
520 aPos
.AdjustY(nDist
);
522 ExtendBy(rNodeRect
, nSize
> 1 ? RectCopyMBL::None
: RectCopyMBL::Arg
);
527 mnFormulaBaseline
= GetBaseline();
530 SmTmpDevice
aTmpDev (rDev
, true);
531 aTmpDev
.SetFont(GetFont());
533 SmRect
aRect(aTmpDev
, &rFormat
, "a", GetFont().GetBorderWidth());
534 mnFormulaBaseline
= GetAlignM();
535 // move from middle position by constant - distance
536 // between middle and baseline for single letter
537 mnFormulaBaseline
+= aRect
.GetBaseline() - aRect
.GetAlignM();
541 const SmNode
* SmTableNode::GetLeftMost() const
547 long SmTableNode::GetFormulaBaseline() const
549 return mnFormulaBaseline
;
553 /**************************************************************************/
556 void SmLineNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
558 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
560 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
561 // to the rest of the formula compared to the 'FNT_MATH' font.
562 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
563 Flags() |= FontChangeMask::Face
;
567 /**************************************************************************/
570 void SmLineNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
571 // arranges all subnodes in one row with some extra space between
574 size_t nSize
= GetNumSubNodes();
575 for (size_t i
= 0; i
< nSize
; ++i
)
577 if (nullptr != (pNode
= GetSubNode(i
)))
578 pNode
->Arrange(rDev
, rFormat
);
581 SmTmpDevice
aTmpDev (rDev
, true);
582 aTmpDev
.SetFont(GetFont());
586 // provide an empty rectangle with alignment parameters for the "current"
587 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
588 // same sub-/supscript positions.)
589 //! be sure to use a character that has explicitly defined HiAttribut
590 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
592 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, "a",
593 GetFont().GetBorderWidth()));
594 // make sure that the rectangle occupies (almost) no space
596 SetItalicSpaces(0, 0);
600 // make distance depend on font size
601 long nDist
= (rFormat
.GetDistance(DIS_HORIZONTAL
) * GetFont().GetFontSize().Height()) / 100L;
602 if (!IsUseExtraSpaces())
606 // copy the first node into LineNode and extend by the others
607 if (nullptr != (pNode
= GetSubNode(0)))
608 SmRect::operator = (pNode
->GetRect());
610 for (size_t i
= 1; i
< nSize
; ++i
)
612 if (nullptr != (pNode
= GetSubNode(i
)))
614 aPos
= pNode
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
616 // add horizontal space to the left for each but the first sub node
617 aPos
.AdjustX(nDist
);
620 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
);
664 aPos
.AdjustX(nDist
);
665 pNode1
->MoveTo(aPos
);
666 ExtendBy(*pNode1
, RectCopyMBL::Xor
);
670 /**************************************************************************/
674 void lcl_GetHeightVerOffset(const SmRect
&rRect
,
675 long &rHeight
, long &rVerOffset
)
676 // calculate height and vertical offset of root sign suitable for 'rRect'
678 rVerOffset
= (rRect
.GetBottom() - rRect
.GetAlignB()) / 2;
679 rHeight
= rRect
.GetHeight() - rVerOffset
;
681 OSL_ENSURE(rHeight
>= 0, "Sm : Ooops...");
682 OSL_ENSURE(rVerOffset
>= 0, "Sm : Ooops...");
686 Point
lcl_GetExtraPos(const SmRect
&rRootSymbol
,
687 const SmRect
&rExtra
)
689 const Size
&rSymSize
= rRootSymbol
.GetSize();
691 Point aPos
= rRootSymbol
.GetTopLeft()
692 + Point((rSymSize
.Width() * 70) / 100,
693 (rSymSize
.Height() * 52) / 100);
695 // from this calculate topleft edge of 'rExtra'
696 aPos
.AdjustX( -(rExtra
.GetWidth() + rExtra
.GetItalicRightSpace()) );
697 aPos
.AdjustY( -(rExtra
.GetHeight()) );
698 // if there's enough space move a bit less to the right
699 // examples: "nroot i a", "nroot j a"
700 // (it looks better if we don't use italic-spaces here)
701 long nX
= rRootSymbol
.GetLeft() + (rSymSize
.Width() * 30) / 100;
710 void SmRootNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
712 //! pExtra needs to have the smaller index than pRootSym in order to
713 //! not to get the root symbol but the pExtra when clicking on it in the
714 //! GraphicWindow. (That is because of the simplicity of the algorithm
715 //! that finds the node corresponding to a mouseclick in the window.)
716 SmNode
*pExtra
= GetSubNode(0),
717 *pRootSym
= GetSubNode(1),
718 *pBody
= GetSubNode(2);
722 pBody
->Arrange(rDev
, rFormat
);
726 lcl_GetHeightVerOffset(*pBody
, nHeight
, nVerOffset
);
727 nHeight
+= rFormat
.GetDistance(DIS_ROOT
)
728 * GetFont().GetFontSize().Height() / 100L;
730 // font specialist advised to change the width first
731 pRootSym
->AdaptToY(rDev
, nHeight
);
732 pRootSym
->AdaptToX(rDev
, pBody
->GetItalicWidth());
734 pRootSym
->Arrange(rDev
, rFormat
);
736 Point aPos
= pRootSym
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, RectVerAlign::Baseline
);
737 //! override calculated vertical position
738 aPos
.setY( pRootSym
->GetTop() + pBody
->GetBottom() - pRootSym
->GetBottom() );
739 aPos
.AdjustY( -nVerOffset
);
740 pRootSym
->MoveTo(aPos
);
743 { pExtra
->SetSize(Fraction(rFormat
.GetRelSize(SIZ_INDEX
), 100));
744 pExtra
->Arrange(rDev
, rFormat
);
746 aPos
= lcl_GetExtraPos(*pRootSym
, *pExtra
);
747 pExtra
->MoveTo(aPos
);
750 SmRect::operator = (*pBody
);
751 ExtendBy(*pRootSym
, RectCopyMBL::This
);
753 ExtendBy(*pExtra
, RectCopyMBL::This
, true);
757 void SmRootNode::CreateTextFromNode(OUStringBuffer
&rText
)
759 SmNode
*pExtra
= GetSubNode(0);
762 rText
.append("nroot ");
763 pExtra
->CreateTextFromNode(rText
);
766 rText
.append("sqrt ");
768 if (!pExtra
&& GetSubNode(2)->GetNumSubNodes() > 1)
771 GetSubNode(2)->CreateTextFromNode(rText
);
773 if (!pExtra
&& GetSubNode(2)->GetNumSubNodes() > 1)
777 /**************************************************************************/
780 void SmBinHorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
782 SmNode
*pLeft
= LeftOperand(),
784 *pRight
= RightOperand();
789 pOper
->SetSize(Fraction (rFormat
.GetRelSize(SIZ_OPERATOR
), 100));
791 pLeft
->Arrange(rDev
, rFormat
);
792 pOper
->Arrange(rDev
, rFormat
);
793 pRight
->Arrange(rDev
, rFormat
);
795 const SmRect
&rOpRect
= pOper
->GetRect();
797 long nDist
= (rOpRect
.GetWidth() *
798 rFormat
.GetDistance(DIS_HORIZONTAL
)) / 100L;
800 SmRect::operator = (*pLeft
);
803 aPos
= pOper
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
804 aPos
.AdjustX(nDist
);
806 ExtendBy(*pOper
, RectCopyMBL::Xor
);
808 aPos
= pRight
->AlignTo(*this, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
809 aPos
.AdjustX(nDist
);
811 pRight
->MoveTo(aPos
);
812 ExtendBy(*pRight
, RectCopyMBL::Xor
);
816 /**************************************************************************/
819 void SmBinVerNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
821 SmNode
*pNum
= GetSubNode(0),
822 *pLine
= GetSubNode(1),
823 *pDenom
= GetSubNode(2);
828 bool bIsTextmode
= rFormat
.IsTextmode();
831 Fraction
aFraction(rFormat
.GetRelSize(SIZ_INDEX
), 100);
832 pNum
->SetSize(aFraction
);
833 pLine
->SetSize(aFraction
);
834 pDenom
->SetSize(aFraction
);
837 pNum
->Arrange(rDev
, rFormat
);
838 pDenom
->Arrange(rDev
, rFormat
);
840 long nFontHeight
= GetFont().GetFontSize().Height(),
841 nExtLen
= nFontHeight
* rFormat
.GetDistance(DIS_FRACTION
) / 100L,
842 nThick
= nFontHeight
* rFormat
.GetDistance(DIS_STROKEWIDTH
) / 100L,
843 nWidth
= std::max(pNum
->GetItalicWidth(), pDenom
->GetItalicWidth()),
844 nNumDist
= bIsTextmode
? 0 :
845 nFontHeight
* rFormat
.GetDistance(DIS_NUMERATOR
) / 100L,
846 nDenomDist
= bIsTextmode
? 0 :
847 nFontHeight
* rFormat
.GetDistance(DIS_DENOMINATOR
) / 100L;
849 // font specialist advised to change the width first
850 pLine
->AdaptToY(rDev
, nThick
);
851 pLine
->AdaptToX(rDev
, nWidth
+ 2 * nExtLen
);
852 pLine
->Arrange(rDev
, rFormat
);
854 // get horizontal alignment for numerator
855 const SmNode
*pLM
= pNum
->GetLeftMost();
856 RectHorAlign eHorAlign
= pLM
->GetRectHorAlign();
858 // move numerator to its position
859 Point aPos
= pNum
->AlignTo(*pLine
, RectPos::Top
, eHorAlign
, RectVerAlign::Baseline
);
860 aPos
.AdjustY( -nNumDist
);
863 // get horizontal alignment for denominator
864 pLM
= pDenom
->GetLeftMost();
865 eHorAlign
= pLM
->GetRectHorAlign();
867 // move denominator to its position
868 aPos
= pDenom
->AlignTo(*pLine
, RectPos::Bottom
, eHorAlign
, RectVerAlign::Baseline
);
869 aPos
.AdjustY(nDenomDist
);
870 pDenom
->MoveTo(aPos
);
872 SmRect::operator = (*pNum
);
873 ExtendBy(*pDenom
, RectCopyMBL::None
).ExtendBy(*pLine
, RectCopyMBL::None
, pLine
->GetCenterY());
876 void SmBinVerNode::CreateTextFromNode(OUStringBuffer
&rText
)
878 SmNode
*pNum
= GetSubNode(0),
879 *pDenom
= GetSubNode(2);
880 pNum
->CreateTextFromNode(rText
);
881 rText
.append("over ");
882 pDenom
->CreateTextFromNode(rText
);
885 const SmNode
* SmBinVerNode::GetLeftMost() const
893 /// @return value of the determinant formed by the two points
894 double Det(const Point
&rHeading1
, const Point
&rHeading2
)
896 return rHeading1
.X() * rHeading2
.Y() - rHeading1
.Y() * rHeading2
.X();
900 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
901 /// and has the direction vector 'rHeading2'
902 bool IsPointInLine(const Point
&rPoint1
,
903 const Point
&rPoint2
, const Point
&rHeading2
)
905 assert(rHeading2
!= Point());
908 static const double eps
= 5.0 * DBL_EPSILON
;
911 if (labs(rHeading2
.X()) > labs(rHeading2
.Y()))
913 fLambda
= (rPoint1
.X() - rPoint2
.X()) / static_cast<double>(rHeading2
.X());
914 bRes
= fabs(rPoint1
.Y() - (rPoint2
.Y() + fLambda
* rHeading2
.Y())) < eps
;
918 fLambda
= (rPoint1
.Y() - rPoint2
.Y()) / static_cast<double>(rHeading2
.Y());
919 bRes
= fabs(rPoint1
.X() - (rPoint2
.X() + fLambda
* rHeading2
.X())) < eps
;
926 sal_uInt16
GetLineIntersectionPoint(Point
&rResult
,
927 const Point
& rPoint1
, const Point
&rHeading1
,
928 const Point
& rPoint2
, const Point
&rHeading2
)
930 assert(rHeading1
!= Point());
931 assert(rHeading2
!= Point());
934 static const double eps
= 5.0 * DBL_EPSILON
;
936 // are the direction vectors linearly dependent?
937 double fDet
= Det(rHeading1
, rHeading2
);
938 if (fabs(fDet
) < eps
)
940 nRes
= IsPointInLine(rPoint1
, rPoint2
, rHeading2
) ? USHRT_MAX
: 0;
941 rResult
= nRes
? rPoint1
: Point();
945 // here we do not pay attention to the computational accuracy
946 // (that would be more complicated and is not really worth it in this case)
947 double fLambda
= ( (rPoint1
.Y() - rPoint2
.Y()) * rHeading2
.X()
948 - (rPoint1
.X() - rPoint2
.X()) * rHeading2
.Y())
950 rResult
= Point(rPoint1
.X() + static_cast<long>(fLambda
* rHeading1
.X()),
951 rPoint1
.Y() + static_cast<long>(fLambda
* rHeading1
.Y()));
960 /// @return position and size of the diagonal line
961 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
962 void SmBinDiagonalNode::GetOperPosSize(Point
&rPos
, Size
&rSize
,
963 const Point
&rDiagPoint
, double fAngleDeg
) const
966 static const double fPi
= 3.1415926535897932384626433;
967 double fAngleRad
= fAngleDeg
/ 180.0 * fPi
;
968 long nRectLeft
= GetItalicLeft(),
969 nRectRight
= GetItalicRight(),
971 nRectBottom
= GetBottom();
972 Point
aRightHdg (100, 0),
974 aDiagHdg ( static_cast<long>(100.0 * cos(fAngleRad
)),
975 static_cast<long>(-100.0 * sin(fAngleRad
)) );
977 long nLeft
, nRight
, nTop
, nBottom
; // margins of the rectangle for the diagonal
981 // determine top right corner
982 GetLineIntersectionPoint(aPoint
,
983 Point(nRectLeft
, nRectTop
), aRightHdg
,
984 rDiagPoint
, aDiagHdg
);
985 // is there a point of intersection with the top border?
986 if (aPoint
.X() <= nRectRight
)
993 // there has to be a point of intersection with the right border!
994 GetLineIntersectionPoint(aPoint
,
995 Point(nRectRight
, nRectTop
), aDownHdg
,
996 rDiagPoint
, aDiagHdg
);
1002 // determine bottom left corner
1003 GetLineIntersectionPoint(aPoint
,
1004 Point(nRectLeft
, nRectBottom
), aRightHdg
,
1005 rDiagPoint
, aDiagHdg
);
1006 // is there a point of intersection with the bottom border?
1007 if (aPoint
.X() >= nRectLeft
)
1010 nBottom
= nRectBottom
;
1014 // there has to be a point of intersection with the left border!
1015 GetLineIntersectionPoint(aPoint
,
1016 Point(nRectLeft
, nRectTop
), aDownHdg
,
1017 rDiagPoint
, aDiagHdg
);
1020 nBottom
= aPoint
.Y();
1025 // determine top left corner
1026 GetLineIntersectionPoint(aPoint
,
1027 Point(nRectLeft
, nRectTop
), aRightHdg
,
1028 rDiagPoint
, aDiagHdg
);
1029 // is there a point of intersection with the top border?
1030 if (aPoint
.X() >= nRectLeft
)
1037 // there has to be a point of intersection with the left border!
1038 GetLineIntersectionPoint(aPoint
,
1039 Point(nRectLeft
, nRectTop
), aDownHdg
,
1040 rDiagPoint
, aDiagHdg
);
1046 // determine bottom right corner
1047 GetLineIntersectionPoint(aPoint
,
1048 Point(nRectLeft
, nRectBottom
), aRightHdg
,
1049 rDiagPoint
, aDiagHdg
);
1050 // is there a point of intersection with the bottom border?
1051 if (aPoint
.X() <= nRectRight
)
1053 nRight
= aPoint
.X();
1054 nBottom
= nRectBottom
;
1058 // there has to be a point of intersection with the right border!
1059 GetLineIntersectionPoint(aPoint
,
1060 Point(nRectRight
, nRectTop
), aDownHdg
,
1061 rDiagPoint
, aDiagHdg
);
1063 nRight
= nRectRight
;
1064 nBottom
= aPoint
.Y();
1068 rSize
= Size(nRight
- nLeft
+ 1, nBottom
- nTop
+ 1);
1074 void SmBinDiagonalNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1076 // Both arguments have to get into the SubNodes before the Operator so that clicking
1077 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1078 SmNode
*pLeft
= GetSubNode(0),
1079 *pRight
= GetSubNode(1),
1080 *pLine
= GetSubNode(2);
1083 assert(pLine
&& pLine
->GetType() == SmNodeType::PolyLine
);
1085 SmPolyLineNode
*pOper
= static_cast<SmPolyLineNode
*>(pLine
);
1088 //! some routines being called extract some info from the OutputDevice's
1089 //! font (eg the space to be used for borders OR the font name(!!)).
1090 //! Thus the font should reflect the needs and has to be set!
1091 SmTmpDevice
aTmpDev (rDev
, true);
1092 aTmpDev
.SetFont(GetFont());
1094 pLeft
->Arrange(aTmpDev
, rFormat
);
1095 pRight
->Arrange(aTmpDev
, rFormat
);
1097 // determine implicitly the values (incl. the margin) of the diagonal line
1098 pOper
->Arrange(aTmpDev
, rFormat
);
1100 long nDelta
= pOper
->GetWidth() * 8 / 10;
1102 // determine TopLeft position from the right argument
1104 aPos
.setX( pLeft
->GetItalicRight() + nDelta
+ pRight
->GetItalicLeftSpace() );
1106 aPos
.setY( pLeft
->GetBottom() + nDelta
);
1108 aPos
.setY( pLeft
->GetTop() - nDelta
- pRight
->GetHeight() );
1110 pRight
->MoveTo(aPos
);
1112 // determine new baseline
1113 long nTmpBaseline
= IsAscending() ? (pLeft
->GetBottom() + pRight
->GetTop()) / 2
1114 : (pLeft
->GetTop() + pRight
->GetBottom()) / 2;
1115 Point
aLogCenter ((pLeft
->GetItalicRight() + pRight
->GetItalicLeft()) / 2,
1118 SmRect::operator = (*pLeft
);
1119 ExtendBy(*pRight
, RectCopyMBL::None
);
1122 // determine position and size of diagonal line
1124 GetOperPosSize(aPos
, aTmpSize
, aLogCenter
, IsAscending() ? 60.0 : -60.0);
1126 // font specialist advised to change the width first
1127 pOper
->AdaptToY(aTmpDev
, aTmpSize
.Height());
1128 pOper
->AdaptToX(aTmpDev
, aTmpSize
.Width());
1129 // and make it active
1130 pOper
->Arrange(aTmpDev
, rFormat
);
1132 pOper
->MoveTo(aPos
);
1134 ExtendBy(*pOper
, RectCopyMBL::None
, nTmpBaseline
);
1138 /**************************************************************************/
1141 void SmSubSupNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1143 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES
,
1144 "Sm: wrong number of subnodes");
1146 SmNode
*pBody
= GetBody();
1149 long nOrigHeight
= pBody
->GetFont().GetFontSize().Height();
1151 pBody
->Arrange(rDev
, rFormat
);
1153 const SmRect
&rBodyRect
= pBody
->GetRect();
1154 SmRect::operator = (rBodyRect
);
1156 // line that separates sub- and supscript rectangles
1157 long nDelimLine
= SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1162 // iterate over all possible sub-/supscripts
1163 SmRect
aTmpRect (rBodyRect
);
1164 for (int i
= 0; i
< SUBSUP_NUM_ENTRIES
; i
++)
1166 SmSubSup eSubSup
= static_cast<SmSubSup
>(i
);
1167 SmNode
*pSubSup
= GetSubSup(eSubSup
);
1172 // switch position of limits if we are in textmode
1173 if (rFormat
.IsTextmode() && (GetToken().nGroup
& TG::Limit
))
1175 { case CSUB
: eSubSup
= RSUB
; break;
1176 case CSUP
: eSubSup
= RSUP
; break;
1181 // prevent sub-/supscripts from diminishing in size
1182 // (as would be in "a_{1_{2_{3_4}}}")
1183 if (GetFont().GetFontSize().Height() > rFormat
.GetBaseSize().Height() / 3)
1185 sal_uInt16 nIndex
= (eSubSup
== CSUB
|| eSubSup
== CSUP
) ?
1186 SIZ_LIMITS
: SIZ_INDEX
;
1187 Fraction
aFraction ( rFormat
.GetRelSize(nIndex
), 100 );
1188 pSubSup
->SetSize(aFraction
);
1191 pSubSup
->Arrange(rDev
, rFormat
);
1193 bool bIsTextmode
= rFormat
.IsTextmode();
1196 //! be sure that CSUB, CSUP are handled before the other cases!
1202 * rFormat
.GetDistance(DIS_SUBSCRIPT
) / 100L;
1203 aPos
= pSubSup
->GetRect().AlignTo(aTmpRect
,
1204 eSubSup
== LSUB
? RectPos::Left
: RectPos::Right
,
1205 RectHorAlign::Center
, RectVerAlign::Bottom
);
1206 aPos
.AdjustY(nDist
);
1207 nDelta
= nDelimLine
- aPos
.Y();
1209 aPos
.AdjustY(nDelta
);
1215 * rFormat
.GetDistance(DIS_SUPERSCRIPT
) / 100L;
1216 aPos
= pSubSup
->GetRect().AlignTo(aTmpRect
,
1217 eSubSup
== LSUP
? RectPos::Left
: RectPos::Right
,
1218 RectHorAlign::Center
, RectVerAlign::Top
);
1219 aPos
.AdjustY( -nDist
);
1220 nDelta
= aPos
.Y() + pSubSup
->GetHeight() - nDelimLine
;
1222 aPos
.AdjustY( -nDelta
);
1227 * rFormat
.GetDistance(DIS_LOWERLIMIT
) / 100L;
1228 aPos
= pSubSup
->GetRect().AlignTo(rBodyRect
, RectPos::Bottom
,
1229 RectHorAlign::Center
, RectVerAlign::Baseline
);
1230 aPos
.AdjustY(nDist
);
1235 * rFormat
.GetDistance(DIS_UPPERLIMIT
) / 100L;
1236 aPos
= pSubSup
->GetRect().AlignTo(rBodyRect
, RectPos::Top
,
1237 RectHorAlign::Center
, RectVerAlign::Baseline
);
1238 aPos
.AdjustY( -nDist
);
1242 pSubSup
->MoveTo(aPos
);
1243 ExtendBy(*pSubSup
, RectCopyMBL::This
, true);
1245 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1246 // will be aligned to
1247 if (eSubSup
== CSUB
|| eSubSup
== CSUP
)
1252 void SmSubSupNode::CreateTextFromNode(OUStringBuffer
&rText
)
1255 GetSubNode(0)->CreateTextFromNode(rText
);
1257 if (nullptr != (pNode
= GetSubNode(LSUB
+1)))
1259 rText
.append("lsub ");
1260 pNode
->CreateTextFromNode(rText
);
1262 if (nullptr != (pNode
= GetSubNode(LSUP
+1)))
1264 rText
.append("lsup ");
1265 pNode
->CreateTextFromNode(rText
);
1267 if (nullptr != (pNode
= GetSubNode(CSUB
+1)))
1269 rText
.append("csub ");
1270 pNode
->CreateTextFromNode(rText
);
1272 if (nullptr != (pNode
= GetSubNode(CSUP
+1)))
1274 rText
.append("csup ");
1275 pNode
->CreateTextFromNode(rText
);
1277 if (nullptr != (pNode
= GetSubNode(RSUB
+1)))
1279 rText
.stripEnd(' ');
1281 pNode
->CreateTextFromNode(rText
);
1283 if (nullptr != (pNode
= GetSubNode(RSUP
+1)))
1285 rText
.stripEnd(' ');
1287 pNode
->CreateTextFromNode(rText
);
1292 /**************************************************************************/
1294 void SmBraceNode::CreateTextFromNode(OUStringBuffer
&rText
)
1296 if (GetScaleMode() == SmScaleMode::Height
)
1297 rText
.append("left ");
1299 OUStringBuffer aStrBuf
;
1300 OpeningBrace()->CreateTextFromNode(aStrBuf
);
1301 OUString aStr
= aStrBuf
.makeStringAndClear();
1302 aStr
= comphelper::string::strip(aStr
, ' ');
1303 aStr
= comphelper::string::stripStart(aStr
, '\\');
1304 if (!aStr
.isEmpty())
1306 if (aStr
== "divides")
1307 rText
.append("lline");
1308 else if (aStr
== "parallel")
1309 rText
.append("ldline");
1310 else if (aStr
== "<")
1311 rText
.append("langle");
1317 rText
.append("none ");
1319 Body()->CreateTextFromNode(rText
);
1320 if (GetScaleMode() == SmScaleMode::Height
)
1321 rText
.append("right ");
1323 OUStringBuffer aStrBuf
;
1324 ClosingBrace()->CreateTextFromNode(aStrBuf
);
1325 OUString aStr
= aStrBuf
.makeStringAndClear();
1326 aStr
= comphelper::string::strip(aStr
, ' ');
1327 aStr
= comphelper::string::stripStart(aStr
, '\\');
1328 if (!aStr
.isEmpty())
1330 if (aStr
== "divides")
1331 rText
.append("rline");
1332 else if (aStr
== "parallel")
1333 rText
.append("rdline");
1334 else if (aStr
== ">")
1335 rText
.append("rangle");
1341 rText
.append("none ");
1346 void SmBraceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1348 SmNode
*pLeft
= OpeningBrace(),
1350 *pRight
= ClosingBrace();
1355 pBody
->Arrange(rDev
, rFormat
);
1357 bool bIsScaleNormal
= rFormat
.IsScaleNormalBrackets(),
1358 bScale
= pBody
->GetHeight() > 0 &&
1359 (GetScaleMode() == SmScaleMode::Height
|| bIsScaleNormal
),
1360 bIsABS
= GetToken().eType
== TABS
;
1362 long nFaceHeight
= GetFont().GetFontSize().Height();
1364 // determine oversize in %
1365 sal_uInt16 nPerc
= 0;
1366 if (!bIsABS
&& bScale
)
1367 { // in case of oversize braces...
1368 sal_uInt16 nIndex
= GetScaleMode() == SmScaleMode::Height
?
1369 DIS_BRACKETSIZE
: DIS_NORMALBRACKETSIZE
;
1370 nPerc
= rFormat
.GetDistance(nIndex
);
1373 // determine the height for the braces
1377 nBraceHeight
= pBody
->GetType() == SmNodeType::Bracebody
?
1378 static_cast<SmBracebodyNode
*>(pBody
)->GetBodyHeight()
1379 : pBody
->GetHeight();
1380 nBraceHeight
+= 2 * (nBraceHeight
* nPerc
/ 100L);
1383 nBraceHeight
= nFaceHeight
;
1385 // distance to the argument
1386 nPerc
= bIsABS
? 0 : rFormat
.GetDistance(DIS_BRACKETSPACE
);
1387 long nDist
= nFaceHeight
* nPerc
/ 100L;
1389 // if wanted, scale the braces to the wanted size
1392 Size
aTmpSize (pLeft
->GetFont().GetFontSize());
1393 OSL_ENSURE(pRight
->GetFont().GetFontSize() == aTmpSize
,
1394 "Sm : different font sizes");
1395 aTmpSize
.setWidth( std::min(nBraceHeight
* 60L / 100L,
1396 rFormat
.GetBaseSize().Height() * 3L / 2L) );
1397 // correction factor since change from StarMath to OpenSymbol font
1398 // because of the different font width in the FontMetric
1399 aTmpSize
.setWidth( aTmpSize
.Width() * 182 );
1400 aTmpSize
.setWidth( aTmpSize
.Width() / 267 );
1402 sal_Unicode cChar
= pLeft
->GetToken().cMathChar
;
1403 if (cChar
!= MS_LINE
&& cChar
!= MS_DLINE
&&
1404 cChar
!= MS_VERTLINE
&& cChar
!= MS_DVERTLINE
)
1405 pLeft
->GetFont().SetSize(aTmpSize
);
1407 cChar
= pRight
->GetToken().cMathChar
;
1408 if (cChar
!= MS_LINE
&& cChar
!= MS_DLINE
&&
1409 cChar
!= MS_VERTLINE
&& cChar
!= MS_DVERTLINE
)
1410 pRight
->GetFont().SetSize(aTmpSize
);
1412 pLeft
->AdaptToY(rDev
, nBraceHeight
);
1413 pRight
->AdaptToY(rDev
, nBraceHeight
);
1416 pLeft
->Arrange(rDev
, rFormat
);
1417 pRight
->Arrange(rDev
, rFormat
);
1419 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1420 RectVerAlign eVerAlign
= bScale
? RectVerAlign::CenterY
: RectVerAlign::Baseline
;
1423 aPos
= pLeft
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, eVerAlign
);
1424 aPos
.AdjustX( -nDist
);
1425 pLeft
->MoveTo(aPos
);
1427 aPos
= pRight
->AlignTo(*pBody
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
);
1428 aPos
.AdjustX(nDist
);
1429 pRight
->MoveTo(aPos
);
1431 SmRect::operator = (*pBody
);
1432 ExtendBy(*pLeft
, RectCopyMBL::This
).ExtendBy(*pRight
, RectCopyMBL::This
);
1436 /**************************************************************************/
1439 void SmBracebodyNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1441 size_t nNumSubNodes
= GetNumSubNodes();
1442 if (nNumSubNodes
== 0)
1445 // arrange arguments
1446 for (size_t i
= 0; i
< nNumSubNodes
; i
+= 2)
1447 GetSubNode(i
)->Arrange(rDev
, rFormat
);
1449 // build reference rectangle with necessary info for vertical alignment
1450 SmRect
aRefRect (*GetSubNode(0));
1451 for (size_t i
= 0; i
< nNumSubNodes
; i
+= 2)
1453 SmRect
aTmpRect (*GetSubNode(i
));
1454 Point aPos
= aTmpRect
.AlignTo(aRefRect
, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1455 aTmpRect
.MoveTo(aPos
);
1456 aRefRect
.ExtendBy(aTmpRect
, RectCopyMBL::Xor
);
1459 mnBodyHeight
= aRefRect
.GetHeight();
1461 // scale separators to required height and arrange them
1462 bool bScale
= GetScaleMode() == SmScaleMode::Height
|| rFormat
.IsScaleNormalBrackets();
1463 long nHeight
= bScale
? aRefRect
.GetHeight() : GetFont().GetFontSize().Height();
1464 sal_uInt16 nIndex
= GetScaleMode() == SmScaleMode::Height
?
1465 DIS_BRACKETSIZE
: DIS_NORMALBRACKETSIZE
;
1466 sal_uInt16 nPerc
= rFormat
.GetDistance(nIndex
);
1468 nHeight
+= 2 * (nHeight
* nPerc
/ 100L);
1469 for (size_t i
= 1; i
< nNumSubNodes
; i
+= 2)
1471 SmNode
*pNode
= GetSubNode(i
);
1472 pNode
->AdaptToY(rDev
, nHeight
);
1473 pNode
->Arrange(rDev
, rFormat
);
1476 // horizontal distance between argument and brackets or separators
1477 long nDist
= GetFont().GetFontSize().Height()
1478 * rFormat
.GetDistance(DIS_BRACKETSPACE
) / 100L;
1480 SmNode
*pLeft
= GetSubNode(0);
1481 SmRect::operator = (*pLeft
);
1482 for (size_t i
= 1; i
< nNumSubNodes
; ++i
)
1484 bool bIsSeparator
= i
% 2 != 0;
1485 RectVerAlign eVerAlign
= bIsSeparator
? RectVerAlign::CenterY
: RectVerAlign::Baseline
;
1487 SmNode
*pRight
= GetSubNode(i
);
1488 Point aPosX
= pRight
->AlignTo(*pLeft
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
),
1489 aPosY
= pRight
->AlignTo(aRefRect
, RectPos::Right
, RectHorAlign::Center
, eVerAlign
);
1490 aPosX
.AdjustX(nDist
);
1492 pRight
->MoveTo(Point(aPosX
.X(), aPosY
.Y()));
1493 ExtendBy(*pRight
, bIsSeparator
? RectCopyMBL::This
: RectCopyMBL::Xor
);
1500 /**************************************************************************/
1503 void SmVerticalBraceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1505 SmNode
*pBody
= Body(),
1507 *pScript
= Script();
1512 SmTmpDevice
aTmpDev (rDev
, true);
1513 aTmpDev
.SetFont(GetFont());
1515 pBody
->Arrange(aTmpDev
, rFormat
);
1517 // size is the same as for limits for this part
1518 pScript
->SetSize( Fraction( rFormat
.GetRelSize(SIZ_LIMITS
), 100 ) );
1519 // braces are a bit taller than usually
1520 pBrace
->SetSize( Fraction(3, 2) );
1522 long nItalicWidth
= pBody
->GetItalicWidth();
1523 if (nItalicWidth
> 0)
1524 pBrace
->AdaptToX(aTmpDev
, nItalicWidth
);
1526 pBrace
->Arrange(aTmpDev
, rFormat
);
1527 pScript
->Arrange(aTmpDev
, rFormat
);
1529 // determine the relative position and the distances between each other
1531 long nFontHeight
= pBody
->GetFont().GetFontSize().Height();
1532 long nDistBody
= nFontHeight
* rFormat
.GetDistance(DIS_ORNAMENTSIZE
),
1533 nDistScript
= nFontHeight
;
1534 if (GetToken().eType
== TOVERBRACE
)
1536 eRectPos
= RectPos::Top
;
1537 nDistBody
= - nDistBody
;
1538 nDistScript
*= - rFormat
.GetDistance(DIS_UPPERLIMIT
);
1542 eRectPos
= RectPos::Bottom
;
1543 nDistScript
*= + rFormat
.GetDistance(DIS_LOWERLIMIT
);
1548 Point aPos
= pBrace
->AlignTo(*pBody
, eRectPos
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1549 aPos
.AdjustY(nDistBody
);
1550 pBrace
->MoveTo(aPos
);
1552 aPos
= pScript
->AlignTo(*pBrace
, eRectPos
, RectHorAlign::Center
, RectVerAlign::Baseline
);
1553 aPos
.AdjustY(nDistScript
);
1554 pScript
->MoveTo(aPos
);
1556 SmRect::operator = (*pBody
);
1557 ExtendBy(*pBrace
, RectCopyMBL::This
).ExtendBy(*pScript
, RectCopyMBL::This
);
1561 /**************************************************************************/
1564 SmNode
* SmOperNode::GetSymbol()
1566 SmNode
*pNode
= GetSubNode(0);
1569 if (pNode
->GetType() == SmNodeType::SubSup
)
1570 pNode
= static_cast<SmSubSupNode
*>(pNode
)->GetBody();
1572 OSL_ENSURE(pNode
, "Sm: NULL pointer!");
1577 long SmOperNode::CalcSymbolHeight(const SmNode
&rSymbol
,
1578 const SmFormat
&rFormat
) const
1579 // returns the font height to be used for operator-symbol
1581 long nHeight
= GetFont().GetFontSize().Height();
1583 SmTokenType eTmpType
= GetToken().eType
;
1584 if (eTmpType
== TLIM
|| eTmpType
== TLIMINF
|| eTmpType
== TLIMSUP
)
1587 if (!rFormat
.IsTextmode())
1589 // set minimum size ()
1590 nHeight
+= (nHeight
* 20L) / 100L;
1593 * rFormat
.GetDistance(DIS_OPERATORSIZE
) / 100L;
1594 nHeight
= nHeight
* 686L / 845L;
1597 // correct user-defined symbols to match height of sum from used font
1598 if (rSymbol
.GetToken().eType
== TSPECIAL
)
1599 nHeight
= nHeight
* 845L / 686L;
1605 void SmOperNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1607 SmNode
*pOper
= GetSubNode(0);
1608 SmNode
*pBody
= GetSubNode(1);
1613 SmNode
*pSymbol
= GetSymbol();
1614 pSymbol
->SetSize(Fraction(CalcSymbolHeight(*pSymbol
, rFormat
),
1615 pSymbol
->GetFont().GetFontSize().Height()));
1617 pBody
->Arrange(rDev
, rFormat
);
1618 bool bDynamicallySized
= false;
1619 if (pSymbol
->GetToken().eType
== TINTD
)
1621 long nBodyHeight
= pBody
->GetHeight();
1622 long nFontHeight
= pSymbol
->GetFont().GetFontSize().Height();
1623 if (nFontHeight
< nBodyHeight
)
1625 pSymbol
->SetSize(Fraction(nBodyHeight
, nFontHeight
));
1626 bDynamicallySized
= true;
1629 pOper
->Arrange(rDev
, rFormat
);
1631 long nOrigHeight
= GetFont().GetFontSize().Height(),
1633 * rFormat
.GetDistance(DIS_OPERATORSPACE
) / 100L;
1635 Point aPos
= pOper
->AlignTo(*pBody
, RectPos::Left
, RectHorAlign::Center
, bDynamicallySized
? RectVerAlign::CenterY
: RectVerAlign::Mid
);
1636 aPos
.AdjustX( -nDist
);
1637 pOper
->MoveTo(aPos
);
1639 SmRect::operator = (*pBody
);
1640 ExtendBy(*pOper
, RectCopyMBL::This
);
1644 /**************************************************************************/
1647 void SmAlignNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1648 // set alignment within the entire subtree (including current node)
1650 assert(GetNumSubNodes() == 1);
1652 SmNode
*pNode
= GetSubNode(0);
1655 RectHorAlign eHorAlign
= RectHorAlign::Center
;
1656 switch (GetToken().eType
)
1658 case TALIGNL
: eHorAlign
= RectHorAlign::Left
; break;
1659 case TALIGNC
: eHorAlign
= RectHorAlign::Center
; break;
1660 case TALIGNR
: eHorAlign
= RectHorAlign::Right
; break;
1664 SetRectHorAlign(eHorAlign
);
1666 pNode
->Arrange(rDev
, rFormat
);
1668 SmRect::operator = (pNode
->GetRect());
1672 /**************************************************************************/
1675 void SmAttributNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1677 SmNode
*pAttr
= Attribute(),
1682 pBody
->Arrange(rDev
, rFormat
);
1684 if (GetScaleMode() == SmScaleMode::Width
)
1685 pAttr
->AdaptToX(rDev
, pBody
->GetItalicWidth());
1686 pAttr
->Arrange(rDev
, rFormat
);
1688 // get relative position of attribute
1689 RectVerAlign eVerAlign
;
1691 switch (GetToken().eType
)
1693 eVerAlign
= RectVerAlign::AttributeLo
;
1696 eVerAlign
= RectVerAlign::AttributeMid
;
1699 eVerAlign
= RectVerAlign::AttributeHi
;
1700 if (pBody
->GetType() == SmNodeType::Attribut
)
1701 nDist
= GetFont().GetFontSize().Height()
1702 * rFormat
.GetDistance(DIS_ORNAMENTSPACE
) / 100L;
1704 Point aPos
= pAttr
->AlignTo(*pBody
, RectPos::Attribute
, RectHorAlign::Center
, eVerAlign
);
1705 aPos
.AdjustY( -nDist
);
1706 pAttr
->MoveTo(aPos
);
1708 SmRect::operator = (*pBody
);
1709 ExtendBy(*pAttr
, RectCopyMBL::This
, true);
1712 void SmFontNode::CreateTextFromNode(OUStringBuffer
&rText
)
1716 switch (GetToken().eType
)
1719 rText
.append("bold ");
1722 rText
.append("nbold ");
1725 rText
.append("italic ");
1728 rText
.append("nitalic ");
1731 rText
.append("phantom ");
1735 rText
.append("size ");
1738 case FontSizeType::PLUS
:
1741 case FontSizeType::MINUS
:
1744 case FontSizeType::MULTIPLY
:
1747 case FontSizeType::DIVIDE
:
1750 case FontSizeType::ABSOLUT
:
1754 rText
.append(::rtl::math::doubleToUString(
1755 static_cast<double>(maFontSize
),
1756 rtl_math_StringFormat_Automatic
,
1757 rtl_math_DecimalPlaces_Max
, '.', true));
1762 rText
.append("color black ");
1765 rText
.append("color white ");
1768 rText
.append("color red ");
1771 rText
.append("color green ");
1774 rText
.append("color blue ");
1777 rText
.append("color cyan ");
1780 rText
.append("color magenta ");
1783 rText
.append("color yellow ");
1786 rText
.append("color teal ");
1789 rText
.append("color silver ");
1792 rText
.append("color gray ");
1795 rText
.append("color maroon ");
1798 rText
.append("color purple ");
1801 rText
.append("color lime ");
1804 rText
.append("color olive ");
1807 rText
.append("color navy ");
1810 rText
.append("color aqua ");
1813 rText
.append("color fuchsia ");
1816 rText
.append("font sans ");
1819 rText
.append("font serif ");
1822 rText
.append("font fixed ");
1827 if (GetNumSubNodes() > 1)
1828 GetSubNode(1)->CreateTextFromNode(rText
);
1830 rText
.stripEnd(' ');
1834 void SmFontNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
1836 //! prepare subnodes first
1837 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
1840 switch (GetToken().eType
)
1842 case TFIXED
: nFnt
= FNT_FIXED
; break;
1843 case TSANS
: nFnt
= FNT_SANS
; break;
1844 case TSERIF
: nFnt
= FNT_SERIF
; break;
1849 { GetFont() = rFormat
.GetFont( sal::static_int_cast
< sal_uInt16
>(nFnt
) );
1853 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
1854 //! other font nodes (those with lower depth in the tree)
1855 Flags() |= FontChangeMask::Face
;
1858 void SmFontNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
1860 SmNode
*pNode
= GetSubNode(1);
1863 switch (GetToken().eType
)
1865 pNode
->SetFontSize(maFontSize
, meSizeType
);
1870 pNode
->SetFont(GetFont());
1872 case TUNKNOWN
: break; // no assertion on "font <?> <?>"
1874 case TPHANTOM
: SetPhantom(true); break;
1875 case TBOLD
: SetAttribut(FontAttribute::Bold
); break;
1876 case TITALIC
: SetAttribut(FontAttribute::Italic
); break;
1877 case TNBOLD
: ClearAttribut(FontAttribute::Bold
); break;
1878 case TNITALIC
: ClearAttribut(FontAttribute::Italic
); break;
1880 case TBLACK
: SetColor(COL_BLACK
); break;
1881 case TWHITE
: SetColor(COL_WHITE
); break;
1882 case TRED
: SetColor(COL_LIGHTRED
); break;
1883 case TGREEN
: SetColor(COL_GREEN
); break;
1884 case TBLUE
: SetColor(COL_LIGHTBLUE
); break;
1885 case TCYAN
: SetColor(COL_LIGHTCYAN
); break; // as in Calc
1886 case TMAGENTA
: SetColor(COL_LIGHTMAGENTA
); break; // as in Calc
1887 case TYELLOW
: SetColor(COL_YELLOW
); break;
1888 case TTEAL
: SetColor(COL_CYAN
); break;
1889 case TSILVER
: SetColor(COL_LIGHTGRAY
); break;
1890 case TGRAY
: SetColor(COL_GRAY
); break;
1891 case TMAROON
: SetColor(COL_RED
); break;
1892 case TPURPLE
: SetColor(COL_MAGENTA
); break;
1893 case TLIME
: SetColor(COL_LIGHTGREEN
); break;
1894 case TOLIVE
: SetColor(COL_BROWN
); break;
1895 case TNAVY
: SetColor(COL_BLUE
); break;
1896 case TAQUA
: SetColor(COL_LIGHTCYAN
); break;
1897 case TFUCHSIA
: SetColor(COL_LIGHTMAGENTA
); break;
1900 SAL_WARN("starmath", "unknown case");
1903 pNode
->Arrange(rDev
, rFormat
);
1905 SmRect::operator = (pNode
->GetRect());
1909 void SmFontNode::SetSizeParameter(const Fraction
& rValue
, FontSizeType eType
)
1912 maFontSize
= rValue
;
1916 /**************************************************************************/
1919 SmPolyLineNode::SmPolyLineNode(const SmToken
&rNodeToken
)
1920 : SmGraphicNode(SmNodeType::PolyLine
, rNodeToken
)
1928 void SmPolyLineNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong nNewWidth
)
1930 maToSize
.setWidth( nNewWidth
);
1934 void SmPolyLineNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong nNewHeight
)
1936 GetFont().FreezeBorderWidth();
1937 maToSize
.setHeight( 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 assert(maPoly
.GetSize() == 2);
1953 Point aPointA
, aPointB
;
1954 if (GetToken().eType
== TWIDESLASH
)
1956 aPointA
.setX( nBorderwidth
);
1957 aPointA
.setY( maToSize
.Height() - nBorderwidth
);
1958 aPointB
.setX( maToSize
.Width() - nBorderwidth
);
1959 aPointB
.setY( nBorderwidth
);
1963 OSL_ENSURE(GetToken().eType
== TWIDEBACKSLASH
, "Sm : unexpected token");
1964 aPointA
.setX( nBorderwidth
);
1965 aPointA
.setY( nBorderwidth
);
1966 aPointB
.setX( maToSize
.Width() - nBorderwidth
);
1967 aPointB
.setY( maToSize
.Height() - nBorderwidth
);
1969 maPoly
.SetPoint(aPointA
, 0);
1970 maPoly
.SetPoint(aPointB
, 1);
1972 long nThick
= GetFont().GetFontSize().Height()
1973 * rFormat
.GetDistance(DIS_STROKEWIDTH
) / 100L;
1974 mnWidth
= nThick
+ 2 * nBorderwidth
;
1976 SmRect::operator = (SmRect(maToSize
.Width(), maToSize
.Height()));
1980 /**************************************************************************/
1982 void SmRootSymbolNode::AdaptToX(OutputDevice
&/*rDev*/, sal_uLong nWidth
)
1984 mnBodyWidth
= 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 maToSize
.setWidth( nWidth
);
2005 void SmRectangleNode::AdaptToY(OutputDevice
&/*rDev*/, sal_uLong nHeight
)
2007 GetFont().FreezeBorderWidth();
2008 maToSize
.setHeight( nHeight
);
2012 void SmRectangleNode::Arrange(OutputDevice
&rDev
, const SmFormat
&/*rFormat*/)
2014 long nFontHeight
= GetFont().GetFontSize().Height();
2015 long nWidth
= maToSize
.Width(),
2016 nHeight
= maToSize
.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 , mnFontDesc(nFontDescP
)
2041 , mnSelectionStart(0)
2046 SmTextNode::SmTextNode( const SmToken
&rNodeToken
, sal_uInt16 nFontDescP
)
2047 : SmVisibleNode(SmNodeType::Text
, rNodeToken
)
2048 , mnFontDesc(nFontDescP
)
2049 , mnSelectionStart(0)
2054 void SmTextNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2056 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
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 maText
= 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 its 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
, maText
, GetFont().GetBorderWidth()));
2094 void SmTextNode::CreateTextFromNode(OUStringBuffer
&rText
)
2097 if (GetToken().eType
== TTEXT
)
2104 SmParser aParseTest
;
2105 auto pTable
= aParseTest
.Parse(GetToken().aText
);
2106 assert(pTable
->GetType() == SmNodeType::Table
);
2108 if (pTable
->GetNumSubNodes() == 1)
2110 SmNode
*pResult
= pTable
->GetSubNode(0);
2111 if ( (pResult
->GetType() == SmNodeType::Line
) &&
2112 (pResult
->GetNumSubNodes() == 1) )
2114 pResult
= pResult
->GetSubNode(0);
2115 if (pResult
->GetType() == SmNodeType::Text
)
2120 if ((GetToken().eType
== TIDENT
) && (GetFontDesc() == FNT_FUNCTION
))
2122 //Search for existing functions and remove extraneous keyword
2123 rText
.append("func ");
2126 rText
.append("italic ");
2133 rText
.append(GetToken().aText
);
2140 void SmTextNode::GetAccessibleText( OUStringBuffer
&rText
) const
2142 rText
.append(maText
);
2145 void SmTextNode::AdjustFontDesc()
2147 if (GetToken().eType
== TTEXT
)
2148 mnFontDesc
= FNT_TEXT
;
2149 else if(GetToken().eType
== TFUNC
)
2150 mnFontDesc
= FNT_FUNCTION
;
2153 const SmTokenTableEntry
*pEntry
= SmParser::GetTokenTableEntry( maText
);
2154 if (pEntry
&& pEntry
->nGroup
== TG::Function
) {
2155 nTok
= pEntry
->eType
;
2156 mnFontDesc
= FNT_FUNCTION
;
2158 sal_Unicode firstChar
= maText
[0];
2159 if( ('0' <= firstChar
&& firstChar
<= '9') || firstChar
== '.' || firstChar
== ',') {
2160 mnFontDesc
= FNT_NUMBER
;
2162 } else if (maText
.getLength() > 1) {
2163 mnFontDesc
= FNT_VARIABLE
;
2166 mnFontDesc
= FNT_VARIABLE
;
2170 SmToken tok
= GetToken();
2176 sal_Unicode
SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn
)
2178 //Find the best match in accepted unicode for our private area symbols
2179 static const sal_Unicode aStarMathPrivateToUnicode
[] =
2181 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
2182 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
2183 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
2184 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
2185 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
2186 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
2187 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
2188 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
2189 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
2190 0xE0DA, 0x2190, 0x2191, 0x2193
2192 if ((nIn
>= 0xE080) && (nIn
<= 0xE0DD))
2193 nIn
= aStarMathPrivateToUnicode
[nIn
-0xE080];
2195 //For whatever unicode glyph that equation editor doesn't ship with that
2196 //we have a possible match we can munge it to.
2209 /**************************************************************************/
2211 void SmMatrixNode::CreateTextFromNode(OUStringBuffer
&rText
)
2213 rText
.append("matrix {");
2214 for (size_t i
= 0; i
< mnNumRows
; ++i
)
2216 for (size_t j
= 0; j
< mnNumCols
; ++j
)
2218 SmNode
*pNode
= GetSubNode(i
* mnNumCols
+ j
);
2220 pNode
->CreateTextFromNode(rText
);
2221 if (j
!= mnNumCols
- 1U)
2224 if (i
!= mnNumRows
- 1U)
2225 rText
.append("## ");
2227 rText
.stripEnd(' ');
2231 void SmMatrixNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2235 // initialize array that is to hold the maximum widths of all
2236 // elements (subnodes) in that column.
2237 std::vector
<long> aColWidth(mnNumCols
);
2239 // arrange subnodes and calculate the above arrays contents
2240 size_t nNodes
= GetNumSubNodes();
2241 for (size_t i
= 0; i
< nNodes
; ++i
)
2243 size_t nIdx
= nNodes
- 1 - i
;
2244 if (nullptr != (pNode
= GetSubNode(nIdx
)))
2246 pNode
->Arrange(rDev
, rFormat
);
2247 int nCol
= nIdx
% mnNumCols
;
2248 aColWidth
[nCol
] = std::max(aColWidth
[nCol
], pNode
->GetItalicWidth());
2252 // norm distance from which the following two are calculated
2253 const long nNormDist
= 3 * GetFont().GetFontSize().Height();
2255 // define horizontal and vertical minimal distances that separate
2257 long nHorDist
= nNormDist
* rFormat
.GetDistance(DIS_MATRIXCOL
) / 100L,
2258 nVerDist
= nNormDist
* rFormat
.GetDistance(DIS_MATRIXROW
) / 100L;
2260 // build array that holds the leftmost position for each column
2261 std::vector
<long> aColLeft(mnNumCols
);
2263 for (size_t j
= 0; j
< mnNumCols
; ++j
)
2266 nX
+= aColWidth
[j
] + nHorDist
;
2269 SmRect::operator = (SmRect());
2270 for (size_t i
= 0; i
< mnNumRows
; ++i
)
2274 for (size_t j
= 0; j
< mnNumCols
; ++j
)
2276 SmNode
*pTmpNode
= GetSubNode(i
* mnNumCols
+ j
);
2279 const SmRect
&rNodeRect
= pTmpNode
->GetRect();
2281 // align all baselines in that row if possible
2282 aPos
= rNodeRect
.AlignTo(aLineRect
, RectPos::Right
, RectHorAlign::Center
, RectVerAlign::Baseline
);
2284 // get horizontal alignment
2285 const SmNode
*pCoNode
= pTmpNode
->GetLeftMost();
2286 RectHorAlign eHorAlign
= pCoNode
->GetRectHorAlign();
2288 // calculate horizontal position of element depending on column
2289 // and horizontal alignment
2291 { case RectHorAlign::Left
:
2292 aPos
.setX( aColLeft
[j
] );
2294 case RectHorAlign::Center
:
2295 aPos
.setX( rNodeRect
.GetLeft() + aColLeft
[j
]
2297 - rNodeRect
.GetItalicCenterX() );
2299 case RectHorAlign::Right
:
2300 aPos
.setX( aColLeft
[j
]
2301 + aColWidth
[j
] - rNodeRect
.GetItalicWidth() );
2307 pTmpNode
->MoveTo(aPos
);
2308 aLineRect
.ExtendBy(rNodeRect
, RectCopyMBL::Xor
);
2311 aPos
= aLineRect
.AlignTo(*this, RectPos::Bottom
, RectHorAlign::Center
, RectVerAlign::Baseline
);
2313 aPos
.AdjustY(nVerDist
);
2315 // move 'aLineRect' and rectangles in that line to final position
2316 Point
aDelta(0, // since horizontal alignment is already done
2317 aPos
.Y() - aLineRect
.GetTop());
2318 aLineRect
.Move(aDelta
);
2319 for (size_t j
= 0; j
< mnNumCols
; ++j
)
2321 if (nullptr != (pNode
= GetSubNode(i
* mnNumCols
+ j
)))
2322 pNode
->Move(aDelta
);
2325 ExtendBy(aLineRect
, RectCopyMBL::None
);
2330 void SmMatrixNode::SetRowCol(sal_uInt16 nMatrixRows
, sal_uInt16 nMatrixCols
)
2332 mnNumRows
= nMatrixRows
;
2333 mnNumCols
= nMatrixCols
;
2337 const SmNode
* SmMatrixNode::GetLeftMost() const
2343 /**************************************************************************/
2346 SmMathSymbolNode::SmMathSymbolNode(const SmToken
&rNodeToken
)
2347 : SmSpecialNode(SmNodeType::Math
, rNodeToken
, FNT_MATH
)
2349 sal_Unicode cChar
= GetToken().cMathChar
;
2351 SetText(OUString(cChar
));
2354 void SmMathSymbolNode::AdaptToX(OutputDevice
&rDev
, sal_uLong nWidth
)
2356 // Since there is no function to do this, we try to approximate it:
2357 Size
aFntSize (GetFont().GetFontSize());
2359 //! however the result is a bit better with 'nWidth' as initial font width
2360 aFntSize
.setWidth( nWidth
);
2361 GetFont().SetSize(aFntSize
);
2363 SmTmpDevice
aTmpDev (rDev
, true);
2364 aTmpDev
.SetFont(GetFont());
2366 // get denominator of error factor for width
2367 long nTmpBorderWidth
= GetFont().GetBorderWidth();
2368 long nDenom
= SmRect(aTmpDev
, nullptr, GetText(), nTmpBorderWidth
).GetItalicWidth();
2370 // scale fontwidth with this error factor
2371 aFntSize
.setWidth( aFntSize
.Width() * nWidth
);
2372 aFntSize
.setWidth( aFntSize
.Width() / ( nDenom
? nDenom
: 1) );
2374 GetFont().SetSize(aFntSize
);
2377 void SmMathSymbolNode::AdaptToY(OutputDevice
&rDev
, sal_uLong nHeight
)
2379 GetFont().FreezeBorderWidth();
2380 Size
aFntSize (GetFont().GetFontSize());
2382 // Since we only want to scale the height, we might have
2383 // to determine the font width in order to keep it
2384 if (aFntSize
.Width() == 0)
2386 rDev
.Push(PushFlags::FONT
| PushFlags::MAPMODE
);
2387 rDev
.SetFont(GetFont());
2388 aFntSize
.setWidth( rDev
.GetFontMetric().GetFontSize().Width() );
2391 OSL_ENSURE(aFntSize
.Width() != 0, "Sm: ");
2393 //! however the result is a bit better with 'nHeight' as initial
2395 aFntSize
.setHeight( nHeight
);
2396 GetFont().SetSize(aFntSize
);
2398 SmTmpDevice
aTmpDev (rDev
, true);
2399 aTmpDev
.SetFont(GetFont());
2401 // get denominator of error factor for height
2402 long nTmpBorderWidth
= GetFont().GetBorderWidth();
2403 long nDenom
= SmRect(aTmpDev
, nullptr, GetText(), nTmpBorderWidth
).GetHeight();
2405 // scale fontwidth with this error factor
2406 aFntSize
.setHeight( aFntSize
.Height() * nHeight
);
2407 aFntSize
.setHeight( aFntSize
.Height() / ( nDenom
? nDenom
: 1) );
2409 GetFont().SetSize(aFntSize
);
2413 void SmMathSymbolNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2415 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2417 GetFont() = rFormat
.GetFont(GetFontDesc());
2418 // use same font size as is used for variables
2419 GetFont().SetSize( rFormat
.GetFont( FNT_VARIABLE
).GetFontSize() );
2421 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL
||
2422 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE
,
2423 "wrong charset for character from StarMath/OpenSymbol font");
2425 Flags() |= FontChangeMask::Face
| FontChangeMask::Italic
;
2429 void SmMathSymbolNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2431 const OUString
&rText
= GetText();
2433 if (rText
.isEmpty() || rText
[0] == '\0')
2434 { SmRect::operator = (SmRect());
2438 PrepareAttributes();
2440 GetFont() *= Fraction (rFormat
.GetRelSize(SIZ_TEXT
), 100);
2442 SmTmpDevice
aTmpDev (rDev
, true);
2443 aTmpDev
.SetFont(GetFont());
2445 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, rText
, GetFont().GetBorderWidth()));
2448 void SmMathSymbolNode::CreateTextFromNode(OUStringBuffer
&rText
)
2450 sal_Unicode cChar
= GetToken().cMathChar
;
2451 if (cChar
== MS_INT
&& GetScaleMode() == SmScaleMode::Height
)
2452 rText
.append("intd ");
2454 MathType::LookupChar(cChar
, rText
, 3);
2457 void SmRectangleNode::CreateTextFromNode(OUStringBuffer
&rText
)
2459 switch (GetToken().eType
)
2462 rText
.append("underline ");
2465 rText
.append("overline ");
2468 rText
.append("overstrike ");
2475 void SmAttributNode::CreateTextFromNode(OUStringBuffer
&rText
)
2478 assert(GetNumSubNodes() == 2);
2480 sal_Unicode nLast
=0;
2481 if (nullptr != (pNode
= Attribute()))
2483 OUStringBuffer aStr
;
2484 pNode
->CreateTextFromNode(aStr
);
2485 if (aStr
.getLength() > 1)
2492 case MS_BAR
: // MACRON
2493 rText
.append("overline ");
2495 case MS_DOT
: // DOT ABOVE
2496 rText
.append("dot ");
2498 case 0x2dc: // SMALL TILDE
2499 rText
.append("widetilde ");
2501 case MS_DDOT
: // DIAERESIS
2502 rText
.append("ddot ");
2507 case MS_DDDOT
: // COMBINING THREE DOTS ABOVE
2508 rText
.append("dddot ");
2510 case MS_ACUTE
: // ACUTE ACCENT
2511 case MS_COMBACUTE
: // COMBINING ACUTE ACCENT
2512 rText
.append("acute ");
2514 case MS_GRAVE
: // GRAVE ACCENT
2515 case MS_COMBGRAVE
: // COMBINING GRAVE ACCENT
2516 rText
.append("grave ");
2518 case MS_CHECK
: // CARON
2519 case MS_COMBCHECK
: // COMBINING CARON
2520 rText
.append("check ");
2522 case MS_BREVE
: // BREVE
2523 case MS_COMBBREVE
: // COMBINING BREVE
2524 rText
.append("breve ");
2526 case MS_CIRCLE
: // RING ABOVE
2527 case MS_COMBCIRCLE
: // COMBINING RING ABOVE
2528 rText
.append("circle ");
2530 case MS_RIGHTARROW
: // RIGHTWARDS ARROW
2531 case MS_VEC
: // COMBINING RIGHT ARROW ABOVE
2532 rText
.append("vec ");
2534 case MS_TILDE
: // TILDE
2535 case MS_COMBTILDE
: // COMBINING TILDE
2536 rText
.append("tilde ");
2538 case MS_HAT
: // CIRCUMFLEX ACCENT
2539 case MS_COMBHAT
: // COMBINING CIRCUMFLEX ACCENT
2540 rText
.append("hat ");
2542 case MS_COMBBAR
: // COMBINING MACRON
2543 rText
.append("bar ");
2546 rText
.append(OUStringLiteral1(nLast
));
2552 if (nullptr != (pNode
= Body()))
2553 pNode
->CreateTextFromNode(rText
);
2555 rText
.stripEnd(' ');
2557 if (nLast
== 0xE082)
2558 rText
.append(" overbrace {}");
2563 /**************************************************************************/
2565 static bool lcl_IsFromGreekSymbolSet( const OUString
&rTokenText
)
2569 // valid symbol name needs to have a '%' at pos 0 and at least an additional char
2570 if (rTokenText
.getLength() > 2 && rTokenText
[0] == u
'%')
2572 OUString
aName( rTokenText
.copy(1) );
2573 SmSym
*pSymbol
= SM_MOD()->GetSymbolManager().GetSymbolByName( aName
);
2574 if (pSymbol
&& SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol
->GetSymbolSetName()) == "Greek")
2582 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType
, const SmToken
&rNodeToken
, sal_uInt16 _nFontDesc
)
2583 : SmTextNode(eNodeType
, rNodeToken
, _nFontDesc
)
2584 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken
.aText
))
2589 SmSpecialNode::SmSpecialNode(const SmToken
&rNodeToken
)
2590 : SmTextNode(SmNodeType::Special
, rNodeToken
, FNT_MATH
) // default Font isn't always correct!
2591 , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken
.aText
))
2596 void SmSpecialNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2598 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2601 SmModule
*pp
= SM_MOD();
2603 OUString
aName(GetToken().aText
.copy(1));
2604 if (nullptr != (pSym
= pp
->GetSymbolManager().GetSymbolByName( aName
)))
2606 sal_UCS4 cChar
= pSym
->GetCharacter();
2607 OUString
aTmp( &cChar
, 1 );
2609 GetFont() = pSym
->GetFace();
2613 SetText( GetToken().aText
);
2614 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
2616 // use same font size as is used for variables
2617 GetFont().SetSize( rFormat
.GetFont( FNT_VARIABLE
).GetFontSize() );
2619 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2620 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2621 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2623 //! see also SmFontStyles::GetStyleName
2624 if (IsItalic( GetFont() ))
2625 SetAttribut(FontAttribute::Italic
);
2626 if (IsBold( GetFont() ))
2627 SetAttribut(FontAttribute::Bold
);
2629 Flags() |= FontChangeMask::Face
;
2631 if (mbIsFromGreekSymbolSet
)
2633 OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
2634 bool bItalic
= false;
2635 sal_Int16 nStyle
= rFormat
.GetGreekCharStyle();
2636 OSL_ENSURE( nStyle
>= 0 && nStyle
<= 2, "unexpected value for GreekCharStyle" );
2639 else if (nStyle
== 2)
2641 const OUString
& rTmp(GetText());
2642 if (!rTmp
.isEmpty())
2644 static const sal_Unicode cUppercaseAlpha
= 0x0391;
2645 static const sal_Unicode cUppercaseOmega
= 0x03A9;
2646 sal_Unicode cChar
= rTmp
[0];
2647 // uppercase letters should be straight and lowercase letters italic
2648 bItalic
= !(cUppercaseAlpha
<= cChar
&& cChar
<= cUppercaseOmega
);
2653 Attributes() |= FontAttribute::Italic
;
2655 Attributes() &= ~FontAttribute::Italic
;
2660 void SmSpecialNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2662 PrepareAttributes();
2664 SmTmpDevice
aTmpDev (rDev
, true);
2665 aTmpDev
.SetFont(GetFont());
2667 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(), GetFont().GetBorderWidth()));
2670 /**************************************************************************/
2673 void SmGlyphSpecialNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2675 PrepareAttributes();
2677 SmTmpDevice
aTmpDev (rDev
, true);
2678 aTmpDev
.SetFont(GetFont());
2680 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(),
2681 GetFont().GetBorderWidth()).AsGlyphRect());
2685 /**************************************************************************/
2688 void SmPlaceNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2690 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2692 GetFont().SetColor(COL_GRAY
);
2693 Flags() |= FontChangeMask::Color
| FontChangeMask::Face
| FontChangeMask::Italic
;
2697 void SmPlaceNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2699 PrepareAttributes();
2701 SmTmpDevice
aTmpDev (rDev
, true);
2702 aTmpDev
.SetFont(GetFont());
2704 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, GetText(), GetFont().GetBorderWidth()));
2708 /**************************************************************************/
2711 void SmErrorNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2713 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2715 GetFont().SetColor(COL_RED
);
2716 Flags() |= FontChangeMask::Phantom
| FontChangeMask::Bold
| FontChangeMask::Italic
2717 | FontChangeMask::Color
| FontChangeMask::Face
| FontChangeMask::Size
;
2721 void SmErrorNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2723 PrepareAttributes();
2725 SmTmpDevice
aTmpDev (rDev
, true);
2726 aTmpDev
.SetFont(GetFont());
2728 const OUString
&rText
= GetText();
2729 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, rText
, GetFont().GetBorderWidth()));
2732 /**************************************************************************/
2734 void SmBlankNode::IncreaseBy(const SmToken
&rToken
, sal_uInt32 nMultiplyBy
)
2736 switch(rToken
.eType
)
2738 case TBLANK
: mnNum
+= (4 * nMultiplyBy
); break;
2739 case TSBLANK
: mnNum
+= (1 * nMultiplyBy
); break;
2745 void SmBlankNode::Prepare(const SmFormat
&rFormat
, const SmDocShell
&rDocShell
, int nDepth
)
2747 SmNode::Prepare(rFormat
, rDocShell
, nDepth
);
2749 // Here it need/should not be the StarMath font, so that for the character
2750 // used in Arrange a normal (non-clipped) rectangle is generated
2751 GetFont() = rFormat
.GetFont(FNT_VARIABLE
);
2753 Flags() |= FontChangeMask::Face
| FontChangeMask::Bold
| FontChangeMask::Italic
;
2757 void SmBlankNode::Arrange(OutputDevice
&rDev
, const SmFormat
&rFormat
)
2759 SmTmpDevice
aTmpDev (rDev
, true);
2760 aTmpDev
.SetFont(GetFont());
2762 // make distance depend on the font height
2763 // (so that it increases when scaling (e.g. size *2 {a ~ b})
2764 long nDist
= GetFont().GetFontSize().Height() / 10L,
2765 nSpace
= mnNum
* nDist
;
2767 // get a SmRect with Baseline and all the bells and whistles
2768 SmRect::operator = (SmRect(aTmpDev
, &rFormat
, OUString(' '),
2769 GetFont().GetBorderWidth()));
2771 // and resize it to the requested size
2772 SetItalicSpaces(0, 0);
2776 void SmBlankNode::CreateTextFromNode(OUStringBuffer
&rText
)
2780 sal_uInt16 nWide
= mnNum
/ 4;
2781 sal_uInt16 nNarrow
= mnNum
% 4;
2782 for (sal_uInt16 i
= 0; i
< nWide
; i
++)
2784 for (sal_uInt16 i
= 0; i
< nNarrow
; i
++)
2790 /**************************************************************************/
2791 //Implementation of all accept methods for SmVisitor
2793 void SmTableNode::Accept(SmVisitor
* pVisitor
) {
2794 pVisitor
->Visit(this);
2797 void SmBraceNode::Accept(SmVisitor
* pVisitor
) {
2798 pVisitor
->Visit(this);
2801 void SmBracebodyNode::Accept(SmVisitor
* pVisitor
) {
2802 pVisitor
->Visit(this);
2805 void SmOperNode::Accept(SmVisitor
* pVisitor
) {
2806 pVisitor
->Visit(this);
2809 void SmAlignNode::Accept(SmVisitor
* pVisitor
) {
2810 pVisitor
->Visit(this);
2813 void SmAttributNode::Accept(SmVisitor
* pVisitor
) {
2814 pVisitor
->Visit(this);
2817 void SmFontNode::Accept(SmVisitor
* pVisitor
) {
2818 pVisitor
->Visit(this);
2821 void SmUnHorNode::Accept(SmVisitor
* pVisitor
) {
2822 pVisitor
->Visit(this);
2825 void SmBinHorNode::Accept(SmVisitor
* pVisitor
) {
2826 pVisitor
->Visit(this);
2829 void SmBinVerNode::Accept(SmVisitor
* pVisitor
) {
2830 pVisitor
->Visit(this);
2833 void SmBinDiagonalNode::Accept(SmVisitor
* pVisitor
) {
2834 pVisitor
->Visit(this);
2837 void SmSubSupNode::Accept(SmVisitor
* pVisitor
) {
2838 pVisitor
->Visit(this);
2841 void SmMatrixNode::Accept(SmVisitor
* pVisitor
) {
2842 pVisitor
->Visit(this);
2845 void SmPlaceNode::Accept(SmVisitor
* pVisitor
) {
2846 pVisitor
->Visit(this);
2849 void SmTextNode::Accept(SmVisitor
* pVisitor
) {
2850 pVisitor
->Visit(this);
2853 void SmSpecialNode::Accept(SmVisitor
* pVisitor
) {
2854 pVisitor
->Visit(this);
2857 void SmGlyphSpecialNode::Accept(SmVisitor
* pVisitor
) {
2858 pVisitor
->Visit(this);
2861 void SmMathSymbolNode::Accept(SmVisitor
* pVisitor
) {
2862 pVisitor
->Visit(this);
2865 void SmBlankNode::Accept(SmVisitor
* pVisitor
) {
2866 pVisitor
->Visit(this);
2869 void SmErrorNode::Accept(SmVisitor
* pVisitor
) {
2870 pVisitor
->Visit(this);
2873 void SmLineNode::Accept(SmVisitor
* pVisitor
) {
2874 pVisitor
->Visit(this);
2877 void SmExpressionNode::Accept(SmVisitor
* pVisitor
) {
2878 pVisitor
->Visit(this);
2881 void SmPolyLineNode::Accept(SmVisitor
* pVisitor
) {
2882 pVisitor
->Visit(this);
2885 void SmRootNode::Accept(SmVisitor
* pVisitor
) {
2886 pVisitor
->Visit(this);
2889 void SmRootSymbolNode::Accept(SmVisitor
* pVisitor
) {
2890 pVisitor
->Visit(this);
2893 void SmRectangleNode::Accept(SmVisitor
* pVisitor
) {
2894 pVisitor
->Visit(this);
2897 void SmVerticalBraceNode::Accept(SmVisitor
* pVisitor
) {
2898 pVisitor
->Visit(this);
2901 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */