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