xref: /trunk/main/starmath/source/rect.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
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.
14  *
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).
20  *
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.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_starmath.hxx"
30 
31 
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>
37 
38 
39 #include "rect.hxx"
40 #include "types.hxx"
41 #include "utility.hxx"
42 #include "smmod.hxx"
43 
44 
45 ////////////////////////////////////////////////////////////////////////////////
46 
47 
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[] =
52 {
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')
62 };
63 
64 sal_Bool SmIsMathAlpha(const XubString &rText)
65     // ergibt genau dann sal_True, wenn das Zeichen (aus dem StarMath Font) wie ein
66     // Buchstabe behandelt werden soll.
67 {
68     if (rText.Len() == 0)
69         return sal_False;
70 
71     DBG_ASSERT(rText.Len() == 1, "Sm : String enthaelt nicht genau ein Zeichen");
72     xub_Unicode cChar = rText.GetChar(0);
73 
74     // ist es ein griechisches Zeichen ?
75     if (xub_Unicode(0xE0AC) <= cChar  &&  cChar <= xub_Unicode(0xE0D4))
76         return sal_True;
77     else
78     {
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');
84     }
85 }
86 
87 
88 ////////////////////////////////////////
89 //
90 // SmRect members
91 //
92 
93 
94 SmRect::SmRect()
95     // constructs empty rectangle at (0, 0) with width and height 0.
96 {
97     DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
98     DBG_ASSERT(aSize == Size(0, 0), "Sm: ooops...");
99 
100     bHasBaseline = bHasAlignInfo = sal_False;
101     nBaseline = nAlignT = nAlignM = nAlignB =
102     nGlyphTop = nGlyphBottom =
103     nItalicLeftSpace = nItalicRightSpace =
104     nLoAttrFence = nHiAttrFence = 0;
105     nBorderWidth = 0;
106 }
107 
108 
109 SmRect::SmRect(const SmRect &rRect)
110 :   aTopLeft(rRect.aTopLeft),
111     aSize(rRect.aSize)
112 {
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;
126 }
127 
128 
129 void SmRect::CopyAlignInfo(const SmRect &rRect)
130 {
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;
139 }
140 
141 
142 void SmRect::BuildRect(const OutputDevice &rDev, const SmFormat *pFormat,
143                        const XubString &rText, sal_uInt16 nBorder)
144 {
145     DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: Ooops...");
146 
147     aSize = Size(rDev.GetTextWidth(rText), rDev.GetTextHeight());
148 
149     const FontMetric  aFM (rDev.GetFontMetric());
150     sal_Bool              bIsMath  = aFM.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH );
151     sal_Bool              bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
152     const long        nFontHeight = rDev.GetFont().GetSize().Height();
153 
154     nBorderWidth  = nBorder;
155     bHasAlignInfo = sal_True;
156     bHasBaseline  = sal_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;
164 
165     // workaround for printer fonts with very small (possible 0 or even
166     // negative(!)) leading
167     if (aFM.GetIntLeading() < 5  &&  rDev.GetOutDevType() == OUTDEV_PRINTER)
168     {
169         OutputDevice    *pWindow = Application::GetDefaultDevice();
170 
171         pWindow->Push(PUSH_MAPMODE | PUSH_FONT);
172 
173         pWindow->SetMapMode(rDev.GetMapMode());
174         pWindow->SetFont(rDev.GetFontMetric());
175 
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;
181         }
182         SetTop(GetTop() - nDelta);
183 
184         pWindow->Pop();
185     }
186 
187     // get GlyphBoundRect
188     Rectangle  aGlyphRect;
189 #if OSL_DEBUG_LEVEL > 1
190     sal_Bool bSuccess =
191 #endif
192                 SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
193 #if OSL_DEBUG_LEVEL > 1
194     if (!bSuccess)
195     {
196         DBG_ERROR( "Sm : Ooops... (fehlt evtl. der Font?)");
197     }
198 #endif
199 
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;
206 
207     long  nDist = 0;
208     if (pFormat)
209         nDist = (rDev.GetFont().GetSize().Height()
210                 * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100L;
211 
212     nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist;
213     nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
214 
215     nGlyphTop    = aGlyphRect.Top() - nBorderWidth;
216     nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
217 
218     if (bAllowSmaller)
219     {
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);
224     }
225 
226     if (nHiAttrFence < GetTop())
227         nHiAttrFence = GetTop();
228 
229     if (nLoAttrFence > GetBottom())
230         nLoAttrFence = GetBottom();
231 
232     DBG_ASSERT(rText.Len() == 0  ||  !IsEmpty(),
233                "Sm: leeres Rechteck erzeugt");
234 }
235 
236 
237 void SmRect::Init(const OutputDevice &rDev, const SmFormat *pFormat,
238                   const XubString &rText, sal_uInt16 nEBorderWidth)
239     // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
240 {
241     BuildRect(rDev, pFormat, rText, nEBorderWidth);
242 }
243 
244 
245 SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
246                const XubString &rText, long nEBorderWidth)
247 {
248     DBG_ASSERT( nEBorderWidth >= 0, "BorderWidth negativ" );
249     if (nEBorderWidth < 0)
250         nEBorderWidth = 0;
251     Init(rDev, pFormat, rText, (sal_uInt16) nEBorderWidth);
252 }
253 
254 
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)
261 {
262     DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
263 
264     bHasBaseline  = sal_False;
265     bHasAlignInfo = sal_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;
274 }
275 
276 
277 void SmRect::SetLeft(long nLeft)
278 {
279     if (nLeft <= GetRight())
280     {   aSize.Width() = GetRight() - nLeft + 1;
281         aTopLeft.X()  = nLeft;
282     }
283 }
284 
285 
286 void SmRect::SetRight(long nRight)
287 {
288     if (nRight >= GetLeft())
289         aSize.Width() = nRight - GetLeft() + 1;
290 }
291 
292 
293 void SmRect::SetBottom(long nBottom)
294 {
295     if (nBottom >= GetTop())
296         aSize.Height() = nBottom - GetTop() + 1;
297 }
298 
299 
300 void SmRect::SetTop(long nTop)
301 {
302     if (nTop <= GetBottom())
303     {   aSize.Height()   = GetBottom() - nTop + 1;
304         aTopLeft.Y() = nTop;
305     }
306 }
307 
308 
309 void SmRect::Move(const Point &rPosition)
310     // move rectangle by position 'rPosition'.
311 {
312     aTopLeft  += rPosition;
313 
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;
323 }
324 
325 
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
330 
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(sal_False, "Sm: unbekannter Fall");
353     }
354 
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(sal_False, "Sm: unbekannter Fall");
390         }
391 
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(sal_False, "Sm: unbekannter Fall");
407         }
408 
409     return aPos;
410 }
411 
412 
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!
418 {
419     if (rRect.IsEmpty())
420         return *this;
421 
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;
430 
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;
443     }
444 
445     SetLeft(nL);
446     SetRight(nR);
447     SetTop(nT);
448     SetBottom(nB);
449     nGlyphTop    = nGT;
450     nGlyphBottom = nGB;
451 
452     return *this;
453 }
454 
455 
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.
462 {
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());
467 
468     Union(rRect);
469 
470     SetItalicSpaces(GetLeft() - nL, nR - GetRight());
471 
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...");
480 
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(sal_False, "Sm: unbekannter Fall");
498         }
499     }
500 
501     return *this;
502 }
503 
504 
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)
511 {
512     DBG_ASSERT(HasAlignInfo(), "Sm: keine Align Info");
513 
514     ExtendBy(rRect, eCopyMode);
515     nAlignM = nNewAlignM;
516 
517     return *this;
518 }
519 
520 
521 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
522                           sal_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.)
527 {
528     long  nOldAlignT   = GetAlignT(),
529           nOldAlignM   = GetAlignM(),
530           nOldAlignB   = GetAlignB(),
531           nOldBaseline = nBaseline;     //! depends not on 'HasBaseline'
532     sal_Bool  bOldHasAlignInfo = HasAlignInfo();
533 
534     ExtendBy(rRect, eCopyMode);
535 
536     if (bKeepVerAlignParams)
537     {   nAlignT   = nOldAlignT;
538         nAlignM   = nOldAlignM;
539         nAlignB   = nOldAlignB;
540         nBaseline = nOldBaseline;
541         bHasAlignInfo = bOldHasAlignInfo;
542     }
543 
544     return *this;
545 }
546 
547 
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.
553 {
554     sal_Bool  bIsInside = IsInsideItalicRect(rPoint);
555 
556     // build reference point to define the distance
557     Point  aRef;
558     if (bIsInside)
559     {   Point  aIC (GetItalicCenterX(), GetCenterY());
560 
561         aRef.X() = rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft();
562         aRef.Y() = rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop();
563     }
564     else
565     {
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();
580     }
581 
582     // build distance vector
583     Point  aDist (aRef - rPoint);
584 
585     long nAbsX = labs(aDist.X()),
586          nAbsY = labs(aDist.Y());
587 
588     return bIsInside ? - Min(nAbsX, nAbsY) : Max (nAbsX, nAbsY);
589 }
590 
591 
592 sal_Bool SmRect::IsInsideRect(const Point &rPoint) const
593 {
594     return     rPoint.Y() >= GetTop()
595            &&  rPoint.Y() <= GetBottom()
596            &&  rPoint.X() >= GetLeft()
597            &&  rPoint.X() <= GetRight();
598 }
599 
600 
601 sal_Bool SmRect::IsInsideItalicRect(const Point &rPoint) const
602 {
603     return     rPoint.Y() >= GetTop()
604            &&  rPoint.Y() <= GetBottom()
605            &&  rPoint.X() >= GetItalicLeft()
606            &&  rPoint.X() <= GetItalicRight();
607 }
608 
609 SmRect SmRect::AsGlyphRect() const
610 {
611     SmRect aRect (*this);
612     aRect.SetTop(nGlyphTop);
613     aRect.SetBottom(nGlyphBottom);
614     return aRect;
615 }
616 
617 #ifdef SM_RECT_DEBUG
618 
619 // forward declaration
620 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
621                  const Color aCol = COL_BLACK);
622 
623 void SmRect::Draw(OutputDevice &rDev, const Point &rPosition, int nFlags) const
624 {
625     if (IsEmpty())
626         return;
627 
628     rDev.Push(PUSH_LINECOLOR);
629 
630     if (nFlags & SM_RECT_LINES)
631     {   long   nLeftSpace  = 0,
632                nRightSpace = 0;
633 
634         if (nFlags & SM_RECT_ITALIC)
635         {   nLeftSpace  = GetItalicLeftSpace();
636             nRightSpace = GetItalicRightSpace();
637         }
638 
639         long  nLeft  = GetLeft()  - nLeftSpace,
640               nRight = GetRight() + nRightSpace;
641 
642         Point aOffset (rPosition - GetTopLeft());
643 
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);
652 
653         rDev.SetLineColor(COL_GRAY);
654         rDev.DrawLine(Point(nLeft,  GetHiAttrFence()) += aOffset,
655                       Point(nRight, GetHiAttrFence()) += aOffset);
656     }
657 
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);
663 
664         rDev.SetLineColor(COL_LIGHTGREEN);
665         rDev.DrawLine(aCenter - aLenX, aCenter + aLenX);
666         rDev.DrawLine(aCenter - aLenY, aCenter + aLenY);
667     }
668 
669     if (nFlags & SM_RECT_ITALIC)
670         SmDrawFrame(rDev, Rectangle(rPosition - Point(GetItalicLeftSpace(), 0),
671                 GetItalicSize()));
672 
673     if (nFlags & SM_RECT_CORE)
674         SmDrawFrame(rDev, Rectangle(rPosition, GetSize()), COL_LIGHTRED);
675 
676     rDev.Pop();
677 }
678 
679 
680 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
681                  const Color aCol)
682 {
683     rDev.Push(PUSH_LINECOLOR);
684 
685     rDev.SetLineColor(aCol);
686 
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());
691 
692     rDev.Pop();
693 }
694 
695 #endif //SM_RECT_DEBUG
696 
697 
698 sal_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.
702 {
703     // handle special case first
704     xub_StrLen nLen = rText.Len();
705     if (nLen == 0)
706     {   rRect.SetEmpty();
707         return sal_True;
708     }
709 
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
715     {
716         // since we format for the printer (where GetTextBoundRect will fail)
717         // we need a virtual device here.
718         pGlyphDev = &SM_MOD()->GetDefaultVirtualDev();
719     }
720 
721     const FontMetric  aDevFM (rDev.GetFontMetric());
722 
723     pGlyphDev->Push(PUSH_FONT | PUSH_MAPMODE);
724     Font aFnt(rDev.GetFont());
725     aFnt.SetAlign(ALIGN_TOP);
726 
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();
731 
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;
736 
737     aFnt.SetSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) );
738     pGlyphDev->SetFont(aFnt);
739 
740     long nTextWidth = rDev.GetTextWidth(rText);
741     Point aPoint;
742     Rectangle   aResult (aPoint, Size(nTextWidth, rDev.GetTextHeight())),
743                 aTmp;
744 
745     sal_Bool bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText, 0, 0);
746     DBG_ASSERT( bSuccess, "GetTextBoundRect failed" );
747 
748 
749     if (!aTmp.IsEmpty())
750     {
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... */
754         {
755             long nGDTextWidth  = pGlyphDev->GetTextWidth(rText);
756             if (nGDTextWidth != 0  &&
757                 nTextWidth != nGDTextWidth)
758             {
759                 aResult.Right() *= nTextWidth;
760                 aResult.Right() /= nGDTextWidth * nScaleFactor;
761             }
762         }
763     }
764 
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);
769 
770     pGlyphDev->Pop();
771 
772     rRect = aResult;
773     return bSuccess;
774 }
775 
776 
777