/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifdef _MSC_VER
#pragma hdrstop
#endif

#include <vcl/svapp.hxx>
#include <vcl/virdev.hxx>


#include "rect.hxx"
#include "smmod.hxx"
namespace binfilter {


////////////////////////////////////////////////////////////////////////////////


// '\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 const aMathAlpha[] =
{
    MS_ALEPH,               MS_IM,                  MS_RE,
    MS_WP,                  xub_Unicode(0xE070),    MS_EMPTYSET,
    xub_Unicode(0x2113),    xub_Unicode(0xE0D6),    xub_Unicode(0x2107),
    xub_Unicode(0x2127),    xub_Unicode(0x210A),    MS_HBAR,
    MS_LAMBDABAR,           MS_SETN,                MS_SETZ,
    MS_SETQ,                MS_SETR,                MS_SETC,
    xub_Unicode(0x2373),    xub_Unicode(0xE0A5),    xub_Unicode(0x2112),
    xub_Unicode(0x2130),    xub_Unicode(0x2131),
    xub_Unicode('\0')
};

/*N*/ BOOL SmIsMathAlpha(const XubString &rText)
/*N*/   // ergibt genau dann TRUE, wenn das Zeichen (aus dem StarMath Font) wie ein
/*N*/   // Buchstabe behandelt werden soll.
/*N*/ {
/*N*/   if (rText.Len() == 0)
/*N*/       return FALSE;
/*N*/
/*N*/   DBG_ASSERT(rText.Len() == 1, "Sm : String enthaelt nicht genau ein Zeichen");
/*N*/   xub_Unicode cChar = rText.GetChar(0);
/*N*/
/*N*/   // ist es ein griechisches Zeichen ?
/*N*/     if (xub_Unicode(0xE0AC) <= cChar  &&  cChar <= xub_Unicode(0xE0D4))
/*N*/       return TRUE;
/*N*/   else
/*N*/   {
/*N*/       // kommt es in 'aMathAlpha' vor ?
/*N*/       const xub_Unicode *pChar = aMathAlpha;
/*N*/       while (*pChar  &&  *pChar != cChar)
/*N*/           pChar++;
/*N*/       return *pChar != xub_Unicode('\0');
/*N*/   }
/*N*/ }


////////////////////////////////////////
//
// SmRect members
//


/*N*/ SmRect::SmRect()
/*N*/   // constructs empty rectangle at (0, 0) with width and height 0.
/*N*/ {
/*N*/   DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
/*N*/   DBG_ASSERT(aSize == Size(0, 0), "Sm: ooops...");
/*N*/
/*N*/   bHasBaseline = bHasAlignInfo = FALSE;
/*N*/   nBaseline = nAlignT = nAlignM = nAlignB =
/*N*/   nGlyphTop = nGlyphBottom =
/*N*/   nItalicLeftSpace = nItalicRightSpace =
/*N*/   nLoAttrFence = nHiAttrFence = 0;
/*N*/   nBorderWidth = 0;
/*N*/ }


/*N*/ SmRect::SmRect(const SmRect &rRect)
/*N*/ : aTopLeft(rRect.aTopLeft),
/*N*/   aSize(rRect.aSize)
/*N*/ {
/*N*/   bHasBaseline  = rRect.bHasBaseline;
/*N*/   nBaseline     = rRect.nBaseline;
/*N*/   nAlignT       = rRect.nAlignT;
/*N*/   nAlignM       = rRect.nAlignM;
/*N*/   nAlignB       = rRect.nAlignB;
/*N*/   nGlyphTop     = rRect.nGlyphTop;
/*N*/   nGlyphBottom  = rRect.nGlyphBottom;
/*N*/   nHiAttrFence  = rRect.nHiAttrFence;
/*N*/   nLoAttrFence  = rRect.nLoAttrFence;
/*N*/   bHasAlignInfo = rRect.bHasAlignInfo;
/*N*/   nItalicLeftSpace  = rRect.nItalicLeftSpace;
/*N*/   nItalicRightSpace = rRect.nItalicRightSpace;
/*N*/     nBorderWidth  = rRect.nBorderWidth;
/*N*/ }


/*N*/ void SmRect::CopyAlignInfo(const SmRect &rRect)
/*N*/ {
/*N*/   nBaseline     = rRect.nBaseline;
/*N*/   bHasBaseline  = rRect.bHasBaseline;
/*N*/   nAlignT       = rRect.nAlignT;
/*N*/   nAlignM       = rRect.nAlignM;
/*N*/   nAlignB       = rRect.nAlignB;
/*N*/   bHasAlignInfo = rRect.bHasAlignInfo;
/*N*/   nLoAttrFence  = rRect.nLoAttrFence;
/*N*/   nHiAttrFence  = rRect.nHiAttrFence;
/*N*/ }


/*N*/ void SmRect::BuildRect(const OutputDevice &rDev, const SmFormat *pFormat,
/*N*/                        const XubString &rText, USHORT nBorder)
/*N*/ {
/*N*/ #ifdef DBG_UTIL
/*N*/   if (rDev.GetOutDevType() != OUTDEV_PRINTER)
/*N*/       DBG_WARNING("Sm :  Referenz-Device ist kein Drucker");
/*N*/ #endif
/*N*/
/*N*/   DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: Ooops...");
/*N*/
/*N*/   aSize = Size(rDev.GetTextWidth(rText), rDev.GetTextHeight());
/*N*/
/*N*/   const FontMetric  aFM (rDev.GetFontMetric());
/*N*/     BOOL              bIsMath  = aFM.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH ) ||
/*N*/                                  aFM.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH2 );
/*N*/   BOOL              bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
/*N*/   const long        nFontHeight = rDev.GetFont().GetSize().Height();
/*N*/
/*N*/   nBorderWidth  = nBorder;
/*N*/   bHasAlignInfo = TRUE;
/*N*/   bHasBaseline  = TRUE;
/*N*/   nBaseline     = aFM.GetAscent();
/*N*/   nAlignT       = nBaseline - nFontHeight * 750L / 1000L;
/*N*/   nAlignM       = nBaseline - nFontHeight * 121L / 422L;
/*N*/       // that's where the horizontal bars of '+', '-', ... are
/*N*/       // (1/3 of ascent over baseline)
/*N*/       // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
/*N*/   nAlignB       = nBaseline;
/*N*/
/*N*/   // workaround for printer fonts with very small (possible 0 or even
/*N*/   // negative(!)) leading
/*N*/   if (aFM.GetIntLeading() < 5  &&  rDev.GetOutDevType() == OUTDEV_PRINTER)
/*N*/   {
/*?*/       OutputDevice    *pWindow = Application::GetDefaultDevice();
/*?*/
/*?*/       pWindow->Push(PUSH_MAPMODE | PUSH_FONT);
/*?*/
/*?*/       pWindow->SetMapMode(rDev.GetMapMode());
/*?*/       pWindow->SetFont(rDev.GetFontMetric());
/*?*/
/*?*/       long  nDelta = pWindow->GetFontMetric().GetIntLeading();
/*?*/       if (nDelta == 0)
/*?*/       {   // dieser Wert entspricht etwa einem Leading von 80 bei einer
/*?*/           // Fonthoehe von 422 (12pt)
/*?*/           nDelta = nFontHeight * 8L / 43;
/*?*/       }
/*?*/       SetTop(GetTop() - nDelta);
/*?*/
/*?*/       pWindow->Pop();
/*N*/   }
/*N*/
/*N*/   // get GlyphBoundRect
/*N*/   Rectangle  aGlyphRect;
/*N*/   BOOL       bSuccess = SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
/*N*/   DBG_ASSERT(bSuccess, "Sm : Ooops... (fehlt evtl. der Font?)");
/*N*/   (void)bSuccess;
/*N*/
/*N*/   nItalicLeftSpace  = GetLeft() - aGlyphRect.Left() + nBorderWidth;
/*N*/   nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
/*N*/   if (nItalicLeftSpace  < 0  &&  !bAllowSmaller)
/*N*/       nItalicLeftSpace  = 0;
/*N*/   if (nItalicRightSpace < 0  &&  !bAllowSmaller)
/*N*/       nItalicRightSpace = 0;
/*N*/
/*N*/   long  nDist = 0;
/*N*/   if (pFormat)
/*N*/       nDist = (rDev.GetFont().GetSize().Height()
/*N*/               * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100L;
/*N*/
/*N*/   nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist;
/*N*/   nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
/*N*/
/*N*/   nGlyphTop    = aGlyphRect.Top() - nBorderWidth;
/*N*/   nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
/*N*/
/*N*/   if (bAllowSmaller)
/*N*/   {
/*N*/       // fuer Symbole und Operatoren aus dem StarMath Font passen wir den
/*N*/       // oberen und unteren Rand dem Zeichen an.
/*N*/       SetTop(nGlyphTop);
/*N*/       SetBottom(nGlyphBottom);
/*N*/   }
/*N*/
/*N*/   if (nHiAttrFence < GetTop())
/*N*/       nHiAttrFence = GetTop();
/*N*/
/*N*/   if (nLoAttrFence > GetBottom())
/*N*/       nLoAttrFence = GetBottom();
/*N*/
/*N*/   DBG_ASSERT(rText.Len() == 0  ||  !IsEmpty(),
/*N*/              "Sm: leeres Rechteck erzeugt");
/*N*/ }


