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 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 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 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 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 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 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 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 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 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 282 void SmRect::SetRight(long nRight) 283 { 284 if (nRight >= GetLeft()) 285 aSize.Width() = nRight - GetLeft() + 1; 286 } 287 288 289 void SmRect::SetBottom(long nBottom) 290 { 291 if (nBottom >= GetTop()) 292 aSize.Height() = nBottom - GetTop() + 1; 293 } 294 295 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 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 322 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 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 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 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 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 544 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 588 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 597 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 605 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 619 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 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 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