summaryrefslogtreecommitdiff
path: root/starmath/source/rect.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'starmath/source/rect.cxx')
-rw-r--r--starmath/source/rect.cxx908
1 files changed, 908 insertions, 0 deletions
diff --git a/starmath/source/rect.cxx b/starmath/source/rect.cxx
new file mode 100644
index 000000000000..12853c07189c
--- /dev/null
+++ b/starmath/source/rect.cxx
@@ -0,0 +1,908 @@
+/*************************************************************************
+ *
+ * $RCSfile: rect.cxx,v $
+ *
+ * $Revision: 1.1.1.1 $
+ *
+ * last change: $Author: hr $ $Date: 2000-09-18 16:57:26 $
+ *
+ * The Contents of this file are made available subject to the terms of
+ * either of the following licenses
+ *
+ * - GNU Lesser General Public License Version 2.1
+ * - Sun Industry Standards Source License Version 1.1
+ *
+ * Sun Microsystems Inc., October, 2000
+ *
+ * GNU Lesser General Public License Version 2.1
+ * =============================================
+ * Copyright 2000 by Sun Microsystems, Inc.
+ * 901 San Antonio Road, Palo Alto, CA 94303, USA
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software Foundation.
+ *
+ * This library 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 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ *
+ * Sun Industry Standards Source License Version 1.1
+ * =================================================
+ * The contents of this file are subject to the Sun Industry Standards
+ * Source License Version 1.1 (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.openoffice.org/license.html.
+ *
+ * Software provided under this License is provided on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+ * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+ * See the License for the specific provisions governing your rights and
+ * obligations concerning the Software.
+ *
+ * The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+ *
+ * Copyright: 2000 by Sun Microsystems, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * Contributor(s): _______________________________________
+ *
+ *
+ ************************************************************************/
+
+#pragma hdrstop
+
+#ifndef _STRING_HXX //autogen
+#include <tools/string.hxx>
+#endif
+#ifndef _TOOLS_DEBUG_HXX //autogen
+#include <tools/debug.hxx>
+#endif
+#ifndef _SV_SVAPP_HXX //autogen
+#include <vcl/svapp.hxx>
+#endif
+#ifndef _SV_WRKWIN_HXX //autogen
+#include <vcl/wrkwin.hxx>
+#endif
+#ifndef _SV_VIRDEV_HXX //autogen
+#include <vcl/virdev.hxx>
+#endif
+
+
+#include "rect.hxx"
+#include "types.hxx"
+#include "xchar.hxx"
+#include "utility.hxx"
+#include "smmod.hxx"
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+// '\0' terminiertes Array mit Zeichen, die im StarMath Font als Buchstaben
+// betrachtet werden sollen, (um im Gegensatz zu den anderen Operatoren
+// und Symbolen ein "normales"(ungecliptes) SmRect zu erhalten).
+static xub_Unicode __READONLY_DATA aMathAlpha[] =
+{
+ MS_ALEPH, MS_IM, MS_RE, MS_WP,
+ xub_Unicode('\x70'), MS_EMPTYSET, xub_Unicode('\xF0'), xub_Unicode('\xF1'),
+ xub_Unicode('\xF2'), xub_Unicode('\xF3'), xub_Unicode('\xF4'), MS_HBAR,
+ MS_LAMBDABAR, MS_SETN, MS_SETZ, MS_SETQ,
+ MS_SETR, MS_SETC, xub_Unicode('\xB4'), xub_Unicode('\xB5'),
+ xub_Unicode('\xB8'), xub_Unicode('\xB9'), xub_Unicode('\xBA'),
+ xub_Unicode('\0')
+};
+
+BOOL SmIsMathAlpha(const XubString &rText)
+ // ergibt genau dann TRUE, wenn das Zeichen (aus dem StarMath Font) wie ein
+ // Buchstabe behandelt werden soll.
+{
+ if (rText.Len() == 0)
+ return FALSE;
+
+ DBG_ASSERT(rText.Len() == 1, "Sm : String enthlt nicht genau ein Zeichen");
+ xub_Unicode cChar = rText.GetChar(0);
+
+ // ist es ein griechisches Zeichen ?
+ if (xub_Unicode('\xC6') <= cChar && cChar <= xub_Unicode('\xEE'))
+ return TRUE;
+ else
+ {
+ // kommt es in 'aMathAlpha' vor ?
+ const xub_Unicode *pChar = aMathAlpha;
+ while (*pChar && *pChar != cChar)
+ pChar++;
+ return *pChar != xub_Unicode('\0');
+ }
+}
+
+
+////////////////////////////////////////
+//
+// SmRect members
+//
+
+
+SmRect::SmRect()
+ // constructs empty rectangle at (0, 0) with width and height 0.
+{
+ DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
+ DBG_ASSERT(aSize == Size(0, 0), "Sm: ooops...");
+
+ bHasBaseline = bHasAlignInfo = FALSE;
+ nBaseline = nAlignT = nAlignM = nAlignB =
+ nGlyphTop = nGlyphBottom =
+ nItalicLeftSpace = nItalicRightSpace =
+ nLoAttrFence = nHiAttrFence = 0;
+}
+
+
+SmRect::SmRect(const SmRect &rRect)
+: aTopLeft(rRect.aTopLeft),
+ aSize(rRect.aSize)
+{
+ bHasBaseline = rRect.bHasBaseline;
+ nBaseline = rRect.nBaseline;
+ nAlignT = rRect.nAlignT;
+ nAlignM = rRect.nAlignM;
+ nAlignB = rRect.nAlignB;
+ nGlyphTop = rRect.nGlyphTop;
+ nGlyphBottom = rRect.nGlyphBottom;
+ nHiAttrFence = rRect.nHiAttrFence;
+ nLoAttrFence = rRect.nLoAttrFence;
+ bHasAlignInfo = rRect.bHasAlignInfo;
+ nItalicLeftSpace = rRect.nItalicLeftSpace;
+ nItalicRightSpace = rRect.nItalicRightSpace;
+}
+
+
+void SmRect::CopyAlignInfo(const SmRect &rRect)
+{
+ nBaseline = rRect.nBaseline;
+ bHasBaseline = rRect.bHasBaseline;
+ nAlignT = rRect.nAlignT;
+ nAlignM = rRect.nAlignM;
+ nAlignB = rRect.nAlignB;
+ bHasAlignInfo = rRect.bHasAlignInfo;
+ nLoAttrFence = rRect.nLoAttrFence;
+ nHiAttrFence = rRect.nHiAttrFence;
+}
+
+
+void SmRect::BuildRect(const OutputDevice &rDev, const SmFormat *pFormat,
+ const XubString &rText, long nBorderWidth)
+{
+#ifndef PRODUCT
+ if (rDev.GetOutDevType() != OUTDEV_PRINTER)
+ DBG_WARNING("Sm : Referenz-Device ist kein Drucker");
+#endif
+
+ DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: Ooops...");
+
+ aSize = Size(rDev.GetTextWidth(rText), rDev.GetTextHeight());
+
+ const FontMetric aFM (rDev.GetFontMetric());
+ BOOL bIsMath = aFM.GetName().EqualsIgnoreCaseAscii("StarMath");
+ BOOL bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
+ const long nFontHeight = rDev.GetFont().GetSize().Height();
+
+ bHasAlignInfo = TRUE;
+ bHasBaseline = TRUE;
+ nBaseline = aFM.GetAscent();
+ nAlignT = nBaseline - nFontHeight * 750L / 1000L;
+ nAlignM = nBaseline - nFontHeight * 121L / 422L;
+ // that's where the horizontal bars of '+', '-', ... are
+ // (1/3 of ascent over baseline)
+ // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
+ nAlignB = nBaseline;
+
+ // workaround for printer fonts with very small (possible 0 or even
+ // negative(!)) leading
+ if (aFM.GetLeading() < 5 && rDev.GetOutDevType() == OUTDEV_PRINTER)
+ {
+#if SUPD >= 593
+ OutputDevice *pWindow = Application::GetDefaultDevice();
+#else
+ Window *pWindow = Application::GetAppWindow();
+#endif
+
+ pWindow->Push(PUSH_MAPMODE | PUSH_FONT);
+
+ pWindow->SetMapMode(rDev.GetMapMode());
+ pWindow->SetFont(rDev.GetFontMetric());
+
+ long nDelta = pWindow->GetFontMetric().GetLeading();
+ if (nDelta == 0)
+ { // dieser Wert entspricht etwa einem Leading von 80 bei einer
+ // Fonthhe von 422 (12pt)
+ nDelta = nFontHeight * 8L / 43;
+ }
+ SetTop(GetTop() - nDelta);
+
+ pWindow->Pop();
+ }
+
+ // get GlyphBoundRect
+ Rectangle aGlyphRect;
+ BOOL bSuccess = SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
+ DBG_ASSERT(bSuccess, "Sm : Ooops... (fehlt evtl. der Font?)");
+
+ nItalicLeftSpace = GetLeft() - aGlyphRect.Left() + nBorderWidth;
+ nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
+ if (nItalicLeftSpace < 0 && !bAllowSmaller)
+ nItalicLeftSpace = 0;
+ if (nItalicRightSpace < 0 && !bAllowSmaller)
+ nItalicRightSpace = 0;
+
+ long nDist = 0;
+ if (pFormat)
+ nDist = (rDev.GetFont().GetSize().Height()
+ * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100L;
+
+ nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist;
+ nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
+
+ nGlyphTop = aGlyphRect.Top() - nBorderWidth;
+ nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
+
+ if (bAllowSmaller)
+ {
+ // fr Symbole und Operatoren aus dem StarMath Font passen wir den
+ // oberen und unteren Rand dem Zeichen an.
+ SetTop(nGlyphTop);
+ SetBottom(nGlyphBottom);
+ }
+
+ if (nHiAttrFence < GetTop())
+ nHiAttrFence = GetTop();
+
+ if (nLoAttrFence > GetBottom())
+ nLoAttrFence = GetBottom();
+
+ DBG_ASSERT(rText.Len() == 0 || !IsEmpty(),
+ "Sm: leeres Rechteck erzeugt");
+}
+
+
+void SmRect::Init(const OutputDevice &rDev, const SmFormat *pFormat,
+ const XubString &rText, long nBorderWidth)
+ // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
+{
+ SmRectCache *pRectCache = SM_MOD1()->GetRectCache();
+ DBG_ASSERT(pRectCache, "Sm : NULL pointer");
+
+ // build key for rectangle (to look up in cache for)
+ const SmRectCache::Key aKey (rText, rDev.GetFont());
+
+ const SmRect *pResult = pRectCache->Search(aKey);
+ if (pResult)
+ *this = *pResult;
+ else
+ { // build rectangle and put it in cache
+ BuildRect(rDev, pFormat, rText, nBorderWidth);
+ pResult = pRectCache->Add(aKey, *this);
+ }
+ DBG_ASSERT(pResult, "Sm : NULL pointer");
+}
+
+
+SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
+ const XubString &rText, long nBorderWidth)
+{
+ Init(rDev, pFormat, rText, nBorderWidth);
+}
+
+
+SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
+ const SmPolygon &rPoly, long nBorderWidth)
+{
+ Init(rDev, pFormat, rPoly.GetChar(), nBorderWidth);
+
+ // den Offset in der Zeichenzelle passend waehlen
+ Point aPolyOffset (rPoly.GetOrigPos());
+ aPolyOffset.X() *= rPoly.GetScaleX();
+ aPolyOffset.Y() *= rPoly.GetScaleY();
+
+ // und es an diese Position schieben
+ Rectangle aPolyRect ( rPoly.GetBoundRect(rDev) );
+ Point aDelta (aPolyOffset - aPolyRect.TopLeft());
+ aPolyRect.Move( aDelta.X(), aDelta.Y() );
+
+ aTopLeft.X() = aPolyRect.Left() - nBorderWidth;
+ aTopLeft.Y() = aPolyRect.Top() - nBorderWidth;
+
+ aSize = aPolyRect.GetSize();
+ aSize.Width() += 2 * nBorderWidth;
+ aSize.Height() += 2 * nBorderWidth;
+
+ nItalicLeftSpace = nItalicRightSpace = 0;
+}
+
+
+SmRect::SmRect(long nWidth, long nHeight)
+ // this constructor should never be used for anything textlike because
+ // it will not provide useful values for baseline, AlignT and AlignB!
+ // It's purpose is to get a 'SmRect' for the horizontal line in fractions
+ // as used in 'SmBinVerNode'.
+: aSize(nWidth, nHeight)
+{
+ DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
+
+ bHasBaseline = FALSE;
+ bHasAlignInfo = TRUE;
+ nBaseline = 0;
+ nAlignT = GetTop();
+ nAlignB = GetBottom();
+ nAlignM = (nAlignT + nAlignB) / 2; // this is the default
+ nItalicLeftSpace = nItalicRightSpace = 0;
+ nGlyphTop = nHiAttrFence = GetTop();
+ nGlyphBottom = nLoAttrFence = GetBottom();
+}
+
+
+void SmRect::SetLeft(long nLeft)
+{
+ if (nLeft <= GetRight())
+ { aSize.Width() = GetRight() - nLeft + 1;
+ aTopLeft.X() = nLeft;
+ }
+}
+
+
+void SmRect::SetRight(long nRight)
+{
+ if (nRight >= GetLeft())
+ aSize.Width() = nRight - GetLeft() + 1;
+}
+
+
+void SmRect::SetBottom(long nBottom)
+{
+ if (nBottom >= GetTop())
+ aSize.Height() = nBottom - GetTop() + 1;
+}
+
+
+void SmRect::SetTop(long nTop)
+{
+ if (nTop <= GetBottom())
+ { aSize.Height() = GetBottom() - nTop + 1;
+ aTopLeft.Y() = nTop;
+ }
+}
+
+
+void SmRect::Move(const Point &rPosition)
+ // move rectangle by position 'rPosition'.
+{
+ aTopLeft += rPosition;
+
+ long nDelta = rPosition.Y();
+ nBaseline += nDelta;
+ nAlignT += nDelta;
+ nAlignM += nDelta;
+ nAlignB += nDelta;
+ nGlyphTop += nDelta;
+ nGlyphBottom += nDelta;
+ nHiAttrFence += nDelta;
+ nLoAttrFence += nDelta;
+}
+
+
+const Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
+ RectHorAlign eHor, RectVerAlign eVer) const
+{ Point aPos (GetTopLeft());
+ // will become the topleft point of the new rectangle position
+
+ // set horizontal or vertical new rectangle position depending on
+ // 'ePos' is one of 'RP_LEFT', 'RP_RIGHT' or 'RP_TOP', 'RP_BOTTOM'
+ switch (ePos)
+ { case RP_LEFT :
+ aPos.X() = rRect.GetItalicLeft() - GetItalicRightSpace()
+ - GetWidth();
+ break;
+ case RP_RIGHT :
+ aPos.X() = rRect.GetItalicRight() + 1 + GetItalicLeftSpace();
+ break;
+ case RP_TOP :
+ aPos.Y() = rRect.GetTop() - GetHeight();
+ break;
+ case RP_BOTTOM :
+ aPos.Y() = rRect.GetBottom() + 1;
+ break;
+ case RP_ATTRIBUT :
+ aPos.X() = rRect.GetItalicCenterX() - GetItalicWidth() / 2
+ + GetItalicLeftSpace();
+ break;
+ default :
+ DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
+ }
+
+ // check if horizontal position is already set
+ if (ePos == RP_LEFT || ePos == RP_RIGHT || ePos == RP_ATTRIBUT)
+ // correct error in current vertical position
+ switch (eVer)
+ { case RVA_TOP :
+ aPos.Y() += rRect.GetAlignT() - GetAlignT();
+ break;
+ case RVA_MID :
+ aPos.Y() += rRect.GetAlignM() - GetAlignM();
+ break;
+ case RVA_BASELINE :
+ // align baselines if possible else align mid's
+ if (HasBaseline() && rRect.HasBaseline())
+ aPos.Y() += rRect.GetBaseline() - GetBaseline();
+ else
+ aPos.Y() += rRect.GetAlignM() - GetAlignM();
+ break;
+ case RVA_BOTTOM :
+ aPos.Y() += rRect.GetAlignB() - GetAlignB();
+ break;
+ case RVA_CENTERY :
+ aPos.Y() += rRect.GetCenterY() - GetCenterY();
+ break;
+ case RVA_ATTRIBUT_HI:
+ aPos.Y() += rRect.GetHiAttrFence() - GetBottom();
+ break;
+ case RVA_ATTRIBUT_MID :
+ aPos.Y() += SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4)
+ - GetCenterY();
+ break;
+ case RVA_ATTRIBUT_LO :
+ aPos.Y() += rRect.GetLoAttrFence() - GetTop();
+ break;
+ default :
+ DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
+ }
+
+ // check if vertical position is already set
+ if (ePos == RP_TOP || ePos == RP_BOTTOM)
+ // correct error in current horizontal position
+ switch (eHor)
+ { case RHA_LEFT :
+ aPos.X() += rRect.GetItalicLeft() - GetItalicLeft();
+ break;
+ case RHA_CENTER :
+ aPos.X() += rRect.GetItalicCenterX() - GetItalicCenterX();
+ break;
+ case RHA_RIGHT :
+ aPos.X() += rRect.GetItalicRight() - GetItalicRight();
+ break;
+ default :
+ DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
+ }
+
+ return aPos;
+}
+
+
+SmRect & SmRect::Union(const SmRect &rRect)
+ // rectangle union of current one with 'rRect'. The result is to be the
+ // smallest rectangles that covers the space of both rectangles.
+ // (empty rectangles cover no space)
+ //! Italic correction is NOT taken into account here!
+{
+ if (rRect.IsEmpty())
+ return *this;
+
+ long nL = rRect.GetLeft(),
+ nR = rRect.GetRight(),
+ nT = rRect.GetTop(),
+ nB = rRect.GetBottom(),
+ nGT = rRect.nGlyphTop,
+ nGB = rRect.nGlyphBottom;
+ if (!IsEmpty())
+ { long nTmp;
+
+ if ((nTmp = GetLeft()) < nL)
+ nL = nTmp;
+ if ((nTmp = GetRight()) > nR)
+ nR = nTmp;
+ if ((nTmp = GetTop()) < nT)
+ nT = nTmp;
+ if ((nTmp = GetBottom()) > nB)
+ nB = nTmp;
+ if ((nTmp = nGlyphTop) < nGT)
+ nGT = nTmp;
+ if ((nTmp = nGlyphBottom) > nGB)
+ nGB = nTmp;
+ }
+
+ SetLeft(nL);
+ SetRight(nR);
+ SetTop(nT);
+ SetBottom(nB);
+ nGlyphTop = nGT;
+ nGlyphBottom = nGB;
+
+ return *this;
+}
+
+
+SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
+ // let current rectangle be the union of itself and 'rRect'
+ // (the smallest rectangle surrounding both). Also adapt values for
+ // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
+ // The baseline is set according to 'eCopyMode'.
+ // If one of the rectangles has no relevant info the other one is copied.
+{
+ // get some values used for (italic) spaces adaption
+ // ! (need to be done before changing current SmRect) !
+ long nL = Min(GetItalicLeft(), rRect.GetItalicLeft()),
+ nR = Max(GetItalicRight(), rRect.GetItalicRight());
+
+ Union(rRect);
+
+ SetItalicSpaces(GetLeft() - nL, nR - GetRight());
+
+ if (!HasAlignInfo())
+ CopyAlignInfo(rRect);
+ else if (rRect.HasAlignInfo())
+ { nAlignT = Min(GetAlignT(), rRect.GetAlignT());
+ nAlignB = Max(GetAlignB(), rRect.GetAlignB());
+ nHiAttrFence = Min(GetHiAttrFence(), rRect.GetHiAttrFence());
+ nLoAttrFence = Max(GetLoAttrFence(), rRect.GetLoAttrFence());
+ DBG_ASSERT(HasAlignInfo(), "Sm: ooops...");
+
+ switch (eCopyMode)
+ { case RCP_THIS:
+ // already done
+ break;
+ case RCP_ARG:
+ CopyMBL(rRect);
+ break;
+ case RCP_NONE:
+ ClearBaseline();
+ nAlignM = (nAlignT + nAlignB) / 2;
+ break;
+ case RCP_XOR:
+ if (!HasBaseline())
+ CopyMBL(rRect);
+ break;
+ default :
+ DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
+ }
+ }
+
+ return *this;
+}
+
+
+SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
+ long nNewAlignM)
+ // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
+ // (this version will be used in 'SmBinVerNode' to provide means to
+ // align eg "{a over b} over c" correctly where AlignM should not
+ // be (AlignT + AlignB) / 2)
+{
+ DBG_ASSERT(HasAlignInfo(), "Sm: keine Align Info");
+
+ ExtendBy(rRect, eCopyMode);
+ nAlignM = nNewAlignM;
+
+ return *this;
+}
+
+
+SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
+ BOOL bKeepVerAlignParams)
+ // as 'ExtendBy' but keeps original values for AlignT, -M and -B and
+ // baseline.
+ // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
+ // be allowed to modify these values.)
+{
+ long nOldAlignT = GetAlignT(),
+ nOldAlignM = GetAlignM(),
+ nOldAlignB = GetAlignB(),
+ nOldBaseline = nBaseline; //! depends not on 'HasBaseline'
+ BOOL bOldHasAlignInfo = HasAlignInfo();
+
+ ExtendBy(rRect, eCopyMode);
+
+ if (bKeepVerAlignParams)
+ { nAlignT = nOldAlignT;
+ nAlignM = nOldAlignM;
+ nAlignB = nOldAlignB;
+ nBaseline = nOldBaseline;
+ bHasAlignInfo = bOldHasAlignInfo;
+ }
+
+ return *this;
+}
+
+
+SmRect & SmRect::ExtendBy(const Point &rPoint)
+ // extend current rectangle to include 'rPoint'.
+ // The effect should be similar to
+ // "ExtendBy(rRect, RCP_THIS, (BOOL) TRUE)"
+ // where 'rRect' is a SmRect of size and width 1 with no italic spaces
+ // (as by "SmRect (1, 1)") and position at 'rPoint'.
+{
+ // get some values used for italic spaces adaption
+ // ! (need to be done before changing current SmRect) !
+ long nL = Min(GetItalicLeft(), rPoint.X()),
+ nR = Max(GetItalicRight(), rPoint.X());
+
+ // this is the adaption of rectangle union
+ if (rPoint.X() < GetLeft())
+ SetLeft(rPoint.X());
+ if (rPoint.X() > GetRight())
+ SetRight(rPoint.X());
+ if (rPoint.Y() < GetTop())
+ SetTop(rPoint.Y());
+ if (rPoint.Y() > GetBottom())
+ SetBottom(rPoint.Y());
+
+ SetItalicSpaces(GetLeft() - nL, nR - GetRight());
+
+ return *this;
+}
+
+
+long SmRect::OrientedDist(const Point &rPoint) const
+ // return oriented distance of rPoint to the current rectangle,
+ // especially the return value is <= 0 iff the point is inside the
+ // rectangle.
+ // For simplicity the maximum-norm is used.
+{
+ BOOL bIsInside = IsInsideItalicRect(rPoint);
+
+ // build reference point to define the distance
+ Point aRef;
+ if (bIsInside)
+ { Point aIC (GetItalicCenterX(), GetCenterY());
+
+ aRef.X() = rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft();
+ aRef.Y() = rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop();
+ }
+ else
+ {
+ // x-coordinate
+ if (rPoint.X() > GetItalicRight())
+ aRef.X() = GetItalicRight();
+ else if (rPoint.X() < GetItalicLeft())
+ aRef.X() = GetItalicLeft();
+ else
+ aRef.X() = rPoint.X();
+ // y-coordinate
+ if (rPoint.Y() > GetBottom())
+ aRef.Y() = GetBottom();
+ else if (rPoint.Y() < GetTop())
+ aRef.Y() = GetTop();
+ else
+ aRef.Y() = rPoint.Y();
+ }
+
+ // build distance vector
+ Point aDist (aRef - rPoint);
+
+ long nAbsX = labs(aDist.X()),
+ nAbsY = labs(aDist.Y());
+
+ return bIsInside ? - Min(nAbsX, nAbsY) : Max (nAbsX, nAbsY);
+}
+
+
+BOOL SmRect::IsInsideRect(const Point &rPoint) const
+{
+ return rPoint.Y() >= GetTop()
+ && rPoint.Y() <= GetBottom()
+ && rPoint.X() >= GetLeft()
+ && rPoint.X() <= GetRight();
+}
+
+
+BOOL SmRect::IsInsideItalicRect(const Point &rPoint) const
+{
+ return rPoint.Y() >= GetTop()
+ && rPoint.Y() <= GetBottom()
+ && rPoint.X() >= GetItalicLeft()
+ && rPoint.X() <= GetItalicRight();
+}
+
+SmRect SmRect::AsGlyphRect() const
+{
+ SmRect aRect (*this);
+ aRect.SetTop(nGlyphTop);
+ aRect.SetBottom(nGlyphBottom);
+ return aRect;
+}
+
+
+// forward declaration
+void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
+ const Color aCol = COL_BLACK);
+
+void SmRect::Draw(OutputDevice &rDev, const Point &rPosition, int nFlags) const
+{
+ if (IsEmpty())
+ return;
+
+ rDev.Push(PUSH_LINECOLOR);
+
+ if (nFlags & SM_RECT_LINES)
+ { long nLeftSpace = 0,
+ nRightSpace = 0;
+
+ if (nFlags & SM_RECT_ITALIC)
+ { nLeftSpace = GetItalicLeftSpace();
+ nRightSpace = GetItalicRightSpace();
+ }
+
+ long nLeft = GetLeft() - nLeftSpace,
+ nRight = GetRight() + nRightSpace;
+
+ Point aOffset (rPosition - GetTopLeft());
+
+ rDev.SetLineColor(COL_LIGHTBLUE);
+ rDev.DrawLine(Point(nLeft, GetAlignB()) += aOffset,
+ Point(nRight, GetAlignB()) += aOffset);
+ rDev.DrawLine(Point(nLeft, GetAlignT()) += aOffset,
+ Point(nRight, GetAlignT()) += aOffset);
+ if (HasBaseline())
+ rDev.DrawLine(Point(nLeft, GetBaseline()) += aOffset,
+ Point(nRight, GetBaseline()) += aOffset);
+
+ rDev.SetLineColor(COL_GRAY);
+ rDev.DrawLine(Point(nLeft, GetHiAttrFence()) += aOffset,
+ Point(nRight, GetHiAttrFence()) += aOffset);
+ }
+
+ if (nFlags & SM_RECT_MID)
+ { Point aCenter = rPosition
+ + (Point(GetItalicCenterX(), GetAlignM()) -= GetTopLeft()),
+ aLenX (GetWidth() / 5, 0),
+ aLenY (0, GetHeight() / 16);
+
+ rDev.SetLineColor(COL_LIGHTGREEN);
+ rDev.DrawLine(aCenter - aLenX, aCenter + aLenX);
+ rDev.DrawLine(aCenter - aLenY, aCenter + aLenY);
+ }
+
+ if (nFlags & SM_RECT_ITALIC)
+ SmDrawFrame(rDev, Rectangle(rPosition - Point(GetItalicLeftSpace(), 0),
+ GetItalicSize()));
+
+ if (nFlags & SM_RECT_CORE)
+ SmDrawFrame(rDev, Rectangle(rPosition, GetSize()), COL_LIGHTRED);
+
+ rDev.Pop();
+}
+
+
+
+////////////////////////////////////////
+// misc functions
+//
+
+
+void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
+ const Color aCol)
+{
+ rDev.Push(PUSH_LINECOLOR);
+
+ rDev.SetLineColor(aCol);
+
+ rDev.DrawLine(rRec.TopLeft(), rRec.BottomLeft());
+ rDev.DrawLine(rRec.BottomLeft(), rRec.BottomRight());
+ rDev.DrawLine(rRec.BottomRight(), rRec.TopRight());
+ rDev.DrawLine(rRec.TopRight(), rRec.TopLeft());
+
+ rDev.Pop();
+}
+
+
+BOOL SmGetGlyphBoundRect(const OutputDevice &rDev,
+ const XubString &rText, Rectangle &rRect)
+ // basically the same as 'GetGlyphBoundRect' (in class 'OutputDevice')
+ // but with a string as argument.
+{
+ // handle special case first
+ xub_StrLen nLen = rText.Len();
+ if (nLen == 0)
+ { rRect.SetEmpty();
+ return TRUE;
+ }
+
+ // get a device where 'OutputDevice::GetGlyphBoundRect' will be successful
+ OutputDevice *pGlyphDev;
+ if (rDev.GetOutDevType() != OUTDEV_PRINTER)
+ pGlyphDev = (OutputDevice *) &rDev;
+ else
+ {
+ // since we format for the printer (where GetGlyphBoundRect will fail)
+ // we need a virtual device here.
+ pGlyphDev = SM_MOD1()->GetRectCache()->GetVirDev();
+ }
+
+ const FontMetric aDevFM (rDev.GetFontMetric());
+
+ pGlyphDev->Push(PUSH_FONT);
+ pGlyphDev->SetFont(rDev.GetFont());
+ //! Da in der FontMetric die Weite immer != 0 ist (was fuer wide-Attribute
+ //! und skalierbare Klammern auch so bentigt wird) kann dies zu einer
+ //! Verzerrung der Proportionen im 'pGlyphDev' gegnber dem 'rDev' kommen!
+
+ const BOOL bOptimize = FALSE;
+ BOOL bSuccess = TRUE;
+ Point aPoint;
+ Rectangle aResult (aPoint, Size(rDev.GetTextWidth(rText), rDev.GetTextHeight())),
+ aTmp;
+ long nDelta;
+
+ // setzen des linken Randes (dabei Leerzeichen erhalten!)
+ xub_Unicode cChar = rText.GetChar(0);
+ if (cChar != xub_Unicode(' '))
+ {
+ bSuccess &= pGlyphDev->GetGlyphBoundRect(cChar, aTmp, bOptimize);
+ if (!aTmp.IsEmpty())
+ {
+ // linken Rand am 'rDev' ermitteln
+ // (wir nehmen den linken Rand bezglich 'pGlyphDev' und skalieren
+ // ihn passen fr 'rDev')
+ long nLeftSpace = aTmp.Left() * rDev.GetTextWidth(cChar)
+ / pGlyphDev->GetTextWidth(cChar);
+ aResult.Left() += nLeftSpace;
+ }
+ }
+
+ // setzen des rechten Randes (dabei Leerzeichen erhalten!)
+ cChar = rText.GetChar(nLen - 1);
+ if (cChar != xub_Unicode(' '))
+ {
+ bSuccess &= pGlyphDev->GetGlyphBoundRect(cChar, aTmp, bOptimize);
+ if (!aTmp.IsEmpty())
+ {
+ // rechten Rand am 'rDev' ermitteln (analog wie beim linken Rand)
+ long nGlyphWidth = pGlyphDev->GetTextWidth(cChar),
+ nRightSpace = (nGlyphWidth - 1 - aTmp.Right())
+ * rDev.GetTextWidth(cChar)
+ / nGlyphWidth;
+ aResult.Right() -= nRightSpace;
+ }
+ }
+
+ // oberen und unteren Rand bestimmen.
+ // Im Augenblick gehen wird davon aus, da die Texthhen an den beiden
+ // Devices im wesentlichen gleich sind und skalieren diese Rnder daher
+ // nicht um.
+ long nTop = aResult.Bottom() + 1,
+ nBottom = aResult.Top() - 1;
+ for (USHORT i = 0; i < nLen; i++)
+ {
+ cChar = rText.GetChar(i);
+ if (cChar != xub_Unicode(' '))
+ {
+ //! Anmerkung: Leerzeichen *knnen* leere Rechtecke ergeben, aber
+ //! der Returnwert sollte auch dann TRUE sein.
+ bSuccess &= pGlyphDev->GetGlyphBoundRect(cChar, aTmp, bOptimize);
+
+ if (!aTmp.IsEmpty() && aTmp.Top() < nTop)
+ nTop = aTmp.Top();
+ if (!aTmp.IsEmpty() && aTmp.Bottom() > nBottom)
+ nBottom = aTmp.Bottom();
+ }
+ }
+ aResult.Top() = nTop;
+ aResult.Bottom() = nBottom;
+
+ // move rectangle to match possibly different baselines
+ // (because of different devices)
+ nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent();
+ aResult.Move(0, nDelta);
+
+ rRect = aResult;
+ pGlyphDev->Pop();
+ return bSuccess;
+}
+
+