/*N*/ void SmRect::Init(const OutputDevice &rDev, const SmFormat *pFormat,
/*N*/                   const XubString &rText, USHORT nInBorderWidth)
/*N*/   // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
/*N*/ {
/*N*/   SmRectCache *pRectCache = SM_MOD1()->GetRectCache();
/*N*/   DBG_ASSERT(pRectCache, "Sm : NULL pointer");
/*N*/
/*N*/   // build key for rectangle (to look up in cache for)
/*N*/   const SmRectCache::Key  aKey (rText, rDev.GetFont());
/*N*/
/*N*/   const SmRect *pResult = pRectCache->Search(aKey);
/*N*/   if (pResult)
/*?*/       *this = *pResult;
/*N*/   else
/*N*/   {   // build rectangle and put it in cache
/*N*/       BuildRect(rDev, pFormat, rText, nInBorderWidth);
/*N*/       pResult = pRectCache->Add(aKey, *this);
/*N*/   }
/*N*/   DBG_ASSERT(pResult, "Sm : NULL pointer");
/*N*/ }


/*N*/ SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
/*N*/                const XubString &rText, long nInBorderWidth)
/*N*/ {
/*N*/     DBG_ASSERT( nInBorderWidth >= 0, "BorderWidth negativ" );
/*N*/     if (nInBorderWidth < 0)
/*N*/         nInBorderWidth = 0;
/*N*/     Init(rDev, pFormat, rText, (USHORT) nInBorderWidth);
/*N*/ }


