xref: /core/starmath/source/node.cxx (revision 3c91fb75)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
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>
33 
34 namespace {
35 
36 template<typename F>
ForEachNonNull(SmNode * pNode,F && f)37 void ForEachNonNull(SmNode *pNode, F && f)
38 {
39     size_t nSize = pNode->GetNumSubNodes();
40     for (size_t i = 0; i < nSize; ++i)
41     {
42         SmNode *pSubNode = pNode->GetSubNode(i);
43         if (pSubNode != nullptr)
44             f(pSubNode);
45     }
46 }
47 
48 }
49 
SmNode(SmNodeType eNodeType,SmToken aNodeToken)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 )
61 {
62 }
63 
~SmNode()64 SmNode::~SmNode()
65 {
66 }
67 
GetLeftMost() const68 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).
72 {
73     const SmNode *pNode = GetNumSubNodes() > 0 ?
74                         GetSubNode(0) : nullptr;
75 
76     return pNode ? pNode->GetLeftMost() : this;
77 }
78 
79 
SetPhantom(bool bIsPhantomP)80 void SmNode::SetPhantom(bool bIsPhantomP)
81 {
82     if (! (Flags() & FontChangeMask::Phantom))
83         mbIsPhantom = bIsPhantomP;
84 
85     bool b = mbIsPhantom;
86     ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
87 }
88 
89 
SetColor(const Color & rColor)90 void SmNode::SetColor(const Color& rColor)
91 {
92     if (! (Flags() & FontChangeMask::Color))
93         GetFont().SetColor(rColor);
94 
95     ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
96 }
97 
98 
SetAttribute(FontAttribute nAttrib)99 void SmNode::SetAttribute(FontAttribute nAttrib)
100 {
101     if (
102         (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
103         (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
104        )
105     {
106         mnAttributes |= nAttrib;
107     }
108 
109     ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribute(nAttrib);});
110 }
111 
112 
ClearAttribute(FontAttribute nAttrib)113 void SmNode::ClearAttribute(FontAttribute nAttrib)
114 {
115     if (
116         (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
117         (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
118        )
119     {
120         mnAttributes &= ~nAttrib;
121     }
122 
123     ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribute(nAttrib);});
124 }
125 
126 
SetFont(const SmFace & rFace)127 void SmNode::SetFont(const SmFace &rFace)
128 {
129     if (!(Flags() & FontChangeMask::Face))
130         GetFont() = rFace;
131     ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
132 }
133 
134 
SetFontSize(const Fraction & rSize,FontSizeType nType)135 void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
136     //! 'rSize' is in units of pts
137 {
138     Size  aFntSize;
139 
140     if (!(Flags() & FontChangeMask::Size))
141     {
142         Fraction aVal(conversionFract(o3tl::Length::pt, SmO3tlLengthUnit()) * rSize);
143         tools::Long      nHeight = static_cast<tools::Long>(aVal);
144 
145         aFntSize = GetFont().GetFontSize();
146         aFntSize.setWidth( 0 );
147         switch(nType)
148         {
149             case FontSizeType::ABSOLUT:
150                 aFntSize.setHeight( nHeight );
151                 break;
152 
153             case FontSizeType::PLUS:
154                 aFntSize.AdjustHeight(nHeight );
155                 break;
156 
157             case FontSizeType::MINUS:
158                 aFntSize.AdjustHeight( -nHeight );
159                 break;
160 
161             case FontSizeType::MULTIPLY:
162                 aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) * rSize) );
163                 break;
164 
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;
171         }
172 
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 );
177 
178         GetFont().SetSize(aFntSize);
179     }
180 
181     ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
182 }
183 
184 
SetSize(const Fraction & rSize)185 void SmNode::SetSize(const Fraction &rSize)
186 {
187     GetFont() *= rSize;
188 
189     ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
190 }
191 
192 
SetRectHorAlign(RectHorAlign eHorAlign,bool bApplyToSubTree)193 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
194 {
195     meRectHorAlign = eHorAlign;
196 
197     if (bApplyToSubTree)
198         ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
199 }
200 
201 
PrepareAttributes()202 void SmNode::PrepareAttributes()
203 {
204     GetFont().SetWeight((Attributes() & FontAttribute::Bold)   ? WEIGHT_BOLD   : WEIGHT_NORMAL);
205     GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
206 }
207 
208 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)209 void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
210 {
211     if (nDepth > 1024)
212         throw std::range_error("parser depth limit");
213 
214     mbIsPhantom  = false;
215     mnFlags      = FontChangeMask::None;
216     mnAttributes = FontAttribute::None;
217 
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;
222     }
223 
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);
229 
230     ForEachNonNull(this, [&rFormat, &rDocShell, nDepth](SmNode *pNode){pNode->Prepare(rFormat, rDocShell, nDepth + 1);});
231 }
232 
Move(const Point & rVector)233 void SmNode::Move(const Point& rVector)
234 {
235     if (rVector.X() == 0  &&  rVector.Y() == 0)
236         return;
237 
238     SmRect::Move(rVector);
239 
240     ForEachNonNull(this, [&rVector](SmNode *pNode){pNode->Move(rVector);});
241 }
242 
AdaptToX(OutputDevice &,sal_uLong)243 void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
244 {
245 }
246 
247 
AdaptToY(OutputDevice &,sal_uLong)248 void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
249 {
250 }
251 
252 
FindTokenAt(sal_uInt16 nRow,sal_uInt16 nCol) const253 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)
257 {
258     if (    IsVisible()
259         &&  nRow == GetSelection().nStartPara
260         &&  nCol >= GetSelection().nStartPos  &&  nCol <= GetSelection().nEndPos )
261         return this;
262     else
263     {
264         size_t nNumSubNodes = GetNumSubNodes();
265         for (size_t i = 0;  i < nNumSubNodes; ++i)
266         {
267             const SmNode *pNode = GetSubNode(i);
268 
269             if (!pNode)
270                 continue;
271 
272             const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
273             if (pResult)
274                 return pResult;
275         }
276     }
277 
278     return nullptr;
279 }
280 
281 
FindRectClosestTo(const Point & rPoint) const282 const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
283 {
284     tools::Long          nDist   = LONG_MAX;
285     const SmNode *pResult = nullptr;
286 
287     if (IsVisible())
288         pResult = this;
289     else
290     {
291         size_t nNumSubNodes = GetNumSubNodes();
292         for (size_t i = 0;  i < nNumSubNodes; ++i)
293         {
294             const SmNode *pNode = GetSubNode(i);
295 
296             if (!pNode)
297                 continue;
298 
299             const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
300             if (pFound)
301             {
302                 tools::Long nTmp = pFound->OrientedDist(rPoint);
303                 if (nTmp < nDist)
304                 {
305                     nDist   = nTmp;
306                     pResult = pFound;
307 
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;
316                 }
317             }
318         }
319     }
320 
321     return pResult;
322 }
323 
FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const324 const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
325 {
326     const SmNode *pResult = nullptr;
327 
328     sal_Int32 nIdx = GetAccessibleIndex();
329     OUStringBuffer aTxt;
330     if (nIdx >= 0)
331         GetAccessibleText( aTxt );  // get text if used in following 'if' statement
332 
333     if (nIdx >= 0
334         &&  nIdx <= nAccIdx  &&  nAccIdx < nIdx + aTxt.getLength())
335         pResult = this;
336     else
337     {
338         size_t nNumSubNodes = GetNumSubNodes();
339         for (size_t i = 0; i < nNumSubNodes; ++i)
340         {
341             const SmNode *pNode = GetSubNode(i);
342             if (!pNode)
343                 continue;
344 
345             pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
346             if (pResult)
347                 return pResult;
348         }
349     }
350 
351     return pResult;
352 }
353 
354 
~SmStructureNode()355 SmStructureNode::~SmStructureNode()
356 {
357     ForEachNonNull(this, std::default_delete<SmNode>());
358 }
359 
360 
ClearSubNodes()361 void SmStructureNode::ClearSubNodes()
362 {
363     maSubNodes.clear();
364 }
365 
SetSubNodes(std::unique_ptr<SmNode> pFirst,std::unique_ptr<SmNode> pSecond,std::unique_ptr<SmNode> pThird)366 void SmStructureNode::SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
367 {
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();
376 
377     ClaimPaternity();
378 }
379 
SetSubNodes(SmNode * pFirst,SmNode * pSecond,SmNode * pThird)380 void SmStructureNode::SetSubNodes(SmNode* pFirst, SmNode* pSecond, SmNode* pThird)
381 {
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;
390 
391     ClaimPaternity();
392 }
393 
SetSubNodesBinMo(std::unique_ptr<SmNode> pFirst,std::unique_ptr<SmNode> pSecond,std::unique_ptr<SmNode> pThird)394 void SmStructureNode::SetSubNodesBinMo(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
395 {
396     if(GetType()==SmNodeType::BinDiagonal)
397     {
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();
406     }
407     else
408     {
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();
417     }
418     ClaimPaternity();
419 }
420 
SetSubNodes(SmNodeArray && rNodeArray)421 void SmStructureNode::SetSubNodes(SmNodeArray&& rNodeArray)
422 {
423     maSubNodes = std::move(rNodeArray);
424     ClaimPaternity();
425 }
426 
IsVisible() const427 bool SmStructureNode::IsVisible() const
428 {
429     return false;
430 }
431 
GetNumSubNodes() const432 size_t SmStructureNode::GetNumSubNodes() const
433 {
434     return maSubNodes.size();
435 }
436 
GetSubNode(size_t nIndex)437 SmNode* SmStructureNode::GetSubNode(size_t nIndex)
438 {
439     return maSubNodes[nIndex];
440 }
441 
GetSubNodeBinMo(size_t nIndex) const442 SmNode* SmStructureNode::GetSubNodeBinMo(size_t nIndex) const
443 {
444     if(GetType()==SmNodeType::BinDiagonal)
445     {
446         if (nIndex==1)
447             nIndex = 2;
448         else if (nIndex==2)
449             nIndex = 1;
450     }
451     return maSubNodes[nIndex];
452 }
453 
GetAccessibleText(OUStringBuffer & rText) const454 void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
455 {
456     ForEachNonNull(const_cast<SmStructureNode *>(this),
457                    [&rText](SmNode *pNode)
458         {
459             if (pNode->IsVisible())
460                 pNode->SetAccessibleIndex(rText.getLength());
461             pNode->GetAccessibleText( rText );
462         });
463 }
464 
ClaimPaternity()465 void SmStructureNode::ClaimPaternity()
466 {
467     ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
468 }
469 
IndexOfSubNode(SmNode const * pSubNode)470 int SmStructureNode::IndexOfSubNode(SmNode const * pSubNode)
471 {
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;
477 }
478 
SetSubNode(size_t nIndex,SmNode * pNode)479 void SmStructureNode::SetSubNode(size_t nIndex, SmNode* pNode)
480 {
481     size_t size = maSubNodes.size();
482     if (size <= nIndex)
483     {
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;
489     }
490     maSubNodes[nIndex] = pNode;
491     if (pNode)
492         pNode->SetParent(this);
493 }
494 
IsVisible() const495 bool SmVisibleNode::IsVisible() const
496 {
497     return true;
498 }
499 
GetNumSubNodes() const500 size_t SmVisibleNode::GetNumSubNodes() const
501 {
502     return 0;
503 }
504 
GetSubNode(size_t)505 SmNode * SmVisibleNode::GetSubNode(size_t /*nIndex*/)
506 {
507     return nullptr;
508 }
509 
GetAccessibleText(OUStringBuffer & rText) const510 void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
511 {
512     rText.append(GetToken().aText);
513 }
514 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)515 void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
516     // arranges all subnodes in one column
517 {
518     SmNode *pNode;
519     size_t nSize = GetNumSubNodes();
520 
521     // make distance depend on font size
522     tools::Long  nDist = +(rFormat.GetDistance(DIS_VERTICAL)
523                     * GetFont().GetFontSize().Height()) / 100;
524 
525     if (nSize < 1)
526         return;
527 
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)
532     {
533         if (nullptr != (pNode = GetSubNode(i)))
534         {   pNode->Arrange(rDev, rFormat);
535             if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
536                 nMaxWidth = nTmp;
537         }
538     }
539 
540     Point  aPos;
541     SmRect::operator = (SmRect(nMaxWidth, 1));
542     for (size_t i = 0; i < nSize; ++i)
543     {
544         if (nullptr != (pNode = GetSubNode(i)))
545         {   const SmRect &rNodeRect = pNode->GetRect();
546             const SmNode *pCoNode   = pNode->GetLeftMost();
547             RectHorAlign  eHorAlign = pCoNode->GetRectHorAlign();
548 
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);
555         }
556     }
557     // #i972#
558     if (HasBaseline())
559         mnFormulaBaseline = GetBaseline();
560     else
561     {
562         SmTmpDevice aTmpDev (rDev, true);
563         aTmpDev.SetFont(GetFont());
564 
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();
570     }
571 }
572 
GetLeftMost() const573 const SmNode * SmTableNode::GetLeftMost() const
574 {
575     return this;
576 }
577 
578 
GetFormulaBaseline() const579 tools::Long SmTableNode::GetFormulaBaseline() const
580 {
581     return mnFormulaBaseline;
582 }
583 
584 
585 /**************************************************************************/
586 
587 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)588 void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
589 {
590     SmNode::Prepare(rFormat, rDocShell, nDepth);
591 
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;
596 }
597 
598 
599 /**************************************************************************/
600 
601 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)602 void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
603     // arranges all subnodes in one row with some extra space between
604 {
605     SmNode *pNode;
606     size_t nSize = GetNumSubNodes();
607     for (size_t i = 0; i < nSize; ++i)
608     {
609         if (nullptr != (pNode = GetSubNode(i)))
610             pNode->Arrange(rDev, rFormat);
611     }
612 
613     SmTmpDevice aTmpDev (rDev, true);
614     aTmpDev.SetFont(GetFont());
615 
616     if (nSize < 1)
617     {
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;
630     }
631 
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;
636 
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());
641 
642     for (size_t i = 1;  i < nSize; ++i)
643     {
644         if (nullptr != (pNode = GetSubNode(i)))
645         {
646             aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
647 
648             // add horizontal space to the left for each but the first sub node
649             aPos.AdjustX(nDist );
650 
651             pNode->MoveTo(aPos);
652             ExtendBy( *pNode, RectCopyMBL::Xor );
653         }
654     }
655 }
656 
657 
658 /**************************************************************************/
659 
660 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)661 void SmExpressionNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
662     // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
663 {
664     SmLineNode::Arrange(rDev, rFormat);
665 
666     //  copy alignment of leftmost subnode if any
667     const SmNode *pNode = GetLeftMost();
668     if (pNode)
669         SetRectHorAlign(pNode->GetRectHorAlign(), false);
670 }
671 
672 
673 /**************************************************************************/
674 
675 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)676 void SmUnHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
677 {
678     bool  bIsPostfix = GetToken().eType == TFACT;
679 
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);
686 
687     pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
688     pOper->Arrange(rDev, rFormat);
689     pBody->Arrange(rDev, rFormat);
690 
691     tools::Long nDist = (pOper->GetRect().GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
692 
693     SmRect::operator = (*pNode0);
694 
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);
699 }
700 
701 
702 /**************************************************************************/
703 
704 namespace {
705 
lcl_GetHeightVerOffset(const SmRect & rRect,tools::Long & rHeight,tools::Long & rVerOffset)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'
709 {
710     rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
711     rHeight    = rRect.GetHeight() - rVerOffset;
712 
713     OSL_ENSURE(rHeight    >= 0, "Sm : Ooops...");
714     OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
715 }
716 
717 
lcl_GetExtraPos(const SmRect & rRootSymbol,const SmRect & rExtra)718 Point lcl_GetExtraPos(const SmRect &rRootSymbol,
719                               const SmRect &rExtra)
720 {
721     const Size &rSymSize = rRootSymbol.GetSize();
722 
723     Point  aPos = rRootSymbol.GetTopLeft()
724             + Point((rSymSize.Width()  * 70) / 100,
725                     (rSymSize.Height() * 52) / 100);
726 
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 );
736 
737     return aPos;
738 }
739 
740 }
741 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)742 void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
743 {
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);
753 
754     pBody->Arrange(rDev, rFormat);
755 
756     tools::Long  nHeight,
757           nVerOffset;
758     lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset);
759     nHeight += rFormat.GetDistance(DIS_ROOT)
760                * GetFont().GetFontSize().Height() / 100;
761 
762     if (nHeight < 0)
763     {
764         SAL_WARN("starmath", "negative height");
765         nHeight = 0;
766     }
767 
768     // font specialist advised to change the width first
769     pRootSym->AdaptToY(rDev, nHeight);
770     pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
771 
772     pRootSym->Arrange(rDev, rFormat);
773 
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());
779 
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);
785 
786     if (pExtra)
787     {   pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
788         pExtra->Arrange(rDev, rFormat);
789 
790         aPos = lcl_GetExtraPos(*pRootSym, *pExtra);
791         pExtra->MoveTo(aPos);
792     }
793 
794     SmRect::operator = (*pBody);
795     ExtendBy(*pRootSym, RectCopyMBL::This);
796     if (pExtra)
797         ExtendBy(*pExtra, RectCopyMBL::This, true);
798 }
799 
800 /**************************************************************************/
801 
802 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)803 void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
804 {
805     SmNode *pLeft  = LeftOperand(),
806            *pOper  = Symbol(),
807            *pRight = RightOperand();
808     assert(pLeft);
809     assert(pOper);
810     assert(pRight);
811 
812     pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
813 
814     pLeft ->Arrange(rDev, rFormat);
815     pOper ->Arrange(rDev, rFormat);
816     pRight->Arrange(rDev, rFormat);
817 
818     const SmRect &rOpRect = pOper->GetRect();
819 
820     tools::Long nMul;
821     if (o3tl::checked_multiply<tools::Long>(rOpRect.GetWidth(), rFormat.GetDistance(DIS_HORIZONTAL), nMul))
822     {
823         SAL_WARN("starmath", "integer overflow");
824         return;
825     }
826 
827     tools::Long nDist = nMul / 100;
828 
829     SmRect::operator = (*pLeft);
830 
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);
836 
837     aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
838     aPos.AdjustX(nDist );
839 
840     pRight->MoveTo(aPos);
841     ExtendBy(*pRight, RectCopyMBL::Xor);
842 }
843 
844 
845 /**************************************************************************/
846 
847 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)848 void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
849 {
850     SmNode *pNum   = GetSubNode(0),
851            *pLine  = GetSubNode(1),
852            *pDenom = GetSubNode(2);
853     assert(pNum);
854     assert(pLine);
855     assert(pDenom);
856 
857     bool  bIsTextmode = rFormat.IsTextmode();
858     if (bIsTextmode)
859     {
860         Fraction  aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
861         pNum  ->SetSize(aFraction);
862         pLine ->SetSize(aFraction);
863         pDenom->SetSize(aFraction);
864     }
865 
866     pNum  ->Arrange(rDev, rFormat);
867     pDenom->Arrange(rDev, rFormat);
868 
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;
877 
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);
882 
883     // get horizontal alignment for numerator
884     const SmNode *pLM       = pNum->GetLeftMost();
885     RectHorAlign  eHorAlign = pLM->GetRectHorAlign();
886 
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);
891 
892     // get horizontal alignment for denominator
893     pLM       = pDenom->GetLeftMost();
894     eHorAlign = pLM->GetRectHorAlign();
895 
896     // move denominator to its position
897     aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
898     aPos.AdjustY(nDenomDist );
899     pDenom->MoveTo(aPos);
900 
901     SmRect::operator = (*pNum);
902     ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
903 }
904 
GetLeftMost() const905 const SmNode * SmBinVerNode::GetLeftMost() const
906 {
907     return this;
908 }
909 
910 
911 namespace {
912 
913 /// @return value of the determinant formed by the two points
Det(const Point & rHeading1,const Point & rHeading2)914 double Det(const Point &rHeading1, const Point &rHeading2)
915 {
916     return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
917 }
918 
919 
920 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
921 /// and has the direction vector 'rHeading2'
IsPointInLine(const Point & rPoint1,const Point & rPoint2,const Point & rHeading2)922 bool IsPointInLine(const Point &rPoint1,
923                    const Point &rPoint2, const Point &rHeading2)
924 {
925     assert(rHeading2 != Point());
926 
927     bool bRes = false;
928     static const double eps = 5.0 * DBL_EPSILON;
929 
930     double fLambda;
931     if (std::abs(rHeading2.X()) > std::abs(rHeading2.Y()))
932     {
933         fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
934         bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
935     }
936     else
937     {
938         fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
939         bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
940     }
941 
942     return bRes;
943 }
944 
945 
GetLineIntersectionPoint(Point & rResult,const Point & rPoint1,const Point & rHeading1,const Point & rPoint2,const Point & rHeading2)946 sal_uInt16 GetLineIntersectionPoint(Point &rResult,
947                                 const Point& rPoint1, const Point &rHeading1,
948                                 const Point& rPoint2, const Point &rHeading2)
949 {
950     assert(rHeading1 != Point());
951     assert(rHeading2 != Point());
952 
953     sal_uInt16 nRes = 1;
954     static const double eps = 5.0 * DBL_EPSILON;
955 
956     // are the direction vectors linearly dependent?
957     double  fDet = Det(rHeading1, rHeading2);
958     if (fabs(fDet) < eps)
959     {
960         nRes    = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
961         rResult = nRes ? rPoint1 : Point();
962     }
963     else
964     {
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()));
972     }
973 
974     return nRes;
975 }
976 
977 }
978 
979 
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
GetOperPosSize(Point & rPos,Size & rSize,const Point & rDiagPoint,double fAngleDeg) const982 void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
983                         const Point &rDiagPoint, double fAngleDeg) const
984 
985 {
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)) );
995 
996     tools::Long  nLeft, nRight, nTop, nBottom;     // margins of the rectangle for the diagonal
997     Point aPoint;
998     if (IsAscending())
999     {
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)
1006         {
1007             nRight = aPoint.X();
1008             nTop   = nRectTop;
1009         }
1010         else
1011         {
1012             // there has to be a point of intersection with the right border!
1013             GetLineIntersectionPoint(aPoint,
1014                 Point(nRectRight, nRectTop), aDownHdg,
1015                 rDiagPoint, aDiagHdg);
1016 
1017             nRight = nRectRight;
1018             nTop   = aPoint.Y();
1019         }
1020 
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)
1027         {
1028             nLeft   = aPoint.X();
1029             nBottom = nRectBottom;
1030         }
1031         else
1032         {
1033             // there has to be a point of intersection with the left border!
1034             GetLineIntersectionPoint(aPoint,
1035                 Point(nRectLeft, nRectTop), aDownHdg,
1036                 rDiagPoint, aDiagHdg);
1037 
1038             nLeft   = nRectLeft;
1039             nBottom = aPoint.Y();
1040         }
1041     }
1042     else
1043     {
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)
1050         {
1051             nLeft = aPoint.X();
1052             nTop  = nRectTop;
1053         }
1054         else
1055         {
1056             // there has to be a point of intersection with the left border!
1057             GetLineIntersectionPoint(aPoint,
1058                 Point(nRectLeft, nRectTop), aDownHdg,
1059                 rDiagPoint, aDiagHdg);
1060 
1061             nLeft = nRectLeft;
1062             nTop  = aPoint.Y();
1063         }
1064 
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)
1071         {
1072             nRight  = aPoint.X();
1073             nBottom = nRectBottom;
1074         }
1075         else
1076         {
1077             // there has to be a point of intersection with the right border!
1078             GetLineIntersectionPoint(aPoint,
1079                 Point(nRectRight, nRectTop), aDownHdg,
1080                 rDiagPoint, aDiagHdg);
1081 
1082             nRight  = nRectRight;
1083             nBottom = aPoint.Y();
1084         }
1085     }
1086 
1087     rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
1088     rPos.setX( nLeft );
1089     rPos.setY( nTop );
1090 }
1091 
1092 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1093 void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1094 {
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);
1103 
1104     SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
1105     assert(pOper);
1106 
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());
1112 
1113     pLeft->Arrange(aTmpDev, rFormat);
1114     pRight->Arrange(aTmpDev, rFormat);
1115 
1116     // determine implicitly the values (incl. the margin) of the diagonal line
1117     pOper->Arrange(aTmpDev, rFormat);
1118 
1119     tools::Long nDelta = pOper->GetWidth() * 8 / 10;
1120 
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() );
1128 
1129     pRight->MoveTo(aPos);
1130 
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);
1136 
1137     SmRect::operator = (*pLeft);
1138     ExtendBy(*pRight, RectCopyMBL::None);
1139 
1140 
1141     // determine position and size of diagonal line
1142     Size  aTmpSize;
1143     GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
1144 
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);
1150 
1151     pOper->MoveTo(aPos);
1152 
1153     ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
1154 }
1155 
1156 
1157 /**************************************************************************/
1158 
1159 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1160 void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1161 {
1162     OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
1163                "Sm: wrong number of subnodes");
1164 
1165     SmNode *pBody = GetBody();
1166     assert(pBody);
1167 
1168     tools::Long  nOrigHeight = pBody->GetFont().GetFontSize().Height();
1169 
1170     pBody->Arrange(rDev, rFormat);
1171 
1172     const SmRect &rBodyRect = pBody->GetRect();
1173     SmRect::operator = (rBodyRect);
1174 
1175     // line that separates sub- and supscript rectangles
1176     tools::Long  nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1177 
1178     Point  aPos;
1179     tools::Long   nDelta, nDist;
1180 
1181     // iterate over all possible sub-/supscripts
1182     SmRect  aTmpRect (rBodyRect);
1183     for (int i = 0;  i < SUBSUP_NUM_ENTRIES;  i++)
1184     {
1185         SmSubSup eSubSup = static_cast<SmSubSup>(i);
1186         SmNode *pSubSup = GetSubSup(eSubSup);
1187 
1188         if (!pSubSup)
1189             continue;
1190 
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;
1198             }
1199 
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)
1203         {
1204             sal_uInt16 nIndex = (eSubSup == CSUB  ||  eSubSup == CSUP) ?
1205                                     SIZ_LIMITS : SIZ_INDEX;
1206             Fraction  aFraction ( rFormat.GetRelSize(nIndex), 100 );
1207             pSubSup->SetSize(aFraction);
1208         }
1209 
1210         pSubSup->Arrange(rDev, rFormat);
1211 
1212         bool  bIsTextmode = rFormat.IsTextmode();
1213         nDist = 0;
1214 
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;
1259         }
1260 
1261         pSubSup->MoveTo(aPos);
1262         ExtendBy(*pSubSup, RectCopyMBL::This, true);
1263 
1264         // update rectangle to which  RSUB, RSUP, LSUB, LSUP
1265         // will be aligned to
1266         if (eSubSup == CSUB  ||  eSubSup == CSUP)
1267             aTmpRect = *this;
1268     }
1269 }
1270 
1271 /**************************************************************************/
1272 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1273 void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1274 {
1275     SmNode *pLeft  = OpeningBrace(),
1276            *pBody  = Body(),
1277            *pRight = ClosingBrace();
1278     assert(pLeft);
1279     assert(pBody);
1280     assert(pRight);
1281 
1282     pBody->Arrange(rDev, rFormat);
1283 
1284     bool  bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
1285           bScale         = pBody->GetHeight() > 0  &&
1286                            (GetScaleMode() == SmScaleMode::Height  ||  bIsScaleNormal),
1287           bIsABS         = GetToken().eType == TABS;
1288 
1289     tools::Long  nFaceHeight = GetFont().GetFontSize().Height();
1290 
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);
1298     }
1299 
1300     // determine the height for the braces
1301     tools::Long  nBraceHeight;
1302     if (bScale)
1303     {
1304         nBraceHeight = pBody->GetType() == SmNodeType::Bracebody ?
1305                               static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
1306                             : pBody->GetHeight();
1307         nBraceHeight += 2 * (nBraceHeight * nPerc / 100);
1308     }
1309     else
1310         nBraceHeight = nFaceHeight;
1311 
1312     // distance to the argument
1313     nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
1314     tools::Long  nDist = nFaceHeight * nPerc / 100;
1315 
1316     // if wanted, scale the braces to the wanted size
1317     if (bScale)
1318     {
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 );
1328 
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);
1333 
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);
1338 
1339         pLeft ->AdaptToY(rDev, nBraceHeight);
1340         pRight->AdaptToY(rDev, nBraceHeight);
1341     }
1342 
1343     pLeft ->Arrange(rDev, rFormat);
1344     pRight->Arrange(rDev, rFormat);
1345 
1346     // required in order to make "\(a\) - (a) - left ( a right )" look alright
1347     RectVerAlign  eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1348 
1349     Point         aPos;
1350     aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
1351     aPos.AdjustX( -nDist );
1352     pLeft->MoveTo(aPos);
1353 
1354     aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
1355     aPos.AdjustX(nDist );
1356     pRight->MoveTo(aPos);
1357 
1358     SmRect::operator = (*pBody);
1359     ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
1360 }
1361 
1362 
1363 /**************************************************************************/
1364 
1365 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1366 void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1367 {
1368     size_t nNumSubNodes = GetNumSubNodes();
1369     if (nNumSubNodes == 0)
1370         return;
1371 
1372     // arrange arguments
1373     for (size_t i = 0;  i < nNumSubNodes; i += 2)
1374         GetSubNode(i)->Arrange(rDev, rFormat);
1375 
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)
1379     {
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);
1384     }
1385 
1386     mnBodyHeight = aRefRect.GetHeight();
1387 
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)
1397     {
1398         SmNode *pNode = GetSubNode(i);
1399         pNode->AdaptToY(rDev, nHeight);
1400         pNode->Arrange(rDev, rFormat);
1401     }
1402 
1403     // horizontal distance between argument and brackets or separators
1404     tools::Long  nDist = GetFont().GetFontSize().Height()
1405                   * rFormat.GetDistance(DIS_BRACKETSPACE) / 100;
1406 
1407     SmNode *pLeft = GetSubNode(0);
1408     SmRect::operator = (*pLeft);
1409     for (size_t i = 1; i < nNumSubNodes; ++i)
1410     {
1411         bool          bIsSeparator = i % 2 != 0;
1412         RectVerAlign  eVerAlign    = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
1413 
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 );
1418 
1419         pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
1420         ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
1421 
1422         pLeft = pRight;
1423     }
1424 }
1425 
1426 
1427 /**************************************************************************/
1428 
1429 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1430 void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1431 {
1432     SmNode *pBody   = Body(),
1433            *pBrace  = Brace(),
1434            *pScript = Script();
1435     assert(pBody);
1436     assert(pBrace);
1437     assert(pScript);
1438 
1439     SmTmpDevice aTmpDev (rDev, true);
1440     aTmpDev.SetFont(GetFont());
1441 
1442     pBody->Arrange(aTmpDev, rFormat);
1443 
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) );
1448 
1449     tools::Long  nItalicWidth = pBody->GetItalicWidth();
1450     if (nItalicWidth > 0)
1451         pBrace->AdaptToX(aTmpDev, nItalicWidth);
1452 
1453     pBrace ->Arrange(aTmpDev, rFormat);
1454     pScript->Arrange(aTmpDev, rFormat);
1455 
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)
1462     {
1463         eRectPos = RectPos::Top;
1464         nDistBody    = - nDistBody;
1465         nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
1466     }
1467     else // TUNDERBRACE
1468     {
1469         eRectPos = RectPos::Bottom;
1470         nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
1471     }
1472     nDistBody   /= 100;
1473     nDistScript /= 100;
1474 
1475     Point  aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1476     aPos.AdjustY(nDistBody );
1477     pBrace->MoveTo(aPos);
1478 
1479     aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
1480     aPos.AdjustY(nDistScript );
1481     pScript->MoveTo(aPos);
1482 
1483     SmRect::operator = (*pBody);
1484     ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
1485 }
1486 
1487 
1488 /**************************************************************************/
1489 
1490 
GetSymbol()1491 SmNode * SmOperNode::GetSymbol()
1492 {
1493     SmNode *pNode = GetSubNode(0);
1494     assert(pNode);
1495 
1496     if (pNode->GetType() == SmNodeType::SubSup)
1497         pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
1498 
1499     OSL_ENSURE(pNode, "Sm: NULL pointer!");
1500     return pNode;
1501 }
1502 
1503 
CalcSymbolHeight(const SmNode & rSymbol,const SmFormat & rFormat) const1504 tools::Long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
1505                                   const SmFormat &rFormat) const
1506     // returns the font height to be used for operator-symbol
1507 {
1508     tools::Long  nHeight = GetFont().GetFontSize().Height();
1509 
1510     SmTokenType  eTmpType = GetToken().eType;
1511     if (eTmpType == TLIM  ||  eTmpType == TLIMINF  ||  eTmpType == TLIMSUP)
1512         return nHeight;
1513 
1514     if (!rFormat.IsTextmode())
1515     {
1516         // set minimum size ()
1517         nHeight += (nHeight * 20) / 100;
1518 
1519         nHeight += nHeight
1520                    * rFormat.GetDistance(DIS_OPERATORSIZE) / 100;
1521         nHeight = nHeight * 686 / 845;
1522     }
1523 
1524     // correct user-defined symbols to match height of sum from used font
1525     if (rSymbol.GetToken().eType == TSPECIAL)
1526         nHeight = nHeight * 845 / 686;
1527 
1528     return nHeight;
1529 }
1530 
1531 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1532 void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1533 {
1534     SmNode *pOper = GetSubNode(0);
1535     SmNode *pBody = GetSubNode(1);
1536 
1537     assert(pOper);
1538     assert(pBody);
1539 
1540     SmNode *pSymbol = GetSymbol();
1541     pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
1542                               pSymbol->GetFont().GetFontSize().Height()));
1543 
1544     pBody->Arrange(rDev, rFormat);
1545     bool bDynamicallySized = false;
1546     if (pSymbol->GetToken().eType == TINTD)
1547     {
1548         tools::Long nBodyHeight = pBody->GetHeight();
1549         tools::Long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
1550         if (nFontHeight < nBodyHeight)
1551         {
1552             pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
1553             bDynamicallySized = true;
1554         }
1555     }
1556     pOper->Arrange(rDev, rFormat);
1557 
1558     tools::Long  nOrigHeight = GetFont().GetFontSize().Height(),
1559           nDist = nOrigHeight
1560                   * rFormat.GetDistance(DIS_OPERATORSPACE) / 100;
1561 
1562     Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
1563     aPos.AdjustX( -nDist );
1564     pOper->MoveTo(aPos);
1565 
1566     SmRect::operator = (*pBody);
1567     ExtendBy(*pOper, RectCopyMBL::This);
1568 }
1569 
1570 
1571 /**************************************************************************/
1572 
1573 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1574 void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1575     // set alignment within the entire subtree (including current node)
1576 {
1577     assert(GetNumSubNodes() == 1);
1578 
1579     SmNode  *pNode = GetSubNode(0);
1580     assert(pNode);
1581 
1582     RectHorAlign  eHorAlign = RectHorAlign::Center;
1583     switch (GetToken().eType)
1584     {
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;
1590     }
1591     SetRectHorAlign(eHorAlign);
1592 
1593     pNode->Arrange(rDev, rFormat);
1594 
1595     SmRect::operator = (pNode->GetRect());
1596 }
1597 
1598 
1599 /**************************************************************************/
1600 
1601 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1602 void SmAttributeNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1603 {
1604     SmNode *pAttr = Attribute(),
1605            *pBody = Body();
1606     assert(pBody);
1607     assert(pAttr);
1608 
1609     pBody->Arrange(rDev, rFormat);
1610 
1611     if (GetScaleMode() == SmScaleMode::Width)
1612         pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
1613     pAttr->Arrange(rDev, rFormat);
1614 
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;
1630     }
1631     Point  aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
1632     aPos.AdjustY( -nDist );
1633     pAttr->MoveTo(aPos);
1634 
1635     SmRect::operator = (*pBody);
1636     ExtendBy(*pAttr, RectCopyMBL::This, true);
1637 }
1638 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)1639 void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1640 {
1641     //! prepare subnodes first
1642     SmNode::Prepare(rFormat, rDocShell, nDepth);
1643 
1644     int  nFnt = -1;
1645     switch (GetToken().eType)
1646     {
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;
1652     }
1653     if (nFnt != -1)
1654     {   GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
1655         SetFont(GetFont());
1656     }
1657 
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;
1661 }
1662 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1663 void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1664 {
1665     SmNode *pNode = GetSubNode(1);
1666     assert(pNode);
1667     sal_uInt32 nc;
1668 
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 <?> <?>"
1679 
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;
1685 
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;
1697 
1698         default:
1699             SAL_WARN("starmath", "unknown case");
1700     }
1701 
1702     pNode->Arrange(rDev, rFormat);
1703 
1704     SmRect::operator = (pNode->GetRect());
1705 }
1706 
1707 /**************************************************************************/
1708 
1709 
SmPolyLineNode(const SmToken & rNodeToken)1710 SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
1711     : SmGraphicNode(SmNodeType::PolyLine, rNodeToken)
1712     , maPoly(2)
1713     , mnWidth(0)
1714 {
1715 }
1716 
1717 
AdaptToX(OutputDevice &,sal_uLong nNewWidth)1718 void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
1719 {
1720     maToSize.setWidth( nNewWidth );
1721 }
1722 
1723 
AdaptToY(OutputDevice &,sal_uLong nNewHeight)1724 void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
1725 {
1726     GetFont().FreezeBorderWidth();
1727     maToSize.setHeight( nNewHeight );
1728 }
1729 
1730 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1731 void SmPolyLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1732 {
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());
1738 
1739     tools::Long  nBorderwidth = GetFont().GetBorderWidth();
1740 
1741     // create polygon using both endpoints
1742     assert(maPoly.GetSize() == 2);
1743     Point  aPointA, aPointB;
1744     if (GetToken().eType == TWIDESLASH)
1745     {
1746         aPointA.setX( nBorderwidth );
1747         aPointA.setY( maToSize.Height() - nBorderwidth );
1748         aPointB.setX( maToSize.Width() - nBorderwidth );
1749         aPointB.setY( nBorderwidth );
1750     }
1751     else
1752     {
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 );
1758     }
1759     maPoly.SetPoint(aPointA, 0);
1760     maPoly.SetPoint(aPointB, 1);
1761 
1762     tools::Long  nThick       = GetFont().GetFontSize().Height()
1763                             * rFormat.GetDistance(DIS_STROKEWIDTH) / 100;
1764     mnWidth = nThick + 2 * nBorderwidth;
1765 
1766     SmRect::operator = (SmRect(maToSize.Width(), maToSize.Height()));
1767 }
1768 
1769 
1770 /**************************************************************************/
1771 
AdaptToX(OutputDevice &,sal_uLong nWidth)1772 void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1773 {
1774     mnBodyWidth = nWidth;
1775 }
1776 
1777 
AdaptToY(OutputDevice & rDev,sal_uLong nHeight)1778 void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
1779 {
1780     // some additional length so that the horizontal
1781     // bar will be positioned above the argument
1782     SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10);
1783 }
1784 
1785 
1786 /**************************************************************************/
1787 
1788 
AdaptToX(OutputDevice &,sal_uLong nWidth)1789 void SmRectangleNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
1790 {
1791     maToSize.setWidth( nWidth );
1792 }
1793 
1794 
AdaptToY(OutputDevice &,sal_uLong nHeight)1795 void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
1796 {
1797     GetFont().FreezeBorderWidth();
1798     maToSize.setHeight( nHeight );
1799 }
1800 
1801 
Arrange(OutputDevice & rDev,const SmFormat &)1802 void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
1803 {
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;
1811 
1812     SmTmpDevice aTmpDev (rDev, true);
1813     aTmpDev.SetFont(GetFont());
1814 
1815     // add some borderspace
1816     sal_uLong  nTmpBorderWidth = GetFont().GetBorderWidth();
1817     nHeight += 2 * nTmpBorderWidth;
1818 
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));
1822 }
1823 
1824 
1825 /**************************************************************************/
1826 
1827 
SmTextNode(SmNodeType eNodeType,const SmToken & rNodeToken,sal_uInt16 nFontDescP)1828 SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
1829     : SmVisibleNode(eNodeType, rNodeToken)
1830     , mnFontDesc(nFontDescP)
1831     , mnSelectionStart(0)
1832     , mnSelectionEnd(0)
1833 {
1834 }
1835 
SmTextNode(const SmToken & rNodeToken,sal_uInt16 nFontDescP)1836 SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
1837     : SmVisibleNode(SmNodeType::Text, rNodeToken)
1838     , mnFontDesc(nFontDescP)
1839     , mnSelectionStart(0)
1840     , mnSelectionEnd(0)
1841 {
1842 }
1843 
ChangeText(const OUString & rText)1844 void SmTextNode::ChangeText(const OUString &rText) {
1845     maText = rText;
1846     GetToken().aText = rText;
1847     AdjustFontDesc();
1848 }
1849 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)1850 void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
1851 {
1852     SmNode::Prepare(rFormat, rDocShell, nDepth);
1853 
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 );
1859 
1860     maText = GetToken().aText;
1861     GetFont() = rFormat.GetFont(GetFontDesc());
1862 
1863     if (IsItalic( GetFont() ))
1864         Attributes() |= FontAttribute::Italic;
1865     if (IsBold( GetFont() ))
1866         Attributes() |= FontAttribute::Bold;
1867 
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;
1873 
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())
1877     {
1878         sal_Int32 nIndex = 0;
1879         while (nIndex < maText.getLength())
1880         {
1881             sal_uInt32 cChar = maText.iterateCodePoints(&nIndex);
1882             if (u_getIntPropertyValue(cChar, UCHAR_SCRIPT) == USCRIPT_ARABIC)
1883             {
1884                 Attributes() &= ~FontAttribute::Italic;
1885                 break;
1886             }
1887         }
1888     }
1889 };
1890 
1891 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1892 void SmTextNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1893 {
1894     PrepareAttributes();
1895 
1896     sal_uInt16  nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
1897                             SIZ_FUNCTION : SIZ_TEXT;
1898     GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
1899 
1900     SmTmpDevice aTmpDev (rDev, true);
1901     aTmpDev.SetFont(GetFont());
1902 
1903     SmRect::operator = (SmRect(aTmpDev, &rFormat, maText, GetFont().GetBorderWidth()));
1904 }
1905 
GetAccessibleText(OUStringBuffer & rText) const1906 void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
1907 {
1908     rText.append(maText);
1909 }
1910 
AdjustFontDesc()1911 void SmTextNode::AdjustFontDesc()
1912 {
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;
1920     }
1921 }
1922 
ConvertSymbolToUnicode(sal_Unicode nIn)1923 sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
1924 {
1925     //Find the best match in accepted unicode for our private area symbols
1926     static const sal_Unicode aStarMathPrivateToUnicode[] =
1927     {
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
1938     };
1939     if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
1940         nIn = aStarMathPrivateToUnicode[nIn-0xE080];
1941 
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)
1945     {
1946         case 0x2223:
1947             nIn = '|';
1948             break;
1949         default:
1950             break;
1951     }
1952 
1953     return nIn;
1954 }
1955 
1956 /**************************************************************************/
1957 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)1958 void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
1959 {
1960     SmNode *pNode;
1961 
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);
1965 
1966     // arrange subnodes and calculate the above arrays contents
1967     size_t nNodes = GetNumSubNodes();
1968     for (size_t i = 0; i < nNodes; ++i)
1969     {
1970         size_t nIdx = nNodes - 1 - i;
1971         if (nullptr != (pNode = GetSubNode(nIdx)))
1972         {
1973             pNode->Arrange(rDev, rFormat);
1974             int  nCol = nIdx % mnNumCols;
1975             aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
1976         }
1977     }
1978 
1979     // norm distance from which the following two are calculated
1980     const tools::Long  nNormDist = 3 * GetFont().GetFontSize().Height();
1981 
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;
1986 
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)
1991     {
1992         aColLeft[j] = nX;
1993         nX += aColWidth[j] + nHorDist;
1994     }
1995 
1996     SmRect::operator = (SmRect());
1997     for (size_t i = 0;  i < mnNumRows; ++i)
1998     {
1999         Point aPos;
2000         SmRect aLineRect;
2001         for (size_t j = 0;  j < mnNumCols; ++j)
2002         {
2003             SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
2004             assert(pTmpNode);
2005 
2006             const SmRect &rNodeRect = pTmpNode->GetRect();
2007 
2008             // align all baselines in that row if possible
2009             aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
2010 
2011             // get horizontal alignment
2012             const SmNode *pCoNode   = pTmpNode->GetLeftMost();
2013             RectHorAlign  eHorAlign = pCoNode->GetRectHorAlign();
2014 
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);
2032             }
2033 
2034             pTmpNode->MoveTo(aPos);
2035             aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
2036         }
2037 
2038         aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
2039         if (i > 0)
2040             aPos.AdjustY(nVerDist );
2041 
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)
2047         {
2048             if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
2049                 pNode->Move(aDelta);
2050         }
2051 
2052         ExtendBy(aLineRect, RectCopyMBL::None);
2053     }
2054 }
2055 
GetLeftMost() const2056 const SmNode * SmMatrixNode::GetLeftMost() const
2057 {
2058     return this;
2059 }
2060 
2061 
2062 /**************************************************************************/
2063 
2064 
SmMathSymbolNode(const SmToken & rNodeToken)2065 SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
2066 :   SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH)
2067 {
2068     SetText(GetToken().cMathChar);
2069 }
2070 
AdaptToX(OutputDevice & rDev,sal_uLong nWidth)2071 void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
2072 {
2073     // Since there is no function to do this, we try to approximate it:
2074     Size  aFntSize (GetFont().GetFontSize());
2075 
2076     //! however the result is a bit better with 'nWidth' as initial font width
2077     aFntSize.setWidth( nWidth );
2078     GetFont().SetSize(aFntSize);
2079 
2080     SmTmpDevice aTmpDev (rDev, true);
2081     aTmpDev.SetFont(GetFont());
2082 
2083     // get denominator of error factor for width
2084     tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
2085     tools::Long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
2086 
2087     // scale fontwidth with this error factor
2088     aFntSize.setWidth( aFntSize.Width() * nWidth );
2089     aFntSize.setWidth( aFntSize.Width() / ( nDenom ? nDenom : 1) );
2090 
2091     GetFont().SetSize(aFntSize);
2092 }
2093 
AdaptToY(OutputDevice & rDev,sal_uLong nHeight)2094 void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
2095 {
2096     GetFont().FreezeBorderWidth();
2097     Size  aFntSize (GetFont().GetFontSize());
2098 
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)
2102     {
2103         rDev.Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE);
2104         rDev.SetFont(GetFont());
2105         aFntSize.setWidth( rDev.GetFontMetric().GetFontSize().Width() );
2106         rDev.Pop();
2107     }
2108     OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
2109 
2110     //! however the result is a bit better with 'nHeight' as initial
2111     //! font height
2112     aFntSize.setHeight( nHeight );
2113     GetFont().SetSize(aFntSize);
2114 
2115     SmTmpDevice aTmpDev (rDev, true);
2116     aTmpDev.SetFont(GetFont());
2117 
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();
2123 
2124     // scale fontwidth with this error factor
2125     aFntSize.setHeight( aFntSize.Height() * nHeight );
2126     aFntSize.setHeight( aFntSize.Height() / ( nDenom ? nDenom : 1) );
2127 
2128     GetFont().SetSize(aFntSize);
2129 }
2130 
2131 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)2132 void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2133 {
2134     SmNode::Prepare(rFormat, rDocShell, nDepth);
2135 
2136     GetFont() = rFormat.GetFont(GetFontDesc());
2137     // use same font size as is used for variables
2138     GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2139 
2140     OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL  ||
2141                GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
2142         "wrong charset for character from StarMath/OpenSymbol font");
2143 
2144     Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
2145 };
2146 
2147 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)2148 void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2149 {
2150     const OUString &rText = GetText();
2151 
2152     if (rText.isEmpty() || rText[0] == '\0')
2153     {   SmRect::operator = (SmRect());
2154         return;
2155     }
2156 
2157     PrepareAttributes();
2158 
2159     GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
2160 
2161     SmTmpDevice aTmpDev (rDev, true);
2162     aTmpDev.SetFont(GetFont());
2163 
2164     SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2165 }
2166 
2167 /**************************************************************************/
2168 
SmSpecialNode(SmNodeType eNodeType,const SmToken & rNodeToken,sal_uInt16 _nFontDesc)2169 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
2170     : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
2171 {
2172 }
2173 
2174 
SmSpecialNode(const SmToken & rNodeToken)2175 SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
2176     : SmTextNode(SmNodeType::Special, rNodeToken, FNT_VARIABLE)  // default Font isn't always correct!
2177 {
2178 }
2179 
2180 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)2181 void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2182 {
2183     SmNode::Prepare(rFormat, rDocShell, nDepth);
2184 
2185     const SmSym   *pSym;
2186     SmModule  *pp = SM_MOD();
2187 
2188     bool bIsGreekSymbol = false;
2189     bool bIsSpecialSymbol = false;
2190     bool bIsArabic = false;
2191 
2192     if ((!GetToken().aText.isEmpty())
2193         && (nullptr
2194             != (pSym = pp->GetSymbolManager().GetSymbolByName(GetToken().aText.subView(1)))))
2195     {
2196         sal_UCS4 cChar = pSym->GetCharacter();
2197         OUString aTmp( &cChar, 1 );
2198         SetText( aTmp );
2199         GetFont() = pSym->GetFace(&rFormat);
2200 
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;
2208     }
2209     else
2210     {
2211         SetText( GetToken().aText );
2212         GetFont() = rFormat.GetFont(FNT_VARIABLE);
2213     }
2214     // use same font size as is used for variables
2215     GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
2216 
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)
2220 
2221     //! see also SmFontStyles::GetStyleName
2222     if (IsItalic( GetFont() ))
2223         SetAttribute(FontAttribute::Italic);
2224     if (IsBold( GetFont() ))
2225         SetAttribute(FontAttribute::Bold);
2226 
2227     Flags() |= FontChangeMask::Face;
2228 
2229     sal_uInt32 cChar = 0;
2230     if (!GetText().isEmpty())
2231     {
2232         sal_Int32 nIndex = 0;
2233         cChar = GetText().iterateCodePoints(&nIndex);
2234         if (!bIsArabic)
2235             bIsArabic = u_getIntPropertyValue(cChar, UCHAR_SCRIPT) == USCRIPT_ARABIC;
2236     }
2237 
2238     if (!bIsGreekSymbol && !bIsSpecialSymbol && !bIsArabic)
2239         return;
2240 
2241     // Arabic and special symbols should not be italic,
2242     // Greek is italic only in some cases.
2243     bool bItalic = false;
2244     if (bIsGreekSymbol)
2245     {
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)
2251         {
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;
2256         }
2257     }
2258 
2259     if (bItalic)
2260         Attributes() |= FontAttribute::Italic;
2261     else
2262         Attributes() &= ~FontAttribute::Italic;
2263 };
2264 
2265 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)2266 void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2267 {
2268     PrepareAttributes();
2269 
2270     SmTmpDevice aTmpDev (rDev, true);
2271     aTmpDev.SetFont(GetFont());
2272 
2273     SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2274 }
2275 
2276 /**************************************************************************/
2277 
2278 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)2279 void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2280 {
2281     PrepareAttributes();
2282 
2283     SmTmpDevice aTmpDev (rDev, true);
2284     aTmpDev.SetFont(GetFont());
2285 
2286     SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
2287                                GetFont().GetBorderWidth()).AsGlyphRect());
2288 }
2289 
2290 
2291 /**************************************************************************/
2292 
2293 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)2294 void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2295 {
2296     SmNode::Prepare(rFormat, rDocShell, nDepth);
2297 
2298     GetFont().SetColor(COL_GRAY);
2299     Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
2300 };
2301 
2302 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)2303 void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2304 {
2305     PrepareAttributes();
2306 
2307     SmTmpDevice aTmpDev (rDev, true);
2308     aTmpDev.SetFont(GetFont());
2309 
2310     SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2311 }
2312 
2313 
2314 /**************************************************************************/
2315 
2316 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)2317 void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2318 {
2319     SmNode::Prepare(rFormat, rDocShell, nDepth);
2320 
2321     GetFont().SetColor(COL_RED);
2322     Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
2323                | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
2324 }
2325 
2326 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)2327 void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2328 {
2329     PrepareAttributes();
2330 
2331     SmTmpDevice aTmpDev (rDev, true);
2332     aTmpDev.SetFont(GetFont());
2333 
2334     const OUString &rText = GetText();
2335     SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2336 }
2337 
2338 /**************************************************************************/
2339 
IncreaseBy(const SmToken & rToken,sal_uInt32 nMultiplyBy)2340 void SmBlankNode::IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy)
2341 {
2342     switch(rToken.eType)
2343     {
2344         case TBLANK:  mnNum += (4 * nMultiplyBy); break;
2345         case TSBLANK: mnNum += (1 * nMultiplyBy); break;
2346         default:
2347             break;
2348     }
2349 }
2350 
Prepare(const SmFormat & rFormat,const SmDocShell & rDocShell,int nDepth)2351 void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
2352 {
2353     SmNode::Prepare(rFormat, rDocShell, nDepth);
2354 
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);
2358 
2359     Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
2360 }
2361 
2362 
Arrange(OutputDevice & rDev,const SmFormat & rFormat)2363 void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
2364 {
2365     SmTmpDevice aTmpDev (rDev, true);
2366     aTmpDev.SetFont(GetFont());
2367 
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;
2372 
2373     // get a SmRect with Baseline and all the bells and whistles
2374     SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
2375                                GetFont().GetBorderWidth()));
2376 
2377     // and resize it to the requested size
2378     SetItalicSpaces(0, 0);
2379     SetWidth(nSpace);
2380 }
2381 
2382 /**************************************************************************/
2383 //Implementation of all accept methods for SmVisitor
2384 
Accept(SmVisitor * pVisitor)2385 void SmTableNode::Accept(SmVisitor* pVisitor) {
2386     pVisitor->Visit(this);
2387 }
2388 
Accept(SmVisitor * pVisitor)2389 void SmBraceNode::Accept(SmVisitor* pVisitor) {
2390     pVisitor->Visit(this);
2391 }
2392 
Accept(SmVisitor * pVisitor)2393 void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
2394     pVisitor->Visit(this);
2395 }
2396 
Accept(SmVisitor * pVisitor)2397 void SmOperNode::Accept(SmVisitor* pVisitor) {
2398     pVisitor->Visit(this);
2399 }
2400 
Accept(SmVisitor * pVisitor)2401 void SmAlignNode::Accept(SmVisitor* pVisitor) {
2402     pVisitor->Visit(this);
2403 }
2404 
Accept(SmVisitor * pVisitor)2405 void SmAttributeNode::Accept(SmVisitor* pVisitor) {
2406     pVisitor->Visit(this);
2407 }
2408 
Accept(SmVisitor * pVisitor)2409 void SmFontNode::Accept(SmVisitor* pVisitor) {
2410     pVisitor->Visit(this);
2411 }
2412 
Accept(SmVisitor * pVisitor)2413 void SmUnHorNode::Accept(SmVisitor* pVisitor) {
2414     pVisitor->Visit(this);
2415 }
2416 
Accept(SmVisitor * pVisitor)2417 void SmBinHorNode::Accept(SmVisitor* pVisitor) {
2418     pVisitor->Visit(this);
2419 }
2420 
Accept(SmVisitor * pVisitor)2421 void SmBinVerNode::Accept(SmVisitor* pVisitor) {
2422     pVisitor->Visit(this);
2423 }
2424 
Accept(SmVisitor * pVisitor)2425 void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
2426     pVisitor->Visit(this);
2427 }
2428 
Accept(SmVisitor * pVisitor)2429 void SmSubSupNode::Accept(SmVisitor* pVisitor) {
2430     pVisitor->Visit(this);
2431 }
2432 
Accept(SmVisitor * pVisitor)2433 void SmMatrixNode::Accept(SmVisitor* pVisitor) {
2434     pVisitor->Visit(this);
2435 }
2436 
Accept(SmVisitor * pVisitor)2437 void SmPlaceNode::Accept(SmVisitor* pVisitor) {
2438     pVisitor->Visit(this);
2439 }
2440 
Accept(SmVisitor * pVisitor)2441 void SmTextNode::Accept(SmVisitor* pVisitor) {
2442     pVisitor->Visit(this);
2443 }
2444 
Accept(SmVisitor * pVisitor)2445 void SmSpecialNode::Accept(SmVisitor* pVisitor) {
2446     pVisitor->Visit(this);
2447 }
2448 
Accept(SmVisitor * pVisitor)2449 void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
2450     pVisitor->Visit(this);
2451 }
2452 
Accept(SmVisitor * pVisitor)2453 void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
2454     pVisitor->Visit(this);
2455 }
2456 
Accept(SmVisitor * pVisitor)2457 void SmBlankNode::Accept(SmVisitor* pVisitor) {
2458     pVisitor->Visit(this);
2459 }
2460 
Accept(SmVisitor * pVisitor)2461 void SmErrorNode::Accept(SmVisitor* pVisitor) {
2462     pVisitor->Visit(this);
2463 }
2464 
Accept(SmVisitor * pVisitor)2465 void SmLineNode::Accept(SmVisitor* pVisitor) {
2466     pVisitor->Visit(this);
2467 }
2468 
Accept(SmVisitor * pVisitor)2469 void SmExpressionNode::Accept(SmVisitor* pVisitor) {
2470     pVisitor->Visit(this);
2471 }
2472 
Accept(SmVisitor * pVisitor)2473 void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
2474     pVisitor->Visit(this);
2475 }
2476 
Accept(SmVisitor * pVisitor)2477 void SmRootNode::Accept(SmVisitor* pVisitor) {
2478     pVisitor->Visit(this);
2479 }
2480 
Accept(SmVisitor * pVisitor)2481 void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
2482     pVisitor->Visit(this);
2483 }
2484 
Accept(SmVisitor * pVisitor)2485 void SmRectangleNode::Accept(SmVisitor* pVisitor) {
2486     pVisitor->Visit(this);
2487 }
2488 
Accept(SmVisitor * pVisitor)2489 void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
2490     pVisitor->Visit(this);
2491 }
2492 
2493 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2494