/* -*- 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 .
*/
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;
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 constint nMaxVal = o3tl::convert(128, o3tl::Length::pt, SmO3tlLengthUnit()); if (aFntSize.Height() > nMaxVal)
aFntSize.setHeight( nMaxVal );
// 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;
}
}
}
}
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();
SmRect aRect(aTmpDev, &rFormat, u"a"_ustr, GetFont().GetBorderWidth());
mnFormulaBaseline = GetAlignM(); // move from middle position by constant - distance // between middle and baseline for single letter
mnFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
}
}
// 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);
}
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, u"a"_ustr,
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 );
// 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);
// 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());
// 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);
/// @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());
// 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
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);
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);
//! 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());
// 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);
// 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);
}
// 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 );
// required in order to make "\(a\) - (a) - left ( a right )" look alright
RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
// 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;
// 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);
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();
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);
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;
}
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;
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());
void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, tools::Long nHeight)
{ // some additional length so that the horizontal // bar will be positioned above the argument
SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10);
}
//! 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));
}
// 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 );
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;
}
}
}
};
//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;
}
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);
}
void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, tools::Long 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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.