/*N*/ SmRect::SmRect(long nWidth, long nHeight)
/*N*/   // this constructor should never be used for anything textlike because
/*N*/   // it will not provide useful values for baseline, AlignT and AlignB!
/*N*/   // It's purpose is to get a 'SmRect' for the horizontal line in fractions
/*N*/   // as used in 'SmBinVerNode'.
/*N*/ : aSize(nWidth, nHeight)
/*N*/ {
/*N*/   DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
/*N*/
/*N*/   bHasBaseline  = FALSE;
/*N*/   bHasAlignInfo = TRUE;
/*N*/   nBaseline     = 0;
/*N*/   nAlignT       = GetTop();
/*N*/   nAlignB       = GetBottom();
/*N*/   nAlignM       = (nAlignT + nAlignB) / 2;        // this is the default
/*N*/   nItalicLeftSpace = nItalicRightSpace = 0;
/*N*/   nGlyphTop    = nHiAttrFence  = GetTop();
/*N*/   nGlyphBottom = nLoAttrFence  = GetBottom();
/*N*/     nBorderWidth  = 0;
/*N*/ }


/*N*/ void SmRect::SetLeft(long nLeft)
/*N*/ {
/*N*/   if (nLeft <= GetRight())
/*N*/   {   aSize.Width() = GetRight() - nLeft + 1;
/*N*/       aTopLeft.X()  = nLeft;
/*N*/   }
/*N*/ }


