cmcfixes76: #i113073# redundant dereferences
[LibreOffice.git] / starmath / source / rect.cxx
blob248983a393a81fc0bda4708bafeae24681029be4
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_starmath.hxx"
32 #include <tools/string.hxx>
33 #include <tools/debug.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/wrkwin.hxx>
36 #include <vcl/virdev.hxx>
39 #include "rect.hxx"
40 #include "types.hxx"
41 #include "utility.hxx"
42 #include "smmod.hxx"
45 ////////////////////////////////////////////////////////////////////////////////
48 // '\0' terminiertes Array mit Zeichen, die im StarMath Font als Buchstaben
49 // betrachtet werden sollen, (um im Gegensatz zu den anderen Operatoren
50 // und Symbolen ein "normales"(ungecliptes) SmRect zu erhalten).
51 static xub_Unicode __READONLY_DATA aMathAlpha[] =
53 MS_ALEPH, MS_IM, MS_RE,
54 MS_WP, xub_Unicode(0xE070), MS_EMPTYSET,
55 xub_Unicode(0x2113), xub_Unicode(0xE0D6), xub_Unicode(0x2107),
56 xub_Unicode(0x2127), xub_Unicode(0x210A), MS_HBAR,
57 MS_LAMBDABAR, MS_SETN, MS_SETZ,
58 MS_SETQ, MS_SETR, MS_SETC,
59 xub_Unicode(0x2373), xub_Unicode(0xE0A5), xub_Unicode(0x2112),
60 xub_Unicode(0x2130), xub_Unicode(0x2131),
61 xub_Unicode('\0')
64 BOOL SmIsMathAlpha(const XubString &rText)
65 // ergibt genau dann TRUE, wenn das Zeichen (aus dem StarMath Font) wie ein
66 // Buchstabe behandelt werden soll.
68 if (rText.Len() == 0)
69 return FALSE;
71 DBG_ASSERT(rText.Len() == 1, "Sm : String enthaelt nicht genau ein Zeichen");
72 xub_Unicode cChar = rText.GetChar(0);
74 // ist es ein griechisches Zeichen ?
75 if (xub_Unicode(0xE0AC) <= cChar && cChar <= xub_Unicode(0xE0D4))
76 return TRUE;
77 else
79 // kommt es in 'aMathAlpha' vor ?
80 const xub_Unicode *pChar = aMathAlpha;
81 while (*pChar && *pChar != cChar)
82 pChar++;
83 return *pChar != xub_Unicode('\0');
88 ////////////////////////////////////////
90 // SmRect members
94 SmRect::SmRect()
95 // constructs empty rectangle at (0, 0) with width and height 0.
97 DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
98 DBG_ASSERT(aSize == Size(0, 0), "Sm: ooops...");
100 bHasBaseline = bHasAlignInfo = FALSE;
101 nBaseline = nAlignT = nAlignM = nAlignB =
102 nGlyphTop = nGlyphBottom =
103 nItalicLeftSpace = nItalicRightSpace =
104 nLoAttrFence = nHiAttrFence = 0;
105 nBorderWidth = 0;
109 SmRect::SmRect(const SmRect &rRect)
110 : aTopLeft(rRect.aTopLeft),
111 aSize(rRect.aSize)
113 bHasBaseline = rRect.bHasBaseline;
114 nBaseline = rRect.nBaseline;
115 nAlignT = rRect.nAlignT;
116 nAlignM = rRect.nAlignM;
117 nAlignB = rRect.nAlignB;
118 nGlyphTop = rRect.nGlyphTop;
119 nGlyphBottom = rRect.nGlyphBottom;
120 nHiAttrFence = rRect.nHiAttrFence;
121 nLoAttrFence = rRect.nLoAttrFence;
122 bHasAlignInfo = rRect.bHasAlignInfo;
123 nItalicLeftSpace = rRect.nItalicLeftSpace;
124 nItalicRightSpace = rRect.nItalicRightSpace;
125 nBorderWidth = rRect.nBorderWidth;
129 void SmRect::CopyAlignInfo(const SmRect &rRect)
131 nBaseline = rRect.nBaseline;
132 bHasBaseline = rRect.bHasBaseline;
133 nAlignT = rRect.nAlignT;
134 nAlignM = rRect.nAlignM;
135 nAlignB = rRect.nAlignB;
136 bHasAlignInfo = rRect.bHasAlignInfo;
137 nLoAttrFence = rRect.nLoAttrFence;
138 nHiAttrFence = rRect.nHiAttrFence;
142 void SmRect::BuildRect(const OutputDevice &rDev, const SmFormat *pFormat,
143 const XubString &rText, USHORT nBorder)
145 DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: Ooops...");
147 aSize = Size(rDev.GetTextWidth(rText), rDev.GetTextHeight());
149 const FontMetric aFM (rDev.GetFontMetric());
150 BOOL bIsMath = aFM.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH );
151 BOOL bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
152 const long nFontHeight = rDev.GetFont().GetSize().Height();
154 nBorderWidth = nBorder;
155 bHasAlignInfo = TRUE;
156 bHasBaseline = TRUE;
157 nBaseline = aFM.GetAscent();
158 nAlignT = nBaseline - nFontHeight * 750L / 1000L;
159 nAlignM = nBaseline - nFontHeight * 121L / 422L;
160 // that's where the horizontal bars of '+', '-', ... are
161 // (1/3 of ascent over baseline)
162 // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
163 nAlignB = nBaseline;
165 // workaround for printer fonts with very small (possible 0 or even
166 // negative(!)) leading
167 if (aFM.GetIntLeading() < 5 && rDev.GetOutDevType() == OUTDEV_PRINTER)
169 OutputDevice *pWindow = Application::GetDefaultDevice();
171 pWindow->Push(PUSH_MAPMODE | PUSH_FONT);
173 pWindow->SetMapMode(rDev.GetMapMode());
174 pWindow->SetFont(rDev.GetFontMetric());
176 long nDelta = pWindow->GetFontMetric().GetIntLeading();
177 if (nDelta == 0)
178 { // dieser Wert entspricht etwa einem Leading von 80 bei einer
179 // Fonthoehe von 422 (12pt)
180 nDelta = nFontHeight * 8L / 43;
182 SetTop(GetTop() - nDelta);
184 pWindow->Pop();
187 // get GlyphBoundRect
188 Rectangle aGlyphRect;
189 #if OSL_DEBUG_LEVEL > 1
190 BOOL bSuccess =
191 #endif
192 SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
193 #if OSL_DEBUG_LEVEL > 1
194 if (!bSuccess)
196 DBG_ERROR( "Sm : Ooops... (fehlt evtl. der Font?)");
198 #endif
200 nItalicLeftSpace = GetLeft() - aGlyphRect.Left() + nBorderWidth;
201 nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
202 if (nItalicLeftSpace < 0 && !bAllowSmaller)
203 nItalicLeftSpace = 0;
204 if (nItalicRightSpace < 0 && !bAllowSmaller)
205 nItalicRightSpace = 0;
207 long nDist = 0;
208 if (pFormat)
209 nDist = (rDev.GetFont().GetSize().Height()
210 * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100L;
212 nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist;
213 nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
215 nGlyphTop = aGlyphRect.Top() - nBorderWidth;
216 nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
218 if (bAllowSmaller)
220 // fuer Symbole und Operatoren aus dem StarMath Font passen wir den
221 // oberen und unteren Rand dem Zeichen an.
222 SetTop(nGlyphTop);
223 SetBottom(nGlyphBottom);
226 if (nHiAttrFence < GetTop())
227 nHiAttrFence = GetTop();
229 if (nLoAttrFence > GetBottom())
230 nLoAttrFence = GetBottom();
232 DBG_ASSERT(rText.Len() == 0 || !IsEmpty(),
233 "Sm: leeres Rechteck erzeugt");
237 void SmRect::Init(const OutputDevice &rDev, const SmFormat *pFormat,
238 const XubString &rText, USHORT nEBorderWidth)
239 // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
241 BuildRect(rDev, pFormat, rText, nEBorderWidth);
245 SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
246 const XubString &rText, long nEBorderWidth)
248 DBG_ASSERT( nEBorderWidth >= 0, "BorderWidth negativ" );
249 if (nEBorderWidth < 0)
250 nEBorderWidth = 0;
251 Init(rDev, pFormat, rText, (USHORT) nEBorderWidth);
255 SmRect::SmRect(long nWidth, long nHeight)
256 // this constructor should never be used for anything textlike because
257 // it will not provide useful values for baseline, AlignT and AlignB!
258 // It's purpose is to get a 'SmRect' for the horizontal line in fractions
259 // as used in 'SmBinVerNode'.
260 : aSize(nWidth, nHeight)
262 DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
264 bHasBaseline = FALSE;
265 bHasAlignInfo = TRUE;
266 nBaseline = 0;
267 nAlignT = GetTop();
268 nAlignB = GetBottom();
269 nAlignM = (nAlignT + nAlignB) / 2; // this is the default
270 nItalicLeftSpace = nItalicRightSpace = 0;
271 nGlyphTop = nHiAttrFence = GetTop();
272 nGlyphBottom = nLoAttrFence = GetBottom();
273 nBorderWidth = 0;
277 void SmRect::SetLeft(long nLeft)
279 if (nLeft <= GetRight())
280 { aSize.Width() = GetRight() - nLeft + 1;
281 aTopLeft.X() = nLeft;
286 void SmRect::SetRight(long nRight)
288 if (nRight >= GetLeft())
289 aSize.Width() = nRight - GetLeft() + 1;
293 void SmRect::SetBottom(long nBottom)
295 if (nBottom >= GetTop())
296 aSize.Height() = nBottom - GetTop() + 1;
300 void SmRect::SetTop(long nTop)
302 if (nTop <= GetBottom())
303 { aSize.Height() = GetBottom() - nTop + 1;
304 aTopLeft.Y() = nTop;
309 void SmRect::Move(const Point &rPosition)
310 // move rectangle by position 'rPosition'.
312 aTopLeft += rPosition;
314 long nDelta = rPosition.Y();
315 nBaseline += nDelta;
316 nAlignT += nDelta;
317 nAlignM += nDelta;
318 nAlignB += nDelta;
319 nGlyphTop += nDelta;
320 nGlyphBottom += nDelta;
321 nHiAttrFence += nDelta;
322 nLoAttrFence += nDelta;
326 const Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
327 RectHorAlign eHor, RectVerAlign eVer) const
328 { Point aPos (GetTopLeft());
329 // will become the topleft point of the new rectangle position
331 // set horizontal or vertical new rectangle position depending on
332 // 'ePos' is one of 'RP_LEFT', 'RP_RIGHT' or 'RP_TOP', 'RP_BOTTOM'
333 switch (ePos)
334 { case RP_LEFT :
335 aPos.X() = rRect.GetItalicLeft() - GetItalicRightSpace()
336 - GetWidth();
337 break;
338 case RP_RIGHT :
339 aPos.X() = rRect.GetItalicRight() + 1 + GetItalicLeftSpace();
340 break;
341 case RP_TOP :
342 aPos.Y() = rRect.GetTop() - GetHeight();
343 break;
344 case RP_BOTTOM :
345 aPos.Y() = rRect.GetBottom() + 1;
346 break;
347 case RP_ATTRIBUT :
348 aPos.X() = rRect.GetItalicCenterX() - GetItalicWidth() / 2
349 + GetItalicLeftSpace();
350 break;
351 default :
352 DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
355 // check if horizontal position is already set
356 if (ePos == RP_LEFT || ePos == RP_RIGHT || ePos == RP_ATTRIBUT)
357 // correct error in current vertical position
358 switch (eVer)
359 { case RVA_TOP :
360 aPos.Y() += rRect.GetAlignT() - GetAlignT();
361 break;
362 case RVA_MID :
363 aPos.Y() += rRect.GetAlignM() - GetAlignM();
364 break;
365 case RVA_BASELINE :
366 // align baselines if possible else align mid's
367 if (HasBaseline() && rRect.HasBaseline())
368 aPos.Y() += rRect.GetBaseline() - GetBaseline();
369 else
370 aPos.Y() += rRect.GetAlignM() - GetAlignM();
371 break;
372 case RVA_BOTTOM :
373 aPos.Y() += rRect.GetAlignB() - GetAlignB();
374 break;
375 case RVA_CENTERY :
376 aPos.Y() += rRect.GetCenterY() - GetCenterY();
377 break;
378 case RVA_ATTRIBUT_HI:
379 aPos.Y() += rRect.GetHiAttrFence() - GetBottom();
380 break;
381 case RVA_ATTRIBUT_MID :
382 aPos.Y() += SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4)
383 - GetCenterY();
384 break;
385 case RVA_ATTRIBUT_LO :
386 aPos.Y() += rRect.GetLoAttrFence() - GetTop();
387 break;
388 default :
389 DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
392 // check if vertical position is already set
393 if (ePos == RP_TOP || ePos == RP_BOTTOM)
394 // correct error in current horizontal position
395 switch (eHor)
396 { case RHA_LEFT :
397 aPos.X() += rRect.GetItalicLeft() - GetItalicLeft();
398 break;
399 case RHA_CENTER :
400 aPos.X() += rRect.GetItalicCenterX() - GetItalicCenterX();
401 break;
402 case RHA_RIGHT :
403 aPos.X() += rRect.GetItalicRight() - GetItalicRight();
404 break;
405 default :
406 DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
409 return aPos;
413 SmRect & SmRect::Union(const SmRect &rRect)
414 // rectangle union of current one with 'rRect'. The result is to be the
415 // smallest rectangles that covers the space of both rectangles.
416 // (empty rectangles cover no space)
417 //! Italic correction is NOT taken into account here!
419 if (rRect.IsEmpty())
420 return *this;
422 long nL = rRect.GetLeft(),
423 nR = rRect.GetRight(),
424 nT = rRect.GetTop(),
425 nB = rRect.GetBottom(),
426 nGT = rRect.nGlyphTop,
427 nGB = rRect.nGlyphBottom;
428 if (!IsEmpty())
429 { long nTmp;
431 if ((nTmp = GetLeft()) < nL)
432 nL = nTmp;
433 if ((nTmp = GetRight()) > nR)
434 nR = nTmp;
435 if ((nTmp = GetTop()) < nT)
436 nT = nTmp;
437 if ((nTmp = GetBottom()) > nB)
438 nB = nTmp;
439 if ((nTmp = nGlyphTop) < nGT)
440 nGT = nTmp;
441 if ((nTmp = nGlyphBottom) > nGB)
442 nGB = nTmp;
445 SetLeft(nL);
446 SetRight(nR);
447 SetTop(nT);
448 SetBottom(nB);
449 nGlyphTop = nGT;
450 nGlyphBottom = nGB;
452 return *this;
456 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
457 // let current rectangle be the union of itself and 'rRect'
458 // (the smallest rectangle surrounding both). Also adapt values for
459 // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
460 // The baseline is set according to 'eCopyMode'.
461 // If one of the rectangles has no relevant info the other one is copied.
463 // get some values used for (italic) spaces adaption
464 // ! (need to be done before changing current SmRect) !
465 long nL = Min(GetItalicLeft(), rRect.GetItalicLeft()),
466 nR = Max(GetItalicRight(), rRect.GetItalicRight());
468 Union(rRect);
470 SetItalicSpaces(GetLeft() - nL, nR - GetRight());
472 if (!HasAlignInfo())
473 CopyAlignInfo(rRect);
474 else if (rRect.HasAlignInfo())
475 { nAlignT = Min(GetAlignT(), rRect.GetAlignT());
476 nAlignB = Max(GetAlignB(), rRect.GetAlignB());
477 nHiAttrFence = Min(GetHiAttrFence(), rRect.GetHiAttrFence());
478 nLoAttrFence = Max(GetLoAttrFence(), rRect.GetLoAttrFence());
479 DBG_ASSERT(HasAlignInfo(), "Sm: ooops...");
481 switch (eCopyMode)
482 { case RCP_THIS:
483 // already done
484 break;
485 case RCP_ARG:
486 CopyMBL(rRect);
487 break;
488 case RCP_NONE:
489 ClearBaseline();
490 nAlignM = (nAlignT + nAlignB) / 2;
491 break;
492 case RCP_XOR:
493 if (!HasBaseline())
494 CopyMBL(rRect);
495 break;
496 default :
497 DBG_ASSERT(FALSE, "Sm: unbekannter Fall");
501 return *this;
505 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
506 long nNewAlignM)
507 // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
508 // (this version will be used in 'SmBinVerNode' to provide means to
509 // align eg "{a over b} over c" correctly where AlignM should not
510 // be (AlignT + AlignB) / 2)
512 DBG_ASSERT(HasAlignInfo(), "Sm: keine Align Info");
514 ExtendBy(rRect, eCopyMode);
515 nAlignM = nNewAlignM;
517 return *this;
521 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
522 BOOL bKeepVerAlignParams)
523 // as 'ExtendBy' but keeps original values for AlignT, -M and -B and
524 // baseline.
525 // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
526 // be allowed to modify these values.)
528 long nOldAlignT = GetAlignT(),
529 nOldAlignM = GetAlignM(),
530 nOldAlignB = GetAlignB(),
531 nOldBaseline = nBaseline; //! depends not on 'HasBaseline'
532 BOOL bOldHasAlignInfo = HasAlignInfo();
534 ExtendBy(rRect, eCopyMode);
536 if (bKeepVerAlignParams)
537 { nAlignT = nOldAlignT;
538 nAlignM = nOldAlignM;
539 nAlignB = nOldAlignB;
540 nBaseline = nOldBaseline;
541 bHasAlignInfo = bOldHasAlignInfo;
544 return *this;
548 long SmRect::OrientedDist(const Point &rPoint) const
549 // return oriented distance of rPoint to the current rectangle,
550 // especially the return value is <= 0 iff the point is inside the
551 // rectangle.
552 // For simplicity the maximum-norm is used.
554 BOOL bIsInside = IsInsideItalicRect(rPoint);
556 // build reference point to define the distance
557 Point aRef;
558 if (bIsInside)
559 { Point aIC (GetItalicCenterX(), GetCenterY());
561 aRef.X() = rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft();
562 aRef.Y() = rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop();
564 else
566 // x-coordinate
567 if (rPoint.X() > GetItalicRight())
568 aRef.X() = GetItalicRight();
569 else if (rPoint.X() < GetItalicLeft())
570 aRef.X() = GetItalicLeft();
571 else
572 aRef.X() = rPoint.X();
573 // y-coordinate
574 if (rPoint.Y() > GetBottom())
575 aRef.Y() = GetBottom();
576 else if (rPoint.Y() < GetTop())
577 aRef.Y() = GetTop();
578 else
579 aRef.Y() = rPoint.Y();
582 // build distance vector
583 Point aDist (aRef - rPoint);
585 long nAbsX = labs(aDist.X()),
586 nAbsY = labs(aDist.Y());
588 return bIsInside ? - Min(nAbsX, nAbsY) : Max (nAbsX, nAbsY);
592 BOOL SmRect::IsInsideRect(const Point &rPoint) const
594 return rPoint.Y() >= GetTop()
595 && rPoint.Y() <= GetBottom()
596 && rPoint.X() >= GetLeft()
597 && rPoint.X() <= GetRight();
601 BOOL SmRect::IsInsideItalicRect(const Point &rPoint) const
603 return rPoint.Y() >= GetTop()
604 && rPoint.Y() <= GetBottom()
605 && rPoint.X() >= GetItalicLeft()
606 && rPoint.X() <= GetItalicRight();
609 SmRect SmRect::AsGlyphRect() const
611 SmRect aRect (*this);
612 aRect.SetTop(nGlyphTop);
613 aRect.SetBottom(nGlyphBottom);
614 return aRect;
617 #ifdef SM_RECT_DEBUG
619 // forward declaration
620 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
621 const Color aCol = COL_BLACK);
623 void SmRect::Draw(OutputDevice &rDev, const Point &rPosition, int nFlags) const
625 if (IsEmpty())
626 return;
628 rDev.Push(PUSH_LINECOLOR);
630 if (nFlags & SM_RECT_LINES)
631 { long nLeftSpace = 0,
632 nRightSpace = 0;
634 if (nFlags & SM_RECT_ITALIC)
635 { nLeftSpace = GetItalicLeftSpace();
636 nRightSpace = GetItalicRightSpace();
639 long nLeft = GetLeft() - nLeftSpace,
640 nRight = GetRight() + nRightSpace;
642 Point aOffset (rPosition - GetTopLeft());
644 rDev.SetLineColor(COL_LIGHTBLUE);
645 rDev.DrawLine(Point(nLeft, GetAlignB()) += aOffset,
646 Point(nRight, GetAlignB()) += aOffset);
647 rDev.DrawLine(Point(nLeft, GetAlignT()) += aOffset,
648 Point(nRight, GetAlignT()) += aOffset);
649 if (HasBaseline())
650 rDev.DrawLine(Point(nLeft, GetBaseline()) += aOffset,
651 Point(nRight, GetBaseline()) += aOffset);
653 rDev.SetLineColor(COL_GRAY);
654 rDev.DrawLine(Point(nLeft, GetHiAttrFence()) += aOffset,
655 Point(nRight, GetHiAttrFence()) += aOffset);
658 if (nFlags & SM_RECT_MID)
659 { Point aCenter = rPosition
660 + (Point(GetItalicCenterX(), GetAlignM()) -= GetTopLeft()),
661 aLenX (GetWidth() / 5, 0),
662 aLenY (0, GetHeight() / 16);
664 rDev.SetLineColor(COL_LIGHTGREEN);
665 rDev.DrawLine(aCenter - aLenX, aCenter + aLenX);
666 rDev.DrawLine(aCenter - aLenY, aCenter + aLenY);
669 if (nFlags & SM_RECT_ITALIC)
670 SmDrawFrame(rDev, Rectangle(rPosition - Point(GetItalicLeftSpace(), 0),
671 GetItalicSize()));
673 if (nFlags & SM_RECT_CORE)
674 SmDrawFrame(rDev, Rectangle(rPosition, GetSize()), COL_LIGHTRED);
676 rDev.Pop();
680 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
681 const Color aCol)
683 rDev.Push(PUSH_LINECOLOR);
685 rDev.SetLineColor(aCol);
687 rDev.DrawLine(rRec.TopLeft(), rRec.BottomLeft());
688 rDev.DrawLine(rRec.BottomLeft(), rRec.BottomRight());
689 rDev.DrawLine(rRec.BottomRight(), rRec.TopRight());
690 rDev.DrawLine(rRec.TopRight(), rRec.TopLeft());
692 rDev.Pop();
695 #endif //SM_RECT_DEBUG
698 BOOL SmGetGlyphBoundRect(const OutputDevice &rDev,
699 const XubString &rText, Rectangle &rRect)
700 // basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
701 // but with a string as argument.
703 // handle special case first
704 xub_StrLen nLen = rText.Len();
705 if (nLen == 0)
706 { rRect.SetEmpty();
707 return TRUE;
710 // get a device where 'OutputDevice::GetTextBoundRect' will be successful
711 OutputDevice *pGlyphDev;
712 if (rDev.GetOutDevType() != OUTDEV_PRINTER)
713 pGlyphDev = (OutputDevice *) &rDev;
714 else
716 // since we format for the printer (where GetTextBoundRect will fail)
717 // we need a virtual device here.
718 pGlyphDev = &SM_MOD()->GetDefaultVirtualDev();
721 const FontMetric aDevFM (rDev.GetFontMetric());
723 pGlyphDev->Push(PUSH_FONT | PUSH_MAPMODE);
724 Font aFnt(rDev.GetFont());
725 aFnt.SetAlign(ALIGN_TOP);
727 // use scale factor when calling GetTextBoundRect to counter
728 // negative effects from antialiasing which may otherwise result
729 // in significant incorrect bounding rectangles for some charcters.
730 Size aFntSize = aFnt.GetSize();
732 // HDU: workaround to avoid HUGE font sizes and resulting problems (#112783#)
733 long nScaleFactor = 1;
734 while( aFntSize.Height() > 2000 * nScaleFactor )
735 nScaleFactor *= 2;
737 aFnt.SetSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) );
738 pGlyphDev->SetFont(aFnt);
740 long nTextWidth = rDev.GetTextWidth(rText);
741 Point aPoint;
742 Rectangle aResult (aPoint, Size(nTextWidth, rDev.GetTextHeight())),
743 aTmp;
745 BOOL bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText, 0, 0);
746 DBG_ASSERT( bSuccess, "GetTextBoundRect failed" );
749 if (!aTmp.IsEmpty())
751 aResult = Rectangle(aTmp.Left() * nScaleFactor, aTmp.Top() * nScaleFactor,
752 aTmp.Right() * nScaleFactor, aTmp.Bottom() * nScaleFactor);
753 if (&rDev != pGlyphDev) /* only when rDev is a printer... */
755 long nGDTextWidth = pGlyphDev->GetTextWidth(rText);
756 if (nGDTextWidth != 0 &&
757 nTextWidth != nGDTextWidth)
759 aResult.Right() *= nTextWidth;
760 aResult.Right() /= nGDTextWidth * nScaleFactor;
765 // move rectangle to match possibly different baselines
766 // (because of different devices)
767 long nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent() * nScaleFactor;
768 aResult.Move(0, nDelta);
770 pGlyphDev->Pop();
772 rRect = aResult;
773 return bSuccess;