xref: /AOO41X/main/starmath/source/rect.cxx (revision d107581f588b53ce11339a8a18091c0f86ec8bb4)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_starmath.hxx"
26 
27 
28 #include <tools/string.hxx>
29 #include <tools/debug.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/wrkwin.hxx>
32 #include <vcl/virdev.hxx>
33 
34 
35 #include "rect.hxx"
36 #include "types.hxx"
37 #include "utility.hxx"
38 #include "smmod.hxx"
39 
40 
41 ////////////////////////////////////////////////////////////////////////////////
42 
43 
44 // '\0' terminiertes Array mit Zeichen, die im StarMath Font als Buchstaben
45 // betrachtet werden sollen, (um im Gegensatz zu den anderen Operatoren
46 // und Symbolen ein "normales"(ungecliptes) SmRect zu erhalten).
47 static xub_Unicode __READONLY_DATA aMathAlpha[] =
48 {
49     MS_ALEPH,               MS_IM,                  MS_RE,
50     MS_WP,                  xub_Unicode(0xE070),    MS_EMPTYSET,
51     xub_Unicode(0x2113),    xub_Unicode(0xE0D6),    xub_Unicode(0x2107),
52     xub_Unicode(0x2127),    xub_Unicode(0x210A),    MS_HBAR,
53     MS_LAMBDABAR,           MS_SETN,                MS_SETZ,
54     MS_SETQ,                MS_SETR,                MS_SETC,
55     xub_Unicode(0x2373),    xub_Unicode(0xE0A5),    xub_Unicode(0x2112),
56     xub_Unicode(0x2130),    xub_Unicode(0x2131),
57     xub_Unicode('\0')
58 };
59 
SmIsMathAlpha(const XubString & rText)60 sal_Bool SmIsMathAlpha(const XubString &rText)
61     // ergibt genau dann sal_True, wenn das Zeichen (aus dem StarMath Font) wie ein
62     // Buchstabe behandelt werden soll.
63 {
64     if (rText.Len() == 0)
65         return sal_False;
66 
67     DBG_ASSERT(rText.Len() == 1, "Sm : String enthaelt nicht genau ein Zeichen");
68     xub_Unicode cChar = rText.GetChar(0);
69 
70     // ist es ein griechisches Zeichen ?
71     if (xub_Unicode(0xE0AC) <= cChar  &&  cChar <= xub_Unicode(0xE0D4))
72         return sal_True;
73     else
74     {
75         // kommt es in 'aMathAlpha' vor ?
76         const xub_Unicode *pChar = aMathAlpha;
77         while (*pChar  &&  *pChar != cChar)
78             pChar++;
79         return *pChar != xub_Unicode('\0');
80     }
81 }
82 
83 
84 ////////////////////////////////////////
85 //
86 // SmRect members
87 //
88 
89 
SmRect()90 SmRect::SmRect()
91     // constructs empty rectangle at (0, 0) with width and height 0.
92 {
93     DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
94     DBG_ASSERT(aSize == Size(0, 0), "Sm: ooops...");
95 
96     bHasBaseline = bHasAlignInfo = sal_False;
97     nBaseline = nAlignT = nAlignM = nAlignB =
98     nGlyphTop = nGlyphBottom =
99     nItalicLeftSpace = nItalicRightSpace =
100     nLoAttrFence = nHiAttrFence = 0;
101     nBorderWidth = 0;
102 }
103 
104 
SmRect(const SmRect & rRect)105 SmRect::SmRect(const SmRect &rRect)
106 :   aTopLeft(rRect.aTopLeft),
107     aSize(rRect.aSize)
108 {
109     bHasBaseline  = rRect.bHasBaseline;
110     nBaseline     = rRect.nBaseline;
111     nAlignT       = rRect.nAlignT;
112     nAlignM       = rRect.nAlignM;
113     nAlignB       = rRect.nAlignB;
114     nGlyphTop     = rRect.nGlyphTop;
115     nGlyphBottom  = rRect.nGlyphBottom;
116     nHiAttrFence  = rRect.nHiAttrFence;
117     nLoAttrFence  = rRect.nLoAttrFence;
118     bHasAlignInfo = rRect.bHasAlignInfo;
119     nItalicLeftSpace  = rRect.nItalicLeftSpace;
120     nItalicRightSpace = rRect.nItalicRightSpace;
121     nBorderWidth  = rRect.nBorderWidth;
122 }
123 
124 
CopyAlignInfo(const SmRect & rRect)125 void SmRect::CopyAlignInfo(const SmRect &rRect)
126 {
127     nBaseline     = rRect.nBaseline;
128     bHasBaseline  = rRect.bHasBaseline;
129     nAlignT       = rRect.nAlignT;
130     nAlignM       = rRect.nAlignM;
131     nAlignB       = rRect.nAlignB;
132     bHasAlignInfo = rRect.bHasAlignInfo;
133     nLoAttrFence  = rRect.nLoAttrFence;
134     nHiAttrFence  = rRect.nHiAttrFence;
135 }
136 
137 
BuildRect(const OutputDevice & rDev,const SmFormat * pFormat,const XubString & rText,sal_uInt16 nBorder)138 void SmRect::BuildRect(const OutputDevice &rDev, const SmFormat *pFormat,
139                        const XubString &rText, sal_uInt16 nBorder)
140 {
141     DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: Ooops...");
142 
143     aSize = Size(rDev.GetTextWidth(rText), rDev.GetTextHeight());
144 
145     const FontMetric  aFM (rDev.GetFontMetric());
146     sal_Bool              bIsMath  = aFM.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH );
147     sal_Bool              bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
148     const long        nFontHeight = rDev.GetFont().GetSize().Height();
149 
150     nBorderWidth  = nBorder;
151     bHasAlignInfo = sal_True;
152     bHasBaseline  = sal_True;
153     nBaseline     = aFM.GetAscent();
154     nAlignT       = nBaseline - nFontHeight * 750L / 1000L;
155     nAlignM       = nBaseline - nFontHeight * 121L / 422L;
156         // that's where the horizontal bars of '+', '-', ... are
157         // (1/3 of ascent over baseline)
158         // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
159     nAlignB       = nBaseline;
160 
161     // workaround for printer fonts with very small (possible 0 or even
162     // negative(!)) leading
163     if (aFM.GetIntLeading() < 5  &&  rDev.GetOutDevType() == OUTDEV_PRINTER)
164     {
165         OutputDevice    *pWindow = Application::GetDefaultDevice();
166 
167         pWindow->Push(PUSH_MAPMODE | PUSH_FONT);
168 
169         pWindow->SetMapMode(rDev.GetMapMode());
170         pWindow->SetFont(rDev.GetFontMetric());
171 
172         long  nDelta = pWindow->GetFontMetric().GetIntLeading();
173         if (nDelta == 0)
174         {   // dieser Wert entspricht etwa einem Leading von 80 bei einer
175             // Fonthoehe von 422 (12pt)
176             nDelta = nFontHeight * 8L / 43;
177         }
178         SetTop(GetTop() - nDelta);
179 
180         pWindow->Pop();
181     }
182 
183     // get GlyphBoundRect
184     Rectangle  aGlyphRect;
185 #if OSL_DEBUG_LEVEL > 1
186     sal_Bool bSuccess =
187 #endif
188                 SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
189 #if OSL_DEBUG_LEVEL > 1
190     if (!bSuccess)
191     {
192         DBG_ERROR( "Sm : Ooops... (fehlt evtl. der Font?)");
193     }
194 #endif
195 
196     nItalicLeftSpace  = GetLeft() - aGlyphRect.Left() + nBorderWidth;
197     nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
198     if (nItalicLeftSpace  < 0  &&  !bAllowSmaller)
199         nItalicLeftSpace  = 0;
200     if (nItalicRightSpace < 0  &&  !bAllowSmaller)
201         nItalicRightSpace = 0;
202 
203     long  nDist = 0;
204     if (pFormat)
205         nDist = (rDev.GetFont().GetSize().Height()
206                 * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100L;
207 
208     nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist;
209     nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
210 
211     nGlyphTop    = aGlyphRect.Top() - nBorderWidth;
212     nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
213 
214     if (bAllowSmaller)
215     {
216         // fuer Symbole und Operatoren aus dem StarMath Font passen wir den
217         // oberen und unteren Rand dem Zeichen an.
218         SetTop(nGlyphTop);
219         SetBottom(nGlyphBottom);
220     }
221 
222     if (nHiAttrFence < GetTop())
223         nHiAttrFence = GetTop();
224 
225     if (nLoAttrFence > GetBottom())
226         nLoAttrFence = GetBottom();
227 
228     DBG_ASSERT(rText.Len() == 0  ||  !IsEmpty(),
229                "Sm: leeres Rechteck erzeugt");
230 }
231 
232 
Init(const OutputDevice & rDev,const SmFormat * pFormat,const XubString & rText,sal_uInt16 nEBorderWidth)233 void SmRect::Init(const OutputDevice &rDev, const SmFormat *pFormat,
234                   const XubString &rText, sal_uInt16 nEBorderWidth)
235     // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
236 {
237     BuildRect(rDev, pFormat, rText, nEBorderWidth);
238 }
239 
240 
SmRect(const OutputDevice & rDev,const SmFormat * pFormat,const XubString & rText,long nEBorderWidth)241 SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
242                const XubString &rText, long nEBorderWidth)
243 {
244     DBG_ASSERT( nEBorderWidth >= 0, "BorderWidth negativ" );
245     if (nEBorderWidth < 0)
246         nEBorderWidth = 0;
247     Init(rDev, pFormat, rText, (sal_uInt16) nEBorderWidth);
248 }
249 
250 
SmRect(long nWidth,long nHeight)251 SmRect::SmRect(long nWidth, long nHeight)
252     // this constructor should never be used for anything textlike because
253     // it will not provide useful values for baseline, AlignT and AlignB!
254     // It's purpose is to get a 'SmRect' for the horizontal line in fractions
255     // as used in 'SmBinVerNode'.
256 :   aSize(nWidth, nHeight)
257 {
258     DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
259 
260     bHasBaseline  = sal_False;
261     bHasAlignInfo = sal_True;
262     nBaseline     = 0;
263     nAlignT       = GetTop();
264     nAlignB       = GetBottom();
265     nAlignM       = (nAlignT + nAlignB) / 2;        // this is the default
266     nItalicLeftSpace = nItalicRightSpace = 0;
267     nGlyphTop    = nHiAttrFence  = GetTop();
268     nGlyphBottom = nLoAttrFence  = GetBottom();
269     nBorderWidth  = 0;
270 }
271 
272 
SetLeft(long nLeft)273 void SmRect::SetLeft(long nLeft)
274 {
275     if (nLeft <= GetRight())
276     {   aSize.Width() = GetRight() - nLeft + 1;
277         aTopLeft.X()  = nLeft;
278     }
279 }
280 
281 
SetRight(long nRight)282 void SmRect::SetRight(long nRight)
283 {
284     if (nRight >= GetLeft())
285         aSize.Width() = nRight - GetLeft() + 1;
286 }
287 
288 
SetBottom(long nBottom)289 void SmRect::SetBottom(long nBottom)
290 {
291     if (nBottom >= GetTop())
292         aSize.Height() = nBottom - GetTop() + 1;
293 }
294 
295 
SetTop(long nTop)296 void SmRect::SetTop(long nTop)
297 {
298     if (nTop <= GetBottom())
299     {   aSize.Height()   = GetBottom() - nTop + 1;
300         aTopLeft.Y() = nTop;
301     }
302 }
303 
304 
Move(const Point & rPosition)305 void SmRect::Move(const Point &rPosition)
306     // move rectangle by position 'rPosition'.
307 {
308     aTopLeft  += rPosition;
309 
310     long  nDelta = rPosition.Y();
311     nBaseline += nDelta;
312     nAlignT   += nDelta;
313     nAlignM   += nDelta;
314     nAlignB   += nDelta;
315     nGlyphTop    += nDelta;
316     nGlyphBottom += nDelta;
317     nHiAttrFence += nDelta;
318     nLoAttrFence += nDelta;
319 }
320 
321 
AlignTo(const SmRect & rRect,RectPos ePos,RectHorAlign eHor,RectVerAlign eVer) const322 const Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
323                             RectHorAlign eHor, RectVerAlign eVer) const
324 {   Point  aPos (GetTopLeft());
325         // will become the topleft point of the new rectangle position
326 
327     // set horizontal or vertical new rectangle position depending on
328     // 'ePos' is one of 'RP_LEFT', 'RP_RIGHT' or 'RP_TOP', 'RP_BOTTOM'
329     switch (ePos)
330     {   case RP_LEFT :
331             aPos.X() = rRect.GetItalicLeft() - GetItalicRightSpace()
332                        - GetWidth();
333             break;
334         case RP_RIGHT :
335             aPos.X() = rRect.GetItalicRight() + 1 + GetItalicLeftSpace();
336             break;
337         case RP_TOP :
338             aPos.Y() = rRect.GetTop() - GetHeight();
339             break;
340         case RP_BOTTOM :
341             aPos.Y() = rRect.GetBottom() + 1;
342             break;
343         case RP_ATTRIBUT :
344             aPos.X() = rRect.GetItalicCenterX() - GetItalicWidth() / 2
345                        + GetItalicLeftSpace();
346             break;
347         default :
348             DBG_ASSERT(sal_False, "Sm: unbekannter Fall");
349     }
350 
351     // check if horizontal position is already set
352     if (ePos == RP_LEFT  ||  ePos == RP_RIGHT  ||  ePos == RP_ATTRIBUT)
353         // correct error in current vertical position
354         switch (eVer)
355         {   case RVA_TOP :
356                 aPos.Y() += rRect.GetAlignT() - GetAlignT();
357                 break;
358             case RVA_MID :
359                 aPos.Y() += rRect.GetAlignM() - GetAlignM();
360                 break;
361             case RVA_BASELINE :
362                 // align baselines if possible else align mid's
363                 if (HasBaseline() && rRect.HasBaseline())
364                     aPos.Y() += rRect.GetBaseline() - GetBaseline();
365                 else
366                     aPos.Y() += rRect.GetAlignM() - GetAlignM();
367                 break;
368             case RVA_BOTTOM :
369                 aPos.Y() += rRect.GetAlignB() - GetAlignB();
370                 break;
371             case RVA_CENTERY :
372                 aPos.Y() += rRect.GetCenterY() - GetCenterY();
373                 break;
374             case RVA_ATTRIBUT_HI:
375                 aPos.Y() += rRect.GetHiAttrFence() - GetBottom();
376                 break;
377             case RVA_ATTRIBUT_MID :
378                 aPos.Y() += SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4)
379                             - GetCenterY();
380                 break;
381             case RVA_ATTRIBUT_LO :
382                 aPos.Y() += rRect.GetLoAttrFence() - GetTop();
383                 break;
384         default :
385                 DBG_ASSERT(sal_False, "Sm: unbekannter Fall");
386         }
387 
388     // check if vertical position is already set
389     if (ePos == RP_TOP  ||  ePos == RP_BOTTOM)
390         // correct error in current horizontal position
391         switch (eHor)
392         {   case RHA_LEFT :
393                 aPos.X() += rRect.GetItalicLeft() - GetItalicLeft();
394                 break;
395             case RHA_CENTER :
396                 aPos.X() += rRect.GetItalicCenterX() - GetItalicCenterX();
397                 break;
398             case RHA_RIGHT :
399                 aPos.X() += rRect.GetItalicRight() - GetItalicRight();
400                 break;
401             default :
402                 DBG_ASSERT(sal_False, "Sm: unbekannter Fall");
403         }
404 
405     return aPos;
406 }
407 
408 
Union(const SmRect & rRect)409 SmRect & SmRect::Union(const SmRect &rRect)
410     // rectangle union of current one with 'rRect'. The result is to be the
411     // smallest rectangles that covers the space of both rectangles.
412     // (empty rectangles cover no space)
413     //! Italic correction is NOT taken into account here!
414 {
415     if (rRect.IsEmpty())
416         return *this;
417 
418     long  nL  = rRect.GetLeft(),
419           nR  = rRect.GetRight(),
420           nT  = rRect.GetTop(),
421           nB  = rRect.GetBottom(),
422           nGT = rRect.nGlyphTop,
423           nGB = rRect.nGlyphBottom;
424     if (!IsEmpty())
425     {   long  nTmp;
426 
427         if ((nTmp = GetLeft()) < nL)
428             nL = nTmp;
429         if ((nTmp = GetRight()) > nR)
430             nR = nTmp;
431         if ((nTmp = GetTop()) < nT)
432             nT = nTmp;
433         if ((nTmp = GetBottom()) > nB)
434             nB = nTmp;
435         if ((nTmp = nGlyphTop) < nGT)
436             nGT = nTmp;
437         if ((nTmp = nGlyphBottom) > nGB)
438             nGB = nTmp;
439     }
440 
441     SetLeft(nL);
442     SetRight(nR);
443     SetTop(nT);
444     SetBottom(nB);
445     nGlyphTop    = nGT;
446     nGlyphBottom = nGB;
447 
448     return *this;
449 }
450 
451 
ExtendBy(const SmRect & rRect,RectCopyMBL eCopyMode)452 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
453     // let current rectangle be the union of itself and 'rRect'
454     // (the smallest rectangle surrounding both). Also adapt values for
455     // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
456     // The baseline is set according to 'eCopyMode'.
457     // If one of the rectangles has no relevant info the other one is copied.
458 {
459     // get some values used for (italic) spaces adaption
460     // ! (need to be done before changing current SmRect) !
461     long  nL = Min(GetItalicLeft(),  rRect.GetItalicLeft()),
462           nR = Max(GetItalicRight(), rRect.GetItalicRight());
463 
464     Union(rRect);
465 
466     SetItalicSpaces(GetLeft() - nL, nR - GetRight());
467 
468     if (!HasAlignInfo())
469         CopyAlignInfo(rRect);
470     else if (rRect.HasAlignInfo())
471     {   nAlignT = Min(GetAlignT(), rRect.GetAlignT());
472         nAlignB = Max(GetAlignB(), rRect.GetAlignB());
473         nHiAttrFence = Min(GetHiAttrFence(), rRect.GetHiAttrFence());
474         nLoAttrFence = Max(GetLoAttrFence(), rRect.GetLoAttrFence());
475         DBG_ASSERT(HasAlignInfo(), "Sm: ooops...");
476 
477         switch (eCopyMode)
478         {   case RCP_THIS:
479                 // already done
480                 break;
481             case RCP_ARG:
482                 CopyMBL(rRect);
483                 break;
484             case RCP_NONE:
485                 ClearBaseline();
486                 nAlignM = (nAlignT + nAlignB) / 2;
487                 break;
488             case RCP_XOR:
489                 if (!HasBaseline())
490                     CopyMBL(rRect);
491                 break;
492             default :
493                 DBG_ASSERT(sal_False, "Sm: unbekannter Fall");
494         }
495     }
496 
497     return *this;
498 }
499 
500 
ExtendBy(const SmRect & rRect,RectCopyMBL eCopyMode,long nNewAlignM)501 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
502                           long nNewAlignM)
503     // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
504     // (this version will be used in 'SmBinVerNode' to provide means to
505     // align eg "{a over b} over c" correctly where AlignM should not
506     // be (AlignT + AlignB) / 2)
507 {
508     DBG_ASSERT(HasAlignInfo(), "Sm: keine Align Info");
509 
510     ExtendBy(rRect, eCopyMode);
511     nAlignM = nNewAlignM;
512 
513     return *this;
514 }
515 
516 
ExtendBy(const SmRect & rRect,RectCopyMBL eCopyMode,sal_Bool bKeepVerAlignParams)517 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
518                           sal_Bool bKeepVerAlignParams)
519     // as 'ExtendBy' but keeps original values for AlignT, -M and -B and
520     // baseline.
521     // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
522     // be allowed to modify these values.)
523 {
524     long  nOldAlignT   = GetAlignT(),
525           nOldAlignM   = GetAlignM(),
526           nOldAlignB   = GetAlignB(),
527           nOldBaseline = nBaseline;     //! depends not on 'HasBaseline'
528     sal_Bool  bOldHasAlignInfo = HasAlignInfo();
529 
530     ExtendBy(rRect, eCopyMode);
531 
532     if (bKeepVerAlignParams)
533     {   nAlignT   = nOldAlignT;
534         nAlignM   = nOldAlignM;
535         nAlignB   = nOldAlignB;
536         nBaseline = nOldBaseline;
537         bHasAlignInfo = bOldHasAlignInfo;
538     }
539 
540     return *this;
541 }
542 
543 
OrientedDist(const Point & rPoint) const544 long SmRect::OrientedDist(const Point &rPoint) const
545     // return oriented distance of rPoint to the current rectangle,
546     // especially the return value is <= 0 iff the point is inside the
547     // rectangle.
548     // For simplicity the maximum-norm is used.
549 {
550     sal_Bool  bIsInside = IsInsideItalicRect(rPoint);
551 
552     // build reference point to define the distance
553     Point  aRef;
554     if (bIsInside)
555     {   Point  aIC (GetItalicCenterX(), GetCenterY());
556 
557         aRef.X() = rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft();
558         aRef.Y() = rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop();
559     }
560     else
561     {
562         // x-coordinate
563         if (rPoint.X() > GetItalicRight())
564             aRef.X() = GetItalicRight();
565         else if (rPoint.X() < GetItalicLeft())
566             aRef.X() = GetItalicLeft();
567         else
568             aRef.X() = rPoint.X();
569         // y-coordinate
570         if (rPoint.Y() > GetBottom())
571             aRef.Y() = GetBottom();
572         else if (rPoint.Y() < GetTop())
573             aRef.Y() = GetTop();
574         else
575             aRef.Y() = rPoint.Y();
576     }
577 
578     // build distance vector
579     Point  aDist (aRef - rPoint);
580 
581     long nAbsX = labs(aDist.X()),
582          nAbsY = labs(aDist.Y());
583 
584     return bIsInside ? - Min(nAbsX, nAbsY) : Max (nAbsX, nAbsY);
585 }
586 
587 
IsInsideRect(const Point & rPoint) const588 sal_Bool SmRect::IsInsideRect(const Point &rPoint) const
589 {
590     return     rPoint.Y() >= GetTop()
591            &&  rPoint.Y() <= GetBottom()
592            &&  rPoint.X() >= GetLeft()
593            &&  rPoint.X() <= GetRight();
594 }
595 
596 
IsInsideItalicRect(const Point & rPoint) const597 sal_Bool SmRect::IsInsideItalicRect(const Point &rPoint) const
598 {
599     return     rPoint.Y() >= GetTop()
600            &&  rPoint.Y() <= GetBottom()
601            &&  rPoint.X() >= GetItalicLeft()
602            &&  rPoint.X() <= GetItalicRight();
603 }
604 
AsGlyphRect() const605 SmRect SmRect::AsGlyphRect() const
606 {
607     SmRect aRect (*this);
608     aRect.SetTop(nGlyphTop);
609     aRect.SetBottom(nGlyphBottom);
610     return aRect;
611 }
612 
613 #ifdef SM_RECT_DEBUG
614 
615 // forward declaration
616 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
617                  const Color aCol = COL_BLACK);
618 
Draw(OutputDevice & rDev,const Point & rPosition,int nFlags) const619 void SmRect::Draw(OutputDevice &rDev, const Point &rPosition, int nFlags) const
620 {
621     if (IsEmpty())
622         return;
623 
624     rDev.Push(PUSH_LINECOLOR);
625 
626     if (nFlags & SM_RECT_LINES)
627     {   long   nLeftSpace  = 0,
628                nRightSpace = 0;
629 
630         if (nFlags & SM_RECT_ITALIC)
631         {   nLeftSpace  = GetItalicLeftSpace();
632             nRightSpace = GetItalicRightSpace();
633         }
634 
635         long  nLeft  = GetLeft()  - nLeftSpace,
636               nRight = GetRight() + nRightSpace;
637 
638         Point aOffset (rPosition - GetTopLeft());
639 
640         rDev.SetLineColor(COL_LIGHTBLUE);
641         rDev.DrawLine(Point(nLeft,  GetAlignB()) += aOffset,
642                       Point(nRight, GetAlignB()) += aOffset);
643         rDev.DrawLine(Point(nLeft,  GetAlignT()) += aOffset,
644                       Point(nRight, GetAlignT()) += aOffset);
645         if (HasBaseline())
646             rDev.DrawLine(Point(nLeft,  GetBaseline()) += aOffset,
647                           Point(nRight, GetBaseline()) += aOffset);
648 
649         rDev.SetLineColor(COL_GRAY);
650         rDev.DrawLine(Point(nLeft,  GetHiAttrFence()) += aOffset,
651                       Point(nRight, GetHiAttrFence()) += aOffset);
652     }
653 
654     if (nFlags & SM_RECT_MID)
655     {   Point   aCenter = rPosition
656                           + (Point(GetItalicCenterX(), GetAlignM()) -= GetTopLeft()),
657                 aLenX     (GetWidth() / 5, 0),
658                 aLenY     (0, GetHeight() / 16);
659 
660         rDev.SetLineColor(COL_LIGHTGREEN);
661         rDev.DrawLine(aCenter - aLenX, aCenter + aLenX);
662         rDev.DrawLine(aCenter - aLenY, aCenter + aLenY);
663     }
664 
665     if (nFlags & SM_RECT_ITALIC)
666         SmDrawFrame(rDev, Rectangle(rPosition - Point(GetItalicLeftSpace(), 0),
667                 GetItalicSize()));
668 
669     if (nFlags & SM_RECT_CORE)
670         SmDrawFrame(rDev, Rectangle(rPosition, GetSize()), COL_LIGHTRED);
671 
672     rDev.Pop();
673 }
674 
675 
SmDrawFrame(OutputDevice & rDev,const Rectangle & rRec,const Color aCol)676 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
677                  const Color aCol)
678 {
679     rDev.Push(PUSH_LINECOLOR);
680 
681     rDev.SetLineColor(aCol);
682 
683     rDev.DrawLine(rRec.TopLeft(),     rRec.BottomLeft());
684     rDev.DrawLine(rRec.BottomLeft(),  rRec.BottomRight());
685     rDev.DrawLine(rRec.BottomRight(), rRec.TopRight());
686     rDev.DrawLine(rRec.TopRight(),    rRec.TopLeft());
687 
688     rDev.Pop();
689 }
690 
691 #endif //SM_RECT_DEBUG
692 
693 
SmGetGlyphBoundRect(const OutputDevice & rDev,const XubString & rText,Rectangle & rRect)694 sal_Bool SmGetGlyphBoundRect(const OutputDevice &rDev,
695                          const XubString &rText, Rectangle &rRect)
696     // basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
697     // but with a string as argument.
698 {
699     // handle special case first
700     xub_StrLen nLen = rText.Len();
701     if (nLen == 0)
702     {   rRect.SetEmpty();
703         return sal_True;
704     }
705 
706     // get a device where 'OutputDevice::GetTextBoundRect' will be successful
707     OutputDevice *pGlyphDev;
708     if (rDev.GetOutDevType() != OUTDEV_PRINTER)
709         pGlyphDev = (OutputDevice *) &rDev;
710     else
711     {
712         // since we format for the printer (where GetTextBoundRect will fail)
713         // we need a virtual device here.
714         pGlyphDev = &SM_MOD()->GetDefaultVirtualDev();
715     }
716 
717     const FontMetric  aDevFM (rDev.GetFontMetric());
718 
719     pGlyphDev->Push(PUSH_FONT | PUSH_MAPMODE);
720     Font aFnt(rDev.GetFont());
721     aFnt.SetAlign(ALIGN_TOP);
722 
723     // use scale factor when calling GetTextBoundRect to counter
724     // negative effects from antialiasing which may otherwise result
725     // in significant incorrect bounding rectangles for some charcters.
726     Size aFntSize = aFnt.GetSize();
727 
728     // HDU: workaround to avoid HUGE font sizes and resulting problems (#112783#)
729     long nScaleFactor = 1;
730     while( aFntSize.Height() > 2000 * nScaleFactor )
731         nScaleFactor *= 2;
732 
733     aFnt.SetSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) );
734     pGlyphDev->SetFont(aFnt);
735 
736     long nTextWidth = rDev.GetTextWidth(rText);
737     Point aPoint;
738     Rectangle   aResult (aPoint, Size(nTextWidth, rDev.GetTextHeight())),
739                 aTmp;
740 
741     sal_Bool bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText, 0, 0);
742     DBG_ASSERT( bSuccess, "GetTextBoundRect failed" );
743 
744 
745     if (!aTmp.IsEmpty())
746     {
747         aResult = Rectangle(aTmp.Left() * nScaleFactor, aTmp.Top() * nScaleFactor,
748                             aTmp.Right() * nScaleFactor, aTmp.Bottom() * nScaleFactor);
749         if (&rDev != pGlyphDev) /* only when rDev is a printer... */
750         {
751             long nGDTextWidth  = pGlyphDev->GetTextWidth(rText);
752             if (nGDTextWidth != 0  &&
753                 nTextWidth != nGDTextWidth)
754             {
755                 aResult.Right() *= nTextWidth;
756                 aResult.Right() /= nGDTextWidth * nScaleFactor;
757             }
758         }
759     }
760 
761     // move rectangle to match possibly different baselines
762     // (because of different devices)
763     long nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent() * nScaleFactor;
764     aResult.Move(0, nDelta);
765 
766     pGlyphDev->Pop();
767 
768     rRect = aResult;
769     return bSuccess;
770 }
771 
772 
773