/*N*/ void SmRect::SetRight(long nRight)
/*N*/ {
/*N*/   if (nRight >= GetLeft())
/*N*/       aSize.Width() = nRight - GetLeft() + 1;
/*N*/ }


/*N*/ void SmRect::SetBottom(long nBottom)
/*N*/ {
/*N*/   if (nBottom >= GetTop())
/*N*/       aSize.Height() = nBottom - GetTop() + 1;
/*N*/ }


/*N*/ void SmRect::SetTop(long nTop)
/*N*/ {
/*N*/   if (nTop <= GetBottom())
/*N*/   {   aSize.Height()   = GetBottom() - nTop + 1;
/*N*/       aTopLeft.Y() = nTop;
/*N*/   }
/*N*/ }


/*N*/ void SmRect::Move(const Point &rPosition)
/*N*/   // move rectangle by position 'rPosition'.
/*N*/ {
/*N*/   aTopLeft  += rPosition;
/*N*/
/*N*/   long  nDelta = rPosition.Y();
/*N*/   nBaseline += nDelta;
/*N*/   nAlignT   += nDelta;
/*N*/   nAlignM   += nDelta;
/*N*/   nAlignB   += nDelta;
/*N*/   nGlyphTop    += nDelta;
/*N*/   nGlyphBottom += nDelta;
/*N*/   nHiAttrFence += nDelta;
/*N*/   nLoAttrFence += nDelta;
/*N*/ }


