summaryrefslogtreecommitdiffstats
path: root/starmath/source/node.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'starmath/source/node.cxx')
-rw-r--r--starmath/source/node.cxx2491
1 files changed, 2491 insertions, 0 deletions
diff --git a/starmath/source/node.cxx b/starmath/source/node.cxx
new file mode 100644
index 0000000000..778baaa74d
--- /dev/null
+++ b/starmath/source/node.cxx
@@ -0,0 +1,2491 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <symbol.hxx>
+#include <smmod.hxx>
+#include "tmpdevice.hxx"
+#include <utility>
+#include <visitors.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/metric.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/numeric/ftools.hxx>
+#include <unicode/uchar.h>
+#include <unicode/uscript.h>
+
+namespace {
+
+template<typename F>
+void ForEachNonNull(SmNode *pNode, F && f)
+{
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ SmNode *pSubNode = pNode->GetSubNode(i);
+ if (pSubNode != nullptr)
+ f(pSubNode);
+ }
+}
+
+}
+
+SmNode::SmNode(SmNodeType eNodeType, SmToken aNodeToken)
+ : maNodeToken(std::move( aNodeToken ))
+ , meType( eNodeType )
+ , meScaleMode( SmScaleMode::None )
+ , meRectHorAlign( RectHorAlign::Left )
+ , mnFlags( FontChangeMask::None )
+ , mnAttributes( FontAttribute::None )
+ , mbIsPhantom( false )
+ , mbIsSelected( false )
+ , mnAccIndex( -1 )
+ , mpParentNode( nullptr )
+{
+}
+
+SmNode::~SmNode()
+{
+}
+
+const SmNode * SmNode::GetLeftMost() const
+ // returns leftmost node of current subtree.
+ //! (this assumes the one with index 0 is always the leftmost subnode
+ //! for the current node).
+{
+ const SmNode *pNode = GetNumSubNodes() > 0 ?
+ GetSubNode(0) : nullptr;
+
+ return pNode ? pNode->GetLeftMost() : this;
+}
+
+
+void SmNode::SetPhantom(bool bIsPhantomP)
+{
+ if (! (Flags() & FontChangeMask::Phantom))
+ mbIsPhantom = bIsPhantomP;
+
+ bool b = mbIsPhantom;
+ ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
+}
+
+
+void SmNode::SetColor(const Color& rColor)
+{
+ if (! (Flags() & FontChangeMask::Color))
+ GetFont().SetColor(rColor);
+
+ ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
+}
+
+
+void SmNode::SetAttribute(FontAttribute nAttrib)
+{
+ if (
+ (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
+ (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
+ )
+ {
+ mnAttributes |= nAttrib;
+ }
+
+ ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribute(nAttrib);});
+}
+
+
+void SmNode::ClearAttribute(FontAttribute nAttrib)
+{
+ if (
+ (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
+ (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
+ )
+ {
+ mnAttributes &= ~nAttrib;
+ }
+
+ ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribute(nAttrib);});
+}
+
+
+void SmNode::SetFont(const SmFace &rFace)
+{
+ if (!(Flags() & FontChangeMask::Face))
+ GetFont() = rFace;
+ ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
+}
+
+
+void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
+ //! 'rSize' is in units of pts
+{
+ Size aFntSize;
+
+ if (!(Flags() & FontChangeMask::Size))
+ {
+ Fraction aVal(conversionFract(o3tl::Length::pt, SmO3tlLengthUnit()) * rSize);
+ tools::Long nHeight = static_cast<tools::Long>(aVal);
+
+ aFntSize = GetFont().GetFontSize();
+ aFntSize.setWidth( 0 );
+ switch(nType)
+ {
+ case FontSizeType::ABSOLUT:
+ aFntSize.setHeight( nHeight );
+ break;
+
+ case FontSizeType::PLUS:
+ aFntSize.AdjustHeight(nHeight );
+ break;
+
+ case FontSizeType::MINUS:
+ aFntSize.AdjustHeight( -nHeight );
+ break;
+
+ case FontSizeType::MULTIPLY:
+ aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) * rSize) );
+ break;
+
+ case FontSizeType::DIVIDE:
+ if (rSize != Fraction(0))
+ aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) / rSize) );
+ break;
+ default:
+ break;
+ }
+
+ // check the requested size against maximum value
+ const int nMaxVal = o3tl::convert(128, o3tl::Length::pt, SmO3tlLengthUnit());
+ if (aFntSize.Height() > nMaxVal)
+ aFntSize.setHeight( nMaxVal );
+
+ GetFont().SetSize(aFntSize);
+ }
+
+ ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
+}
+
+
+void SmNode::SetSize(const Fraction &rSize)
+{
+ GetFont() *= rSize;
+
+ ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
+}
+
+
+void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
+{
+ meRectHorAlign = eHorAlign;
+
+ if (bApplyToSubTree)
+ ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
+}
+
+
+void SmNode::PrepareAttributes()
+{
+ GetFont().SetWeight((Attributes() & FontAttribute::Bold) ? WEIGHT_BOLD : WEIGHT_NORMAL);
+ GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
+}
+
+
+void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ if (nDepth > 1024)
+ throw std::range_error("parser depth limit");
+
+ mbIsPhantom = false;
+ mnFlags = FontChangeMask::None;
+ mnAttributes = FontAttribute::None;
+
+ switch (rFormat.GetHorAlign())
+ { case SmHorAlign::Left: meRectHorAlign = RectHorAlign::Left; break;
+ case SmHorAlign::Center: meRectHorAlign = RectHorAlign::Center; break;
+ case SmHorAlign::Right: meRectHorAlign = RectHorAlign::Right; break;
+ }
+
+ GetFont() = rFormat.GetFont(FNT_MATH);
+ OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
+ "unexpected CharSet" );
+ GetFont().SetWeight(WEIGHT_NORMAL);
+ GetFont().SetItalic(ITALIC_NONE);
+
+ ForEachNonNull(this, [&rFormat, &rDocShell, nDepth](SmNode *pNode){pNode->Prepare(rFormat, rDocShell, nDepth + 1);});
+}
+
+void SmNode::Move(const Point& rVector)
+{
+ if (rVector.X() == 0 && rVector.Y() == 0)
+ return;
+
+ SmRect::Move(rVector);
+
+ ForEachNonNull(this, [&rVector](SmNode *pNode){pNode->Move(rVector);});
+}
+
+void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
+{
+}
+
+
+void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
+{
+}
+
+
+const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
+ // returns (first) ** visible ** (sub)node with the tokens text at
+ // position 'nRow', 'nCol'.
+ //! (there should be exactly one such node if any)
+{
+ if ( IsVisible()
+ && nRow == GetSelection().nStartPara
+ && nCol >= GetSelection().nStartPos && nCol <= GetSelection().nEndPos )
+ return this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+
+ if (!pNode)
+ continue;
+
+ const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
+ if (pResult)
+ return pResult;
+ }
+ }
+
+ return nullptr;
+}
+
+
+const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
+{
+ tools::Long nDist = LONG_MAX;
+ const SmNode *pResult = nullptr;
+
+ if (IsVisible())
+ pResult = this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+
+ if (!pNode)
+ continue;
+
+ const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
+ if (pFound)
+ {
+ tools::Long nTmp = pFound->OrientedDist(rPoint);
+ if (nTmp < nDist)
+ {
+ nDist = nTmp;
+ pResult = pFound;
+
+ // quit immediately if 'rPoint' is inside the *should not
+ // overlap with other rectangles* part.
+ // This (partly) serves for getting the attributes in eg
+ // "bar overstrike a".
+ // ('nDist < 0' is used as *quick shot* to avoid evaluation of
+ // the following expression, where the result is already determined)
+ if (nDist < 0 && pFound->IsInsideRect(rPoint))
+ break;
+ }
+ }
+ }
+ }
+
+ return pResult;
+}
+
+const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
+{
+ const SmNode *pResult = nullptr;
+
+ sal_Int32 nIdx = GetAccessibleIndex();
+ OUStringBuffer aTxt;
+ if (nIdx >= 0)
+ GetAccessibleText( aTxt ); // get text if used in following 'if' statement
+
+ if (nIdx >= 0
+ && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.getLength())
+ pResult = this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+ if (!pNode)
+ continue;
+
+ pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
+ if (pResult)
+ return pResult;
+ }
+ }
+
+ return pResult;
+}
+
+
+SmStructureNode::~SmStructureNode()
+{
+ ForEachNonNull(this, std::default_delete<SmNode>());
+}
+
+
+void SmStructureNode::ClearSubNodes()
+{
+ maSubNodes.clear();
+}
+
+void SmStructureNode::SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
+{
+ size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst.release();
+ if (pSecond)
+ maSubNodes[1] = pSecond.release();
+ if (pThird)
+ maSubNodes[2] = pThird.release();
+
+ ClaimPaternity();
+}
+
+void SmStructureNode::SetSubNodes(SmNode* pFirst, SmNode* pSecond, SmNode* pThird)
+{
+ size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst;
+ if (pSecond)
+ maSubNodes[1] = pSecond;
+ if (pThird)
+ maSubNodes[2] = pThird;
+
+ ClaimPaternity();
+}
+
+void SmStructureNode::SetSubNodesBinMo(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
+{
+ if(GetType()==SmNodeType::BinDiagonal)
+ {
+ size_t nSize = pSecond ? 3 : (pThird ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst.release();
+ if (pSecond)
+ maSubNodes[2] = pSecond.release();
+ if (pThird)
+ maSubNodes[1] = pThird.release();
+ }
+ else
+ {
+ size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst.release();
+ if (pSecond)
+ maSubNodes[1] = pSecond.release();
+ if (pThird)
+ maSubNodes[2] = pThird.release();
+ }
+ ClaimPaternity();
+}
+
+void SmStructureNode::SetSubNodes(SmNodeArray&& rNodeArray)
+{
+ maSubNodes = std::move(rNodeArray);
+ ClaimPaternity();
+}
+
+bool SmStructureNode::IsVisible() const
+{
+ return false;
+}
+
+size_t SmStructureNode::GetNumSubNodes() const
+{
+ return maSubNodes.size();
+}
+
+SmNode* SmStructureNode::GetSubNode(size_t nIndex)
+{
+ return maSubNodes[nIndex];
+}
+
+SmNode* SmStructureNode::GetSubNodeBinMo(size_t nIndex) const
+{
+ if(GetType()==SmNodeType::BinDiagonal)
+ {
+ if (nIndex==1)
+ nIndex = 2;
+ else if (nIndex==2)
+ nIndex = 1;
+ }
+ return maSubNodes[nIndex];
+}
+
+void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ ForEachNonNull(const_cast<SmStructureNode *>(this),
+ [&rText](SmNode *pNode)
+ {
+ if (pNode->IsVisible())
+ pNode->SetAccessibleIndex(rText.getLength());
+ pNode->GetAccessibleText( rText );
+ });
+}
+
+void SmStructureNode::ClaimPaternity()
+{
+ ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
+}
+
+int SmStructureNode::IndexOfSubNode(SmNode const * pSubNode)
+{
+ size_t nSize = GetNumSubNodes();
+ for (size_t i = 0; i < nSize; i++)
+ if (pSubNode == GetSubNode(i))
+ return i;
+ return -1;
+}
+
+void SmStructureNode::SetSubNode(size_t nIndex, SmNode* pNode)
+{
+ size_t size = maSubNodes.size();
+ if (size <= nIndex)
+ {
+ //Resize subnodes array
+ maSubNodes.resize(nIndex + 1);
+ //Set new slots to NULL except at nIndex
+ for (size_t i = size; i < nIndex; i++)
+ maSubNodes[i] = nullptr;
+ }
+ maSubNodes[nIndex] = pNode;
+ if (pNode)
+ pNode->SetParent(this);
+}
+
+bool SmVisibleNode::IsVisible() const
+{
+ return true;
+}
+
+size_t SmVisibleNode::GetNumSubNodes() const
+{
+ return 0;
+}
+
+SmNode * SmVisibleNode::GetSubNode(size_t /*nIndex*/)
+{
+ return nullptr;
+}
+
+void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ rText.append(GetToken().aText);
+}
+
+void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // arranges all subnodes in one column
+{
+ SmNode *pNode;
+ size_t nSize = GetNumSubNodes();
+
+ // make distance depend on font size
+ tools::Long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
+ * GetFont().GetFontSize().Height()) / 100;
+
+ if (nSize < 1)
+ return;
+
+ // arrange subnodes and get maximum width of them
+ tools::Long nMaxWidth = 0,
+ nTmp;
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ { pNode->Arrange(rDev, rFormat);
+ if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
+ nMaxWidth = nTmp;
+ }
+ }
+
+ Point aPos;
+ SmRect::operator = (SmRect(nMaxWidth, 1));
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ { const SmRect &rNodeRect = pNode->GetRect();
+ const SmNode *pCoNode = pNode->GetLeftMost();
+ RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
+
+ aPos = rNodeRect.AlignTo(*this, RectPos::Bottom,
+ eHorAlign, RectVerAlign::Baseline);
+ if (i)
+ aPos.AdjustY(nDist );
+ pNode->MoveTo(aPos);
+ ExtendBy(rNodeRect, nSize > 1 ? RectCopyMBL::None : RectCopyMBL::Arg);
+ }
+ }
+ // #i972#
+ if (HasBaseline())
+ mnFormulaBaseline = GetBaseline();
+ else
+ {
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect aRect(aTmpDev, &rFormat, "a", GetFont().GetBorderWidth());
+ mnFormulaBaseline = GetAlignM();
+ // move from middle position by constant - distance
+ // between middle and baseline for single letter
+ mnFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
+ }
+}
+
+const SmNode * SmTableNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+tools::Long SmTableNode::GetFormulaBaseline() const
+{
+ return mnFormulaBaseline;
+}
+
+
+/**************************************************************************/
+
+
+void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
+ // to the rest of the formula compared to the 'FNT_MATH' font.
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+ Flags() |= FontChangeMask::Face;
+}
+
+
+/**************************************************************************/
+
+
+void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // arranges all subnodes in one row with some extra space between
+{
+ SmNode *pNode;
+ size_t nSize = GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ pNode->Arrange(rDev, rFormat);
+ }
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ if (nSize < 1)
+ {
+ // provide an empty rectangle with alignment parameters for the "current"
+ // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
+ // same sub-/supscript positions.)
+ //! be sure to use a character that has explicitly defined HiAttribut
+ //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
+ //! 'vec {a}'.
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, "a",
+ GetFont().GetBorderWidth()));
+ // make sure that the rectangle occupies (almost) no space
+ SetWidth(1);
+ SetItalicSpaces(0, 0);
+ return;
+ }
+
+ // make distance depend on font size
+ tools::Long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetFontSize().Height()) / 100;
+ if (!IsUseExtraSpaces())
+ nDist = 0;
+
+ Point aPos;
+ // copy the first node into LineNode and extend by the others
+ if (nullptr != (pNode = GetSubNode(0)))
+ SmRect::operator = (pNode->GetRect());
+
+ for (size_t i = 1; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ {
+ aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+
+ // add horizontal space to the left for each but the first sub node
+ aPos.AdjustX(nDist );
+
+ pNode->MoveTo(aPos);
+ ExtendBy( *pNode, RectCopyMBL::Xor );
+ }
+ }
+}
+
+
+/**************************************************************************/
+
+
+void SmExpressionNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
+{
+ SmLineNode::Arrange(rDev, rFormat);
+
+ // copy alignment of leftmost subnode if any
+ const SmNode *pNode = GetLeftMost();
+ if (pNode)
+ SetRectHorAlign(pNode->GetRectHorAlign(), false);
+}
+
+
+/**************************************************************************/
+
+
+void SmUnHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ bool bIsPostfix = GetToken().eType == TFACT;
+
+ SmNode *pNode0 = GetSubNode(0),
+ *pNode1 = GetSubNode(1);
+ SmNode *pOper = bIsPostfix ? pNode1 : pNode0,
+ *pBody = bIsPostfix ? pNode0 : pNode1;
+ assert(pOper);
+ assert(pBody);
+
+ pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
+ pOper->Arrange(rDev, rFormat);
+ pBody->Arrange(rDev, rFormat);
+
+ tools::Long nDist = (pOper->GetRect().GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
+
+ SmRect::operator = (*pNode0);
+
+ Point aPos = pNode1->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+ pNode1->MoveTo(aPos);
+ ExtendBy(*pNode1, RectCopyMBL::Xor);
+}
+
+
+/**************************************************************************/
+
+namespace {
+
+void lcl_GetHeightVerOffset(const SmRect &rRect,
+ tools::Long &rHeight, tools::Long &rVerOffset)
+ // calculate height and vertical offset of root sign suitable for 'rRect'
+{
+ rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
+ rHeight = rRect.GetHeight() - rVerOffset;
+
+ OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
+ OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
+}
+
+
+Point lcl_GetExtraPos(const SmRect &rRootSymbol,
+ const SmRect &rExtra)
+{
+ const Size &rSymSize = rRootSymbol.GetSize();
+
+ Point aPos = rRootSymbol.GetTopLeft()
+ + Point((rSymSize.Width() * 70) / 100,
+ (rSymSize.Height() * 52) / 100);
+
+ // from this calculate topleft edge of 'rExtra'
+ aPos.AdjustX( -(rExtra.GetWidth() + rExtra.GetItalicRightSpace()) );
+ aPos.AdjustY( -(rExtra.GetHeight()) );
+ // if there's enough space move a bit less to the right
+ // examples: "nroot i a", "nroot j a"
+ // (it looks better if we don't use italic-spaces here)
+ tools::Long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
+ if (aPos.X() > nX)
+ aPos.setX( nX );
+
+ return aPos;
+}
+
+}
+
+void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ //! pExtra needs to have the smaller index than pRootSym in order to
+ //! not to get the root symbol but the pExtra when clicking on it in the
+ //! GraphicWindow. (That is because of the simplicity of the algorithm
+ //! that finds the node corresponding to a mouseclick in the window.)
+ SmNode *pExtra = GetSubNode(0),
+ *pRootSym = GetSubNode(1),
+ *pBody = GetSubNode(2);
+ assert(pRootSym);
+ assert(pBody);
+
+ pBody->Arrange(rDev, rFormat);
+
+ tools::Long nHeight,
+ nVerOffset;
+ lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset);
+ nHeight += rFormat.GetDistance(DIS_ROOT)
+ * GetFont().GetFontSize().Height() / 100;
+
+ if (nHeight < 0)
+ {
+ SAL_WARN("starmath", "negative height");
+ nHeight = 0;
+ }
+
+ // font specialist advised to change the width first
+ pRootSym->AdaptToY(rDev, nHeight);
+ pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
+
+ pRootSym->Arrange(rDev, rFormat);
+
+ // Set the top and bottom of the root symbol to the top and bottom of its glyph bounding rect,
+ // to get accurate position of the root symbol.
+ SmRect rRootSymRect = pRootSym->AsGlyphRect();
+ pRootSym->SetTop(rRootSymRect.GetTop());
+ pRootSym->SetBottom(rRootSymRect.GetBottom());
+
+ Point aPos = pRootSym->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, RectVerAlign::Baseline);
+ //! override calculated vertical position
+ aPos.setY( pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom() );
+ aPos.AdjustY( -nVerOffset );
+ pRootSym->MoveTo(aPos);
+
+ if (pExtra)
+ { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
+ pExtra->Arrange(rDev, rFormat);
+
+ aPos = lcl_GetExtraPos(*pRootSym, *pExtra);
+ pExtra->MoveTo(aPos);
+ }
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pRootSym, RectCopyMBL::This);
+ if (pExtra)
+ ExtendBy(*pExtra, RectCopyMBL::This, true);
+}
+
+/**************************************************************************/
+
+
+void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pLeft = LeftOperand(),
+ *pOper = Symbol(),
+ *pRight = RightOperand();
+ assert(pLeft);
+ assert(pOper);
+ assert(pRight);
+
+ pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
+
+ pLeft ->Arrange(rDev, rFormat);
+ pOper ->Arrange(rDev, rFormat);
+ pRight->Arrange(rDev, rFormat);
+
+ const SmRect &rOpRect = pOper->GetRect();
+
+ tools::Long nMul;
+ if (o3tl::checked_multiply<tools::Long>(rOpRect.GetWidth(), rFormat.GetDistance(DIS_HORIZONTAL), nMul))
+ {
+ SAL_WARN("starmath", "integer overflow");
+ return;
+ }
+
+ tools::Long nDist = nMul / 100;
+
+ SmRect::operator = (*pLeft);
+
+ Point aPos;
+ aPos = pOper->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+ pOper->MoveTo(aPos);
+ ExtendBy(*pOper, RectCopyMBL::Xor);
+
+ aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+
+ pRight->MoveTo(aPos);
+ ExtendBy(*pRight, RectCopyMBL::Xor);
+}
+
+
+/**************************************************************************/
+
+
+void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNum = GetSubNode(0),
+ *pLine = GetSubNode(1),
+ *pDenom = GetSubNode(2);
+ assert(pNum);
+ assert(pLine);
+ assert(pDenom);
+
+ bool bIsTextmode = rFormat.IsTextmode();
+ if (bIsTextmode)
+ {
+ Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
+ pNum ->SetSize(aFraction);
+ pLine ->SetSize(aFraction);
+ pDenom->SetSize(aFraction);
+ }
+
+ pNum ->Arrange(rDev, rFormat);
+ pDenom->Arrange(rDev, rFormat);
+
+ tools::Long nFontHeight = GetFont().GetFontSize().Height(),
+ nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100,
+ nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100,
+ nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
+ nNumDist = bIsTextmode ? 0 :
+ nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100,
+ nDenomDist = bIsTextmode ? 0 :
+ nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100;
+
+ // font specialist advised to change the width first
+ pLine->AdaptToY(rDev, nThick);
+ pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
+ pLine->Arrange(rDev, rFormat);
+
+ // get horizontal alignment for numerator
+ const SmNode *pLM = pNum->GetLeftMost();
+ RectHorAlign eHorAlign = pLM->GetRectHorAlign();
+
+ // move numerator to its position
+ Point aPos = pNum->AlignTo(*pLine, RectPos::Top, eHorAlign, RectVerAlign::Baseline);
+ aPos.AdjustY( -nNumDist );
+ pNum->MoveTo(aPos);
+
+ // get horizontal alignment for denominator
+ pLM = pDenom->GetLeftMost();
+ eHorAlign = pLM->GetRectHorAlign();
+
+ // move denominator to its position
+ aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
+ aPos.AdjustY(nDenomDist );
+ pDenom->MoveTo(aPos);
+
+ SmRect::operator = (*pNum);
+ ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
+}
+
+const SmNode * SmBinVerNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+namespace {
+
+/// @return value of the determinant formed by the two points
+double Det(const Point &rHeading1, const Point &rHeading2)
+{
+ return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
+}
+
+
+/// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
+/// and has the direction vector 'rHeading2'
+bool IsPointInLine(const Point &rPoint1,
+ const Point &rPoint2, const Point &rHeading2)
+{
+ assert(rHeading2 != Point());
+
+ bool bRes = false;
+ static const double eps = 5.0 * DBL_EPSILON;
+
+ double fLambda;
+ if (std::abs(rHeading2.X()) > std::abs(rHeading2.Y()))
+ {
+ fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
+ bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
+ }
+ else
+ {
+ fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
+ bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
+ }
+
+ return bRes;
+}
+
+
+sal_uInt16 GetLineIntersectionPoint(Point &rResult,
+ const Point& rPoint1, const Point &rHeading1,
+ const Point& rPoint2, const Point &rHeading2)
+{
+ assert(rHeading1 != Point());
+ assert(rHeading2 != Point());
+
+ sal_uInt16 nRes = 1;
+ static const double eps = 5.0 * DBL_EPSILON;
+
+ // are the direction vectors linearly dependent?
+ double fDet = Det(rHeading1, rHeading2);
+ if (fabs(fDet) < eps)
+ {
+ nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
+ rResult = nRes ? rPoint1 : Point();
+ }
+ else
+ {
+ // here we do not pay attention to the computational accuracy
+ // (that would be more complicated and is not really worth it in this case)
+ double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
+ - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
+ / fDet;
+ rResult = Point(rPoint1.X() + static_cast<tools::Long>(fLambda * rHeading1.X()),
+ rPoint1.Y() + static_cast<tools::Long>(fLambda * rHeading1.Y()));
+ }
+
+ return nRes;
+}
+
+}
+
+
+/// @return position and size of the diagonal line
+/// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
+void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
+ const Point &rDiagPoint, double fAngleDeg) const
+
+{
+ double fAngleRad = basegfx::deg2rad(fAngleDeg);
+ tools::Long nRectLeft = GetItalicLeft(),
+ nRectRight = GetItalicRight(),
+ nRectTop = GetTop(),
+ nRectBottom = GetBottom();
+ Point aRightHdg (100, 0),
+ aDownHdg (0, 100),
+ aDiagHdg ( static_cast<tools::Long>(100.0 * cos(fAngleRad)),
+ static_cast<tools::Long>(-100.0 * sin(fAngleRad)) );
+
+ tools::Long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
+ Point aPoint;
+ if (IsAscending())
+ {
+ // determine top right corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the top border?
+ if (aPoint.X() <= nRectRight)
+ {
+ nRight = aPoint.X();
+ nTop = nRectTop;
+ }
+ else
+ {
+ // there has to be a point of intersection with the right border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectRight, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nRight = nRectRight;
+ nTop = aPoint.Y();
+ }
+
+ // determine bottom left corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectBottom), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the bottom border?
+ if (aPoint.X() >= nRectLeft)
+ {
+ nLeft = aPoint.X();
+ nBottom = nRectBottom;
+ }
+ else
+ {
+ // there has to be a point of intersection with the left border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nLeft = nRectLeft;
+ nBottom = aPoint.Y();
+ }
+ }
+ else
+ {
+ // determine top left corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the top border?
+ if (aPoint.X() >= nRectLeft)
+ {
+ nLeft = aPoint.X();
+ nTop = nRectTop;
+ }
+ else
+ {
+ // there has to be a point of intersection with the left border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nLeft = nRectLeft;
+ nTop = aPoint.Y();
+ }
+
+ // determine bottom right corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectBottom), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the bottom border?
+ if (aPoint.X() <= nRectRight)
+ {
+ nRight = aPoint.X();
+ nBottom = nRectBottom;
+ }
+ else
+ {
+ // there has to be a point of intersection with the right border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectRight, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nRight = nRectRight;
+ nBottom = aPoint.Y();
+ }
+ }
+
+ rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
+ rPos.setX( nLeft );
+ rPos.setY( nTop );
+}
+
+
+void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ // Both arguments have to get into the SubNodes before the Operator so that clicking
+ // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
+ SmNode *pLeft = GetSubNode(0),
+ *pRight = GetSubNode(1),
+ *pLine = GetSubNode(2);
+ assert(pLeft);
+ assert(pRight);
+ assert(pLine && pLine->GetType() == SmNodeType::PolyLine);
+
+ SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
+ assert(pOper);
+
+ //! some routines being called extract some info from the OutputDevice's
+ //! font (eg the space to be used for borders OR the font name(!!)).
+ //! Thus the font should reflect the needs and has to be set!
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ pLeft->Arrange(aTmpDev, rFormat);
+ pRight->Arrange(aTmpDev, rFormat);
+
+ // determine implicitly the values (incl. the margin) of the diagonal line
+ pOper->Arrange(aTmpDev, rFormat);
+
+ tools::Long nDelta = pOper->GetWidth() * 8 / 10;
+
+ // determine TopLeft position from the right argument
+ Point aPos;
+ aPos.setX( pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace() );
+ if (IsAscending())
+ aPos.setY( pLeft->GetBottom() + nDelta );
+ else
+ aPos.setY( pLeft->GetTop() - nDelta - pRight->GetHeight() );
+
+ pRight->MoveTo(aPos);
+
+ // determine new baseline
+ tools::Long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
+ : (pLeft->GetTop() + pRight->GetBottom()) / 2;
+ Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
+ nTmpBaseline);
+
+ SmRect::operator = (*pLeft);
+ ExtendBy(*pRight, RectCopyMBL::None);
+
+
+ // determine position and size of diagonal line
+ Size aTmpSize;
+ GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
+
+ // font specialist advised to change the width first
+ pOper->AdaptToY(aTmpDev, aTmpSize.Height());
+ pOper->AdaptToX(aTmpDev, aTmpSize.Width());
+ // and make it active
+ pOper->Arrange(aTmpDev, rFormat);
+
+ pOper->MoveTo(aPos);
+
+ ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
+}
+
+
+/**************************************************************************/
+
+
+void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
+ "Sm: wrong number of subnodes");
+
+ SmNode *pBody = GetBody();
+ assert(pBody);
+
+ tools::Long nOrigHeight = pBody->GetFont().GetFontSize().Height();
+
+ pBody->Arrange(rDev, rFormat);
+
+ const SmRect &rBodyRect = pBody->GetRect();
+ SmRect::operator = (rBodyRect);
+
+ // line that separates sub- and supscript rectangles
+ tools::Long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
+
+ Point aPos;
+ tools::Long nDelta, nDist;
+
+ // iterate over all possible sub-/supscripts
+ SmRect aTmpRect (rBodyRect);
+ for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
+ {
+ SmSubSup eSubSup = static_cast<SmSubSup>(i);
+ SmNode *pSubSup = GetSubSup(eSubSup);
+
+ if (!pSubSup)
+ continue;
+
+ // switch position of limits if we are in textmode
+ if (rFormat.IsTextmode() && (GetToken().nGroup & TG::Limit))
+ switch (eSubSup)
+ { case CSUB: eSubSup = RSUB; break;
+ case CSUP: eSubSup = RSUP; break;
+ default:
+ break;
+ }
+
+ // prevent sub-/supscripts from diminishing in size
+ // (as would be in "a_{1_{2_{3_4}}}")
+ if (GetFont().GetFontSize().Height() > rFormat.GetBaseSize().Height() / 3)
+ {
+ sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
+ SIZ_LIMITS : SIZ_INDEX;
+ Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
+ pSubSup->SetSize(aFraction);
+ }
+
+ pSubSup->Arrange(rDev, rFormat);
+
+ bool bIsTextmode = rFormat.IsTextmode();
+ nDist = 0;
+
+ //! be sure that CSUB, CSUP are handled before the other cases!
+ switch (eSubSup)
+ { case RSUB :
+ case LSUB :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_SUBSCRIPT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(aTmpRect,
+ eSubSup == LSUB ? RectPos::Left : RectPos::Right,
+ RectHorAlign::Center, RectVerAlign::Bottom);
+ aPos.AdjustY(nDist );
+ nDelta = nDelimLine - aPos.Y();
+ if (nDelta > 0)
+ aPos.AdjustY(nDelta );
+ break;
+ case RSUP :
+ case LSUP :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(aTmpRect,
+ eSubSup == LSUP ? RectPos::Left : RectPos::Right,
+ RectHorAlign::Center, RectVerAlign::Top);
+ aPos.AdjustY( -nDist );
+ nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
+ if (nDelta > 0)
+ aPos.AdjustY( -nDelta );
+ break;
+ case CSUB :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_LOWERLIMIT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Bottom,
+ RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDist );
+ break;
+ case CSUP :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_UPPERLIMIT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Top,
+ RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY( -nDist );
+ break;
+ }
+
+ pSubSup->MoveTo(aPos);
+ ExtendBy(*pSubSup, RectCopyMBL::This, true);
+
+ // update rectangle to which RSUB, RSUP, LSUB, LSUP
+ // will be aligned to
+ if (eSubSup == CSUB || eSubSup == CSUP)
+ aTmpRect = *this;
+ }
+}
+
+/**************************************************************************/
+
+void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pLeft = OpeningBrace(),
+ *pBody = Body(),
+ *pRight = ClosingBrace();
+ assert(pLeft);
+ assert(pBody);
+ assert(pRight);
+
+ pBody->Arrange(rDev, rFormat);
+
+ bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
+ bScale = pBody->GetHeight() > 0 &&
+ (GetScaleMode() == SmScaleMode::Height || bIsScaleNormal),
+ bIsABS = GetToken().eType == TABS;
+
+ tools::Long nFaceHeight = GetFont().GetFontSize().Height();
+
+ // determine oversize in %
+ sal_uInt16 nPerc = 0;
+ if (!bIsABS && bScale)
+ { // in case of oversize braces...
+ sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
+ DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
+ nPerc = rFormat.GetDistance(nIndex);
+ }
+
+ // determine the height for the braces
+ tools::Long nBraceHeight;
+ if (bScale)
+ {
+ nBraceHeight = pBody->GetType() == SmNodeType::Bracebody ?
+ static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
+ : pBody->GetHeight();
+ nBraceHeight += 2 * (nBraceHeight * nPerc / 100);
+ }
+ else
+ nBraceHeight = nFaceHeight;
+
+ // distance to the argument
+ nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
+ tools::Long nDist = nFaceHeight * nPerc / 100;
+
+ // if wanted, scale the braces to the wanted size
+ if (bScale)
+ {
+ Size aTmpSize (pLeft->GetFont().GetFontSize());
+ OSL_ENSURE(pRight->GetFont().GetFontSize() == aTmpSize,
+ "Sm : different font sizes");
+ aTmpSize.setWidth( std::min(nBraceHeight * 60 / 100,
+ rFormat.GetBaseSize().Height() * 3 / 2) );
+ // correction factor since change from StarMath to OpenSymbol font
+ // because of the different font width in the FontMetric
+ aTmpSize.setWidth( aTmpSize.Width() * 182 );
+ aTmpSize.setWidth( aTmpSize.Width() / 267 );
+
+ sal_Unicode cChar = pLeft->GetToken().cMathChar[0];
+ if (cChar != MS_LINE && cChar != MS_DLINE &&
+ cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
+ pLeft ->GetFont().SetSize(aTmpSize);
+
+ cChar = pRight->GetToken().cMathChar[0];
+ if (cChar != MS_LINE && cChar != MS_DLINE &&
+ cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
+ pRight->GetFont().SetSize(aTmpSize);
+
+ pLeft ->AdaptToY(rDev, nBraceHeight);
+ pRight->AdaptToY(rDev, nBraceHeight);
+ }
+
+ pLeft ->Arrange(rDev, rFormat);
+ pRight->Arrange(rDev, rFormat);
+
+ // required in order to make "\(a\) - (a) - left ( a right )" look alright
+ RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
+
+ Point aPos;
+ aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustX( -nDist );
+ pLeft->MoveTo(aPos);
+
+ aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustX(nDist );
+ pRight->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ size_t nNumSubNodes = GetNumSubNodes();
+ if (nNumSubNodes == 0)
+ return;
+
+ // arrange arguments
+ for (size_t i = 0; i < nNumSubNodes; i += 2)
+ GetSubNode(i)->Arrange(rDev, rFormat);
+
+ // build reference rectangle with necessary info for vertical alignment
+ SmRect aRefRect (*GetSubNode(0));
+ for (size_t i = 0; i < nNumSubNodes; i += 2)
+ {
+ SmRect aTmpRect (*GetSubNode(i));
+ Point aPos = aTmpRect.AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aTmpRect.MoveTo(aPos);
+ aRefRect.ExtendBy(aTmpRect, RectCopyMBL::Xor);
+ }
+
+ mnBodyHeight = aRefRect.GetHeight();
+
+ // scale separators to required height and arrange them
+ bool bScale = GetScaleMode() == SmScaleMode::Height || rFormat.IsScaleNormalBrackets();
+ tools::Long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetFontSize().Height();
+ sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
+ DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
+ sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
+ if (bScale)
+ nHeight += 2 * (nHeight * nPerc / 100);
+ for (size_t i = 1; i < nNumSubNodes; i += 2)
+ {
+ SmNode *pNode = GetSubNode(i);
+ pNode->AdaptToY(rDev, nHeight);
+ pNode->Arrange(rDev, rFormat);
+ }
+
+ // horizontal distance between argument and brackets or separators
+ tools::Long nDist = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_BRACKETSPACE) / 100;
+
+ SmNode *pLeft = GetSubNode(0);
+ SmRect::operator = (*pLeft);
+ for (size_t i = 1; i < nNumSubNodes; ++i)
+ {
+ bool bIsSeparator = i % 2 != 0;
+ RectVerAlign eVerAlign = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
+
+ SmNode *pRight = GetSubNode(i);
+ Point aPosX = pRight->AlignTo(*pLeft, RectPos::Right, RectHorAlign::Center, eVerAlign),
+ aPosY = pRight->AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, eVerAlign);
+ aPosX.AdjustX(nDist );
+
+ pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
+ ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
+
+ pLeft = pRight;
+ }
+}
+
+
+/**************************************************************************/
+
+
+void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pBody = Body(),
+ *pBrace = Brace(),
+ *pScript = Script();
+ assert(pBody);
+ assert(pBrace);
+ assert(pScript);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ pBody->Arrange(aTmpDev, rFormat);
+
+ // size is the same as for limits for this part
+ pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
+ // braces are a bit taller than usually
+ pBrace ->SetSize( Fraction(3, 2) );
+
+ tools::Long nItalicWidth = pBody->GetItalicWidth();
+ if (nItalicWidth > 0)
+ pBrace->AdaptToX(aTmpDev, nItalicWidth);
+
+ pBrace ->Arrange(aTmpDev, rFormat);
+ pScript->Arrange(aTmpDev, rFormat);
+
+ // determine the relative position and the distances between each other
+ RectPos eRectPos;
+ tools::Long nFontHeight = pBody->GetFont().GetFontSize().Height();
+ tools::Long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
+ nDistScript = nFontHeight;
+ if (GetToken().eType == TOVERBRACE)
+ {
+ eRectPos = RectPos::Top;
+ nDistBody = - nDistBody;
+ nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
+ }
+ else // TUNDERBRACE
+ {
+ eRectPos = RectPos::Bottom;
+ nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
+ }
+ nDistBody /= 100;
+ nDistScript /= 100;
+
+ Point aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDistBody );
+ pBrace->MoveTo(aPos);
+
+ aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDistScript );
+ pScript->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+SmNode * SmOperNode::GetSymbol()
+{
+ SmNode *pNode = GetSubNode(0);
+ assert(pNode);
+
+ if (pNode->GetType() == SmNodeType::SubSup)
+ pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
+
+ OSL_ENSURE(pNode, "Sm: NULL pointer!");
+ return pNode;
+}
+
+
+tools::Long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
+ const SmFormat &rFormat) const
+ // returns the font height to be used for operator-symbol
+{
+ tools::Long nHeight = GetFont().GetFontSize().Height();
+
+ SmTokenType eTmpType = GetToken().eType;
+ if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
+ return nHeight;
+
+ if (!rFormat.IsTextmode())
+ {
+ // set minimum size ()
+ nHeight += (nHeight * 20) / 100;
+
+ nHeight += nHeight
+ * rFormat.GetDistance(DIS_OPERATORSIZE) / 100;
+ nHeight = nHeight * 686 / 845;
+ }
+
+ // correct user-defined symbols to match height of sum from used font
+ if (rSymbol.GetToken().eType == TSPECIAL)
+ nHeight = nHeight * 845 / 686;
+
+ return nHeight;
+}
+
+
+void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pOper = GetSubNode(0);
+ SmNode *pBody = GetSubNode(1);
+
+ assert(pOper);
+ assert(pBody);
+
+ SmNode *pSymbol = GetSymbol();
+ pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
+ pSymbol->GetFont().GetFontSize().Height()));
+
+ pBody->Arrange(rDev, rFormat);
+ bool bDynamicallySized = false;
+ if (pSymbol->GetToken().eType == TINTD)
+ {
+ tools::Long nBodyHeight = pBody->GetHeight();
+ tools::Long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
+ if (nFontHeight < nBodyHeight)
+ {
+ pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
+ bDynamicallySized = true;
+ }
+ }
+ pOper->Arrange(rDev, rFormat);
+
+ tools::Long nOrigHeight = GetFont().GetFontSize().Height(),
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_OPERATORSPACE) / 100;
+
+ Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
+ aPos.AdjustX( -nDist );
+ pOper->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pOper, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // set alignment within the entire subtree (including current node)
+{
+ assert(GetNumSubNodes() == 1);
+
+ SmNode *pNode = GetSubNode(0);
+ assert(pNode);
+
+ RectHorAlign eHorAlign = RectHorAlign::Center;
+ switch (GetToken().eType)
+ {
+ case TALIGNL: eHorAlign = RectHorAlign::Left; break;
+ case TALIGNC: eHorAlign = RectHorAlign::Center; break;
+ case TALIGNR: eHorAlign = RectHorAlign::Right; break;
+ default:
+ break;
+ }
+ SetRectHorAlign(eHorAlign);
+
+ pNode->Arrange(rDev, rFormat);
+
+ SmRect::operator = (pNode->GetRect());
+}
+
+
+/**************************************************************************/
+
+
+void SmAttributeNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pAttr = Attribute(),
+ *pBody = Body();
+ assert(pBody);
+ assert(pAttr);
+
+ pBody->Arrange(rDev, rFormat);
+
+ if (GetScaleMode() == SmScaleMode::Width)
+ pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
+ pAttr->Arrange(rDev, rFormat);
+
+ // get relative position of attribute
+ RectVerAlign eVerAlign;
+ tools::Long nDist = 0;
+ switch (GetToken().eType)
+ { case TUNDERLINE :
+ eVerAlign = RectVerAlign::AttributeLo;
+ break;
+ case TOVERSTRIKE :
+ eVerAlign = RectVerAlign::AttributeMid;
+ break;
+ default :
+ eVerAlign = RectVerAlign::AttributeHi;
+ if (pBody->GetType() == SmNodeType::Attribute)
+ nDist = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100;
+ }
+ Point aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustY( -nDist );
+ pAttr->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pAttr, RectCopyMBL::This, true);
+}
+
+void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ //! prepare subnodes first
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ int nFnt = -1;
+ switch (GetToken().eType)
+ {
+ case TFIXED: nFnt = FNT_FIXED; break;
+ case TSANS: nFnt = FNT_SANS; break;
+ case TSERIF: nFnt = FNT_SERIF; break;
+ default:
+ break;
+ }
+ if (nFnt != -1)
+ { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
+ SetFont(GetFont());
+ }
+
+ //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
+ //! other font nodes (those with lower depth in the tree)
+ Flags() |= FontChangeMask::Face;
+}
+
+void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNode = GetSubNode(1);
+ assert(pNode);
+ sal_uInt32 nc;
+
+ switch (GetToken().eType)
+ { case TSIZE :
+ pNode->SetFontSize(maFontSize, meSizeType);
+ break;
+ case TSANS :
+ case TSERIF :
+ case TFIXED :
+ pNode->SetFont(GetFont());
+ break;
+ case TUNKNOWN : break; // no assertion on "font <?> <?>"
+
+ case TPHANTOM : SetPhantom(true); break;
+ case TBOLD : SetAttribute(FontAttribute::Bold); break;
+ case TITALIC : SetAttribute(FontAttribute::Italic); break;
+ case TNBOLD : ClearAttribute(FontAttribute::Bold); break;
+ case TNITALIC : ClearAttribute(FontAttribute::Italic); break;
+
+ // Using HTML CSS Level 1 standard
+ case TRGB :
+ case TRGBA :
+ case THTMLCOL :
+ case TMATHMLCOL :
+ case TDVIPSNAMESCOL:
+ case TICONICCOL :
+ case THEX :
+ nc = GetToken().cMathChar.toUInt32(16);
+ SetColor(Color(ColorTransparency, nc));
+ break;
+
+ default:
+ SAL_WARN("starmath", "unknown case");
+ }
+
+ pNode->Arrange(rDev, rFormat);
+
+ SmRect::operator = (pNode->GetRect());
+}
+
+/**************************************************************************/
+
+
+SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
+ : SmGraphicNode(SmNodeType::PolyLine, rNodeToken)
+ , maPoly(2)
+ , mnWidth(0)
+{
+}
+
+
+void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
+{
+ maToSize.setWidth( nNewWidth );
+}
+
+
+void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
+{
+ GetFont().FreezeBorderWidth();
+ maToSize.setHeight( nNewHeight );
+}
+
+
+void SmPolyLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ //! some routines being called extract some info from the OutputDevice's
+ //! font (eg the space to be used for borders OR the font name(!!)).
+ //! Thus the font should reflect the needs and has to be set!
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ tools::Long nBorderwidth = GetFont().GetBorderWidth();
+
+ // create polygon using both endpoints
+ assert(maPoly.GetSize() == 2);
+ Point aPointA, aPointB;
+ if (GetToken().eType == TWIDESLASH)
+ {
+ aPointA.setX( nBorderwidth );
+ aPointA.setY( maToSize.Height() - nBorderwidth );
+ aPointB.setX( maToSize.Width() - nBorderwidth );
+ aPointB.setY( nBorderwidth );
+ }
+ else
+ {
+ OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
+ aPointA.setX( nBorderwidth );
+ aPointA.setY( nBorderwidth );
+ aPointB.setX( maToSize.Width() - nBorderwidth );
+ aPointB.setY( maToSize.Height() - nBorderwidth );
+ }
+ maPoly.SetPoint(aPointA, 0);
+ maPoly.SetPoint(aPointB, 1);
+
+ tools::Long nThick = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_STROKEWIDTH) / 100;
+ mnWidth = nThick + 2 * nBorderwidth;
+
+ SmRect::operator = (SmRect(maToSize.Width(), maToSize.Height()));
+}
+
+
+/**************************************************************************/
+
+void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
+{
+ mnBodyWidth = nWidth;
+}
+
+
+void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
+{
+ // some additional length so that the horizontal
+ // bar will be positioned above the argument
+ SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10);
+}
+
+
+/**************************************************************************/
+
+
+void SmRectangleNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
+{
+ maToSize.setWidth( nWidth );
+}
+
+
+void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
+{
+ GetFont().FreezeBorderWidth();
+ maToSize.setHeight( nHeight );
+}
+
+
+void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
+{
+ tools::Long nFontHeight = GetFont().GetFontSize().Height();
+ tools::Long nWidth = maToSize.Width(),
+ nHeight = maToSize.Height();
+ if (nHeight == 0)
+ nHeight = nFontHeight / 30;
+ if (nWidth == 0)
+ nWidth = nFontHeight / 3;
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // add some borderspace
+ sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth();
+ nHeight += 2 * nTmpBorderWidth;
+
+ //! use this method in order to have 'SmRect::HasAlignInfo() == true'
+ //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
+ SmRect::operator = (SmRect(nWidth, nHeight));
+}
+
+
+/**************************************************************************/
+
+
+SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
+ : SmVisibleNode(eNodeType, rNodeToken)
+ , mnFontDesc(nFontDescP)
+ , mnSelectionStart(0)
+ , mnSelectionEnd(0)
+{
+}
+
+SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
+ : SmVisibleNode(SmNodeType::Text, rNodeToken)
+ , mnFontDesc(nFontDescP)
+ , mnSelectionStart(0)
+ , mnSelectionEnd(0)
+{
+}
+
+void SmTextNode::ChangeText(const OUString &rText) {
+ maText = rText;
+ GetToken().aText = rText;
+ AdjustFontDesc();
+}
+
+void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // default setting for horizontal alignment of nodes with TTEXT
+ // content is as alignl (cannot be done in Arrange since it would
+ // override the settings made by an SmAlignNode before)
+ if (TTEXT == GetToken().eType)
+ SetRectHorAlign( RectHorAlign::Left );
+
+ maText = GetToken().aText;
+ GetFont() = rFormat.GetFont(GetFontDesc());
+
+ if (IsItalic( GetFont() ))
+ Attributes() |= FontAttribute::Italic;
+ if (IsBold( GetFont() ))
+ Attributes() |= FontAttribute::Bold;
+
+ // special handling for ':' where it is a token on its own and is likely
+ // to be used for mathematical notations. (E.g. a:b = 2:3)
+ // In that case it should not be displayed in italic.
+ if (maText.getLength() == 1 && GetToken().aText[0] == ':')
+ Attributes() &= ~FontAttribute::Italic;
+
+ // Arabic text should not be italic, so we check for any character in Arabic script and
+ // remove italic attribute.
+ if (!maText.isEmpty())
+ {
+ sal_Int32 nIndex = 0;
+ while (nIndex < maText.getLength())
+ {
+ sal_uInt32 cChar = maText.iterateCodePoints(&nIndex);
+ if (u_getIntPropertyValue(cChar, UCHAR_SCRIPT) == USCRIPT_ARABIC)
+ {
+ Attributes() &= ~FontAttribute::Italic;
+ break;
+ }
+ }
+ }
+};
+
+
+void SmTextNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
+ SIZ_FUNCTION : SIZ_TEXT;
+ GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, maText, GetFont().GetBorderWidth()));
+}
+
+void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ rText.append(maText);
+}
+
+void SmTextNode::AdjustFontDesc()
+{
+ if (GetToken().nGroup == TG::Function) mnFontDesc = FNT_FUNCTION;
+ else if (GetToken().eType == TTEXT) mnFontDesc = FNT_TEXT;
+ else {
+ sal_Unicode firstChar = maText[0];
+ if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',')
+ mnFontDesc = FNT_NUMBER;
+ else mnFontDesc = FNT_VARIABLE;
+ }
+}
+
+sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
+{
+ //Find the best match in accepted unicode for our private area symbols
+ static const sal_Unicode aStarMathPrivateToUnicode[] =
+ {
+ 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
+ 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
+ 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
+ 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
+ 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
+ 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
+ 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
+ 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
+ 0xE0DA, 0x2190, 0x2191, 0x2193
+ };
+ if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
+ nIn = aStarMathPrivateToUnicode[nIn-0xE080];
+
+ //For whatever unicode glyph that equation editor doesn't ship with that
+ //we have a possible match we can munge it to.
+ switch (nIn)
+ {
+ case 0x2223:
+ nIn = '|';
+ break;
+ default:
+ break;
+ }
+
+ return nIn;
+}
+
+/**************************************************************************/
+
+void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNode;
+
+ // initialize array that is to hold the maximum widths of all
+ // elements (subnodes) in that column.
+ std::vector<tools::Long> aColWidth(mnNumCols);
+
+ // arrange subnodes and calculate the above arrays contents
+ size_t nNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNodes; ++i)
+ {
+ size_t nIdx = nNodes - 1 - i;
+ if (nullptr != (pNode = GetSubNode(nIdx)))
+ {
+ pNode->Arrange(rDev, rFormat);
+ int nCol = nIdx % mnNumCols;
+ aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
+ }
+ }
+
+ // norm distance from which the following two are calculated
+ const tools::Long nNormDist = 3 * GetFont().GetFontSize().Height();
+
+ // define horizontal and vertical minimal distances that separate
+ // the elements
+ tools::Long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100,
+ nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100;
+
+ // build array that holds the leftmost position for each column
+ std::vector<tools::Long> aColLeft(mnNumCols);
+ tools::Long nX = 0;
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ aColLeft[j] = nX;
+ nX += aColWidth[j] + nHorDist;
+ }
+
+ SmRect::operator = (SmRect());
+ for (size_t i = 0; i < mnNumRows; ++i)
+ {
+ Point aPos;
+ SmRect aLineRect;
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
+ assert(pTmpNode);
+
+ const SmRect &rNodeRect = pTmpNode->GetRect();
+
+ // align all baselines in that row if possible
+ aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+
+ // get horizontal alignment
+ const SmNode *pCoNode = pTmpNode->GetLeftMost();
+ RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
+
+ // calculate horizontal position of element depending on column
+ // and horizontal alignment
+ switch (eHorAlign)
+ { case RectHorAlign::Left:
+ aPos.setX( aColLeft[j] );
+ break;
+ case RectHorAlign::Center:
+ aPos.setX( rNodeRect.GetLeft() + aColLeft[j]
+ + aColWidth[j] / 2
+ - rNodeRect.GetItalicCenterX() );
+ break;
+ case RectHorAlign::Right:
+ aPos.setX( aColLeft[j]
+ + aColWidth[j] - rNodeRect.GetItalicWidth() );
+ break;
+ default:
+ assert(false);
+ }
+
+ pTmpNode->MoveTo(aPos);
+ aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
+ }
+
+ aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
+ if (i > 0)
+ aPos.AdjustY(nVerDist );
+
+ // move 'aLineRect' and rectangles in that line to final position
+ Point aDelta(0, // since horizontal alignment is already done
+ aPos.Y() - aLineRect.GetTop());
+ aLineRect.Move(aDelta);
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
+ pNode->Move(aDelta);
+ }
+
+ ExtendBy(aLineRect, RectCopyMBL::None);
+ }
+}
+
+const SmNode * SmMatrixNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+/**************************************************************************/
+
+
+SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
+: SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH)
+{
+ SetText(GetToken().cMathChar);
+}
+
+void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
+{
+ // Since there is no function to do this, we try to approximate it:
+ Size aFntSize (GetFont().GetFontSize());
+
+ //! however the result is a bit better with 'nWidth' as initial font width
+ aFntSize.setWidth( nWidth );
+ GetFont().SetSize(aFntSize);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // get denominator of error factor for width
+ tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
+ tools::Long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
+
+ // scale fontwidth with this error factor
+ aFntSize.setWidth( aFntSize.Width() * nWidth );
+ aFntSize.setWidth( aFntSize.Width() / ( nDenom ? nDenom : 1) );
+
+ GetFont().SetSize(aFntSize);
+}
+
+void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
+{
+ GetFont().FreezeBorderWidth();
+ Size aFntSize (GetFont().GetFontSize());
+
+ // Since we only want to scale the height, we might have
+ // to determine the font width in order to keep it
+ if (aFntSize.Width() == 0)
+ {
+ rDev.Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE);
+ rDev.SetFont(GetFont());
+ aFntSize.setWidth( rDev.GetFontMetric().GetFontSize().Width() );
+ rDev.Pop();
+ }
+ OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
+
+ //! however the result is a bit better with 'nHeight' as initial
+ //! font height
+ aFntSize.setHeight( nHeight );
+ GetFont().SetSize(aFntSize);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // get denominator of error factor for height
+ tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
+ tools::Long nDenom = 0;
+ if (!GetText().isEmpty())
+ nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetHeight();
+
+ // scale fontwidth with this error factor
+ aFntSize.setHeight( aFntSize.Height() * nHeight );
+ aFntSize.setHeight( aFntSize.Height() / ( nDenom ? nDenom : 1) );
+
+ GetFont().SetSize(aFntSize);
+}
+
+
+void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont() = rFormat.GetFont(GetFontDesc());
+ // use same font size as is used for variables
+ GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
+
+ OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
+ GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
+ "wrong charset for character from StarMath/OpenSymbol font");
+
+ Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
+};
+
+
+void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ const OUString &rText = GetText();
+
+ if (rText.isEmpty() || rText[0] == '\0')
+ { SmRect::operator = (SmRect());
+ return;
+ }
+
+ PrepareAttributes();
+
+ GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
+}
+
+/**************************************************************************/
+
+SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
+ : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
+{
+}
+
+
+SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
+ : SmTextNode(SmNodeType::Special, rNodeToken, FNT_VARIABLE) // default Font isn't always correct!
+{
+}
+
+
+void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ const SmSym *pSym;
+ SmModule *pp = SM_MOD();
+
+ bool bIsGreekSymbol = false;
+ bool bIsSpecialSymbol = false;
+ bool bIsArabic = false;
+
+ if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName(GetToken().aText.subView(1))))
+ {
+ sal_UCS4 cChar = pSym->GetCharacter();
+ OUString aTmp( &cChar, 1 );
+ SetText( aTmp );
+ GetFont() = pSym->GetFace(&rFormat);
+
+ OUString aSymbolSetName = SmLocalizedSymbolData::GetExportSymbolSetName(pSym->GetSymbolSetName());
+ if (aSymbolSetName == "Greek")
+ bIsGreekSymbol = true;
+ else if (aSymbolSetName == "Special")
+ bIsSpecialSymbol = true;
+ else if (aSymbolSetName == "Arabic")
+ bIsArabic = true;
+ }
+ else
+ {
+ SetText( GetToken().aText );
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+ }
+ // use same font size as is used for variables
+ GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
+
+ // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
+ // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
+ // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
+
+ //! see also SmFontStyles::GetStyleName
+ if (IsItalic( GetFont() ))
+ SetAttribute(FontAttribute::Italic);
+ if (IsBold( GetFont() ))
+ SetAttribute(FontAttribute::Bold);
+
+ Flags() |= FontChangeMask::Face;
+
+ sal_uInt32 cChar = 0;
+ if (!GetText().isEmpty())
+ {
+ sal_Int32 nIndex = 0;
+ cChar = GetText().iterateCodePoints(&nIndex);
+ if (!bIsArabic)
+ bIsArabic = u_getIntPropertyValue(cChar, UCHAR_SCRIPT) == USCRIPT_ARABIC;
+ }
+
+ if (!bIsGreekSymbol && !bIsSpecialSymbol && !bIsArabic)
+ return;
+
+ // Arabic and special symbols should not be italic,
+ // Greek is italic only in some cases.
+ bool bItalic = false;
+ if (bIsGreekSymbol)
+ {
+ sal_Int16 nStyle = rFormat.GetGreekCharStyle();
+ OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
+ if (nStyle == 1)
+ bItalic = true;
+ else if (nStyle == 2)
+ {
+ static const sal_Unicode cUppercaseAlpha = 0x0391;
+ static const sal_Unicode cUppercaseOmega = 0x03A9;
+ // uppercase letters should be straight and lowercase letters italic
+ bItalic = cUppercaseAlpha > cChar || cChar > cUppercaseOmega;
+ }
+ }
+
+ if (bItalic)
+ Attributes() |= FontAttribute::Italic;
+ else
+ Attributes() &= ~FontAttribute::Italic;
+};
+
+
+void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
+}
+
+/**************************************************************************/
+
+
+void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
+ GetFont().GetBorderWidth()).AsGlyphRect());
+}
+
+
+/**************************************************************************/
+
+
+void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont().SetColor(COL_GRAY);
+ Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
+};
+
+
+void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
+}
+
+
+/**************************************************************************/
+
+
+void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont().SetColor(COL_RED);
+ Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
+ | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
+}
+
+
+void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ const OUString &rText = GetText();
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
+}
+
+/**************************************************************************/
+
+void SmBlankNode::IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy)
+{
+ switch(rToken.eType)
+ {
+ case TBLANK: mnNum += (4 * nMultiplyBy); break;
+ case TSBLANK: mnNum += (1 * nMultiplyBy); break;
+ default:
+ break;
+ }
+}
+
+void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // Here it need/should not be the StarMath font, so that for the character
+ // used in Arrange a normal (non-clipped) rectangle is generated
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+
+ Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
+}
+
+
+void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // make distance depend on the font height
+ // (so that it increases when scaling (e.g. size *2 {a ~ b})
+ tools::Long nDist = GetFont().GetFontSize().Height() / 10,
+ nSpace = mnNum * nDist;
+
+ // get a SmRect with Baseline and all the bells and whistles
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
+ GetFont().GetBorderWidth()));
+
+ // and resize it to the requested size
+ SetItalicSpaces(0, 0);
+ SetWidth(nSpace);
+}
+
+/**************************************************************************/
+//Implementation of all accept methods for SmVisitor
+
+void SmTableNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBraceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmOperNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmAlignNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmAttributeNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmFontNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmUnHorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinHorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinVerNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmSubSupNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmMatrixNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmPlaceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmTextNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmSpecialNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBlankNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmErrorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmLineNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmExpressionNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRootNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRectangleNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */