| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 <tools/gen.hxx> |
| #include <tools/fract.hxx> |
| #include <rtl/math.hxx> |
| #include <tools/color.hxx> |
| #include <vcl/metric.hxx> |
| #include <vcl/lineinfo.hxx> |
| #include <vcl/outdev.hxx> |
| #include <sfx2/module.hxx> |
| |
| #include <math.h> |
| #include <float.h> |
| |
| |
| #define APPEND(str,ascii) str.AppendAscii(RTL_CONSTASCII_STRINGPARAM(ascii)) |
| |
| // define this to draw rectangles for debugging |
| //#define SM_RECT_DEBUG |
| |
| |
| using ::rtl::OUString; |
| |
| |
| //////////////////////////////////////// |
| // 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, sal_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, sal_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() |
| { |
| } |
| |
| |
| sal_Bool SmNode::IsVisible() const |
| { |
| return sal_False; |
| } |
| |
| |
| sal_uInt16 SmNode::GetNumSubNodes() const |
| { |
| return 0; |
| } |
| |
| |
| SmNode * SmNode::GetSubNode(sal_uInt16 /*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(sal_Bool bIsPhantomP) |
| { |
| if (! (Flags() & FLG_VISIBLE)) |
| bIsPhantom = bIsPhantomP; |
| |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 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; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 i = 0; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| pNode->SetColor(rColor); |
| } |
| |
| |
| void SmNode::SetAttribut(sal_uInt16 nAttrib) |
| { |
| if ( |
| (nAttrib == ATTR_BOLD && !(Flags() & FLG_BOLD)) || |
| (nAttrib == ATTR_ITALIC && !(Flags() & FLG_ITALIC)) |
| ) |
| { |
| nAttributes |= nAttrib; |
| } |
| |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 i = 0; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| pNode->SetAttribut(nAttrib); |
| } |
| |
| |
| void SmNode::ClearAttribut(sal_uInt16 nAttrib) |
| { |
| if ( |
| (nAttrib == ATTR_BOLD && !(Flags() & FLG_BOLD)) || |
| (nAttrib == ATTR_ITALIC && !(Flags() & FLG_ITALIC)) |
| ) |
| { |
| nAttributes &= ~nAttrib; |
| } |
| |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 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; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 i = 0; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| pNode->SetFont(rFace); |
| } |
| |
| |
| void SmNode::SetFontSize(const Fraction &rSize, sal_uInt16 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; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 i = 0; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| pNode->SetFontSize(rSize, nType); |
| } |
| |
| |
| void SmNode::SetSize(const Fraction &rSize) |
| { |
| GetFont() *= rSize; |
| |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 i = 0; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| pNode->SetSize(rSize); |
| } |
| |
| |
| void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, sal_Bool bApplyToSubTree ) |
| { |
| if (!(Flags() & FLG_HORALIGN)) |
| eRectHorAlign = eHorAlign; |
| |
| if (bApplyToSubTree) |
| { |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 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 = sal_True; |
| #else |
| bIsDebug = sal_False; |
| #endif |
| bIsPhantom = sal_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; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 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 ? sal_False : sal_True; |
| |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 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; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 i = 0; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| pNode->Move(rPosition); |
| } |
| |
| |
| void SmNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) |
| { |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 i = 0; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| pNode->Arrange(rDev, rFormat); |
| } |
| |
| void SmNode::CreateTextFromNode(String &rText) |
| { |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| if (nSize > 1) |
| rText.Append('{'); |
| for (sal_uInt16 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*/, sal_uLong /*nWidth*/) |
| { |
| } |
| |
| |
| void SmNode::AdaptToY(const OutputDevice &/*rDev*/, sal_uLong /*nHeight*/) |
| { |
| } |
| |
| |
| void SmNode::Draw(OutputDevice &rDev, const Point &rPosition) const |
| { |
| if (IsPhantom()) |
| return; |
| |
| const SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| for (sal_uInt16 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(sal_uInt16 nRow, sal_uInt16 nCol) const |
| // returns (first) ** visible ** (sub)node with the tokens text at |
| // position 'nRow', 'nCol'. |
| //! (there should be exactly one such node if any) |
| { |
| if ( IsVisible() |
| && nRow == GetToken().nRow |
| && nCol >= GetToken().nCol && nCol < GetToken().nCol + GetToken().aText.Len()) |
| return this; |
| else |
| { |
| sal_uInt16 nNumSubNodes = GetNumSubNodes(); |
| for (sal_uInt16 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 |
| { |
| sal_uInt16 nNumSubNodes = GetNumSubNodes(); |
| for (sal_uInt16 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 |
| { |
| sal_uInt16 nNumSubNodes = GetNumSubNodes(); |
| for (sal_uInt16 i = 0; i < nNumSubNodes; i++) |
| { |
| const SmNode *pNode = GetSubNode(i); |
| if (!pNode) |
| continue; |
| |
| pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx); |
| if (pResult) |
| return pResult; |
| } |
| } |
| |
| return pResult; |
| } |
| |
| |
| long SmNode::GetFormulaBaseline() const |
| { |
| DBG_ASSERT( 0, "This dummy implementation should not have been called." ); |
| return 0; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| SmStructureNode::SmStructureNode( const SmStructureNode &rNode ) : |
| SmNode( rNode.GetType(), rNode.GetToken() ) |
| { |
| sal_uLong i; |
| for (i = 0; i < aSubNodes.size(); i++) |
| delete aSubNodes[i]; |
| aSubNodes.resize(0); |
| |
| sal_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 (sal_uInt16 i = 0; i < GetNumSubNodes(); i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| delete pNode; |
| } |
| |
| |
| SmStructureNode & SmStructureNode::operator = ( const SmStructureNode &rNode ) |
| { |
| SmNode::operator = ( rNode ); |
| |
| sal_uLong i; |
| for (i = 0; i < aSubNodes.size(); i++) |
| delete aSubNodes[i]; |
| aSubNodes.resize(0); |
| |
| sal_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; |
| } |
| |
| |
| sal_Bool SmStructureNode::IsVisible() const |
| { |
| return sal_False; |
| } |
| |
| |
| sal_uInt16 SmStructureNode::GetNumSubNodes() const |
| { |
| return (sal_uInt16) aSubNodes.size(); |
| } |
| |
| |
| SmNode * SmStructureNode::GetSubNode(sal_uInt16 nIndex) |
| { |
| return aSubNodes[nIndex]; |
| } |
| |
| |
| void SmStructureNode::GetAccessibleText( String &rText ) const |
| { |
| sal_uInt16 nNodes = GetNumSubNodes(); |
| for (sal_uInt16 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( " " ); |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| |
| sal_Bool SmVisibleNode::IsVisible() const |
| { |
| return sal_True; |
| } |
| |
| |
| sal_uInt16 SmVisibleNode::GetNumSubNodes() const |
| { |
| return 0; |
| } |
| |
| |
| SmNode * SmVisibleNode::GetSubNode(sal_uInt16 /*nIndex*/) |
| { |
| return NULL; |
| } |
| |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| void SmGraphicNode::GetAccessibleText( String &rText ) const |
| { |
| rText += GetToken().aText; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| |
| |
| void SmExpressionNode::CreateTextFromNode(String &rText) |
| { |
| SmNode *pNode; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| if (nSize > 1) |
| rText.Append('{'); |
| for (sal_uInt16 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; |
| sal_uInt16 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; |
| sal_uInt16 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, 1)); |
| 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); |
| } |
| } |
| // --> 4.7.2010 #i972# |
| if (HasBaseline()) |
| nFormulaBaseline = GetBaseline(); |
| else |
| { |
| SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); |
| aTmpDev.SetFont(GetFont()); |
| |
| SmRect aRect = (SmRect(aTmpDev, &rFormat, C2S("a"), |
| GetFont().GetBorderWidth())); |
| nFormulaBaseline = GetAlignM(); |
| // move from middle position by constant - distance |
| // between middle and baseline for single letter |
| nFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM(); |
| } |
| // <-- |
| } |
| |
| |
| SmNode * SmTableNode::GetLeftMost() |
| { |
| return this; |
| } |
| |
| |
| long SmTableNode::GetFormulaBaseline() const |
| { |
| return nFormulaBaseline; |
| } |
| |
| |
| /**************************************************************************/ |
| |
| |
| 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; |
| sal_uInt16 nSize = GetNumSubNodes(); |
| sal_uInt16 i; |
| for (i = 0; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| pNode->Arrange(rDev, rFormat); |
| |
| SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); |
| aTmpDev.SetFont(GetFont()); |
| |
| if (nSize < 1) |
| { |
| // provide an empty rectangle with alignment parameters for the "current" |
| // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the |
| // same sub-/supscript positions.) |
| //! be sure to use a character that has explicitly defined HiAttribut |
| //! line in rect.cxx such as 'a' in order to make 'vec a' look same to |
| //! 'vec {a}'. |
| SmRect::operator = (SmRect(aTmpDev, &rFormat, C2S("a"), |
| GetFont().GetBorderWidth())); |
| // make sure that the rectangle occupies (almost) no space |
| SetWidth(1); |
| SetItalicSpaces(0, 0); |
| return; |
| } |
| |
| // make distance depend on font size |
| long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetSize().Height()) / 100L; |
| if (!IsUseExtraSpaces()) |
| nDist = 0; |
| |
| Point aPos; |
| // copy the first node into LineNode and extend by the others |
| if (NULL != (pNode = GetSubNode(0))) |
| SmRect::operator = (pNode->GetRect()); |
| |
| for (i = 1; i < nSize; i++) |
| if (NULL != (pNode = GetSubNode(i))) |
| { |
| aPos = pNode->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE); |
| |
| // add horizontal space to the left for each but the first sub node |
| 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(), sal_False); |
| } |
| |
| |
| /**************************************************************************/ |
| |
| |
| void SmUnHorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) |
| { |
| sal_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, (sal_Bool) sal_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"); |
| |
| sal_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(); |
| } |
| |
| |
| sal_Bool IsPointInLine(const Point &rPoint1, |
| const Point &rPoint2, const Point &rHeading2) |
| // ergibt sal_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"); |
| |
| sal_Bool bRes = sal_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; |
| } |
| |
| |
| sal_uInt16 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"); |
| |
| sal_uInt16 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 = sal_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, sal_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) |
| { |
| sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ? |
| SIZ_LIMITS : SIZ_INDEX; |
| Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 ); |
| pSubSup->SetSize(aFraction); |
| } |
| |
| pSubSup->Arrange(rDev, rFormat); |
| |
| sal_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(sal_False, "Sm: unbekannter Fall"); |
| break; |
| } |
| |
| pSubSup->MoveTo(aPos); |
| ExtendBy(*pSubSup, RCP_THIS, (sal_Bool) sal_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); |
| |
| sal_Bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(), |
| bScale = pBody->GetHeight() > 0 && |
| (GetScaleMode() == SCALE_HEIGHT || bIsScaleNormal), |
| bIsABS = GetToken().eType == TABS; |
| |
| long nFaceHeight = GetFont().GetSize().Height(); |
| |
| // Uebergroesse in % ermitteln |
| sal_uInt16 nPerc = 0; |
| if (!bIsABS && bScale) |
| { // im Fall von Klammern mit Uebergroesse... |
| sal_uInt16 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) |
| { |
| sal_uInt16 nNumSubNodes = GetNumSubNodes(); |
| if (nNumSubNodes == 0) |
| return; |
| |
| // arrange arguments |
| sal_uInt16 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 |
| sal_Bool bScale = GetScaleMode() == SCALE_HEIGHT || rFormat.IsScaleNormalBrackets(); |
| long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetSize().Height(); |
| sal_uInt16 nIndex = GetScaleMode() == SCALE_HEIGHT ? |
| DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE; |
| sal_uInt16 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++) |
| { |
| sal_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, sal_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, (sal_Bool) sal_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<double>(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< 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() |= 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(sal_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(sal_False, "Sm: unbekannter Fall"); |
| } |
| |
| pNode->Arrange(rDev, rFormat); |
| |
| SmRect::operator = (pNode->GetRect()); |
| } |
| |
| |
| void SmFontNode::SetSizeParameter(const Fraction& rValue, sal_uInt16 Type) |
| { |
| nSizeType = Type; |
| aFontSize = rValue; |
| } |
| |
| |
| /**************************************************************************/ |
| |
| |
| SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken) |
| : SmGraphicNode(NPOLYLINE, rNodeToken) |
| { |
| aPoly.SetSize(2); |
| nWidth = 0; |
| } |
| |
| |
| void SmPolyLineNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong nNewWidth) |
| { |
| aToSize.Width() = nNewWidth; |
| } |
| |
| |
| void SmPolyLineNode::AdaptToY(const OutputDevice &/*rDev*/, sal_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, sal_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, sal_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*/, sal_uLong nWidth) |
| { |
| nBodyWidth = nWidth; |
| } |
| |
| |
| void SmRootSymbolNode::AdaptToY(const OutputDevice &rDev, sal_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, sal_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*/, sal_uLong nWidth) |
| { |
| aToSize.Width() = nWidth; |
| } |
| |
| |
| void SmRectangleNode::AdaptToY(const OutputDevice &/*rDev*/, sal_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, sal_True); |
| aTmpDev.SetFont(GetFont()); |
| |
| // add some borderspace |
| sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth(); |
| //nWidth += nTmpBorderWidth; |
| nHeight += 2 * nTmpBorderWidth; |
| |
| //! use this method in order to have 'SmRect::HasAlignInfo() == sal_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, sal_False); |
| aTmpDev.SetFillColor(GetFont().GetColor()); |
| rDev.SetLineColor(); |
| aTmpDev.SetFont(GetFont()); |
| |
| sal_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, sal_uInt16 nFontDescP ) : |
| SmVisibleNode(eNodeType, rNodeToken) |
| { |
| nFontDesc = nFontDescP; |
| } |
| |
| |
| SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 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(); |
| |
| sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ? |
| SIZ_FUNCTION : SIZ_TEXT; |
| GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100); |
| |
| SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); |
| aTmpDev.SetFont(GetFont()); |
| |
| SmRect::operator = (SmRect(aTmpDev, &rFormat, aText, GetFont().GetBorderWidth())); |
| } |
| |
| void SmTextNode::CreateTextFromNode(String &rText) |
| { |
| sal_Bool bQuoted=sal_False; |
| if (GetToken().eType == TTEXT) |
| { |
| rText.Append('\"'); |
| bQuoted=sal_True; |
| } |
| else |
| { |
| SmParser aParseTest; |
| SmNode *pTable = aParseTest.Parse(GetToken().aText); |
| bQuoted=sal_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=sal_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, sal_False); |
| aTmpDev.SetFont(GetFont()); |
| |
| Point aPos (rPosition); |
| aPos.Y() += GetBaselineOffset(); |
| // auf Pixelkoordinaten runden |
| aPos = rDev.PixelToLogic( rDev.LogicToPixel(aPos) ); |
| |
| #if OSL_DEBUG_LEVEL > 1 |
| sal_Int32 nPos = 0; |
| sal_UCS4 cChar = OUString( aText ).iterateCodePoints( &nPos ); |
| (void) cChar; |
| #endif |
| |
| 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 (sal_uInt16 i = 0; i < nNumRows; i++) |
| { |
| for (sal_uInt16 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; |
| sal_uInt16 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 |
| sal_uInt16 nNodes = GetNumSubNodes(); |
| for (i = 0; i < nNodes; i++) |
| { |
| sal_uInt16 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(sal_uInt16 nMatrixRows, sal_uInt16 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, sal_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, sal_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, sal_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, sal_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, sal_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; |
| sal_uInt16 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: // MACRON |
| APPEND(rText,"overline "); |
| break; |
| case 0x2d9: // DOT ABOVE |
| APPEND(rText,"dot "); |
| break; |
| case 0x2dc: // SMALL TILDE |
| APPEND(rText,"widetilde "); |
| break; |
| case 0xA8: // DIAERESIS |
| APPEND(rText,"ddot "); |
| break; |
| case 0xE082: |
| break; |
| case 0xE09B: |
| case 0x20DB: // COMBINING THREE DOTS ABOVE |
| APPEND(rText,"dddot "); |
| break; |
| case 0x301: // COMBINING ACUTE ACCENT |
| APPEND(rText,"acute "); |
| break; |
| case 0x300: // COMBINING GRAVE ACCENT |
| APPEND(rText,"grave "); |
| break; |
| case 0x30C: // COMBINING CARON |
| APPEND(rText,"check "); |
| break; |
| case 0x306: // COMBINING BREVE |
| APPEND(rText,"breve "); |
| break; |
| case 0x30A: // COMBINING RING ABOVE |
| APPEND(rText,"circle "); |
| break; |
| case 0x20D7: // COMBINING RIGHT ARROW ABOVE |
| APPEND(rText,"vec "); |
| break; |
| case 0x303: // COMBINING TILDE |
| APPEND(rText,"tilde "); |
| break; |
| case 0x302: // COMBINING CIRCUMFLEX ACCENT |
| APPEND(rText,"hat "); |
| break; |
| case 0x304: // COMBINING MACRON |
| APPEND(rText,"bar "); |
| 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, sal_uInt16 _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 ))) |
| { |
| sal_UCS4 cChar = pSym->GetCharacter(); |
| String aTmp( OUString( &cChar, 1 ) ); |
| SetText( aTmp ); |
| 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; |
| sal_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, sal_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, sal_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, sal_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, sal_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, sal_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); |
| } |
| |
| |
| |