/*N*/ const Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
/*N*/                           RectHorAlign eHor, RectVerAlign eVer) const
/*N*/ { Point  aPos (GetTopLeft());
/*N*/       // will become the topleft point of the new rectangle position
/*N*/
/*N*/   // set horizontal or vertical new rectangle position depending on
/*N*/   // 'ePos' is one of 'RP_LEFT', 'RP_RIGHT' or 'RP_TOP', 'RP_BOTTOM'
/*N*/   switch (ePos)
/*N*/   {   case RP_LEFT :
/*N*/           aPos.X() = rRect.GetItalicLeft() - GetItalicRightSpace()
/*N*/                      - GetWidth();
/*N*/           break;
/*N*/       case RP_RIGHT :
/*N*/           aPos.X() = rRect.GetItalicRight() + 1 + GetItalicLeftSpace();
/*N*/           break;
/*N*/       case RP_TOP :
/*N*/           aPos.Y() = rRect.GetTop() - GetHeight();
/*N*/           break;
/*N*/       case RP_BOTTOM :
/*N*/           aPos.Y() = rRect.GetBottom() + 1;
/*N*/           break;
/*N*/       case RP_ATTRIBUT :
/*N*/           aPos.X() = rRect.GetItalicCenterX() - GetItalicWidth() / 2
/*N*/                      + GetItalicLeftSpace();
/*N*/           break;
/*N*/       default :
/*N*/           DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
/*N*/   }
/*N*/
/*N*/   // check if horizontal position is already set
/*N*/   if (ePos == RP_LEFT  ||  ePos == RP_RIGHT  ||  ePos == RP_ATTRIBUT)
/*N*/       // correct error in current vertical position
/*N*/       switch (eVer)
/*N*/       {   case RVA_TOP :
/*N*/               aPos.Y() += rRect.GetAlignT() - GetAlignT();
/*N*/               break;
/*N*/           case RVA_MID :
/*N*/               aPos.Y() += rRect.GetAlignM() - GetAlignM();
/*N*/               break;
/*N*/           case RVA_BASELINE :
/*N*/               // align baselines if possible else align mid's
/*N*/               if (HasBaseline() && rRect.HasBaseline())
/*N*/                   aPos.Y() += rRect.GetBaseline() - GetBaseline();
/*N*/               else
/*N*/                   aPos.Y() += rRect.GetAlignM() - GetAlignM();
/*N*/               break;
/*N*/           case RVA_BOTTOM :
/*N*/               aPos.Y() += rRect.GetAlignB() - GetAlignB();
/*N*/               break;
/*N*/           case RVA_CENTERY :
/*N*/               aPos.Y() += rRect.GetCenterY() - GetCenterY();
/*N*/               break;
/*N*/           case RVA_ATTRIBUT_HI:
/*N*/               aPos.Y() += rRect.GetHiAttrFence() - GetBottom();
/*N*/               break;
/*N*/           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 :
/*N*/               DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
/*N*/       }
/*N*/
/*N*/   // check if vertical position is already set
/*N*/   if (ePos == RP_TOP  ||  ePos == RP_BOTTOM)
/*N*/       // correct error in current horizontal position
/*N*/       switch (eHor)
/*N*/       {   case RHA_LEFT :
/*N*/               aPos.X() += rRect.GetItalicLeft() - GetItalicLeft();
/*N*/               break;
/*N*/           case RHA_CENTER :
/*N*/               aPos.X() += rRect.GetItalicCenterX() - GetItalicCenterX();
/*N*/               break;
/*N*/           case RHA_RIGHT :
/*?*/               aPos.X() += rRect.GetItalicRight() - GetItalicRight();
/*?*/               break;
/*N*/           default :
/*N*/               DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
/*N*/       }
/*N*/
/*N*/   return aPos;
/*N*/ }


/*N*/ SmRect & SmRect::Union(const SmRect &rRect)
/*N*/   // rectangle union of current one with 'rRect'. The result is to be the
/*N*/   // smallest rectangles that covers the space of both rectangles.
/*N*/   // (empty rectangles cover no space)
/*N*/   //! Italic correction is NOT taken into account here!
/*N*/ {
/*N*/   if (rRect.IsEmpty())
/*N*/       return *this;
/*N*/
/*N*/   long  nL  = rRect.GetLeft(),
/*N*/         nR  = rRect.GetRight(),
/*N*/         nT  = rRect.GetTop(),
/*N*/         nB  = rRect.GetBottom(),
/*N*/         nGT = rRect.nGlyphTop,
/*N*/         nGB = rRect.nGlyphBottom;
/*N*/   if (!IsEmpty())
/*N*/   {   long  nTmp;
/*N*/
/*N*/       if ((nTmp = GetLeft()) < nL)
/*N*/           nL = nTmp;
/*N*/       if ((nTmp = GetRight()) > nR)
/*N*/           nR = nTmp;
/*N*/       if ((nTmp = GetTop()) < nT)
/*N*/           nT = nTmp;
/*N*/       if ((nTmp = GetBottom()) > nB)
/*N*/           nB = nTmp;
/*N*/       if ((nTmp = nGlyphTop) < nGT)
/*N*/           nGT = nTmp;
/*N*/       if ((nTmp = nGlyphBottom) > nGB)
/*N*/           nGB = nTmp;
/*N*/   }
/*N*/
/*N*/   SetLeft(nL);
/*N*/   SetRight(nR);
/*N*/   SetTop(nT);
/*N*/   SetBottom(nB);
/*N*/   nGlyphTop    = nGT;
/*N*/   nGlyphBottom = nGB;
/*N*/
/*N*/   return *this;
/*N*/ }


/*N*/ SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
/*N*/   // let current rectangle be the union of itself and 'rRect'
/*N*/   // (the smallest rectangle surrounding both). Also adapt values for
/*N*/   // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
/*N*/   // The baseline is set according to 'eCopyMode'.
/*N*/   // If one of the rectangles has no relevant info the other one is copied.
/*N*/ {
/*N*/   // get some values used for (italic) spaces adaption
/*N*/   // ! (need to be done before changing current SmRect) !
/*N*/   long  nL = Min(GetItalicLeft(),  rRect.GetItalicLeft()),
/*N*/         nR = Max(GetItalicRight(), rRect.GetItalicRight());
/*N*/
/*N*/   Union(rRect);
/*N*/
/*N*/   SetItalicSpaces(GetLeft() - nL, nR - GetRight());
/*N*/
/*N*/   if (!HasAlignInfo())
/*N*/       CopyAlignInfo(rRect);
/*N*/   else if (rRect.HasAlignInfo())
/*N*/   {   nAlignT = Min(GetAlignT(), rRect.GetAlignT());
/*N*/       nAlignB = Max(GetAlignB(), rRect.GetAlignB());
/*N*/       nHiAttrFence = Min(GetHiAttrFence(), rRect.GetHiAttrFence());
/*N*/       nLoAttrFence = Max(GetLoAttrFence(), rRect.GetLoAttrFence());
/*N*/       DBG_ASSERT(HasAlignInfo(), "Sm: ooops...");
/*N*/
/*N*/       switch (eCopyMode)
/*N*/       {   case RCP_THIS:
/*N*/               // already done
/*N*/               break;
/*N*/           case RCP_ARG:
/*N*/               CopyMBL(rRect);
/*N*/               break;
/*N*/           case RCP_NONE:
/*N*/               ClearBaseline();
/*N*/               nAlignM = (nAlignT + nAlignB) / 2;
/*N*/               break;
/*N*/           case RCP_XOR:
/*N*/               if (!HasBaseline())
/*N*/                   CopyMBL(rRect);
/*N*/               break;
/*N*/           default :
/*N*/               DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
/*N*/       }
/*N*/   }
/*N*/
/*N*/   return *this;
/*N*/ }


/*N*/ SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
/*N*/                         long nNewAlignM)
/*N*/   // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
/*N*/   // (this version will be used in 'SmBinVerNode' to provide means to
/*N*/   // align eg "{a over b} over c" correctly where AlignM should not
/*N*/   // be (AlignT + AlignB) / 2)
/*N*/ {
/*N*/   DBG_ASSERT(HasAlignInfo(), "Sm: keine Align Info");
/*N*/
/*N*/   ExtendBy(rRect, eCopyMode);
/*N*/   nAlignM = nNewAlignM;
/*N*/
/*N*/   return *this;
/*N*/ }


/*N*/ SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
/*N*/                         BOOL bKeepVerAlignParams)
/*N*/   // as 'ExtendBy' but keeps original values for AlignT, -M and -B and
/*N*/   // baseline.
/*N*/   // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
/*N*/   // be allowed to modify these values.)
/*N*/ {
/*N*/   long  nOldAlignT   = GetAlignT(),
/*N*/         nOldAlignM   = GetAlignM(),
/*N*/         nOldAlignB   = GetAlignB(),
/*N*/         nOldBaseline = nBaseline;     //! depends not on 'HasBaseline'
/*N*/   BOOL  bOldHasAlignInfo = HasAlignInfo();
/*N*/
/*N*/   ExtendBy(rRect, eCopyMode);
/*N*/
/*N*/   if (bKeepVerAlignParams)
/*N*/   {   nAlignT   = nOldAlignT;
/*N*/       nAlignM   = nOldAlignM;
/*N*/       nAlignB   = nOldAlignB;
/*N*/       nBaseline = nOldBaseline;
/*N*/       bHasAlignInfo = bOldHasAlignInfo;
/*N*/   }
/*N*/
/*N*/   return *this;
/*N*/ }

