Update ooo320-m1
[ooovba.git] / starmath / source / rect.cxx
blob3f766b9b1ff2c567d49d26d89864e0aeb5f7a630
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: rect.cxx,v $
10 * $Revision: 1.21 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_starmath.hxx"
35 #include <tools/string.hxx>
36 #include <tools/debug.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vcl/wrkwin.hxx>
39 #include <vcl/virdev.hxx>
42 #include "rect.hxx"
43 #include "types.hxx"
44 #include "utility.hxx"
45 #include "smmod.hxx"
48 ////////////////////////////////////////////////////////////////////////////////
51 // '\0' terminiertes Array mit Zeichen, die im StarMath Font als Buchstaben
52 // betrachtet werden sollen, (um im Gegensatz zu den anderen Operatoren
53 // und Symbolen ein "normales"(ungecliptes) SmRect zu erhalten).
54 static xub_Unicode __READONLY_DATA aMathAlpha[] =
56 MS_ALEPH, MS_IM, MS_RE,
57 MS_WP, xub_Unicode(0xE070), MS_EMPTYSET,
58 xub_Unicode(0x2113), xub_Unicode(0xE0D6), xub_Unicode(0x2107),
59 xub_Unicode(0x2127), xub_Unicode(0x210A), MS_HBAR,
60 MS_LAMBDABAR, MS_SETN, MS_SETZ,
61 MS_SETQ, MS_SETR, MS_SETC,
62 xub_Unicode(0x2373), xub_Unicode(0xE0A5), xub_Unicode(0x2112),
63 xub_Unicode(0x2130), xub_Unicode(0x2131),
64 xub_Unicode('\0')
67 BOOL SmIsMathAlpha(const XubString &rText)
68 // ergibt genau dann TRUE, wenn das Zeichen (aus dem StarMath Font) wie ein
69 // Buchstabe behandelt werden soll.
71 if (rText.Len() == 0)
72 return FALSE;
74 DBG_ASSERT(rText.Len() == 1, "Sm : String enthaelt nicht genau ein Zeichen");
75 xub_Unicode cChar = rText.GetChar(0);
77 // ist es ein griechisches Zeichen ?
78 if (xub_Unicode(0xE0AC) <= cChar && cChar <= xub_Unicode(0xE0D4))
79 return TRUE;
80 else
82 // kommt es in 'aMathAlpha' vor ?
83 const xub_Unicode *pChar = aMathAlpha;
84 while (*pChar && *pChar != cChar)
85 pChar++;
86 return *pChar != xub_Unicode('\0');
91 ////////////////////////////////////////
93 // SmRect members
97 SmRect::SmRect()
98 // constructs empty rectangle at (0, 0) with width and height 0.
100 DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
101 DBG_ASSERT(aSize == Size(0, 0), "Sm: ooops...");
103 bHasBaseline = bHasAlignInfo = FALSE;
104 nBaseline = nAlignT = nAlignM = nAlignB =
105 nGlyphTop = nGlyphBottom =
106 nItalicLeftSpace = nItalicRightSpace =
107 nLoAttrFence = nHiAttrFence = 0;
108 nBorderWidth = 0;
112 SmRect::SmRect(const SmRect &rRect)
113 : aTopLeft(rRect.aTopLeft),
114 aSize(rRect.aSize)
116 bHasBaseline = rRect.bHasBaseline;
117 nBaseline = rRect.nBaseline;
118 nAlignT = rRect.nAlignT;
119 nAlignM = rRect.nAlignM;
120 nAlignB = rRect.nAlignB;
121 nGlyphTop = rRect.nGlyphTop;
122 nGlyphBottom = rRect.nGlyphBottom;
123 nHiAttrFence = rRect.nHiAttrFence;
124 nLoAttrFence = rRect.nLoAttrFence;
125 bHasAlignInfo = rRect.bHasAlignInfo;
126 nItalicLeftSpace = rRect.nItalicLeftSpace;
127 nItalicRightSpace = rRect.nItalicRightSpace;
128 nBorderWidth = rRect.nBorderWidth;
132 void SmRect::CopyAlignInfo(const SmRect &rRect)
134 nBaseline = rRect.nBaseline;
135 bHasBaseline = rRect.bHasBaseline;
136 nAlignT = rRect.nAlignT;
137 nAlignM = rRect.nAlignM;
138 nAlignB = rRect.nAlignB;
139 bHasAlignInfo = rRect.bHasAlignInfo;
140 nLoAttrFence = rRect.nLoAttrFence;
141 nHiAttrFence = rRect.nHiAttrFence;
145 void SmRect::BuildRect(const OutputDevice &rDev, const SmFormat *pFormat,
146 const XubString &rText, USHORT nBorder)
148 DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: Ooops...");
150 aSize = Size(rDev.GetTextWidth(rText), rDev.GetTextHeight());
152 const FontMetric aFM (rDev.GetFontMetric());
153 BOOL bIsMath = aFM.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH ) ||
154 aFM.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH2 );
155 BOOL bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
156 const long nFontHeight = rDev.GetFont().GetSize().Height();
158 nBorderWidth = nBorder;
159 bHasAlignInfo = TRUE;
160 bHasBaseline = TRUE;
161 nBaseline = aFM.GetAscent();
162 nAlignT = nBaseline - nFontHeight * 750L / 1000L;
163 nAlignM = nBaseline - nFontHeight * 121L / 422L;
164 // that's where the horizontal bars of '+', '-', ... are
165 // (1/3 of ascent over baseline)
166 // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
167 nAlignB = nBaseline;
169 // workaround for printer fonts with very small (possible 0 or even
170 // negative(!)) leading
171 if (aFM.GetIntLeading() < 5 && rDev.GetOutDevType() == OUTDEV_PRINTER)
173 OutputDevice *pWindow = Application::GetDefaultDevice();
175 pWindow->Push(PUSH_MAPMODE | PUSH_FONT);
177 pWindow->SetMapMode(rDev.GetMapMode());
178 pWindow->SetFont(rDev.GetFontMetric());
180 long nDelta = pWindow->GetFontMetric().GetIntLeading();
181 if (nDelta == 0)
182 { // dieser Wert entspricht etwa einem Leading von 80 bei einer
183 // Fonthoehe von 422 (12pt)
184 nDelta = nFontHeight * 8L / 43;
186 SetTop(GetTop() - nDelta);
188 pWindow->Pop();
191 // get GlyphBoundRect
192 Rectangle aGlyphRect;
193 #if OSL_DEBUG_LEVEL > 1
194 BOOL bSuccess =
195 #endif
196 SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
197 #if OSL_DEBUG_LEVEL > 1
198 if (!bSuccess)
200 DBG_ERROR( "Sm : Ooops... (fehlt evtl. der Font?)");
202 #endif
204 nItalicLeftSpace = GetLeft() - aGlyphRect.Left() + nBorderWidth;
205 nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
206 if (nItalicLeftSpace < 0 && !bAllowSmaller)
207 nItalicLeftSpace = 0;
208 if (nItalicRightSpace < 0 && !bAllowSmaller)
209 nItalicRightSpace = 0;
211 long nDist = 0;
212 if (pFormat)
213 nDist = (rDev.GetFont().GetSize().Height()
214 * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100L;
216 nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist;
217 nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
219 nGlyphTop = aGlyphRect.Top() - nBorderWidth;
220 nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
222 if (bAllowSmaller)
224 // fuer Symbole und Operatoren aus dem StarMath Font passen wir den
225 // oberen und unteren Rand dem Zeichen an.
226 SetTop(nGlyphTop);
227 SetBottom(nGlyphBottom);
230 if (nHiAttrFence < GetTop())
231 nHiAttrFence = GetTop();
233 if (nLoAttrFence > GetBottom())
234 nLoAttrFence = GetBottom();
236 DBG_ASSERT(rText.Len() == 0 || !IsEmpty(),
237 "Sm: leeres Rechteck erzeugt");
241 void SmRect::Init(const OutputDevice &rDev, const SmFormat *pFormat,
242 const XubString &rText, USHORT nEBorderWidth)
243 // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
245 BuildRect(rDev, pFormat, rText, nEBorderWidth);
249 SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
250 const XubString &rText, long nEBorderWidth)
252 DBG_ASSERT( nEBorderWidth >= 0, "BorderWidth negativ" );
253 if (nEBorderWidth < 0)
254 nEBorderWidth = 0;
255 Init(rDev, pFormat, rText, (USHORT) nEBorderWidth);
259 SmRect::SmRect(long nWidth, long nHeight)
260 // this constructor should never be used for anything textlike because
261 // it will not provide useful values for baseline, AlignT and AlignB!
262 // It's purpose is to get a 'SmRect' for the horizontal line in fractions
263 // as used in 'SmBinVerNode'.
264 : aSize(nWidth, nHeight)
266 DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
268 bHasBaseline = FALSE;
269 bHasAlignInfo = TRUE;
270 nBaseline = 0;
271 nAlignT = GetTop();
272 nAlignB = GetBottom();
273 nAlignM = (nAlignT + nAlignB) / 2; // this is the default
274 nItalicLeftSpace = nItalicRightSpace = 0;
275 nGlyphTop = nHiAttrFence = GetTop();
276 nGlyphBottom = nLoAttrFence = GetBottom();
277 nBorderWidth = 0;
281 void SmRect::SetLeft(long nLeft)
283 if (nLeft <= GetRight())
284 { aSize.Width() = GetRight() - nLeft + 1;
285 aTopLeft.X() = nLeft;
290 void SmRect::SetRight(long nRight)
292 if (nRight >= GetLeft())
293 aSize.Width() = nRight - GetLeft() + 1;
297 void SmRect::SetBottom(long nBottom)
299 if (nBottom >= GetTop())
300 aSize.Height() = nBottom - GetTop() + 1;
304 void SmRect::SetTop(long nTop)
306 if (nTop <= GetBottom())
307 { aSize.Height() = GetBottom() - nTop + 1;
308 aTopLeft.Y() = nTop;
313 void SmRect::Move(const Point &rPosition)
314 // move rectangle by position 'rPosition'.
316 aTopLeft += rPosition;
318 long nDelta = rPosition.Y();
319 nBaseline += nDelta;
320 nAlignT += nDelta;
321 nAlignM += nDelta;
322 nAlignB += nDelta;
323 nGlyphTop += nDelta;
324 nGlyphBottom += nDelta;
325 nHiAttrFence += nDelta;
326 nLoAttrFence += nDelta;
330 const Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
331 RectHorAlign eHor, RectVerAlign eVer) const
332 { Point aPos (GetTopLeft());
333 // will become the topleft point of the new rectangle position
335 // set horizontal or vertical new rectangle position depending on
336 // 'ePos' is one of 'RP_LEFT', 'RP_RIGHT' or 'RP_TOP', 'RP_BOTTOM'
337 switch (ePos)
338 { case RP_LEFT :
339 aPos.X() = rRect.GetItalicLeft() - GetItalicRightSpace()
340 - GetWidth();
341 break;
342 case RP_RIGHT :
343 aPos.X() = rRect.GetItalicRight() + 1 + GetItalicLeftSpace();
344 break;
345 case RP_TOP :
346 aPos.Y() = rRect.GetTop() - GetHeight();
347 break;
348 case RP_BOTTOM :
349 aPos.Y() = rRect.GetBottom() + 1;
350 break;
351 case RP_ATTRIBUT :
352 aPos.X() = rRect.GetItalicCenterX() - GetItalicWidth() / 2
353 + GetItalicLeftSpace();
354 break;
355 default :
356 DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
359 // check if horizontal position is already set
360 if (ePos == RP_LEFT || ePos == RP_RIGHT || ePos == RP_ATTRIBUT)
361 // correct error in current vertical position
362 switch (eVer)
363 { case RVA_TOP :
364 aPos.Y() += rRect.GetAlignT() - GetAlignT();
365 break;
366 case RVA_MID :
367 aPos.Y() += rRect.GetAlignM() - GetAlignM();
368 break;
369 case RVA_BASELINE :
370 // align baselines if possible else align mid's
371 if (HasBaseline() && rRect.HasBaseline())
372 aPos.Y() += rRect.GetBaseline() - GetBaseline();
373 else
374 aPos.Y() += rRect.GetAlignM() - GetAlignM();
375 break;
376 case RVA_BOTTOM :
377 aPos.Y() += rRect.GetAlignB() - GetAlignB();
378 break;
379 case RVA_CENTERY :
380 aPos.Y() += rRect.GetCenterY() - GetCenterY();
381 break;
382 case RVA_ATTRIBUT_HI:
383 aPos.Y() += rRect.GetHiAttrFence() - GetBottom();
384 break;
385 case RVA_ATTRIBUT_MID :
386 aPos.Y() += SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4)
387 - GetCenterY();
388 break;
389 case RVA_ATTRIBUT_LO :
390 aPos.Y() += rRect.GetLoAttrFence() - GetTop();
391 break;
392 default :
393 DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
396 // check if vertical position is already set
397 if (ePos == RP_TOP || ePos == RP_BOTTOM)
398 // correct error in current horizontal position
399 switch (eHor)
400 { case RHA_LEFT :
401 aPos.X() += rRect.GetItalicLeft() - GetItalicLeft();
402 break;
403 case RHA_CENTER :
404 aPos.X() += rRect.GetItalicCenterX() - GetItalicCenterX();
405 break;
406 case RHA_RIGHT :
407 aPos.X() += rRect.GetItalicRight() - GetItalicRight();
408 break;
409 default :
410 DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
413 return aPos;
417 SmRect & SmRect::Union(const SmRect &rRect)
418 // rectangle union of current one with 'rRect'. The result is to be the
419 // smallest rectangles that covers the space of both rectangles.
420 // (empty rectangles cover no space)
421 //! Italic correction is NOT taken into account here!
423 if (rRect.IsEmpty())
424 return *this;
426 long nL = rRect.GetLeft(),
427 nR = rRect.GetRight(),
428 nT = rRect.GetTop(),
429 nB = rRect.GetBottom(),
430 nGT = rRect.nGlyphTop,
431 nGB = rRect.nGlyphBottom;
432 if (!IsEmpty())
433 { long nTmp;
435 if ((nTmp = GetLeft()) < nL)
436 nL = nTmp;
437 if ((nTmp = GetRight()) > nR)
438 nR = nTmp;
439 if ((nTmp = GetTop()) < nT)
440 nT = nTmp;
441 if ((nTmp = GetBottom()) > nB)
442 nB = nTmp;
443 if ((nTmp = nGlyphTop) < nGT)
444 nGT = nTmp;
445 if ((nTmp = nGlyphBottom) > nGB)
446 nGB = nTmp;
449 SetLeft(nL);
450 SetRight(nR);
451 SetTop(nT);
452 SetBottom(nB);
453 nGlyphTop = nGT;
454 nGlyphBottom = nGB;
456 return *this;
460 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
461 // let current rectangle be the union of itself and 'rRect'
462 // (the smallest rectangle surrounding both). Also adapt values for
463 // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
464 // The baseline is set according to 'eCopyMode'.
465 // If one of the rectangles has no relevant info the other one is copied.
467 // get some values used for (italic) spaces adaption
468 // ! (need to be done before changing current SmRect) !
469 long nL = Min(GetItalicLeft(), rRect.GetItalicLeft()),
470 nR = Max(GetItalicRight(), rRect.GetItalicRight());
472 Union(rRect);
474 SetItalicSpaces(GetLeft() - nL, nR - GetRight());
476 if (!HasAlignInfo())
477 CopyAlignInfo(rRect);
478 else if (rRect.HasAlignInfo())
479 { nAlignT = Min(GetAlignT(), rRect.GetAlignT());
480 nAlignB = Max(GetAlignB(), rRect.GetAlignB());
481 nHiAttrFence = Min(GetHiAttrFence(), rRect.GetHiAttrFence());
482 nLoAttrFence = Max(GetLoAttrFence(), rRect.GetLoAttrFence());
483 DBG_ASSERT(HasAlignInfo(), "Sm: ooops...");
485 switch (eCopyMode)
486 { case RCP_THIS:
487 // already done
488 break;
489 case RCP_ARG:
490 CopyMBL(rRect);
491 break;
492 case RCP_NONE:
493 ClearBaseline();
494 nAlignM = (nAlignT + nAlignB) / 2;
495 break;
496 case RCP_XOR:
497 if (!HasBaseline())
498 CopyMBL(rRect);
499 break;
500 default :
501 DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
505 return *this;
509 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
510 long nNewAlignM)
511 // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
512 // (this version will be used in 'SmBinVerNode' to provide means to
513 // align eg "{a over b} over c" correctly where AlignM should not
514 // be (AlignT + AlignB) / 2)
516 DBG_ASSERT(HasAlignInfo(), "Sm: keine Align Info");
518 ExtendBy(rRect, eCopyMode);
519 nAlignM = nNewAlignM;
521 return *this;
525 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
526 BOOL bKeepVerAlignParams)
527 // as 'ExtendBy' but keeps original values for AlignT, -M and -B and
528 // baseline.
529 // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
530 // be allowed to modify these values.)
532 long nOldAlignT = GetAlignT(),
533 nOldAlignM = GetAlignM(),
534 nOldAlignB = GetAlignB(),
535 nOldBaseline = nBaseline; //! depends not on 'HasBaseline'
536 BOOL bOldHasAlignInfo = HasAlignInfo();
538 ExtendBy(rRect, eCopyMode);
540 if (bKeepVerAlignParams)
541 { nAlignT = nOldAlignT;
542 nAlignM = nOldAlignM;
543 nAlignB = nOldAlignB;
544 nBaseline = nOldBaseline;
545 bHasAlignInfo = bOldHasAlignInfo;
548 return *this;
552 long SmRect::OrientedDist(const Point &rPoint) const
553 // return oriented distance of rPoint to the current rectangle,
554 // especially the return value is <= 0 iff the point is inside the
555 // rectangle.
556 // For simplicity the maximum-norm is used.
558 BOOL bIsInside = IsInsideItalicRect(rPoint);
560 // build reference point to define the distance
561 Point aRef;
562 if (bIsInside)
563 { Point aIC (GetItalicCenterX(), GetCenterY());
565 aRef.X() = rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft();
566 aRef.Y() = rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop();
568 else
570 // x-coordinate
571 if (rPoint.X() > GetItalicRight())
572 aRef.X() = GetItalicRight();
573 else if (rPoint.X() < GetItalicLeft())
574 aRef.X() = GetItalicLeft();
575 else
576 aRef.X() = rPoint.X();
577 // y-coordinate
578 if (rPoint.Y() > GetBottom())
579 aRef.Y() = GetBottom();
580 else if (rPoint.Y() < GetTop())
581 aRef.Y() = GetTop();
582 else
583 aRef.Y() = rPoint.Y();
586 // build distance vector
587 Point aDist (aRef - rPoint);
589 long nAbsX = labs(aDist.X()),
590 nAbsY = labs(aDist.Y());
592 return bIsInside ? - Min(nAbsX, nAbsY) : Max (nAbsX, nAbsY);
596 BOOL SmRect::IsInsideRect(const Point &rPoint) const
598 return rPoint.Y() >= GetTop()
599 && rPoint.Y() <= GetBottom()
600 && rPoint.X() >= GetLeft()
601 && rPoint.X() <= GetRight();
605 BOOL SmRect::IsInsideItalicRect(const Point &rPoint) const
607 return rPoint.Y() >= GetTop()
608 && rPoint.Y() <= GetBottom()
609 && rPoint.X() >= GetItalicLeft()
610 && rPoint.X() <= GetItalicRight();
613 SmRect SmRect::AsGlyphRect() const
615 SmRect aRect (*this);
616 aRect.SetTop(nGlyphTop);
617 aRect.SetBottom(nGlyphBottom);
618 return aRect;
621 #ifdef SM_RECT_DEBUG
623 // forward declaration
624 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
625 const Color aCol = COL_BLACK);
627 void SmRect::Draw(OutputDevice &rDev, const Point &rPosition, int nFlags) const
629 if (IsEmpty())
630 return;
632 rDev.Push(PUSH_LINECOLOR);
634 if (nFlags & SM_RECT_LINES)
635 { long nLeftSpace = 0,
636 nRightSpace = 0;
638 if (nFlags & SM_RECT_ITALIC)
639 { nLeftSpace = GetItalicLeftSpace();
640 nRightSpace = GetItalicRightSpace();
643 long nLeft = GetLeft() - nLeftSpace,
644 nRight = GetRight() + nRightSpace;
646 Point aOffset (rPosition - GetTopLeft());
648 rDev.SetLineColor(COL_LIGHTBLUE);
649 rDev.DrawLine(Point(nLeft, GetAlignB()) += aOffset,
650 Point(nRight, GetAlignB()) += aOffset);
651 rDev.DrawLine(Point(nLeft, GetAlignT()) += aOffset,
652 Point(nRight, GetAlignT()) += aOffset);
653 if (HasBaseline())
654 rDev.DrawLine(Point(nLeft, GetBaseline()) += aOffset,
655 Point(nRight, GetBaseline()) += aOffset);
657 rDev.SetLineColor(COL_GRAY);
658 rDev.DrawLine(Point(nLeft, GetHiAttrFence()) += aOffset,
659 Point(nRight, GetHiAttrFence()) += aOffset);
662 if (nFlags & SM_RECT_MID)
663 { Point aCenter = rPosition
664 + (Point(GetItalicCenterX(), GetAlignM()) -= GetTopLeft()),
665 aLenX (GetWidth() / 5, 0),
666 aLenY (0, GetHeight() / 16);
668 rDev.SetLineColor(COL_LIGHTGREEN);
669 rDev.DrawLine(aCenter - aLenX, aCenter + aLenX);
670 rDev.DrawLine(aCenter - aLenY, aCenter + aLenY);
673 if (nFlags & SM_RECT_ITALIC)
674 SmDrawFrame(rDev, Rectangle(rPosition - Point(GetItalicLeftSpace(), 0),
675 GetItalicSize()));
677 if (nFlags & SM_RECT_CORE)
678 SmDrawFrame(rDev, Rectangle(rPosition, GetSize()), COL_LIGHTRED);
680 rDev.Pop();
684 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
685 const Color aCol)
687 rDev.Push(PUSH_LINECOLOR);
689 rDev.SetLineColor(aCol);
691 rDev.DrawLine(rRec.TopLeft(), rRec.BottomLeft());
692 rDev.DrawLine(rRec.BottomLeft(), rRec.BottomRight());
693 rDev.DrawLine(rRec.BottomRight(), rRec.TopRight());
694 rDev.DrawLine(rRec.TopRight(), rRec.TopLeft());
696 rDev.Pop();
699 #endif //SM_RECT_DEBUG
702 BOOL SmGetGlyphBoundRect(const OutputDevice &rDev,
703 const XubString &rText, Rectangle &rRect)
704 // basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
705 // but with a string as argument.
707 // handle special case first
708 xub_StrLen nLen = rText.Len();
709 if (nLen == 0)
710 { rRect.SetEmpty();
711 return TRUE;
714 // get a device where 'OutputDevice::GetTextBoundRect' will be successful
715 OutputDevice *pGlyphDev;
716 if (rDev.GetOutDevType() != OUTDEV_PRINTER)
717 pGlyphDev = (OutputDevice *) &rDev;
718 else
720 // since we format for the printer (where GetTextBoundRect will fail)
721 // we need a virtual device here.
722 pGlyphDev = &SM_MOD1()->GetDefaultVirtualDev();
725 const FontMetric aDevFM (rDev.GetFontMetric());
727 pGlyphDev->Push(PUSH_FONT | PUSH_MAPMODE);
728 Font aFnt(rDev.GetFont());
729 aFnt.SetAlign(ALIGN_TOP);
731 // use scale factor when calling GetTextBoundRect to counter
732 // negative effects from antialiasing which may otherwise result
733 // in significant incorrect bounding rectangles for some charcters.
734 Size aFntSize = aFnt.GetSize();
736 // HDU: workaround to avoid HUGE font sizes and resulting problems (#112783#)
737 long nScaleFactor = 1;
738 while( aFntSize.Height() > 2000 * nScaleFactor )
739 nScaleFactor *= 2;
741 aFnt.SetSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) );
742 pGlyphDev->SetFont(aFnt);
744 long nTextWidth = rDev.GetTextWidth(rText);
745 Point aPoint;
746 Rectangle aResult (aPoint, Size(nTextWidth, rDev.GetTextHeight())),
747 aTmp;
749 BOOL bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText, 0, 0);
750 DBG_ASSERT( bSuccess, "GetTextBoundRect failed" );
753 if (!aTmp.IsEmpty())
755 aResult = Rectangle(aTmp.Left() * nScaleFactor, aTmp.Top() * nScaleFactor,
756 aTmp.Right() * nScaleFactor, aTmp.Bottom() * nScaleFactor);
757 if (&rDev != pGlyphDev) /* only when rDev is a printer... */
759 long nGDTextWidth = pGlyphDev->GetTextWidth(rText);
760 if (nGDTextWidth != 0 &&
761 nTextWidth != nGDTextWidth)
763 aResult.Right() *= nTextWidth;
764 aResult.Right() /= nGDTextWidth * nScaleFactor;
769 // move rectangle to match possibly different baselines
770 // (because of different devices)
771 long nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent() * nScaleFactor;
772 aResult.Move(0, nDelta);
774 pGlyphDev->Pop();
776 rRect = aResult;
777 return bSuccess;