bump product version to 5.0.4.1
[LibreOffice.git] / starmath / source / node.cxx
blob311df24840caac6f8793f0ef00ec973a1906a4b7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "node.hxx"
22 #include "rect.hxx"
23 #include "symbol.hxx"
24 #include "smmod.hxx"
25 #include "document.hxx"
26 #include "view.hxx"
27 #include "mathtype.hxx"
28 #include "tmpdevice.hxx"
29 #include "visitors.hxx"
31 #include <comphelper/string.hxx>
32 #include <tools/color.hxx>
33 #include <tools/fract.hxx>
34 #include <tools/gen.hxx>
35 #include <vcl/outdev.hxx>
37 #include <math.h>
38 #include <float.h>
39 #include <vector>
41 SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken)
42 : aNodeToken( rNodeToken )
43 , eType( eNodeType )
44 , eScaleMode( SCALE_NONE )
45 , eRectHorAlign( RHA_LEFT )
46 , nFlags( 0 )
47 , nAttributes( 0 )
48 , bIsPhantom( false )
49 , bIsDebug( false )
50 , bIsSelected( false )
51 , nAccIndex( -1 )
52 , aParentNode( NULL )
57 SmNode::~SmNode()
62 bool SmNode::IsVisible() const
64 return false;
68 sal_uInt16 SmNode::GetNumSubNodes() const
70 return 0;
74 SmNode * SmNode::GetSubNode(sal_uInt16 /*nIndex*/)
76 return NULL;
80 SmNode * SmNode::GetLeftMost()
81 // returns leftmost node of current subtree.
82 //! (this assumes the one with index 0 is always the leftmost subnode
83 //! for the current node).
85 SmNode *pNode = GetNumSubNodes() > 0 ?
86 GetSubNode(0) : NULL;
88 return pNode ? pNode->GetLeftMost() : this;
92 void SmNode::SetPhantom(bool bIsPhantomP)
94 if (! (Flags() & FLG_VISIBLE))
95 bIsPhantom = bIsPhantomP;
97 SmNode *pNode;
98 sal_uInt16 nSize = GetNumSubNodes();
99 for (sal_uInt16 i = 0; i < nSize; i++)
100 if (NULL != (pNode = GetSubNode(i)))
101 pNode->SetPhantom(bIsPhantom);
105 void SmNode::SetColor(const Color& rColor)
107 if (! (Flags() & FLG_COLOR))
108 GetFont().SetColor(rColor);
110 SmNode *pNode;
111 sal_uInt16 nSize = GetNumSubNodes();
112 for (sal_uInt16 i = 0; i < nSize; i++)
113 if (NULL != (pNode = GetSubNode(i)))
114 pNode->SetColor(rColor);
118 void SmNode::SetAttribut(sal_uInt16 nAttrib)
120 if (
121 (nAttrib == ATTR_BOLD && !(Flags() & FLG_BOLD)) ||
122 (nAttrib == ATTR_ITALIC && !(Flags() & FLG_ITALIC))
125 nAttributes |= nAttrib;
128 SmNode *pNode;
129 sal_uInt16 nSize = GetNumSubNodes();
130 for (sal_uInt16 i = 0; i < nSize; i++)
131 if (NULL != (pNode = GetSubNode(i)))
132 pNode->SetAttribut(nAttrib);
136 void SmNode::ClearAttribut(sal_uInt16 nAttrib)
138 if (
139 (nAttrib == ATTR_BOLD && !(Flags() & FLG_BOLD)) ||
140 (nAttrib == ATTR_ITALIC && !(Flags() & FLG_ITALIC))
143 nAttributes &= ~nAttrib;
146 SmNode *pNode;
147 sal_uInt16 nSize = GetNumSubNodes();
148 for (sal_uInt16 i = 0; i < nSize; i++)
149 if (NULL != (pNode = GetSubNode(i)))
150 pNode->ClearAttribut(nAttrib);
154 void SmNode::SetFont(const SmFace &rFace)
156 if (!(Flags() & FLG_FONT))
157 GetFont() = rFace;
159 SmNode *pNode;
160 sal_uInt16 nSize = GetNumSubNodes();
161 for (sal_uInt16 i = 0; i < nSize; i++)
162 if (NULL != (pNode = GetSubNode(i)))
163 pNode->SetFont(rFace);
167 void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
168 //! 'rSize' is in units of pts
170 Size aFntSize;
172 if (!(Flags() & FLG_SIZE))
174 Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()),
175 rSize.GetDenominator());
176 long nHeight = (long)aVal;
178 aFntSize = GetFont().GetSize();
179 aFntSize.Width() = 0;
180 switch(nType)
182 case FontSizeType::ABSOLUT:
183 aFntSize.Height() = nHeight;
184 break;
186 case FontSizeType::PLUS:
187 aFntSize.Height() += nHeight;
188 break;
190 case FontSizeType::MINUS:
191 aFntSize.Height() -= nHeight;
192 break;
194 case FontSizeType::MULTIPLY:
195 aFntSize.Height() = (long) (Fraction(aFntSize.Height()) * rSize);
196 break;
198 case FontSizeType::DIVIDE:
199 if (rSize != Fraction(0L))
200 aFntSize.Height() = (long) (Fraction(aFntSize.Height()) / rSize);
201 break;
202 default:
203 break;
206 // check the requested size against maximum value
207 static int const nMaxVal = SmPtsTo100th_mm(128);
208 if (aFntSize.Height() > nMaxVal)
209 aFntSize.Height() = nMaxVal;
211 GetFont().SetSize(aFntSize);
214 SmNode *pNode;
215 sal_uInt16 nSize = GetNumSubNodes();
216 for (sal_uInt16 i = 0; i < nSize; i++)
217 if (NULL != (pNode = GetSubNode(i)))
218 pNode->SetFontSize(rSize, nType);
222 void SmNode::SetSize(const Fraction &rSize)
224 GetFont() *= rSize;
226 SmNode *pNode;
227 sal_uInt16 nSize = GetNumSubNodes();
228 for (sal_uInt16 i = 0; i < nSize; i++)
229 if (NULL != (pNode = GetSubNode(i)))
230 pNode->SetSize(rSize);
234 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
236 if (!(Flags() & FLG_HORALIGN))
237 eRectHorAlign = eHorAlign;
239 if (bApplyToSubTree)
241 SmNode *pNode;
242 sal_uInt16 nSize = GetNumSubNodes();
243 for (sal_uInt16 i = 0; i < nSize; i++)
244 if (NULL != (pNode = GetSubNode(i)))
245 pNode->SetRectHorAlign(eHorAlign);
250 void SmNode::PrepareAttributes()
252 GetFont().SetWeight((Attributes() & ATTR_BOLD) ? WEIGHT_BOLD : WEIGHT_NORMAL);
253 GetFont().SetItalic((Attributes() & ATTR_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE);
257 void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
259 #if OSL_DEBUG_LEVEL > 1
260 bIsDebug = true;
261 #else
262 bIsDebug = false;
263 #endif
264 bIsPhantom = false;
265 nFlags = 0;
266 nAttributes = 0;
268 switch (rFormat.GetHorAlign())
269 { case AlignLeft: eRectHorAlign = RHA_LEFT; break;
270 case AlignCenter: eRectHorAlign = RHA_CENTER; break;
271 case AlignRight: eRectHorAlign = RHA_RIGHT; break;
274 GetFont() = rFormat.GetFont(FNT_MATH);
275 OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
276 "unexpected CharSet" );
277 GetFont().SetWeight(WEIGHT_NORMAL);
278 GetFont().SetItalic(ITALIC_NONE);
280 SmNode *pNode;
281 sal_uInt16 nSize = GetNumSubNodes();
282 for (sal_uInt16 i = 0; i < nSize; i++)
283 if (NULL != (pNode = GetSubNode(i)))
284 pNode->Prepare(rFormat, rDocShell);
287 sal_uInt16 SmNode::FindIndex() const
289 const SmStructureNode* pParent = GetParent();
290 if (!pParent) { return 0; }
292 for (sal_uInt16 i = 0; i < pParent->GetNumSubNodes(); ++i) {
293 if (pParent->GetSubNode(i) == this) {
294 return i;
298 DBG_ASSERT(false, "Connection between parent and child is inconsistent.");
299 return 0;
303 #if OSL_DEBUG_LEVEL > 1
304 void SmNode::ToggleDebug() const
305 // toggle 'bIsDebug' in current subtree
307 SmNode *pThis = (SmNode *) this;
309 pThis->bIsDebug = bIsDebug ? false : true;
311 SmNode *pNode;
312 sal_uInt16 nSize = GetNumSubNodes();
313 for (sal_uInt16 i = 0; i < nSize; i++)
314 if (NULL != (pNode = pThis->GetSubNode(i)))
315 pNode->ToggleDebug();
317 #endif
320 void SmNode::Move(const Point& rPosition)
322 if (rPosition.X() == 0 && rPosition.Y() == 0)
323 return;
325 SmRect::Move(rPosition);
327 SmNode *pNode;
328 sal_uInt16 nSize = GetNumSubNodes();
329 for (sal_uInt16 i = 0; i < nSize; i++)
330 if (NULL != (pNode = GetSubNode(i)))
331 pNode->Move(rPosition);
335 void SmNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
337 SmNode *pNode;
338 sal_uInt16 nSize = GetNumSubNodes();
339 for (sal_uInt16 i = 0; i < nSize; i++)
340 if (NULL != (pNode = GetSubNode(i)))
341 pNode->Arrange(rDev, rFormat);
344 void SmNode::CreateTextFromNode(OUString &rText)
346 SmNode *pNode;
347 sal_uInt16 nSize = GetNumSubNodes();
348 if (nSize > 1)
349 rText += "{";
350 for (sal_uInt16 i = 0; i < nSize; i++)
351 if (NULL != (pNode = GetSubNode(i)))
352 pNode->CreateTextFromNode(rText);
353 if (nSize > 1)
355 rText = comphelper::string::stripEnd(rText, ' ');
356 rText += "} ";
361 void SmNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
366 void SmNode::AdaptToY(const OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
371 const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
372 // returns (first) ** visible ** (sub)node with the tokens text at
373 // position 'nRow', 'nCol'.
374 //! (there should be exactly one such node if any)
376 if ( IsVisible()
377 && nRow == GetToken().nRow
378 && nCol >= GetToken().nCol && nCol < GetToken().nCol + GetToken().aText.getLength())
379 return this;
380 else
382 sal_uInt16 nNumSubNodes = GetNumSubNodes();
383 for (sal_uInt16 i = 0; i < nNumSubNodes; i++)
384 { const SmNode *pNode = GetSubNode(i);
386 if (!pNode)
387 continue;
389 const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
390 if (pResult)
391 return pResult;
395 return 0;
399 const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
401 long nDist = LONG_MAX;
402 const SmNode *pResult = 0;
404 if (IsVisible())
405 pResult = this;
406 else
408 sal_uInt16 nNumSubNodes = GetNumSubNodes();
409 for (sal_uInt16 i = 0; i < nNumSubNodes; i++)
410 { const SmNode *pNode = GetSubNode(i);
412 if (!pNode)
413 continue;
415 long nTmp;
416 const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
417 if (pFound && (nTmp = pFound->OrientedDist(rPoint)) < nDist)
418 { nDist = nTmp;
419 pResult = pFound;
421 // quit immediately if 'rPoint' is inside the *should not
422 // overlap with other rectangles* part.
423 // This (partly) serves for getting the attributes in eg
424 // "bar overstrike a".
425 // ('nDist < 0' is used as *quick shot* to avoid evaluation of
426 // the following expression, where the result is already determined)
427 if (nDist < 0 && pFound->IsInsideRect(rPoint))
428 break;
433 return pResult;
436 void SmNode::GetAccessibleText( OUStringBuffer &/*rText*/ ) const
438 SAL_WARN("starmath", "SmNode: GetAccessibleText not overridden");
441 const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
443 const SmNode *pResult = 0;
445 sal_Int32 nIdx = GetAccessibleIndex();
446 OUStringBuffer aTxt;
447 if (nIdx >= 0)
448 GetAccessibleText( aTxt ); // get text if used in following 'if' statement
450 if (nIdx >= 0
451 && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.getLength())
452 pResult = this;
453 else
455 sal_uInt16 nNumSubNodes = GetNumSubNodes();
456 for (sal_uInt16 i = 0; i < nNumSubNodes; i++)
458 const SmNode *pNode = GetSubNode(i);
459 if (!pNode)
460 continue;
462 pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
463 if (pResult)
464 return pResult;
468 return pResult;
471 #ifdef DEBUG_ENABLE_DUMPASDOT
472 void SmNode::DumpAsDot(std::ostream &out, OUString* label, int number, int& id, int parent) const
474 //If this is the root start the file
475 if(number == -1){
476 out<<"digraph {"<<std::endl;
477 if(label){
478 out<<"labelloc = \"t\";"<<std::endl;
479 OUString eq(*label);
480 //CreateTextFromNode(eq);
481 eq = eq.replaceAll("\n", " ");
482 eq = eq.replaceAll("\\", "\\\\");
483 eq = eq.replaceAll("\"", "\\\"");
484 out<<"label= \"Equation: \\\"";
485 out<< OUStringToOString(eq, RTL_TEXTENCODING_UTF8).getStr();
486 out<<"\\\"\";"<<std::endl;
490 //Some how out<<(int)this; doesn't work... So we do this nasty workaround...
491 char strid[100];
492 sprintf(strid, "%i", id);
494 char strnr[100];
495 sprintf(strnr, "%i", number);
497 //Dump connection to this node
498 if( parent != -1 ){
499 char pid[100];
500 sprintf(pid, "%i", parent);
501 out<<"n"<<pid<<" -> n"<<strid<<" [label=\""<<strnr<<"\"];"<<std::endl;
502 //If doesn't have parent and isn't a rootnode:
503 } else if(number != -1) {
504 out<<"orphaned -> n"<<strid<<" [label=\""<<strnr<<"\"];"<<std::endl;
507 //Dump this node
508 out<<"n"<< strid<<" [label=\"";
509 switch( GetType() ) {
510 case NTABLE: out<<"SmTableNode"; break;
511 case NBRACE: out<<"SmBraceNode"; break;
512 case NBRACEBODY: out<<"SmBracebodyNode"; break;
513 case NOPER: out<<"SmOperNode"; break;
514 case NALIGN: out<<"SmAlignNode"; break;
515 case NATTRIBUT: out<<"SmAttributNode"; break;
516 case NFONT: out<<"SmFontNode"; break;
517 case NUNHOR: out<<"SmUnHorNode"; break;
518 case NBINHOR: out<<"SmBinHorNode"; break;
519 case NBINVER: out<<"SmBinVerNode"; break;
520 case NBINDIAGONAL: out<<"SmBinDiagonalNode"; break;
521 case NSUBSUP: out<<"SmSubSupNode"; break;
522 case NMATRIX: out<<"SmMatrixNode"; break;
523 case NPLACE: out<<"SmPlaceNode"; break;
524 case NTEXT:
525 out<<"SmTextNode: ";
526 out<< OUStringToOString(((SmTextNode*)this)->GetText(), RTL_TEXTENCODING_UTF8).getStr();
527 break;
528 case NSPECIAL: out<<"SmSpecialNode"; break;
529 case NGLYPH_SPECIAL: out<<"SmGlyphSpecialNode"; break;
530 case NMATH:
531 out<<"SmMathSymbolNode: ";
532 out<< OUStringToOString(((SmMathSymbolNode*)this)->GetText(), RTL_TEXTENCODING_UTF8).getStr();
533 break;
534 case NBLANK: out<<"SmBlankNode"; break;
535 case NERROR: out<<"SmErrorNode"; break;
536 case NLINE: out<<"SmLineNode"; break;
537 case NEXPRESSION: out<<"SmExpressionNode"; break;
538 case NPOLYLINE: out<<"SmPolyLineNode"; break;
539 case NROOT: out<<"SmRootNode"; break;
540 case NROOTSYMBOL: out<<"SmRootSymbolNode"; break;
541 case NRECTANGLE: out<<"SmRectangleNode"; break;
542 case NVERTICAL_BRACE: out<<"SmVerticalBraceNode"; break;
543 case NMATHIDENT: out<<"SmMathIdentifierNode"; break;
544 case NINTDYNSYMBOL: out<<"SmDynIntegralSymbolNode"; break;
545 case NINTDYN: out<<"SmDynIntegralNode"; break;
546 default:
547 out<<"Unknown Node";
549 out<<"\"";
550 if(IsSelected())
551 out<<", style=dashed";
552 out<<"];"<<std::endl;
554 //Dump subnodes
555 int myid = id;
556 const SmNode *pNode;
557 sal_uInt16 nSize = GetNumSubNodes();
558 for (sal_uInt16 i = 0; i < nSize; i++)
559 if (NULL != (pNode = GetSubNode(i)))
560 pNode->DumpAsDot(out, NULL, i, ++id, myid);
562 //If this is the root end the file
563 if( number == -1 )
564 out<<"}"<<std::endl;
566 #endif /* DEBUG_ENABLE_DUMPASDOT */
568 long SmNode::GetFormulaBaseline() const
570 SAL_WARN("starmath", "This dummy implementation should not have been called.");
571 return 0;
576 SmStructureNode::SmStructureNode( const SmStructureNode &rNode ) :
577 SmNode( rNode.GetType(), rNode.GetToken() )
579 size_t i;
580 for (i = 0; i < aSubNodes.size(); i++)
581 delete aSubNodes[i];
582 aSubNodes.resize(0);
584 auto nSize = rNode.aSubNodes.size();
585 aSubNodes.resize( nSize );
586 for (i = 0; i < nSize; ++i)
588 SmNode *pNode = rNode.aSubNodes[i];
589 aSubNodes[i] = pNode ? new SmNode( *pNode ) : 0;
591 ClaimPaternity();
595 SmStructureNode::~SmStructureNode()
597 SmNode *pNode;
599 for (sal_uInt16 i = 0; i < GetNumSubNodes(); i++)
600 if (NULL != (pNode = GetSubNode(i)))
601 delete pNode;
605 SmStructureNode & SmStructureNode::operator = ( const SmStructureNode &rNode )
607 SmNode::operator = ( rNode );
609 size_t i;
610 for (i = 0; i < aSubNodes.size(); i++)
611 delete aSubNodes[i];
612 aSubNodes.resize(0);
614 auto nSize = rNode.aSubNodes.size();
615 aSubNodes.resize( nSize );
616 for (i = 0; i < nSize; ++i)
618 SmNode *pNode = rNode.aSubNodes[i];
619 aSubNodes[i] = pNode ? new SmNode( *pNode ) : 0;
622 ClaimPaternity();
624 return *this;
628 void SmStructureNode::SetSubNodes(SmNode *pFirst, SmNode *pSecond, SmNode *pThird)
630 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
631 aSubNodes.resize( nSize );
632 if (pFirst)
633 aSubNodes[0] = pFirst;
634 if (pSecond)
635 aSubNodes[1] = pSecond;
636 if (pThird)
637 aSubNodes[2] = pThird;
639 ClaimPaternity();
643 void SmStructureNode::SetSubNodes(const SmNodeArray &rNodeArray)
645 aSubNodes = rNodeArray;
646 ClaimPaternity();
650 bool SmStructureNode::IsVisible() const
652 return false;
656 sal_uInt16 SmStructureNode::GetNumSubNodes() const
658 return (sal_uInt16) aSubNodes.size();
662 SmNode * SmStructureNode::GetSubNode(sal_uInt16 nIndex)
664 return aSubNodes[nIndex];
668 void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
670 sal_uInt16 nNodes = GetNumSubNodes();
671 for (sal_uInt16 i = 0; i < nNodes; ++i)
673 SmNode *pNode = const_cast<SmStructureNode *>(this)->GetSubNode(i);
674 if (pNode)
676 if (pNode->IsVisible())
677 static_cast<SmStructureNode *>(pNode)->nAccIndex = rText.getLength();
678 pNode->GetAccessibleText( rText );
686 bool SmVisibleNode::IsVisible() const
688 return true;
692 sal_uInt16 SmVisibleNode::GetNumSubNodes() const
694 return 0;
698 SmNode * SmVisibleNode::GetSubNode(sal_uInt16 /*nIndex*/)
700 return NULL;
706 void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
708 rText.append(GetToken().aText);
714 void SmExpressionNode::CreateTextFromNode(OUString &rText)
716 SmNode *pNode;
717 sal_uInt16 nSize = GetNumSubNodes();
718 if (nSize > 1)
719 rText += "{";
720 for (sal_uInt16 i = 0; i < nSize; i++)
721 if (NULL != (pNode = GetSubNode(i)))
723 pNode->CreateTextFromNode(rText);
724 //Just a bit of foo to make unary +asd -asd +-asd -+asd look nice
725 if (pNode->GetType() == NMATH)
726 if ((nSize != 2) ||
727 ( !rText.endsWith("+") && !rText.endsWith("-") ))
728 rText += " ";
731 if (nSize > 1)
733 rText = comphelper::string::stripEnd(rText, ' ');
734 rText += "} ";
741 void SmTableNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
742 // arranges all subnodes in one column
744 SmNode *pNode;
745 sal_uInt16 nSize = GetNumSubNodes();
747 // make distance depend on font size
748 long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
749 * GetFont().GetSize().Height()) / 100L;
751 if (nSize < 1)
752 return;
754 // arrange subnodes and get maximum width of them
755 long nMaxWidth = 0,
756 nTmp;
757 sal_uInt16 i;
758 for (i = 0; i < nSize; i++)
759 if (NULL != (pNode = GetSubNode(i)))
760 { pNode->Arrange(rDev, rFormat);
761 if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
762 nMaxWidth = nTmp;
765 Point aPos;
766 SmRect::operator = (SmRect(nMaxWidth, 1));
767 for (i = 0; i < nSize; i++)
768 { if (NULL != (pNode = GetSubNode(i)))
769 { const SmRect &rNodeRect = pNode->GetRect();
770 const SmNode *pCoNode = pNode->GetLeftMost();
771 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
773 aPos = rNodeRect.AlignTo(*this, RP_BOTTOM,
774 eHorAlign, RVA_BASELINE);
775 if (i)
776 aPos.Y() += nDist;
777 pNode->MoveTo(aPos);
778 ExtendBy(rNodeRect, nSize > 1 ? RCP_NONE : RCP_ARG);
781 // #i972#
782 if (HasBaseline())
783 nFormulaBaseline = GetBaseline();
784 else
786 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
787 aTmpDev.SetFont(GetFont());
789 SmRect aRect = (SmRect(aTmpDev, &rFormat, OUString("a"),
790 GetFont().GetBorderWidth()));
791 nFormulaBaseline = GetAlignM();
792 // move from middle position by constant - distance
793 // between middle and baseline for single letter
794 nFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
799 SmNode * SmTableNode::GetLeftMost()
801 return this;
805 long SmTableNode::GetFormulaBaseline() const
807 return nFormulaBaseline;
811 /**************************************************************************/
814 void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
816 SmNode::Prepare(rFormat, rDocShell);
818 // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
819 // to the rest of the formula compared to the 'FNT_MATH' font.
820 GetFont() = rFormat.GetFont(FNT_VARIABLE);
821 Flags() |= FLG_FONT;
825 /**************************************************************************/
828 void SmLineNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
829 // arranges all subnodes in one row with some extra space between
831 SmNode *pNode;
832 sal_uInt16 nSize = GetNumSubNodes();
833 sal_uInt16 i;
834 for (i = 0; i < nSize; i++)
835 if (NULL != (pNode = GetSubNode(i)))
836 pNode->Arrange(rDev, rFormat);
838 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
839 aTmpDev.SetFont(GetFont());
841 if (nSize < 1)
843 // provide an empty rectangle with alignment parameters for the "current"
844 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
845 // same sub-/supscript positions.)
846 //! be sure to use a character that has explicitly defined HiAttribut
847 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
848 //! 'vec {a}'.
849 SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString("a"),
850 GetFont().GetBorderWidth()));
851 // make sure that the rectangle occupies (almost) no space
852 SetWidth(1);
853 SetItalicSpaces(0, 0);
854 return;
857 // make distance depend on font size
858 long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetSize().Height()) / 100L;
859 if (!IsUseExtraSpaces())
860 nDist = 0;
862 Point aPos;
863 // copy the first node into LineNode and extend by the others
864 if (NULL != (pNode = GetSubNode(0)))
865 SmRect::operator = (pNode->GetRect());
867 for (i = 1; i < nSize; i++)
868 if (NULL != (pNode = GetSubNode(i)))
870 aPos = pNode->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE);
872 // add horizontal space to the left for each but the first sub node
873 aPos.X() += nDist;
875 pNode->MoveTo(aPos);
876 ExtendBy( *pNode, RCP_XOR );
881 /**************************************************************************/
884 void SmExpressionNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
885 // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
887 SmLineNode::Arrange(rDev, rFormat);
889 // copy alignment of leftmost subnode if any
890 SmNode *pNode = GetLeftMost();
891 if (pNode)
892 SetRectHorAlign(pNode->GetRectHorAlign(), false);
896 /**************************************************************************/
899 void SmUnHorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
901 bool bIsPostfix = GetToken().eType == TFACT;
903 SmNode *pOper = GetSubNode(bIsPostfix ? 1 : 0),
904 *pBody = GetSubNode(bIsPostfix ? 0 : 1);
905 OSL_ENSURE(pOper, "Sm: NULL pointer");
906 OSL_ENSURE(pBody, "Sm: NULL pointer");
908 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
909 pOper->Arrange(rDev, rFormat);
910 pBody->Arrange(rDev, rFormat);
912 Point aPos = pOper->AlignTo(*pBody, bIsPostfix ? RP_RIGHT : RP_LEFT,
913 RHA_CENTER, RVA_BASELINE);
914 // add a bit space between operator and argument
915 // (worst case -{1 over 2} where - and over have almost no space inbetween)
916 long nDelta = pOper->GetFont().GetSize().Height() / 20;
917 if (bIsPostfix)
918 aPos.X() += nDelta;
919 else
920 aPos.X() -= nDelta;
921 pOper->MoveTo(aPos);
923 SmRect::operator = (*pBody);
924 long nOldBot = GetBottom();
926 ExtendBy(*pOper, RCP_XOR);
928 // workaround for Bug 50865: "a^2 a^+2" have different baselines
929 // for exponents (if size of exponent is large enough)
930 SetBottom(nOldBot);
934 /**************************************************************************/
937 void SmRootNode::GetHeightVerOffset(const SmRect &rRect,
938 long &rHeight, long &rVerOffset)
939 // calculate height and vertical offset of root sign suitable for 'rRect'
941 rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
942 rHeight = rRect.GetHeight() - rVerOffset;
944 OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
945 OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
949 Point SmRootNode::GetExtraPos(const SmRect &rRootSymbol,
950 const SmRect &rExtra)
952 const Size &rSymSize = rRootSymbol.GetSize();
954 Point aPos = rRootSymbol.GetTopLeft()
955 + Point((rSymSize.Width() * 70) / 100,
956 (rSymSize.Height() * 52) / 100);
958 // from this calculate topleft edge of 'rExtra'
959 aPos.X() -= rExtra.GetWidth() + rExtra.GetItalicRightSpace();
960 aPos.Y() -= rExtra.GetHeight();
961 // if there's enough space move a bit less to the right
962 // examples: "nroot i a", "nroot j a"
963 // (it looks better if we don't use italic-spaces here)
964 long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
965 if (aPos.X() > nX)
966 aPos.X() = nX;
968 return aPos;
972 void SmRootNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
974 //! pExtra needs to have the smaller index than pRootSym in order to
975 //! not to get the root symbol but the pExtra when clicking on it in the
976 //! GraphicWindow. (That is because of the simplicity of the algorithm
977 //! that finds the node corresponding to a mouseclick in the window.)
978 SmNode *pExtra = GetSubNode(0),
979 *pRootSym = GetSubNode(1),
980 *pBody = GetSubNode(2);
981 OSL_ENSURE(pRootSym, "Sm: NULL pointer");
982 OSL_ENSURE(pBody, "Sm: NULL pointer");
984 pBody->Arrange(rDev, rFormat);
986 long nHeight,
987 nVerOffset;
988 GetHeightVerOffset(*pBody, nHeight, nVerOffset);
989 nHeight += rFormat.GetDistance(DIS_ROOT)
990 * GetFont().GetSize().Height() / 100L;
992 // font specialist advised to change the width first
993 pRootSym->AdaptToY(rDev, nHeight);
994 pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
996 pRootSym->Arrange(rDev, rFormat);
998 Point aPos = pRootSym->AlignTo(*pBody, RP_LEFT, RHA_CENTER, RVA_BASELINE);
999 //! override calculated vertical position
1000 aPos.Y() = pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom();
1001 aPos.Y() -= nVerOffset;
1002 pRootSym->MoveTo(aPos);
1004 if (pExtra)
1005 { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
1006 pExtra->Arrange(rDev, rFormat);
1008 aPos = GetExtraPos(*pRootSym, *pExtra);
1009 pExtra->MoveTo(aPos);
1012 SmRect::operator = (*pBody);
1013 ExtendBy(*pRootSym, RCP_THIS);
1014 if (pExtra)
1015 ExtendBy(*pExtra, RCP_THIS, true);
1019 void SmRootNode::CreateTextFromNode(OUString &rText)
1021 SmNode *pExtra = GetSubNode(0);
1022 if (pExtra)
1024 rText += "nroot ";
1025 pExtra->CreateTextFromNode(rText);
1027 else
1028 rText += "sqrt ";
1030 if (!pExtra && GetSubNode(2)->GetNumSubNodes() > 1)
1031 rText += "{ ";
1033 GetSubNode(2)->CreateTextFromNode(rText);
1035 if (!pExtra && GetSubNode(2)->GetNumSubNodes() > 1)
1036 rText += "} ";
1039 /**************************************************************************/
1042 void SmDynIntegralNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1044 SmNode *pDynIntegralSym = Symbol(),
1045 *pBody = Body();
1046 OSL_ENSURE(pDynIntegralSym, "Sm: NULL pointer");
1047 OSL_ENSURE(pBody, "Sm: NULL pointer");
1049 pBody->Arrange(rDev, rFormat);
1051 long nHeight = pBody->GetHeight();
1052 pDynIntegralSym->AdaptToY(rDev, nHeight);
1054 pDynIntegralSym->Arrange(rDev, rFormat);
1056 Point aPos = pDynIntegralSym->AlignTo(*pBody, RP_LEFT, RHA_CENTER, RVA_BASELINE);
1057 //! override calculated vertical position
1058 aPos.Y() = pDynIntegralSym->GetTop() + pBody->GetBottom() - pDynIntegralSym->GetBottom();
1059 pDynIntegralSym->MoveTo(aPos);
1062 // override its own rectangle with pBody's
1063 SmRect::operator = (*pBody);
1064 // extends this rectangle with the symbol's one
1065 ExtendBy(*pDynIntegralSym, RCP_THIS);
1070 void SmDynIntegralNode::CreateTextFromNode(OUString &rText)
1073 rText += "intd ";
1074 SmNode *pBody = GetSubNode(1);
1076 if (pBody->GetNumSubNodes() > 1)
1077 rText += "{ ";
1079 pBody->CreateTextFromNode(rText);
1081 if (pBody->GetNumSubNodes() > 1)
1082 rText += "} ";
1087 /**************************************************************************/
1090 void SmBinHorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1092 SmNode *pLeft = GetSubNode(0),
1093 *pOper = GetSubNode(1),
1094 *pRight = GetSubNode(2);
1095 OSL_ENSURE(pLeft != NULL, "Sm: NULL pointer");
1096 OSL_ENSURE(pOper != NULL, "Sm: NULL pointer");
1097 OSL_ENSURE(pRight != NULL, "Sm: NULL pointer");
1099 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
1101 pLeft ->Arrange(rDev, rFormat);
1102 pOper ->Arrange(rDev, rFormat);
1103 pRight->Arrange(rDev, rFormat);
1105 const SmRect &rOpRect = pOper->GetRect();
1107 long nDist = (rOpRect.GetWidth() *
1108 rFormat.GetDistance(DIS_HORIZONTAL)) / 100L;
1110 SmRect::operator = (*pLeft);
1112 Point aPos;
1113 aPos = pOper->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE);
1114 aPos.X() += nDist;
1115 pOper->MoveTo(aPos);
1116 ExtendBy(*pOper, RCP_XOR);
1118 aPos = pRight->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE);
1119 aPos.X() += nDist;
1121 pRight->MoveTo(aPos);
1122 ExtendBy(*pRight, RCP_XOR);
1126 /**************************************************************************/
1129 void SmBinVerNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1131 SmNode *pNum = GetSubNode(0),
1132 *pLine = GetSubNode(1),
1133 *pDenom = GetSubNode(2);
1134 OSL_ENSURE(pNum, "Sm : NULL pointer");
1135 OSL_ENSURE(pLine, "Sm : NULL pointer");
1136 OSL_ENSURE(pDenom, "Sm : NULL pointer");
1138 bool bIsTextmode = rFormat.IsTextmode();
1139 if (bIsTextmode)
1141 Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
1142 pNum ->SetSize(aFraction);
1143 pLine ->SetSize(aFraction);
1144 pDenom->SetSize(aFraction);
1147 pNum ->Arrange(rDev, rFormat);
1148 pDenom->Arrange(rDev, rFormat);
1150 long nFontHeight = GetFont().GetSize().Height(),
1151 nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100L,
1152 nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L,
1153 nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
1154 nNumDist = bIsTextmode ? 0 :
1155 nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100L,
1156 nDenomDist = bIsTextmode ? 0 :
1157 nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100L;
1159 // font specialist advised to change the width first
1160 pLine->AdaptToY(rDev, nThick);
1161 pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
1162 pLine->Arrange(rDev, rFormat);
1164 // get horizontal alignment for numerator
1165 const SmNode *pLM = pNum->GetLeftMost();
1166 RectHorAlign eHorAlign = pLM->GetRectHorAlign();
1168 // move numerator to its position
1169 Point aPos = pNum->AlignTo(*pLine, RP_TOP, eHorAlign, RVA_BASELINE);
1170 aPos.Y() -= nNumDist;
1171 pNum->MoveTo(aPos);
1173 // get horizontal alignment for denominator
1174 pLM = pDenom->GetLeftMost();
1175 eHorAlign = pLM->GetRectHorAlign();
1177 // move denominator to its position
1178 aPos = pDenom->AlignTo(*pLine, RP_BOTTOM, eHorAlign, RVA_BASELINE);
1179 aPos.Y() += nDenomDist;
1180 pDenom->MoveTo(aPos);
1182 SmRect::operator = (*pNum);
1183 ExtendBy(*pDenom, RCP_NONE).ExtendBy(*pLine, RCP_NONE, pLine->GetCenterY());
1186 void SmBinVerNode::CreateTextFromNode(OUString &rText)
1188 SmNode *pNum = GetSubNode(0),
1189 *pDenom = GetSubNode(2);
1190 pNum->CreateTextFromNode(rText);
1191 rText += "over ";
1192 pDenom->CreateTextFromNode(rText);
1196 SmNode * SmBinVerNode::GetLeftMost()
1198 return this;
1202 namespace {
1204 /// @return value of the determinant formed by the two points
1205 double Det(const Point &rHeading1, const Point &rHeading2)
1207 return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
1211 /// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
1212 /// and has the direction vector 'rHeading2'
1213 bool IsPointInLine(const Point &rPoint1,
1214 const Point &rPoint2, const Point &rHeading2)
1216 OSL_ENSURE(rHeading2 != Point(), "Sm : 0 vector");
1218 bool bRes = false;
1219 static const double eps = 5.0 * DBL_EPSILON;
1221 double fLambda;
1222 if (labs(rHeading2.X()) > labs(rHeading2.Y()))
1224 fLambda = (rPoint1.X() - rPoint2.X()) / (double) rHeading2.X();
1225 bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
1227 else
1229 fLambda = (rPoint1.Y() - rPoint2.Y()) / (double) rHeading2.Y();
1230 bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
1233 return bRes;
1237 sal_uInt16 GetLineIntersectionPoint(Point &rResult,
1238 const Point& rPoint1, const Point &rHeading1,
1239 const Point& rPoint2, const Point &rHeading2)
1241 OSL_ENSURE(rHeading1 != Point(), "Sm : 0 vector");
1242 OSL_ENSURE(rHeading2 != Point(), "Sm : 0 vector");
1244 sal_uInt16 nRes = 1;
1245 static const double eps = 5.0 * DBL_EPSILON;
1247 // are the direction vectors linearly dependent?
1248 double fDet = Det(rHeading1, rHeading2);
1249 if (fabs(fDet) < eps)
1251 nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
1252 rResult = nRes ? rPoint1 : Point();
1254 else
1256 // here we do not pay attention to the computational accurancy
1257 // (that would be more complicated and is not really worth it in this case)
1258 double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
1259 - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
1260 / fDet;
1261 rResult = Point(rPoint1.X() + (long) (fLambda * rHeading1.X()),
1262 rPoint1.Y() + (long) (fLambda * rHeading1.Y()));
1265 return nRes;
1271 SmBinDiagonalNode::SmBinDiagonalNode(const SmToken &rNodeToken)
1272 : SmStructureNode(NBINDIAGONAL, rNodeToken)
1274 bAscending = false;
1275 SetNumSubNodes(3);
1279 /// @return position and size of the diagonal line
1280 /// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
1281 void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
1282 const Point &rDiagPoint, double fAngleDeg) const
1285 static const double fPi = 3.1415926535897932384626433;
1286 double fAngleRad = fAngleDeg / 180.0 * fPi;
1287 long nRectLeft = GetItalicLeft(),
1288 nRectRight = GetItalicRight(),
1289 nRectTop = GetTop(),
1290 nRectBottom = GetBottom();
1291 Point aRightHdg (100, 0),
1292 aDownHdg (0, 100),
1293 aDiagHdg ( (long)(100.0 * cos(fAngleRad)),
1294 (long)(-100.0 * sin(fAngleRad)) );
1296 long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
1297 Point aPoint;
1298 if (IsAscending())
1300 // determine top right corner
1301 GetLineIntersectionPoint(aPoint,
1302 Point(nRectLeft, nRectTop), aRightHdg,
1303 rDiagPoint, aDiagHdg);
1304 // is there a point of intersection with the top border?
1305 if (aPoint.X() <= nRectRight)
1307 nRight = aPoint.X();
1308 nTop = nRectTop;
1310 else
1312 // there has to be a point of intersection with the right border!
1313 GetLineIntersectionPoint(aPoint,
1314 Point(nRectRight, nRectTop), aDownHdg,
1315 rDiagPoint, aDiagHdg);
1317 nRight = nRectRight;
1318 nTop = aPoint.Y();
1321 // determine bottom left corner
1322 GetLineIntersectionPoint(aPoint,
1323 Point(nRectLeft, nRectBottom), aRightHdg,
1324 rDiagPoint, aDiagHdg);
1325 // is there a point of intersection with the bottom border?
1326 if (aPoint.X() >= nRectLeft)
1328 nLeft = aPoint.X();
1329 nBottom = nRectBottom;
1331 else
1333 // there has to be a point of intersection with the left border!
1334 GetLineIntersectionPoint(aPoint,
1335 Point(nRectLeft, nRectTop), aDownHdg,
1336 rDiagPoint, aDiagHdg);
1338 nLeft = nRectLeft;
1339 nBottom = aPoint.Y();
1342 else
1344 // determine top left corner
1345 GetLineIntersectionPoint(aPoint,
1346 Point(nRectLeft, nRectTop), aRightHdg,
1347 rDiagPoint, aDiagHdg);
1348 // is there a point of intersection with the top border?
1349 if (aPoint.X() >= nRectLeft)
1351 nLeft = aPoint.X();
1352 nTop = nRectTop;
1354 else
1356 // there has to be a point of intersection with the left border!
1357 GetLineIntersectionPoint(aPoint,
1358 Point(nRectLeft, nRectTop), aDownHdg,
1359 rDiagPoint, aDiagHdg);
1361 nLeft = nRectLeft;
1362 nTop = aPoint.Y();
1365 // determine bottom right corner
1366 GetLineIntersectionPoint(aPoint,
1367 Point(nRectLeft, nRectBottom), aRightHdg,
1368 rDiagPoint, aDiagHdg);
1369 // is there a point of intersection with the bottom border?
1370 if (aPoint.X() <= nRectRight)
1372 nRight = aPoint.X();
1373 nBottom = nRectBottom;
1375 else
1377 // there has to be a point of intersection with the right border!
1378 GetLineIntersectionPoint(aPoint,
1379 Point(nRectRight, nRectTop), aDownHdg,
1380 rDiagPoint, aDiagHdg);
1382 nRight = nRectRight;
1383 nBottom = aPoint.Y();
1387 rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
1388 rPos.X() = nLeft;
1389 rPos.Y() = nTop;
1393 void SmBinDiagonalNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1395 // Both arguments have to get into the SubNodes before the Operator so that clicking
1396 // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
1397 SmNode *pLeft = GetSubNode(0),
1398 *pRight = GetSubNode(1);
1399 OSL_ENSURE(pLeft, "Sm : NULL pointer");
1400 OSL_ENSURE(pRight, "Sm : NULL pointer");
1402 OSL_ENSURE(GetSubNode(2)->GetType() == NPOLYLINE, "Sm : wrong node type");
1403 SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(GetSubNode(2));
1404 OSL_ENSURE(pOper, "Sm : NULL pointer");
1406 //! some routines being called extract some info from the OutputDevice's
1407 //! font (eg the space to be used for borders OR the font name(!!)).
1408 //! Thus the font should reflect the needs and has to be set!
1409 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
1410 aTmpDev.SetFont(GetFont());
1412 pLeft->Arrange(aTmpDev, rFormat);
1413 pRight->Arrange(aTmpDev, rFormat);
1415 // determine implicitly the values (incl. the margin) of the diagonal line
1416 pOper->Arrange(aTmpDev, rFormat);
1418 long nDelta = pOper->GetWidth() * 8 / 10;
1420 // determine TopLeft position from the right argument
1421 Point aPos;
1422 aPos.X() = pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace();
1423 if (IsAscending())
1424 aPos.Y() = pLeft->GetBottom() + nDelta;
1425 else
1426 aPos.Y() = pLeft->GetTop() - nDelta - pRight->GetHeight();
1428 pRight->MoveTo(aPos);
1430 // determine new baseline
1431 long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
1432 : (pLeft->GetTop() + pRight->GetBottom()) / 2;
1433 Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
1434 nTmpBaseline);
1436 SmRect::operator = (*pLeft);
1437 ExtendBy(*pRight, RCP_NONE);
1440 // determine position and size of diagonal line
1441 Size aTmpSize;
1442 GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
1444 // font specialist advised to change the width first
1445 pOper->AdaptToY(aTmpDev, aTmpSize.Height());
1446 pOper->AdaptToX(aTmpDev, aTmpSize.Width());
1447 // and make it active
1448 pOper->Arrange(aTmpDev, rFormat);
1450 pOper->MoveTo(aPos);
1452 ExtendBy(*pOper, RCP_NONE, nTmpBaseline);
1456 /**************************************************************************/
1459 void SmSubSupNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1461 OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
1462 "Sm: wrong number of subnodes");
1464 SmNode *pBody = GetBody();
1465 OSL_ENSURE(pBody, "Sm: NULL pointer");
1467 long nOrigHeight = pBody->GetFont().GetSize().Height();
1469 pBody->Arrange(rDev, rFormat);
1471 const SmRect &rBodyRect = pBody->GetRect();
1472 SmRect::operator = (rBodyRect);
1474 // line that separates sub- and supscript rectangles
1475 long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
1477 Point aPos;
1478 long nDelta, nDist;
1480 // iterate over all possible sub-/supscripts
1481 SmRect aTmpRect (rBodyRect);
1482 for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
1483 { SmSubSup eSubSup = (SmSubSup) i; // cast
1484 SmNode *pSubSup = GetSubSup(eSubSup);
1486 if (!pSubSup)
1487 continue;
1489 // switch position of limits if we are in textmode
1490 if (rFormat.IsTextmode() && (GetToken().nGroup & TGLIMIT))
1491 switch (eSubSup)
1492 { case CSUB: eSubSup = RSUB; break;
1493 case CSUP: eSubSup = RSUP; break;
1494 default:
1495 break;
1498 // prevent sub-/supscripts from diminishing in size
1499 // (as would be in "a_{1_{2_{3_4}}}")
1500 if (GetFont().GetSize().Height() > rFormat.GetBaseSize().Height() / 3)
1502 sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
1503 SIZ_LIMITS : SIZ_INDEX;
1504 Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
1505 pSubSup->SetSize(aFraction);
1508 pSubSup->Arrange(rDev, rFormat);
1510 bool bIsTextmode = rFormat.IsTextmode();
1511 nDist = 0;
1513 //! be sure that CSUB, CSUP are handled before the other cases!
1514 switch (eSubSup)
1515 { case RSUB :
1516 case LSUB :
1517 if (!bIsTextmode)
1518 nDist = nOrigHeight
1519 * rFormat.GetDistance(DIS_SUBSCRIPT) / 100L;
1520 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1521 eSubSup == LSUB ? RP_LEFT : RP_RIGHT,
1522 RHA_CENTER, RVA_BOTTOM);
1523 aPos.Y() += nDist;
1524 nDelta = nDelimLine - aPos.Y();
1525 if (nDelta > 0)
1526 aPos.Y() += nDelta;
1527 break;
1528 case RSUP :
1529 case LSUP :
1530 if (!bIsTextmode)
1531 nDist = nOrigHeight
1532 * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100L;
1533 aPos = pSubSup->GetRect().AlignTo(aTmpRect,
1534 eSubSup == LSUP ? RP_LEFT : RP_RIGHT,
1535 RHA_CENTER, RVA_TOP);
1536 aPos.Y() -= nDist;
1537 nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
1538 if (nDelta > 0)
1539 aPos.Y() -= nDelta;
1540 break;
1541 case CSUB :
1542 if (!bIsTextmode)
1543 nDist = nOrigHeight
1544 * rFormat.GetDistance(DIS_LOWERLIMIT) / 100L;
1545 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RP_BOTTOM,
1546 RHA_CENTER, RVA_BASELINE);
1547 aPos.Y() += nDist;
1548 break;
1549 case CSUP :
1550 if (!bIsTextmode)
1551 nDist = nOrigHeight
1552 * rFormat.GetDistance(DIS_UPPERLIMIT) / 100L;
1553 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RP_TOP,
1554 RHA_CENTER, RVA_BASELINE);
1555 aPos.Y() -= nDist;
1556 break;
1559 pSubSup->MoveTo(aPos);
1560 ExtendBy(*pSubSup, RCP_THIS, true);
1562 // update rectangle to which RSUB, RSUP, LSUB, LSUP
1563 // will be aligned to
1564 if (eSubSup == CSUB || eSubSup == CSUP)
1565 aTmpRect = *this;
1569 void SmSubSupNode::CreateTextFromNode(OUString &rText)
1571 SmNode *pNode;
1572 GetSubNode(0)->CreateTextFromNode(rText);
1574 if (NULL != (pNode = GetSubNode(LSUB+1)))
1576 rText += "lsub ";
1577 pNode->CreateTextFromNode(rText);
1579 if (NULL != (pNode = GetSubNode(LSUP+1)))
1581 rText += "lsup ";
1582 pNode->CreateTextFromNode(rText);
1584 if (NULL != (pNode = GetSubNode(CSUB+1)))
1586 rText += "csub ";
1587 pNode->CreateTextFromNode(rText);
1589 if (NULL != (pNode = GetSubNode(CSUP+1)))
1591 rText += "csup ";
1592 pNode->CreateTextFromNode(rText);
1594 if (NULL != (pNode = GetSubNode(RSUB+1)))
1596 rText = comphelper::string::stripEnd(rText, ' ');
1597 rText += "_";
1598 pNode->CreateTextFromNode(rText);
1600 if (NULL != (pNode = GetSubNode(RSUP+1)))
1602 rText = comphelper::string::stripEnd(rText, ' ');
1603 rText += "^";
1604 pNode->CreateTextFromNode(rText);
1609 /**************************************************************************/
1611 void SmBraceNode::CreateTextFromNode(OUString &rText)
1613 if (GetScaleMode() == SCALE_HEIGHT)
1614 rText += "left ";
1616 OUString aStr;
1617 GetSubNode(0)->CreateTextFromNode(aStr);
1618 aStr = comphelper::string::strip(aStr, ' ');
1619 aStr = comphelper::string::stripStart(aStr, '\\');
1620 if (!aStr.isEmpty())
1622 if (aStr == "divides")
1623 rText += "lline";
1624 else if (aStr == "parallel")
1625 rText += "ldline";
1626 else if (aStr == "<")
1627 rText += "langle";
1628 else
1629 rText += aStr;
1630 rText += " ";
1632 else
1633 rText += "none ";
1635 GetSubNode(1)->CreateTextFromNode(rText);
1636 if (GetScaleMode() == SCALE_HEIGHT)
1637 rText += "right ";
1639 OUString aStr;
1640 GetSubNode(2)->CreateTextFromNode(aStr);
1641 aStr = comphelper::string::strip(aStr, ' ');
1642 aStr = comphelper::string::stripStart(aStr, '\\');
1643 if (!aStr.isEmpty())
1645 if (aStr == "divides")
1646 rText += "rline";
1647 else if (aStr == "parallel")
1648 rText += "rdline";
1649 else if (aStr == ">")
1650 rText += "rangle";
1651 else
1652 rText += aStr;
1653 rText += " ";
1655 else
1656 rText += "none ";
1658 rText += " ";
1662 void SmBraceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1664 SmNode *pLeft = GetSubNode(0),
1665 *pBody = GetSubNode(1),
1666 *pRight = GetSubNode(2);
1667 OSL_ENSURE(pLeft, "Sm: NULL pointer");
1668 OSL_ENSURE(pBody, "Sm: NULL pointer");
1669 OSL_ENSURE(pRight, "Sm: NULL pointer");
1671 pBody->Arrange(rDev, rFormat);
1673 bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
1674 bScale = pBody->GetHeight() > 0 &&
1675 (GetScaleMode() == SCALE_HEIGHT || bIsScaleNormal),
1676 bIsABS = GetToken().eType == TABS;
1678 long nFaceHeight = GetFont().GetSize().Height();
1680 // determine oversize in %
1681 sal_uInt16 nPerc = 0;
1682 if (!bIsABS && bScale)
1683 { // in case of oversize braces...
1684 sal_uInt16 nIndex = GetScaleMode() == SCALE_HEIGHT ?
1685 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1686 nPerc = rFormat.GetDistance(nIndex);
1689 // determine the height for the braces
1690 long nBraceHeight;
1691 if (bScale)
1693 nBraceHeight = pBody->GetType() == NBRACEBODY ?
1694 static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
1695 : pBody->GetHeight();
1696 nBraceHeight += 2 * (nBraceHeight * nPerc / 100L);
1698 else
1699 nBraceHeight = nFaceHeight;
1701 // distance to the argument
1702 nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
1703 long nDist = nFaceHeight * nPerc / 100L;
1705 // if wanted, scale the braces to the wanted size
1706 if (bScale)
1708 Size aTmpSize (pLeft->GetFont().GetSize());
1709 OSL_ENSURE(pRight->GetFont().GetSize() == aTmpSize,
1710 "Sm : different font sizes");
1711 aTmpSize.Width() = std::min((long) nBraceHeight * 60L / 100L,
1712 rFormat.GetBaseSize().Height() * 3L / 2L);
1713 // correction factor since change from StarMath to OpenSymbol font
1714 // because of the different font width in the FontMetric
1715 aTmpSize.Width() *= 182;
1716 aTmpSize.Width() /= 267;
1718 sal_Unicode cChar = pLeft->GetToken().cMathChar;
1719 if (cChar != MS_LINE && cChar != MS_DLINE &&
1720 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1721 pLeft ->GetFont().SetSize(aTmpSize);
1723 cChar = pRight->GetToken().cMathChar;
1724 if (cChar != MS_LINE && cChar != MS_DLINE &&
1725 cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
1726 pRight->GetFont().SetSize(aTmpSize);
1728 pLeft ->AdaptToY(rDev, nBraceHeight);
1729 pRight->AdaptToY(rDev, nBraceHeight);
1732 pLeft ->Arrange(rDev, rFormat);
1733 pRight->Arrange(rDev, rFormat);
1735 // required in order to make "\(a\) - (a) - left ( a right )" look alright
1736 RectVerAlign eVerAlign = bScale ? RVA_CENTERY : RVA_BASELINE;
1738 Point aPos;
1739 aPos = pLeft->AlignTo(*pBody, RP_LEFT, RHA_CENTER, eVerAlign);
1740 aPos.X() -= nDist;
1741 pLeft->MoveTo(aPos);
1743 aPos = pRight->AlignTo(*pBody, RP_RIGHT, RHA_CENTER, eVerAlign);
1744 aPos.X() += nDist;
1745 pRight->MoveTo(aPos);
1747 SmRect::operator = (*pBody);
1748 ExtendBy(*pLeft, RCP_THIS).ExtendBy(*pRight, RCP_THIS);
1752 /**************************************************************************/
1755 void SmBracebodyNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1757 sal_uInt16 nNumSubNodes = GetNumSubNodes();
1758 if (nNumSubNodes == 0)
1759 return;
1761 // arrange arguments
1762 sal_uInt16 i;
1763 for (i = 0; i < nNumSubNodes; i += 2)
1764 GetSubNode(i)->Arrange(rDev, rFormat);
1766 // build reference rectangle with necessary info for vertical alignment
1767 SmRect aRefRect (*GetSubNode(0));
1768 for (i = 0; i < nNumSubNodes; i += 2)
1770 SmRect aTmpRect (*GetSubNode(i));
1771 Point aPos = aTmpRect.AlignTo(aRefRect, RP_RIGHT, RHA_CENTER, RVA_BASELINE);
1772 aTmpRect.MoveTo(aPos);
1773 aRefRect.ExtendBy(aTmpRect, RCP_XOR);
1776 nBodyHeight = aRefRect.GetHeight();
1778 // scale separators to required height and arrange them
1779 bool bScale = GetScaleMode() == SCALE_HEIGHT || rFormat.IsScaleNormalBrackets();
1780 long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetSize().Height();
1781 sal_uInt16 nIndex = GetScaleMode() == SCALE_HEIGHT ?
1782 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
1783 sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
1784 if (bScale)
1785 nHeight += 2 * (nHeight * nPerc / 100L);
1786 for (i = 1; i < nNumSubNodes; i += 2)
1788 SmNode *pNode = GetSubNode(i);
1789 pNode->AdaptToY(rDev, nHeight);
1790 pNode->Arrange(rDev, rFormat);
1793 // horizontal distance between argument and brackets or separators
1794 long nDist = GetFont().GetSize().Height()
1795 * rFormat.GetDistance(DIS_BRACKETSPACE) / 100L;
1797 SmNode *pLeft = GetSubNode(0);
1798 SmRect::operator = (*pLeft);
1799 for (i = 1; i < nNumSubNodes; i++)
1801 bool bIsSeparator = i % 2 != 0;
1802 RectVerAlign eVerAlign = bIsSeparator ? RVA_CENTERY : RVA_BASELINE;
1804 SmNode *pRight = GetSubNode(i);
1805 Point aPosX = pRight->AlignTo(*pLeft, RP_RIGHT, RHA_CENTER, eVerAlign),
1806 aPosY = pRight->AlignTo(aRefRect, RP_RIGHT, RHA_CENTER, eVerAlign);
1807 aPosX.X() += nDist;
1809 pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
1810 ExtendBy(*pRight, bIsSeparator ? RCP_THIS : RCP_XOR);
1812 pLeft = pRight;
1817 /**************************************************************************/
1820 void SmVerticalBraceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1822 SmNode *pBody = GetSubNode(0),
1823 *pBrace = GetSubNode(1),
1824 *pScript = GetSubNode(2);
1825 OSL_ENSURE(pBody, "Sm: NULL pointer!");
1826 OSL_ENSURE(pBrace, "Sm: NULL pointer!");
1827 OSL_ENSURE(pScript, "Sm: NULL pointer!");
1829 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
1830 aTmpDev.SetFont(GetFont());
1832 pBody->Arrange(aTmpDev, rFormat);
1834 // size is the same as for limits for this part
1835 pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
1836 // braces are a bit taller than usually
1837 pBrace ->SetSize( Fraction(3, 2) );
1839 long nItalicWidth = pBody->GetItalicWidth();
1840 if (nItalicWidth > 0)
1841 pBrace->AdaptToX(aTmpDev, nItalicWidth);
1843 pBrace ->Arrange(aTmpDev, rFormat);
1844 pScript->Arrange(aTmpDev, rFormat);
1846 // determine the relative position and the distances between each other
1847 RectPos eRectPos;
1848 long nFontHeight = pBody->GetFont().GetSize().Height();
1849 long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
1850 nDistScript = nFontHeight;
1851 if (GetToken().eType == TOVERBRACE)
1853 eRectPos = RP_TOP;
1854 nDistBody = - nDistBody;
1855 nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
1857 else // TUNDERBRACE
1859 eRectPos = RP_BOTTOM;
1860 nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
1862 nDistBody /= 100L;
1863 nDistScript /= 100L;
1865 Point aPos = pBrace->AlignTo(*pBody, eRectPos, RHA_CENTER, RVA_BASELINE);
1866 aPos.Y() += nDistBody;
1867 pBrace->MoveTo(aPos);
1869 aPos = pScript->AlignTo(*pBrace, eRectPos, RHA_CENTER, RVA_BASELINE);
1870 aPos.Y() += nDistScript;
1871 pScript->MoveTo(aPos);
1873 SmRect::operator = (*pBody);
1874 ExtendBy(*pBrace, RCP_THIS).ExtendBy(*pScript, RCP_THIS);
1878 /**************************************************************************/
1881 SmNode * SmOperNode::GetSymbol()
1883 SmNode *pNode = GetSubNode(0);
1884 OSL_ENSURE(pNode, "Sm: NULL pointer!");
1886 if (pNode->GetType() == NSUBSUP)
1887 pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
1889 OSL_ENSURE(pNode, "Sm: NULL pointer!");
1890 return pNode;
1894 long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
1895 const SmFormat &rFormat) const
1896 // returns the font height to be used for operator-symbol
1898 long nHeight = GetFont().GetSize().Height();
1900 SmTokenType eTmpType = GetToken().eType;
1901 if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
1902 return nHeight;
1904 if (!rFormat.IsTextmode())
1906 // set minimum size ()
1907 nHeight += (nHeight * 20L) / 100L;
1909 nHeight += nHeight
1910 * rFormat.GetDistance(DIS_OPERATORSIZE) / 100L;
1911 nHeight = nHeight * 686L / 845L;
1914 // correct user-defined symbols to match height of sum from used font
1915 if (rSymbol.GetToken().eType == TSPECIAL)
1916 nHeight = nHeight * 845L / 686L;
1918 return nHeight;
1922 void SmOperNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1924 SmNode *pOper = GetSubNode(0);
1925 SmNode *pBody = GetSubNode(1);
1927 OSL_ENSURE(pOper, "Sm: missing subnode");
1928 OSL_ENSURE(pBody, "Sm: missing subnode");
1930 SmNode *pSymbol = GetSymbol();
1931 pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
1932 pSymbol->GetFont().GetSize().Height()));
1934 pBody->Arrange(rDev, rFormat);
1935 pOper->Arrange(rDev, rFormat);
1937 long nOrigHeight = GetFont().GetSize().Height(),
1938 nDist = nOrigHeight
1939 * rFormat.GetDistance(DIS_OPERATORSPACE) / 100L;
1941 Point aPos = pOper->AlignTo(*pBody, RP_LEFT, RHA_CENTER, /*RVA_CENTERY*/RVA_MID);
1942 aPos.X() -= nDist;
1943 pOper->MoveTo(aPos);
1945 SmRect::operator = (*pBody);
1946 ExtendBy(*pOper, RCP_THIS);
1950 /**************************************************************************/
1953 void SmAlignNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1954 // set alignment within the entire subtree (including current node)
1956 OSL_ENSURE(GetNumSubNodes() > 0, "Sm: missing subnode");
1958 SmNode *pNode = GetSubNode(0);
1960 RectHorAlign eHorAlign = RHA_CENTER;
1961 switch (GetToken().eType)
1963 case TALIGNL: eHorAlign = RHA_LEFT; break;
1964 case TALIGNC: eHorAlign = RHA_CENTER; break;
1965 case TALIGNR: eHorAlign = RHA_RIGHT; break;
1966 default:
1967 break;
1969 SetRectHorAlign(eHorAlign);
1971 pNode->Arrange(rDev, rFormat);
1973 SmRect::operator = (pNode->GetRect());
1977 /**************************************************************************/
1980 void SmAttributNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
1982 SmNode *pAttr = GetSubNode(0),
1983 *pBody = GetSubNode(1);
1984 OSL_ENSURE(pBody, "Sm: body missing");
1985 OSL_ENSURE(pAttr, "Sm: attribute missing");
1987 pBody->Arrange(rDev, rFormat);
1989 if (GetScaleMode() == SCALE_WIDTH)
1990 pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
1991 pAttr->Arrange(rDev, rFormat);
1993 // get relative position of attribute
1994 RectVerAlign eVerAlign;
1995 long nDist = 0;
1996 switch (GetToken().eType)
1997 { case TUNDERLINE :
1998 eVerAlign = RVA_ATTRIBUT_LO;
1999 break;
2000 case TOVERSTRIKE :
2001 eVerAlign = RVA_ATTRIBUT_MID;
2002 break;
2003 default :
2004 eVerAlign = RVA_ATTRIBUT_HI;
2005 if (pBody->GetType() == NATTRIBUT)
2006 nDist = GetFont().GetSize().Height()
2007 * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100L;
2009 Point aPos = pAttr->AlignTo(*pBody, RP_ATTRIBUT, RHA_CENTER, eVerAlign);
2010 aPos.Y() -= nDist;
2011 pAttr->MoveTo(aPos);
2013 SmRect::operator = (*pBody);
2014 ExtendBy(*pAttr, RCP_THIS, true);
2017 void SmFontNode::CreateTextFromNode(OUString &rText)
2019 switch (GetToken().eType)
2021 case TBOLD:
2022 rText += "bold ";
2023 break;
2024 case TNBOLD:
2025 rText += "nbold ";
2026 break;
2027 case TITALIC:
2028 rText += "italic ";
2029 break;
2030 case TNITALIC:
2031 rText += "nitalic ";
2032 break;
2033 case TPHANTOM:
2034 rText += "phantom ";
2035 break;
2036 case TSIZE:
2038 rText += "size ";
2039 switch (nSizeType)
2041 case FontSizeType::PLUS:
2042 rText += "+";
2043 break;
2044 case FontSizeType::MINUS:
2045 rText += "-";
2046 break;
2047 case FontSizeType::MULTIPLY:
2048 rText += "*";
2049 break;
2050 case FontSizeType::DIVIDE:
2051 rText += "/";
2052 break;
2053 case FontSizeType::ABSOLUT:
2054 default:
2055 break;
2057 rText += ::rtl::math::doubleToUString(
2058 static_cast<double>(aFontSize),
2059 rtl_math_StringFormat_Automatic,
2060 rtl_math_DecimalPlaces_Max, '.', true);
2061 rText += " ";
2063 break;
2064 case TBLACK:
2065 rText += "color black ";
2066 break;
2067 case TWHITE:
2068 rText += "color white ";
2069 break;
2070 case TRED:
2071 rText += "color red ";
2072 break;
2073 case TGREEN:
2074 rText += "color green ";
2075 break;
2076 case TBLUE:
2077 rText += "color blue ";
2078 break;
2079 case TCYAN:
2080 rText += "color cyan ";
2081 break;
2082 case TMAGENTA:
2083 rText += "color magenta ";
2084 break;
2085 case TYELLOW:
2086 rText += "color yellow ";
2087 break;
2088 case TTEAL:
2089 rText += "color teal";
2090 break;
2091 case TSILVER:
2092 rText += "color silver";
2093 break;
2094 case TGRAY:
2095 rText += "color gray";
2096 break;
2097 case TMAROON:
2098 rText += "color maroon";
2099 break;
2100 case TPURPLE:
2101 rText += "color purple";
2102 break;
2103 case TLIME:
2104 rText += "color lime";
2105 break;
2106 case TOLIVE:
2107 rText += "color olive";
2108 break;
2109 case TNAVY:
2110 rText += "color navy";
2111 break;
2112 case TAQUA:
2113 rText += "color aqua";
2114 break;
2115 case TFUCHSIA:
2116 rText += "color fuchsia";
2117 break;
2118 case TSANS:
2119 rText += "font sans ";
2120 break;
2121 case TSERIF:
2122 rText += "font serif ";
2123 break;
2124 case TFIXED:
2125 rText += "font fixed ";
2126 break;
2127 default:
2128 break;
2130 GetSubNode(1)->CreateTextFromNode(rText);
2133 void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2135 //! prepare subnodes first
2136 SmNode::Prepare(rFormat, rDocShell);
2138 int nFnt = -1;
2139 switch (GetToken().eType)
2141 case TFIXED: nFnt = FNT_FIXED; break;
2142 case TSANS: nFnt = FNT_SANS; break;
2143 case TSERIF: nFnt = FNT_SERIF; break;
2144 default:
2145 break;
2147 if (nFnt != -1)
2148 { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
2149 SetFont(GetFont());
2152 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
2153 //! other font nodes (those with lower depth in the tree)
2154 Flags() |= FLG_FONT;
2157 void SmFontNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
2159 SmNode *pNode = GetSubNode(1);
2160 OSL_ENSURE(pNode, "Sm: missing subnode");
2162 switch (GetToken().eType)
2163 { case TSIZE :
2164 pNode->SetFontSize(aFontSize, nSizeType);
2165 break;
2166 case TSANS :
2167 case TSERIF :
2168 case TFIXED :
2169 pNode->SetFont(GetFont());
2170 break;
2171 case TUNKNOWN : break; // no assertion on "font <?> <?>"
2173 case TPHANTOM : SetPhantom(true); break;
2174 case TBOLD : SetAttribut(ATTR_BOLD); break;
2175 case TITALIC : SetAttribut(ATTR_ITALIC); break;
2176 case TNBOLD : ClearAttribut(ATTR_BOLD); break;
2177 case TNITALIC : ClearAttribut(ATTR_ITALIC); break;
2179 case TBLACK : SetColor(Color(COL_BLACK)); break;
2180 case TWHITE : SetColor(Color(COL_WHITE)); break;
2181 case TRED : SetColor(Color(COL_LIGHTRED)); break;
2182 case TGREEN : SetColor(Color(COL_GREEN)); break;
2183 case TBLUE : SetColor(Color(COL_LIGHTBLUE)); break;
2184 case TCYAN : SetColor(Color(COL_LIGHTCYAN)); break; // as in Calc
2185 case TMAGENTA : SetColor(Color(COL_LIGHTMAGENTA)); break; // as in Calc
2186 case TYELLOW : SetColor(Color(COL_YELLOW)); break;
2187 case TTEAL : SetColor(Color(COL_CYAN)); break;
2188 case TSILVER : SetColor(Color(COL_LIGHTGRAY)); break;
2189 case TGRAY : SetColor(Color(COL_GRAY)); break;
2190 case TMAROON : SetColor(Color(COL_RED)); break;
2191 case TPURPLE : SetColor(Color(COL_MAGENTA)); break;
2192 case TLIME : SetColor(Color(COL_LIGHTGREEN)); break;
2193 case TOLIVE : SetColor(Color(COL_BROWN)); break;
2194 case TNAVY : SetColor(Color(COL_BLUE)); break;
2195 case TAQUA : SetColor(Color(COL_LIGHTCYAN)); break;
2196 case TFUCHSIA : SetColor(Color(COL_LIGHTMAGENTA)); break;
2198 default:
2199 SAL_WARN("starmath", "unknown case");
2202 pNode->Arrange(rDev, rFormat);
2204 SmRect::operator = (pNode->GetRect());
2208 void SmFontNode::SetSizeParameter(const Fraction& rValue, FontSizeType Type)
2210 nSizeType = Type;
2211 aFontSize = rValue;
2215 /**************************************************************************/
2218 SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
2219 : SmGraphicNode(NPOLYLINE, rNodeToken)
2221 aPoly.SetSize(2);
2222 nWidth = 0;
2226 void SmPolyLineNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong nNewWidth)
2228 aToSize.Width() = nNewWidth;
2232 void SmPolyLineNode::AdaptToY(const OutputDevice &/*rDev*/, sal_uLong nNewHeight)
2234 GetFont().FreezeBorderWidth();
2235 aToSize.Height() = nNewHeight;
2239 void SmPolyLineNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
2241 //! some routines being called extract some info from the OutputDevice's
2242 //! font (eg the space to be used for borders OR the font name(!!)).
2243 //! Thus the font should reflect the needs and has to be set!
2244 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
2245 aTmpDev.SetFont(GetFont());
2247 long nBorderwidth = GetFont().GetBorderWidth();
2249 // create polygon using both endpoints
2250 OSL_ENSURE(aPoly.GetSize() == 2, "Sm : wrong number of points");
2251 Point aPointA, aPointB;
2252 if (GetToken().eType == TWIDESLASH)
2254 aPointA.X() = nBorderwidth;
2255 aPointA.Y() = aToSize.Height() - nBorderwidth;
2256 aPointB.X() = aToSize.Width() - nBorderwidth;
2257 aPointB.Y() = nBorderwidth;
2259 else
2261 OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
2262 aPointA.X() =
2263 aPointA.Y() = nBorderwidth;
2264 aPointB.X() = aToSize.Width() - nBorderwidth;
2265 aPointB.Y() = aToSize.Height() - nBorderwidth;
2267 aPoly.SetPoint(aPointA, 0);
2268 aPoly.SetPoint(aPointB, 1);
2270 long nThick = GetFont().GetSize().Height()
2271 * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L;
2272 nWidth = nThick + 2 * nBorderwidth;
2274 SmRect::operator = (SmRect(aToSize.Width(), aToSize.Height()));
2278 /**************************************************************************/
2280 void SmRootSymbolNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong nWidth)
2282 nBodyWidth = nWidth;
2286 void SmRootSymbolNode::AdaptToY(const OutputDevice &rDev, sal_uLong nHeight)
2288 // some additional length so that the horizontal
2289 // bar will be positioned above the argument
2290 SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10L);
2294 /**************************************************************************/
2297 void SmDynIntegralSymbolNode::AdaptToY(const OutputDevice &rDev, sal_uLong nHeight)
2299 static const long nFactor = 12L;
2301 // The new height equals (1 + nFactor) * oldHeight
2302 // nFactor was chosen for keeping the integral sign from becoming too "fat".
2303 SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / nFactor);
2305 // keep the ratio
2306 long nCurWidth = GetSize().Width();
2307 SmMathSymbolNode::AdaptToX(rDev, nCurWidth + nCurWidth / nFactor);
2311 /**************************************************************************/
2314 void SmRectangleNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong nWidth)
2316 aToSize.Width() = nWidth;
2320 void SmRectangleNode::AdaptToY(const OutputDevice &/*rDev*/, sal_uLong nHeight)
2322 GetFont().FreezeBorderWidth();
2323 aToSize.Height() = nHeight;
2327 void SmRectangleNode::Arrange(const OutputDevice &rDev, const SmFormat &/*rFormat*/)
2329 long nFontHeight = GetFont().GetSize().Height();
2330 long nWidth = aToSize.Width(),
2331 nHeight = aToSize.Height();
2332 if (nHeight == 0)
2333 nHeight = nFontHeight / 30;
2334 if (nWidth == 0)
2335 nWidth = nFontHeight / 3;
2337 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
2338 aTmpDev.SetFont(GetFont());
2340 // add some borderspace
2341 sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth();
2342 nHeight += 2 * nTmpBorderWidth;
2344 //! use this method in order to have 'SmRect::HasAlignInfo() == true'
2345 //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
2346 SmRect::operator = (SmRect(nWidth, nHeight));
2350 /**************************************************************************/
2353 SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
2354 : SmVisibleNode(eNodeType, rNodeToken)
2355 , nFontDesc(nFontDescP)
2356 , nSelectionStart(0)
2357 , nSelectionEnd(0)
2361 SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
2362 : SmVisibleNode(NTEXT, rNodeToken)
2363 , nFontDesc(nFontDescP)
2364 , nSelectionStart(0)
2365 , nSelectionEnd(0)
2369 void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2371 SmNode::Prepare(rFormat, rDocShell);
2373 // default setting for horizontal alignment of nodes with TTEXT
2374 // content is as alignl (cannot be done in Arrange since it would
2375 // override the settings made by an SmAlignNode before)
2376 if (TTEXT == GetToken().eType)
2377 SetRectHorAlign( RHA_LEFT );
2379 aText = GetToken().aText;
2380 GetFont() = rFormat.GetFont(GetFontDesc());
2382 if (IsItalic( GetFont() ))
2383 Attributes() |= ATTR_ITALIC;
2384 if (IsBold( GetFont() ))
2385 Attributes() |= ATTR_BOLD;
2387 // special handling for ':' where it is a token on it's own and is likely
2388 // to be used for mathematical notations. (E.g. a:b = 2:3)
2389 // In that case it should not be displayed in italic.
2390 if (GetToken().aText.getLength() == 1 && GetToken().aText[0] == ':')
2391 Attributes() &= ~ATTR_ITALIC;
2395 void SmTextNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
2397 PrepareAttributes();
2399 sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
2400 SIZ_FUNCTION : SIZ_TEXT;
2401 GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
2403 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
2404 aTmpDev.SetFont(GetFont());
2406 SmRect::operator = (SmRect(aTmpDev, &rFormat, aText, GetFont().GetBorderWidth()));
2409 void SmTextNode::CreateTextFromNode(OUString &rText)
2411 bool bQuoted=false;
2412 if (GetToken().eType == TTEXT)
2414 rText += "\"";
2415 bQuoted=true;
2417 else
2419 SmParser aParseTest;
2420 SmNode *pTable = aParseTest.Parse(GetToken().aText);
2421 bQuoted=true;
2422 if ( (pTable->GetType() == NTABLE) && (pTable->GetNumSubNodes() == 1) )
2424 SmNode *pResult = pTable->GetSubNode(0);
2425 if ( (pResult->GetType() == NLINE) &&
2426 (pResult->GetNumSubNodes() == 1) )
2428 pResult = pResult->GetSubNode(0);
2429 if ( (pResult->GetType() == NEXPRESSION) &&
2430 (pResult->GetNumSubNodes() == 1) )
2432 pResult = pResult->GetSubNode(0);
2433 if (pResult->GetType() == NTEXT)
2434 bQuoted=false;
2438 delete pTable;
2440 if ((GetToken().eType == TIDENT) && (GetFontDesc() == FNT_FUNCTION))
2442 //Search for existing functions and remove extraenous keyword
2443 rText += "func ";
2445 else if (bQuoted)
2446 rText += "italic ";
2448 if (bQuoted)
2449 rText += "\"";
2453 rText += GetToken().aText;
2455 if (bQuoted)
2456 rText += "\"";
2457 rText += " ";
2461 void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
2463 rText.append(aText);
2466 void SmTextNode::AdjustFontDesc()
2468 if (GetToken().eType == TTEXT)
2469 nFontDesc = FNT_TEXT;
2470 else if(GetToken().eType == TFUNC)
2471 nFontDesc = FNT_FUNCTION;
2472 else {
2473 SmTokenType nTok;
2474 const SmTokenTableEntry *pEntry = SmParser::GetTokenTableEntry( aText );
2475 if (pEntry && pEntry->nGroup == TGFUNCTION) {
2476 nTok = pEntry->eType;
2477 nFontDesc = FNT_FUNCTION;
2478 } else {
2479 sal_Unicode firstChar = aText[0];
2480 if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',') {
2481 nFontDesc = FNT_NUMBER;
2482 nTok = TNUMBER;
2483 } else if (aText.getLength() > 1) {
2484 nFontDesc = FNT_VARIABLE;
2485 nTok = TIDENT;
2486 } else {
2487 nFontDesc = FNT_VARIABLE;
2488 nTok = TCHARACTER;
2491 SmToken tok = GetToken();
2492 tok.eType = nTok;
2493 SetToken(tok);
2497 sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
2499 //Find the best match in accepted unicode for our private area symbols
2500 static const sal_Unicode aStarMathPrivateToUnicode[] =
2502 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
2503 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
2504 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
2505 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
2506 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
2507 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
2508 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
2509 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
2510 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
2511 0xE0DA, 0x2190, 0x2191, 0x2193
2513 if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
2514 nIn = aStarMathPrivateToUnicode[nIn-0xE080];
2516 //For whatever unicode glyph that equation editor doesn't ship with that
2517 //we have a possible match we can munge it to.
2518 switch (nIn)
2520 case 0x2223:
2521 nIn = '|';
2522 break;
2523 default:
2524 break;
2527 return nIn;
2530 /**************************************************************************/
2532 void SmMatrixNode::CreateTextFromNode(OUString &rText)
2534 rText += "matrix {";
2535 for (sal_uInt16 i = 0; i < nNumRows; i++)
2537 for (sal_uInt16 j = 0; j < nNumCols; j++)
2539 SmNode *pNode = GetSubNode(i * nNumCols + j);
2540 if (pNode)
2541 pNode->CreateTextFromNode(rText);
2542 if (j != nNumCols-1)
2543 rText += "# ";
2545 if (i != nNumRows-1)
2546 rText += "## ";
2548 rText = comphelper::string::stripEnd(rText, ' ');
2549 rText += "} ";
2553 void SmMatrixNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
2555 SmNode *pNode;
2556 sal_uInt16 i, j;
2558 // initialize array that is to hold the maximum widths of all
2559 // elements (subnodes) in that column.
2560 std::vector<long> aColWidth(nNumCols);
2562 // arrange subnodes and calculate the aboves arrays contents
2563 sal_uInt16 nNodes = GetNumSubNodes();
2564 for (i = 0; i < nNodes; i++)
2566 sal_uInt16 nIdx = nNodes - 1 - i;
2567 if (NULL != (pNode = GetSubNode(nIdx)))
2569 pNode->Arrange(rDev, rFormat);
2570 int nCol = nIdx % nNumCols;
2571 aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
2575 // norm distance from which the following two are calcutated
2576 const long nNormDist = 3 * GetFont().GetSize().Height();
2578 // define horizontal and vertical minimal distances that separate
2579 // the elements
2580 long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100L,
2581 nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100L;
2583 // build array that holds the leftmost position for each column
2584 std::vector<long> aColLeft(nNumCols);
2585 long nX = 0;
2586 for (j = 0; j < nNumCols; j++)
2588 aColLeft[j] = nX;
2589 nX += aColWidth[j] + nHorDist;
2592 Point aPos, aDelta;
2593 SmRect aLineRect;
2594 SmRect::operator = (SmRect());
2595 for (i = 0; i < nNumRows; i++)
2596 { aLineRect = SmRect();
2597 for (j = 0; j < nNumCols; j++)
2598 { SmNode *pTmpNode = GetSubNode(i * nNumCols + j);
2599 OSL_ENSURE(pTmpNode, "Sm: NULL pointer");
2601 const SmRect &rNodeRect = pTmpNode->GetRect();
2603 // align all baselines in that row if possible
2604 aPos = rNodeRect.AlignTo(aLineRect, RP_RIGHT, RHA_CENTER, RVA_BASELINE);
2605 aPos.X() += nHorDist;
2607 // get horizontal alignment
2608 const SmNode *pCoNode = pTmpNode->GetLeftMost();
2609 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
2611 // calculate horizontal position of element depending on column
2612 // and horizontal alignment
2613 switch (eHorAlign)
2614 { case RHA_LEFT:
2615 aPos.X() = rNodeRect.GetLeft() + aColLeft[j];
2616 break;
2617 case RHA_CENTER:
2618 aPos.X() = rNodeRect.GetLeft() + aColLeft[j]
2619 + aColWidth[j] / 2
2620 - rNodeRect.GetItalicCenterX();
2621 break;
2622 case RHA_RIGHT:
2623 aPos.X() = rNodeRect.GetLeft() + aColLeft[j]
2624 + aColWidth[j] - rNodeRect.GetItalicWidth();
2625 break;
2628 pTmpNode->MoveTo(aPos);
2629 aLineRect.ExtendBy(rNodeRect, RCP_XOR);
2632 aPos = aLineRect.AlignTo(*this, RP_BOTTOM, RHA_CENTER, RVA_BASELINE);
2633 aPos.Y() += nVerDist;
2635 // move 'aLineRect' and rectangles in that line to final position
2636 aDelta.X() = 0; // since horizontal alignment is already done
2637 aDelta.Y() = aPos.Y() - aLineRect.GetTop();
2638 aLineRect.Move(aDelta);
2639 for (j = 0; j < nNumCols; j++)
2640 if (NULL != (pNode = GetSubNode(i * nNumCols + j)))
2641 pNode->Move(aDelta);
2643 ExtendBy(aLineRect, RCP_NONE);
2648 void SmMatrixNode::SetRowCol(sal_uInt16 nMatrixRows, sal_uInt16 nMatrixCols)
2650 nNumRows = nMatrixRows;
2651 nNumCols = nMatrixCols;
2655 SmNode * SmMatrixNode::GetLeftMost()
2657 return this;
2661 /**************************************************************************/
2664 SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
2665 : SmSpecialNode(NMATH, rNodeToken, FNT_MATH)
2667 sal_Unicode cChar = GetToken().cMathChar;
2668 if ((sal_Unicode) '\0' != cChar)
2669 SetText(OUString(cChar));
2672 void SmMathSymbolNode::AdaptToX(const OutputDevice &rDev, sal_uLong nWidth)
2674 // Since there is no function to do this, we try to approximate it:
2675 Size aFntSize (GetFont().GetSize());
2677 //! however the result is a bit better with 'nWidth' as initial font width
2678 aFntSize.Width() = nWidth;
2679 GetFont().SetSize(aFntSize);
2681 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
2682 aTmpDev.SetFont(GetFont());
2684 // get denominator of error factor for width
2685 long nTmpBorderWidth = GetFont().GetBorderWidth();
2686 long nDenom = SmRect(aTmpDev, NULL, GetText(), nTmpBorderWidth).GetItalicWidth();
2688 // scale fontwidth with this error factor
2689 aFntSize.Width() *= nWidth;
2690 aFntSize.Width() /= nDenom ? nDenom : 1;
2692 GetFont().SetSize(aFntSize);
2695 void SmMathSymbolNode::AdaptToY(const OutputDevice &rDev, sal_uLong nHeight)
2697 GetFont().FreezeBorderWidth();
2698 Size aFntSize (GetFont().GetSize());
2700 // Since we only want to scale the height, we might have
2701 // to determine the font width in order to keep it
2702 if (aFntSize.Width() == 0)
2704 OutputDevice &rDevNC = (OutputDevice &) rDev;
2705 rDevNC.Push(PushFlags::FONT | PushFlags::MAPMODE);
2706 rDevNC.SetFont(GetFont());
2707 aFntSize.Width() = rDev.GetFontMetric().GetSize().Width();
2708 rDevNC.Pop();
2710 OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
2712 //! however the result is a bit better with 'nHeight' as initial
2713 //! font height
2714 aFntSize.Height() = nHeight;
2715 GetFont().SetSize(aFntSize);
2717 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
2718 aTmpDev.SetFont(GetFont());
2720 // get denominator of error factor for height
2721 long nTmpBorderWidth = GetFont().GetBorderWidth();
2722 long nDenom = SmRect(aTmpDev, NULL, GetText(), nTmpBorderWidth).GetHeight();
2724 // scale fontwidth with this error factor
2725 aFntSize.Height() *= nHeight;
2726 aFntSize.Height() /= nDenom ? nDenom : 1;
2728 GetFont().SetSize(aFntSize);
2732 void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2734 SmNode::Prepare(rFormat, rDocShell);
2736 GetFont() = rFormat.GetFont(GetFontDesc());
2737 // use same font size as is used for variables
2738 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetSize() );
2740 OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
2741 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
2742 "wrong charset for character from StarMath/OpenSymbol font");
2744 Flags() |= FLG_FONT | FLG_ITALIC;
2748 void SmMathSymbolNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
2750 const OUString &rText = GetText();
2752 if (rText.isEmpty() || rText[0] == '\0')
2753 { SmRect::operator = (SmRect());
2754 return;
2757 PrepareAttributes();
2759 GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
2761 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
2762 aTmpDev.SetFont(GetFont());
2764 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
2767 void SmMathSymbolNode::CreateTextFromNode(OUString &rText)
2769 OUString sStr;
2770 MathType::LookupChar(GetToken().cMathChar, sStr);
2771 rText += sStr;
2774 void SmRectangleNode::CreateTextFromNode(OUString &rText)
2776 switch (GetToken().eType)
2778 case TUNDERLINE:
2779 rText += "underline ";
2780 break;
2781 case TOVERLINE:
2782 rText += "overline ";
2783 break;
2784 case TOVERSTRIKE:
2785 rText += "overstrike ";
2786 break;
2787 default:
2788 break;
2792 void SmAttributNode::CreateTextFromNode(OUString &rText)
2794 SmNode *pNode;
2795 sal_uInt16 nSize = GetNumSubNodes();
2796 OSL_ENSURE(nSize == 2, "Node missing members");
2797 rText += "{";
2798 sal_Unicode nLast=0;
2799 if (NULL != (pNode = GetSubNode(0)))
2801 OUString aStr;
2802 pNode->CreateTextFromNode(aStr);
2803 if (aStr.getLength() > 1)
2804 rText += aStr;
2805 else
2807 nLast = aStr[0];
2808 switch (nLast)
2810 case MS_BAR: // MACRON
2811 rText += "overline ";
2812 break;
2813 case MS_DOT: // DOT ABOVE
2814 rText += "dot ";
2815 break;
2816 case 0x2dc: // SMALL TILDE
2817 rText += "widetilde ";
2818 break;
2819 case MS_DDOT: // DIAERESIS
2820 rText += "ddot ";
2821 break;
2822 case 0xE082:
2823 break;
2824 case 0xE09B:
2825 case MS_DDDOT: // COMBINING THREE DOTS ABOVE
2826 rText += "dddot ";
2827 break;
2828 case MS_ACUTE: // ACUTE ACCENT
2829 case MS_COMBACUTE: // COMBINING ACUTE ACCENT
2830 rText += "acute ";
2831 break;
2832 case MS_GRAVE: // GRAVE ACCENT
2833 case MS_COMBGRAVE: // COMBINING GRAVE ACCENT
2834 rText += "grave ";
2835 break;
2836 case MS_CHECK: // CARON
2837 case MS_COMBCHECK: // COMBINING CARON
2838 rText += "check ";
2839 break;
2840 case MS_BREVE: // BREVE
2841 case MS_COMBBREVE: // COMBINING BREVE
2842 rText += "breve ";
2843 break;
2844 case MS_CIRCLE: // RING ABOVE
2845 case MS_COMBCIRCLE: // COMBINING RING ABOVE
2846 rText += "circle ";
2847 break;
2848 case MS_RIGHTARROW: // RIGHTWARDS ARROW
2849 case MS_VEC: // COMBINING RIGHT ARROW ABOVE
2850 rText += "vec ";
2851 break;
2852 case MS_TILDE: // TILDE
2853 case MS_COMBTILDE: // COMBINING TILDE
2854 rText += "tilde ";
2855 break;
2856 case MS_HAT: // CIRCUMFLEX ACCENT
2857 case MS_COMBHAT: // COMBINING CIRCUMFLEX ACCENT
2858 rText += "hat ";
2859 break;
2860 case MS_COMBBAR: // COMBINING MACRON
2861 rText += "bar ";
2862 break;
2863 default:
2864 rText += OUString( nLast );
2865 break;
2870 if (nSize == 2)
2871 if (NULL != (pNode = GetSubNode(1)))
2872 pNode->CreateTextFromNode(rText);
2874 rText = comphelper::string::stripEnd(rText, ' ');
2876 if (nLast == 0xE082)
2877 rText += " overbrace {}";
2879 rText += "} ";
2882 /**************************************************************************/
2884 static bool lcl_IsFromGreekSymbolSet( const OUString &rTokenText )
2886 bool bRes = false;
2888 // valid symbol name needs to have a '%' at pos 0 and at least an additional char
2889 if (rTokenText.getLength() > 2 && rTokenText[0] == (sal_Unicode)'%')
2891 OUString aName( rTokenText.copy(1) );
2892 SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName );
2893 if (pSymbol && GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
2894 bRes = true;
2897 return bRes;
2901 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc) :
2902 SmTextNode(eNodeType, rNodeToken, _nFontDesc)
2904 bIsFromGreekSymbolSet = lcl_IsFromGreekSymbolSet( rNodeToken.aText );
2908 SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken) :
2909 SmTextNode(NSPECIAL, rNodeToken, FNT_MATH) // default Font isn't always correct!
2911 bIsFromGreekSymbolSet = lcl_IsFromGreekSymbolSet( rNodeToken.aText );
2915 void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
2917 SmNode::Prepare(rFormat, rDocShell);
2919 const SmSym *pSym;
2920 SmModule *pp = SM_MOD();
2922 OUString aName(GetToken().aText.copy(1));
2923 if (NULL != (pSym = pp->GetSymbolManager().GetSymbolByName( aName )))
2925 sal_UCS4 cChar = pSym->GetCharacter();
2926 OUString aTmp( &cChar, 1 );
2927 SetText( aTmp );
2928 GetFont() = pSym->GetFace();
2930 else
2932 SetText( GetToken().aText );
2933 GetFont() = rFormat.GetFont(FNT_VARIABLE);
2935 // use same font size as is used for variables
2936 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetSize() );
2938 // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
2939 // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
2940 // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
2942 //! see also SmFontStyles::GetStyleName
2943 if (IsItalic( GetFont() ))
2944 SetAttribut(ATTR_ITALIC);
2945 if (IsBold( GetFont() ))
2946 SetAttribut(ATTR_BOLD);
2948 Flags() |= FLG_FONT;
2950 if (bIsFromGreekSymbolSet)
2952 OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
2953 bool bItalic = false;
2954 sal_Int16 nStyle = rFormat.GetGreekCharStyle();
2955 OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
2956 if (nStyle == 1)
2957 bItalic = true;
2958 else if (nStyle == 2)
2960 const OUString& rTmp(GetText());
2961 if (rTmp.isEmpty())
2963 static const sal_Unicode cUppercaseAlpha = 0x0391;
2964 static const sal_Unicode cUppercaseOmega = 0x03A9;
2965 sal_Unicode cChar = rTmp[0];
2966 // uppercase letters should be straight and lowercase letters italic
2967 bItalic = !(cUppercaseAlpha <= cChar && cChar <= cUppercaseOmega);
2971 if (bItalic)
2972 Attributes() |= ATTR_ITALIC;
2973 else
2974 Attributes() &= ~ATTR_ITALIC;
2979 void SmSpecialNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
2981 PrepareAttributes();
2983 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
2984 aTmpDev.SetFont(GetFont());
2986 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
2989 /**************************************************************************/
2992 void SmGlyphSpecialNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
2994 PrepareAttributes();
2996 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
2997 aTmpDev.SetFont(GetFont());
2999 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
3000 GetFont().GetBorderWidth()).AsGlyphRect());
3004 /**************************************************************************/
3007 void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
3009 SmNode::Prepare(rFormat, rDocShell);
3011 GetFont().SetColor(COL_GRAY);
3012 Flags() |= FLG_COLOR | FLG_FONT | FLG_ITALIC;
3016 void SmPlaceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
3018 PrepareAttributes();
3020 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
3021 aTmpDev.SetFont(GetFont());
3023 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
3027 /**************************************************************************/
3030 void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
3032 SmNode::Prepare(rFormat, rDocShell);
3034 GetFont().SetColor(COL_RED);
3035 Flags() |= FLG_VISIBLE | FLG_BOLD | FLG_ITALIC
3036 | FLG_COLOR | FLG_FONT | FLG_SIZE;
3040 void SmErrorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
3042 PrepareAttributes();
3044 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
3045 aTmpDev.SetFont(GetFont());
3047 const OUString &rText = GetText();
3048 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
3052 /**************************************************************************/
3055 void SmBlankNode::IncreaseBy(const SmToken &rToken)
3057 switch(rToken.eType)
3059 case TBLANK: nNum += 4; break;
3060 case TSBLANK: nNum += 1; break;
3061 default:
3062 break;
3067 void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell)
3069 SmNode::Prepare(rFormat, rDocShell);
3071 // Here it need/should not be the StarMath font, so that for the character
3072 // used in Arrange a normal (non-clipped) rectangle is generated
3073 GetFont() = rFormat.GetFont(FNT_VARIABLE);
3075 Flags() |= FLG_FONT | FLG_BOLD | FLG_ITALIC;
3079 void SmBlankNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat)
3081 SmTmpDevice aTmpDev ((OutputDevice &) rDev, true);
3082 aTmpDev.SetFont(GetFont());
3084 // make distance depend on the font height
3085 // (so that it increases when scaling (e.g. size *2 {a ~ b})
3086 long nDist = GetFont().GetSize().Height() / 10L,
3087 nSpace = nNum * nDist;
3089 // get a SmRect with Baseline and all the bells and whistles
3090 SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
3091 GetFont().GetBorderWidth()));
3093 // and resize it to the requested size
3094 SetItalicSpaces(0, 0);
3095 SetWidth(nSpace);
3098 /**************************************************************************/
3099 //Implementation of all accept methods for SmVisitor
3101 void SmNode::Accept(SmVisitor*){
3102 //This method is only implemented to avoid making SmNode abstract because an
3103 //obscure copy constructor is used... I can't find it's implementation, and
3104 //don't want to figure out how to fix it... If you want to, just delete this
3105 //method, making SmNode abstract, and see where you can an problem with that.
3106 SAL_WARN("starmath", "SmNode should not be visitable!");
3109 void SmTableNode::Accept(SmVisitor* pVisitor) {
3110 pVisitor->Visit(this);
3113 void SmBraceNode::Accept(SmVisitor* pVisitor) {
3114 pVisitor->Visit(this);
3117 void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
3118 pVisitor->Visit(this);
3121 void SmOperNode::Accept(SmVisitor* pVisitor) {
3122 pVisitor->Visit(this);
3125 void SmAlignNode::Accept(SmVisitor* pVisitor) {
3126 pVisitor->Visit(this);
3129 void SmAttributNode::Accept(SmVisitor* pVisitor) {
3130 pVisitor->Visit(this);
3133 void SmFontNode::Accept(SmVisitor* pVisitor) {
3134 pVisitor->Visit(this);
3137 void SmUnHorNode::Accept(SmVisitor* pVisitor) {
3138 pVisitor->Visit(this);
3141 void SmBinHorNode::Accept(SmVisitor* pVisitor) {
3142 pVisitor->Visit(this);
3145 void SmBinVerNode::Accept(SmVisitor* pVisitor) {
3146 pVisitor->Visit(this);
3149 void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
3150 pVisitor->Visit(this);
3153 void SmSubSupNode::Accept(SmVisitor* pVisitor) {
3154 pVisitor->Visit(this);
3157 void SmMatrixNode::Accept(SmVisitor* pVisitor) {
3158 pVisitor->Visit(this);
3161 void SmPlaceNode::Accept(SmVisitor* pVisitor) {
3162 pVisitor->Visit(this);
3165 void SmTextNode::Accept(SmVisitor* pVisitor) {
3166 pVisitor->Visit(this);
3169 void SmSpecialNode::Accept(SmVisitor* pVisitor) {
3170 pVisitor->Visit(this);
3173 void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
3174 pVisitor->Visit(this);
3177 void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
3178 pVisitor->Visit(this);
3181 void SmBlankNode::Accept(SmVisitor* pVisitor) {
3182 pVisitor->Visit(this);
3185 void SmErrorNode::Accept(SmVisitor* pVisitor) {
3186 pVisitor->Visit(this);
3189 void SmLineNode::Accept(SmVisitor* pVisitor) {
3190 pVisitor->Visit(this);
3193 void SmExpressionNode::Accept(SmVisitor* pVisitor) {
3194 pVisitor->Visit(this);
3197 void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
3198 pVisitor->Visit(this);
3201 void SmRootNode::Accept(SmVisitor* pVisitor) {
3202 pVisitor->Visit(this);
3205 void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
3206 pVisitor->Visit(this);
3209 void SmDynIntegralNode::Accept(SmVisitor* pVisitor) {
3210 pVisitor->Visit(this);
3214 void SmDynIntegralSymbolNode::Accept(SmVisitor* pVisitor) {
3215 pVisitor->Visit(this);
3218 void SmRectangleNode::Accept(SmVisitor* pVisitor) {
3219 pVisitor->Visit(this);
3222 void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
3223 pVisitor->Visit(this);
3226 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */