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