Files
libreoffice/starmath/source/node.cxx
Khaled Hosny 7de4c6b95d tdf#62174: Fix saving of localized iGreek symbols
When saving symbols we use the non-localized name, but iGreek symbols
are a bit of a hack since they are added at runtime and don’t have
localizations of there own but use the Greek symbols names with an “i”
prefix.

Instead of looking directly for localized names, we now go through the
symbol manager that already knows about iGreek and use the export name
of the symbol. We have to also make sure the iGreek symbols has
non-localized export name.

Change-Id: Ia3e38579abba56ae12ade126f564f8c6f9c3229a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157137
Tested-by: Jenkins
Reviewed-by: خالد حسني <khaled@libreoffice.org>
2023-09-21 12:33:19 +02:00

2472 lines
75 KiB
C++

/* -*- 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>
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 (GetToken().aText.getLength() == 1 && GetToken().aText[0] == ':')
Attributes() &= ~FontAttribute::Italic;
};
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()));
}
/**************************************************************************/
static bool lcl_IsFromGreekSymbolSet( std::u16string_view aTokenText )
{
bool bRes = false;
// valid symbol name needs to have a '%' at pos 0 and at least an additional char
if (aTokenText.size() > 2 && aTokenText[0] == u'%')
{
SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName(aTokenText.substr(1));
if (pSymbol && SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
bRes = true;
}
return bRes;
}
SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
: SmTextNode(eNodeType, rNodeToken, _nFontDesc)
, mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
{
}
SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
: SmTextNode(SmNodeType::Special, rNodeToken, FNT_MATH) // default Font isn't always correct!
, mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
{
}
void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
{
SmNode::Prepare(rFormat, rDocShell, nDepth);
const SmSym *pSym;
SmModule *pp = SM_MOD();
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);
}
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;
if (!mbIsFromGreekSymbolSet)
return;
OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
bool bItalic = false;
sal_Int16 nStyle = rFormat.GetGreekCharStyle();
OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
if (nStyle == 1)
bItalic = true;
else if (nStyle == 2)
{
const OUString& rTmp(GetText());
if (!rTmp.isEmpty())
{
static const sal_Unicode cUppercaseAlpha = 0x0391;
static const sal_Unicode cUppercaseOmega = 0x03A9;
sal_Unicode cChar = rTmp[0];
// 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: */