SmRect SmRect::AsGlyphRect() const
{
    SmRect aRect (*this);
    aRect.SetTop(nGlyphTop);
    aRect.SetBottom(nGlyphBottom);
    return aRect;
}


/*N*/ BOOL SmGetGlyphBoundRect(const OutputDevice &rDev,
/*N*/                        const XubString &rText, Rectangle &rRect)
/*N*/     // basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
/*N*/   // but with a string as argument.
/*N*/ {
/*N*/   // handle special case first
/*N*/   xub_StrLen nLen = rText.Len();
/*N*/   if (nLen == 0)
/*N*/   {   rRect.SetEmpty();
/*N*/       return TRUE;
/*N*/   }
/*N*/
/*N*/     // get a device where 'OutputDevice::GetTextBoundRect' will be successful
/*N*/   OutputDevice *pGlyphDev;
/*N*/   if (rDev.GetOutDevType() != OUTDEV_PRINTER)
/*?*/       pGlyphDev = (OutputDevice *) &rDev;
/*N*/   else
/*N*/   {
/*N*/         // since we format for the printer (where GetTextBoundRect will fail)
/*N*/       // we need a virtual device here.
/*N*/       pGlyphDev = SM_MOD1()->GetRectCache()->GetVirDev();
/*N*/   }
/*N*/
/*N*/   const FontMetric  aDevFM (rDev.GetFontMetric());
/*N*/
/*N*/     pGlyphDev->Push(PUSH_FONT | PUSH_MAPMODE);
/*N*/     Font aFnt(rDev.GetFont());
/*N*/     aFnt.SetAlign(ALIGN_TOP);
/*N*/
/*N*/     // use scale factor when calling GetTextBoundRect to counter
/*N*/     // negative effects from antialiasing which may otherwise result
/*N*/     // in significant incorrect bounding rectangles for some charcters.
/*N*/     long nScaleFactor = 16;
/*N*/   Size aFntSize = aFnt.GetSize();
/*N*/     aFnt.SetSize( Size( aFntSize.Width() * nScaleFactor, aFntSize.Height() * nScaleFactor ) );
/*N*/     pGlyphDev->SetFont(aFnt);
/*N*/
/*N*/     long nTextWidth = rDev.GetTextWidth(rText);
/*N*/     Point aPoint;
/*N*/     Rectangle   aResult (aPoint, Size(nTextWidth, rDev.GetTextHeight())),
/*N*/               aTmp;
/*N*/
/*N*/     BOOL bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText, 0, 0);
/*N*/     DBG_ASSERT( bSuccess, "GetTextBoundRect failed" );
/*N*/
/*N*/
/*N*/     if (!aTmp.IsEmpty())
/*N*/     {
/*N*/         aResult = Rectangle(aTmp.Left() / nScaleFactor, aTmp.Top() / nScaleFactor,
/*N*/                             aTmp.Right() / nScaleFactor, aTmp.Bottom() / nScaleFactor);
/*N*/         if (&rDev != pGlyphDev) /* only when rDev is a printer... */
/*N*/         {
/*N*/             long nGDTextWidth  = pGlyphDev->GetTextWidth(rText);
/*N*/             if (nGDTextWidth != 0  &&
/*N*/                 nTextWidth != nGDTextWidth)
/*N*/             {
/*N*/                 aResult.Right() *= nTextWidth;
/*N*/                 aResult.Right() /= nGDTextWidth / nScaleFactor;
/*N*/             }
/*N*/         }
/*N*/     }
/*N*/
/*N*/   // move rectangle to match possibly different baselines
/*N*/   // (because of different devices)
/*N*/     long nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent() / nScaleFactor;
/*N*/   aResult.Move(0, nDelta);
/*N*/
/*N*/   pGlyphDev->Pop();
/*N*/
/*N*/     rRect = aResult;
/*N*/   return bSuccess;
/*N*/ }


}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
