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