b6e2576798
Change-Id: I906deadf53e1c564c786e5b5f8184e706c4dbdec Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157425 Tested-by: Julien Nabet <serval2412@yahoo.fr> Reviewed-by: Julien Nabet <serval2412@yahoo.fr>
2491 lines
75 KiB
C++
2491 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>
|
|
#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: */
|