/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_starmath.hxx" #include "node.hxx" #include "rect.hxx" #include "symbol.hxx" #include "smmod.hxx" #include "document.hxx" #include "view.hxx" #include "mathtype.hxx" #include #include #include #include #include #include #include #include #include #include // define this to draw rectangles for debugging //#define SM_RECT_DEBUG #define APPEND(str,ascii) str.AppendAscii(RTL_CONSTASCII_STRINGPARAM(ascii)) //////////////////////////////////////// // SmTmpDevice // Allows for font and color changes. The original settings will be restored // in the destructor. // It's main purpose is to allow for the "const" in the 'OutputDevice' // argument in the 'Arrange' functions and restore changes made in the 'Draw' // functions. // Usually a MapMode of 1/100th mm will be used. // class SmTmpDevice { OutputDevice &rOutDev; // disallow use of copy-constructor and assignment-operator SmTmpDevice(const SmTmpDevice &rTmpDev); SmTmpDevice & operator = (const SmTmpDevice &rTmpDev); Color Impl_GetColor( const Color& rColor ); public: SmTmpDevice(OutputDevice &rTheDev, BOOL bUseMap100th_mm); ~SmTmpDevice() { rOutDev.Pop(); } void SetFont(const Font &rNewFont); void SetLineColor( const Color& rColor ) { rOutDev.SetLineColor( Impl_GetColor(rColor) ); } void SetFillColor( const Color& rColor ) { rOutDev.SetFillColor( Impl_GetColor(rColor) ); } void SetTextColor( const Color& rColor ) { rOutDev.SetTextColor( Impl_GetColor(rColor) ); } operator OutputDevice & () { return rOutDev; } }; SmTmpDevice::SmTmpDevice(OutputDevice &rTheDev, BOOL bUseMap100th_mm) : rOutDev(rTheDev) { rOutDev.Push( PUSH_FONT | PUSH_MAPMODE | PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_TEXTCOLOR ); if (bUseMap100th_mm && MAP_100TH_MM != rOutDev.GetMapMode().GetMapUnit()) { DBG_ERROR( "incorrect MapMode?" ); rOutDev.SetMapMode( MAP_100TH_MM ); //Immer fuer 100% fomatieren } } Color SmTmpDevice::Impl_GetColor( const Color& rColor ) { ColorData nNewCol = rColor.GetColor(); if (COL_AUTO == nNewCol) { if (OUTDEV_PRINTER == rOutDev.GetOutDevType()) nNewCol = COL_BLACK; else { Color aBgCol( rOutDev.GetBackground().GetColor() ); if (OUTDEV_WINDOW == rOutDev.GetOutDevType()) aBgCol = ((Window &) rOutDev).GetDisplayBackground().GetColor(); nNewCol = SM_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor; Color aTmpColor( nNewCol ); if (aBgCol.IsDark() && aTmpColor.IsDark()) nNewCol = COL_WHITE; else if (aBgCol.IsBright() && aTmpColor.IsBright()) nNewCol = COL_BLACK; } } return Color( nNewCol ); } void SmTmpDevice::SetFont(const Font &rNewFont) { rOutDev.SetFont( rNewFont ); rOutDev.SetTextColor( Impl_GetColor( rNewFont.GetColor() ) ); } /////////////////////////////////////////////////////////////////////////// SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken) { eType = eNodeType; eScaleMode = SCALE_NONE; aNodeToken = rNodeToken; nAccIndex = -1; } SmNode::~SmNode() { } BOOL SmNode::IsVisible() const { return FALSE; } USHORT SmNode::GetNumSubNodes() const { return 0; } SmNode * SmNode::GetSubNode(USHORT /*nIndex*/) { return NULL; } SmNode * SmNode::GetLeftMost() // returns leftmost node of current subtree. //! (this assumes the one with index 0 is always the leftmost subnode //! for the current node). { SmNode *pNode = GetNumSubNodes() > 0 ? GetSubNode(0) : NULL; return pNode ? pNode->GetLeftMost() : this; } void SmNode::SetPhantom(BOOL bIsPhantomP) { if (! (Flags() & FLG_VISIBLE)) bIsPhantom = bIsPhantomP; SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->SetPhantom(bIsPhantom); } void SmNode::SetColor(const Color& rColor) { if (! (Flags() & FLG_COLOR)) GetFont().SetColor(rColor); SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->SetColor(rColor); } void SmNode::SetAttribut(USHORT nAttrib) { if ( (nAttrib == ATTR_BOLD && !(Flags() & FLG_BOLD)) || (nAttrib == ATTR_ITALIC && !(Flags() & FLG_ITALIC)) ) { nAttributes |= nAttrib; } SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->SetAttribut(nAttrib); } void SmNode::ClearAttribut(USHORT nAttrib) { if ( (nAttrib == ATTR_BOLD && !(Flags() & FLG_BOLD)) || (nAttrib == ATTR_ITALIC && !(Flags() & FLG_ITALIC)) ) { nAttributes &= ~nAttrib; } SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->ClearAttribut(nAttrib); } void SmNode::SetFont(const SmFace &rFace) { if (!(Flags() & FLG_FONT)) GetFont() = rFace; SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->SetFont(rFace); } void SmNode::SetFontSize(const Fraction &rSize, USHORT nType) //! 'rSize' is in units of pts { Size aFntSize; if (!(Flags() & FLG_SIZE)) { Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()), rSize.GetDenominator()); //long nHeight = ::rtl::math::round(aVal); long nHeight = (long)aVal; aFntSize = GetFont().GetSize(); aFntSize.Width() = 0; switch(nType) { case FNTSIZ_ABSOLUT: aFntSize.Height() = nHeight; break; case FNTSIZ_PLUS: aFntSize.Height() += nHeight; break; case FNTSIZ_MINUS: aFntSize.Height() -= nHeight; break; case FNTSIZ_MULTIPLY: aFntSize.Height() = (long) (Fraction(aFntSize.Height()) * rSize); break; case FNTSIZ_DIVIDE: if (rSize != Fraction(0L)) aFntSize.Height() = (long) (Fraction(aFntSize.Height()) / rSize); break; default: break; } // check the requested size against maximum value static int __READONLY_DATA nMaxVal = SmPtsTo100th_mm(128); if (aFntSize.Height() > nMaxVal) aFntSize.Height() = nMaxVal; GetFont().SetSize(aFntSize); } SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->SetFontSize(rSize, nType); } void SmNode::SetSize(const Fraction &rSize) { GetFont() *= rSize; SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->SetSize(rSize); } void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, BOOL bApplyToSubTree ) { if (!(Flags() & FLG_HORALIGN)) eRectHorAlign = eHorAlign; if (bApplyToSubTree) { SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->SetRectHorAlign(eHorAlign); } } void SmNode::PrepareAttributes() { GetFont().SetWeight((Attributes() & ATTR_BOLD) ? WEIGHT_BOLD : WEIGHT_NORMAL); GetFont().SetItalic((Attributes() & ATTR_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE); } void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { #if OSL_DEBUG_LEVEL > 1 bIsDebug = TRUE; #else bIsDebug = FALSE; #endif bIsPhantom = FALSE; nFlags = 0; nAttributes = 0; switch (rFormat.GetHorAlign()) { case AlignLeft: eRectHorAlign = RHA_LEFT; break; case AlignCenter: eRectHorAlign = RHA_CENTER; break; case AlignRight: eRectHorAlign = RHA_RIGHT; break; } GetFont() = rFormat.GetFont(FNT_MATH); //GetFont().SetCharSet(RTL_TEXTENCODING_SYMBOL); DBG_ASSERT( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE, "unexpected CharSet" ); GetFont().SetWeight(WEIGHT_NORMAL); GetFont().SetItalic(ITALIC_NONE); SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->Prepare(rFormat, rDocShell); } #if OSL_DEBUG_LEVEL > 1 void SmNode::ToggleDebug() const // toggle 'bIsDebug' in current subtree { SmNode *pThis = (SmNode *) this; pThis->bIsDebug = bIsDebug ? FALSE : TRUE; SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = pThis->GetSubNode(i))) pNode->ToggleDebug(); } #endif void SmNode::Move(const Point& rPosition) { if (rPosition.X() == 0 && rPosition.Y() == 0) return; SmRect::Move(rPosition); SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->Move(rPosition); } void SmNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->Arrange(rDev, rFormat); } void SmNode::CreateTextFromNode(String &rText) { SmNode *pNode; USHORT nSize = GetNumSubNodes(); if (nSize > 1) rText.Append('{'); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->CreateTextFromNode(rText); if (nSize > 1) { rText.EraseTrailingChars(); APPEND(rText,"} "); } } void SmNode::AdaptToX(const OutputDevice &/*rDev*/, ULONG /*nWidth*/) { } void SmNode::AdaptToY(const OutputDevice &/*rDev*/, ULONG /*nHeight*/) { } void SmNode::Draw(OutputDevice &rDev, const Point &rPosition) const { if (IsPhantom()) return; const SmNode *pNode; USHORT nSize = GetNumSubNodes(); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) { Point aOffset (pNode->GetTopLeft() - GetTopLeft()); pNode->Draw(rDev, rPosition + aOffset); } #ifdef SM_RECT_DEBUG if (!IsDebug()) return; int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; SmRect::Draw(rDev, rPosition, nRFlags); #endif } const SmNode * SmNode::FindTokenAt(USHORT nRow, USHORT 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 == GetToken().nRow && nCol >= GetToken().nCol && nCol < GetToken().nCol + GetToken().aText.Len()) return this; else { USHORT nNumSubNodes = GetNumSubNodes(); for (USHORT 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 0; } const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const { long nDist = LONG_MAX; const SmNode *pResult = 0; if (IsVisible()) pResult = this; else { USHORT nNumSubNodes = GetNumSubNodes(); for (USHORT i = 0; i < nNumSubNodes; i++) { const SmNode *pNode = GetSubNode(i); if (!pNode) continue; long nTmp; const SmNode *pFound = pNode->FindRectClosestTo(rPoint); if (pFound && (nTmp = pFound->OrientedDist(rPoint)) < 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; } void SmNode::GetAccessibleText( String &/*rText*/ ) const { DBG_ERROR( "SmNode: GetAccessibleText not overloaded" ); } const SmNode * SmNode::FindNodeWithAccessibleIndex(xub_StrLen nAccIdx) const { const SmNode *pResult = 0; sal_Int32 nIdx = GetAccessibleIndex(); String aTxt; if (nIdx >= 0) GetAccessibleText( aTxt ); // get text if used in following 'if' statement if (nIdx >= 0 && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.Len()) pResult = this; else { USHORT nNumSubNodes = GetNumSubNodes(); for (USHORT 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( const SmStructureNode &rNode ) : SmNode( rNode.GetType(), rNode.GetToken() ) { ULONG i; for (i = 0; i < aSubNodes.size(); i++) delete aSubNodes[i]; aSubNodes.resize(0); ULONG nSize = rNode.aSubNodes.size(); aSubNodes.resize( nSize ); for (i = 0; i < nSize; ++i) { SmNode *pNode = rNode.aSubNodes[i]; aSubNodes[i] = pNode ? new SmNode( *pNode ) : 0; } } SmStructureNode::~SmStructureNode() { SmNode *pNode; for (USHORT i = 0; i < GetNumSubNodes(); i++) if (NULL != (pNode = GetSubNode(i))) delete pNode; } SmStructureNode & SmStructureNode::operator = ( const SmStructureNode &rNode ) { SmNode::operator = ( rNode ); ULONG i; for (i = 0; i < aSubNodes.size(); i++) delete aSubNodes[i]; aSubNodes.resize(0); ULONG nSize = rNode.aSubNodes.size(); aSubNodes.resize( nSize ); for (i = 0; i < nSize; ++i) { SmNode *pNode = rNode.aSubNodes[i]; aSubNodes[i] = pNode ? new SmNode( *pNode ) : 0; } return *this; } void SmStructureNode::SetSubNodes(SmNode *pFirst, SmNode *pSecond, SmNode *pThird) { size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0)); aSubNodes.resize( nSize ); if (pFirst) aSubNodes[0] = pFirst; if (pSecond) aSubNodes[1] = pSecond; if (pThird) aSubNodes[2] = pThird; } void SmStructureNode::SetSubNodes(const SmNodeArray &rNodeArray) { aSubNodes = rNodeArray; } BOOL SmStructureNode::IsVisible() const { return FALSE; } USHORT SmStructureNode::GetNumSubNodes() const { return (USHORT) aSubNodes.size(); } SmNode * SmStructureNode::GetSubNode(USHORT nIndex) { return aSubNodes[nIndex]; } void SmStructureNode::GetAccessibleText( String &rText ) const { USHORT nNodes = GetNumSubNodes(); for (USHORT i = 0; i < nNodes; ++i) { const SmNode *pNode = ((SmStructureNode *) this)->GetSubNode(i); if (pNode) { if (pNode->IsVisible()) ((SmStructureNode *) pNode)->nAccIndex = rText.Len(); pNode->GetAccessibleText( rText ); // if (rText.Len() && ' ' != rText.GetChar( rText.Len() - 1 )) // rText += String::CreateFromAscii( " " ); } } } /////////////////////////////////////////////////////////////////////////// BOOL SmVisibleNode::IsVisible() const { return TRUE; } USHORT SmVisibleNode::GetNumSubNodes() const { return 0; } SmNode * SmVisibleNode::GetSubNode(USHORT /*nIndex*/) { return NULL; } /////////////////////////////////////////////////////////////////////////// void SmGraphicNode::GetAccessibleText( String &rText ) const { rText += GetToken().aText; } /////////////////////////////////////////////////////////////////////////// void SmExpressionNode::CreateTextFromNode(String &rText) { SmNode *pNode; USHORT nSize = GetNumSubNodes(); if (nSize > 1) rText.Append('{'); for (USHORT i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) { pNode->CreateTextFromNode(rText); //Just a bit of foo to make unary +asd -asd +-asd -+asd look nice if (pNode->GetType() == NMATH) if ((nSize != 2) || ((rText.GetChar(rText.Len()-1) != '+') && (rText.GetChar(rText.Len()-1) != '-'))) rText.Append(' '); } if (nSize > 1) { rText.EraseTrailingChars(); APPEND(rText,"} "); } } /////////////////////////////////////////////////////////////////////////// void SmTableNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) // arranges all subnodes in one column { Point rPosition; SmNode *pNode; USHORT nSize = GetNumSubNodes(); // make distance depend on font size long nDist = +(rFormat.GetDistance(DIS_VERTICAL) * GetFont().GetSize().Height()) / 100L; if (nSize < 1) return; // arrange subnodes and get maximum width of them long nMaxWidth = 0, nTmp; USHORT i; for (i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) { pNode->Arrange(rDev, rFormat); if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth) nMaxWidth = nTmp; } Point aPos; SmRect::operator = (SmRect(nMaxWidth, 0)); for (i = 0; i < nSize; i++) { if (NULL != (pNode = GetSubNode(i))) { const SmRect &rNodeRect = pNode->GetRect(); const SmNode *pCoNode = pNode->GetLeftMost(); //SmTokenType eType = pCoNode->GetToken().eType; RectHorAlign eHorAlign = pCoNode->GetRectHorAlign(); aPos = rNodeRect.AlignTo(*this, RP_BOTTOM, eHorAlign, RVA_BASELINE); if (i) aPos.Y() += nDist; pNode->MoveTo(aPos); ExtendBy(rNodeRect, nSize > 1 ? RCP_NONE : RCP_ARG); } } } SmNode * SmTableNode::GetLeftMost() { return this; } /**************************************************************************/ void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { SmNode::Prepare(rFormat, rDocShell); //! wir verwenden hier den 'FNT_VARIABLE' Font, da er vom Ascent und Descent //! ia besser zum Rest der Formel passt als der 'FNT_MATH' Font. GetFont() = rFormat.GetFont(FNT_VARIABLE); Flags() |= FLG_FONT; } /**************************************************************************/ void SmLineNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) // arranges all subnodes in one row with some extra space between { SmNode *pNode; USHORT nSize = GetNumSubNodes(); USHORT i; for (i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) pNode->Arrange(rDev, rFormat); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); // 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, C2S("a"), GetFont().GetBorderWidth())); // make sure that the rectangle occupies (almost) no space SetWidth(1); SetItalicSpaces(0, 0); if (nSize < 1) return; // make distance depend on font size long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetSize().Height()) / 100L; if (!IsUseExtraSpaces()) nDist = 0; Point aPos; for (i = 0; i < nSize; i++) if (NULL != (pNode = GetSubNode(i))) { aPos = pNode->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE); // no horizontal space before first node if (i) aPos.X() += nDist; pNode->MoveTo(aPos); ExtendBy( *pNode, RCP_XOR ); } } /**************************************************************************/ void SmExpressionNode::Arrange(const 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 SmNode *pNode = GetLeftMost(); if (pNode) SetRectHorAlign(pNode->GetRectHorAlign(), FALSE); } /**************************************************************************/ void SmUnHorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { BOOL bIsPostfix = GetToken().eType == TFACT; SmNode *pOper = GetSubNode(bIsPostfix ? 1 : 0), *pBody = GetSubNode(bIsPostfix ? 0 : 1); DBG_ASSERT(pOper, "Sm: NULL pointer"); DBG_ASSERT(pBody, "Sm: NULL pointer"); pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100)); pOper->Arrange(rDev, rFormat); pBody->Arrange(rDev, rFormat); Point aPos = pOper->AlignTo(*pBody, bIsPostfix ? RP_RIGHT : RP_LEFT, RHA_CENTER, RVA_BASELINE); // add a bit space between operator and argument // (worst case -{1 over 2} where - and over have almost no space inbetween) long nDelta = pOper->GetFont().GetSize().Height() / 20; if (bIsPostfix) aPos.X() += nDelta; else aPos.X() -= nDelta; pOper->MoveTo(aPos); SmRect::operator = (*pBody); long nOldBot = GetBottom(); ExtendBy(*pOper, RCP_XOR); // workaround for Bug 50865: "a^2 a^+2" have different baselines // for exponents (if size of exponent is large enough) SetBottom(nOldBot); } /**************************************************************************/ void SmRootNode::GetHeightVerOffset(const SmRect &rRect, long &rHeight, long &rVerOffset) const // calculate height and vertical offset of root sign suitable for 'rRect' { rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2; rHeight = rRect.GetHeight() - rVerOffset; DBG_ASSERT(rHeight >= 0, "Sm : Ooops..."); DBG_ASSERT(rVerOffset >= 0, "Sm : Ooops..."); } Point SmRootNode::GetExtraPos(const SmRect &rRootSymbol, const SmRect &rExtra) const { 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.X() -= rExtra.GetWidth() + rExtra.GetItalicRightSpace(); aPos.Y() -= 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) long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100; if (aPos.X() > nX) aPos.X() = nX; return aPos; } void SmRootNode::Arrange(const 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); DBG_ASSERT(pRootSym, "Sm: NULL pointer"); DBG_ASSERT(pBody, "Sm: NULL pointer"); pBody->Arrange(rDev, rFormat); long nHeight, nVerOffset; GetHeightVerOffset(*pBody, nHeight, nVerOffset); nHeight += rFormat.GetDistance(DIS_ROOT) * GetFont().GetSize().Height() / 100L; // font specialist advised to change the width first pRootSym->AdaptToY(rDev, nHeight); pRootSym->AdaptToX(rDev, pBody->GetItalicWidth()); pRootSym->Arrange(rDev, rFormat); Point aPos = pRootSym->AlignTo(*pBody, RP_LEFT, RHA_CENTER, RVA_BASELINE); //! overrride calulated vertical position aPos.Y() = pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom(); aPos.Y() -= nVerOffset; pRootSym->MoveTo(aPos); if (pExtra) { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100)); pExtra->Arrange(rDev, rFormat); aPos = GetExtraPos(*pRootSym, *pExtra); pExtra->MoveTo(aPos); } SmRect::operator = (*pBody); ExtendBy(*pRootSym, RCP_THIS); if (pExtra) ExtendBy(*pExtra, RCP_THIS, (BOOL) TRUE); } void SmRootNode::CreateTextFromNode(String &rText) { SmNode *pExtra = GetSubNode(0); if (pExtra) { APPEND(rText,"nroot "); pExtra->CreateTextFromNode(rText); } else APPEND(rText,"sqrt "); GetSubNode(2)->CreateTextFromNode(rText); } /**************************************************************************/ void SmBinHorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmNode *pLeft = GetSubNode(0), *pOper = GetSubNode(1), *pRight = GetSubNode(2); DBG_ASSERT(pLeft != NULL, "Sm: NULL pointer"); DBG_ASSERT(pOper != NULL, "Sm: NULL pointer"); DBG_ASSERT(pRight != NULL, "Sm: NULL pointer"); 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(); long nDist = (rOpRect.GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100L; SmRect::operator = (*pLeft); Point aPos; aPos = pOper->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE); aPos.X() += nDist; pOper->MoveTo(aPos); ExtendBy(*pOper, RCP_XOR); aPos = pRight->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE); aPos.X() += nDist; pRight->MoveTo(aPos); ExtendBy(*pRight, RCP_XOR); } /**************************************************************************/ void SmBinVerNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmNode *pNum = GetSubNode(0), *pLine = GetSubNode(1), *pDenom = GetSubNode(2); DBG_ASSERT(pNum, "Sm : NULL pointer"); DBG_ASSERT(pLine, "Sm : NULL pointer"); DBG_ASSERT(pDenom, "Sm : NULL pointer"); 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); long nFontHeight = GetFont().GetSize().Height(), nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100L, nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L, nWidth = Max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()), nNumDist = bIsTextmode ? 0 : nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100L, nDenomDist = bIsTextmode ? 0 : nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100L; // 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, RP_TOP, eHorAlign, RVA_BASELINE); aPos.Y() -= nNumDist; pNum->MoveTo(aPos); // get horizontal alignment for denominator pLM = pDenom->GetLeftMost(); eHorAlign = pLM->GetRectHorAlign(); // move denominator to its position aPos = pDenom->AlignTo(*pLine, RP_BOTTOM, eHorAlign, RVA_BASELINE); aPos.Y() += nDenomDist; pDenom->MoveTo(aPos); SmRect::operator = (*pNum); ExtendBy(*pDenom, RCP_NONE).ExtendBy(*pLine, RCP_NONE, pLine->GetCenterY()); } void SmBinVerNode::CreateTextFromNode(String &rText) { SmNode *pNum = GetSubNode(0), // *pLine = GetSubNode(1), *pDenom = GetSubNode(2); pNum->CreateTextFromNode(rText); APPEND(rText,"over "); pDenom->CreateTextFromNode(rText); } SmNode * SmBinVerNode::GetLeftMost() { return this; } /**************************************************************************/ double Det(const Point &rHeading1, const Point &rHeading2) // gibt den Wert der durch die beiden Punkte gebildeten Determinante // zurueck { return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X(); } BOOL IsPointInLine(const Point &rPoint1, const Point &rPoint2, const Point &rHeading2) // ergibt TRUE genau dann, wenn der Punkt 'rPoint1' zu der Gerade gehoert die // durch den Punkt 'rPoint2' geht und den Richtungsvektor 'rHeading2' hat { DBG_ASSERT(rHeading2 != Point(), "Sm : 0 vector"); BOOL bRes = FALSE; const double eps = 5.0 * DBL_EPSILON; double fLambda; if (labs(rHeading2.X()) > labs(rHeading2.Y())) { fLambda = (rPoint1.X() - rPoint2.X()) / (double) rHeading2.X(); bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps; } else { fLambda = (rPoint1.Y() - rPoint2.Y()) / (double) rHeading2.Y(); bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps; } return bRes; } USHORT GetLineIntersectionPoint(Point &rResult, const Point& rPoint1, const Point &rHeading1, const Point& rPoint2, const Point &rHeading2) { DBG_ASSERT(rHeading1 != Point(), "Sm : 0 vector"); DBG_ASSERT(rHeading2 != Point(), "Sm : 0 vector"); USHORT nRes = 1; const double eps = 5.0 * DBL_EPSILON; // sind die Richtumgsvektoren linear abhaengig ? double fDet = Det(rHeading1, rHeading2); if (fabs(fDet) < eps) { nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0; rResult = nRes ? rPoint1 : Point(); } else { // hier achten wir nicht auf Rechengenauigkeit // (das wuerde aufwendiger und lohnt sich hier kaum) double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X() - (rPoint1.X() - rPoint2.X()) * rHeading2.Y()) / fDet; rResult = Point(rPoint1.X() + (long) (fLambda * rHeading1.X()), rPoint1.Y() + (long) (fLambda * rHeading1.Y())); } return nRes; } SmBinDiagonalNode::SmBinDiagonalNode(const SmToken &rNodeToken) : SmStructureNode(NBINDIAGONAL, rNodeToken) { bAscending = FALSE; SetNumSubNodes(3); } void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize, const Point &rDiagPoint, double fAngleDeg) const // gibt die Position und Groesse fuer den Diagonalstrich zurueck. // Vor.: das SmRect des Nodes gibt die Begrenzung vor(!), muss also selbst // bereits bekannt sein. { const double fPi = 3.1415926535897932384626433; double fAngleRad = fAngleDeg / 180.0 * fPi; long nRectLeft = GetItalicLeft(), nRectRight = GetItalicRight(), nRectTop = GetTop(), nRectBottom = GetBottom(); Point aRightHdg (100, 0), aDownHdg (0, 100), aDiagHdg ( (long)(100.0 * cos(fAngleRad)), (long)(-100.0 * sin(fAngleRad)) ); long nLeft, nRight, nTop, nBottom; // Raender des Rechtecks fuer die // Diagonale Point aPoint; if (IsAscending()) { // // obere rechte Ecke bestimmen // GetLineIntersectionPoint(aPoint, Point(nRectLeft, nRectTop), aRightHdg, rDiagPoint, aDiagHdg); // // gibt es einen Schnittpunkt mit dem oberen Rand ? if (aPoint.X() <= nRectRight) { nRight = aPoint.X(); nTop = nRectTop; } else { // es muss einen Schnittpunkt mit dem rechten Rand geben! GetLineIntersectionPoint(aPoint, Point(nRectRight, nRectTop), aDownHdg, rDiagPoint, aDiagHdg); nRight = nRectRight; nTop = aPoint.Y(); } // // untere linke Ecke bestimmen // GetLineIntersectionPoint(aPoint, Point(nRectLeft, nRectBottom), aRightHdg, rDiagPoint, aDiagHdg); // // gibt es einen Schnittpunkt mit dem unteren Rand ? if (aPoint.X() >= nRectLeft) { nLeft = aPoint.X(); nBottom = nRectBottom; } else { // es muss einen Schnittpunkt mit dem linken Rand geben! GetLineIntersectionPoint(aPoint, Point(nRectLeft, nRectTop), aDownHdg, rDiagPoint, aDiagHdg); nLeft = nRectLeft; nBottom = aPoint.Y(); } } else { // // obere linke Ecke bestimmen // GetLineIntersectionPoint(aPoint, Point(nRectLeft, nRectTop), aRightHdg, rDiagPoint, aDiagHdg); // // gibt es einen Schnittpunkt mit dem oberen Rand ? if (aPoint.X() >= nRectLeft) { nLeft = aPoint.X(); nTop = nRectTop; } else { // es muss einen Schnittpunkt mit dem linken Rand geben! GetLineIntersectionPoint(aPoint, Point(nRectLeft, nRectTop), aDownHdg, rDiagPoint, aDiagHdg); nLeft = nRectLeft; nTop = aPoint.Y(); } // // untere rechte Ecke bestimmen // GetLineIntersectionPoint(aPoint, Point(nRectLeft, nRectBottom), aRightHdg, rDiagPoint, aDiagHdg); // // gibt es einen Schnittpunkt mit dem unteren Rand ? if (aPoint.X() <= nRectRight) { nRight = aPoint.X(); nBottom = nRectBottom; } else { // es muss einen Schnittpunkt mit dem rechten Rand geben! GetLineIntersectionPoint(aPoint, Point(nRectRight, nRectTop), aDownHdg, rDiagPoint, aDiagHdg); nRight = nRectRight; nBottom = aPoint.Y(); } } rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1); rPos.X() = nLeft; rPos.Y() = nTop; } void SmBinDiagonalNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { //! die beiden Argumente muessen in den Subnodes vor dem Operator kommen, //! damit das anklicken im GraphicWindow den FormulaCursor richtig setzt //! (vgl SmRootNode) SmNode *pLeft = GetSubNode(0), *pRight = GetSubNode(1); DBG_ASSERT(pLeft, "Sm : NULL pointer"); DBG_ASSERT(pRight, "Sm : NULL pointer"); DBG_ASSERT(GetSubNode(2)->GetType() == NPOLYLINE, "Sm : falscher Nodetyp"); SmPolyLineNode *pOper = (SmPolyLineNode *) GetSubNode(2); DBG_ASSERT(pOper, "Sm : NULL pointer"); //! 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 ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); pLeft->Arrange(aTmpDev, rFormat); pRight->Arrange(aTmpDev, rFormat); // implizit die Weite (incl Rand) des Diagonalstrichs ermitteln pOper->Arrange(aTmpDev, rFormat); long nDelta = pOper->GetWidth() * 8 / 10; // TopLeft Position vom rechten Argument ermitteln Point aPos; aPos.X() = pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace(); if (IsAscending()) aPos.Y() = pLeft->GetBottom() + nDelta; else aPos.Y() = pLeft->GetTop() - nDelta - pRight->GetHeight(); pRight->MoveTo(aPos); // neue Baseline bestimmen 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, RCP_NONE); // Position und Groesse des Diagonalstrich ermitteln 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()); // und diese wirksam machen pOper->Arrange(aTmpDev, rFormat); pOper->MoveTo(aPos); ExtendBy(*pOper, RCP_NONE, nTmpBaseline); } /**************************************************************************/ void SmSubSupNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { DBG_ASSERT(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES, "Sm: falsche Anzahl von subnodes"); SmNode *pBody = GetBody(); DBG_ASSERT(pBody, "Sm: NULL pointer"); long nOrigHeight = pBody->GetFont().GetSize().Height(); pBody->Arrange(rDev, rFormat); const SmRect &rBodyRect = pBody->GetRect(); SmRect::operator = (rBodyRect); // line that separates sub- and supscript rectangles long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4); Point aPos; long nDelta, nDist; // iterate over all possible sub-/supscripts SmRect aTmpRect (rBodyRect); for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++) { SmSubSup eSubSup = (SmSubSup) i; // cast SmNode *pSubSup = GetSubSup(eSubSup); if (!pSubSup) continue; // switch position of limits if we are in textmode if (rFormat.IsTextmode() && (GetToken().nGroup & TGLIMIT)) 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().GetSize().Height() > rFormat.GetBaseSize().Height() / 3) { USHORT 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) / 100L; aPos = pSubSup->GetRect().AlignTo(aTmpRect, eSubSup == LSUB ? RP_LEFT : RP_RIGHT, RHA_CENTER, RVA_BOTTOM); aPos.Y() += nDist; nDelta = nDelimLine - aPos.Y(); if (nDelta > 0) aPos.Y() += nDelta; break; case RSUP : case LSUP : if (!bIsTextmode) nDist = nOrigHeight * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100L; aPos = pSubSup->GetRect().AlignTo(aTmpRect, eSubSup == LSUP ? RP_LEFT : RP_RIGHT, RHA_CENTER, RVA_TOP); aPos.Y() -= nDist; nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine; if (nDelta > 0) aPos.Y() -= nDelta; break; case CSUB : if (!bIsTextmode) nDist = nOrigHeight * rFormat.GetDistance(DIS_LOWERLIMIT) / 100L; aPos = pSubSup->GetRect().AlignTo(rBodyRect, RP_BOTTOM, RHA_CENTER, RVA_BASELINE); aPos.Y() += nDist; break; case CSUP : if (!bIsTextmode) nDist = nOrigHeight * rFormat.GetDistance(DIS_UPPERLIMIT) / 100L; aPos = pSubSup->GetRect().AlignTo(rBodyRect, RP_TOP, RHA_CENTER, RVA_BASELINE); aPos.Y() -= nDist; break; default : DBG_ASSERT(FALSE, "Sm: unbekannter Fall"); break; } pSubSup->MoveTo(aPos); ExtendBy(*pSubSup, RCP_THIS, (BOOL) TRUE); // update rectangle to which RSUB, RSUP, LSUB, LSUP // will be aligned to if (eSubSup == CSUB || eSubSup == CSUP) aTmpRect = *this; } } void SmSubSupNode::CreateTextFromNode(String &rText) { SmNode *pNode; GetSubNode(0)->CreateTextFromNode(rText); if (NULL != (pNode = GetSubNode(LSUB+1))) { APPEND(rText,"lsub "); pNode->CreateTextFromNode(rText); } if (NULL != (pNode = GetSubNode(LSUP+1))) { APPEND(rText,"lsup "); pNode->CreateTextFromNode(rText); } if (NULL != (pNode = GetSubNode(CSUB+1))) { APPEND(rText,"csub "); pNode->CreateTextFromNode(rText); } if (NULL != (pNode = GetSubNode(CSUP+1))) { APPEND(rText,"csup "); pNode->CreateTextFromNode(rText); } if (NULL != (pNode = GetSubNode(RSUB+1))) { rText.EraseTrailingChars(); rText.Append('_'); pNode->CreateTextFromNode(rText); } if (NULL != (pNode = GetSubNode(RSUP+1))) { rText.EraseTrailingChars(); rText.Append('^'); pNode->CreateTextFromNode(rText); } } /**************************************************************************/ void SmBraceNode::CreateTextFromNode(String &rText) { if (GetScaleMode() == SCALE_HEIGHT) APPEND(rText,"left "); { String aStr; GetSubNode(0)->CreateTextFromNode(aStr); aStr.EraseLeadingAndTrailingChars(); aStr.EraseLeadingChars('\\'); if (aStr.Len()) { if (aStr.EqualsAscii("divides")) APPEND(rText,"lline"); else if (aStr.EqualsAscii("parallel")) APPEND(rText,"ldline"); else if (aStr.EqualsAscii("<")) APPEND(rText,"langle"); else rText.Append(aStr); rText.Append(' '); } else APPEND(rText,"none "); } GetSubNode(1)->CreateTextFromNode(rText); if (GetScaleMode() == SCALE_HEIGHT) APPEND(rText,"right "); { String aStr; GetSubNode(2)->CreateTextFromNode(aStr); aStr.EraseLeadingAndTrailingChars(); aStr.EraseLeadingChars('\\'); if (aStr.Len()) { if (aStr.EqualsAscii("divides")) APPEND(rText,"rline"); else if (aStr.EqualsAscii("parallel")) APPEND(rText,"rdline"); else if (aStr.EqualsAscii(">")) APPEND(rText,"rangle"); else rText.Append(aStr); rText.Append(' '); } else APPEND(rText,"none "); } rText.Append(' '); } void SmBraceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmNode *pLeft = GetSubNode(0), *pBody = GetSubNode(1), *pRight = GetSubNode(2); DBG_ASSERT(pLeft, "Sm: NULL pointer"); DBG_ASSERT(pBody, "Sm: NULL pointer"); DBG_ASSERT(pRight, "Sm: NULL pointer"); pBody->Arrange(rDev, rFormat); BOOL bIsScaleNormal = rFormat.IsScaleNormalBrackets(), bScale = pBody->GetHeight() > 0 && (GetScaleMode() == SCALE_HEIGHT || bIsScaleNormal), bIsABS = GetToken().eType == TABS; long nFaceHeight = GetFont().GetSize().Height(); // Uebergroesse in % ermitteln USHORT nPerc = 0; if (!bIsABS && bScale) { // im Fall von Klammern mit Uebergroesse... USHORT nIndex = GetScaleMode() == SCALE_HEIGHT ? DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE; nPerc = rFormat.GetDistance(nIndex); } // ermitteln der Hoehe fuer die Klammern long nBraceHeight; if (bScale) { nBraceHeight = pBody->GetType() == NBRACEBODY ? ((SmBracebodyNode *) pBody)->GetBodyHeight() : pBody->GetHeight(); nBraceHeight += 2 * (nBraceHeight * nPerc / 100L); } else nBraceHeight = nFaceHeight; // Abstand zum Argument nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE); long nDist = nFaceHeight * nPerc / 100L; // sofern erwuenscht skalieren der Klammern auf die gewuenschte Groesse if (bScale) { Size aTmpSize (pLeft->GetFont().GetSize()); DBG_ASSERT(pRight->GetFont().GetSize() == aTmpSize, "Sm : unterschiedliche Fontgroessen"); aTmpSize.Width() = Min((long) nBraceHeight * 60L / 100L, rFormat.GetBaseSize().Height() * 3L / 2L); // correction factor since change from StarMath to OpenSymbol font // because of the different font width in the FontMetric aTmpSize.Width() *= 182; aTmpSize.Width() /= 267; xub_Unicode cChar = pLeft->GetToken().cMathChar; if (cChar != MS_LINE && cChar != MS_DLINE) pLeft ->GetFont().SetSize(aTmpSize); cChar = pRight->GetToken().cMathChar; if (cChar != MS_LINE && cChar != MS_DLINE) pRight->GetFont().SetSize(aTmpSize); pLeft ->AdaptToY(rDev, nBraceHeight); pRight->AdaptToY(rDev, nBraceHeight); } pLeft ->Arrange(rDev, rFormat); pRight->Arrange(rDev, rFormat); // damit auch "\(a\) - (a) - left ( a right )" vernuenftig aussieht RectVerAlign eVerAlign = bScale ? RVA_CENTERY : RVA_BASELINE; Point aPos; aPos = pLeft->AlignTo(*pBody, RP_LEFT, RHA_CENTER, eVerAlign); aPos.X() -= nDist; pLeft->MoveTo(aPos); aPos = pRight->AlignTo(*pBody, RP_RIGHT, RHA_CENTER, eVerAlign); aPos.X() += nDist; pRight->MoveTo(aPos); SmRect::operator = (*pBody); ExtendBy(*pLeft, RCP_THIS).ExtendBy(*pRight, RCP_THIS); } /**************************************************************************/ void SmBracebodyNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { USHORT nNumSubNodes = GetNumSubNodes(); if (nNumSubNodes == 0) return; // arrange arguments USHORT i; for (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 (i = 0; i < nNumSubNodes; i += 2) { SmRect aTmpRect (*GetSubNode(i)); Point aPos = aTmpRect.AlignTo(aRefRect, RP_RIGHT, RHA_CENTER, RVA_BASELINE); aTmpRect.MoveTo(aPos); aRefRect.ExtendBy(aTmpRect, RCP_XOR); } nBodyHeight = aRefRect.GetHeight(); // scale separators to required height and arrange them BOOL bScale = GetScaleMode() == SCALE_HEIGHT || rFormat.IsScaleNormalBrackets(); long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetSize().Height(); USHORT nIndex = GetScaleMode() == SCALE_HEIGHT ? DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE; USHORT nPerc = rFormat.GetDistance(nIndex); if (bScale) nHeight += 2 * (nHeight * nPerc / 100L); for (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 long nDist = GetFont().GetSize().Height() * rFormat.GetDistance(DIS_BRACKETSPACE) / 100L; SmNode *pLeft = GetSubNode(0); SmRect::operator = (*pLeft); for (i = 1; i < nNumSubNodes; i++) { BOOL bIsSeparator = i % 2 != 0; RectVerAlign eVerAlign = bIsSeparator ? RVA_CENTERY : RVA_BASELINE; SmNode *pRight = GetSubNode(i); Point aPosX = pRight->AlignTo(*pLeft, RP_RIGHT, RHA_CENTER, eVerAlign), aPosY = pRight->AlignTo(aRefRect, RP_RIGHT, RHA_CENTER, eVerAlign); aPosX.X() += nDist; pRight->MoveTo(Point(aPosX.X(), aPosY.Y())); ExtendBy(*pRight, bIsSeparator ? RCP_THIS : RCP_XOR); pLeft = pRight; } } /**************************************************************************/ void SmVerticalBraceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmNode *pBody = GetSubNode(0), *pBrace = GetSubNode(1), *pScript = GetSubNode(2); DBG_ASSERT(pBody, "Sm: NULL pointer!"); DBG_ASSERT(pBrace, "Sm: NULL pointer!"); DBG_ASSERT(pScript, "Sm: NULL pointer!"); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); pBody->Arrange(aTmpDev, rFormat); // Groesse wie bei Grenzen fuer diesen Teil pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) ); // etwas hoehere Klammern als normal pBrace ->SetSize( Fraction(3, 2) ); long nItalicWidth = pBody->GetItalicWidth(); if (nItalicWidth > 0) pBrace->AdaptToX(aTmpDev, nItalicWidth); pBrace ->Arrange(aTmpDev, rFormat); pScript->Arrange(aTmpDev, rFormat); // die relativen Position und die Abstaende zueinander bestimmen RectPos eRectPos; long nFontHeight = pBody->GetFont().GetSize().Height(); long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE), nDistScript = nFontHeight; if (GetToken().eType == TOVERBRACE) { eRectPos = RP_TOP; nDistBody = - nDistBody; nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT); } else // TUNDERBRACE { eRectPos = RP_BOTTOM; nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT); } nDistBody /= 100L; nDistScript /= 100L; Point aPos = pBrace->AlignTo(*pBody, eRectPos, RHA_CENTER, RVA_BASELINE); aPos.Y() += nDistBody; pBrace->MoveTo(aPos); aPos = pScript->AlignTo(*pBrace, eRectPos, RHA_CENTER, RVA_BASELINE); aPos.Y() += nDistScript; pScript->MoveTo(aPos); SmRect::operator = (*pBody); ExtendBy(*pBrace, RCP_THIS).ExtendBy(*pScript, RCP_THIS); } /**************************************************************************/ SmNode * SmOperNode::GetSymbol() { SmNode *pNode = GetSubNode(0); DBG_ASSERT(pNode, "Sm: NULL pointer!"); if (pNode->GetType() == NSUBSUP) pNode = ((SmSubSupNode *) pNode)->GetBody(); DBG_ASSERT(pNode, "Sm: NULL pointer!"); return pNode; } long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol, const SmFormat &rFormat) const // returns the font height to be used for operator-symbol { long nHeight = GetFont().GetSize().Height(); SmTokenType eTmpType = GetToken().eType; if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP) return nHeight; if (!rFormat.IsTextmode()) { // set minimum size () nHeight += (nHeight * 20L) / 100L; nHeight += nHeight * rFormat.GetDistance(DIS_OPERATORSIZE) / 100L; nHeight = nHeight * 686L / 845L; } // correct user-defined symbols to match height of sum from used font if (rSymbol.GetToken().eType == TSPECIAL) nHeight = nHeight * 845L / 686L; return nHeight; } void SmOperNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmNode *pOper = GetSubNode(0); SmNode *pBody = GetSubNode(1); DBG_ASSERT(pOper, "Sm: Subnode fehlt"); DBG_ASSERT(pBody, "Sm: Subnode fehlt"); SmNode *pSymbol = GetSymbol(); pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat), pSymbol->GetFont().GetSize().Height())); pBody->Arrange(rDev, rFormat); pOper->Arrange(rDev, rFormat); long nOrigHeight = GetFont().GetSize().Height(), nDist = nOrigHeight * rFormat.GetDistance(DIS_OPERATORSPACE) / 100L; Point aPos = pOper->AlignTo(*pBody, RP_LEFT, RHA_CENTER, /*RVA_CENTERY*/RVA_MID); aPos.X() -= nDist; pOper->MoveTo(aPos); SmRect::operator = (*pBody); ExtendBy(*pOper, RCP_THIS); } /**************************************************************************/ void SmAlignNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) // setzt im ganzen subtree (incl aktuellem node) das alignment { DBG_ASSERT(GetNumSubNodes() > 0, "Sm: SubNode fehlt"); SmNode *pNode = GetSubNode(0); RectHorAlign eHorAlign = RHA_CENTER; switch (GetToken().eType) { case TALIGNL: eHorAlign = RHA_LEFT; break; case TALIGNC: eHorAlign = RHA_CENTER; break; case TALIGNR: eHorAlign = RHA_RIGHT; break; default: break; } SetRectHorAlign(eHorAlign); pNode->Arrange(rDev, rFormat); SmRect::operator = (pNode->GetRect()); } /**************************************************************************/ void SmAttributNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmNode *pAttr = GetSubNode(0), *pBody = GetSubNode(1); DBG_ASSERT(pBody, "Sm: Body fehlt"); DBG_ASSERT(pAttr, "Sm: Attribut fehlt"); pBody->Arrange(rDev, rFormat); if (GetScaleMode() == SCALE_WIDTH) pAttr->AdaptToX(rDev, pBody->GetItalicWidth()); pAttr->Arrange(rDev, rFormat); // get relative position of attribut RectVerAlign eVerAlign; long nDist = 0; switch (GetToken().eType) { case TUNDERLINE : eVerAlign = RVA_ATTRIBUT_LO; break; case TOVERSTRIKE : eVerAlign = RVA_ATTRIBUT_MID; break; default : eVerAlign = RVA_ATTRIBUT_HI; if (pBody->GetType() == NATTRIBUT) nDist = GetFont().GetSize().Height() * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100L; } Point aPos = pAttr->AlignTo(*pBody, RP_ATTRIBUT, RHA_CENTER, eVerAlign); aPos.Y() -= nDist; pAttr->MoveTo(aPos); SmRect::operator = (*pBody); ExtendBy(*pAttr, RCP_THIS, (BOOL) TRUE); } /**************************************************************************/ void SmFontNode::CreateTextFromNode(String &rText) { switch (GetToken().eType) { case TBOLD: APPEND(rText,"bold "); break; case TNBOLD: APPEND(rText,"nbold "); break; case TITALIC: APPEND(rText,"italic "); break; case TNITALIC: APPEND(rText,"nitalic "); break; case TPHANTOM: APPEND(rText,"phantom "); break; case TSIZE: { APPEND(rText,"size "); switch (nSizeType) { case FNTSIZ_PLUS: rText.Append('+'); break; case FNTSIZ_MINUS: rText.Append('-'); break; case FNTSIZ_MULTIPLY: rText.Append('*'); break; case FNTSIZ_DIVIDE: rText.Append('/'); break; case FNTSIZ_ABSOLUT: default: break; } rText += String( ::rtl::math::doubleToUString( static_cast(aFontSize), rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', sal_True)); rText.Append(' '); } break; case TBLACK: APPEND(rText,"color black "); break; case TWHITE: APPEND(rText,"color white "); break; case TRED: APPEND(rText,"color red "); break; case TGREEN: APPEND(rText,"color green "); break; case TBLUE: APPEND(rText,"color blue "); break; case TCYAN: APPEND(rText,"color cyan "); break; case TMAGENTA: APPEND(rText,"color magenta "); break; case TYELLOW: APPEND(rText,"color yellow "); break; case TSANS: APPEND(rText,"font sans "); break; case TSERIF: APPEND(rText,"font serif "); break; case TFIXED: APPEND(rText,"font fixed "); break; default: break; } GetSubNode(1)->CreateTextFromNode(rText); } void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { //! prepare subnodes first SmNode::Prepare(rFormat, rDocShell); 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< USHORT >(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() |= FLG_FONT; } void SmFontNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmNode *pNode = GetSubNode(1); DBG_ASSERT(pNode, "Sm: SubNode fehlt"); switch (GetToken().eType) { case TSIZE : pNode->SetFontSize(aFontSize, nSizeType); 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 : SetAttribut(ATTR_BOLD); break; case TITALIC : SetAttribut(ATTR_ITALIC); break; case TNBOLD : ClearAttribut(ATTR_BOLD); break; case TNITALIC : ClearAttribut(ATTR_ITALIC); break; case TBLACK : SetColor(Color(COL_BLACK)); break; case TWHITE : SetColor(Color(COL_WHITE)); break; case TRED : SetColor(Color(COL_RED)); break; case TGREEN : SetColor(Color(COL_GREEN)); break; case TBLUE : SetColor(Color(COL_BLUE)); break; case TCYAN : SetColor(Color(COL_CYAN)); break; case TMAGENTA : SetColor(Color(COL_MAGENTA)); break; case TYELLOW : SetColor(Color(COL_YELLOW)); break; default: DBG_ASSERT(FALSE, "Sm: unbekannter Fall"); } pNode->Arrange(rDev, rFormat); SmRect::operator = (pNode->GetRect()); } void SmFontNode::SetSizeParameter(const Fraction& rValue, USHORT Type) { nSizeType = Type; aFontSize = rValue; } /**************************************************************************/ SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken) : SmGraphicNode(NPOLYLINE, rNodeToken) { aPoly.SetSize(2); nWidth = 0; } void SmPolyLineNode::AdaptToX(const OutputDevice &/*rDev*/, ULONG nNewWidth) { aToSize.Width() = nNewWidth; } void SmPolyLineNode::AdaptToY(const OutputDevice &/*rDev*/, ULONG nNewHeight) { GetFont().FreezeBorderWidth(); aToSize.Height() = nNewHeight; } void SmPolyLineNode::Arrange(const 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 ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); long nBorderwidth = GetFont().GetBorderWidth(); // // Das Polygon mit den beiden Endpunkten bilden // DBG_ASSERT(aPoly.GetSize() == 2, "Sm : falsche Anzahl von Punkten"); Point aPointA, aPointB; if (GetToken().eType == TWIDESLASH) { aPointA.X() = nBorderwidth; aPointA.Y() = aToSize.Height() - nBorderwidth; aPointB.X() = aToSize.Width() - nBorderwidth; aPointB.Y() = nBorderwidth; } else { DBG_ASSERT(GetToken().eType == TWIDEBACKSLASH, "Sm : unerwartetes Token"); aPointA.X() = aPointA.Y() = nBorderwidth; aPointB.X() = aToSize.Width() - nBorderwidth; aPointB.Y() = aToSize.Height() - nBorderwidth; } aPoly.SetPoint(aPointA, 0); aPoly.SetPoint(aPointB, 1); long nThick = GetFont().GetSize().Height() * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L; nWidth = nThick + 2 * nBorderwidth; SmRect::operator = (SmRect(aToSize.Width(), aToSize.Height())); } void SmPolyLineNode::Draw(OutputDevice &rDev, const Point &rPosition) const { if (IsPhantom()) return; long nBorderwidth = GetFont().GetBorderWidth(); LineInfo aInfo; aInfo.SetWidth(nWidth - 2 * nBorderwidth); Point aOffset (Point() - aPoly.GetBoundRect().TopLeft() + Point(nBorderwidth, nBorderwidth)), aPos (rPosition + aOffset); ((Polygon &) aPoly).Move(aPos.X(), aPos.Y()); SmTmpDevice aTmpDev ((OutputDevice &) rDev, FALSE); aTmpDev.SetLineColor( GetFont().GetColor() ); rDev.DrawPolyLine(aPoly, aInfo); #ifdef SM_RECT_DEBUG if (!IsDebug()) return; int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; SmRect::Draw(rDev, rPosition, nRFlags); #endif } /**************************************************************************/ void SmRootSymbolNode::AdaptToX(const OutputDevice &/*rDev*/, ULONG nWidth) { nBodyWidth = nWidth; } void SmRootSymbolNode::AdaptToY(const OutputDevice &rDev, ULONG nHeight) { // etwas extra Laenge damit der horizontale Balken spaeter ueber dem // Argument positioniert ist SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10L); } void SmRootSymbolNode::Draw(OutputDevice &rDev, const Point &rPosition) const { if (IsPhantom()) return; // draw root-sign itself SmMathSymbolNode::Draw(rDev, rPosition); SmTmpDevice aTmpDev( (OutputDevice &) rDev, TRUE ); aTmpDev.SetFillColor(GetFont().GetColor()); rDev.SetLineColor(); aTmpDev.SetFont( GetFont() ); // since the width is always unscaled it corresponds ot the _original_ // _unscaled_ font height to be used, we use that to calculate the // bar height. Thus it is independent of the arguments height. // ( see display of sqrt QQQ versus sqrt stack{Q#Q#Q#Q} ) long nBarHeight = GetWidth() * 7L / 100L; long nBarWidth = nBodyWidth + GetBorderWidth(); Point aBarOffset( GetWidth(), +GetBorderWidth() ); Point aBarPos( rPosition + aBarOffset ); Rectangle aBar(aBarPos, Size( nBarWidth, nBarHeight) ); //! avoid GROWING AND SHRINKING of drawn rectangle when constantly //! increasing zoomfactor. // This is done by shifting it's output-position to a point that // corresponds exactly to a pixel on the output device. Point aDrawPos( rDev.PixelToLogic(rDev.LogicToPixel(aBar.TopLeft())) ); //aDrawPos.X() = aBar.Left(); //! don't change X position aBar.SetPos( aDrawPos ); rDev.DrawRect( aBar ); #ifdef SM_RECT_DEBUG if (!IsDebug()) return; int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; SmRect::Draw(rDev, rPosition, nRFlags); #endif } /**************************************************************************/ void SmRectangleNode::AdaptToX(const OutputDevice &/*rDev*/, ULONG nWidth) { aToSize.Width() = nWidth; } void SmRectangleNode::AdaptToY(const OutputDevice &/*rDev*/, ULONG nHeight) { GetFont().FreezeBorderWidth(); aToSize.Height() = nHeight; } void SmRectangleNode::Arrange(const OutputDevice &rDev, const SmFormat &/*rFormat*/) { long nFontHeight = GetFont().GetSize().Height(); long nWidth = aToSize.Width(), nHeight = aToSize.Height(); if (nHeight == 0) nHeight = nFontHeight / 30; if (nWidth == 0) nWidth = nFontHeight / 3; SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); // add some borderspace ULONG nTmpBorderWidth = GetFont().GetBorderWidth(); //nWidth += nTmpBorderWidth; nHeight += 2 * nTmpBorderWidth; //! use this method in order to have 'SmRect::HasAlignInfo() == TRUE' //! and thus having the attribut-fences updated in 'SmRect::ExtendBy' SmRect::operator = (SmRect(nWidth, nHeight)); } void SmRectangleNode::Draw(OutputDevice &rDev, const Point &rPosition) const { if (IsPhantom()) return; SmTmpDevice aTmpDev ((OutputDevice &) rDev, FALSE); aTmpDev.SetFillColor(GetFont().GetColor()); rDev.SetLineColor(); aTmpDev.SetFont(GetFont()); ULONG nTmpBorderWidth = GetFont().GetBorderWidth(); // get rectangle and remove borderspace Rectangle aTmp (AsRectangle() + rPosition - GetTopLeft()); aTmp.Left() += nTmpBorderWidth; aTmp.Right() -= nTmpBorderWidth; aTmp.Top() += nTmpBorderWidth; aTmp.Bottom() -= nTmpBorderWidth; DBG_ASSERT(aTmp.GetHeight() > 0 && aTmp.GetWidth() > 0, "Sm: leeres Rechteck"); //! avoid GROWING AND SHRINKING of drawn rectangle when constantly //! increasing zoomfactor. // This is done by shifting it's output-position to a point that // corresponds exactly to a pixel on the output device. Point aPos (rDev.PixelToLogic(rDev.LogicToPixel(aTmp.TopLeft()))); aTmp.SetPos(aPos); rDev.DrawRect(aTmp); #ifdef SM_RECT_DEBUG if (!IsDebug()) return; int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; SmRect::Draw(rDev, rPosition, nRFlags); #endif } /**************************************************************************/ SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, USHORT nFontDescP ) : SmVisibleNode(eNodeType, rNodeToken) { nFontDesc = nFontDescP; } SmTextNode::SmTextNode( const SmToken &rNodeToken, USHORT nFontDescP ) : SmVisibleNode(NTEXT, rNodeToken) { nFontDesc = nFontDescP; } void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { SmNode::Prepare(rFormat, rDocShell); // 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( RHA_LEFT ); aText = GetToken().aText; GetFont() = rFormat.GetFont(GetFontDesc()); if (IsItalic( GetFont() )) Attributes() |= ATTR_ITALIC; if (IsBold( GetFont() )) Attributes() |= ATTR_BOLD; // special handling for ':' where it is a token on it's own and is likely // to be used for mathematical notations. (E.g. a:b = 2:3) // In that case it should not be displayed in italic. if (GetToken().aText.Len() == 1 && GetToken().aText.GetChar(0) == ':') Attributes() &= ~ATTR_ITALIC; }; void SmTextNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { PrepareAttributes(); USHORT nSizeDesc = GetFontDesc() == FNT_FUNCTION ? SIZ_FUNCTION : SIZ_TEXT; GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); SmRect::operator = (SmRect(aTmpDev, &rFormat, aText, GetFont().GetBorderWidth())); } void SmTextNode::CreateTextFromNode(String &rText) { BOOL bQuoted=FALSE; if (GetToken().eType == TTEXT) { rText.Append('\"'); bQuoted=TRUE; } else { SmParser aParseTest; SmNode *pTable = aParseTest.Parse(GetToken().aText); bQuoted=TRUE; if ( (pTable->GetType() == NTABLE) && (pTable->GetNumSubNodes() == 1) ) { SmNode *pResult = pTable->GetSubNode(0); if ( (pResult->GetType() == NLINE) && (pResult->GetNumSubNodes() == 1) ) { pResult = pResult->GetSubNode(0); if ( (pResult->GetType() == NEXPRESSION) && (pResult->GetNumSubNodes() == 1) ) { pResult = pResult->GetSubNode(0); if (pResult->GetType() == NTEXT) bQuoted=FALSE; } } } delete pTable; if ((GetToken().eType == TIDENT) && (GetFontDesc() == FNT_FUNCTION)) { //Search for existing functions and remove extraenous keyword APPEND(rText,"func "); } else if (bQuoted) APPEND(rText,"italic "); if (bQuoted) rText.Append('\"'); } rText.Append(GetToken().aText); if (bQuoted) rText.Append('\"'); rText.Append(' '); } void SmTextNode::Draw(OutputDevice &rDev, const Point& rPosition) const { if (IsPhantom() || aText.Len() == 0 || aText.GetChar(0) == xub_Unicode('\0')) return; SmTmpDevice aTmpDev ((OutputDevice &) rDev, FALSE); aTmpDev.SetFont(GetFont()); Point aPos (rPosition); aPos.Y() += GetBaselineOffset(); // auf Pixelkoordinaten runden aPos = rDev.PixelToLogic( rDev.LogicToPixel(aPos) ); rDev.DrawStretchText(aPos, GetWidth(), aText); #ifdef SM_RECT_DEBUG if (!IsDebug()) return; int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; SmRect::Draw(rDev, rPosition, nRFlags); #endif } void SmTextNode::GetAccessibleText( String &rText ) const { rText += aText; } /**************************************************************************/ void SmMatrixNode::CreateTextFromNode(String &rText) { APPEND(rText,"matrix {"); for (USHORT i = 0; i < nNumRows; i++) { for (USHORT j = 0; j < nNumCols; j++) { SmNode *pNode = GetSubNode(i * nNumCols + j); pNode->CreateTextFromNode(rText); if (j != nNumCols-1) APPEND(rText,"# "); } if (i != nNumRows-1) APPEND(rText,"## "); } rText.EraseTrailingChars(); APPEND(rText,"} "); } void SmMatrixNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { Point aPosition, aOffset; SmNode *pNode; USHORT i, j; // initialize array that is to hold the maximum widhts of all // elements (subnodes) in that column. long *pColWidth = new long[nNumCols]; for (j = 0; j < nNumCols; j++) pColWidth[j] = 0; // arrange subnodes and calculate the aboves arrays contents USHORT nNodes = GetNumSubNodes(); for (i = 0; i < nNodes; i++) { USHORT nIdx = nNodes - 1 - i; if (NULL != (pNode = GetSubNode(nIdx))) { pNode->Arrange(rDev, rFormat); int nCol = nIdx % nNumCols; pColWidth[nCol] = Max(pColWidth[nCol], pNode->GetItalicWidth()); } } // norm distance from which the following two are calcutated const int nNormDist = 3 * GetFont().GetSize().Height(); // define horizontal and vertical minimal distances that seperate // the elements long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100L, nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100L; // build array that holds the leftmost position for each column long *pColLeft = new long[nNumCols]; long nX = 0; for (j = 0; j < nNumCols; j++) { pColLeft[j] = nX; nX += pColWidth[j] + nHorDist; } Point aPos, aDelta; SmRect aLineRect; SmRect::operator = (SmRect()); for (i = 0; i < nNumRows; i++) { aLineRect = SmRect(); for (j = 0; j < nNumCols; j++) { SmNode *pTmpNode = GetSubNode(i * nNumCols + j); DBG_ASSERT(pTmpNode, "Sm: NULL pointer"); const SmRect &rNodeRect = pTmpNode->GetRect(); // align all baselines in that row if possible aPos = rNodeRect.AlignTo(aLineRect, RP_RIGHT, RHA_CENTER, RVA_BASELINE); aPos.X() += nHorDist; // get horizontal alignment const SmNode *pCoNode = pTmpNode->GetLeftMost(); RectHorAlign eHorAlign = pCoNode->GetRectHorAlign(); // caculate horizontal position of element depending on column // and horizontal alignment switch (eHorAlign) { case RHA_LEFT: aPos.X() = rNodeRect.GetLeft() + pColLeft[j]; break; case RHA_CENTER: aPos.X() = rNodeRect.GetLeft() + pColLeft[j] + pColWidth[j] / 2 - rNodeRect.GetItalicCenterX(); break; case RHA_RIGHT: aPos.X() = rNodeRect.GetLeft() + pColLeft[j] + pColWidth[j] - rNodeRect.GetItalicWidth(); break; } pTmpNode->MoveTo(aPos); aLineRect.ExtendBy(rNodeRect, RCP_XOR); } aPos = aLineRect.AlignTo(*this, RP_BOTTOM, RHA_CENTER, RVA_BASELINE); aPos.Y() += nVerDist; // move 'aLineRect' and rectangles in that line to final position aDelta.X() = 0; // since horizontal alignment is already done aDelta.Y() = aPos.Y() - aLineRect.GetTop(); aLineRect.Move(aDelta); for (j = 0; j < nNumCols; j++) if (NULL != (pNode = GetSubNode(i * nNumCols + j))) pNode->Move(aDelta); ExtendBy(aLineRect, RCP_NONE); } delete [] pColLeft; delete [] pColWidth; } void SmMatrixNode::SetRowCol(USHORT nMatrixRows, USHORT nMatrixCols) { nNumRows = nMatrixRows; nNumCols = nMatrixCols; } SmNode * SmMatrixNode::GetLeftMost() { return this; } /**************************************************************************/ SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken) : SmSpecialNode(NMATH, rNodeToken, FNT_MATH) { xub_Unicode cChar = GetToken().cMathChar; if ((xub_Unicode) '\0' != cChar) SetText( cChar ); } void SmMathSymbolNode::AdaptToX(const OutputDevice &rDev, ULONG nWidth) { // Since there is no function to do this, we try to approximate it: Size aFntSize (GetFont().GetSize()); //! however the result is a bit better with 'nWidth' as initial font width aFntSize.Width() = nWidth; GetFont().SetSize(aFntSize); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); // get denominator of error factor for width long nTmpBorderWidth = GetFont().GetBorderWidth(); long nDenom = SmRect(aTmpDev, NULL, GetText(), nTmpBorderWidth).GetItalicWidth(); // scale fontwidth with this error factor aFntSize.Width() *= nWidth; aFntSize.Width() /= nDenom ? nDenom : 1; GetFont().SetSize(aFntSize); } void SmMathSymbolNode::AdaptToY(const OutputDevice &rDev, ULONG nHeight) { GetFont().FreezeBorderWidth(); Size aFntSize (GetFont().GetSize()); // da wir nur die Hoehe skalieren wollen muesen wir hier ggf die Fontweite // ermitteln um diese beizubehalten. if (aFntSize.Width() == 0) { OutputDevice &rDevNC = (OutputDevice &) rDev; rDevNC.Push(PUSH_FONT | PUSH_MAPMODE); rDevNC.SetFont(GetFont()); aFntSize.Width() = rDev.GetFontMetric().GetSize().Width(); rDevNC.Pop(); } DBG_ASSERT(aFntSize.Width() != 0, "Sm: "); //! however the result is a bit better with 'nHeight' as initial //! font height aFntSize.Height() = nHeight; GetFont().SetSize(aFntSize); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); // get denominator of error factor for height long nTmpBorderWidth = GetFont().GetBorderWidth(); long nDenom = SmRect(aTmpDev, NULL, GetText(), nTmpBorderWidth).GetHeight(); // scale fontwidth with this error factor aFntSize.Height() *= nHeight; aFntSize.Height() /= nDenom ? nDenom : 1; GetFont().SetSize(aFntSize); } void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { SmNode::Prepare(rFormat, rDocShell); GetFont() = rFormat.GetFont(GetFontDesc()); // use same font size as is used for variables GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetSize() ); DBG_ASSERT(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL || GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE, "incorrect charset for character from StarMath/OpenSymbol font"); Flags() |= FLG_FONT | FLG_ITALIC; }; void SmMathSymbolNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { const XubString &rText = GetText(); if (rText.Len() == 0 || rText.GetChar(0) == xub_Unicode('\0')) { SmRect::operator = (SmRect()); return; } PrepareAttributes(); GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth())); } void SmMathSymbolNode::CreateTextFromNode(String &rText) { String sStr; MathType::LookupChar(GetToken().cMathChar, sStr); rText.Append(sStr); } void SmRectangleNode::CreateTextFromNode(String &rText) { switch (GetToken().eType) { case TUNDERLINE: APPEND(rText,"underline "); break; case TOVERLINE: APPEND(rText,"overline "); break; case TOVERSTRIKE: APPEND(rText,"overstrike "); break; default: break; } } void SmAttributNode::CreateTextFromNode(String &rText) { SmNode *pNode; USHORT nSize = GetNumSubNodes(); DBG_ASSERT(nSize == 2, "Node missing members"); rText.Append('{'); sal_Unicode nLast=0; if (NULL != (pNode = GetSubNode(0))) { String aStr; pNode->CreateTextFromNode(aStr); if (aStr.Len() > 1) rText.Append(aStr); else { nLast = aStr.GetChar(0); switch (nLast) { case 0xAF: APPEND(rText,"overline "); break; case 0x2d9: APPEND(rText,"dot "); break; case 0x2dc: APPEND(rText,"widetilde "); break; case 0xA8: APPEND(rText,"ddot "); break; case 0xE082: break; case 0xE09B: APPEND(rText,"dddot "); break; default: rText.Append(nLast); break; } } } if (nSize == 2) if (NULL != (pNode = GetSubNode(1))) pNode->CreateTextFromNode(rText); rText.EraseTrailingChars(); if (nLast == 0xE082) APPEND(rText," overbrace {}"); APPEND(rText,"} "); } /**************************************************************************/ bool lcl_IsFromGreekSymbolSet( const String &rTokenText ) { bool bRes = false; // valid symbol name needs to have a '%' at pos 0 and at least an additonal char if (rTokenText.Len() > 2 && rTokenText.GetBuffer()[0] == (sal_Unicode)'%') { String aName( rTokenText.Copy(1) ); SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName ); if (pSymbol && GetExportSymbolSetName( pSymbol->GetSymbolSetName() ).EqualsAscii( "Greek" ) ) bRes = true; } return bRes; } SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, USHORT _nFontDesc) : SmTextNode(eNodeType, rNodeToken, _nFontDesc) { bIsFromGreekSymbolSet = lcl_IsFromGreekSymbolSet( rNodeToken.aText ); } SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken) : SmTextNode(NSPECIAL, rNodeToken, FNT_MATH) //! default Font nicht immer richtig { bIsFromGreekSymbolSet = lcl_IsFromGreekSymbolSet( rNodeToken.aText ); } void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { SmNode::Prepare(rFormat, rDocShell); const SmSym *pSym; SmModule *pp = SM_MOD(); String aName( GetToken().aText.Copy(1) ); if (NULL != (pSym = pp->GetSymbolManager().GetSymbolByName( aName ))) { SetText( pSym->GetCharacter() ); GetFont() = pSym->GetFace(); } else { SetText( GetToken().aText ); GetFont() = rFormat.GetFont(FNT_VARIABLE); } // use same font size as is used for variables GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetSize() ); //! eigentlich sollten nur WEIGHT_NORMAL und WEIGHT_BOLD vorkommen... //! In der sms-Datei gibt es jedoch zB auch 'WEIGHT_ULTRALIGHT' //! daher vergleichen wir hier mit > statt mit != . //! (Langfristig sollte die Notwendigkeit fuer 'PrepareAttribut', und damit //! fuer dieses hier, mal entfallen.) // //! see also SmFontStyles::GetStyleName if (IsItalic( GetFont() )) SetAttribut(ATTR_ITALIC); if (IsBold( GetFont() )) SetAttribut(ATTR_BOLD); Flags() |= FLG_FONT; if (bIsFromGreekSymbolSet) { DBG_ASSERT( GetText().Len() == 1, "a symbol should only consist of 1 char!" ); bool bItalic = false; INT16 nStyle = rFormat.GetGreekCharStyle(); DBG_ASSERT( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" ); if (nStyle == 1) bItalic = true; else if (nStyle == 2) { String aTmp( GetText() ); if (aTmp.Len() > 0) { const sal_Unicode cUppercaseAlpha = 0x0391; const sal_Unicode cUppercaseOmega = 0x03A9; sal_Unicode cChar = aTmp.GetBuffer()[0]; // uppercase letters should be straight and lowercase letters italic bItalic = !(cUppercaseAlpha <= cChar && cChar <= cUppercaseOmega); } } if (bItalic) Attributes() |= ATTR_ITALIC; else Attributes() &= ~ATTR_ITALIC;; } }; void SmSpecialNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { PrepareAttributes(); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth())); } void SmSpecialNode::Draw(OutputDevice &rDev, const Point& rPosition) const { //! since this chars might come from any font, that we may not have //! set to ALIGN_BASELINE yet, we do it now. ((SmSpecialNode *)this)->GetFont().SetAlign(ALIGN_BASELINE); SmTextNode::Draw(rDev, rPosition); } /**************************************************************************/ void SmGlyphSpecialNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { PrepareAttributes(); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()).AsGlyphRect()); } /**************************************************************************/ void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { SmNode::Prepare(rFormat, rDocShell); GetFont().SetColor(COL_GRAY); Flags() |= FLG_COLOR | FLG_FONT | FLG_ITALIC; }; void SmPlaceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { PrepareAttributes(); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth())); } /**************************************************************************/ void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { SmNode::Prepare(rFormat, rDocShell); GetFont().SetColor(COL_RED); Flags() |= FLG_VISIBLE | FLG_BOLD | FLG_ITALIC | FLG_COLOR | FLG_FONT | FLG_SIZE; } void SmErrorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { PrepareAttributes(); SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); const XubString &rText = GetText(); SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth())); } /**************************************************************************/ void SmBlankNode::IncreaseBy(const SmToken &rToken) { switch(rToken.eType) { case TBLANK: nNum += 4; break; case TSBLANK: nNum += 1; break; default: break; } } void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) { SmNode::Prepare(rFormat, rDocShell); //! hier muss/sollte es lediglich nicht der StarMath Font sein, //! damit fuer das in Arrange verwendete Zeichen ein "normales" //! (ungecliptes) Rechteck erzeugt wird. GetFont() = rFormat.GetFont(FNT_VARIABLE); Flags() |= FLG_FONT | FLG_BOLD | FLG_ITALIC; } void SmBlankNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) { SmTmpDevice aTmpDev ((OutputDevice &) rDev, TRUE); aTmpDev.SetFont(GetFont()); // Abstand von der Fonthoehe abhaengig machen // (damit er beim skalieren (zB size *2 {a ~ b}) mitwaechst) long nDist = GetFont().GetSize().Height() / 10L, nSpace = nNum * nDist; // ein SmRect mit Baseline und allem drum und dran besorgen SmRect::operator = (SmRect(aTmpDev, &rFormat, XubString(xub_Unicode(' ')), GetFont().GetBorderWidth())); // und dieses auf die gewuenschte Breite bringen SetItalicSpaces(0, 0); SetWidth(nSpace); }