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