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_vcl.hxx" 26 27 #include "i18npool/mslangid.hxx" 28 29 #include "rtl/tencinfo.h" 30 #include "rtl/logfile.hxx" 31 32 #include "tools/debug.hxx" 33 #include "tools/poly.hxx" 34 35 #include "basegfx/polygon/b2dpolygon.hxx" 36 #include "basegfx/polygon/b2dpolypolygon.hxx" 37 #include "basegfx/matrix/b2dhommatrix.hxx" 38 39 #include "vcl/metric.hxx" 40 #include "vcl/metaact.hxx" 41 #include "vcl/gdimtf.hxx" 42 #include "vcl/virdev.hxx" 43 #include "vcl/print.hxx" 44 #include "vcl/event.hxx" 45 #include "vcl/window.hxx" 46 #include "vcl/svapp.hxx" 47 #include "vcl/bmpacc.hxx" 48 #include "vcl/outdev.hxx" 49 #include "vcl/edit.hxx" 50 // declare system types in sysdata.hxx 51 #include <svsys.h> 52 #include "vcl/sysdata.hxx" 53 #include "vcl/unohelp.hxx" 54 #include "vcl/controllayout.hxx" 55 56 #include "salgdi.hxx" 57 #include "sallayout.hxx" 58 #include "svdata.hxx" 59 #include "impfont.hxx" 60 #include "outdata.hxx" 61 #include "outfont.hxx" 62 #include "outdev.h" 63 #include "textlayout.hxx" 64 #include "svids.hrc" 65 #include "window.h" 66 67 #include "unotools/fontcvt.hxx" 68 #include "unotools/fontcfg.hxx" 69 70 #include "osl/file.h" 71 72 #ifdef ENABLE_GRAPHITE 73 #include "graphite_features.hxx" 74 #endif 75 #ifdef USE_BUILTIN_RASTERIZER 76 #include "glyphcache.hxx" 77 #endif 78 79 #include "pdfwriter_impl.hxx" 80 81 #include "com/sun/star/beans/PropertyValues.hpp" 82 #include "com/sun/star/i18n/XBreakIterator.hpp" 83 #include "com/sun/star/i18n/WordType.hpp" 84 #include "com/sun/star/linguistic2/XLinguServiceManager.hpp" 85 86 #if defined UNX 87 #define GLYPH_FONT_HEIGHT 128 88 #elif defined OS2 89 #define GLYPH_FONT_HEIGHT 176 90 #else 91 #define GLYPH_FONT_HEIGHT 256 92 #endif 93 94 #include "sal/alloca.h" 95 96 #include <cmath> 97 #include <cstring> 98 99 #include <memory> 100 #include <algorithm> 101 102 103 // ======================================================================= 104 105 DBG_NAMEEX( OutputDevice ) 106 DBG_NAMEEX( Font ) 107 108 // ======================================================================= 109 110 using namespace ::com::sun::star; 111 using namespace ::com::sun::star::uno; 112 using namespace ::rtl; 113 using namespace ::vcl; 114 using namespace ::utl; 115 116 // ======================================================================= 117 118 #define TEXT_DRAW_ELLIPSIS (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) 119 120 // ======================================================================= 121 122 #define UNDERLINE_LAST UNDERLINE_BOLDWAVE 123 #define STRIKEOUT_LAST STRIKEOUT_X 124 125 // ======================================================================= 126 127 static void ImplRotatePos( long nOriginX, long nOriginY, long& rX, long& rY, 128 int nOrientation ) 129 { 130 if ( (nOrientation >= 0) && !(nOrientation % 900) ) 131 { 132 if ( (nOrientation >= 3600) ) 133 nOrientation %= 3600; 134 135 if ( nOrientation ) 136 { 137 rX -= nOriginX; 138 rY -= nOriginY; 139 140 if ( nOrientation == 900 ) 141 { 142 long nTemp = rX; 143 rX = rY; 144 rY = -nTemp; 145 } 146 else if ( nOrientation == 1800 ) 147 { 148 rX = -rX; 149 rY = -rY; 150 } 151 else /* ( nOrientation == 2700 ) */ 152 { 153 long nTemp = rX; 154 rX = -rY; 155 rY = nTemp; 156 } 157 158 rX += nOriginX; 159 rY += nOriginY; 160 } 161 } 162 else 163 { 164 double nRealOrientation = nOrientation*F_PI1800; 165 double nCos = cos( nRealOrientation ); 166 double nSin = sin( nRealOrientation ); 167 168 // Translation... 169 long nX = rX-nOriginX; 170 long nY = rY-nOriginY; 171 172 // Rotation... 173 rX = +((long)(nCos*nX + nSin*nY)) + nOriginX; 174 rY = -((long)(nSin*nX - nCos*nY)) + nOriginY; 175 } 176 } 177 178 // ======================================================================= 179 180 void OutputDevice::ImplUpdateFontData( bool bNewFontLists ) 181 { 182 // the currently selected logical font is no longer needed 183 if ( mpFontEntry ) 184 { 185 mpFontCache->Release( mpFontEntry ); 186 mpFontEntry = NULL; 187 } 188 189 mbInitFont = true; 190 mbNewFont = true; 191 192 if ( bNewFontLists ) 193 { 194 if ( mpGetDevFontList ) 195 { 196 delete mpGetDevFontList; 197 mpGetDevFontList = NULL; 198 } 199 if ( mpGetDevSizeList ) 200 { 201 delete mpGetDevSizeList; 202 mpGetDevSizeList = NULL; 203 } 204 205 // release all physically selected fonts on this device 206 if( ImplGetGraphics() ) 207 mpGraphics->ReleaseFonts(); 208 } 209 210 if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter ) 211 { 212 ImplSVData* pSVData = ImplGetSVData(); 213 214 if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache ) 215 mpFontCache->Invalidate(); 216 217 if ( bNewFontLists ) 218 { 219 // we need a graphics 220 if ( ImplGetGraphics() ) 221 { 222 if( mpFontList && mpFontList != pSVData->maGDIData.mpScreenFontList ) 223 mpFontList->Clear(); 224 225 if( mpPDFWriter ) 226 { 227 if( mpFontList && mpFontList != pSVData->maGDIData.mpScreenFontList ) 228 delete mpFontList; 229 if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache ) 230 delete mpFontCache; 231 mpFontList = mpPDFWriter->filterDevFontList( pSVData->maGDIData.mpScreenFontList ); 232 mpFontCache = new ImplFontCache( sal_False ); 233 } 234 else 235 { 236 if( mpOutDevData ) 237 mpOutDevData->maDevFontSubst.Clear(); 238 mpGraphics->GetDevFontList( mpFontList ); 239 mpGraphics->GetDevFontSubstList( this ); 240 } 241 } 242 } 243 } 244 245 // also update child windows if needed 246 if ( GetOutDevType() == OUTDEV_WINDOW ) 247 { 248 Window* pChild = ((Window*)this)->mpWindowImpl->mpFirstChild; 249 while ( pChild ) 250 { 251 pChild->ImplUpdateFontData( true ); 252 pChild = pChild->mpWindowImpl->mpNext; 253 } 254 } 255 } 256 257 // ----------------------------------------------------------------------- 258 259 void OutputDevice::ImplUpdateAllFontData( bool bNewFontLists ) 260 { 261 ImplSVData* pSVData = ImplGetSVData(); 262 263 // update all windows 264 Window* pFrame = pSVData->maWinData.mpFirstFrame; 265 while ( pFrame ) 266 { 267 pFrame->ImplUpdateFontData( bNewFontLists ); 268 269 Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap; 270 while ( pSysWin ) 271 { 272 pSysWin->ImplUpdateFontData( bNewFontLists ); 273 pSysWin = pSysWin->mpWindowImpl->mpNextOverlap; 274 } 275 276 pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame; 277 } 278 279 // update all virtual devices 280 VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev; 281 while ( pVirDev ) 282 { 283 pVirDev->ImplUpdateFontData( bNewFontLists ); 284 pVirDev = pVirDev->mpNext; 285 } 286 287 // update all printers 288 Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter; 289 while ( pPrinter ) 290 { 291 pPrinter->ImplUpdateFontData( bNewFontLists ); 292 pPrinter = pPrinter->mpNext; 293 } 294 295 // clear global font lists to have them updated 296 pSVData->maGDIData.mpScreenFontCache->Invalidate(); 297 if ( bNewFontLists ) 298 { 299 pSVData->maGDIData.mpScreenFontList->Clear(); 300 pFrame = pSVData->maWinData.mpFirstFrame; 301 if ( pFrame ) 302 { 303 if ( pFrame->ImplGetGraphics() ) 304 // MT: Stupid typecast here and somewhere ((OutputDevice*)&aVDev)->, because bug in .NET2002 compiler. 305 ((OutputDevice*)pFrame)->mpGraphics->GetDevFontList( pFrame->mpWindowImpl->mpFrameData->mpFontList ); 306 } 307 } 308 } 309 310 // ======================================================================= 311 312 313 // ======================================================================= 314 315 // TODO: remove this method when the CWS-gfbfcfg dust has settled 316 void ImplFreeOutDevFontData() 317 {} 318 319 // ======================================================================= 320 321 void OutputDevice::BeginFontSubstitution() 322 { 323 ImplSVData* pSVData = ImplGetSVData(); 324 pSVData->maGDIData.mbFontSubChanged = sal_False; 325 } 326 327 // ----------------------------------------------------------------------- 328 329 void OutputDevice::EndFontSubstitution() 330 { 331 ImplSVData* pSVData = ImplGetSVData(); 332 if ( pSVData->maGDIData.mbFontSubChanged ) 333 { 334 ImplUpdateAllFontData( false ); 335 336 Application* pApp = GetpApp(); 337 DataChangedEvent aDCEvt( DATACHANGED_FONTSUBSTITUTION ); 338 pApp->DataChanged( aDCEvt ); 339 pApp->NotifyAllWindows( aDCEvt ); 340 pSVData->maGDIData.mbFontSubChanged = sal_False; 341 } 342 } 343 344 // ----------------------------------------------------------------------- 345 346 void OutputDevice::AddFontSubstitute( const XubString& rFontName, 347 const XubString& rReplaceFontName, 348 sal_uInt16 nFlags ) 349 { 350 ImplDirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; 351 if( !rpSubst ) 352 rpSubst = new ImplDirectFontSubstitution(); 353 rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags ); 354 ImplGetSVData()->maGDIData.mbFontSubChanged = sal_True; 355 } 356 357 // ----------------------------------------------------------------------- 358 359 void ImplDirectFontSubstitution::AddFontSubstitute( const String& rFontName, 360 const String& rSubstFontName, sal_uInt16 nFlags ) 361 { 362 maFontSubstList.push_back( ImplFontSubstEntry( rFontName, rSubstFontName, nFlags ) ); 363 } 364 365 // ----------------------------------------------------------------------- 366 367 ImplFontSubstEntry::ImplFontSubstEntry( const String& rFontName, 368 const String& rSubstFontName, sal_uInt16 nSubstFlags ) 369 : maName( rFontName ) 370 , maReplaceName( rSubstFontName ) 371 , mnFlags( nSubstFlags ) 372 { 373 maSearchName = rFontName; 374 maSearchReplaceName = rSubstFontName; 375 GetEnglishSearchFontName( maSearchName ); 376 GetEnglishSearchFontName( maSearchReplaceName ); 377 } 378 379 // ----------------------------------------------------------------------- 380 381 void OutputDevice::ImplAddDevFontSubstitute( const XubString& rFontName, 382 const XubString& rReplaceFontName, 383 sal_uInt16 nFlags ) 384 { 385 ImplInitOutDevData(); 386 mpOutDevData->maDevFontSubst.AddFontSubstitute( rFontName, rReplaceFontName, nFlags ); 387 } 388 389 // ----------------------------------------------------------------------- 390 391 void OutputDevice::RemoveFontSubstitute( sal_uInt16 n ) 392 { 393 ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; 394 if( pSubst ) 395 pSubst->RemoveFontSubstitute( n ); 396 } 397 398 // ----------------------------------------------------------------------- 399 400 void ImplDirectFontSubstitution::RemoveFontSubstitute( int nIndex ) 401 { 402 FontSubstList::iterator it = maFontSubstList.begin(); 403 for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ; 404 if( it != maFontSubstList.end() ) 405 maFontSubstList.erase( it ); 406 } 407 408 // ----------------------------------------------------------------------- 409 410 sal_uInt16 OutputDevice::GetFontSubstituteCount() 411 { 412 const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; 413 if( !pSubst ) 414 return 0; 415 int nCount = pSubst->GetFontSubstituteCount(); 416 return (sal_uInt16)nCount; 417 } 418 419 // ----------------------------------------------------------------------- 420 421 void OutputDevice::GetFontSubstitute( sal_uInt16 n, 422 XubString& rFontName, 423 XubString& rReplaceFontName, 424 sal_uInt16& rFlags ) 425 { 426 const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; 427 if( pSubst ) 428 pSubst->GetFontSubstitute( n, rFontName, rReplaceFontName, rFlags ); 429 } 430 431 // ----------------------------------------------------------------------- 432 433 bool ImplDirectFontSubstitution::GetFontSubstitute( int nIndex, 434 String& rFontName, String& rSubstFontName, sal_uInt16& rFlags ) const 435 { 436 FontSubstList::const_iterator it = maFontSubstList.begin(); 437 for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ; 438 if( it == maFontSubstList.end() ) 439 return false; 440 441 const ImplFontSubstEntry* pEntry = &(*it); 442 rFontName = pEntry->maName; 443 rSubstFontName = pEntry->maReplaceName; 444 rFlags = pEntry->mnFlags; 445 return true; 446 } 447 448 // ----------------------------------------------------------------------- 449 450 bool ImplDirectFontSubstitution::FindFontSubstitute( String& rSubstName, 451 const String& rSearchName, sal_uInt16 nFlags ) const 452 { 453 // TODO: get rid of O(N) searches 454 FontSubstList::const_iterator it = maFontSubstList.begin(); 455 for(; it != maFontSubstList.end(); ++it ) 456 { 457 const ImplFontSubstEntry& rEntry = *it; 458 if( ((rEntry.mnFlags & nFlags) || !nFlags) 459 && (rEntry.maSearchName == rSearchName) ) 460 { 461 rSubstName = rEntry.maSearchReplaceName; 462 return true; 463 } 464 } 465 466 return false; 467 } 468 469 // ----------------------------------------------------------------------- 470 471 static void ImplFontSubstitute( String& rFontName, 472 sal_uInt16 nFlags, ImplDirectFontSubstitution* pDevSpecific ) 473 { 474 #ifdef DBG_UTIL 475 String aTempName = rFontName; 476 GetEnglishSearchFontName( aTempName ); 477 DBG_ASSERT( aTempName == rFontName, "ImplFontSubstitute() called without a searchname" ); 478 #endif 479 480 String aSubstFontName; 481 482 // apply user-configurable font replacement (eg, from the list in Tools->Options) 483 const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; 484 if( pSubst && pSubst->FindFontSubstitute( aSubstFontName, rFontName, FONT_SUBSTITUTE_ALWAYS ) ) 485 { 486 rFontName = aSubstFontName; 487 return; 488 } 489 490 // apply device specific font replacement (e.g. to use printer builtin fonts) 491 if( !pDevSpecific ) 492 return; 493 494 if( pDevSpecific->FindFontSubstitute( aSubstFontName, rFontName, nFlags ) ) 495 { 496 rFontName = aSubstFontName; 497 return; 498 } 499 } 500 501 // ----------------------------------------------------------------------- 502 503 Font OutputDevice::GetDefaultFont( sal_uInt16 nType, LanguageType eLang, 504 sal_uLong nFlags, const OutputDevice* pOutDev ) 505 { 506 DBG_TRACE( "OutputDevice::GetDefaultFont()" ); 507 508 com::sun::star::lang::Locale aLocale; 509 if( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW ) 510 { 511 aLocale = Application::GetSettings().GetUILocale(); 512 } 513 else 514 { 515 MsLangId::convertLanguageToLocale( eLang, aLocale ); 516 } 517 518 utl::DefaultFontConfiguration& rDefaults = *utl::DefaultFontConfiguration::get(); 519 String aSearch = rDefaults.getUserInterfaceFont( aLocale ); // ensure a fallback 520 String aDefault = rDefaults.getDefaultFont( aLocale, nType ); 521 if( aDefault.Len() ) 522 aSearch = aDefault; 523 524 int nDefaultHeight = 12; 525 526 Font aFont; 527 aFont.SetPitch( PITCH_VARIABLE ); 528 529 switch ( nType ) 530 { 531 case DEFAULTFONT_SANS_UNICODE: 532 case DEFAULTFONT_UI_SANS: 533 aFont.SetFamily( FAMILY_SWISS ); 534 break; 535 536 case DEFAULTFONT_SANS: 537 case DEFAULTFONT_LATIN_HEADING: 538 case DEFAULTFONT_LATIN_SPREADSHEET: 539 case DEFAULTFONT_LATIN_DISPLAY: 540 aFont.SetFamily( FAMILY_SWISS ); 541 break; 542 543 case DEFAULTFONT_SERIF: 544 case DEFAULTFONT_LATIN_TEXT: 545 case DEFAULTFONT_LATIN_PRESENTATION: 546 aFont.SetFamily( FAMILY_ROMAN ); 547 break; 548 549 case DEFAULTFONT_FIXED: 550 case DEFAULTFONT_LATIN_FIXED: 551 case DEFAULTFONT_UI_FIXED: 552 aFont.SetPitch( PITCH_FIXED ); 553 aFont.SetFamily( FAMILY_MODERN ); 554 break; 555 556 case DEFAULTFONT_SYMBOL: 557 aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL ); 558 break; 559 560 case DEFAULTFONT_CJK_TEXT: 561 case DEFAULTFONT_CJK_PRESENTATION: 562 case DEFAULTFONT_CJK_SPREADSHEET: 563 case DEFAULTFONT_CJK_HEADING: 564 case DEFAULTFONT_CJK_DISPLAY: 565 aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later... 566 break; 567 568 case DEFAULTFONT_CTL_TEXT: 569 case DEFAULTFONT_CTL_PRESENTATION: 570 case DEFAULTFONT_CTL_SPREADSHEET: 571 case DEFAULTFONT_CTL_HEADING: 572 case DEFAULTFONT_CTL_DISPLAY: 573 aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later... 574 break; 575 } 576 577 if ( aSearch.Len() ) 578 { 579 aFont.SetHeight( nDefaultHeight ); 580 aFont.SetWeight( WEIGHT_NORMAL ); 581 aFont.SetLanguage( eLang ); 582 583 if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW ) 584 aFont.SetCharSet( gsl_getSystemTextEncoding() ); 585 586 // Should we only return available fonts on the given device 587 if ( pOutDev ) 588 { 589 pOutDev->ImplInitFontList(); 590 591 // Search Font in the FontList 592 String aName; 593 String aSearchName; 594 xub_StrLen nIndex = 0; 595 do 596 { 597 aSearchName = GetNextFontToken( aSearch, nIndex ); 598 GetEnglishSearchFontName( aSearchName ); 599 ImplDevFontListData* pFontFamily = pOutDev->mpFontList->ImplFindBySearchName( aSearchName ); 600 if( pFontFamily ) 601 { 602 AddTokenFontName( aName, pFontFamily->GetFamilyName() ); 603 if( nFlags & DEFAULTFONT_FLAGS_ONLYONE ) 604 break; 605 } 606 } 607 while ( nIndex != STRING_NOTFOUND ); 608 aFont.SetName( aName ); 609 } 610 611 // No Name, than set all names 612 if ( !aFont.GetName().Len() ) 613 { 614 xub_StrLen nIndex = 0; 615 if ( nFlags & DEFAULTFONT_FLAGS_ONLYONE ) 616 { 617 //aFont.SetName( aSearch.GetToken( 0, ';', nIndex ) ); 618 if( !pOutDev ) 619 pOutDev = (const OutputDevice *)ImplGetSVData()->mpDefaultWin; 620 if( !pOutDev ) 621 aFont.SetName( aSearch.GetToken( 0, ';', nIndex ) ); 622 else 623 { 624 pOutDev->ImplInitFontList(); 625 626 aFont.SetName( aSearch ); 627 628 // convert to pixel height 629 Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetSize() ); 630 if ( !aSize.Height() ) 631 { 632 // use default pixel height only when logical height is zero 633 if ( aFont.GetHeight() ) 634 aSize.Height() = 1; 635 else 636 aSize.Height() = (12*pOutDev->mnDPIY)/72; 637 } 638 639 // use default width only when logical width is zero 640 if( (0 == aSize.Width()) && (0 != aFont.GetSize().Width()) ) 641 aSize.Width() = 1; 642 643 // get the name of the first available font 644 float fExactHeight = static_cast<float>(aSize.Height()); 645 ImplFontEntry* pEntry = pOutDev->mpFontCache->GetFontEntry( pOutDev->mpFontList, aFont, aSize, fExactHeight, pOutDev->mpOutDevData ? &pOutDev->mpOutDevData->maDevFontSubst : NULL ); 646 if( pEntry->maFontSelData.mpFontData ) 647 aFont.SetName( pEntry->maFontSelData.mpFontData->maName ); 648 else 649 aFont.SetName( pEntry->maFontSelData.maTargetName ); 650 } 651 } 652 else 653 aFont.SetName( aSearch ); 654 } 655 } 656 657 #if OSL_DEBUG_LEVEL > 2 658 const char* s = "DEFAULTFONT_SANS_UNKNOWN"; 659 switch ( nType ) 660 { 661 case DEFAULTFONT_SANS_UNICODE: s = "DEFAULTFONT_SANS_UNICODE"; break; 662 case DEFAULTFONT_UI_SANS: s = "DEFAULTFONT_UI_SANS"; break; 663 664 case DEFAULTFONT_SANS: s = "DEFAULTFONT_SANS"; break; 665 case DEFAULTFONT_LATIN_HEADING: s = "DEFAULTFONT_LATIN_HEADING"; break; 666 case DEFAULTFONT_LATIN_SPREADSHEET: s = "DEFAULTFONT_LATIN_SPREADSHEET"; break; 667 case DEFAULTFONT_LATIN_DISPLAY: s = "DEFAULTFONT_LATIN_DISPLAY"; break; 668 669 case DEFAULTFONT_SERIF: s = "DEFAULTFONT_SERIF"; break; 670 case DEFAULTFONT_LATIN_TEXT: s = "DEFAULTFONT_LATIN_TEXT"; break; 671 case DEFAULTFONT_LATIN_PRESENTATION: s = "DEFAULTFONT_LATIN_PRESENTATION"; break; 672 673 case DEFAULTFONT_FIXED: s = "DEFAULTFONT_FIXED"; break; 674 case DEFAULTFONT_LATIN_FIXED: s = "DEFAULTFONT_LATIN_FIXED"; break; 675 case DEFAULTFONT_UI_FIXED: s = "DEFAULTFONT_UI_FIXED"; break; 676 677 case DEFAULTFONT_SYMBOL: s = "DEFAULTFONT_SYMBOL"; break; 678 679 case DEFAULTFONT_CJK_TEXT: s = "DEFAULTFONT_CJK_TEXT"; break; 680 case DEFAULTFONT_CJK_PRESENTATION: s = "DEFAULTFONT_CJK_PRESENTATION"; break; 681 case DEFAULTFONT_CJK_SPREADSHEET: s = "DEFAULTFONT_CJK_SPREADSHEET"; break; 682 case DEFAULTFONT_CJK_HEADING: s = "DEFAULTFONT_CJK_HEADING"; break; 683 case DEFAULTFONT_CJK_DISPLAY: s = "DEFAULTFONT_CJK_DISPLAY"; break; 684 685 case DEFAULTFONT_CTL_TEXT: s = "DEFAULTFONT_CTL_TEXT"; break; 686 case DEFAULTFONT_CTL_PRESENTATION: s = "DEFAULTFONT_CTL_PRESENTATION"; break; 687 case DEFAULTFONT_CTL_SPREADSHEET: s = "DEFAULTFONT_CTL_SPREADSHEET"; break; 688 case DEFAULTFONT_CTL_HEADING: s = "DEFAULTFONT_CTL_HEADING"; break; 689 case DEFAULTFONT_CTL_DISPLAY: s = "DEFAULTFONT_CTL_DISPLAY"; break; 690 } 691 fprintf( stderr, " OutputDevice::GetDefaultFont() Type=\"%s\" lang=%d flags=%ld FontName=\"%s\"\n", 692 s, eLang, nFlags, 693 OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr() 694 ); 695 #endif 696 697 return aFont; 698 } 699 700 // ======================================================================= 701 702 static unsigned ImplIsCJKFont( const String& rFontName ) 703 { 704 // Test, if Fontname includes CJK characters --> In this case we 705 // mention that it is a CJK font 706 const sal_Unicode* pStr = rFontName.GetBuffer(); 707 while ( *pStr ) 708 { 709 // japanese 710 if ( ((*pStr >= 0x3040) && (*pStr <= 0x30FF)) || 711 ((*pStr >= 0x3190) && (*pStr <= 0x319F)) ) 712 return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_JP; 713 714 // korean 715 if ( ((*pStr >= 0xAC00) && (*pStr <= 0xD7AF)) || 716 ((*pStr >= 0x3130) && (*pStr <= 0x318F)) || 717 ((*pStr >= 0x1100) && (*pStr <= 0x11FF)) ) 718 return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_KR; 719 720 // chinese 721 if ( ((*pStr >= 0x3400) && (*pStr <= 0x9FFF)) ) 722 return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_TC|IMPL_FONT_ATTR_CJK_SC; 723 724 // cjk 725 if ( ((*pStr >= 0x3000) && (*pStr <= 0xD7AF)) || 726 ((*pStr >= 0xFF00) && (*pStr <= 0xFFEE)) ) 727 return IMPL_FONT_ATTR_CJK; 728 729 pStr++; 730 } 731 732 return 0; 733 } 734 735 // ----------------------------------------------------------------------- 736 737 static void ImplCalcType( sal_uLong& rType, FontWeight& rWeight, FontWidth& rWidth, 738 FontFamily eFamily, const FontNameAttr* pFontAttr ) 739 { 740 if ( eFamily != FAMILY_DONTKNOW ) 741 { 742 if ( eFamily == FAMILY_SWISS ) 743 rType |= IMPL_FONT_ATTR_SANSSERIF; 744 else if ( eFamily == FAMILY_ROMAN ) 745 rType |= IMPL_FONT_ATTR_SERIF; 746 else if ( eFamily == FAMILY_SCRIPT ) 747 rType |= IMPL_FONT_ATTR_SCRIPT; 748 else if ( eFamily == FAMILY_MODERN ) 749 rType |= IMPL_FONT_ATTR_FIXED; 750 else if ( eFamily == FAMILY_DECORATIVE ) 751 rType |= IMPL_FONT_ATTR_DECORATIVE; 752 } 753 754 if ( pFontAttr ) 755 { 756 rType |= pFontAttr->Type; 757 758 if ( ((rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL)) && 759 (pFontAttr->Weight != WEIGHT_DONTKNOW) ) 760 rWeight = pFontAttr->Weight; 761 if ( ((rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL)) && 762 (pFontAttr->Width != WIDTH_DONTKNOW) ) 763 rWidth = pFontAttr->Width; 764 } 765 } 766 767 // ======================================================================= 768 769 ImplFontData::ImplFontData( const ImplDevFontAttributes& rDFA, int nMagic ) 770 : ImplDevFontAttributes( rDFA ), 771 mnWidth(0), 772 mnHeight(0), 773 mnMagic( nMagic ), 774 mpNext( NULL ) 775 { 776 // StarSymbol is a unicode font, but it still deserves the symbol flag 777 if( !mbSymbolFlag ) 778 if( 0 == GetFamilyName().CompareIgnoreCaseToAscii( "starsymbol", 10) 779 || 0 == GetFamilyName().CompareIgnoreCaseToAscii( "opensymbol", 10) ) 780 mbSymbolFlag = true; 781 } 782 783 // ----------------------------------------------------------------------- 784 785 StringCompare ImplFontData::CompareIgnoreSize( const ImplFontData& rOther ) const 786 { 787 // compare their width, weight, italic and style name 788 if( meWidthType < rOther.meWidthType ) 789 return COMPARE_LESS; 790 else if( meWidthType > rOther.meWidthType ) 791 return COMPARE_GREATER; 792 793 if( meWeight < rOther.meWeight ) 794 return COMPARE_LESS; 795 else if( meWeight > rOther.meWeight ) 796 return COMPARE_GREATER; 797 798 if( meItalic < rOther.meItalic ) 799 return COMPARE_LESS; 800 else if( meItalic > rOther.meItalic ) 801 return COMPARE_GREATER; 802 803 StringCompare eCompare = maName.CompareTo( rOther.maName ); 804 return eCompare; 805 } 806 807 // ----------------------------------------------------------------------- 808 809 StringCompare ImplFontData::CompareWithSize( const ImplFontData& rOther ) const 810 { 811 StringCompare eCompare = CompareIgnoreSize( rOther ); 812 if( eCompare != COMPARE_EQUAL ) 813 return eCompare; 814 815 if( mnHeight < rOther.mnHeight ) 816 return COMPARE_LESS; 817 else if( mnHeight > rOther.mnHeight ) 818 return COMPARE_GREATER; 819 820 if( mnWidth < rOther.mnWidth ) 821 return COMPARE_LESS; 822 else if( mnWidth > rOther.mnWidth ) 823 return COMPARE_GREATER; 824 825 return COMPARE_EQUAL; 826 } 827 828 // ----------------------------------------------------------------------- 829 830 struct FontMatchStatus 831 { 832 public: 833 int mnFaceMatch; 834 int mnHeightMatch; 835 int mnWidthMatch; 836 const xub_Unicode* mpTargetStyleName; 837 }; 838 839 bool ImplFontData::IsBetterMatch( const ImplFontSelectData& rFSD, FontMatchStatus& rStatus ) const 840 { 841 int nMatch = 0; 842 843 const String& rFontName = rFSD.maTargetName; 844 if( (rFontName == maName) || rFontName.EqualsIgnoreCaseAscii( maName ) ) 845 nMatch += 240000; 846 847 if( rStatus.mpTargetStyleName 848 && maStyleName.EqualsIgnoreCaseAscii( rStatus.mpTargetStyleName ) ) 849 nMatch += 120000; 850 851 if( (rFSD.mePitch != PITCH_DONTKNOW) && (rFSD.mePitch == mePitch) ) 852 nMatch += 20000; 853 854 // prefer NORMAL font width 855 // TODO: change when the upper layers can tell their width preference 856 if( meWidthType == WIDTH_NORMAL ) 857 nMatch += 400; 858 else if( (meWidthType == WIDTH_SEMI_EXPANDED) || (meWidthType == WIDTH_SEMI_CONDENSED) ) 859 nMatch += 300; 860 861 if( rFSD.meWeight != WEIGHT_DONTKNOW ) 862 { 863 // if not bold prefer light fonts to bold fonts 864 int nReqWeight = (int)rFSD.meWeight; 865 if ( rFSD.meWeight > WEIGHT_MEDIUM ) 866 nReqWeight += 100; 867 868 int nGivenWeight = (int)meWeight; 869 if( meWeight > WEIGHT_MEDIUM ) 870 nGivenWeight += 100; 871 872 int nWeightDiff = nReqWeight - nGivenWeight; 873 874 if ( nWeightDiff == 0 ) 875 nMatch += 1000; 876 else if ( nWeightDiff == +1 || nWeightDiff == -1 ) 877 nMatch += 700; 878 else if ( nWeightDiff < +50 && nWeightDiff > -50) 879 nMatch += 200; 880 } 881 else // requested weight == WEIGHT_DONTKNOW 882 { 883 // prefer NORMAL font weight 884 // TODO: change when the upper layers can tell their weight preference 885 if( meWeight == WEIGHT_NORMAL ) 886 nMatch += 450; 887 else if( meWeight == WEIGHT_MEDIUM ) 888 nMatch += 350; 889 else if( (meWeight == WEIGHT_SEMILIGHT) || (meWeight == WEIGHT_SEMIBOLD) ) 890 nMatch += 200; 891 else if( meWeight == WEIGHT_LIGHT ) 892 nMatch += 150; 893 } 894 895 if ( rFSD.meItalic == ITALIC_NONE ) 896 { 897 if( meItalic == ITALIC_NONE ) 898 nMatch += 900; 899 } 900 else 901 { 902 if( rFSD.meItalic == meItalic ) 903 nMatch += 900; 904 else if( meItalic != ITALIC_NONE ) 905 nMatch += 600; 906 } 907 908 if( mbDevice ) 909 nMatch += 1; 910 911 int nHeightMatch = 0; 912 int nWidthMatch = 0; 913 914 if( IsScalable() ) 915 { 916 if( rFSD.mnOrientation != 0 ) 917 nMatch += 80; 918 else if( rFSD.mnWidth != 0 ) 919 nMatch += 25; 920 else 921 nMatch += 5; 922 } 923 else 924 { 925 if( rFSD.mnHeight == mnHeight ) 926 { 927 nMatch += 20; 928 if( rFSD.mnWidth == mnWidth ) 929 nMatch += 10; 930 } 931 else 932 { 933 // for non-scalable fonts the size difference is very important 934 // prefer the smaller font face because of clipping/overlapping issues 935 int nHeightDiff = (rFSD.mnHeight - mnHeight) * 1000; 936 nHeightMatch = (nHeightDiff >= 0) ? -nHeightDiff : 100+nHeightDiff; 937 if( rFSD.mnHeight ) 938 nHeightMatch /= rFSD.mnHeight; 939 940 if( (rFSD.mnWidth != 0) && (mnWidth != 0) && (rFSD.mnWidth != mnWidth) ) 941 { 942 int nWidthDiff = (rFSD.mnWidth - mnWidth) * 100; 943 nWidthMatch = (nWidthDiff >= 0) ? -nWidthDiff : +nWidthDiff; 944 } 945 } 946 } 947 948 if( rStatus.mnFaceMatch > nMatch ) 949 return false; 950 else if( rStatus.mnFaceMatch < nMatch ) 951 { 952 rStatus.mnFaceMatch = nMatch; 953 rStatus.mnHeightMatch = nHeightMatch; 954 rStatus.mnWidthMatch = nWidthMatch; 955 return true; 956 } 957 958 // when two fonts are still competing prefer the 959 // one with the best matching height 960 if( rStatus.mnHeightMatch > nHeightMatch ) 961 return false; 962 else if( rStatus.mnHeightMatch < nHeightMatch ) 963 { 964 rStatus.mnHeightMatch = nHeightMatch; 965 rStatus.mnWidthMatch = nWidthMatch; 966 return true; 967 } 968 969 if( rStatus.mnWidthMatch > nWidthMatch ) 970 return false; 971 972 rStatus.mnWidthMatch = nWidthMatch; 973 return true; 974 } 975 976 // ======================================================================= 977 978 ImplFontEntry::ImplFontEntry( const ImplFontSelectData& rFontSelData ) 979 : maFontSelData( rFontSelData ), 980 maMetric( rFontSelData ), 981 mpConversion( NULL ), 982 mnRefCount( 1 ), 983 mnSetFontFlags( 0 ), 984 mnOwnOrientation( 0 ), 985 mnOrientation( 0 ), 986 mbInit( false ), 987 mpUnicodeFallbackList( NULL ) 988 { 989 maFontSelData.mpFontEntry = this; 990 } 991 992 // ----------------------------------------------------------------------- 993 994 ImplFontEntry::~ImplFontEntry() 995 { 996 delete mpUnicodeFallbackList; 997 } 998 999 // ----------------------------------------------------------------------- 1000 1001 size_t ImplFontEntry::GFBCacheKey_Hash::operator()( const GFBCacheKey& rData ) const 1002 { 1003 std::hash<sal_UCS4> a; 1004 std::hash<int > b; 1005 return a(rData.first) ^ b(rData.second); 1006 } 1007 1008 inline void ImplFontEntry::AddFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const String& rFontName ) 1009 { 1010 if( !mpUnicodeFallbackList ) 1011 mpUnicodeFallbackList = new UnicodeFallbackList; 1012 (*mpUnicodeFallbackList)[ GFBCacheKey(cChar,eWeight) ] = rFontName; 1013 } 1014 1015 // ----------------------------------------------------------------------- 1016 1017 inline bool ImplFontEntry::GetFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, String* pFontName ) const 1018 { 1019 if( !mpUnicodeFallbackList ) 1020 return false; 1021 1022 UnicodeFallbackList::const_iterator it = mpUnicodeFallbackList->find( GFBCacheKey(cChar,eWeight) ); 1023 if( it == mpUnicodeFallbackList->end() ) 1024 return false; 1025 1026 *pFontName = (*it).second; 1027 return true; 1028 } 1029 1030 // ----------------------------------------------------------------------- 1031 1032 inline void ImplFontEntry::IgnoreFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const String& rFontName ) 1033 { 1034 // DBG_ASSERT( mpUnicodeFallbackList, "ImplFontEntry::IgnoreFallbackForUnicode no list" ); 1035 UnicodeFallbackList::iterator it = mpUnicodeFallbackList->find( GFBCacheKey(cChar,eWeight) ); 1036 // DBG_ASSERT( it != mpUnicodeFallbackList->end(), "ImplFontEntry::IgnoreFallbackForUnicode no match" ); 1037 if( it == mpUnicodeFallbackList->end() ) 1038 return; 1039 if( (*it).second == rFontName ) 1040 mpUnicodeFallbackList->erase( it ); 1041 } 1042 1043 // ======================================================================= 1044 1045 ImplDevFontListData::ImplDevFontListData( const String& rSearchName ) 1046 : mpFirst( NULL ), 1047 maSearchName( rSearchName ), 1048 mnTypeFaces( 0 ), 1049 mnMatchType( 0 ), 1050 meMatchWeight( WEIGHT_DONTKNOW ), 1051 meMatchWidth( WIDTH_DONTKNOW ), 1052 meFamily( FAMILY_DONTKNOW ), 1053 mePitch( PITCH_DONTKNOW ), 1054 mnMinQuality( -1 ) 1055 {} 1056 1057 // ----------------------------------------------------------------------- 1058 1059 ImplDevFontListData::~ImplDevFontListData() 1060 { 1061 // release all physical font faces 1062 while( mpFirst ) 1063 { 1064 ImplFontData* pFace = mpFirst; 1065 mpFirst = pFace->GetNextFace(); 1066 delete pFace; 1067 } 1068 } 1069 1070 // ----------------------------------------------------------------------- 1071 1072 bool ImplDevFontListData::AddFontFace( ImplFontData* pNewData ) 1073 { 1074 pNewData->mpNext = NULL; 1075 1076 if( !mpFirst ) 1077 { 1078 maName = pNewData->maName; 1079 maMapNames = pNewData->maMapNames; 1080 meFamily = pNewData->meFamily; 1081 mePitch = pNewData->mePitch; 1082 mnMinQuality = pNewData->mnQuality; 1083 } 1084 else 1085 { 1086 if( meFamily == FAMILY_DONTKNOW ) 1087 meFamily = pNewData->meFamily; 1088 if( mePitch == PITCH_DONTKNOW ) 1089 mePitch = pNewData->mePitch; 1090 if( mnMinQuality > pNewData->mnQuality ) 1091 mnMinQuality = pNewData->mnQuality; 1092 } 1093 1094 // set attributes for attribute based font matching 1095 if( pNewData->IsScalable() ) 1096 mnTypeFaces |= IMPL_DEVFONT_SCALABLE; 1097 1098 if( pNewData->IsSymbolFont() ) 1099 mnTypeFaces |= IMPL_DEVFONT_SYMBOL; 1100 else 1101 mnTypeFaces |= IMPL_DEVFONT_NONESYMBOL; 1102 1103 if( pNewData->meWeight != WEIGHT_DONTKNOW ) 1104 { 1105 if( pNewData->meWeight >= WEIGHT_SEMIBOLD ) 1106 mnTypeFaces |= IMPL_DEVFONT_BOLD; 1107 else if( pNewData->meWeight <= WEIGHT_SEMILIGHT ) 1108 mnTypeFaces |= IMPL_DEVFONT_LIGHT; 1109 else 1110 mnTypeFaces |= IMPL_DEVFONT_NORMAL; 1111 } 1112 1113 if( pNewData->meItalic == ITALIC_NONE ) 1114 mnTypeFaces |= IMPL_DEVFONT_NONEITALIC; 1115 else if( (pNewData->meItalic == ITALIC_NORMAL) 1116 || (pNewData->meItalic == ITALIC_OBLIQUE) ) 1117 mnTypeFaces |= IMPL_DEVFONT_ITALIC; 1118 1119 if( (meMatchWeight == WEIGHT_DONTKNOW) 1120 || (meMatchWidth == WIDTH_DONTKNOW) 1121 || (mnMatchType == 0) ) 1122 { 1123 // TODO: is it cheaper to calc matching attributes now or on demand? 1124 // calc matching attributes if other entries are already initialized 1125 1126 // MT: Perform05: Do lazy, quite expensive, not needed in start-up! 1127 // const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); 1128 // InitMatchData( rFontSubst, maSearchName ); 1129 // mbMatchData=true; // Somewhere else??? 1130 } 1131 1132 // reassign name (sharing saves memory) 1133 if( pNewData->maName == maName ) 1134 pNewData->maName = maName; 1135 1136 // insert new physical font face into linked list 1137 // TODO: get rid of linear search? 1138 ImplFontData* pData; 1139 ImplFontData** ppHere = &mpFirst; 1140 for(; (pData=*ppHere) != NULL; ppHere=&pData->mpNext ) 1141 { 1142 StringCompare eComp = pNewData->CompareWithSize( *pData ); 1143 if( eComp == COMPARE_GREATER ) 1144 continue; 1145 if( eComp == COMPARE_LESS ) 1146 break; 1147 1148 // ignore duplicate if its quality is worse 1149 if( pNewData->mnQuality < pData->mnQuality ) 1150 return false; 1151 1152 // keep the device font if its quality is good enough 1153 if( (pNewData->mnQuality == pData->mnQuality) 1154 && (pData->mbDevice || !pNewData->mbDevice) ) 1155 return false; 1156 1157 // replace existing font face with a better one 1158 pNewData->mpNext = pData->mpNext; 1159 *ppHere = pNewData; 1160 delete pData; 1161 return true; 1162 } 1163 1164 // insert into or append to list of physical font faces 1165 pNewData->mpNext = pData; 1166 *ppHere = pNewData; 1167 return true; 1168 } 1169 1170 // ----------------------------------------------------------------------- 1171 1172 // get font attributes using the normalized font family name 1173 void ImplDevFontListData::InitMatchData( const utl::FontSubstConfiguration& rFontSubst, 1174 const String& rSearchName ) 1175 { 1176 String aShortName; 1177 // get font attributes from the decorated font name 1178 rFontSubst.getMapName( rSearchName, aShortName, maMatchFamilyName, 1179 meMatchWeight, meMatchWidth, mnMatchType ); 1180 const FontNameAttr* pFontAttr = rFontSubst.getSubstInfo( rSearchName ); 1181 // eventually use the stripped name 1182 if( !pFontAttr ) 1183 if( aShortName != rSearchName ) 1184 pFontAttr = rFontSubst.getSubstInfo( aShortName ); 1185 ImplCalcType( mnMatchType, meMatchWeight, meMatchWidth, meFamily, pFontAttr ); 1186 mnMatchType |= ImplIsCJKFont( maName ); 1187 } 1188 1189 // ----------------------------------------------------------------------- 1190 1191 ImplFontData* ImplDevFontListData::FindBestFontFace( const ImplFontSelectData& rFSD ) const 1192 { 1193 if( !mpFirst ) 1194 return NULL; 1195 if( !mpFirst->GetNextFace() ) 1196 return mpFirst; 1197 1198 // FontName+StyleName should map to FamilyName+StyleName 1199 const String& rSearchName = rFSD.maTargetName; 1200 const xub_Unicode* pTargetStyleName = NULL; 1201 if( (rSearchName.Len() > maSearchName.Len()) 1202 && rSearchName.Equals( maSearchName, 0, maSearchName.Len() ) ) 1203 pTargetStyleName = rSearchName.GetBuffer() + maSearchName.Len() + 1; 1204 1205 // linear search, TODO: improve? 1206 ImplFontData* pFontFace = mpFirst; 1207 ImplFontData* pBestFontFace = pFontFace; 1208 FontMatchStatus aFontMatchStatus = {0,0,0, pTargetStyleName}; 1209 for(; pFontFace; pFontFace = pFontFace->GetNextFace() ) 1210 if( pFontFace->IsBetterMatch( rFSD, aFontMatchStatus ) ) 1211 pBestFontFace = pFontFace; 1212 1213 return pBestFontFace; 1214 } 1215 1216 // ----------------------------------------------------------------------- 1217 1218 // update device font list with unique font faces, with uniqueness 1219 // meaning different font attributes, but not different fonts sizes 1220 void ImplDevFontListData::UpdateDevFontList( ImplGetDevFontList& rDevFontList ) const 1221 { 1222 ImplFontData* pPrevFace = NULL; 1223 for( ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) 1224 { 1225 if( !pPrevFace || pFace->CompareIgnoreSize( *pPrevFace ) ) 1226 rDevFontList.Add( pFace ); 1227 pPrevFace = pFace; 1228 } 1229 } 1230 1231 // ----------------------------------------------------------------------- 1232 1233 void ImplDevFontListData::GetFontHeights( std::set<int>& rHeights ) const 1234 { 1235 // add all available font heights 1236 for( const ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) 1237 rHeights.insert( pFace->GetHeight() ); 1238 } 1239 1240 // ----------------------------------------------------------------------- 1241 1242 void ImplDevFontListData::UpdateCloneFontList( ImplDevFontList& rDevFontList, 1243 bool bScalable, bool bEmbeddable ) const 1244 { 1245 for( ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() ) 1246 { 1247 if( bScalable && !pFace->IsScalable() ) 1248 continue; 1249 if( bEmbeddable && !pFace->IsEmbeddable() && !pFace->IsSubsettable() ) 1250 continue; 1251 1252 ImplFontData* pClonedFace = pFace->Clone(); 1253 rDevFontList.Add( pClonedFace ); 1254 } 1255 } 1256 1257 // ======================================================================= 1258 1259 ImplDevFontList::ImplDevFontList() 1260 : mbMatchData( false ) 1261 , mbMapNames( false ) 1262 , mpPreMatchHook( NULL ) 1263 , mpFallbackHook( NULL ) 1264 , mpFallbackList( NULL ) 1265 , mnFallbackCount( -1 ) 1266 {} 1267 1268 // ----------------------------------------------------------------------- 1269 1270 ImplDevFontList::~ImplDevFontList() 1271 { 1272 Clear(); 1273 } 1274 1275 // ----------------------------------------------------------------------- 1276 1277 void ImplDevFontList::SetPreMatchHook( ImplPreMatchFontSubstitution* pHook ) 1278 { 1279 mpPreMatchHook = pHook; 1280 } 1281 1282 // ----------------------------------------------------------------------- 1283 1284 void ImplDevFontList::SetFallbackHook( ImplGlyphFallbackFontSubstitution* pHook ) 1285 { 1286 mpFallbackHook = pHook; 1287 } 1288 1289 // ----------------------------------------------------------------------- 1290 1291 void ImplDevFontList::Clear() 1292 { 1293 // remove fallback lists 1294 delete[] mpFallbackList; 1295 mpFallbackList = NULL; 1296 mnFallbackCount = -1; 1297 1298 // clear all entries in the device font list 1299 DevFontList::iterator it = maDevFontList.begin(); 1300 for(; it != maDevFontList.end(); ++it ) 1301 { 1302 ImplDevFontListData* pEntry = (*it).second; 1303 delete pEntry; 1304 } 1305 1306 maDevFontList.clear(); 1307 1308 // match data must be recalculated too 1309 mbMatchData = false; 1310 } 1311 1312 1313 // ----------------------------------------------------------------------- 1314 1315 void ImplDevFontList::InitGenericGlyphFallback( void ) const 1316 { 1317 // normalized family names of fonts suited for glyph fallback 1318 // if a font is available related fonts can be ignored 1319 // TODO: implement dynamic lists 1320 static const char* aGlyphFallbackList[] = { 1321 // empty strings separate the names of unrelated fonts 1322 "eudc", "", 1323 "arialunicodems", "cyberbit", "code2000", "", 1324 "andalesansui", "", 1325 "starsymbol", "opensymbol", "", 1326 "msmincho", "fzmingti", "fzheiti", "ipamincho", "sazanamimincho", "kochimincho", "", 1327 "sunbatang", "sundotum", "baekmukdotum", "gulim", "batang", "dotum", "", 1328 "hgmincholightj", "msunglightsc", "msunglighttc", "hymyeongjolightk", "", 1329 "tahoma", "dejavusans", "timesnewroman", "liberationsans", "", 1330 "shree", "mangal", "", 1331 "raavi", "shruti", "tunga", "", 1332 "latha", "gautami", "kartika", "vrinda", "", 1333 "shayyalmt", "naskmt", "scheherazade", "", 1334 "david", "nachlieli", "lucidagrande", "", 1335 "norasi", "angsanaupc", "", 1336 "khmerossystem", "", 1337 "muktinarrow", "", 1338 "phetsarathot", "", 1339 "padauk", "pinlonmyanmar", "", 1340 "iskoolapota", "lklug", "", 1341 0 1342 }; 1343 1344 bool bHasEudc = false; 1345 int nMaxLevel = 0; 1346 int nBestQuality = 0; 1347 ImplDevFontListData** pFallbackList = NULL; 1348 for( const char** ppNames = &aGlyphFallbackList[0];; ++ppNames ) 1349 { 1350 // advance to next sub-list when end-of-sublist marker 1351 if( !**ppNames ) // #i46456# check for empty string, i.e., deref string itself not only ptr to it 1352 { 1353 if( nBestQuality > 0 ) 1354 if( ++nMaxLevel >= MAX_FALLBACK ) 1355 break; 1356 if( !ppNames[1] ) 1357 break; 1358 nBestQuality = 0; 1359 continue; 1360 } 1361 1362 // test if the glyph fallback candidate font is available and scalable 1363 String aTokenName( *ppNames, RTL_TEXTENCODING_UTF8 ); 1364 ImplDevFontListData* pFallbackFont = FindFontFamily( aTokenName ); 1365 if( !pFallbackFont ) 1366 continue; 1367 if( !pFallbackFont->IsScalable() ) 1368 continue; 1369 1370 // keep the best font of the glyph fallback sub-list 1371 if( nBestQuality < pFallbackFont->GetMinQuality() ) 1372 { 1373 nBestQuality = pFallbackFont->GetMinQuality(); 1374 // store available glyph fallback fonts 1375 if( !pFallbackList ) 1376 pFallbackList = new ImplDevFontListData*[ MAX_FALLBACK ]; 1377 pFallbackList[ nMaxLevel ] = pFallbackFont; 1378 if( !bHasEudc && !nMaxLevel ) 1379 bHasEudc = !strncmp( *ppNames, "eudc", 5 ); 1380 } 1381 } 1382 1383 #ifdef SAL_FONTENUM_STABLE_ON_PLATFORM // #i113472# 1384 // sort the list of fonts for glyph fallback by quality (highest first) 1385 // #i33947# keep the EUDC font at the front of the list 1386 // an insertion sort is good enough for this short list 1387 const int nSortStart = bHasEudc ? 1 : 0; 1388 for( int i = nSortStart+1, j; i < nMaxLevel; ++i ) 1389 { 1390 ImplDevFontListData* pTestFont = pFallbackList[ i ]; 1391 int nTestQuality = pTestFont->GetMinQuality(); 1392 for( j = i; --j >= nSortStart; ) 1393 if( nTestQuality > pFallbackList[j]->GetMinQuality() ) 1394 pFallbackList[ j+1 ] = pFallbackList[ j ]; 1395 else 1396 break; 1397 pFallbackList[ j+1 ] = pTestFont; 1398 } 1399 #endif 1400 1401 #if defined(HDU_DEBUG) 1402 for( int i = 0; i < nMaxLevel; ++i ) 1403 { 1404 ImplDevFontListData* pFont = pFallbackList[ i ]; 1405 ByteString aFontName( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ); 1406 fprintf( stderr, "GlyphFallbackFont[%d] (quality=%05d): \"%s\"\n", 1407 i, pFont->GetMinQuality(), aFontName.GetBuffer() ); 1408 } 1409 #endif 1410 1411 mnFallbackCount = nMaxLevel; 1412 mpFallbackList = pFallbackList; 1413 } 1414 1415 // ----------------------------------------------------------------------- 1416 1417 ImplDevFontListData* ImplDevFontList::GetGlyphFallbackFont( ImplFontSelectData& rFontSelData, 1418 rtl::OUString& rMissingCodes, int nFallbackLevel ) const 1419 { 1420 ImplDevFontListData* pFallbackData = NULL; 1421 1422 // find a matching font candidate for platform specific glyph fallback 1423 if( mpFallbackHook ) 1424 { 1425 // check cache for the first matching entry 1426 // to avoid calling the expensive fallback hook (#i83491#) 1427 sal_UCS4 cChar = 0; 1428 bool bCached = true; 1429 sal_Int32 nStrIndex = 0; 1430 while( nStrIndex < rMissingCodes.getLength() ) 1431 { 1432 cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); 1433 bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ); 1434 // ignore entries which don't have a fallback 1435 if( !bCached || (rFontSelData.maSearchName.Len() != 0) ) 1436 break; 1437 } 1438 1439 if( bCached ) 1440 { 1441 // there is a matching fallback in the cache 1442 // so update rMissingCodes with codepoints not yet resolved by this fallback 1443 int nRemainingLength = 0; 1444 sal_UCS4* pRemainingCodes = (sal_UCS4*)alloca( rMissingCodes.getLength() * sizeof(sal_UCS4) ); 1445 String aFontName; 1446 while( nStrIndex < rMissingCodes.getLength() ) 1447 { 1448 cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); 1449 bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &aFontName ); 1450 if( !bCached || (rFontSelData.maSearchName != aFontName) ) 1451 pRemainingCodes[ nRemainingLength++ ] = cChar; 1452 } 1453 rMissingCodes = rtl::OUString( pRemainingCodes, nRemainingLength ); 1454 } 1455 else 1456 { 1457 rtl::OUString aOldMissingCodes = rMissingCodes; 1458 // call the hook to query the best matching glyph fallback font 1459 if( mpFallbackHook->FindFontSubstitute( rFontSelData, rMissingCodes ) ) 1460 // apply outdev3.cxx specific fontname normalization 1461 GetEnglishSearchFontName( rFontSelData.maSearchName ); 1462 else 1463 rFontSelData.maSearchName = String(); 1464 1465 // cache the result even if there was no match 1466 for(;;) 1467 { 1468 if( !rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ) ) 1469 rFontSelData.mpFontEntry->AddFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName ); 1470 if( nStrIndex >= aOldMissingCodes.getLength() ) 1471 break; 1472 cChar = aOldMissingCodes.iterateCodePoints( &nStrIndex ); 1473 } 1474 if( rFontSelData.maSearchName.Len() != 0 ) 1475 { 1476 // remove cache entries that were still not resolved 1477 for( nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); ) 1478 { 1479 cChar = rMissingCodes.iterateCodePoints( &nStrIndex ); 1480 rFontSelData.mpFontEntry->IgnoreFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName ); 1481 } 1482 } 1483 } 1484 1485 // find the matching device font 1486 if( rFontSelData.maSearchName.Len() != 0 ) 1487 pFallbackData = FindFontFamily( rFontSelData.maSearchName ); 1488 } 1489 1490 // else find a matching font candidate for generic glyph fallback 1491 if( !pFallbackData ) 1492 { 1493 // initialize font candidates for generic glyph fallback if needed 1494 if( mnFallbackCount < 0 ) 1495 InitGenericGlyphFallback(); 1496 // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook 1497 if( nFallbackLevel < mnFallbackCount ) 1498 pFallbackData = mpFallbackList[ nFallbackLevel ]; 1499 } 1500 1501 return pFallbackData; 1502 } 1503 1504 // ----------------------------------------------------------------------- 1505 1506 void ImplDevFontList::Add( ImplFontData* pNewData ) 1507 { 1508 int nAliasQuality = pNewData->mnQuality - 100; 1509 String aMapNames = pNewData->maMapNames; 1510 pNewData->maMapNames = String(); 1511 1512 bool bKeepNewData = false; 1513 for( xub_StrLen nMapNameIndex = 0; nMapNameIndex != STRING_NOTFOUND; ) 1514 { 1515 String aSearchName = pNewData->maName; 1516 GetEnglishSearchFontName( aSearchName ); 1517 1518 DevFontList::const_iterator it = maDevFontList.find( aSearchName ); 1519 ImplDevFontListData* pFoundData = NULL; 1520 if( it != maDevFontList.end() ) 1521 pFoundData = (*it).second; 1522 1523 if( !pFoundData ) 1524 { 1525 pFoundData = new ImplDevFontListData( aSearchName ); 1526 maDevFontList[ aSearchName ] = pFoundData; 1527 } 1528 1529 bKeepNewData = pFoundData->AddFontFace( pNewData ); 1530 1531 // add font alias if available 1532 // a font alias should never win against an original font with similar quality 1533 if( aMapNames.Len() <= nMapNameIndex ) 1534 break; 1535 if( bKeepNewData ) // try to recycle obsoleted object 1536 pNewData = pNewData->CreateAlias(); 1537 bKeepNewData = false; 1538 pNewData->mnQuality = nAliasQuality; 1539 pNewData->maName = GetNextFontToken( aMapNames, nMapNameIndex ); 1540 } 1541 1542 if( !bKeepNewData ) 1543 delete pNewData; 1544 } 1545 1546 // ----------------------------------------------------------------------- 1547 1548 // find the font from the normalized font family name 1549 ImplDevFontListData* ImplDevFontList::ImplFindBySearchName( const String& rSearchName ) const 1550 { 1551 #ifdef DEBUG 1552 String aTempName = rSearchName; 1553 GetEnglishSearchFontName( aTempName ); 1554 DBG_ASSERT( aTempName == rSearchName, "ImplDevFontList::ImplFindBySearchName() called with non-normalized name" ); 1555 #endif 1556 1557 DevFontList::const_iterator it = maDevFontList.find( rSearchName ); 1558 if( it == maDevFontList.end() ) 1559 return NULL; 1560 1561 ImplDevFontListData* pFoundData = (*it).second; 1562 return pFoundData; 1563 } 1564 1565 // ----------------------------------------------------------------------- 1566 1567 ImplDevFontListData* ImplDevFontList::ImplFindByAliasName( const String& rSearchName, const String& rShortName ) const 1568 { 1569 // short circuit for impossible font name alias 1570 if( !rSearchName.Len() ) 1571 return NULL; 1572 1573 // short circuit if no alias names are available 1574 if( !mbMapNames ) 1575 return NULL; 1576 1577 // use the font's alias names to find the font 1578 // TODO: get rid of linear search 1579 DevFontList::const_iterator it = maDevFontList.begin(); 1580 while( it != maDevFontList.end() ) 1581 { 1582 ImplDevFontListData* pData = (*it).second; 1583 if( !pData->maMapNames.Len() ) 1584 continue; 1585 1586 // if one alias name matches we found a matching font 1587 String aTempName; 1588 xub_StrLen nIndex = 0; 1589 do 1590 { 1591 aTempName = GetNextFontToken( pData->maMapNames, nIndex ); 1592 // Test, if the Font name match with one of the mapping names 1593 if ( (aTempName == rSearchName) || (aTempName == rShortName) ) 1594 return pData; 1595 } 1596 while ( nIndex != STRING_NOTFOUND ); 1597 } 1598 1599 return NULL; 1600 } 1601 1602 // ----------------------------------------------------------------------- 1603 1604 ImplDevFontListData* ImplDevFontList::FindFontFamily( const String& rFontName ) const 1605 { 1606 // normalize the font fomily name and 1607 String aName = rFontName; 1608 GetEnglishSearchFontName( aName ); 1609 ImplDevFontListData* pFound = ImplFindBySearchName( aName ); 1610 return pFound; 1611 } 1612 1613 // ----------------------------------------------------------------------- 1614 1615 ImplDevFontListData* ImplDevFontList::ImplFindByTokenNames( const String& rTokenStr ) const 1616 { 1617 ImplDevFontListData* pFoundData = NULL; 1618 1619 // use normalized font name tokens to find the font 1620 for( xub_StrLen nTokenPos = 0; nTokenPos != STRING_NOTFOUND; ) 1621 { 1622 String aSearchName = GetNextFontToken( rTokenStr, nTokenPos ); 1623 if( !aSearchName.Len() ) 1624 continue; 1625 GetEnglishSearchFontName( aSearchName ); 1626 pFoundData = ImplFindBySearchName( aSearchName ); 1627 if( pFoundData ) 1628 break; 1629 } 1630 1631 return pFoundData; 1632 } 1633 1634 // ----------------------------------------------------------------------- 1635 1636 ImplDevFontListData* ImplDevFontList::ImplFindBySubstFontAttr( const utl::FontNameAttr& rFontAttr ) const 1637 { 1638 ImplDevFontListData* pFoundData = NULL; 1639 1640 // use the font substitutions suggested by the FontNameAttr to find the font 1641 ::std::vector< String >::const_iterator it = rFontAttr.Substitutions.begin(); 1642 for(; it != rFontAttr.Substitutions.end(); ++it ) 1643 { 1644 String aSearchName( *it ); 1645 GetEnglishSearchFontName( aSearchName ); 1646 1647 pFoundData = ImplFindBySearchName( aSearchName ); 1648 if( pFoundData ) 1649 return pFoundData; 1650 } 1651 1652 // use known attributes from the configuration to find a matching substitute 1653 const sal_uLong nSearchType = rFontAttr.Type; 1654 if( nSearchType != 0 ) 1655 { 1656 const FontWeight eSearchWeight = rFontAttr.Weight; 1657 const FontWidth eSearchWidth = rFontAttr.Width; 1658 const FontItalic eSearchSlant = ITALIC_DONTKNOW; 1659 const FontFamily eSearchFamily = FAMILY_DONTKNOW; 1660 const String aSearchName; 1661 pFoundData = ImplFindByAttributes( nSearchType, 1662 eSearchWeight, eSearchWidth, eSearchFamily, eSearchSlant, aSearchName ); 1663 if( pFoundData ) 1664 return pFoundData; 1665 } 1666 1667 return NULL; 1668 } 1669 1670 // ----------------------------------------------------------------------- 1671 1672 void ImplDevFontList::InitMatchData() const 1673 { 1674 // short circuit if already done 1675 if( mbMatchData ) 1676 return; 1677 mbMatchData = true; 1678 1679 // calculate MatchData for all entries 1680 const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); 1681 1682 DevFontList::const_iterator it = maDevFontList.begin(); 1683 for(; it != maDevFontList.end(); ++it ) 1684 { 1685 const String& rSearchName = (*it).first; 1686 ImplDevFontListData* pEntry = (*it).second; 1687 1688 pEntry->InitMatchData( rFontSubst, rSearchName ); 1689 } 1690 } 1691 1692 //---------------------------------------------------------------------------- 1693 ImplDevFontListData* ImplDevFontList::ImplFindByLocale( com::sun::star::lang::Locale& rLocale ) const 1694 { 1695 // get the default font for a specified locale 1696 const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get(); 1697 const String aDefault = rDefaults.getUserInterfaceFont( rLocale ); 1698 ImplDevFontListData* pFontData = ImplFindByTokenNames( aDefault ); 1699 if( pFontData ) 1700 return pFontData; 1701 return NULL; 1702 } 1703 1704 // ----------------------------------------------------------------------- 1705 1706 ImplDevFontListData* ImplDevFontList::ImplFindByAttributes( sal_uLong nSearchType, 1707 FontWeight eSearchWeight, FontWidth eSearchWidth, FontFamily /*eSearchFamily*/, 1708 FontItalic eSearchItalic, const String& rSearchFamilyName ) const 1709 { 1710 if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) ) 1711 nSearchType |= IMPL_FONT_ATTR_ITALIC; 1712 1713 // don't bother to match attributes if the attributes aren't worth matching 1714 if( !nSearchType 1715 && ((eSearchWeight == WEIGHT_DONTKNOW) || (eSearchWeight == WEIGHT_NORMAL)) 1716 && ((eSearchWidth == WIDTH_DONTKNOW) || (eSearchWidth == WIDTH_NORMAL)) ) 1717 return NULL; 1718 1719 InitMatchData(); 1720 ImplDevFontListData* pFoundData = NULL; 1721 1722 long nTestMatch; 1723 long nBestMatch = 40000; 1724 sal_uLong nBestType = 0; 1725 1726 DevFontList::const_iterator it = maDevFontList.begin(); 1727 for(; it != maDevFontList.end(); ++it ) 1728 { 1729 ImplDevFontListData* pData = (*it).second; 1730 1731 // Get all information about the matching font 1732 sal_uLong nMatchType = pData->mnMatchType; 1733 FontWeight eMatchWeight= pData->meMatchWeight; 1734 FontWidth eMatchWidth = pData->meMatchWidth; 1735 1736 // Calculate Match Value 1737 // 1000000000 1738 // 100000000 1739 // 10000000 CJK, CTL, None-Latin, Symbol 1740 // 1000000 FamilyName, Script, Fixed, -Special, -Decorative, 1741 // Titling, Capitals, Outline, Shadow 1742 // 100000 Match FamilyName, Serif, SansSerif, Italic, 1743 // Width, Weight 1744 // 10000 Scalable, Standard, Default, 1745 // full, Normal, Knownfont, 1746 // Otherstyle, +Special, +Decorative, 1747 // 1000 Typewriter, Rounded, Gothic, Schollbook 1748 // 100 1749 nTestMatch = 0; 1750 1751 // test CJK script attributes 1752 if ( nSearchType & IMPL_FONT_ATTR_CJK ) 1753 { 1754 // Matching language 1755 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_CJK_ALLLANG) ) 1756 nTestMatch += 10000000*3; 1757 if( nMatchType & IMPL_FONT_ATTR_CJK ) 1758 nTestMatch += 10000000*2; 1759 if( nMatchType & IMPL_FONT_ATTR_FULL ) 1760 nTestMatch += 10000000; 1761 } 1762 else if ( nMatchType & IMPL_FONT_ATTR_CJK ) 1763 nTestMatch -= 10000000; 1764 1765 // test CTL script attributes 1766 if( nSearchType & IMPL_FONT_ATTR_CTL ) 1767 { 1768 if( nMatchType & IMPL_FONT_ATTR_CTL ) 1769 nTestMatch += 10000000*2; 1770 if( nMatchType & IMPL_FONT_ATTR_FULL ) 1771 nTestMatch += 10000000; 1772 } 1773 else if ( nMatchType & IMPL_FONT_ATTR_CTL ) 1774 nTestMatch -= 10000000; 1775 1776 // test LATIN script attributes 1777 if( nSearchType & IMPL_FONT_ATTR_NONELATIN ) 1778 { 1779 if( nMatchType & IMPL_FONT_ATTR_NONELATIN ) 1780 nTestMatch += 10000000*2; 1781 if( nMatchType & IMPL_FONT_ATTR_FULL ) 1782 nTestMatch += 10000000; 1783 } 1784 1785 // test SYMBOL attributes 1786 if ( nSearchType & IMPL_FONT_ATTR_SYMBOL ) 1787 { 1788 const String& rSearchName = it->first; 1789 // prefer some special known symbol fonts 1790 if ( rSearchName.EqualsAscii( "starsymbol" ) ) 1791 nTestMatch += 10000000*6+(10000*3); 1792 else if ( rSearchName.EqualsAscii( "opensymbol" ) ) 1793 nTestMatch += 10000000*6; 1794 else if ( rSearchName.EqualsAscii( "starbats" ) 1795 || rSearchName.EqualsAscii( "wingdings" ) 1796 || rSearchName.EqualsAscii( "monotypesorts" ) 1797 || rSearchName.EqualsAscii( "dingbats" ) 1798 || rSearchName.EqualsAscii( "zapfdingbats" ) ) 1799 nTestMatch += 10000000*5; 1800 else if ( pData->mnTypeFaces & IMPL_DEVFONT_SYMBOL ) 1801 nTestMatch += 10000000*4; 1802 else 1803 { 1804 if( nMatchType & IMPL_FONT_ATTR_SYMBOL ) 1805 nTestMatch += 10000000*2; 1806 if( nMatchType & IMPL_FONT_ATTR_FULL ) 1807 nTestMatch += 10000000; 1808 } 1809 } 1810 else if ( (pData->mnTypeFaces & (IMPL_DEVFONT_SYMBOL | IMPL_DEVFONT_NONESYMBOL)) == IMPL_DEVFONT_SYMBOL ) 1811 nTestMatch -= 10000000; 1812 else if ( nMatchType & IMPL_FONT_ATTR_SYMBOL ) 1813 nTestMatch -= 10000; 1814 1815 // match stripped family name 1816 if( rSearchFamilyName.Len() && (rSearchFamilyName == pData->maMatchFamilyName) ) 1817 nTestMatch += 1000000*3; 1818 1819 // match ALLSCRIPT? attribute 1820 if( nSearchType & IMPL_FONT_ATTR_ALLSCRIPT ) 1821 { 1822 if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT ) 1823 nTestMatch += 1000000*2; 1824 if( nSearchType & IMPL_FONT_ATTR_ALLSUBSCRIPT ) 1825 { 1826 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ALLSUBSCRIPT) ) 1827 nTestMatch += 1000000*2; 1828 if( 0 != ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_BRUSHSCRIPT) ) 1829 nTestMatch -= 1000000; 1830 } 1831 } 1832 else if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT ) 1833 nTestMatch -= 1000000; 1834 1835 // test MONOSPACE+TYPEWRITER attributes 1836 if( nSearchType & IMPL_FONT_ATTR_FIXED ) 1837 { 1838 if( nMatchType & IMPL_FONT_ATTR_FIXED ) 1839 nTestMatch += 1000000*2; 1840 // a typewriter attribute is even better 1841 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) ) 1842 nTestMatch += 10000*2; 1843 } 1844 else if( nMatchType & IMPL_FONT_ATTR_FIXED ) 1845 nTestMatch -= 1000000; 1846 1847 // test SPECIAL attribute 1848 if( nSearchType & IMPL_FONT_ATTR_SPECIAL ) 1849 { 1850 if( nMatchType & IMPL_FONT_ATTR_SPECIAL ) 1851 nTestMatch += 10000; 1852 else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) ) 1853 { 1854 if( nMatchType & IMPL_FONT_ATTR_SERIF ) 1855 nTestMatch += 1000*2; 1856 else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) 1857 nTestMatch += 1000; 1858 } 1859 } 1860 else if( (nMatchType & IMPL_FONT_ATTR_SPECIAL) && !(nSearchType & IMPL_FONT_ATTR_SYMBOL) ) 1861 nTestMatch -= 1000000; 1862 1863 // test DECORATIVE attribute 1864 if( nSearchType & IMPL_FONT_ATTR_DECORATIVE ) 1865 { 1866 if( nMatchType & IMPL_FONT_ATTR_DECORATIVE ) 1867 nTestMatch += 10000; 1868 else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) ) 1869 { 1870 if( nMatchType & IMPL_FONT_ATTR_SERIF ) 1871 nTestMatch += 1000*2; 1872 else if ( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) 1873 nTestMatch += 1000; 1874 } 1875 } 1876 else if( nMatchType & IMPL_FONT_ATTR_DECORATIVE ) 1877 nTestMatch -= 1000000; 1878 1879 // test TITLE+CAPITALS attributes 1880 if( nSearchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) 1881 { 1882 if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) 1883 nTestMatch += 1000000*2; 1884 if( 0 == ((nSearchType^nMatchType) & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS))) 1885 nTestMatch += 1000000; 1886 else if( (nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS)) 1887 && (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) ) 1888 nTestMatch += 1000000; 1889 } 1890 else if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) ) 1891 nTestMatch -= 1000000; 1892 1893 // test OUTLINE+SHADOW attributes 1894 if( nSearchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) 1895 { 1896 if( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) 1897 nTestMatch += 1000000*2; 1898 if( 0 == ((nSearchType ^ nMatchType) & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW)) ) 1899 nTestMatch += 1000000; 1900 else if( (nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW)) 1901 && (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) ) 1902 nTestMatch += 1000000; 1903 } 1904 else if ( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) ) 1905 nTestMatch -= 1000000; 1906 1907 // test font name substrings 1908 // TODO: calculate name matching score using e.g. Levenstein distance 1909 if( (rSearchFamilyName.Len() >= 4) && (pData->maMatchFamilyName.Len() >= 4) 1910 && ((rSearchFamilyName.Search( pData->maMatchFamilyName ) != STRING_NOTFOUND) 1911 || (pData->maMatchFamilyName.Search( rSearchFamilyName ) != STRING_NOTFOUND)) ) 1912 nTestMatch += 5000; 1913 1914 // test SERIF attribute 1915 if( nSearchType & IMPL_FONT_ATTR_SERIF ) 1916 { 1917 if( nMatchType & IMPL_FONT_ATTR_SERIF ) 1918 nTestMatch += 1000000*2; 1919 else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) 1920 nTestMatch -= 1000000; 1921 } 1922 1923 // test SANSERIF attribute 1924 if( nSearchType & IMPL_FONT_ATTR_SANSSERIF ) 1925 { 1926 if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) 1927 nTestMatch += 1000000; 1928 else if ( nMatchType & IMPL_FONT_ATTR_SERIF ) 1929 nTestMatch -= 1000000; 1930 } 1931 1932 // test ITALIC attribute 1933 if( nSearchType & IMPL_FONT_ATTR_ITALIC ) 1934 { 1935 if( pData->mnTypeFaces & IMPL_DEVFONT_ITALIC ) 1936 nTestMatch += 1000000*3; 1937 if( nMatchType & IMPL_FONT_ATTR_ITALIC ) 1938 nTestMatch += 1000000; 1939 } 1940 else if( !(nSearchType & IMPL_FONT_ATTR_ALLSCRIPT) 1941 && ((nMatchType & IMPL_FONT_ATTR_ITALIC) 1942 || !(pData->mnTypeFaces & IMPL_DEVFONT_NONEITALIC)) ) 1943 nTestMatch -= 1000000*2; 1944 1945 // test WIDTH attribute 1946 if( (eSearchWidth != WIDTH_DONTKNOW) && (eSearchWidth != WIDTH_NORMAL) ) 1947 { 1948 if( eSearchWidth < WIDTH_NORMAL ) 1949 { 1950 if( eSearchWidth == eMatchWidth ) 1951 nTestMatch += 1000000*3; 1952 else if( (eMatchWidth < WIDTH_NORMAL) && (eMatchWidth != WIDTH_DONTKNOW) ) 1953 nTestMatch += 1000000; 1954 } 1955 else 1956 { 1957 if( eSearchWidth == eMatchWidth ) 1958 nTestMatch += 1000000*3; 1959 else if( eMatchWidth > WIDTH_NORMAL ) 1960 nTestMatch += 1000000; 1961 } 1962 } 1963 else if( (eMatchWidth != WIDTH_DONTKNOW) && (eMatchWidth != WIDTH_NORMAL) ) 1964 nTestMatch -= 1000000; 1965 1966 // test WEIGHT attribute 1967 if( (eSearchWeight != WEIGHT_DONTKNOW) && (eSearchWeight != WEIGHT_NORMAL) && (eSearchWeight != WEIGHT_MEDIUM) ) 1968 { 1969 if( eSearchWeight < WEIGHT_NORMAL ) 1970 { 1971 if( pData->mnTypeFaces & IMPL_DEVFONT_LIGHT ) 1972 nTestMatch += 1000000; 1973 if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) ) 1974 nTestMatch += 1000000; 1975 } 1976 else 1977 { 1978 if( pData->mnTypeFaces & IMPL_DEVFONT_BOLD ) 1979 nTestMatch += 1000000; 1980 if( eMatchWeight > WEIGHT_BOLD ) 1981 nTestMatch += 1000000; 1982 } 1983 } 1984 else if( ((eMatchWeight != WEIGHT_DONTKNOW) && (eMatchWeight != WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_MEDIUM)) 1985 || !(pData->mnTypeFaces & IMPL_DEVFONT_NORMAL) ) 1986 nTestMatch -= 1000000; 1987 1988 // prefer scalable fonts 1989 if( pData->mnTypeFaces & IMPL_DEVFONT_SCALABLE ) 1990 nTestMatch += 10000*4; 1991 else 1992 nTestMatch -= 10000*4; 1993 1994 // test STANDARD+DEFAULT+FULL+NORMAL attributes 1995 if( nMatchType & IMPL_FONT_ATTR_STANDARD ) 1996 nTestMatch += 10000*2; 1997 if( nMatchType & IMPL_FONT_ATTR_DEFAULT ) 1998 nTestMatch += 10000; 1999 if( nMatchType & IMPL_FONT_ATTR_FULL ) 2000 nTestMatch += 10000; 2001 if( nMatchType & IMPL_FONT_ATTR_NORMAL ) 2002 nTestMatch += 10000; 2003 2004 // test OTHERSTYLE attribute 2005 if( nMatchType & IMPL_FONT_ATTR_OTHERSTYLE ) 2006 { 2007 if( !(nMatchType & IMPL_FONT_ATTR_OTHERSTYLE) ) 2008 nTestMatch -= 10000; 2009 } 2010 else if( nMatchType & IMPL_FONT_ATTR_OTHERSTYLE ) 2011 nTestMatch -= 10000; 2012 2013 // test ROUNDED attribute 2014 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ROUNDED) ) 2015 nTestMatch += 1000; 2016 2017 // test TYPEWRITER attribute 2018 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) ) 2019 nTestMatch += 1000; 2020 2021 // test GOTHIC attribute 2022 if( nSearchType & IMPL_FONT_ATTR_GOTHIC ) 2023 { 2024 if( nMatchType & IMPL_FONT_ATTR_GOTHIC ) 2025 nTestMatch += 1000*3; 2026 if( nMatchType & IMPL_FONT_ATTR_SANSSERIF ) 2027 nTestMatch += 1000*2; 2028 } 2029 2030 // test SCHOOLBOOK attribute 2031 if( nSearchType & IMPL_FONT_ATTR_SCHOOLBOOK ) 2032 { 2033 if( nMatchType & IMPL_FONT_ATTR_SCHOOLBOOK ) 2034 nTestMatch += 1000*3; 2035 if( nMatchType & IMPL_FONT_ATTR_SERIF ) 2036 nTestMatch += 1000*2; 2037 } 2038 2039 // compare with best matching font yet 2040 if ( nTestMatch > nBestMatch ) 2041 { 2042 pFoundData = pData; 2043 nBestMatch = nTestMatch; 2044 nBestType = nMatchType; 2045 } 2046 else if( nTestMatch == nBestMatch ) 2047 { 2048 // some fonts are more suitable defaults 2049 if( nMatchType & IMPL_FONT_ATTR_DEFAULT ) 2050 { 2051 pFoundData = pData; 2052 nBestType = nMatchType; 2053 } 2054 else if( (nMatchType & IMPL_FONT_ATTR_STANDARD) && 2055 !(nBestType & IMPL_FONT_ATTR_DEFAULT) ) 2056 { 2057 pFoundData = pData; 2058 nBestType = nMatchType; 2059 } 2060 } 2061 } 2062 2063 return pFoundData; 2064 } 2065 2066 // ----------------------------------------------------------------------- 2067 2068 ImplDevFontListData* ImplDevFontList::FindDefaultFont() const 2069 { 2070 // try to find one of the default fonts of the 2071 // UNICODE, SANSSERIF, SERIF or FIXED default font lists 2072 const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get(); 2073 com::sun::star::lang::Locale aLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() ); 2074 String aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS_UNICODE ); 2075 ImplDevFontListData* pFoundData = ImplFindByTokenNames( aFontname ); 2076 if( pFoundData ) 2077 return pFoundData; 2078 2079 aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS ); 2080 pFoundData = ImplFindByTokenNames( aFontname ); 2081 if( pFoundData ) 2082 return pFoundData; 2083 2084 aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SERIF ); 2085 pFoundData = ImplFindByTokenNames( aFontname ); 2086 if( pFoundData ) 2087 return pFoundData; 2088 2089 aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_FIXED ); 2090 pFoundData = ImplFindByTokenNames( aFontname ); 2091 if( pFoundData ) 2092 return pFoundData; 2093 2094 // now try to find a reasonable non-symbol font 2095 2096 InitMatchData(); 2097 2098 DevFontList::const_iterator it = maDevFontList.begin(); 2099 for(; it != maDevFontList.end(); ++it ) 2100 { 2101 ImplDevFontListData* pData = (*it).second; 2102 if( pData->mnMatchType & IMPL_FONT_ATTR_SYMBOL ) 2103 continue; 2104 pFoundData = pData; 2105 if( pData->mnMatchType & (IMPL_FONT_ATTR_DEFAULT|IMPL_FONT_ATTR_STANDARD) ) 2106 break; 2107 } 2108 if( pFoundData ) 2109 return pFoundData; 2110 2111 // finding any font is better than finding no font at all 2112 it = maDevFontList.begin(); 2113 if( it != maDevFontList.end() ) 2114 pFoundData = (*it).second; 2115 2116 return pFoundData; 2117 } 2118 2119 // ----------------------------------------------------------------------- 2120 2121 ImplDevFontList* ImplDevFontList::Clone( bool bScalable, bool bEmbeddable ) const 2122 { 2123 ImplDevFontList* pClonedList = new ImplDevFontList; 2124 // pClonedList->mbMatchData = mbMatchData; 2125 pClonedList->mbMapNames = mbMapNames; 2126 pClonedList->mpPreMatchHook = mpPreMatchHook; 2127 pClonedList->mpFallbackHook = mpFallbackHook; 2128 2129 // TODO: clone the config-font attributes too? 2130 pClonedList->mbMatchData = false; 2131 2132 DevFontList::const_iterator it = maDevFontList.begin(); 2133 for(; it != maDevFontList.end(); ++it ) 2134 { 2135 const ImplDevFontListData* pFontFace = (*it).second; 2136 pFontFace->UpdateCloneFontList( *pClonedList, bScalable, bEmbeddable ); 2137 } 2138 2139 return pClonedList; 2140 } 2141 2142 // ----------------------------------------------------------------------- 2143 2144 ImplGetDevFontList* ImplDevFontList::GetDevFontList() const 2145 { 2146 ImplGetDevFontList* pGetDevFontList = new ImplGetDevFontList; 2147 2148 DevFontList::const_iterator it = maDevFontList.begin(); 2149 for(; it != maDevFontList.end(); ++it ) 2150 { 2151 const ImplDevFontListData* pFontFamily = (*it).second; 2152 pFontFamily->UpdateDevFontList( *pGetDevFontList ); 2153 } 2154 2155 return pGetDevFontList; 2156 } 2157 2158 // ----------------------------------------------------------------------- 2159 2160 ImplGetDevSizeList* ImplDevFontList::GetDevSizeList( const String& rFontName ) const 2161 { 2162 ImplGetDevSizeList* pGetDevSizeList = new ImplGetDevSizeList( rFontName ); 2163 2164 ImplDevFontListData* pFontFamily = FindFontFamily( rFontName ); 2165 if( pFontFamily != NULL ) 2166 { 2167 std::set<int> rHeights; 2168 pFontFamily->GetFontHeights( rHeights ); 2169 2170 std::set<int>::const_iterator it = rHeights.begin(); 2171 for(; it != rHeights.begin(); ++it ) 2172 pGetDevSizeList->Add( *it ); 2173 } 2174 2175 return pGetDevSizeList; 2176 } 2177 2178 // ======================================================================= 2179 2180 ImplFontSelectData::ImplFontSelectData( const Font& rFont, 2181 const String& rSearchName, const Size& rSize, float fExactHeight) 2182 : maSearchName( rSearchName ), 2183 mnWidth( rSize.Width() ), 2184 mnHeight( rSize.Height() ), 2185 mfExactHeight( fExactHeight), 2186 mnOrientation( rFont.GetOrientation() ), 2187 meLanguage( rFont.GetLanguage() ), 2188 mbVertical( rFont.IsVertical() ), 2189 mbNonAntialiased( false ), 2190 mpFontData( NULL ), 2191 mpFontEntry( NULL ) 2192 { 2193 maTargetName = maName; 2194 2195 rFont.GetFontAttributes( *this ); 2196 2197 // normalize orientation between 0 and 3600 2198 if( 3600 <= (unsigned)mnOrientation ) 2199 { 2200 if( mnOrientation >= 0 ) 2201 mnOrientation %= 3600; 2202 else 2203 mnOrientation = 3600 - (-mnOrientation % 3600); 2204 } 2205 2206 // normalize width and height 2207 if( mnHeight < 0 ) 2208 mnHeight = -mnHeight; 2209 if( mnWidth < 0 ) 2210 mnWidth = -mnWidth; 2211 } 2212 2213 // ----------------------------------------------------------------------- 2214 2215 ImplFontSelectData::ImplFontSelectData( const ImplFontData& rFontData, 2216 const Size& rSize, float fExactHeight, int nOrientation, bool bVertical ) 2217 : ImplFontAttributes( rFontData ), 2218 mnWidth( rSize.Width() ), 2219 mnHeight( rSize.Height() ), 2220 mfExactHeight( fExactHeight ), 2221 mnOrientation( nOrientation ), 2222 meLanguage( 0 ), 2223 mbVertical( bVertical ), 2224 mbNonAntialiased( false ), 2225 mpFontData( &rFontData ), 2226 mpFontEntry( NULL ) 2227 { 2228 maTargetName = maSearchName = maName; 2229 // NOTE: no normalization for width/height/orientation 2230 } 2231 2232 // ======================================================================= 2233 2234 size_t ImplFontCache::IFSD_Hash::operator()( const ImplFontSelectData& rFSD ) const 2235 { 2236 // TODO: does it pay off to improve this hash function? 2237 static FontNameHash aFontNameHash; 2238 size_t nHash = aFontNameHash( rFSD.maSearchName ); 2239 #ifdef ENABLE_GRAPHITE 2240 // check for features and generate a unique hash if necessary 2241 if (rFSD.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) 2242 != STRING_NOTFOUND) 2243 { 2244 nHash = aFontNameHash( rFSD.maTargetName ); 2245 } 2246 #endif 2247 nHash += 11 * rFSD.mnHeight; 2248 nHash += 19 * rFSD.meWeight; 2249 nHash += 29 * rFSD.meItalic; 2250 nHash += 37 * rFSD.mnOrientation; 2251 nHash += 41 * rFSD.meLanguage; 2252 if( rFSD.mbVertical ) 2253 nHash += 53; 2254 return nHash; 2255 } 2256 2257 // ----------------------------------------------------------------------- 2258 2259 bool ImplFontCache::IFSD_Equal::operator()(const ImplFontSelectData& rA, const ImplFontSelectData& rB) const 2260 { 2261 // check normalized font family name 2262 if( rA.maSearchName != rB.maSearchName ) 2263 return false; 2264 2265 // check font transformation 2266 if( (rA.mnHeight != rB.mnHeight) 2267 || (rA.mnWidth != rB.mnWidth) 2268 || (rA.mnOrientation != rB.mnOrientation) ) 2269 return false; 2270 2271 // check mapping relevant attributes 2272 if( (rA.mbVertical != rB.mbVertical) 2273 || (rA.meLanguage != rB.meLanguage) ) 2274 return false; 2275 2276 // check font face attributes 2277 if( (rA.meWeight != rB.meWeight) 2278 || (rA.meItalic != rB.meItalic) 2279 // || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member 2280 || (rA.mePitch != rB.mePitch) ) 2281 return false; 2282 2283 // check style name 2284 if( rA.maStyleName != rB.maStyleName) 2285 return false; 2286 2287 // Symbol fonts may recode from one type to another So they are only 2288 // safely equivalent for equal targets 2289 if ( 2290 (rA.mpFontData && rA.mpFontData->IsSymbolFont()) || 2291 (rB.mpFontData && rB.mpFontData->IsSymbolFont()) 2292 ) 2293 { 2294 if (rA.maTargetName != rB.maTargetName) 2295 return false; 2296 } 2297 2298 #ifdef ENABLE_GRAPHITE 2299 // check for features 2300 if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) 2301 != STRING_NOTFOUND || 2302 rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) 2303 != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName) 2304 return false; 2305 #endif 2306 2307 return true; 2308 } 2309 2310 // ----------------------------------------------------------------------- 2311 2312 ImplFontCache::ImplFontCache( bool bPrinter ) 2313 : mpFirstEntry( NULL ), 2314 mnRef0Count( 0 ), 2315 mbPrinter( bPrinter ) 2316 {} 2317 2318 // ----------------------------------------------------------------------- 2319 2320 ImplFontCache::~ImplFontCache() 2321 { 2322 FontInstanceList::iterator it = maFontInstanceList.begin(); 2323 for(; it != maFontInstanceList.end(); ++it ) 2324 { 2325 ImplFontEntry* pEntry = (*it).second; 2326 delete pEntry; 2327 } 2328 } 2329 2330 // ----------------------------------------------------------------------- 2331 2332 ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList, 2333 const Font& rFont, const Size& rSize, float fExactHeight, ImplDirectFontSubstitution* pDevSpecific ) 2334 { 2335 String aSearchName = rFont.GetName(); 2336 2337 // TODO: also add device specific name caching 2338 if( !pDevSpecific ) 2339 { 2340 // check if the requested font name is already known 2341 // if it is already known get its normalized search name 2342 FontNameList::const_iterator it_name = maFontNameList.find( aSearchName ); 2343 if( it_name != maFontNameList.end() ) 2344 if( !(*it_name).second.EqualsAscii( "hg", 0, 2) 2345 #ifdef ENABLE_GRAPHITE 2346 && (aSearchName.Search(grutils::GrFeatureParser::FEAT_PREFIX) 2347 == STRING_NOTFOUND) 2348 #endif 2349 ) 2350 aSearchName = (*it_name).second; 2351 } 2352 2353 // initialize internal font request object 2354 ImplFontSelectData aFontSelData( rFont, aSearchName, rSize, fExactHeight ); 2355 return GetFontEntry( pFontList, aFontSelData, pDevSpecific ); 2356 } 2357 2358 // ----------------------------------------------------------------------- 2359 2360 ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList, 2361 ImplFontSelectData& aFontSelData, ImplDirectFontSubstitution* pDevSpecific ) 2362 { 2363 // check if a directly matching logical font instance is already cached, 2364 // the most recently used font usually has a hit rate of >50% 2365 ImplFontEntry *pEntry = NULL; 2366 ImplDevFontListData* pFontFamily = NULL; 2367 IFSD_Equal aIFSD_Equal; 2368 if( mpFirstEntry && aIFSD_Equal( aFontSelData, mpFirstEntry->maFontSelData ) ) 2369 pEntry = mpFirstEntry; 2370 else 2371 { 2372 FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData ); 2373 if( it != maFontInstanceList.end() ) 2374 pEntry = (*it).second; 2375 } 2376 2377 if( !pEntry ) // no direct cache hit 2378 { 2379 // find the best matching logical font family and update font selector accordingly 2380 pFontFamily = pFontList->ImplFindByFont( aFontSelData, mbPrinter, pDevSpecific ); 2381 DBG_ASSERT( (pFontFamily != NULL), "ImplFontCache::Get() No logical font found!" ); 2382 if( pFontFamily ) 2383 aFontSelData.maSearchName = pFontFamily->GetSearchName(); 2384 2385 // check if an indirectly matching logical font instance is already cached 2386 FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData ); 2387 if( it != maFontInstanceList.end() ) 2388 { 2389 // we have an indirect cache hit 2390 pEntry = (*it).second; 2391 // cache the requested and the selected font names 2392 // => next time there is a good chance for a direct cache hit 2393 // don't allow the cache to grow too big 2394 // TODO: implement some fancy LRU caching? 2395 if( maFontNameList.size() >= 4000 ) 2396 maFontNameList.clear(); 2397 // TODO: also add device specific name caching 2398 if( !pDevSpecific ) 2399 if( aFontSelData.maName != aFontSelData.maSearchName ) 2400 maFontNameList[ aFontSelData.maName ] = aFontSelData.maSearchName; 2401 } 2402 } 2403 2404 if( pEntry ) // cache hit => use existing font instance 2405 { 2406 // increase the font instance's reference count 2407 if( !pEntry->mnRefCount++ ) 2408 --mnRef0Count; 2409 } 2410 else // no cache hit => create a new font instance 2411 { 2412 // find the best matching physical font face 2413 ImplFontData* pFontData = pFontFamily->FindBestFontFace( aFontSelData ); 2414 aFontSelData.mpFontData = pFontData; 2415 2416 // create a new logical font instance from this physical font face 2417 pEntry = pFontData->CreateFontInstance( aFontSelData ); 2418 2419 // if we found a different symbol font we need a symbol conversion table 2420 if( pFontData->IsSymbolFont() ) 2421 if( aFontSelData.maTargetName != aFontSelData.maSearchName ) 2422 pEntry->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName ); 2423 2424 // add the new entry to the cache 2425 maFontInstanceList[ aFontSelData ] = pEntry; 2426 } 2427 2428 mpFirstEntry = pEntry; 2429 return pEntry; 2430 } 2431 2432 // ----------------------------------------------------------------------- 2433 2434 ImplDevFontListData* ImplDevFontList::ImplFindByFont( ImplFontSelectData& rFSD, 2435 bool bPrinter, ImplDirectFontSubstitution* pDevSpecific ) const 2436 { 2437 // give up if no fonts are available 2438 if( !Count() ) 2439 return NULL; 2440 2441 // test if a font in the token list is available 2442 // substitute the font if this was requested 2443 sal_uInt16 nSubstFlags = FONT_SUBSTITUTE_ALWAYS; 2444 if ( bPrinter ) 2445 nSubstFlags |= FONT_SUBSTITUTE_SCREENONLY; 2446 2447 bool bMultiToken = false; 2448 xub_StrLen nTokenPos = 0; 2449 String& aSearchName = rFSD.maSearchName; // TODO: get rid of reference 2450 for(;;) 2451 { 2452 rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos ); 2453 aSearchName = rFSD.maTargetName; 2454 2455 #ifdef ENABLE_GRAPHITE 2456 // Until features are properly supported, they are appended to the 2457 // font name, so we need to strip them off so the font is found. 2458 xub_StrLen nFeat = aSearchName.Search(grutils::GrFeatureParser::FEAT_PREFIX); 2459 String aOrigName = rFSD.maTargetName; 2460 String aBaseFontName(aSearchName, 0, (nFeat != STRING_NOTFOUND)?nFeat:aSearchName.Len()); 2461 if (nFeat != STRING_NOTFOUND && STRING_NOTFOUND != 2462 aSearchName.Search(grutils::GrFeatureParser::FEAT_ID_VALUE_SEPARATOR, nFeat)) 2463 { 2464 aSearchName = aBaseFontName; 2465 rFSD.maTargetName = aBaseFontName; 2466 } 2467 2468 #endif 2469 2470 GetEnglishSearchFontName( aSearchName ); 2471 ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific ); 2472 // #114999# special emboldening for Ricoh fonts 2473 // TODO: smarter check for special cases by using PreMatch infrastructure? 2474 if( (rFSD.meWeight > WEIGHT_MEDIUM) 2475 && aSearchName.EqualsAscii( "hg", 0, 2) ) 2476 { 2477 String aBoldName; 2478 if( aSearchName.EqualsAscii( "hggothicb", 0, 9) ) 2479 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hggothice")); 2480 else if( aSearchName.EqualsAscii( "hgpgothicb", 0, 10) ) 2481 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpgothice")); 2482 else if( aSearchName.EqualsAscii( "hgminchol", 0, 9) ) 2483 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchob")); 2484 else if( aSearchName.EqualsAscii( "hgpminchol", 0, 10) ) 2485 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchob")); 2486 else if( aSearchName.EqualsAscii( "hgminchob" ) ) 2487 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchoe")); 2488 else if( aSearchName.EqualsAscii( "hgpminchob" ) ) 2489 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchoe")); 2490 2491 if( aBoldName.Len() && ImplFindBySearchName( aBoldName ) ) 2492 { 2493 // the other font is available => use it 2494 aSearchName = aBoldName; 2495 // prevent synthetic emboldening of bold version 2496 rFSD.meWeight = WEIGHT_DONTKNOW; 2497 } 2498 } 2499 2500 #ifdef ENABLE_GRAPHITE 2501 // restore the features to make the font selection data unique 2502 rFSD.maTargetName = aOrigName; 2503 #endif 2504 // check if the current font name token or its substitute is valid 2505 ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName ); 2506 if( pFoundData ) 2507 return pFoundData; 2508 2509 // some systems provide special customization 2510 // e.g. they suggest "serif" as UI-font, but this name cannot be used directly 2511 // because the system wants to map it to another font first, e.g. "Helvetica" 2512 #ifdef ENABLE_GRAPHITE 2513 // use the target name to search in the prematch hook 2514 rFSD.maTargetName = aBaseFontName; 2515 #endif 2516 if( mpPreMatchHook ) 2517 if( mpPreMatchHook->FindFontSubstitute( rFSD ) ) 2518 GetEnglishSearchFontName( aSearchName ); 2519 #ifdef ENABLE_GRAPHITE 2520 // the prematch hook uses the target name to search, but we now need 2521 // to restore the features to make the font selection data unique 2522 rFSD.maTargetName = aOrigName; 2523 #endif 2524 pFoundData = ImplFindBySearchName( aSearchName ); 2525 if( pFoundData ) 2526 return pFoundData; 2527 2528 // break after last font name token was checked unsuccessfully 2529 if( nTokenPos == STRING_NOTFOUND) 2530 break; 2531 bMultiToken = true; 2532 } 2533 2534 // if the first font was not available find the next available font in 2535 // the semicolon separated list of font names. A font is also considered 2536 // available when there is a matching entry in the Tools->Options->Fonts 2537 // dialog witho neither ALWAYS nor SCREENONLY flags set and the substitution 2538 // font is available 2539 for( nTokenPos = 0; nTokenPos != STRING_NOTFOUND; ) 2540 { 2541 if( bMultiToken ) 2542 { 2543 rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos ); 2544 aSearchName = rFSD.maTargetName; 2545 GetEnglishSearchFontName( aSearchName ); 2546 } 2547 else 2548 nTokenPos = STRING_NOTFOUND; 2549 if( mpPreMatchHook ) 2550 if( mpPreMatchHook->FindFontSubstitute( rFSD ) ) 2551 GetEnglishSearchFontName( aSearchName ); 2552 ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific ); 2553 ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName ); 2554 if( pFoundData ) 2555 return pFoundData; 2556 } 2557 2558 // if no font with a directly matching name is available use the 2559 // first font name token and get its attributes to find a replacement 2560 if ( bMultiToken ) 2561 { 2562 nTokenPos = 0; 2563 rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos ); 2564 aSearchName = rFSD.maTargetName; 2565 GetEnglishSearchFontName( aSearchName ); 2566 } 2567 2568 String aSearchShortName; 2569 String aSearchFamilyName; 2570 FontWeight eSearchWeight = rFSD.meWeight; 2571 FontWidth eSearchWidth = rFSD.meWidthType; 2572 sal_uLong nSearchType = 0; 2573 FontSubstConfiguration::getMapName( aSearchName, aSearchShortName, aSearchFamilyName, 2574 eSearchWeight, eSearchWidth, nSearchType ); 2575 2576 // note: the search name was already translated to english (if possible) 2577 2578 // use the font's shortened name if needed 2579 if ( aSearchShortName != aSearchName ) 2580 { 2581 ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchShortName ); 2582 if( pFoundData ) 2583 { 2584 #ifdef UNX 2585 /* #96738# don't use mincho as an replacement for "MS Mincho" on X11: Mincho is 2586 a korean bitmap font that is not suitable here. Use the font replacement table, 2587 that automatically leads to the desired "HG Mincho Light J". Same story for 2588 MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */ 2589 static String aMS_Mincho( RTL_CONSTASCII_USTRINGPARAM("msmincho") ); 2590 static String aMS_Gothic( RTL_CONSTASCII_USTRINGPARAM("msgothic") ); 2591 if ((aSearchName != aMS_Mincho) && (aSearchName != aMS_Gothic)) 2592 // TODO: add heuristic to only throw out the fake ms* fonts 2593 #endif 2594 { 2595 return pFoundData; 2596 } 2597 } 2598 } 2599 2600 // use font fallback 2601 const FontNameAttr* pFontAttr = NULL; 2602 if( aSearchName.Len() ) 2603 { 2604 // get fallback info using FontSubstConfiguration and 2605 // the target name, it's shortened name and family name in that order 2606 const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); 2607 pFontAttr = rFontSubst.getSubstInfo( aSearchName ); 2608 if ( !pFontAttr && (aSearchShortName != aSearchName) ) 2609 pFontAttr = rFontSubst.getSubstInfo( aSearchShortName ); 2610 if ( !pFontAttr && (aSearchFamilyName != aSearchShortName) ) 2611 pFontAttr = rFontSubst.getSubstInfo( aSearchFamilyName ); 2612 2613 // try the font substitutions suggested by the fallback info 2614 if( pFontAttr ) 2615 { 2616 ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pFontAttr ); 2617 if( pFoundData ) 2618 return pFoundData; 2619 } 2620 } 2621 2622 // if a target symbol font is not available use a default symbol font 2623 if( rFSD.IsSymbolFont() ) 2624 { 2625 com::sun::star::lang::Locale aDefaultLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() ); 2626 aSearchName = DefaultFontConfiguration::get()->getDefaultFont( aDefaultLocale, DEFAULTFONT_SYMBOL ); 2627 ImplDevFontListData* pFoundData = ImplFindByTokenNames( aSearchName ); 2628 if( pFoundData ) 2629 return pFoundData; 2630 } 2631 2632 // now try the other font name tokens 2633 while( nTokenPos != STRING_NOTFOUND ) 2634 { 2635 rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos ); 2636 if( !rFSD.maTargetName.Len() ) 2637 continue; 2638 2639 aSearchName = rFSD.maTargetName; 2640 GetEnglishSearchFontName( aSearchName ); 2641 2642 String aTempShortName; 2643 String aTempFamilyName; 2644 sal_uLong nTempType = 0; 2645 FontWeight eTempWeight = rFSD.meWeight; 2646 FontWidth eTempWidth = WIDTH_DONTKNOW; 2647 FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName, 2648 eTempWeight, eTempWidth, nTempType ); 2649 2650 // use a shortend token name if available 2651 if( aTempShortName != aSearchName ) 2652 { 2653 ImplDevFontListData* pFoundData = ImplFindBySearchName( aTempShortName ); 2654 if( pFoundData ) 2655 return pFoundData; 2656 } 2657 2658 // use a font name from font fallback list to determine font attributes 2659 2660 // get fallback info using FontSubstConfiguration and 2661 // the target name, it's shortened name and family name in that order 2662 const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get(); 2663 const FontNameAttr* pTempFontAttr = rFontSubst.getSubstInfo( aSearchName ); 2664 if ( !pTempFontAttr && (aTempShortName != aSearchName) ) 2665 pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName ); 2666 if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) ) 2667 pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName ); 2668 2669 // try the font substitutions suggested by the fallback info 2670 if( pTempFontAttr ) 2671 { 2672 ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pTempFontAttr ); 2673 if( pFoundData ) 2674 return pFoundData; 2675 if( !pFontAttr ) 2676 pFontAttr = pTempFontAttr; 2677 } 2678 } 2679 2680 // if still needed use the alias names of the installed fonts 2681 if( mbMapNames ) 2682 { 2683 ImplDevFontListData* pFoundData = ImplFindByAliasName( rFSD.maTargetName, aSearchShortName ); 2684 if( pFoundData ) 2685 return pFoundData; 2686 } 2687 2688 // if still needed use the font request's attributes to find a good match 2689 switch( rFSD.meLanguage ) 2690 { 2691 case LANGUAGE_CHINESE: 2692 case LANGUAGE_CHINESE_SIMPLIFIED: 2693 case LANGUAGE_CHINESE_SINGAPORE: 2694 nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_SC; 2695 break; 2696 case LANGUAGE_CHINESE_TRADITIONAL: 2697 case LANGUAGE_CHINESE_HONGKONG: 2698 case LANGUAGE_CHINESE_MACAU: 2699 nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_TC; 2700 break; 2701 case LANGUAGE_KOREAN: 2702 case LANGUAGE_KOREAN_JOHAB: 2703 nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_KR; 2704 break; 2705 case LANGUAGE_JAPANESE: 2706 nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_JP; 2707 break; 2708 default: 2709 nSearchType |= ImplIsCJKFont( rFSD.maName ); 2710 if( rFSD.IsSymbolFont() ) 2711 nSearchType |= IMPL_FONT_ATTR_SYMBOL; 2712 break; 2713 } 2714 2715 ImplCalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.meFamily, pFontAttr ); 2716 ImplDevFontListData* pFoundData = ImplFindByAttributes( nSearchType, 2717 eSearchWeight, eSearchWidth, rFSD.meFamily, rFSD.meItalic, aSearchFamilyName ); 2718 2719 if( pFoundData ) 2720 { 2721 // overwrite font selection attributes using info from the typeface flags 2722 if( (eSearchWeight >= WEIGHT_BOLD) 2723 && (eSearchWeight > rFSD.meWeight) 2724 && (pFoundData->mnTypeFaces & IMPL_DEVFONT_BOLD) ) 2725 rFSD.meWeight = eSearchWeight; 2726 else if( (eSearchWeight < WEIGHT_NORMAL) 2727 && (eSearchWeight < rFSD.meWeight) 2728 && (eSearchWeight != WEIGHT_DONTKNOW) 2729 && (pFoundData->mnTypeFaces & IMPL_DEVFONT_LIGHT) ) 2730 rFSD.meWeight = eSearchWeight; 2731 2732 if( (nSearchType & IMPL_FONT_ATTR_ITALIC) 2733 && ((rFSD.meItalic == ITALIC_DONTKNOW) || (rFSD.meItalic == ITALIC_NONE)) 2734 && (pFoundData->mnTypeFaces & IMPL_DEVFONT_ITALIC) ) 2735 rFSD.meItalic = ITALIC_NORMAL; 2736 } 2737 else 2738 { 2739 // if still needed fall back to default fonts 2740 pFoundData = FindDefaultFont(); 2741 } 2742 2743 return pFoundData; 2744 } 2745 2746 // ----------------------------------------------------------------------- 2747 2748 ImplFontEntry* ImplFontCache::GetGlyphFallbackFont( ImplDevFontList* pFontList, 2749 ImplFontSelectData& rFontSelData, int nFallbackLevel, rtl::OUString& rMissingCodes ) 2750 { 2751 // get a candidate font for glyph fallback 2752 // unless the previously selected font got a device specific substitution 2753 // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it 2754 if( nFallbackLevel >= 1) 2755 { 2756 ImplDevFontListData* pFallbackData = pFontList->GetGlyphFallbackFont( 2757 rFontSelData, rMissingCodes, nFallbackLevel-1 ); 2758 // escape when there are no font candidates 2759 if( !pFallbackData ) 2760 return NULL; 2761 // override the font name 2762 rFontSelData.maName = pFallbackData->GetFamilyName(); 2763 // clear the cached normalized name 2764 rFontSelData.maSearchName = String(); 2765 } 2766 2767 // get device font without doing device specific substitutions 2768 ImplFontEntry* pFallbackFont = GetFontEntry( pFontList, rFontSelData, NULL ); 2769 return pFallbackFont; 2770 } 2771 2772 // ----------------------------------------------------------------------- 2773 2774 void ImplFontCache::Release( ImplFontEntry* pEntry ) 2775 { 2776 static const int FONTCACHE_MAX = 50; 2777 2778 DBG_ASSERT( (pEntry->mnRefCount > 0), "ImplFontCache::Release() - font refcount underflow" ); 2779 if( --pEntry->mnRefCount > 0 ) 2780 return; 2781 2782 if( ++mnRef0Count < FONTCACHE_MAX ) 2783 return; 2784 2785 // remove unused entries from font instance cache 2786 FontInstanceList::iterator it_next = maFontInstanceList.begin(); 2787 while( it_next != maFontInstanceList.end() ) 2788 { 2789 FontInstanceList::iterator it = it_next++; 2790 ImplFontEntry* pFontEntry = (*it).second; 2791 if( pFontEntry->mnRefCount > 0 ) 2792 continue; 2793 2794 maFontInstanceList.erase( it ); 2795 delete pFontEntry; 2796 --mnRef0Count; 2797 DBG_ASSERT( (mnRef0Count>=0), "ImplFontCache::Release() - refcount0 underflow" ); 2798 2799 if( mpFirstEntry == pFontEntry ) 2800 mpFirstEntry = NULL; 2801 } 2802 2803 DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Release() - refcount0 mismatch" ); 2804 } 2805 2806 // ----------------------------------------------------------------------- 2807 2808 void ImplFontCache::Invalidate() 2809 { 2810 // delete unreferenced entries 2811 FontInstanceList::iterator it = maFontInstanceList.begin(); 2812 for(; it != maFontInstanceList.end(); ++it ) 2813 { 2814 ImplFontEntry* pFontEntry = (*it).second; 2815 if( pFontEntry->mnRefCount > 0 ) 2816 continue; 2817 2818 delete pFontEntry; 2819 --mnRef0Count; 2820 } 2821 2822 // #112304# make sure the font cache is really clean 2823 mpFirstEntry = NULL; 2824 maFontInstanceList.clear(); 2825 2826 DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Invalidate() - mnRef0Count non-zero" ); 2827 2828 #ifdef USE_BUILTIN_RASTERIZER 2829 // TODO: eventually move into SalGraphics layer 2830 GlyphCache::GetInstance().InvalidateAllGlyphs(); 2831 #endif 2832 } 2833 2834 // ======================================================================= 2835 2836 ImplMultiTextLineInfo::ImplMultiTextLineInfo() 2837 { 2838 mpLines = new PImplTextLineInfo[MULTITEXTLINEINFO_RESIZE]; 2839 mnLines = 0; 2840 mnSize = MULTITEXTLINEINFO_RESIZE; 2841 } 2842 2843 2844 ImplMultiTextLineInfo::~ImplMultiTextLineInfo() 2845 { 2846 for ( xub_StrLen i = 0; i < mnLines; i++ ) 2847 delete mpLines[i]; 2848 delete [] mpLines; 2849 } 2850 2851 void ImplMultiTextLineInfo::AddLine( ImplTextLineInfo* pLine ) 2852 { 2853 if ( mnSize == mnLines ) 2854 { 2855 mnSize += MULTITEXTLINEINFO_RESIZE; 2856 PImplTextLineInfo* pNewLines = new PImplTextLineInfo[mnSize]; 2857 memcpy( pNewLines, mpLines, mnLines*sizeof(PImplTextLineInfo) ); 2858 mpLines = pNewLines; 2859 } 2860 2861 mpLines[mnLines] = pLine; 2862 mnLines++; 2863 } 2864 2865 void ImplMultiTextLineInfo::Clear() 2866 { 2867 for ( xub_StrLen i = 0; i < mnLines; i++ ) 2868 delete mpLines[i]; 2869 mnLines = 0; 2870 } 2871 2872 // ======================================================================= 2873 2874 FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const Font& rFont ) 2875 { 2876 FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark(); 2877 2878 // If no Position is set, then calculate the default position, which 2879 // depends on the language 2880 if ( !(nEmphasisMark & (EMPHASISMARK_POS_ABOVE | EMPHASISMARK_POS_BELOW)) ) 2881 { 2882 LanguageType eLang = rFont.GetLanguage(); 2883 // In Chinese Simplified the EmphasisMarks are below/left 2884 if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) || 2885 (eLang == LANGUAGE_CHINESE_SINGAPORE) ) 2886 nEmphasisMark |= EMPHASISMARK_POS_BELOW; 2887 else 2888 { 2889 eLang = rFont.GetCJKContextLanguage(); 2890 // In Chinese Simplified the EmphasisMarks are below/left 2891 if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) || 2892 (eLang == LANGUAGE_CHINESE_SINGAPORE) ) 2893 nEmphasisMark |= EMPHASISMARK_POS_BELOW; 2894 else 2895 nEmphasisMark |= EMPHASISMARK_POS_ABOVE; 2896 } 2897 } 2898 2899 return nEmphasisMark; 2900 } 2901 2902 // ----------------------------------------------------------------------- 2903 2904 sal_Bool OutputDevice::ImplIsUnderlineAbove( const Font& rFont ) 2905 { 2906 if ( !rFont.IsVertical() ) 2907 return sal_False; 2908 2909 if( (LANGUAGE_JAPANESE == rFont.GetLanguage()) 2910 || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()) ) 2911 // the underline is right for Japanese only 2912 return sal_True; 2913 2914 return sal_False; 2915 } 2916 2917 // ======================================================================= 2918 2919 void OutputDevice::ImplInitFontList() const 2920 { 2921 if( ! mpFontList->Count() ) 2922 { 2923 if( mpGraphics || ImplGetGraphics() ) 2924 { 2925 RTL_LOGFILE_CONTEXT( aLog, "OutputDevice::ImplInitFontList()" ); 2926 mpGraphics->GetDevFontList( mpFontList ); 2927 } 2928 } 2929 if( meOutDevType == OUTDEV_WINDOW && ! mpFontList->Count() ) 2930 { 2931 String aError( RTL_CONSTASCII_USTRINGPARAM( "Application error: no fonts and no vcl resource found on your system" ) ); 2932 ResMgr* pMgr = ImplGetResMgr(); 2933 if( pMgr ) 2934 { 2935 String aResStr( ResId( SV_ACCESSERROR_NO_FONTS, *pMgr ) ); 2936 if( aResStr.Len() ) 2937 aError = aResStr; 2938 } 2939 Application::Abort( aError ); 2940 } 2941 } 2942 2943 // ======================================================================= 2944 2945 void OutputDevice::ImplInitFont() const 2946 { 2947 DBG_TESTSOLARMUTEX(); 2948 2949 if ( mbInitFont ) 2950 { 2951 if ( meOutDevType != OUTDEV_PRINTER ) 2952 { 2953 // decide if antialiasing is appropriate 2954 bool bNonAntialiased = (GetAntialiasing() & ANTIALIASING_DISABLE_TEXT) != 0; 2955 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); 2956 bNonAntialiased |= ((rStyleSettings.GetDisplayOptions() & DISPLAY_OPTION_AA_DISABLE) != 0); 2957 bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontEntry->maFontSelData.mnHeight); 2958 mpFontEntry->maFontSelData.mbNonAntialiased = bNonAntialiased; 2959 } 2960 2961 if( !mpPDFWriter || !mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) ) 2962 { 2963 // select font in the device layers 2964 mpFontEntry->mnSetFontFlags = mpGraphics->SetFont( &(mpFontEntry->maFontSelData), 0 ); 2965 } 2966 mbInitFont = false; 2967 } 2968 } 2969 2970 // ----------------------------------------------------------------------- 2971 2972 void OutputDevice::ImplInitTextColor() 2973 { 2974 DBG_TESTSOLARMUTEX(); 2975 2976 if ( mbInitTextColor ) 2977 { 2978 mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) ); 2979 mbInitTextColor = sal_False; 2980 } 2981 } 2982 2983 // ----------------------------------------------------------------------- 2984 2985 bool OutputDevice::ImplNewFont() const 2986 { 2987 DBG_TESTSOLARMUTEX(); 2988 2989 // get correct font list on the PDF writer if necessary 2990 if( mpPDFWriter ) 2991 { 2992 const ImplSVData* pSVData = ImplGetSVData(); 2993 if( mpFontList == pSVData->maGDIData.mpScreenFontList 2994 || mpFontCache == pSVData->maGDIData.mpScreenFontCache ) 2995 const_cast<OutputDevice&>(*this).ImplUpdateFontData( true ); 2996 } 2997 2998 if ( !mbNewFont ) 2999 return true; 3000 3001 // we need a graphics 3002 if ( !mpGraphics && !ImplGetGraphics() ) 3003 return false; 3004 SalGraphics* pGraphics = mpGraphics; 3005 ImplInitFontList(); 3006 3007 // convert to pixel height 3008 // TODO: replace integer based aSize completely with subpixel accurate type 3009 float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetHeight()) ); 3010 Size aSize = ImplLogicToDevicePixel( maFont.GetSize() ); 3011 if ( !aSize.Height() ) 3012 { 3013 // use default pixel height only when logical height is zero 3014 if ( maFont.GetSize().Height() ) 3015 aSize.Height() = 1; 3016 else 3017 aSize.Height() = (12*mnDPIY)/72; 3018 fExactHeight = static_cast<float>(aSize.Height()); 3019 } 3020 3021 // select the default width only when logical width is zero 3022 if( (0 == aSize.Width()) && (0 != maFont.GetSize().Width()) ) 3023 aSize.Width() = 1; 3024 3025 // get font entry 3026 ImplDirectFontSubstitution* pDevSpecificSubst = NULL; 3027 if( mpOutDevData ) 3028 pDevSpecificSubst = &mpOutDevData->maDevFontSubst; 3029 ImplFontEntry* pOldEntry = mpFontEntry; 3030 mpFontEntry = mpFontCache->GetFontEntry( mpFontList, maFont, aSize, fExactHeight, pDevSpecificSubst ); 3031 if( pOldEntry ) 3032 mpFontCache->Release( pOldEntry ); 3033 3034 ImplFontEntry* pFontEntry = mpFontEntry; 3035 // mark when lower layers need to get involved 3036 mbNewFont = sal_False; 3037 if( pFontEntry != pOldEntry ) 3038 mbInitFont = sal_True; 3039 3040 // select font when it has not been initialized yet 3041 if ( !pFontEntry->mbInit ) 3042 { 3043 ImplInitFont(); 3044 3045 // get metric data from device layers 3046 if ( pGraphics ) 3047 { 3048 pFontEntry->mbInit = true; 3049 3050 pFontEntry->maMetric.mnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation); 3051 if( mpPDFWriter && mpPDFWriter->isBuiltinFont( pFontEntry->maFontSelData.mpFontData ) ) 3052 mpPDFWriter->getFontMetric( &pFontEntry->maFontSelData, &(pFontEntry->maMetric) ); 3053 else 3054 pGraphics->GetFontMetric( &(pFontEntry->maMetric) ); 3055 3056 pFontEntry->maMetric.ImplInitTextLineSize( this ); 3057 pFontEntry->maMetric.ImplInitAboveTextLineSize(); 3058 3059 pFontEntry->mnLineHeight = pFontEntry->maMetric.mnAscent + pFontEntry->maMetric.mnDescent; 3060 3061 if( pFontEntry->maFontSelData.mnOrientation 3062 && !pFontEntry->maMetric.mnOrientation 3063 && (meOutDevType != OUTDEV_PRINTER) ) 3064 { 3065 pFontEntry->mnOwnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation); 3066 pFontEntry->mnOrientation = pFontEntry->mnOwnOrientation; 3067 } 3068 else 3069 pFontEntry->mnOrientation = pFontEntry->maMetric.mnOrientation; 3070 } 3071 } 3072 3073 // enable kerning array if requested 3074 if ( maFont.GetKerning() & KERNING_FONTSPECIFIC ) 3075 { 3076 // TODO: test if physical font supports kerning and disable if not 3077 if( pFontEntry->maMetric.mbKernableFont ) 3078 mbKerning = true; 3079 } 3080 else 3081 mbKerning = false; 3082 if ( maFont.GetKerning() & KERNING_ASIAN ) 3083 mbKerning = true; 3084 3085 // calculate EmphasisArea 3086 mnEmphasisAscent = 0; 3087 mnEmphasisDescent = 0; 3088 if ( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) 3089 { 3090 FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont ); 3091 long nEmphasisHeight = (pFontEntry->mnLineHeight*250)/1000; 3092 if ( nEmphasisHeight < 1 ) 3093 nEmphasisHeight = 1; 3094 if ( nEmphasisMark & EMPHASISMARK_POS_BELOW ) 3095 mnEmphasisDescent = nEmphasisHeight; 3096 else 3097 mnEmphasisAscent = nEmphasisHeight; 3098 } 3099 3100 // calculate text offset depending on TextAlignment 3101 TextAlign eAlign = maFont.GetAlign(); 3102 if ( eAlign == ALIGN_BASELINE ) 3103 { 3104 mnTextOffX = 0; 3105 mnTextOffY = 0; 3106 } 3107 else if ( eAlign == ALIGN_TOP ) 3108 { 3109 mnTextOffX = 0; 3110 mnTextOffY = +pFontEntry->maMetric.mnAscent + mnEmphasisAscent; 3111 if ( pFontEntry->mnOrientation ) 3112 ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation ); 3113 } 3114 else // eAlign == ALIGN_BOTTOM 3115 { 3116 mnTextOffX = 0; 3117 mnTextOffY = -pFontEntry->maMetric.mnDescent + mnEmphasisDescent; 3118 if ( pFontEntry->mnOrientation ) 3119 ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation ); 3120 } 3121 3122 mbTextLines = ((maFont.GetUnderline() != UNDERLINE_NONE) && (maFont.GetUnderline() != UNDERLINE_DONTKNOW)) || 3123 ((maFont.GetOverline() != UNDERLINE_NONE) && (maFont.GetOverline() != UNDERLINE_DONTKNOW)) || 3124 ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW)); 3125 mbTextSpecial = maFont.IsShadow() || maFont.IsOutline() || 3126 (maFont.GetRelief() != RELIEF_NONE); 3127 3128 // #95414# fix for OLE objects which use scale factors very creatively 3129 if( mbMap && !aSize.Width() ) 3130 { 3131 int nOrigWidth = pFontEntry->maMetric.mnWidth; 3132 float fStretch = (float)maMapRes.mnMapScNumX * maMapRes.mnMapScDenomY; 3133 fStretch /= (float)maMapRes.mnMapScNumY * maMapRes.mnMapScDenomX; 3134 int nNewWidth = (int)(nOrigWidth * fStretch + 0.5); 3135 if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) ) 3136 { 3137 Size aOrigSize = maFont.GetSize(); 3138 const_cast<Font&>(maFont).SetSize( Size( nNewWidth, aSize.Height() ) ); 3139 mbMap = sal_False; 3140 mbNewFont = sal_True; 3141 ImplNewFont(); // recurse once using stretched width 3142 mbMap = sal_True; 3143 const_cast<Font&>(maFont).SetSize( aOrigSize ); 3144 } 3145 } 3146 3147 return true; 3148 } 3149 3150 // ----------------------------------------------------------------------- 3151 3152 long OutputDevice::ImplGetTextWidth( const SalLayout& rSalLayout ) const 3153 { 3154 long nWidth = rSalLayout.GetTextWidth(); 3155 nWidth /= rSalLayout.GetUnitsPerPixel(); 3156 return nWidth; 3157 } 3158 3159 // ----------------------------------------------------------------------- 3160 3161 void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY, 3162 long nDistX, long nDistY, long nWidth, long nHeight ) 3163 { 3164 long nX = nDistX; 3165 long nY = nDistY; 3166 3167 short nOrientation = mpFontEntry->mnOrientation; 3168 if ( nOrientation ) 3169 { 3170 // Rotate rect without rounding problems for 90 degree rotations 3171 if ( !(nOrientation % 900) ) 3172 { 3173 if ( nOrientation == 900 ) 3174 { 3175 long nTemp = nX; 3176 nX = nY; 3177 nY = -nTemp; 3178 nTemp = nWidth; 3179 nWidth = nHeight; 3180 nHeight = nTemp; 3181 nY -= nHeight; 3182 } 3183 else if ( nOrientation == 1800 ) 3184 { 3185 nX = -nX; 3186 nY = -nY; 3187 nX -= nWidth; 3188 nY -= nHeight; 3189 } 3190 else /* ( nOrientation == 2700 ) */ 3191 { 3192 long nTemp = nX; 3193 nX = -nY; 3194 nY = nTemp; 3195 nTemp = nWidth; 3196 nWidth = nHeight; 3197 nHeight = nTemp; 3198 nX -= nWidth; 3199 } 3200 } 3201 else 3202 { 3203 nX += nBaseX; 3204 nY += nBaseY; 3205 // inflate because polygons are drawn smaller 3206 Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) ); 3207 Polygon aPoly( aRect ); 3208 aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation ); 3209 ImplDrawPolygon( aPoly ); 3210 return; 3211 } 3212 } 3213 3214 nX += nBaseX; 3215 nY += nBaseY; 3216 mpGraphics->DrawRect( nX, nY, nWidth, nHeight, this ); 3217 } 3218 3219 // ----------------------------------------------------------------------- 3220 3221 void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout ) 3222 { 3223 const long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel(); 3224 const Point aBase = rSalLayout.DrawBase(); 3225 const long nX = aBase.X(); 3226 const long nY = aBase.Y(); 3227 3228 if ( mbLineColor || mbInitLineColor ) 3229 { 3230 mpGraphics->SetLineColor(); 3231 mbInitLineColor = sal_True; 3232 } 3233 mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) ); 3234 mbInitFillColor = sal_True; 3235 3236 ImplDrawTextRect( nX, nY, 0, -(mpFontEntry->maMetric.mnAscent + mnEmphasisAscent), 3237 nWidth, 3238 mpFontEntry->mnLineHeight+mnEmphasisAscent+mnEmphasisDescent ); 3239 } 3240 3241 // ----------------------------------------------------------------------- 3242 3243 Rectangle OutputDevice::ImplGetTextBoundRect( const SalLayout& rSalLayout ) 3244 { 3245 Point aPoint = rSalLayout.GetDrawPosition(); 3246 long nX = aPoint.X(); 3247 long nY = aPoint.Y(); 3248 3249 long nWidth = rSalLayout.GetTextWidth(); 3250 long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; 3251 3252 nY -= mpFontEntry->maMetric.mnAscent + mnEmphasisAscent; 3253 3254 if ( mpFontEntry->mnOrientation ) 3255 { 3256 long nBaseX = nX, nBaseY = nY; 3257 if ( !(mpFontEntry->mnOrientation % 900) ) 3258 { 3259 long nX2 = nX+nWidth; 3260 long nY2 = nY+nHeight; 3261 ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation ); 3262 ImplRotatePos( nBaseX, nBaseY, nX2, nY2, mpFontEntry->mnOrientation ); 3263 nWidth = nX2-nX; 3264 nHeight = nY2-nY; 3265 } 3266 else 3267 { 3268 // inflate by +1+1 because polygons are drawn smaller 3269 Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) ); 3270 Polygon aPoly( aRect ); 3271 aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation ); 3272 return aPoly.GetBoundRect(); 3273 } 3274 } 3275 3276 return Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ); 3277 } 3278 3279 // ----------------------------------------------------------------------- 3280 3281 void OutputDevice::ImplInitTextLineSize() 3282 { 3283 mpFontEntry->maMetric.ImplInitTextLineSize( this ); 3284 } 3285 3286 // ----------------------------------------------------------------------- 3287 3288 void OutputDevice::ImplInitAboveTextLineSize() 3289 { 3290 mpFontEntry->maMetric.ImplInitAboveTextLineSize(); 3291 } 3292 3293 // ----------------------------------------------------------------------- 3294 3295 ImplFontMetricData::ImplFontMetricData( const ImplFontSelectData& rFontSelData ) 3296 : ImplFontAttributes( rFontSelData ) 3297 { 3298 // initialize the members provided by the font request 3299 mnWidth = rFontSelData.mnWidth; 3300 mnSlant = rFontSelData.GetSlant(); 3301 mnOrientation = sal::static_int_cast<short>(rFontSelData.mnOrientation); 3302 3303 // intialize the used font name 3304 if( rFontSelData.mpFontData ) 3305 { 3306 maName = rFontSelData.mpFontData->maName; 3307 maStyleName= rFontSelData.mpFontData->maStyleName; 3308 mbDevice = rFontSelData.mpFontData->mbDevice; 3309 mbKernableFont = true; 3310 } 3311 else 3312 { 3313 xub_StrLen nTokenPos = 0; 3314 maName = GetNextFontToken( rFontSelData.maName, nTokenPos ); 3315 maStyleName= rFontSelData.maStyleName; 3316 mbDevice = false; 3317 mbKernableFont = false; 3318 } 3319 3320 // reset metrics that are usually measured for the font instance 3321 mnAscent = 0; 3322 mnDescent = 0; 3323 mnIntLeading = 0; 3324 mnExtLeading = 0; 3325 mnMinKashida = 0; 3326 3327 // reset metrics that are usually derived from the measurements 3328 mnUnderlineSize = 0; 3329 mnUnderlineOffset = 0; 3330 mnBUnderlineSize = 0; 3331 mnBUnderlineOffset = 0; 3332 mnDUnderlineSize = 0; 3333 mnDUnderlineOffset1 = 0; 3334 mnDUnderlineOffset2 = 0; 3335 mnWUnderlineSize = 0; 3336 mnWUnderlineOffset = 0; 3337 mnAboveUnderlineSize = 0; 3338 mnAboveUnderlineOffset = 0; 3339 mnAboveBUnderlineSize = 0; 3340 mnAboveBUnderlineOffset = 0; 3341 mnAboveDUnderlineSize = 0; 3342 mnAboveDUnderlineOffset1 = 0; 3343 mnAboveDUnderlineOffset2 = 0; 3344 mnAboveWUnderlineSize = 0; 3345 mnAboveWUnderlineOffset = 0; 3346 mnStrikeoutSize = 0; 3347 mnStrikeoutOffset = 0; 3348 mnBStrikeoutSize = 0; 3349 mnBStrikeoutOffset = 0; 3350 mnDStrikeoutSize = 0; 3351 mnDStrikeoutOffset1 = 0; 3352 mnDStrikeoutOffset2 = 0; 3353 } 3354 3355 // ----------------------------------------------------------------------- 3356 3357 void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice* pDev ) 3358 { 3359 long nDescent = mnDescent; 3360 if ( nDescent <= 0 ) 3361 { 3362 nDescent = mnAscent / 10; 3363 if ( !nDescent ) 3364 nDescent = 1; 3365 } 3366 3367 // #i55341# for some fonts it is not a good idea to calculate 3368 // their text line metrics from the real font descent 3369 // => work around this problem just for these fonts 3370 if( 3*nDescent > mnAscent ) 3371 nDescent = mnAscent / 3; 3372 3373 long nLineHeight = ((nDescent*25)+50) / 100; 3374 if ( !nLineHeight ) 3375 nLineHeight = 1; 3376 long nLineHeight2 = nLineHeight / 2; 3377 if ( !nLineHeight2 ) 3378 nLineHeight2 = 1; 3379 3380 long nBLineHeight = ((nDescent*50)+50) / 100; 3381 if ( nBLineHeight == nLineHeight ) 3382 nBLineHeight++; 3383 long nBLineHeight2 = nBLineHeight/2; 3384 if ( !nBLineHeight2 ) 3385 nBLineHeight2 = 1; 3386 3387 long n2LineHeight = ((nDescent*16)+50) / 100; 3388 if ( !n2LineHeight ) 3389 n2LineHeight = 1; 3390 long n2LineDY = n2LineHeight; 3391 /* #117909# 3392 * add some pixels to minimum double line distance on higher resolution devices 3393 */ 3394 long nMin2LineDY = 1 + pDev->ImplGetDPIY()/150; 3395 if ( n2LineDY < nMin2LineDY ) 3396 n2LineDY = nMin2LineDY; 3397 long n2LineDY2 = n2LineDY/2; 3398 if ( !n2LineDY2 ) 3399 n2LineDY2 = 1; 3400 3401 long nUnderlineOffset = mnDescent/2 + 1; 3402 long nStrikeoutOffset = -((mnAscent - mnIntLeading) / 3); 3403 3404 mnUnderlineSize = nLineHeight; 3405 mnUnderlineOffset = nUnderlineOffset - nLineHeight2; 3406 3407 mnBUnderlineSize = nBLineHeight; 3408 mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2; 3409 3410 mnDUnderlineSize = n2LineHeight; 3411 mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight; 3412 mnDUnderlineOffset2 = mnDUnderlineOffset1 + n2LineDY + n2LineHeight; 3413 3414 long nWCalcSize = mnDescent; 3415 if ( nWCalcSize < 6 ) 3416 { 3417 if ( (nWCalcSize == 1) || (nWCalcSize == 2) ) 3418 mnWUnderlineSize = nWCalcSize; 3419 else 3420 mnWUnderlineSize = 3; 3421 } 3422 else 3423 mnWUnderlineSize = ((nWCalcSize*50)+50) / 100; 3424 3425 // #109280# the following line assures that wavelnes are never placed below the descent, however 3426 // for most fonts the waveline then is drawn into the text, so we better keep the old solution 3427 // pFontEntry->maMetric.mnWUnderlineOffset = pFontEntry->maMetric.mnDescent + 1 - pFontEntry->maMetric.mnWUnderlineSize; 3428 mnWUnderlineOffset = nUnderlineOffset; 3429 3430 mnStrikeoutSize = nLineHeight; 3431 mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2; 3432 3433 mnBStrikeoutSize = nBLineHeight; 3434 mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2; 3435 3436 mnDStrikeoutSize = n2LineHeight; 3437 mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight; 3438 mnDStrikeoutOffset2 = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight; 3439 } 3440 3441 // ----------------------------------------------------------------------- 3442 3443 void ImplFontMetricData::ImplInitAboveTextLineSize() 3444 { 3445 long nIntLeading = mnIntLeading; 3446 // TODO: assess usage of nLeading below (changed in extleading CWS) 3447 // if no leading is available, we assume 15% of the ascent 3448 if ( nIntLeading <= 0 ) 3449 { 3450 nIntLeading = mnAscent*15/100; 3451 if ( !nIntLeading ) 3452 nIntLeading = 1; 3453 } 3454 3455 long nLineHeight = ((nIntLeading*25)+50) / 100; 3456 if ( !nLineHeight ) 3457 nLineHeight = 1; 3458 3459 long nBLineHeight = ((nIntLeading*50)+50) / 100; 3460 if ( nBLineHeight == nLineHeight ) 3461 nBLineHeight++; 3462 3463 long n2LineHeight = ((nIntLeading*16)+50) / 100; 3464 if ( !n2LineHeight ) 3465 n2LineHeight = 1; 3466 3467 long nCeiling = -mnAscent; 3468 3469 mnAboveUnderlineSize = nLineHeight; 3470 mnAboveUnderlineOffset = nCeiling + (nIntLeading - nLineHeight + 1) / 2; 3471 3472 mnAboveBUnderlineSize = nBLineHeight; 3473 mnAboveBUnderlineOffset = nCeiling + (nIntLeading - nBLineHeight + 1) / 2; 3474 3475 mnAboveDUnderlineSize = n2LineHeight; 3476 mnAboveDUnderlineOffset1 = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2; 3477 mnAboveDUnderlineOffset2 = nCeiling + (nIntLeading + n2LineHeight + 1) / 2; 3478 3479 long nWCalcSize = nIntLeading; 3480 if ( nWCalcSize < 6 ) 3481 { 3482 if ( (nWCalcSize == 1) || (nWCalcSize == 2) ) 3483 mnAboveWUnderlineSize = nWCalcSize; 3484 else 3485 mnAboveWUnderlineSize = 3; 3486 } 3487 else 3488 mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100; 3489 3490 mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2; 3491 } 3492 3493 // ----------------------------------------------------------------------- 3494 3495 static void ImplDrawWavePixel( long nOriginX, long nOriginY, 3496 long nCurX, long nCurY, 3497 short nOrientation, 3498 SalGraphics* pGraphics, 3499 OutputDevice* pOutDev, 3500 sal_Bool bDrawPixAsRect, 3501 3502 long nPixWidth, long nPixHeight ) 3503 { 3504 if ( nOrientation ) 3505 ImplRotatePos( nOriginX, nOriginY, nCurX, nCurY, nOrientation ); 3506 3507 if ( bDrawPixAsRect ) 3508 { 3509 3510 pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev ); 3511 } 3512 else 3513 { 3514 pGraphics->DrawPixel( nCurX, nCurY, pOutDev ); 3515 } 3516 } 3517 3518 // ----------------------------------------------------------------------- 3519 3520 void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY, 3521 long nDistX, long nDistY, 3522 long nWidth, long nHeight, 3523 long nLineWidth, short nOrientation, 3524 const Color& rColor ) 3525 { 3526 if ( !nHeight ) 3527 return; 3528 3529 long nStartX = nBaseX + nDistX; 3530 long nStartY = nBaseY + nDistY; 3531 3532 // Bei Hoehe von 1 Pixel reicht es, eine Linie auszugeben 3533 if ( (nLineWidth == 1) && (nHeight == 1) ) 3534 { 3535 mpGraphics->SetLineColor( ImplColorToSal( rColor ) ); 3536 mbInitLineColor = sal_True; 3537 3538 long nEndX = nStartX+nWidth; 3539 long nEndY = nStartY; 3540 if ( nOrientation ) 3541 { 3542 ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation ); 3543 ImplRotatePos( nBaseX, nBaseY, nEndX, nEndY, nOrientation ); 3544 } 3545 mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this ); 3546 } 3547 else 3548 { 3549 long nCurX = nStartX; 3550 long nCurY = nStartY; 3551 long nDiffX = 2; 3552 long nDiffY = nHeight-1; 3553 long nCount = nWidth; 3554 long nOffY = -1; 3555 long nFreq; 3556 long i; 3557 long nPixWidth; 3558 long nPixHeight; 3559 sal_Bool bDrawPixAsRect; 3560 // Auf Druckern die Pixel per DrawRect() ausgeben 3561 if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) ) 3562 { 3563 if ( mbLineColor || mbInitLineColor ) 3564 { 3565 mpGraphics->SetLineColor(); 3566 mbInitLineColor = sal_True; 3567 } 3568 mpGraphics->SetFillColor( ImplColorToSal( rColor ) ); 3569 mbInitFillColor = sal_True; 3570 bDrawPixAsRect = sal_True; 3571 nPixWidth = nLineWidth; 3572 nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY; 3573 } 3574 else 3575 { 3576 mpGraphics->SetLineColor( ImplColorToSal( rColor ) ); 3577 mbInitLineColor = sal_True; 3578 nPixWidth = 1; 3579 nPixHeight = 1; 3580 bDrawPixAsRect = sal_False; 3581 } 3582 3583 if ( !nDiffY ) 3584 { 3585 while ( nWidth ) 3586 { 3587 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, 3588 mpGraphics, this, 3589 bDrawPixAsRect, nPixWidth, nPixHeight ); 3590 nCurX++; 3591 nWidth--; 3592 } 3593 } 3594 else 3595 { 3596 nCurY += nDiffY; 3597 nFreq = nCount / (nDiffX+nDiffY); 3598 while ( nFreq-- ) 3599 { 3600 for( i = nDiffY; i; --i ) 3601 { 3602 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, 3603 mpGraphics, this, 3604 bDrawPixAsRect, nPixWidth, nPixHeight ); 3605 nCurX++; 3606 nCurY += nOffY; 3607 } 3608 for( i = nDiffX; i; --i ) 3609 { 3610 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, 3611 mpGraphics, this, 3612 bDrawPixAsRect, nPixWidth, nPixHeight ); 3613 nCurX++; 3614 } 3615 nOffY = -nOffY; 3616 } 3617 nFreq = nCount % (nDiffX+nDiffY); 3618 if ( nFreq ) 3619 { 3620 for( i = nDiffY; i && nFreq; --i, --nFreq ) 3621 { 3622 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, 3623 mpGraphics, this, 3624 bDrawPixAsRect, nPixWidth, nPixHeight ); 3625 nCurX++; 3626 nCurY += nOffY; 3627 3628 } 3629 for( i = nDiffX; i && nFreq; --i, --nFreq ) 3630 { 3631 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, 3632 mpGraphics, this, 3633 bDrawPixAsRect, nPixWidth, nPixHeight ); 3634 nCurX++; 3635 } 3636 } 3637 } 3638 3639 } 3640 } 3641 3642 // ----------------------------------------------------------------------- 3643 3644 void OutputDevice::ImplDrawWaveTextLine( long nBaseX, long nBaseY, 3645 long nDistX, long nDistY, long nWidth, 3646 FontUnderline eTextLine, 3647 Color aColor, 3648 sal_Bool bIsAbove ) 3649 { 3650 ImplFontEntry* pFontEntry = mpFontEntry; 3651 long nLineHeight; 3652 long nLinePos; 3653 3654 if ( bIsAbove ) 3655 { 3656 nLineHeight = pFontEntry->maMetric.mnAboveWUnderlineSize; 3657 nLinePos = pFontEntry->maMetric.mnAboveWUnderlineOffset; 3658 } 3659 else 3660 { 3661 nLineHeight = pFontEntry->maMetric.mnWUnderlineSize; 3662 nLinePos = pFontEntry->maMetric.mnWUnderlineOffset; 3663 } 3664 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) ) 3665 nLineHeight = 3; 3666 long nLineWidth = (mnDPIX/300); 3667 if ( !nLineWidth ) 3668 nLineWidth = 1; 3669 if ( eTextLine == UNDERLINE_BOLDWAVE ) 3670 nLineWidth *= 2; 3671 nLinePos += nDistY - (nLineHeight / 2); 3672 long nLineWidthHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY; 3673 if ( eTextLine == UNDERLINE_DOUBLEWAVE ) 3674 { 3675 long nOrgLineHeight = nLineHeight; 3676 nLineHeight /= 3; 3677 if ( nLineHeight < 2 ) 3678 { 3679 if ( nOrgLineHeight > 1 ) 3680 nLineHeight = 2; 3681 else 3682 nLineHeight = 1; 3683 } 3684 long nLineDY = nOrgLineHeight-(nLineHeight*2); 3685 if ( nLineDY < nLineWidthHeight ) 3686 nLineDY = nLineWidthHeight; 3687 long nLineDY2 = nLineDY/2; 3688 if ( !nLineDY2 ) 3689 nLineDY2 = 1; 3690 3691 nLinePos -= nLineWidthHeight-nLineDY2; 3692 ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, 3693 nLineWidth, mpFontEntry->mnOrientation, aColor ); 3694 nLinePos += nLineWidthHeight+nLineDY; 3695 ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, 3696 nLineWidth, mpFontEntry->mnOrientation, aColor ); 3697 } 3698 else 3699 { 3700 nLinePos -= nLineWidthHeight/2; 3701 ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, 3702 nLineWidth, mpFontEntry->mnOrientation, aColor ); 3703 } 3704 } 3705 3706 // ----------------------------------------------------------------------- 3707 3708 void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY, 3709 long nDistX, long nDistY, long nWidth, 3710 FontUnderline eTextLine, 3711 Color aColor, 3712 sal_Bool bIsAbove ) 3713 { 3714 ImplFontEntry* pFontEntry = mpFontEntry; 3715 long nLineHeight = 0; 3716 long nLinePos = 0; 3717 long nLinePos2 = 0; 3718 3719 const long nY = nDistY; 3720 3721 if ( eTextLine > UNDERLINE_LAST ) 3722 eTextLine = UNDERLINE_SINGLE; 3723 3724 switch ( eTextLine ) 3725 { 3726 case UNDERLINE_SINGLE: 3727 case UNDERLINE_DOTTED: 3728 case UNDERLINE_DASH: 3729 case UNDERLINE_LONGDASH: 3730 case UNDERLINE_DASHDOT: 3731 case UNDERLINE_DASHDOTDOT: 3732 if ( bIsAbove ) 3733 { 3734 nLineHeight = pFontEntry->maMetric.mnAboveUnderlineSize; 3735 nLinePos = nY + pFontEntry->maMetric.mnAboveUnderlineOffset; 3736 } 3737 else 3738 { 3739 nLineHeight = pFontEntry->maMetric.mnUnderlineSize; 3740 nLinePos = nY + pFontEntry->maMetric.mnUnderlineOffset; 3741 } 3742 break; 3743 case UNDERLINE_BOLD: 3744 case UNDERLINE_BOLDDOTTED: 3745 case UNDERLINE_BOLDDASH: 3746 case UNDERLINE_BOLDLONGDASH: 3747 case UNDERLINE_BOLDDASHDOT: 3748 case UNDERLINE_BOLDDASHDOTDOT: 3749 if ( bIsAbove ) 3750 { 3751 nLineHeight = pFontEntry->maMetric.mnAboveBUnderlineSize; 3752 nLinePos = nY + pFontEntry->maMetric.mnAboveBUnderlineOffset; 3753 } 3754 else 3755 { 3756 nLineHeight = pFontEntry->maMetric.mnBUnderlineSize; 3757 nLinePos = nY + pFontEntry->maMetric.mnBUnderlineOffset; 3758 } 3759 break; 3760 case UNDERLINE_DOUBLE: 3761 if ( bIsAbove ) 3762 { 3763 nLineHeight = pFontEntry->maMetric.mnAboveDUnderlineSize; 3764 nLinePos = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset1; 3765 nLinePos2 = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset2; 3766 } 3767 else 3768 { 3769 nLineHeight = pFontEntry->maMetric.mnDUnderlineSize; 3770 nLinePos = nY + pFontEntry->maMetric.mnDUnderlineOffset1; 3771 nLinePos2 = nY + pFontEntry->maMetric.mnDUnderlineOffset2; 3772 } 3773 break; 3774 default: 3775 break; 3776 } 3777 3778 if ( nLineHeight ) 3779 { 3780 if ( mbLineColor || mbInitLineColor ) 3781 { 3782 mpGraphics->SetLineColor(); 3783 mbInitLineColor = sal_True; 3784 } 3785 mpGraphics->SetFillColor( ImplColorToSal( aColor ) ); 3786 mbInitFillColor = sal_True; 3787 3788 long nLeft = nDistX; 3789 3790 switch ( eTextLine ) 3791 { 3792 case UNDERLINE_SINGLE: 3793 case UNDERLINE_BOLD: 3794 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); 3795 break; 3796 case UNDERLINE_DOUBLE: 3797 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); 3798 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); 3799 break; 3800 case UNDERLINE_DOTTED: 3801 case UNDERLINE_BOLDDOTTED: 3802 { 3803 long nDotWidth = nLineHeight*mnDPIY; 3804 nDotWidth += mnDPIY/2; 3805 nDotWidth /= mnDPIY; 3806 long nTempWidth = nDotWidth; 3807 long nEnd = nLeft+nWidth; 3808 while ( nLeft < nEnd ) 3809 { 3810 if ( nLeft+nTempWidth > nEnd ) 3811 nTempWidth = nEnd-nLeft; 3812 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); 3813 nLeft += nDotWidth*2; 3814 } 3815 } 3816 break; 3817 case UNDERLINE_DASH: 3818 case UNDERLINE_LONGDASH: 3819 case UNDERLINE_BOLDDASH: 3820 case UNDERLINE_BOLDLONGDASH: 3821 { 3822 long nDotWidth = nLineHeight*mnDPIY; 3823 nDotWidth += mnDPIY/2; 3824 nDotWidth /= mnDPIY; 3825 long nMinDashWidth; 3826 long nMinSpaceWidth; 3827 long nSpaceWidth; 3828 long nDashWidth; 3829 if ( (eTextLine == UNDERLINE_LONGDASH) || 3830 (eTextLine == UNDERLINE_BOLDLONGDASH) ) 3831 { 3832 nMinDashWidth = nDotWidth*6; 3833 nMinSpaceWidth = nDotWidth*2; 3834 nDashWidth = 200; 3835 nSpaceWidth = 100; 3836 } 3837 else 3838 { 3839 nMinDashWidth = nDotWidth*4; 3840 nMinSpaceWidth = (nDotWidth*150)/100; 3841 nDashWidth = 100; 3842 nSpaceWidth = 50; 3843 } 3844 nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540; 3845 nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540; 3846 // DashWidth wird gegebenenfalls verbreitert, wenn 3847 // die dicke der Linie im Verhaeltnis zur Laenge 3848 // zu dick wird 3849 if ( nDashWidth < nMinDashWidth ) 3850 nDashWidth = nMinDashWidth; 3851 if ( nSpaceWidth < nMinSpaceWidth ) 3852 nSpaceWidth = nMinSpaceWidth; 3853 long nTempWidth = nDashWidth; 3854 long nEnd = nLeft+nWidth; 3855 while ( nLeft < nEnd ) 3856 { 3857 if ( nLeft+nTempWidth > nEnd ) 3858 nTempWidth = nEnd-nLeft; 3859 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); 3860 nLeft += nDashWidth+nSpaceWidth; 3861 } 3862 } 3863 break; 3864 case UNDERLINE_DASHDOT: 3865 case UNDERLINE_BOLDDASHDOT: 3866 { 3867 long nDotWidth = nLineHeight*mnDPIY; 3868 nDotWidth += mnDPIY/2; 3869 nDotWidth /= mnDPIY; 3870 long nDashWidth = ((100*mnDPIX)+1270)/2540; 3871 long nMinDashWidth = nDotWidth*4; 3872 // DashWidth wird gegebenenfalls verbreitert, wenn 3873 // die dicke der Linie im Verhaeltnis zur Laenge 3874 // zu dick wird 3875 if ( nDashWidth < nMinDashWidth ) 3876 nDashWidth = nMinDashWidth; 3877 long nTempDotWidth = nDotWidth; 3878 long nTempDashWidth = nDashWidth; 3879 long nEnd = nLeft+nWidth; 3880 while ( nLeft < nEnd ) 3881 { 3882 if ( nLeft+nTempDotWidth > nEnd ) 3883 nTempDotWidth = nEnd-nLeft; 3884 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); 3885 nLeft += nDotWidth*2; 3886 if ( nLeft > nEnd ) 3887 break; 3888 if ( nLeft+nTempDashWidth > nEnd ) 3889 nTempDashWidth = nEnd-nLeft; 3890 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); 3891 nLeft += nDashWidth+nDotWidth; 3892 } 3893 } 3894 break; 3895 case UNDERLINE_DASHDOTDOT: 3896 case UNDERLINE_BOLDDASHDOTDOT: 3897 { 3898 long nDotWidth = nLineHeight*mnDPIY; 3899 nDotWidth += mnDPIY/2; 3900 nDotWidth /= mnDPIY; 3901 long nDashWidth = ((100*mnDPIX)+1270)/2540; 3902 long nMinDashWidth = nDotWidth*4; 3903 // DashWidth wird gegebenenfalls verbreitert, wenn 3904 // die dicke der Linie im Verhaeltnis zur Laenge 3905 // zu dick wird 3906 if ( nDashWidth < nMinDashWidth ) 3907 nDashWidth = nMinDashWidth; 3908 long nTempDotWidth = nDotWidth; 3909 long nTempDashWidth = nDashWidth; 3910 long nEnd = nLeft+nWidth; 3911 while ( nLeft < nEnd ) 3912 { 3913 if ( nLeft+nTempDotWidth > nEnd ) 3914 nTempDotWidth = nEnd-nLeft; 3915 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); 3916 nLeft += nDotWidth*2; 3917 if ( nLeft > nEnd ) 3918 break; 3919 if ( nLeft+nTempDotWidth > nEnd ) 3920 nTempDotWidth = nEnd-nLeft; 3921 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); 3922 nLeft += nDotWidth*2; 3923 if ( nLeft > nEnd ) 3924 break; 3925 if ( nLeft+nTempDashWidth > nEnd ) 3926 nTempDashWidth = nEnd-nLeft; 3927 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); 3928 nLeft += nDashWidth+nDotWidth; 3929 } 3930 } 3931 break; 3932 default: 3933 break; 3934 } 3935 } 3936 } 3937 3938 // ----------------------------------------------------------------------- 3939 3940 void OutputDevice::ImplDrawStrikeoutLine( long nBaseX, long nBaseY, 3941 long nDistX, long nDistY, long nWidth, 3942 FontStrikeout eStrikeout, 3943 Color aColor ) 3944 { 3945 ImplFontEntry* pFontEntry = mpFontEntry; 3946 long nLineHeight = 0; 3947 long nLinePos = 0; 3948 long nLinePos2 = 0; 3949 3950 long nY = nDistY; 3951 3952 if ( eStrikeout > STRIKEOUT_LAST ) 3953 eStrikeout = STRIKEOUT_SINGLE; 3954 3955 switch ( eStrikeout ) 3956 { 3957 case STRIKEOUT_SINGLE: 3958 nLineHeight = pFontEntry->maMetric.mnStrikeoutSize; 3959 nLinePos = nY + pFontEntry->maMetric.mnStrikeoutOffset; 3960 break; 3961 case STRIKEOUT_BOLD: 3962 nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize; 3963 nLinePos = nY + pFontEntry->maMetric.mnBStrikeoutOffset; 3964 break; 3965 case STRIKEOUT_DOUBLE: 3966 nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize; 3967 nLinePos = nY + pFontEntry->maMetric.mnDStrikeoutOffset1; 3968 nLinePos2 = nY + pFontEntry->maMetric.mnDStrikeoutOffset2; 3969 break; 3970 default: 3971 break; 3972 } 3973 3974 if ( nLineHeight ) 3975 { 3976 if ( mbLineColor || mbInitLineColor ) 3977 { 3978 mpGraphics->SetLineColor(); 3979 mbInitLineColor = sal_True; 3980 } 3981 mpGraphics->SetFillColor( ImplColorToSal( aColor ) ); 3982 mbInitFillColor = sal_True; 3983 3984 const long& nLeft = nDistX; 3985 3986 switch ( eStrikeout ) 3987 { 3988 case STRIKEOUT_SINGLE: 3989 case STRIKEOUT_BOLD: 3990 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); 3991 break; 3992 case STRIKEOUT_DOUBLE: 3993 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); 3994 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); 3995 break; 3996 default: 3997 break; 3998 } 3999 } 4000 } 4001 4002 // ----------------------------------------------------------------------- 4003 4004 void OutputDevice::ImplDrawStrikeoutChar( long nBaseX, long nBaseY, 4005 long nDistX, long nDistY, long nWidth, 4006 FontStrikeout eStrikeout, 4007 Color aColor ) 4008 { 4009 // PDF-export does its own strikeout drawing... why again? 4010 if( mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) 4011 return; 4012 4013 // prepare string for strikeout measurement 4014 static char cStrikeoutChar; 4015 if ( eStrikeout == STRIKEOUT_SLASH ) 4016 cStrikeoutChar = '/'; 4017 else // ( eStrikeout == STRIKEOUT_X ) 4018 cStrikeoutChar = 'X'; 4019 static const int nTestStrLen = 4; 4020 static const int nMaxStrikeStrLen = 2048; 4021 xub_Unicode aChars[ nMaxStrikeStrLen +1]; // +1 for valgrind... 4022 for( int i = 0; i < nTestStrLen; ++i) 4023 aChars[i] = cStrikeoutChar; 4024 const String aStrikeoutTest( aChars, nTestStrLen ); 4025 4026 // calculate approximation of strikeout atom size 4027 long nStrikeoutWidth = nWidth; 4028 SalLayout* pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen ); 4029 if( pLayout ) 4030 { 4031 nStrikeoutWidth = (pLayout->GetTextWidth() +nTestStrLen/2) / (nTestStrLen * pLayout->GetUnitsPerPixel()); 4032 pLayout->Release(); 4033 } 4034 if( nStrikeoutWidth <= 0 ) // sanity check 4035 return; 4036 4037 // calculate acceptable strikeout length 4038 // allow the strikeout to be one pixel larger than the text it strikes out 4039 long nMaxWidth = nStrikeoutWidth * 3 / 4; 4040 if ( nMaxWidth < 2 ) 4041 nMaxWidth = 2; 4042 nMaxWidth += nWidth + 1; 4043 4044 int nStrikeStrLen = (nMaxWidth - 1) / nStrikeoutWidth; 4045 // if the text width is smaller than the strikeout text, then do not 4046 // strike out at all. This case requires user interaction, e.g. adding 4047 // a space to the text 4048 if( nStrikeStrLen <= 0 ) 4049 return; 4050 if( nStrikeStrLen > nMaxStrikeStrLen ) 4051 nStrikeStrLen = nMaxStrikeStrLen; 4052 4053 // build the strikeout string 4054 for( int i = nTestStrLen; i < nStrikeStrLen; ++i) 4055 aChars[i] = cStrikeoutChar; 4056 const String aStrikeoutText( aChars, xub_StrLen(nStrikeStrLen) ); 4057 4058 if( mpFontEntry->mnOrientation ) 4059 ImplRotatePos( 0, 0, nDistX, nDistY, mpFontEntry->mnOrientation ); 4060 nBaseX += nDistX; 4061 nBaseY += nDistY; 4062 4063 // strikeout text has to be left aligned 4064 sal_uLong nOrigTLM = mnTextLayoutMode; 4065 mnTextLayoutMode = TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_COMPLEX_DISABLED; 4066 pLayout = ImplLayout( aStrikeoutText, 0, STRING_LEN ); 4067 mnTextLayoutMode = nOrigTLM; 4068 4069 if( !pLayout ) 4070 return; 4071 4072 // draw the strikeout text 4073 const Color aOldColor = GetTextColor(); 4074 SetTextColor( aColor ); 4075 ImplInitTextColor(); 4076 4077 pLayout->DrawBase() = Point( nBaseX+mnTextOffX, nBaseY+mnTextOffY ); 4078 pLayout->DrawText( *mpGraphics ); 4079 pLayout->Release(); 4080 4081 SetTextColor( aOldColor ); 4082 ImplInitTextColor(); 4083 } 4084 4085 // ----------------------------------------------------------------------- 4086 4087 void OutputDevice::ImplDrawTextLine( long nX, long nY, 4088 long nDistX, long nWidth, 4089 FontStrikeout eStrikeout, 4090 FontUnderline eUnderline, 4091 FontUnderline eOverline, 4092 sal_Bool bUnderlineAbove ) 4093 { 4094 if ( !nWidth ) 4095 return; 4096 4097 Color aStrikeoutColor = GetTextColor(); 4098 Color aUnderlineColor = GetTextLineColor(); 4099 Color aOverlineColor = GetOverlineColor(); 4100 sal_Bool bStrikeoutDone = sal_False; 4101 sal_Bool bUnderlineDone = sal_False; 4102 sal_Bool bOverlineDone = sal_False; 4103 4104 if ( IsRTLEnabled() ) 4105 { 4106 // --- RTL --- mirror at basex 4107 long nXAdd = nWidth - nDistX; 4108 if( mpFontEntry->mnOrientation ) 4109 nXAdd = FRound( nXAdd * cos( mpFontEntry->mnOrientation * F_PI1800 ) ); 4110 nX += nXAdd - 1; 4111 } 4112 4113 if ( !IsTextLineColor() ) 4114 aUnderlineColor = GetTextColor(); 4115 4116 if ( !IsOverlineColor() ) 4117 aOverlineColor = GetTextColor(); 4118 4119 if ( (eUnderline == UNDERLINE_SMALLWAVE) || 4120 (eUnderline == UNDERLINE_WAVE) || 4121 (eUnderline == UNDERLINE_DOUBLEWAVE) || 4122 (eUnderline == UNDERLINE_BOLDWAVE) ) 4123 { 4124 ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 4125 bUnderlineDone = sal_True; 4126 } 4127 if ( (eOverline == UNDERLINE_SMALLWAVE) || 4128 (eOverline == UNDERLINE_WAVE) || 4129 (eOverline == UNDERLINE_DOUBLEWAVE) || 4130 (eOverline == UNDERLINE_BOLDWAVE) ) 4131 { 4132 ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, sal_True ); 4133 bOverlineDone = sal_True; 4134 } 4135 4136 if ( (eStrikeout == STRIKEOUT_SLASH) || 4137 (eStrikeout == STRIKEOUT_X) ) 4138 { 4139 ImplDrawStrikeoutChar( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor ); 4140 bStrikeoutDone = sal_True; 4141 } 4142 4143 if ( !bUnderlineDone ) 4144 ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 4145 4146 if ( !bOverlineDone ) 4147 ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, sal_True ); 4148 4149 if ( !bStrikeoutDone ) 4150 ImplDrawStrikeoutLine( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor ); 4151 } 4152 4153 // ----------------------------------------------------------------------- 4154 4155 void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, 4156 FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, sal_Bool bWordLine, sal_Bool bUnderlineAbove ) 4157 { 4158 if( bWordLine ) 4159 { 4160 // draw everything relative to the layout base point 4161 const Point aStartPt = rSalLayout.DrawBase(); 4162 // calculate distance of each word from the base point 4163 Point aPos; 4164 sal_Int32 nDist = 0, nWidth = 0, nAdvance=0; 4165 for( int nStart = 0;;) 4166 { 4167 // iterate through the layouted glyphs 4168 sal_GlyphId aGlyphId; 4169 if( !rSalLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) ) 4170 break; 4171 4172 // calculate the boundaries of each word 4173 if( !rSalLayout.IsSpacingGlyph( aGlyphId ) ) 4174 { 4175 if( !nWidth ) 4176 { 4177 // get the distance to the base point (as projected to baseline) 4178 nDist = aPos.X() - aStartPt.X(); 4179 if( mpFontEntry->mnOrientation ) 4180 { 4181 const long nDY = aPos.Y() - aStartPt.Y(); 4182 const double fRad = mpFontEntry->mnOrientation * F_PI1800; 4183 nDist = FRound( nDist*cos(fRad) - nDY*sin(fRad) ); 4184 } 4185 } 4186 4187 // update the length of the textline 4188 nWidth += nAdvance; 4189 } 4190 else if( nWidth > 0 ) 4191 { 4192 // draw the textline for each word 4193 ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth, 4194 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 4195 nWidth = 0; 4196 } 4197 } 4198 4199 // draw textline for the last word 4200 if( nWidth > 0 ) 4201 { 4202 ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth, 4203 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 4204 } 4205 } 4206 else 4207 { 4208 Point aStartPt = rSalLayout.GetDrawPosition(); 4209 int nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel(); 4210 ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), 0, nWidth, 4211 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 4212 } 4213 } 4214 4215 // ----------------------------------------------------------------------- 4216 4217 void OutputDevice::ImplDrawMnemonicLine( long nX, long nY, long nWidth ) 4218 { 4219 long nBaseX = nX; 4220 if( /*ImplHasMirroredGraphics() &&*/ IsRTLEnabled() ) 4221 { 4222 // --- RTL --- 4223 // add some strange offset 4224 nX += 2; 4225 // revert the hack that will be done later in ImplDrawTextLine 4226 nX = nBaseX - nWidth - (nX - nBaseX - 1); 4227 } 4228 4229 ImplDrawTextLine( nX, nY, 0, nWidth, STRIKEOUT_NONE, UNDERLINE_SINGLE, UNDERLINE_NONE, sal_False ); 4230 } 4231 4232 // ----------------------------------------------------------------------- 4233 4234 void OutputDevice::ImplGetEmphasisMark( PolyPolygon& rPolyPoly, sal_Bool& rPolyLine, 4235 Rectangle& rRect1, Rectangle& rRect2, 4236 long& rYOff, long& rWidth, 4237 FontEmphasisMark eEmphasis, 4238 long nHeight, short /*nOrient*/ ) 4239 { 4240 static const sal_uInt8 aAccentPolyFlags[24] = 4241 { 4242 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 0, 2, 0, 2, 2 4243 }; 4244 4245 static const long aAccentPos[48] = 4246 { 4247 78, 0, 4248 348, 79, 4249 599, 235, 4250 843, 469, 4251 938, 574, 4252 990, 669, 4253 990, 773, 4254 990, 843, 4255 964, 895, 4256 921, 947, 4257 886, 982, 4258 860, 999, 4259 825, 999, 4260 764, 999, 4261 721, 964, 4262 686, 895, 4263 625, 791, 4264 556, 660, 4265 469, 504, 4266 400, 400, 4267 261, 252, 4268 61, 61, 4269 0, 27, 4270 9, 0 4271 }; 4272 4273 rWidth = 0; 4274 rYOff = 0; 4275 rPolyLine = sal_False; 4276 4277 if ( !nHeight ) 4278 return; 4279 4280 FontEmphasisMark nEmphasisStyle = eEmphasis & EMPHASISMARK_STYLE; 4281 long nDotSize = 0; 4282 switch ( nEmphasisStyle ) 4283 { 4284 case EMPHASISMARK_DOT: 4285 // Dot has 55% of the height 4286 nDotSize = (nHeight*550)/1000; 4287 if ( !nDotSize ) 4288 nDotSize = 1; 4289 if ( nDotSize <= 2 ) 4290 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) ); 4291 else 4292 { 4293 long nRad = nDotSize/2; 4294 Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); 4295 rPolyPoly.Insert( aPoly ); 4296 } 4297 rYOff = ((nHeight*250)/1000)/2; // Center to the anthoer EmphasisMarks 4298 rWidth = nDotSize; 4299 break; 4300 4301 case EMPHASISMARK_CIRCLE: 4302 // Dot has 80% of the height 4303 nDotSize = (nHeight*800)/1000; 4304 if ( !nDotSize ) 4305 nDotSize = 1; 4306 if ( nDotSize <= 2 ) 4307 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) ); 4308 else 4309 { 4310 long nRad = nDotSize/2; 4311 Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); 4312 rPolyPoly.Insert( aPoly ); 4313 // BorderWidth is 15% 4314 long nBorder = (nDotSize*150)/1000; 4315 if ( nBorder <= 1 ) 4316 rPolyLine = sal_True; 4317 else 4318 { 4319 Polygon aPoly2( Point( nRad, nRad ), 4320 nRad-nBorder, nRad-nBorder ); 4321 rPolyPoly.Insert( aPoly2 ); 4322 } 4323 } 4324 rWidth = nDotSize; 4325 break; 4326 4327 case EMPHASISMARK_DISC: 4328 // Dot has 80% of the height 4329 nDotSize = (nHeight*800)/1000; 4330 if ( !nDotSize ) 4331 nDotSize = 1; 4332 if ( nDotSize <= 2 ) 4333 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) ); 4334 else 4335 { 4336 long nRad = nDotSize/2; 4337 Polygon aPoly( Point( nRad, nRad ), nRad, nRad ); 4338 rPolyPoly.Insert( aPoly ); 4339 } 4340 rWidth = nDotSize; 4341 break; 4342 4343 case EMPHASISMARK_ACCENT: 4344 // Dot has 80% of the height 4345 nDotSize = (nHeight*800)/1000; 4346 if ( !nDotSize ) 4347 nDotSize = 1; 4348 if ( nDotSize <= 2 ) 4349 { 4350 if ( nDotSize == 1 ) 4351 { 4352 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) ); 4353 rWidth = nDotSize; 4354 } 4355 else 4356 { 4357 rRect1 = Rectangle( Point(), Size( 1, 1 ) ); 4358 rRect2 = Rectangle( Point( 1, 1 ), Size( 1, 1 ) ); 4359 } 4360 } 4361 else 4362 { 4363 Polygon aPoly( sizeof( aAccentPos ) / sizeof( long ) / 2, 4364 (const Point*)aAccentPos, 4365 aAccentPolyFlags ); 4366 double dScale = ((double)nDotSize)/1000.0; 4367 aPoly.Scale( dScale, dScale ); 4368 Polygon aTemp; 4369 aPoly.AdaptiveSubdivide( aTemp ); 4370 Rectangle aBoundRect = aTemp.GetBoundRect(); 4371 rWidth = aBoundRect.GetWidth(); 4372 nDotSize = aBoundRect.GetHeight(); 4373 rPolyPoly.Insert( aTemp ); 4374 } 4375 break; 4376 } 4377 4378 // calculate position 4379 long nOffY = 1+(mnDPIY/300); // one visible pixel space 4380 long nSpaceY = nHeight-nDotSize; 4381 if ( nSpaceY >= nOffY*2 ) 4382 rYOff += nOffY; 4383 if ( !(eEmphasis & EMPHASISMARK_POS_BELOW) ) 4384 rYOff += nDotSize; 4385 } 4386 4387 // ----------------------------------------------------------------------- 4388 4389 void OutputDevice::ImplDrawEmphasisMark( long nBaseX, long nX, long nY, 4390 const PolyPolygon& rPolyPoly, sal_Bool bPolyLine, 4391 const Rectangle& rRect1, const Rectangle& rRect2 ) 4392 { 4393 // TODO: pass nWidth as width of this mark 4394 long nWidth = 0; 4395 4396 if( IsRTLEnabled() ) 4397 // --- RTL --- mirror at basex 4398 nX = nBaseX - nWidth - (nX - nBaseX - 1); 4399 4400 nX -= mnOutOffX; 4401 nY -= mnOutOffY; 4402 4403 if ( rPolyPoly.Count() ) 4404 { 4405 if ( bPolyLine ) 4406 { 4407 Polygon aPoly = rPolyPoly.GetObject( 0 ); 4408 aPoly.Move( nX, nY ); 4409 DrawPolyLine( aPoly ); 4410 } 4411 else 4412 { 4413 PolyPolygon aPolyPoly = rPolyPoly; 4414 aPolyPoly.Move( nX, nY ); 4415 DrawPolyPolygon( aPolyPoly ); 4416 } 4417 } 4418 4419 if ( !rRect1.IsEmpty() ) 4420 { 4421 Rectangle aRect( Point( nX+rRect1.Left(), 4422 nY+rRect1.Top() ), rRect1.GetSize() ); 4423 DrawRect( aRect ); 4424 } 4425 4426 if ( !rRect2.IsEmpty() ) 4427 { 4428 Rectangle aRect( Point( nX+rRect2.Left(), 4429 nY+rRect2.Top() ), rRect2.GetSize() ); 4430 4431 DrawRect( aRect ); 4432 } 4433 } 4434 4435 // ----------------------------------------------------------------------- 4436 4437 void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout ) 4438 { 4439 Color aOldColor = GetTextColor(); 4440 Color aOldLineColor = GetLineColor(); 4441 Color aOldFillColor = GetFillColor(); 4442 sal_Bool bOldMap = mbMap; 4443 GDIMetaFile* pOldMetaFile = mpMetaFile; 4444 mpMetaFile = NULL; 4445 EnableMapMode( sal_False ); 4446 4447 FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont ); 4448 PolyPolygon aPolyPoly; 4449 Rectangle aRect1; 4450 Rectangle aRect2; 4451 long nEmphasisYOff; 4452 long nEmphasisWidth; 4453 long nEmphasisHeight; 4454 sal_Bool bPolyLine; 4455 4456 if ( nEmphasisMark & EMPHASISMARK_POS_BELOW ) 4457 nEmphasisHeight = mnEmphasisDescent; 4458 else 4459 nEmphasisHeight = mnEmphasisAscent; 4460 4461 ImplGetEmphasisMark( aPolyPoly, bPolyLine, 4462 aRect1, aRect2, 4463 nEmphasisYOff, nEmphasisWidth, 4464 nEmphasisMark, 4465 nEmphasisHeight, mpFontEntry->mnOrientation ); 4466 4467 if ( bPolyLine ) 4468 { 4469 SetLineColor( GetTextColor() ); 4470 SetFillColor(); 4471 } 4472 else 4473 { 4474 SetLineColor(); 4475 SetFillColor( GetTextColor() ); 4476 } 4477 4478 Point aOffset = Point(0,0); 4479 4480 if ( nEmphasisMark & EMPHASISMARK_POS_BELOW ) 4481 aOffset.Y() += mpFontEntry->maMetric.mnDescent + nEmphasisYOff; 4482 else 4483 aOffset.Y() -= mpFontEntry->maMetric.mnAscent + nEmphasisYOff; 4484 4485 long nEmphasisWidth2 = nEmphasisWidth / 2; 4486 long nEmphasisHeight2 = nEmphasisHeight / 2; 4487 aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 ); 4488 4489 Point aOutPoint; 4490 Rectangle aRectangle; 4491 for( int nStart = 0;;) 4492 { 4493 sal_GlyphId aGlyphId; 4494 if( !rSalLayout.GetNextGlyphs( 1, &aGlyphId, aOutPoint, nStart ) ) 4495 break; 4496 4497 if( !mpGraphics->GetGlyphBoundRect( aGlyphId, aRectangle ) ) 4498 continue; 4499 4500 if( !rSalLayout.IsSpacingGlyph( aGlyphId ) ) 4501 { 4502 Point aAdjPoint = aOffset; 4503 aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2; 4504 if ( mpFontEntry->mnOrientation ) 4505 ImplRotatePos( 0, 0, aAdjPoint.X(), aAdjPoint.Y(), mpFontEntry->mnOrientation ); 4506 aOutPoint += aAdjPoint; 4507 aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 ); 4508 ImplDrawEmphasisMark( rSalLayout.DrawBase().X(), 4509 aOutPoint.X(), aOutPoint.Y(), 4510 aPolyPoly, bPolyLine, aRect1, aRect2 ); 4511 } 4512 } 4513 4514 SetLineColor( aOldLineColor ); 4515 SetFillColor( aOldFillColor ); 4516 EnableMapMode( bOldMap ); 4517 mpMetaFile = pOldMetaFile; 4518 } 4519 4520 // ----------------------------------------------------------------------- 4521 4522 bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout ) 4523 { 4524 int nX = rSalLayout.DrawBase().X(); 4525 int nY = rSalLayout.DrawBase().Y(); 4526 4527 Rectangle aBoundRect; 4528 rSalLayout.DrawBase() = Point( 0, 0 ); 4529 rSalLayout.DrawOffset() = Point( 0, 0 ); 4530 if( !rSalLayout.GetBoundRect( *mpGraphics, aBoundRect ) ) 4531 { 4532 // guess vertical text extents if GetBoundRect failed 4533 int nRight = rSalLayout.GetTextWidth(); 4534 int nTop = mpFontEntry->maMetric.mnAscent + mnEmphasisAscent; 4535 long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; 4536 aBoundRect = Rectangle( 0, -nTop, nRight, nHeight - nTop ); 4537 } 4538 4539 // cache virtual device for rotation 4540 if ( !mpOutDevData ) 4541 ImplInitOutDevData(); 4542 if ( !mpOutDevData->mpRotateDev ) 4543 mpOutDevData->mpRotateDev = new VirtualDevice( *this, 1 ); 4544 VirtualDevice* pVDev = mpOutDevData->mpRotateDev; 4545 4546 // size it accordingly 4547 if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) ) 4548 return false; 4549 4550 Font aFont( GetFont() ); 4551 aFont.SetOrientation( 0 ); 4552 aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) ); 4553 pVDev->SetFont( aFont ); 4554 pVDev->SetTextColor( Color( COL_BLACK ) ); 4555 pVDev->SetTextFillColor(); 4556 pVDev->ImplNewFont(); 4557 pVDev->ImplInitFont(); 4558 pVDev->ImplInitTextColor(); 4559 4560 // draw text into upper left corner 4561 rSalLayout.DrawBase() -= aBoundRect.TopLeft(); 4562 rSalLayout.DrawText( *((OutputDevice*)pVDev)->mpGraphics ); 4563 4564 Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() ); 4565 if ( !aBmp || !aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) ) 4566 return false; 4567 4568 // calculate rotation offset 4569 Polygon aPoly( aBoundRect ); 4570 aPoly.Rotate( Point(), mpFontEntry->mnOwnOrientation ); 4571 Point aPoint = aPoly.GetBoundRect().TopLeft(); 4572 aPoint += Point( nX, nY ); 4573 4574 // mask output with text colored bitmap 4575 GDIMetaFile* pOldMetaFile = mpMetaFile; 4576 long nOldOffX = mnOutOffX; 4577 long nOldOffY = mnOutOffY; 4578 sal_Bool bOldMap = mbMap; 4579 4580 mnOutOffX = 0L; 4581 mnOutOffY = 0L; 4582 mpMetaFile = NULL; 4583 EnableMapMode( sal_False ); 4584 4585 DrawMask( aPoint, aBmp, GetTextColor() ); 4586 4587 EnableMapMode( bOldMap ); 4588 mnOutOffX = nOldOffX; 4589 mnOutOffY = nOldOffY; 4590 mpMetaFile = pOldMetaFile; 4591 4592 return true; 4593 } 4594 4595 // ----------------------------------------------------------------------- 4596 4597 void OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, sal_Bool bTextLines ) 4598 { 4599 if( mpFontEntry->mnOwnOrientation ) 4600 if( ImplDrawRotateText( rSalLayout ) ) 4601 return; 4602 4603 long nOldX = rSalLayout.DrawBase().X(); 4604 if( ! (mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) ) 4605 { 4606 if( ImplHasMirroredGraphics() ) 4607 { 4608 long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth(); 4609 long x = rSalLayout.DrawBase().X(); 4610 rSalLayout.DrawBase().X() = w - 1 - x; 4611 if( !IsRTLEnabled() ) 4612 { 4613 OutputDevice *pOutDevRef = (OutputDevice *)this; 4614 // mirror this window back 4615 long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX 4616 rSalLayout.DrawBase().X() = devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ; 4617 } 4618 } 4619 else if( IsRTLEnabled() ) 4620 { 4621 //long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth(); 4622 //long x = rSalLayout.DrawBase().X(); 4623 OutputDevice *pOutDevRef = (OutputDevice *)this; 4624 // mirror this window back 4625 long devX = pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX 4626 rSalLayout.DrawBase().X() = pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX; 4627 } 4628 4629 rSalLayout.DrawText( *mpGraphics ); 4630 } 4631 4632 rSalLayout.DrawBase().X() = nOldX; 4633 4634 if( bTextLines ) 4635 ImplDrawTextLines( rSalLayout, 4636 maFont.GetStrikeout(), maFont.GetUnderline(), maFont.GetOverline(), 4637 maFont.IsWordLineMode(), ImplIsUnderlineAbove( maFont ) ); 4638 4639 // emphasis marks 4640 if( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) 4641 ImplDrawEmphasisMarks( rSalLayout ); 4642 } 4643 4644 // ----------------------------------------------------------------------- 4645 4646 void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout ) 4647 { 4648 Color aOldColor = GetTextColor(); 4649 Color aOldTextLineColor = GetTextLineColor(); 4650 Color aOldOverlineColor = GetOverlineColor(); 4651 FontRelief eRelief = maFont.GetRelief(); 4652 4653 Point aOrigPos = rSalLayout.DrawBase(); 4654 if ( eRelief != RELIEF_NONE ) 4655 { 4656 Color aReliefColor( COL_LIGHTGRAY ); 4657 Color aTextColor( aOldColor ); 4658 4659 Color aTextLineColor( aOldTextLineColor ); 4660 Color aOverlineColor( aOldOverlineColor ); 4661 4662 // we don't have a automatic color, so black is always drawn on white 4663 if ( aTextColor.GetColor() == COL_BLACK ) 4664 aTextColor = Color( COL_WHITE ); 4665 if ( aTextLineColor.GetColor() == COL_BLACK ) 4666 aTextLineColor = Color( COL_WHITE ); 4667 if ( aOverlineColor.GetColor() == COL_BLACK ) 4668 aOverlineColor = Color( COL_WHITE ); 4669 4670 // relief-color is black for white text, in all other cases 4671 // we set this to LightGray 4672 if ( aTextColor.GetColor() == COL_WHITE ) 4673 aReliefColor = Color( COL_BLACK ); 4674 SetTextLineColor( aReliefColor ); 4675 SetOverlineColor( aReliefColor ); 4676 SetTextColor( aReliefColor ); 4677 ImplInitTextColor(); 4678 4679 // calculate offset - for high resolution printers the offset 4680 // should be greater so that the effect is visible 4681 long nOff = 1; 4682 nOff += mnDPIX/300; 4683 4684 if ( eRelief == RELIEF_ENGRAVED ) 4685 nOff = -nOff; 4686 rSalLayout.DrawOffset() += Point( nOff, nOff); 4687 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4688 rSalLayout.DrawOffset() -= Point( nOff, nOff); 4689 4690 SetTextLineColor( aTextLineColor ); 4691 SetOverlineColor( aOverlineColor ); 4692 SetTextColor( aTextColor ); 4693 ImplInitTextColor(); 4694 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4695 4696 SetTextLineColor( aOldTextLineColor ); 4697 SetOverlineColor( aOldOverlineColor ); 4698 4699 if ( aTextColor != aOldColor ) 4700 { 4701 SetTextColor( aOldColor ); 4702 ImplInitTextColor(); 4703 } 4704 } 4705 else 4706 { 4707 if ( maFont.IsShadow() ) 4708 { 4709 long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24); 4710 if ( maFont.IsOutline() ) 4711 nOff++; 4712 SetTextLineColor(); 4713 SetOverlineColor(); 4714 if ( (GetTextColor().GetColor() == COL_BLACK) 4715 || (GetTextColor().GetLuminance() < 8) ) 4716 SetTextColor( Color( COL_LIGHTGRAY ) ); 4717 else 4718 SetTextColor( Color( COL_BLACK ) ); 4719 ImplInitTextColor(); 4720 rSalLayout.DrawBase() += Point( nOff, nOff ); 4721 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4722 rSalLayout.DrawBase() -= Point( nOff, nOff ); 4723 SetTextColor( aOldColor ); 4724 SetTextLineColor( aOldTextLineColor ); 4725 SetOverlineColor( aOldOverlineColor ); 4726 ImplInitTextColor(); 4727 4728 if ( !maFont.IsOutline() ) 4729 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4730 } 4731 4732 if ( maFont.IsOutline() ) 4733 { 4734 rSalLayout.DrawBase() = aOrigPos + Point(-1,-1); 4735 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4736 rSalLayout.DrawBase() = aOrigPos + Point(+1,+1); 4737 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4738 rSalLayout.DrawBase() = aOrigPos + Point(-1,+0); 4739 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4740 rSalLayout.DrawBase() = aOrigPos + Point(-1,+1); 4741 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4742 rSalLayout.DrawBase() = aOrigPos + Point(+0,+1); 4743 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4744 rSalLayout.DrawBase() = aOrigPos + Point(+0,-1); 4745 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4746 rSalLayout.DrawBase() = aOrigPos + Point(+1,-1); 4747 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4748 rSalLayout.DrawBase() = aOrigPos + Point(+1,+0); 4749 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4750 rSalLayout.DrawBase() = aOrigPos; 4751 4752 SetTextColor( Color( COL_WHITE ) ); 4753 SetTextLineColor( Color( COL_WHITE ) ); 4754 SetOverlineColor( Color( COL_WHITE ) ); 4755 ImplInitTextColor(); 4756 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4757 SetTextColor( aOldColor ); 4758 SetTextLineColor( aOldTextLineColor ); 4759 SetOverlineColor( aOldOverlineColor ); 4760 ImplInitTextColor(); 4761 } 4762 } 4763 } 4764 4765 // ----------------------------------------------------------------------- 4766 4767 void OutputDevice::ImplDrawText( SalLayout& rSalLayout ) 4768 { 4769 if( mbInitClipRegion ) 4770 ImplInitClipRegion(); 4771 if( mbOutputClipped ) 4772 return; 4773 if( mbInitTextColor ) 4774 ImplInitTextColor(); 4775 4776 rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY ); 4777 4778 if( IsTextFillColor() ) 4779 ImplDrawTextBackground( rSalLayout ); 4780 4781 if( mbTextSpecial ) 4782 ImplDrawSpecialText( rSalLayout ); 4783 else 4784 ImplDrawTextDirect( rSalLayout, mbTextLines ); 4785 } 4786 4787 // ----------------------------------------------------------------------- 4788 4789 long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo, 4790 long nWidth, const XubString& rStr, 4791 sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout ) 4792 { 4793 DBG_ASSERTWARNING( nWidth >= 0, "ImplGetTextLines: nWidth <= 0!" ); 4794 4795 if ( nWidth <= 0 ) 4796 nWidth = 1; 4797 4798 long nMaxLineWidth = 0; 4799 rLineInfo.Clear(); 4800 if ( rStr.Len() && (nWidth > 0) ) 4801 { 4802 ::rtl::OUString aText( rStr ); 4803 uno::Reference < i18n::XBreakIterator > xBI; 4804 // get service provider 4805 uno::Reference< lang::XMultiServiceFactory > xSMgr( unohelper::GetMultiServiceFactory() ); 4806 4807 uno::Reference< linguistic2::XHyphenator > xHyph; 4808 if( xSMgr.is() ) 4809 { 4810 uno::Reference< linguistic2::XLinguServiceManager> xLinguMgr(xSMgr->createInstance(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.linguistic2.LinguServiceManager"))),uno::UNO_QUERY); 4811 if ( xLinguMgr.is() ) 4812 { 4813 xHyph = xLinguMgr->getHyphenator(); 4814 } 4815 } 4816 4817 i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, uno::Sequence <beans::PropertyValue>(), 1 ); 4818 i18n::LineBreakUserOptions aUserOptions; 4819 4820 xub_StrLen nPos = 0; 4821 xub_StrLen nLen = rStr.Len(); 4822 while ( nPos < nLen ) 4823 { 4824 xub_StrLen nBreakPos = nPos; 4825 4826 while ( ( nBreakPos < nLen ) && ( rStr.GetChar( nBreakPos ) != _CR ) && ( rStr.GetChar( nBreakPos ) != _LF ) ) 4827 nBreakPos++; 4828 4829 long nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); 4830 if ( ( nLineWidth > nWidth ) && ( nStyle & TEXT_DRAW_WORDBREAK ) ) 4831 { 4832 if ( !xBI.is() ) 4833 xBI = vcl::unohelper::CreateBreakIterator(); 4834 4835 if ( xBI.is() ) 4836 { 4837 const com::sun::star::lang::Locale& rDefLocale(Application::GetSettings().GetUILocale()); 4838 xub_StrLen nSoftBreak = _rLayout.GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos ); 4839 DBG_ASSERT( nSoftBreak < nBreakPos, "Break?!" ); 4840 //aHyphOptions.hyphenIndex = nSoftBreak; 4841 i18n::LineBreakResults aLBR = xBI->getLineBreak( aText, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions ); 4842 nBreakPos = (xub_StrLen)aLBR.breakIndex; 4843 if ( nBreakPos <= nPos ) 4844 nBreakPos = nSoftBreak; 4845 if ( (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION) == TEXT_DRAW_WORDBREAK_HYPHENATION ) 4846 { 4847 // Egal ob Trenner oder nicht: Das Wort nach dem Trenner durch 4848 // die Silbentrennung jagen... 4849 // nMaxBreakPos ist das letzte Zeichen was in die Zeile passt, 4850 // nBreakPos ist der Wort-Anfang 4851 // Ein Problem gibt es, wenn das Dok so schmal ist, dass ein Wort 4852 // auf mehr als Zwei Zeilen gebrochen wird... 4853 if ( xHyph.is() ) 4854 { 4855 sal_Unicode cAlternateReplChar = 0; 4856 sal_Unicode cAlternateExtraChar = 0; 4857 i18n::Boundary aBoundary = xBI->getWordBoundary( aText, nBreakPos, rDefLocale, ::com::sun::star::i18n::WordType::DICTIONARY_WORD, sal_True ); 4858 // sal_uInt16 nWordStart = nBreakPos; 4859 // sal_uInt16 nBreakPos_OLD = nBreakPos; 4860 sal_uInt16 nWordStart = nPos; 4861 sal_uInt16 nWordEnd = (sal_uInt16) aBoundary.endPos; 4862 DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" ); 4863 4864 sal_uInt16 nWordLen = nWordEnd - nWordStart; 4865 if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) ) 4866 { 4867 // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD 4868 // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" ); 4869 String aWord( aText, nWordStart, nWordLen ); 4870 sal_uInt16 nMinTrail = static_cast<sal_uInt16>(nWordEnd-nSoftBreak+1); //+1: Vor dem angeknacksten Buchstaben 4871 uno::Reference< linguistic2::XHyphenatedWord > xHyphWord; 4872 if (xHyph.is()) 4873 xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.Len() - nMinTrail, uno::Sequence< beans::PropertyValue >() ); 4874 if (xHyphWord.is()) 4875 { 4876 sal_Bool bAlternate = xHyphWord->isAlternativeSpelling(); 4877 sal_uInt16 _nWordLen = 1 + xHyphWord->getHyphenPos(); 4878 4879 if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= ( 2 ) ) ) 4880 { 4881 if ( !bAlternate ) 4882 { 4883 nBreakPos = nWordStart + _nWordLen; 4884 } 4885 else 4886 { 4887 String aAlt( xHyphWord->getHyphenatedWord() ); 4888 4889 // Wir gehen von zwei Faellen aus, die nun 4890 // vorliegen koennen: 4891 // 1) packen wird zu pak-ken 4892 // 2) Schiffahrt wird zu Schiff-fahrt 4893 // In Fall 1 muss ein Zeichen ersetzt werden, 4894 // in Fall 2 wird ein Zeichen hinzugefuegt. 4895 // Die Identifikation wird erschwert durch Worte wie 4896 // "Schiffahrtsbrennesseln", da der Hyphenator alle 4897 // Position des Wortes auftrennt und "Schifffahrtsbrennnesseln" 4898 // ermittelt. Wir koennen also eigentlich nicht unmittelbar vom 4899 // Index des AlternativWord auf aWord schliessen. 4900 4901 // Das ganze geraffel wird durch eine Funktion am 4902 // Hyphenator vereinfacht werden, sobald AMA sie einbaut... 4903 sal_uInt16 nAltStart = _nWordLen - 1; 4904 sal_uInt16 nTxtStart = nAltStart - (aAlt.Len() - aWord.Len()); 4905 sal_uInt16 nTxtEnd = nTxtStart; 4906 sal_uInt16 nAltEnd = nAltStart; 4907 4908 // Die Bereiche zwischen den nStart und nEnd ist 4909 // die Differenz zwischen Alternativ- und OriginalString. 4910 while( nTxtEnd < aWord.Len() && nAltEnd < aAlt.Len() && 4911 aWord.GetChar(nTxtEnd) != aAlt.GetChar(nAltEnd) ) 4912 { 4913 ++nTxtEnd; 4914 ++nAltEnd; 4915 } 4916 4917 // Wenn ein Zeichen hinzugekommen ist, dann bemerken wir es jetzt: 4918 if( nAltEnd > nTxtEnd && nAltStart == nAltEnd && 4919 aWord.GetChar( nTxtEnd ) == aAlt.GetChar(nAltEnd) ) 4920 { 4921 ++nAltEnd; 4922 ++nTxtStart; 4923 ++nTxtEnd; 4924 } 4925 4926 DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Falsche Annahme!" ); 4927 4928 if ( nTxtEnd > nTxtStart ) 4929 cAlternateReplChar = aAlt.GetChar( nAltStart ); 4930 else 4931 cAlternateExtraChar = aAlt.GetChar( nAltStart ); 4932 4933 nBreakPos = nWordStart + nTxtStart; 4934 if ( cAlternateReplChar ) 4935 nBreakPos++; 4936 } 4937 } // if (xHyphWord.is()) 4938 } // if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) ) 4939 } // if ( xHyph.is() ) 4940 } // if ( (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION) == TEXT_DRAW_WORDBREAK_HYPHENATION ) 4941 } 4942 nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); 4943 } 4944 else 4945 { 4946 // fallback to something really simple 4947 sal_uInt16 nSpacePos = STRING_LEN; 4948 long nW = 0; 4949 do 4950 { 4951 nSpacePos = rStr.SearchBackward( sal_Unicode(' '), nSpacePos ); 4952 if( nSpacePos != STRING_NOTFOUND ) 4953 { 4954 if( nSpacePos > nPos ) 4955 nSpacePos--; 4956 nW = _rLayout.GetTextWidth( rStr, nPos, nSpacePos-nPos ); 4957 } 4958 } while( nW > nWidth ); 4959 4960 if( nSpacePos != STRING_NOTFOUND ) 4961 { 4962 nBreakPos = nSpacePos; 4963 nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos ); 4964 if( nBreakPos < rStr.Len()-1 ) 4965 nBreakPos++; 4966 } 4967 } 4968 } 4969 4970 if ( nLineWidth > nMaxLineWidth ) 4971 nMaxLineWidth = nLineWidth; 4972 4973 rLineInfo.AddLine( new ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) ); 4974 4975 if ( nBreakPos == nPos ) 4976 nBreakPos++; 4977 nPos = nBreakPos; 4978 4979 if ( ( rStr.GetChar( nPos ) == _CR ) || ( rStr.GetChar( nPos ) == _LF ) ) 4980 { 4981 nPos++; 4982 // CR/LF? 4983 if ( ( nPos < nLen ) && ( rStr.GetChar( nPos ) == _LF ) && ( rStr.GetChar( nPos-1 ) == _CR ) ) 4984 nPos++; 4985 } 4986 } 4987 } 4988 #ifdef DBG_UTIL 4989 for ( sal_uInt16 nL = 0; nL < rLineInfo.Count(); nL++ ) 4990 { 4991 ImplTextLineInfo* pLine = rLineInfo.GetLine( nL ); 4992 String aLine( rStr, pLine->GetIndex(), pLine->GetLen() ); 4993 DBG_ASSERT( aLine.Search( _CR ) == STRING_NOTFOUND, "ImplGetTextLines - Found CR!" ); 4994 DBG_ASSERT( aLine.Search( _LF ) == STRING_NOTFOUND, "ImplGetTextLines - Found LF!" ); 4995 } 4996 #endif 4997 4998 return nMaxLineWidth; 4999 } 5000 5001 // ======================================================================= 5002 5003 void OutputDevice::SetAntialiasing( sal_uInt16 nMode ) 5004 { 5005 if ( mnAntialiasing != nMode ) 5006 { 5007 mnAntialiasing = nMode; 5008 mbInitFont = sal_True; 5009 5010 if(mpGraphics) 5011 { 5012 mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW); 5013 } 5014 } 5015 5016 if( mpAlphaVDev ) 5017 mpAlphaVDev->SetAntialiasing( nMode ); 5018 } 5019 5020 // ----------------------------------------------------------------------- 5021 5022 void OutputDevice::SetFont( const Font& rNewFont ) 5023 { 5024 DBG_TRACE( "OutputDevice::SetFont()" ); 5025 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5026 DBG_CHKOBJ( &rNewFont, Font, NULL ); 5027 5028 Font aFont( rNewFont ); 5029 aFont.SetLanguage(rNewFont.GetLanguage()); 5030 if ( mnDrawMode & (DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | DRAWMODE_SETTINGSTEXT | 5031 DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | DRAWMODE_GRAYFILL | DRAWMODE_NOFILL | 5032 DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) ) 5033 { 5034 Color aTextColor( aFont.GetColor() ); 5035 5036 if ( mnDrawMode & DRAWMODE_BLACKTEXT ) 5037 aTextColor = Color( COL_BLACK ); 5038 else if ( mnDrawMode & DRAWMODE_WHITETEXT ) 5039 aTextColor = Color( COL_WHITE ); 5040 else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) 5041 { 5042 const sal_uInt8 cLum = aTextColor.GetLuminance(); 5043 aTextColor = Color( cLum, cLum, cLum ); 5044 } 5045 else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT ) 5046 aTextColor = GetSettings().GetStyleSettings().GetFontColor(); 5047 5048 if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT ) 5049 { 5050 aTextColor = Color( (aTextColor.GetRed() >> 1 ) | 0x80, 5051 (aTextColor.GetGreen() >> 1 ) | 0x80, 5052 (aTextColor.GetBlue() >> 1 ) | 0x80 ); 5053 } 5054 5055 aFont.SetColor( aTextColor ); 5056 5057 sal_Bool bTransFill = aFont.IsTransparent(); 5058 if ( !bTransFill ) 5059 { 5060 Color aTextFillColor( aFont.GetFillColor() ); 5061 5062 if ( mnDrawMode & DRAWMODE_BLACKFILL ) 5063 aTextFillColor = Color( COL_BLACK ); 5064 else if ( mnDrawMode & DRAWMODE_WHITEFILL ) 5065 aTextFillColor = Color( COL_WHITE ); 5066 else if ( mnDrawMode & DRAWMODE_GRAYFILL ) 5067 { 5068 const sal_uInt8 cLum = aTextFillColor.GetLuminance(); 5069 aTextFillColor = Color( cLum, cLum, cLum ); 5070 } 5071 else if( mnDrawMode & DRAWMODE_SETTINGSFILL ) 5072 aTextFillColor = GetSettings().GetStyleSettings().GetWindowColor(); 5073 else if ( mnDrawMode & DRAWMODE_NOFILL ) 5074 { 5075 aTextFillColor = Color( COL_TRANSPARENT ); 5076 bTransFill = sal_True; 5077 } 5078 5079 if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) ) 5080 { 5081 aTextFillColor = Color( (aTextFillColor.GetRed() >> 1) | 0x80, 5082 (aTextFillColor.GetGreen() >> 1) | 0x80, 5083 (aTextFillColor.GetBlue() >> 1) | 0x80 ); 5084 } 5085 5086 aFont.SetFillColor( aTextFillColor ); 5087 } 5088 } 5089 5090 if ( mpMetaFile ) 5091 { 5092 mpMetaFile->AddAction( new MetaFontAction( aFont ) ); 5093 // the color and alignment actions don't belong here 5094 // TODO: get rid of them without breaking anything... 5095 mpMetaFile->AddAction( new MetaTextAlignAction( aFont.GetAlign() ) ); 5096 mpMetaFile->AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) ); 5097 } 5098 5099 #if (OSL_DEBUG_LEVEL > 2) || defined (HDU_DEBUG) 5100 fprintf( stderr, " OutputDevice::SetFont( name=\"%s\", h=%ld)\n", 5101 OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr(), 5102 aFont.GetSize().Height() ); 5103 #endif 5104 5105 if ( !maFont.IsSameInstance( aFont ) ) 5106 { 5107 // Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color, 5108 // because SetTextColor() is used for this. 5109 // #i28759# maTextColor might have been changed behind our back, commit then, too. 5110 if( aFont.GetColor() != COL_TRANSPARENT 5111 && (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) ) 5112 { 5113 maTextColor = aFont.GetColor(); 5114 mbInitTextColor = sal_True; 5115 if( mpMetaFile ) 5116 mpMetaFile->AddAction( new MetaTextColorAction( aFont.GetColor() ) ); 5117 } 5118 maFont = aFont; 5119 mbNewFont = sal_True; 5120 5121 if( mpAlphaVDev ) 5122 { 5123 // #i30463# 5124 // Since SetFont might change the text color, apply that only 5125 // selectively to alpha vdev (which normally paints opaque text 5126 // with COL_BLACK) 5127 if( aFont.GetColor() != COL_TRANSPARENT ) 5128 { 5129 mpAlphaVDev->SetTextColor( COL_BLACK ); 5130 aFont.SetColor( COL_TRANSPARENT ); 5131 } 5132 5133 mpAlphaVDev->SetFont( aFont ); 5134 } 5135 } 5136 } 5137 5138 // ----------------------------------------------------------------------- 5139 5140 void OutputDevice::SetLayoutMode( sal_uLong nTextLayoutMode ) 5141 { 5142 DBG_TRACE( "OutputDevice::SetTextLayoutMode()" ); 5143 5144 if( mpMetaFile ) 5145 mpMetaFile->AddAction( new MetaLayoutModeAction( nTextLayoutMode ) ); 5146 5147 mnTextLayoutMode = nTextLayoutMode; 5148 5149 if( mpAlphaVDev ) 5150 mpAlphaVDev->SetLayoutMode( nTextLayoutMode ); 5151 } 5152 5153 // ----------------------------------------------------------------------- 5154 5155 void OutputDevice::SetDigitLanguage( LanguageType eTextLanguage ) 5156 { 5157 DBG_TRACE( "OutputDevice::SetTextLanguage()" ); 5158 5159 if( mpMetaFile ) 5160 mpMetaFile->AddAction( new MetaTextLanguageAction( eTextLanguage ) ); 5161 5162 meTextLanguage = eTextLanguage; 5163 5164 if( mpAlphaVDev ) 5165 mpAlphaVDev->SetDigitLanguage( eTextLanguage ); 5166 } 5167 5168 // ----------------------------------------------------------------------- 5169 5170 void OutputDevice::SetTextColor( const Color& rColor ) 5171 { 5172 DBG_TRACE( "OutputDevice::SetTextColor()" ); 5173 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5174 5175 Color aColor( rColor ); 5176 5177 if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | 5178 DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | 5179 DRAWMODE_SETTINGSTEXT ) ) 5180 { 5181 if ( mnDrawMode & DRAWMODE_BLACKTEXT ) 5182 aColor = Color( COL_BLACK ); 5183 else if ( mnDrawMode & DRAWMODE_WHITETEXT ) 5184 aColor = Color( COL_WHITE ); 5185 else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) 5186 { 5187 const sal_uInt8 cLum = aColor.GetLuminance(); 5188 aColor = Color( cLum, cLum, cLum ); 5189 } 5190 else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT ) 5191 aColor = GetSettings().GetStyleSettings().GetFontColor(); 5192 5193 if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT ) 5194 { 5195 aColor = Color( (aColor.GetRed() >> 1) | 0x80, 5196 (aColor.GetGreen() >> 1) | 0x80, 5197 (aColor.GetBlue() >> 1) | 0x80 ); 5198 } 5199 } 5200 5201 if ( mpMetaFile ) 5202 mpMetaFile->AddAction( new MetaTextColorAction( aColor ) ); 5203 5204 if ( maTextColor != aColor ) 5205 { 5206 maTextColor = aColor; 5207 mbInitTextColor = sal_True; 5208 } 5209 5210 if( mpAlphaVDev ) 5211 mpAlphaVDev->SetTextColor( COL_BLACK ); 5212 } 5213 5214 // ----------------------------------------------------------------------- 5215 5216 void OutputDevice::SetTextFillColor() 5217 { 5218 DBG_TRACE( "OutputDevice::SetTextFillColor()" ); 5219 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5220 5221 if ( mpMetaFile ) 5222 mpMetaFile->AddAction( new MetaTextFillColorAction( Color(), sal_False ) ); 5223 5224 if ( maFont.GetColor() != Color( COL_TRANSPARENT ) ) 5225 maFont.SetFillColor( Color( COL_TRANSPARENT ) ); 5226 if ( !maFont.IsTransparent() ) 5227 maFont.SetTransparent( sal_True ); 5228 5229 if( mpAlphaVDev ) 5230 mpAlphaVDev->SetTextFillColor(); 5231 } 5232 5233 // ----------------------------------------------------------------------- 5234 5235 void OutputDevice::SetTextFillColor( const Color& rColor ) 5236 { 5237 DBG_TRACE( "OutputDevice::SetTextFillColor()" ); 5238 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5239 5240 Color aColor( rColor ); 5241 sal_Bool bTransFill = ImplIsColorTransparent( aColor ) ? sal_True : sal_False; 5242 5243 if ( !bTransFill ) 5244 { 5245 if ( mnDrawMode & ( DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | 5246 DRAWMODE_GRAYFILL | DRAWMODE_NOFILL | 5247 DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) ) 5248 { 5249 if ( mnDrawMode & DRAWMODE_BLACKFILL ) 5250 aColor = Color( COL_BLACK ); 5251 else if ( mnDrawMode & DRAWMODE_WHITEFILL ) 5252 aColor = Color( COL_WHITE ); 5253 else if ( mnDrawMode & DRAWMODE_GRAYFILL ) 5254 { 5255 const sal_uInt8 cLum = aColor.GetLuminance(); 5256 aColor = Color( cLum, cLum, cLum ); 5257 } 5258 else if( mnDrawMode & DRAWMODE_SETTINGSFILL ) 5259 aColor = GetSettings().GetStyleSettings().GetWindowColor(); 5260 else if ( mnDrawMode & DRAWMODE_NOFILL ) 5261 { 5262 aColor = Color( COL_TRANSPARENT ); 5263 bTransFill = sal_True; 5264 } 5265 5266 if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) ) 5267 { 5268 aColor = Color( (aColor.GetRed() >> 1) | 0x80, 5269 (aColor.GetGreen() >> 1) | 0x80, 5270 (aColor.GetBlue() >> 1) | 0x80 ); 5271 } 5272 } 5273 } 5274 5275 if ( mpMetaFile ) 5276 mpMetaFile->AddAction( new MetaTextFillColorAction( aColor, sal_True ) ); 5277 5278 if ( maFont.GetFillColor() != aColor ) 5279 maFont.SetFillColor( aColor ); 5280 if ( maFont.IsTransparent() != bTransFill ) 5281 maFont.SetTransparent( bTransFill ); 5282 5283 if( mpAlphaVDev ) 5284 mpAlphaVDev->SetTextFillColor( COL_BLACK ); 5285 } 5286 5287 // ----------------------------------------------------------------------- 5288 5289 Color OutputDevice::GetTextFillColor() const 5290 { 5291 if ( maFont.IsTransparent() ) 5292 return Color( COL_TRANSPARENT ); 5293 else 5294 return maFont.GetFillColor(); 5295 } 5296 5297 // ----------------------------------------------------------------------- 5298 5299 void OutputDevice::SetTextLineColor() 5300 { 5301 DBG_TRACE( "OutputDevice::SetTextLineColor()" ); 5302 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5303 5304 if ( mpMetaFile ) 5305 mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), sal_False ) ); 5306 5307 maTextLineColor = Color( COL_TRANSPARENT ); 5308 5309 if( mpAlphaVDev ) 5310 mpAlphaVDev->SetTextLineColor(); 5311 } 5312 5313 // ----------------------------------------------------------------------- 5314 5315 void OutputDevice::SetTextLineColor( const Color& rColor ) 5316 { 5317 DBG_TRACE( "OutputDevice::SetTextLineColor()" ); 5318 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5319 5320 Color aColor( rColor ); 5321 5322 if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | 5323 DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | 5324 DRAWMODE_SETTINGSTEXT ) ) 5325 { 5326 if ( mnDrawMode & DRAWMODE_BLACKTEXT ) 5327 aColor = Color( COL_BLACK ); 5328 else if ( mnDrawMode & DRAWMODE_WHITETEXT ) 5329 aColor = Color( COL_WHITE ); 5330 else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) 5331 { 5332 const sal_uInt8 cLum = aColor.GetLuminance(); 5333 aColor = Color( cLum, cLum, cLum ); 5334 } 5335 else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT ) 5336 aColor = GetSettings().GetStyleSettings().GetFontColor(); 5337 5338 if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT) 5339 && (aColor.GetColor() != COL_TRANSPARENT) ) 5340 { 5341 aColor = Color( (aColor.GetRed() >> 1) | 0x80, 5342 (aColor.GetGreen() >> 1) | 0x80, 5343 (aColor.GetBlue() >> 1) | 0x80 ); 5344 } 5345 } 5346 5347 if ( mpMetaFile ) 5348 mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, sal_True ) ); 5349 5350 maTextLineColor = aColor; 5351 5352 if( mpAlphaVDev ) 5353 mpAlphaVDev->SetTextLineColor( COL_BLACK ); 5354 } 5355 5356 // ----------------------------------------------------------------------- 5357 5358 void OutputDevice::SetOverlineColor() 5359 { 5360 DBG_TRACE( "OutputDevice::SetOverlineColor()" ); 5361 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5362 5363 if ( mpMetaFile ) 5364 mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), sal_False ) ); 5365 5366 maOverlineColor = Color( COL_TRANSPARENT ); 5367 5368 if( mpAlphaVDev ) 5369 mpAlphaVDev->SetOverlineColor(); 5370 } 5371 5372 // ----------------------------------------------------------------------- 5373 5374 void OutputDevice::SetOverlineColor( const Color& rColor ) 5375 { 5376 DBG_TRACE( "OutputDevice::SetOverlineColor()" ); 5377 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5378 5379 Color aColor( rColor ); 5380 5381 if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | 5382 DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | 5383 DRAWMODE_SETTINGSTEXT ) ) 5384 { 5385 if ( mnDrawMode & DRAWMODE_BLACKTEXT ) 5386 aColor = Color( COL_BLACK ); 5387 else if ( mnDrawMode & DRAWMODE_WHITETEXT ) 5388 aColor = Color( COL_WHITE ); 5389 else if ( mnDrawMode & DRAWMODE_GRAYTEXT ) 5390 { 5391 const sal_uInt8 cLum = aColor.GetLuminance(); 5392 aColor = Color( cLum, cLum, cLum ); 5393 } 5394 else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT ) 5395 aColor = GetSettings().GetStyleSettings().GetFontColor(); 5396 5397 if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT) 5398 && (aColor.GetColor() != COL_TRANSPARENT) ) 5399 { 5400 aColor = Color( (aColor.GetRed() >> 1) | 0x80, 5401 (aColor.GetGreen() >> 1) | 0x80, 5402 (aColor.GetBlue() >> 1) | 0x80 ); 5403 } 5404 } 5405 5406 if ( mpMetaFile ) 5407 mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, sal_True ) ); 5408 5409 maOverlineColor = aColor; 5410 5411 if( mpAlphaVDev ) 5412 mpAlphaVDev->SetOverlineColor( COL_BLACK ); 5413 } 5414 5415 // ----------------------------------------------------------------------- 5416 5417 5418 void OutputDevice::SetTextAlign( TextAlign eAlign ) 5419 { 5420 DBG_TRACE( "OutputDevice::SetTextAlign()" ); 5421 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5422 5423 if ( mpMetaFile ) 5424 mpMetaFile->AddAction( new MetaTextAlignAction( eAlign ) ); 5425 5426 if ( maFont.GetAlign() != eAlign ) 5427 { 5428 maFont.SetAlign( eAlign ); 5429 mbNewFont = sal_True; 5430 } 5431 5432 if( mpAlphaVDev ) 5433 mpAlphaVDev->SetTextAlign( eAlign ); 5434 } 5435 5436 // ----------------------------------------------------------------------- 5437 5438 void OutputDevice::DrawTextLine( const Point& rPos, long nWidth, 5439 FontStrikeout eStrikeout, 5440 FontUnderline eUnderline, 5441 FontUnderline eOverline, 5442 sal_Bool bUnderlineAbove ) 5443 { 5444 DBG_TRACE( "OutputDevice::DrawTextLine()" ); 5445 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5446 5447 if ( mpMetaFile ) 5448 mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) ); 5449 5450 if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) && 5451 ((eOverline == UNDERLINE_NONE) || (eOverline == UNDERLINE_DONTKNOW)) && 5452 ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) ) 5453 return; 5454 5455 if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) 5456 return; 5457 5458 // we need a graphics 5459 if( !mpGraphics && !ImplGetGraphics() ) 5460 return; 5461 if( mbInitClipRegion ) 5462 ImplInitClipRegion(); 5463 if( mbOutputClipped ) 5464 return; 5465 5466 // initialize font if needed to get text offsets 5467 // TODO: only needed for mnTextOff!=(0,0) 5468 if( mbNewFont ) 5469 if( !ImplNewFont() ) 5470 return; 5471 if( mbInitFont ) 5472 ImplInitFont(); 5473 5474 Point aPos = ImplLogicToDevicePixel( rPos ); 5475 nWidth = ImplLogicWidthToDevicePixel( nWidth ); 5476 aPos += Point( mnTextOffX, mnTextOffY ); 5477 ImplDrawTextLine( aPos.X(), aPos.X(), 0, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 5478 5479 if( mpAlphaVDev ) 5480 mpAlphaVDev->DrawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 5481 } 5482 5483 // ------------------------------------------------------------------------ 5484 5485 sal_Bool OutputDevice::IsTextUnderlineAbove( const Font& rFont ) 5486 { 5487 return ImplIsUnderlineAbove( rFont ); 5488 } 5489 5490 // ------------------------------------------------------------------------ 5491 5492 void OutputDevice::DrawWaveLine( const Point& rStartPos, const Point& rEndPos, 5493 sal_uInt16 nStyle ) 5494 { 5495 DBG_TRACE( "OutputDevice::DrawWaveLine()" ); 5496 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5497 5498 if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) 5499 return; 5500 5501 // we need a graphics 5502 if( !mpGraphics ) 5503 if( !ImplGetGraphics() ) 5504 return; 5505 5506 if ( mbInitClipRegion ) 5507 ImplInitClipRegion(); 5508 if ( mbOutputClipped ) 5509 return; 5510 5511 if( mbNewFont ) 5512 if( !ImplNewFont() ) 5513 return; 5514 5515 Point aStartPt = ImplLogicToDevicePixel( rStartPos ); 5516 Point aEndPt = ImplLogicToDevicePixel( rEndPos ); 5517 long nStartX = aStartPt.X(); 5518 long nStartY = aStartPt.Y(); 5519 long nEndX = aEndPt.X(); 5520 long nEndY = aEndPt.Y(); 5521 short nOrientation = 0; 5522 5523 // when rotated 5524 if ( (nStartY != nEndY) || (nStartX > nEndX) ) 5525 { 5526 long nDX = nEndX - nStartX; 5527 double nO = atan2( -nEndY + nStartY, ((nDX == 0L) ? 0.000000001 : nDX) ); 5528 nO /= F_PI1800; 5529 nOrientation = (short)nO; 5530 ImplRotatePos( nStartX, nStartY, nEndX, nEndY, -nOrientation ); 5531 } 5532 5533 long nWaveHeight; 5534 if ( nStyle == WAVE_NORMAL ) 5535 { 5536 nWaveHeight = 3; 5537 nStartY++; 5538 nEndY++; 5539 } 5540 else if( nStyle == WAVE_SMALL ) 5541 { 5542 nWaveHeight = 2; 5543 nStartY++; 5544 nEndY++; 5545 } 5546 else // WAVE_FLAT 5547 nWaveHeight = 1; 5548 5549 // #109280# make sure the waveline does not exceed the descent to avoid paint problems 5550 ImplFontEntry* pFontEntry = mpFontEntry; 5551 if( nWaveHeight > pFontEntry->maMetric.mnWUnderlineSize ) 5552 nWaveHeight = pFontEntry->maMetric.mnWUnderlineSize; 5553 5554 ImplDrawWaveLine( nStartX, nStartY, 0, 0, 5555 nEndX-nStartX, nWaveHeight, 1, 5556 nOrientation, GetLineColor() ); 5557 if( mpAlphaVDev ) 5558 mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos, nStyle ); 5559 } 5560 5561 // ----------------------------------------------------------------------- 5562 5563 void OutputDevice::DrawText( const Point& rStartPt, const String& rStr, 5564 xub_StrLen nIndex, xub_StrLen nLen, 5565 MetricVector* pVector, String* pDisplayText 5566 ) 5567 { 5568 if( mpOutDevData && mpOutDevData->mpRecordLayout ) 5569 { 5570 pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects; 5571 pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText; 5572 } 5573 5574 DBG_TRACE( "OutputDevice::DrawText()" ); 5575 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5576 5577 #if OSL_DEBUG_LEVEL > 2 5578 fprintf( stderr, " OutputDevice::DrawText(\"%s\")\n", 5579 OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ).getStr() ); 5580 #endif 5581 5582 if ( mpMetaFile ) 5583 mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) ); 5584 if( pVector ) 5585 { 5586 Region aClip( GetClipRegion() ); 5587 if( meOutDevType == OUTDEV_WINDOW ) 5588 aClip.Intersect( Rectangle( Point(), GetOutputSize() ) ); 5589 if( mpOutDevData && mpOutDevData->mpRecordLayout ) 5590 { 5591 mpOutDevData->mpRecordLayout->m_aLineIndices.push_back( mpOutDevData->mpRecordLayout->m_aDisplayText.Len() ); 5592 aClip.Intersect( mpOutDevData->maRecordRect ); 5593 } 5594 if( ! aClip.IsNull() ) 5595 { 5596 MetricVector aTmp; 5597 GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, aTmp ); 5598 5599 bool bInserted = false; 5600 for( MetricVector::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ ) 5601 { 5602 bool bAppend = false; 5603 5604 if( aClip.IsOver( *it ) ) 5605 bAppend = true; 5606 else if( rStr.GetChar( nIndex ) == ' ' && bInserted ) 5607 { 5608 MetricVector::const_iterator next = it; 5609 ++next; 5610 if( next != aTmp.end() && aClip.IsOver( *next ) ) 5611 bAppend = true; 5612 } 5613 5614 if( bAppend ) 5615 { 5616 pVector->push_back( *it ); 5617 if( pDisplayText ) 5618 pDisplayText->Append( rStr.GetChar( nIndex ) ); 5619 bInserted = true; 5620 } 5621 } 5622 } 5623 else 5624 { 5625 GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, *pVector ); 5626 if( pDisplayText ) 5627 pDisplayText->Append( rStr.Copy( nIndex, nLen ) ); 5628 } 5629 } 5630 5631 if ( !IsDeviceOutputNecessary() || pVector ) 5632 return; 5633 5634 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, NULL, true ); 5635 if( pSalLayout ) 5636 { 5637 ImplDrawText( *pSalLayout ); 5638 pSalLayout->Release(); 5639 } 5640 5641 if( mpAlphaVDev ) 5642 mpAlphaVDev->DrawText( rStartPt, rStr, nIndex, nLen, pVector, pDisplayText ); 5643 } 5644 5645 // ----------------------------------------------------------------------- 5646 5647 long OutputDevice::GetTextWidth( const String& rStr, 5648 xub_StrLen nIndex, xub_StrLen nLen ) const 5649 { 5650 DBG_TRACE( "OutputDevice::GetTextWidth()" ); 5651 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5652 5653 long nWidth = GetTextArray( rStr, NULL, nIndex, nLen ); 5654 return nWidth; 5655 } 5656 5657 // ----------------------------------------------------------------------- 5658 5659 long OutputDevice::GetTextHeight() const 5660 { 5661 DBG_TRACE( "OutputDevice::GetTextHeight()" ); 5662 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5663 5664 if( mbNewFont ) 5665 if( !ImplNewFont() ) 5666 return 0; 5667 if( mbInitFont ) 5668 if( !ImplNewFont() ) 5669 return 0; 5670 5671 long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; 5672 5673 if ( mbMap ) 5674 nHeight = ImplDevicePixelToLogicHeight( nHeight ); 5675 5676 return nHeight; 5677 } 5678 5679 // ----------------------------------------------------------------------- 5680 5681 void OutputDevice::DrawTextArray( const Point& rStartPt, const String& rStr, 5682 const sal_Int32* pDXAry, 5683 xub_StrLen nIndex, xub_StrLen nLen ) 5684 { 5685 DBG_TRACE( "OutputDevice::DrawTextArray()" ); 5686 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5687 5688 if ( mpMetaFile ) 5689 mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) ); 5690 5691 if ( !IsDeviceOutputNecessary() ) 5692 return; 5693 if( !mpGraphics && !ImplGetGraphics() ) 5694 return; 5695 if( mbInitClipRegion ) 5696 ImplInitClipRegion(); 5697 if( mbOutputClipped ) 5698 return; 5699 5700 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true ); 5701 if( pSalLayout ) 5702 { 5703 ImplDrawText( *pSalLayout ); 5704 pSalLayout->Release(); 5705 } 5706 5707 if( mpAlphaVDev ) 5708 mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen ); 5709 } 5710 5711 // ----------------------------------------------------------------------- 5712 5713 long OutputDevice::GetTextArray( const String& rStr, sal_Int32* pDXAry, 5714 xub_StrLen nIndex, xub_StrLen nLen ) const 5715 { 5716 DBG_TRACE( "OutputDevice::GetTextArray()" ); 5717 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5718 5719 if( nIndex >= rStr.Len() ) 5720 return 0; 5721 if( (sal_uLong)nIndex+nLen >= rStr.Len() ) 5722 nLen = rStr.Len() - nIndex; 5723 5724 // do layout 5725 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen ); 5726 if( !pSalLayout ) 5727 return 0; 5728 5729 long nWidth = pSalLayout->FillDXArray( pDXAry ); 5730 int nWidthFactor = pSalLayout->GetUnitsPerPixel(); 5731 pSalLayout->Release(); 5732 5733 // convert virtual char widths to virtual absolute positions 5734 if( pDXAry ) 5735 for( int i = 1; i < nLen; ++i ) 5736 pDXAry[ i ] += pDXAry[ i-1 ]; 5737 5738 // convert from font units to logical units 5739 if( mbMap ) 5740 { 5741 if( pDXAry ) 5742 for( int i = 0; i < nLen; ++i ) 5743 pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] ); 5744 nWidth = ImplDevicePixelToLogicWidth( nWidth ); 5745 } 5746 5747 if( nWidthFactor > 1 ) 5748 { 5749 if( pDXAry ) 5750 for( int i = 0; i < nLen; ++i ) 5751 pDXAry[i] /= nWidthFactor; 5752 nWidth /= nWidthFactor; 5753 } 5754 5755 return nWidth; 5756 } 5757 5758 // ----------------------------------------------------------------------- 5759 5760 bool OutputDevice::GetCaretPositions( const XubString& rStr, sal_Int32* pCaretXArray, 5761 xub_StrLen nIndex, xub_StrLen nLen, 5762 sal_Int32* pDXAry, long nLayoutWidth, 5763 sal_Bool bCellBreaking ) const 5764 { 5765 DBG_TRACE( "OutputDevice::GetCaretPositions()" ); 5766 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5767 5768 if( nIndex >= rStr.Len() ) 5769 return false; 5770 if( (sal_uLong)nIndex+nLen >= rStr.Len() ) 5771 nLen = rStr.Len() - nIndex; 5772 5773 // layout complex text 5774 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, 5775 Point(0,0), nLayoutWidth, pDXAry ); 5776 if( !pSalLayout ) 5777 return false; 5778 5779 int nWidthFactor = pSalLayout->GetUnitsPerPixel(); 5780 pSalLayout->GetCaretPositions( 2*nLen, pCaretXArray ); 5781 long nWidth = pSalLayout->GetTextWidth(); 5782 pSalLayout->Release(); 5783 5784 // fixup unknown caret positions 5785 int i; 5786 for( i = 0; i < 2 * nLen; ++i ) 5787 if( pCaretXArray[ i ] >= 0 ) 5788 break; 5789 long nXPos = pCaretXArray[ i ]; 5790 for( i = 0; i < 2 * nLen; ++i ) 5791 { 5792 if( pCaretXArray[ i ] >= 0 ) 5793 nXPos = pCaretXArray[ i ]; 5794 else 5795 pCaretXArray[ i ] = nXPos; 5796 } 5797 5798 // handle window mirroring 5799 if( IsRTLEnabled() ) 5800 { 5801 for( i = 0; i < 2 * nLen; ++i ) 5802 pCaretXArray[i] = nWidth - pCaretXArray[i] - 1; 5803 } 5804 5805 // convert from font units to logical units 5806 if( mbMap ) 5807 { 5808 for( i = 0; i < 2*nLen; ++i ) 5809 pCaretXArray[i] = ImplDevicePixelToLogicWidth( pCaretXArray[i] ); 5810 } 5811 5812 if( nWidthFactor != 1 ) 5813 { 5814 for( i = 0; i < 2*nLen; ++i ) 5815 pCaretXArray[i] /= nWidthFactor; 5816 } 5817 5818 // if requested move caret position to cell limits 5819 if( bCellBreaking ) 5820 { 5821 ; // TODO 5822 } 5823 5824 return true; 5825 } 5826 5827 // ----------------------------------------------------------------------- 5828 5829 void OutputDevice::DrawStretchText( const Point& rStartPt, sal_uLong nWidth, 5830 const String& rStr, 5831 xub_StrLen nIndex, xub_StrLen nLen ) 5832 { 5833 DBG_TRACE( "OutputDevice::DrawStretchText()" ); 5834 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 5835 5836 if ( mpMetaFile ) 5837 mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) ); 5838 5839 if ( !IsDeviceOutputNecessary() ) 5840 return; 5841 5842 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, nWidth, NULL, true ); 5843 if( pSalLayout ) 5844 { 5845 ImplDrawText( *pSalLayout ); 5846 pSalLayout->Release(); 5847 } 5848 5849 if( mpAlphaVDev ) 5850 mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen ); 5851 } 5852 5853 // ----------------------------------------------------------------------- 5854 5855 ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( String& rStr, 5856 xub_StrLen nMinIndex, xub_StrLen nLen, 5857 long nPixelWidth, const sal_Int32* pDXArray ) const 5858 { 5859 // get string length for calculating extents 5860 xub_StrLen nEndIndex = rStr.Len(); 5861 if( (sal_uLong)nMinIndex + nLen < nEndIndex ) 5862 nEndIndex = nMinIndex + nLen; 5863 5864 // don't bother if there is nothing to do 5865 if( nEndIndex < nMinIndex ) 5866 nEndIndex = nMinIndex; 5867 5868 int nLayoutFlags = 0; 5869 if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL ) 5870 nLayoutFlags |= SAL_LAYOUT_BIDI_RTL; 5871 if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_STRONG ) 5872 nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG; 5873 else if( 0 == (mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) ) 5874 { 5875 // disable Bidi if no RTL hint and no RTL codes used 5876 const xub_Unicode* pStr = rStr.GetBuffer() + nMinIndex; 5877 const xub_Unicode* pEnd = rStr.GetBuffer() + nEndIndex; 5878 for( ; pStr < pEnd; ++pStr ) 5879 if( ((*pStr >= 0x0580) && (*pStr < 0x0800)) // middle eastern scripts 5880 || ((*pStr >= 0xFB18) && (*pStr < 0xFE00)) // hebrew + arabic A presentation forms 5881 || ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation forms B 5882 break; 5883 if( pStr >= pEnd ) 5884 nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG; 5885 } 5886 5887 if( mbKerning ) 5888 nLayoutFlags |= SAL_LAYOUT_KERNING_PAIRS; 5889 if( maFont.GetKerning() & KERNING_ASIAN ) 5890 nLayoutFlags |= SAL_LAYOUT_KERNING_ASIAN; 5891 if( maFont.IsVertical() ) 5892 nLayoutFlags |= SAL_LAYOUT_VERTICAL; 5893 5894 if( mnTextLayoutMode & TEXT_LAYOUT_ENABLE_LIGATURES ) 5895 nLayoutFlags |= SAL_LAYOUT_ENABLE_LIGATURES; 5896 else if( mnTextLayoutMode & TEXT_LAYOUT_COMPLEX_DISABLED ) 5897 nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED; 5898 else 5899 { 5900 // disable CTL for non-CTL text 5901 const sal_Unicode* pStr = rStr.GetBuffer() + nMinIndex; 5902 const sal_Unicode* pEnd = rStr.GetBuffer() + nEndIndex; 5903 for( ; pStr < pEnd; ++pStr ) 5904 if( ((*pStr >= 0x0300) && (*pStr < 0x0370)) // diacritical marks 5905 || ((*pStr >= 0x0590) && (*pStr < 0x10A0)) // many CTL scripts 5906 || ((*pStr >= 0x1100) && (*pStr < 0x1200)) // hangul jamo 5907 || ((*pStr >= 0x1700) && (*pStr < 0x1900)) // many CTL scripts 5908 || ((*pStr >= 0xFB1D) && (*pStr < 0xFE00)) // middle east presentation 5909 || ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) // arabic presentation B 5910 || ((*pStr >= 0xFE00) && (*pStr < 0xFE10)) // variation selectors in BMP 5911 || ((pStr + 1 < pEnd) && (pStr[0] == 0xDB40) && (0xDD00 <= pStr[1]) && (pStr[1] < 0xDEF0)) // variation selector supplement 5912 ) 5913 break; 5914 if( pStr >= pEnd ) 5915 nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED; 5916 } 5917 5918 if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS) 5919 { 5920 // disable character localization when no digits used 5921 const sal_Unicode* pBase = rStr.GetBuffer(); 5922 const sal_Unicode* pStr = pBase + nMinIndex; 5923 const sal_Unicode* pEnd = pBase + nEndIndex; 5924 for( ; pStr < pEnd; ++pStr ) 5925 { 5926 // TODO: are there non-digit localizations? 5927 if( (*pStr >= '0') && (*pStr <= '9') ) 5928 { 5929 // translate characters to local preference 5930 sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage ); 5931 if( cChar != *pStr ) 5932 // TODO: are the localized digit surrogates? 5933 rStr.SetChar( static_cast<sal_uInt16>(pStr - pBase), 5934 static_cast<sal_Unicode>(cChar) ); 5935 } 5936 } 5937 } 5938 5939 // right align for RTL text, DRAWPOS_REVERSED, RTL window style 5940 bool bRightAlign = ((mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) != 0); 5941 if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) 5942 bRightAlign = false; 5943 else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT ) 5944 bRightAlign = true; 5945 // SSA: hack for western office, ie text get right aligned 5946 // for debugging purposes of mirrored UI 5947 //static const char* pEnv = getenv( "SAL_RTL_MIRRORTEXT" ); 5948 bool bRTLWindow = IsRTLEnabled(); 5949 bRightAlign ^= bRTLWindow; 5950 if( bRightAlign ) 5951 nLayoutFlags |= SAL_LAYOUT_RIGHT_ALIGN; 5952 5953 // set layout options 5954 ImplLayoutArgs aLayoutArgs( rStr.GetBuffer(), rStr.Len(), nMinIndex, nEndIndex, nLayoutFlags ); 5955 5956 int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0; 5957 aLayoutArgs.SetOrientation( nOrientation ); 5958 5959 aLayoutArgs.SetLayoutWidth( nPixelWidth ); 5960 aLayoutArgs.SetDXArray( pDXArray ); 5961 5962 return aLayoutArgs; 5963 } 5964 5965 // ----------------------------------------------------------------------- 5966 5967 SalLayout* OutputDevice::ImplLayout( const String& rOrigStr, 5968 xub_StrLen nMinIndex, 5969 xub_StrLen nLen, 5970 const Point& rLogicalPos, 5971 long nLogicalWidth, 5972 const sal_Int32* pDXArray, 5973 bool bFilter ) const 5974 { 5975 // we need a graphics 5976 if( !mpGraphics ) 5977 if( !ImplGetGraphics() ) 5978 return NULL; 5979 5980 // initialize font if needed 5981 if( mbNewFont ) 5982 if( !ImplNewFont() ) 5983 return NULL; 5984 if( mbInitFont ) 5985 ImplInitFont(); 5986 5987 // check string index and length 5988 if( (unsigned)nMinIndex + nLen > rOrigStr.Len() ) 5989 { 5990 const int nNewLen = (int)rOrigStr.Len() - nMinIndex; 5991 if( nNewLen <= 0 ) 5992 return NULL; 5993 nLen = static_cast<xub_StrLen>(nNewLen); 5994 } 5995 5996 String aStr = rOrigStr; 5997 5998 // filter out special markers 5999 if( bFilter ) 6000 { 6001 xub_StrLen nCutStart, nCutStop, nOrgLen = nLen; 6002 bool bFiltered = mpGraphics->filterText( rOrigStr, aStr, nMinIndex, nLen, nCutStart, nCutStop ); 6003 if( !nLen ) 6004 return NULL; 6005 6006 if( bFiltered && nCutStop != nCutStart && pDXArray ) 6007 { 6008 if( !nLen ) 6009 pDXArray = NULL; 6010 else 6011 { 6012 sal_Int32* pAry = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen); 6013 if( nCutStart > nMinIndex ) 6014 memcpy( pAry, pDXArray, sizeof(sal_Int32)*(nCutStart-nMinIndex) ); 6015 // note: nCutStart will never be smaller than nMinIndex 6016 memcpy( pAry+nCutStart-nMinIndex, 6017 pDXArray + nOrgLen - (nCutStop-nMinIndex), 6018 sizeof(sal_Int32)*(nLen - (nCutStart-nMinIndex)) ); 6019 pDXArray = pAry; 6020 } 6021 } 6022 } 6023 6024 // convert from logical units to physical units 6025 // recode string if needed 6026 if( mpFontEntry->mpConversion ) 6027 mpFontEntry->mpConversion->RecodeString( aStr, 0, aStr.Len() ); 6028 6029 long nPixelWidth = nLogicalWidth; 6030 if( nLogicalWidth && mbMap ) 6031 nPixelWidth = ImplLogicWidthToDevicePixel( nLogicalWidth ); 6032 if( pDXArray && mbMap ) 6033 { 6034 // convert from logical units to font units using a temporary array 6035 sal_Int32* pTempDXAry = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) ); 6036 // using base position for better rounding a.k.a. "dancing characters" 6037 int nPixelXOfs = ImplLogicWidthToDevicePixel( rLogicalPos.X() ); 6038 for( int i = 0; i < nLen; ++i ) 6039 pTempDXAry[i] = ImplLogicWidthToDevicePixel( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs; 6040 6041 pDXArray = pTempDXAry; 6042 } 6043 6044 ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray ); 6045 6046 #ifdef MACOSX 6047 // CoreText layouts are immutable and already contain the text color 6048 // so we need to provide the color already for the layout request 6049 // even if this layout will never be drawn 6050 if( mbInitTextColor ) 6051 const_cast<OutputDevice&>(*this).ImplInitTextColor(); 6052 #endif 6053 6054 // get matching layout object for base font 6055 SalLayout* pSalLayout = NULL; 6056 if( mpPDFWriter ) 6057 pSalLayout = mpPDFWriter->GetTextLayout( aLayoutArgs, &mpFontEntry->maFontSelData ); 6058 6059 if( !pSalLayout ) 6060 pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 ); 6061 6062 // layout text 6063 if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) ) 6064 { 6065 pSalLayout->Release(); 6066 pSalLayout = NULL; 6067 } 6068 6069 if( !pSalLayout ) 6070 return NULL; 6071 6072 // do glyph fallback if needed 6073 // #105768# avoid fallback for very small font sizes 6074 if( aLayoutArgs.NeedFallback() ) 6075 if( mpFontEntry && (mpFontEntry->maFontSelData.mnHeight >= 3) ) 6076 pSalLayout = ImplGlyphFallbackLayout( pSalLayout, aLayoutArgs ); 6077 6078 // position, justify, etc. the layout 6079 pSalLayout->AdjustLayout( aLayoutArgs ); 6080 pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos ); 6081 // adjust to right alignment if necessary 6082 if( aLayoutArgs.mnFlags & SAL_LAYOUT_RIGHT_ALIGN ) 6083 { 6084 long nRTLOffset; 6085 if( pDXArray ) 6086 nRTLOffset = pDXArray[ nLen - 1 ]; 6087 else if( nPixelWidth ) 6088 nRTLOffset = nPixelWidth; 6089 else 6090 nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel(); 6091 pSalLayout->DrawOffset().X() = 1 - nRTLOffset; 6092 } 6093 6094 return pSalLayout; 6095 } 6096 6097 // ----------------------------------------------------------------------- 6098 6099 SalLayout* OutputDevice::ImplGlyphFallbackLayout( SalLayout* pSalLayout, ImplLayoutArgs& rLayoutArgs ) const 6100 { 6101 // prepare multi level glyph fallback 6102 MultiSalLayout* pMultiSalLayout = NULL; 6103 ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns; 6104 rLayoutArgs.PrepareFallback(); 6105 rLayoutArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK; 6106 6107 #if defined(HDU_DEBUG) 6108 { 6109 int nCharPos = -1; 6110 bool bRTL = false; 6111 fprintf(stderr,"OD:ImplLayout Glyph Fallback for"); 6112 for( int i=0; i<8 && rLayoutArgs.GetNextPos( &nCharPos, &bRTL); ++i ) 6113 fprintf(stderr," U+%04X", rLayoutArgs.mpStr[ nCharPos ] ); 6114 fprintf(stderr,"\n"); 6115 rLayoutArgs.ResetPos(); 6116 } 6117 #endif 6118 // get list of unicodes that need glyph fallback 6119 int nCharPos = -1; 6120 bool bRTL = false; 6121 rtl::OUStringBuffer aMissingCodeBuf; 6122 while( rLayoutArgs.GetNextPos( &nCharPos, &bRTL) ) 6123 aMissingCodeBuf.append( rLayoutArgs.mpStr[ nCharPos ] ); 6124 rLayoutArgs.ResetPos(); 6125 rtl::OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear(); 6126 6127 ImplFontSelectData aFontSelData = mpFontEntry->maFontSelData; 6128 6129 ImplFontMetricData aOrigMetric( aFontSelData ); 6130 // TODO: use cached metric in fontentry 6131 mpGraphics->GetFontMetric( &aOrigMetric ); 6132 6133 // when device specific font substitution may have been performed for 6134 // the originally selected font then make sure that a fallback to that 6135 // font is performed first 6136 int nDevSpecificFallback = 0; 6137 if( mpOutDevData && !mpOutDevData->maDevFontSubst.Empty() ) 6138 nDevSpecificFallback = 1; 6139 6140 // try if fallback fonts support the missing unicodes 6141 for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel ) 6142 { 6143 // find a font family suited for glyph fallback 6144 #ifndef FONTFALLBACK_HOOKS_DISABLED 6145 // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontEntry 6146 // if the system-specific glyph fallback is active 6147 aFontSelData.mpFontEntry = mpFontEntry; // reset the fontentry to base-level 6148 #endif 6149 ImplFontEntry* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontList, 6150 aFontSelData, nFallbackLevel-nDevSpecificFallback, aMissingCodes ); 6151 if( !pFallbackFont ) 6152 break; 6153 6154 aFontSelData.mpFontEntry = pFallbackFont; 6155 aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData; 6156 if( mpFontEntry && nFallbackLevel < MAX_FALLBACK-1) 6157 { 6158 // ignore fallback font if it is the same as the original font 6159 if( mpFontEntry->maFontSelData.mpFontData == aFontSelData.mpFontData ) 6160 { 6161 mpFontCache->Release( pFallbackFont ); 6162 continue; 6163 } 6164 } 6165 6166 #if defined(HDU_DEBUG) 6167 { 6168 ByteString aOrigFontName( maFont.GetName(), RTL_TEXTENCODING_UTF8); 6169 ByteString aFallbackName( aFontSelData.mpFontData->GetFamilyName(), 6170 RTL_TEXTENCODING_UTF8); 6171 fprintf(stderr,"\tGlyphFallback[lvl=%d] \"%s\" -> \"%s\" (q=%d)\n", 6172 nFallbackLevel, aOrigFontName.GetBuffer(), aFallbackName.GetBuffer(), 6173 aFontSelData.mpFontData->GetQuality()); 6174 } 6175 #endif 6176 6177 // TODO: try to get the metric data from the GFB's mpFontEntry 6178 ImplFontMetricData aSubstituteMetric( aFontSelData ); 6179 pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel ); 6180 mpGraphics->GetFontMetric( &aSubstituteMetric, nFallbackLevel ); 6181 6182 const long nOriginalHeight = aOrigMetric.mnAscent + aOrigMetric.mnDescent; 6183 const long nSubstituteHeight = aSubstituteMetric.mnAscent + aSubstituteMetric.mnDescent; 6184 // Too tall, shrink it a bit. Need a better calculation to include extra 6185 // factors and any extra wriggle room we might have available? 6186 // TODO: should we scale by max-ascent/max-descent instead of design height? 6187 if( nSubstituteHeight > nOriginalHeight ) 6188 { 6189 const float fScale = nOriginalHeight / (float)nSubstituteHeight; 6190 const float fOrigHeight = aFontSelData.mfExactHeight; 6191 const int nOrigHeight = aFontSelData.mnHeight; 6192 aFontSelData.mfExactHeight *= fScale; 6193 aFontSelData.mnHeight = static_cast<int>(aFontSelData.mfExactHeight); 6194 pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel ); 6195 aFontSelData.mnHeight = nOrigHeight; 6196 aFontSelData.mfExactHeight = fOrigHeight; 6197 } 6198 6199 // create and add glyph fallback layout to multilayout 6200 rLayoutArgs.ResetPos(); 6201 SalLayout* pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel ); 6202 if( pFallback ) 6203 { 6204 if( pFallback->LayoutText( rLayoutArgs ) ) 6205 { 6206 if( !pMultiSalLayout ) 6207 pMultiSalLayout = new MultiSalLayout( *pSalLayout ); 6208 pMultiSalLayout->AddFallback( *pFallback, 6209 rLayoutArgs.maRuns, aFontSelData.mpFontData ); 6210 if (nFallbackLevel == MAX_FALLBACK-1) 6211 pMultiSalLayout->SetInComplete(); 6212 } 6213 else 6214 { 6215 // there is no need for a font that couldn't resolve anything 6216 pFallback->Release(); 6217 } 6218 } 6219 6220 mpFontCache->Release( pFallbackFont ); 6221 6222 // break when this fallback was sufficient 6223 if( !rLayoutArgs.PrepareFallback() ) 6224 break; 6225 } 6226 6227 if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) ) 6228 pSalLayout = pMultiSalLayout; 6229 6230 // restore orig font settings 6231 pSalLayout->InitFont(); 6232 rLayoutArgs.maRuns = aLayoutRuns; 6233 6234 return pSalLayout; 6235 } 6236 6237 // ----------------------------------------------------------------------- 6238 6239 sal_Bool OutputDevice::GetTextIsRTL( 6240 const String& rString, 6241 xub_StrLen nIndex, xub_StrLen nLen ) const 6242 { 6243 String aStr( rString ); 6244 ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL ); 6245 bool bRTL = false; 6246 int nCharPos = -1; 6247 aArgs.GetNextPos( &nCharPos, &bRTL ); 6248 return (nCharPos != nIndex) ? sal_True : sal_False; 6249 } 6250 6251 // ----------------------------------------------------------------------- 6252 6253 xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth, 6254 xub_StrLen nIndex, xub_StrLen nLen, 6255 long nCharExtra, sal_Bool /*TODO: bCellBreaking*/ ) const 6256 { 6257 DBG_TRACE( "OutputDevice::GetTextBreak()" ); 6258 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 6259 6260 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen ); 6261 xub_StrLen nRetVal = STRING_LEN; 6262 if( pSalLayout ) 6263 { 6264 // convert logical widths into layout units 6265 // NOTE: be very careful to avoid rounding errors for nCharExtra case 6266 // problem with rounding errors especially for small nCharExtras 6267 // TODO: remove when layout units have subpixel granularity 6268 long nWidthFactor = pSalLayout->GetUnitsPerPixel(); 6269 long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1; 6270 nTextWidth *= nWidthFactor * nSubPixelFactor; 6271 long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth ); 6272 long nExtraPixelWidth = 0; 6273 if( nCharExtra != 0 ) 6274 { 6275 nCharExtra *= nWidthFactor * nSubPixelFactor; 6276 nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra ); 6277 } 6278 nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor )); 6279 6280 pSalLayout->Release(); 6281 } 6282 6283 return nRetVal; 6284 } 6285 6286 // ----------------------------------------------------------------------- 6287 6288 xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth, 6289 sal_Unicode nHyphenatorChar, xub_StrLen& rHyphenatorPos, 6290 xub_StrLen nIndex, xub_StrLen nLen, 6291 long nCharExtra ) const 6292 { 6293 DBG_TRACE( "OutputDevice::GetTextBreak()" ); 6294 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 6295 6296 rHyphenatorPos = STRING_LEN; 6297 6298 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen ); 6299 if( !pSalLayout ) 6300 return STRING_LEN; 6301 6302 // convert logical widths into layout units 6303 // NOTE: be very careful to avoid rounding errors for nCharExtra case 6304 // problem with rounding errors especially for small nCharExtras 6305 // TODO: remove when layout units have subpixel granularity 6306 long nWidthFactor = pSalLayout->GetUnitsPerPixel(); 6307 long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1; 6308 6309 nTextWidth *= nWidthFactor * nSubPixelFactor; 6310 long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth ); 6311 long nExtraPixelWidth = 0; 6312 if( nCharExtra != 0 ) 6313 { 6314 nCharExtra *= nWidthFactor * nSubPixelFactor; 6315 nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra ); 6316 } 6317 6318 // calculate un-hyphenated break position 6319 xub_StrLen nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor )); 6320 6321 // calculate hyphenated break position 6322 String aHyphenatorStr( &nHyphenatorChar, 1 ); 6323 xub_StrLen nTempLen = 1; 6324 SalLayout* pHyphenatorLayout = ImplLayout( aHyphenatorStr, 0, nTempLen ); 6325 if( pHyphenatorLayout ) 6326 { 6327 // calculate subpixel width of hyphenation character 6328 long nHyphenatorPixelWidth = pHyphenatorLayout->GetTextWidth() * nSubPixelFactor; 6329 pHyphenatorLayout->Release(); 6330 6331 // calculate hyphenated break position 6332 nTextPixelWidth -= nHyphenatorPixelWidth; 6333 if( nExtraPixelWidth > 0 ) 6334 nTextPixelWidth -= nExtraPixelWidth; 6335 6336 rHyphenatorPos = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor )); 6337 6338 if( rHyphenatorPos > nRetVal ) 6339 rHyphenatorPos = nRetVal; 6340 } 6341 6342 pSalLayout->Release(); 6343 return nRetVal; 6344 } 6345 6346 // ----------------------------------------------------------------------- 6347 6348 void OutputDevice::ImplDrawText( OutputDevice& rTargetDevice, const Rectangle& rRect, 6349 const String& rOrigStr, sal_uInt16 nStyle, 6350 MetricVector* pVector, String* pDisplayText, 6351 ::vcl::ITextLayout& _rLayout ) 6352 { 6353 Color aOldTextColor; 6354 Color aOldTextFillColor; 6355 sal_Bool bRestoreFillColor = false; 6356 if ( (nStyle & TEXT_DRAW_DISABLE) && ! pVector ) 6357 { 6358 sal_Bool bHighContrastBlack = sal_False; 6359 sal_Bool bHighContrastWhite = sal_False; 6360 const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() ); 6361 if( rStyleSettings.GetHighContrastMode() ) 6362 { 6363 Color aCol; 6364 if( rTargetDevice.IsBackground() ) 6365 aCol = rTargetDevice.GetBackground().GetColor(); 6366 else 6367 // best guess is the face color here 6368 // but it may be totally wrong. the background color 6369 // was typically already reset 6370 aCol = rStyleSettings.GetFaceColor(); 6371 6372 bHighContrastBlack = aCol.IsDark(); 6373 bHighContrastWhite = aCol.IsBright(); 6374 } 6375 6376 aOldTextColor = rTargetDevice.GetTextColor(); 6377 if ( rTargetDevice.IsTextFillColor() ) 6378 { 6379 bRestoreFillColor = sal_True; 6380 aOldTextFillColor = rTargetDevice.GetTextFillColor(); 6381 } 6382 if( bHighContrastBlack ) 6383 rTargetDevice.SetTextColor( COL_GREEN ); 6384 else if( bHighContrastWhite ) 6385 rTargetDevice.SetTextColor( COL_LIGHTGREEN ); 6386 else 6387 { 6388 // draw disabled text always without shadow 6389 // as it fits better with native look 6390 /* 6391 SetTextColor( GetSettings().GetStyleSettings().GetLightColor() ); 6392 Rectangle aRect = rRect; 6393 aRect.Move( 1, 1 ); 6394 DrawText( aRect, rOrigStr, nStyle & ~TEXT_DRAW_DISABLE ); 6395 */ 6396 rTargetDevice.SetTextColor( rTargetDevice.GetSettings().GetStyleSettings().GetDisableColor() ); 6397 } 6398 } 6399 6400 long nWidth = rRect.GetWidth(); 6401 long nHeight = rRect.GetHeight(); 6402 6403 if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) ) 6404 return; 6405 6406 Point aPos = rRect.TopLeft(); 6407 6408 long nTextHeight = rTargetDevice.GetTextHeight(); 6409 TextAlign eAlign = rTargetDevice.GetTextAlign(); 6410 xub_StrLen nMnemonicPos = STRING_NOTFOUND; 6411 6412 String aStr = rOrigStr; 6413 if ( nStyle & TEXT_DRAW_MNEMONIC ) 6414 aStr = GetNonMnemonicString( aStr, nMnemonicPos ); 6415 6416 const bool bDrawMnemonics = !(rTargetDevice.GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector; 6417 6418 // Mehrzeiligen Text behandeln wir anders 6419 if ( nStyle & TEXT_DRAW_MULTILINE ) 6420 { 6421 6422 XubString aLastLine; 6423 ImplMultiTextLineInfo aMultiLineInfo; 6424 ImplTextLineInfo* pLineInfo; 6425 long nMaxTextWidth; 6426 xub_StrLen i; 6427 xub_StrLen nLines; 6428 xub_StrLen nFormatLines; 6429 6430 if ( nTextHeight ) 6431 { 6432 nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _rLayout ); 6433 nLines = (xub_StrLen)(nHeight/nTextHeight); 6434 nFormatLines = aMultiLineInfo.Count(); 6435 if ( !nLines ) 6436 nLines = 1; 6437 if ( nFormatLines > nLines ) 6438 { 6439 if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) 6440 { 6441 // Letzte Zeile zusammenbauen und kuerzen 6442 nFormatLines = nLines-1; 6443 6444 pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); 6445 aLastLine = aStr.Copy( pLineInfo->GetIndex() ); 6446 aLastLine.ConvertLineEnd( LINEEND_LF ); 6447 // Alle LineFeed's durch Spaces ersetzen 6448 xub_StrLen nLastLineLen = aLastLine.Len(); 6449 for ( i = 0; i < nLastLineLen; i++ ) 6450 { 6451 if ( aLastLine.GetChar( i ) == _LF ) 6452 aLastLine.SetChar( i, ' ' ); 6453 } 6454 aLastLine = ImplGetEllipsisString( rTargetDevice, aLastLine, nWidth, nStyle, _rLayout ); 6455 nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); 6456 nStyle |= TEXT_DRAW_TOP; 6457 } 6458 } 6459 else 6460 { 6461 if ( nMaxTextWidth <= nWidth ) 6462 nStyle &= ~TEXT_DRAW_CLIP; 6463 } 6464 6465 // Muss in der Hoehe geclippt werden? 6466 if ( nFormatLines*nTextHeight > nHeight ) 6467 nStyle |= TEXT_DRAW_CLIP; 6468 6469 // Clipping setzen 6470 if ( nStyle & TEXT_DRAW_CLIP ) 6471 { 6472 rTargetDevice.Push( PUSH_CLIPREGION ); 6473 rTargetDevice.IntersectClipRegion( rRect ); 6474 } 6475 6476 // Vertikales Alignment 6477 if ( nStyle & TEXT_DRAW_BOTTOM ) 6478 aPos.Y() += nHeight-(nFormatLines*nTextHeight); 6479 else if ( nStyle & TEXT_DRAW_VCENTER ) 6480 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; 6481 6482 // Font Alignment 6483 if ( eAlign == ALIGN_BOTTOM ) 6484 aPos.Y() += nTextHeight; 6485 else if ( eAlign == ALIGN_BASELINE ) 6486 aPos.Y() += rTargetDevice.GetFontMetric().GetAscent(); 6487 6488 // Alle Zeilen ausgeben, bis auf die letzte 6489 for ( i = 0; i < nFormatLines; i++ ) 6490 { 6491 pLineInfo = aMultiLineInfo.GetLine( i ); 6492 if ( nStyle & TEXT_DRAW_RIGHT ) 6493 aPos.X() += nWidth-pLineInfo->GetWidth(); 6494 else if ( nStyle & TEXT_DRAW_CENTER ) 6495 aPos.X() += (nWidth-pLineInfo->GetWidth())/2; 6496 xub_StrLen nIndex = pLineInfo->GetIndex(); 6497 xub_StrLen nLineLen = pLineInfo->GetLen(); 6498 _rLayout.DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText ); 6499 if ( bDrawMnemonics ) 6500 { 6501 if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) ) 6502 { 6503 long nMnemonicX; 6504 long nMnemonicY; 6505 long nMnemonicWidth; 6506 6507 sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * nLineLen ); 6508 /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray, 6509 nIndex, nLineLen ); 6510 long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)]; 6511 long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1]; 6512 nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) ); 6513 6514 Point aTempPos = rTargetDevice.LogicToPixel( aPos ); 6515 nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( Min( lc_x1, lc_x2 ) ); 6516 nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() ); 6517 rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); 6518 } 6519 } 6520 aPos.Y() += nTextHeight; 6521 aPos.X() = rRect.Left(); 6522 } 6523 6524 6525 // Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben, 6526 // da die Zeile gekuerzt wurde 6527 if ( aLastLine.Len() ) 6528 _rLayout.DrawText( aPos, aLastLine, 0, STRING_LEN, pVector, pDisplayText ); 6529 6530 // Clipping zuruecksetzen 6531 if ( nStyle & TEXT_DRAW_CLIP ) 6532 rTargetDevice.Pop(); 6533 } 6534 } 6535 else 6536 { 6537 long nTextWidth = _rLayout.GetTextWidth( aStr, 0, STRING_LEN ); 6538 6539 // Evt. Text kuerzen 6540 if ( nTextWidth > nWidth ) 6541 { 6542 if ( nStyle & TEXT_DRAW_ELLIPSIS ) 6543 { 6544 aStr = ImplGetEllipsisString( rTargetDevice, aStr, nWidth, nStyle, _rLayout ); 6545 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); 6546 nStyle |= TEXT_DRAW_LEFT; 6547 nTextWidth = _rLayout.GetTextWidth( aStr, 0, aStr.Len() ); 6548 } 6549 } 6550 else 6551 { 6552 if ( nTextHeight <= nHeight ) 6553 nStyle &= ~TEXT_DRAW_CLIP; 6554 } 6555 6556 // horizontal text alignment 6557 if ( nStyle & TEXT_DRAW_RIGHT ) 6558 aPos.X() += nWidth-nTextWidth; 6559 else if ( nStyle & TEXT_DRAW_CENTER ) 6560 aPos.X() += (nWidth-nTextWidth)/2; 6561 6562 // vertical font alignment 6563 if ( eAlign == ALIGN_BOTTOM ) 6564 aPos.Y() += nTextHeight; 6565 else if ( eAlign == ALIGN_BASELINE ) 6566 aPos.Y() += rTargetDevice.GetFontMetric().GetAscent(); 6567 6568 if ( nStyle & TEXT_DRAW_BOTTOM ) 6569 aPos.Y() += nHeight-nTextHeight; 6570 else if ( nStyle & TEXT_DRAW_VCENTER ) 6571 aPos.Y() += (nHeight-nTextHeight)/2; 6572 6573 long nMnemonicX = 0; 6574 long nMnemonicY = 0; 6575 long nMnemonicWidth = 0; 6576 if ( nMnemonicPos != STRING_NOTFOUND ) 6577 { 6578 sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * aStr.Len() ); 6579 /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray, 0, aStr.Len() ); 6580 long lc_x1 = pCaretXArray[2*(nMnemonicPos)]; 6581 long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1]; 6582 nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) ); 6583 6584 Point aTempPos = rTargetDevice.LogicToPixel( aPos ); 6585 nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( Min(lc_x1, lc_x2) ); 6586 nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() ); 6587 } 6588 6589 if ( nStyle & TEXT_DRAW_CLIP ) 6590 { 6591 rTargetDevice.Push( PUSH_CLIPREGION ); 6592 rTargetDevice.IntersectClipRegion( rRect ); 6593 _rLayout.DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText ); 6594 if ( bDrawMnemonics ) 6595 { 6596 if ( nMnemonicPos != STRING_NOTFOUND ) 6597 rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); 6598 } 6599 rTargetDevice.Pop(); 6600 } 6601 else 6602 { 6603 _rLayout.DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText ); 6604 if ( bDrawMnemonics ) 6605 { 6606 if ( nMnemonicPos != STRING_NOTFOUND ) 6607 rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); 6608 } 6609 } 6610 } 6611 6612 if ( nStyle & TEXT_DRAW_DISABLE && !pVector ) 6613 { 6614 rTargetDevice.SetTextColor( aOldTextColor ); 6615 if ( bRestoreFillColor ) 6616 rTargetDevice.SetTextFillColor( aOldTextFillColor ); 6617 } 6618 } 6619 6620 // ----------------------------------------------------------------------- 6621 6622 void OutputDevice::AddTextRectActions( const Rectangle& rRect, 6623 const String& rOrigStr, 6624 sal_uInt16 nStyle, 6625 GDIMetaFile& rMtf ) 6626 { 6627 DBG_TRACE( "OutputDevice::AddTextRectActions( const Rectangle& )" ); 6628 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 6629 6630 if ( !rOrigStr.Len() || rRect.IsEmpty() ) 6631 return; 6632 6633 // we need a graphics 6634 if( !mpGraphics && !ImplGetGraphics() ) 6635 return; 6636 if( mbInitClipRegion ) 6637 ImplInitClipRegion(); 6638 6639 // temporarily swap in passed mtf for action generation, and 6640 // disable output generation. 6641 const sal_Bool bOutputEnabled( IsOutputEnabled() ); 6642 GDIMetaFile* pMtf = mpMetaFile; 6643 6644 mpMetaFile = &rMtf; 6645 EnableOutput( sal_False ); 6646 6647 // #i47157# Factored out to ImplDrawTextRect(), to be shared 6648 // between us and DrawText() 6649 DefaultTextLayout aLayout( *this ); 6650 ImplDrawText( *this, rRect, rOrigStr, nStyle, NULL, NULL, aLayout ); 6651 6652 // and restore again 6653 EnableOutput( bOutputEnabled ); 6654 mpMetaFile = pMtf; 6655 } 6656 6657 // ----------------------------------------------------------------------- 6658 6659 void OutputDevice::DrawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, 6660 MetricVector* pVector, String* pDisplayText, 6661 ::vcl::ITextLayout* _pTextLayout ) 6662 { 6663 if( mpOutDevData && mpOutDevData->mpRecordLayout ) 6664 { 6665 pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects; 6666 pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText; 6667 } 6668 6669 DBG_TRACE( "OutputDevice::DrawText( const Rectangle& )" ); 6670 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 6671 6672 bool bDecomposeTextRectAction = ( _pTextLayout != NULL ) && _pTextLayout->DecomposeTextRectAction(); 6673 if ( mpMetaFile && !bDecomposeTextRectAction ) 6674 mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) ); 6675 6676 if ( ( !IsDeviceOutputNecessary() && !pVector && !bDecomposeTextRectAction ) || !rOrigStr.Len() || rRect.IsEmpty() ) 6677 return; 6678 6679 // we need a graphics 6680 if( !mpGraphics && !ImplGetGraphics() ) 6681 return; 6682 if( mbInitClipRegion ) 6683 ImplInitClipRegion(); 6684 if( mbOutputClipped && !bDecomposeTextRectAction ) 6685 return; 6686 6687 // temporarily disable mtf action generation (ImplDrawText _does_ 6688 // create META_TEXT_ACTIONs otherwise) 6689 GDIMetaFile* pMtf = mpMetaFile; 6690 if ( !bDecomposeTextRectAction ) 6691 mpMetaFile = NULL; 6692 6693 // #i47157# Factored out to ImplDrawText(), to be used also 6694 // from AddTextRectActions() 6695 DefaultTextLayout aDefaultLayout( *this ); 6696 ImplDrawText( *this, rRect, rOrigStr, nStyle, pVector, pDisplayText, _pTextLayout ? *_pTextLayout : aDefaultLayout ); 6697 6698 // and enable again 6699 mpMetaFile = pMtf; 6700 6701 if( mpAlphaVDev ) 6702 mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText ); 6703 } 6704 6705 // ----------------------------------------------------------------------- 6706 6707 Rectangle OutputDevice::GetTextRect( const Rectangle& rRect, 6708 const XubString& rStr, sal_uInt16 nStyle, 6709 TextRectInfo* pInfo, 6710 const ::vcl::ITextLayout* _pTextLayout ) const 6711 { 6712 DBG_TRACE( "OutputDevice::GetTextRect()" ); 6713 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 6714 6715 Rectangle aRect = rRect; 6716 xub_StrLen nLines; 6717 long nWidth = rRect.GetWidth(); 6718 long nMaxWidth; 6719 long nTextHeight = GetTextHeight(); 6720 6721 String aStr = rStr; 6722 if ( nStyle & TEXT_DRAW_MNEMONIC ) 6723 aStr = GetNonMnemonicString( aStr ); 6724 6725 if ( nStyle & TEXT_DRAW_MULTILINE ) 6726 { 6727 ImplMultiTextLineInfo aMultiLineInfo; 6728 ImplTextLineInfo* pLineInfo; 6729 xub_StrLen nFormatLines; 6730 xub_StrLen i; 6731 6732 nMaxWidth = 0; 6733 DefaultTextLayout aDefaultLayout( *const_cast< OutputDevice* >( this ) ); 6734 ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _pTextLayout ? *_pTextLayout : aDefaultLayout ); 6735 nFormatLines = aMultiLineInfo.Count(); 6736 if ( !nTextHeight ) 6737 nTextHeight = 1; 6738 nLines = (sal_uInt16)(aRect.GetHeight()/nTextHeight); 6739 if ( pInfo ) 6740 pInfo->mnLineCount = nFormatLines; 6741 if ( !nLines ) 6742 nLines = 1; 6743 if ( nFormatLines <= nLines ) 6744 nLines = nFormatLines; 6745 else 6746 { 6747 if ( !(nStyle & TEXT_DRAW_ENDELLIPSIS) ) 6748 nLines = nFormatLines; 6749 else 6750 { 6751 if ( pInfo ) 6752 pInfo->mbEllipsis = sal_True; 6753 nMaxWidth = nWidth; 6754 } 6755 } 6756 if ( pInfo ) 6757 { 6758 sal_Bool bMaxWidth = nMaxWidth == 0; 6759 pInfo->mnMaxWidth = 0; 6760 for ( i = 0; i < nLines; i++ ) 6761 { 6762 pLineInfo = aMultiLineInfo.GetLine( i ); 6763 if ( bMaxWidth && (pLineInfo->GetWidth() > nMaxWidth) ) 6764 nMaxWidth = pLineInfo->GetWidth(); 6765 if ( pLineInfo->GetWidth() > pInfo->mnMaxWidth ) 6766 pInfo->mnMaxWidth = pLineInfo->GetWidth(); 6767 } 6768 } 6769 else if ( !nMaxWidth ) 6770 { 6771 for ( i = 0; i < nLines; i++ ) 6772 { 6773 pLineInfo = aMultiLineInfo.GetLine( i ); 6774 if ( pLineInfo->GetWidth() > nMaxWidth ) 6775 nMaxWidth = pLineInfo->GetWidth(); 6776 } 6777 } 6778 } 6779 else 6780 { 6781 nLines = 1; 6782 nMaxWidth = _pTextLayout ? _pTextLayout->GetTextWidth( aStr, 0, aStr.Len() ) : GetTextWidth( aStr ); 6783 6784 if ( pInfo ) 6785 { 6786 pInfo->mnLineCount = 1; 6787 pInfo->mnMaxWidth = nMaxWidth; 6788 } 6789 6790 if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) ) 6791 { 6792 if ( pInfo ) 6793 pInfo->mbEllipsis = sal_True; 6794 nMaxWidth = nWidth; 6795 } 6796 } 6797 6798 if ( nStyle & TEXT_DRAW_RIGHT ) 6799 aRect.Left() = aRect.Right()-nMaxWidth+1; 6800 else if ( nStyle & TEXT_DRAW_CENTER ) 6801 { 6802 aRect.Left() += (nWidth-nMaxWidth)/2; 6803 aRect.Right() = aRect.Left()+nMaxWidth-1; 6804 } 6805 else 6806 aRect.Right() = aRect.Left()+nMaxWidth-1; 6807 6808 if ( nStyle & TEXT_DRAW_BOTTOM ) 6809 aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1; 6810 else if ( nStyle & TEXT_DRAW_VCENTER ) 6811 { 6812 aRect.Top() += (aRect.GetHeight()-(nTextHeight*nLines))/2; 6813 aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1; 6814 } 6815 else 6816 aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1; 6817 6818 aRect.Right()++; // #99188# get rid of rounding problems when using this rect later 6819 return aRect; 6820 } 6821 6822 // ----------------------------------------------------------------------- 6823 6824 static sal_Bool ImplIsCharIn( xub_Unicode c, const sal_Char* pStr ) 6825 { 6826 while ( *pStr ) 6827 { 6828 if ( *pStr == c ) 6829 return sal_True; 6830 pStr++; 6831 } 6832 6833 return sal_False; 6834 } 6835 6836 // ----------------------------------------------------------------------- 6837 6838 String OutputDevice::GetEllipsisString( const String& rOrigStr, long nMaxWidth, 6839 sal_uInt16 nStyle ) const 6840 { 6841 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 6842 DefaultTextLayout aTextLayout( *const_cast< OutputDevice* >( this ) ); 6843 return ImplGetEllipsisString( *this, rOrigStr, nMaxWidth, nStyle, aTextLayout ); 6844 } 6845 6846 // ----------------------------------------------------------------------- 6847 6848 String OutputDevice::ImplGetEllipsisString( const OutputDevice& rTargetDevice, const XubString& rOrigStr, long nMaxWidth, 6849 sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout ) 6850 { 6851 DBG_TRACE( "OutputDevice::ImplGetEllipsisString()" ); 6852 6853 String aStr = rOrigStr; 6854 xub_StrLen nIndex = _rLayout.GetTextBreak( aStr, nMaxWidth, 0, aStr.Len() ); 6855 6856 6857 if ( nIndex != STRING_LEN ) 6858 { 6859 if( (nStyle & TEXT_DRAW_CENTERELLIPSIS) == TEXT_DRAW_CENTERELLIPSIS ) 6860 { 6861 String aTmpStr( aStr ); 6862 xub_StrLen nEraseChars = 4; 6863 while( nEraseChars < aStr.Len() && _rLayout.GetTextWidth( aTmpStr, 0, aTmpStr.Len() ) > nMaxWidth ) 6864 { 6865 aTmpStr = aStr; 6866 xub_StrLen i = (aTmpStr.Len() - nEraseChars)/2; 6867 aTmpStr.Erase( i, nEraseChars++ ); 6868 aTmpStr.InsertAscii( "...", i ); 6869 } 6870 aStr = aTmpStr; 6871 } 6872 else if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) 6873 { 6874 aStr.Erase( nIndex ); 6875 if ( nIndex > 1 ) 6876 { 6877 aStr.AppendAscii( "..." ); 6878 while ( aStr.Len() && (_rLayout.GetTextWidth( aStr, 0, aStr.Len() ) > nMaxWidth) ) 6879 { 6880 if ( (nIndex > 1) || (nIndex == aStr.Len()) ) 6881 nIndex--; 6882 aStr.Erase( nIndex, 1 ); 6883 } 6884 } 6885 6886 if ( !aStr.Len() && (nStyle & TEXT_DRAW_CLIP) ) 6887 aStr += rOrigStr.GetChar( 0 ); 6888 } 6889 else if ( nStyle & TEXT_DRAW_PATHELLIPSIS ) 6890 { 6891 rtl::OUString aPath( rOrigStr ); 6892 rtl::OUString aAbbreviatedPath; 6893 osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, NULL ); 6894 aStr = aAbbreviatedPath; 6895 } 6896 else if ( nStyle & TEXT_DRAW_NEWSELLIPSIS ) 6897 { 6898 static sal_Char const pSepChars[] = "."; 6899 // Letztes Teilstueck ermitteln 6900 xub_StrLen nLastContent = aStr.Len(); 6901 while ( nLastContent ) 6902 { 6903 nLastContent--; 6904 if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) ) 6905 break; 6906 } 6907 while ( nLastContent && 6908 ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) ) 6909 nLastContent--; 6910 6911 XubString aLastStr( aStr, nLastContent, aStr.Len() ); 6912 XubString aTempLastStr1( RTL_CONSTASCII_USTRINGPARAM( "..." ) ); 6913 aTempLastStr1 += aLastStr; 6914 if ( _rLayout.GetTextWidth( aTempLastStr1, 0, aTempLastStr1.Len() ) > nMaxWidth ) 6915 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout ); 6916 else 6917 { 6918 sal_uInt16 nFirstContent = 0; 6919 while ( nFirstContent < nLastContent ) 6920 { 6921 nFirstContent++; 6922 if ( ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) ) 6923 break; 6924 } 6925 while ( (nFirstContent < nLastContent) && 6926 ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) ) 6927 nFirstContent++; 6928 6929 if ( nFirstContent >= nLastContent ) 6930 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout ); 6931 else 6932 { 6933 if ( nFirstContent > 4 ) 6934 nFirstContent = 4; 6935 XubString aFirstStr( aStr, 0, nFirstContent ); 6936 aFirstStr.AppendAscii( "..." ); 6937 XubString aTempStr = aFirstStr; 6938 aTempStr += aLastStr; 6939 if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.Len() ) > nMaxWidth ) 6940 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout ); 6941 else 6942 { 6943 do 6944 { 6945 aStr = aTempStr; 6946 if( nLastContent > aStr.Len() ) 6947 nLastContent = aStr.Len(); 6948 while ( nFirstContent < nLastContent ) 6949 { 6950 nLastContent--; 6951 if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) ) 6952 break; 6953 6954 } 6955 while ( (nFirstContent < nLastContent) && 6956 ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) ) 6957 nLastContent--; 6958 6959 if ( nFirstContent < nLastContent ) 6960 { 6961 XubString aTempLastStr( aStr, nLastContent, aStr.Len() ); 6962 aTempStr = aFirstStr; 6963 aTempStr += aTempLastStr; 6964 if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.Len() ) > nMaxWidth ) 6965 break; 6966 } 6967 } 6968 while ( nFirstContent < nLastContent ); 6969 } 6970 } 6971 } 6972 } 6973 } 6974 6975 return aStr; 6976 } 6977 6978 // ----------------------------------------------------------------------- 6979 6980 void OutputDevice::DrawCtrlText( const Point& rPos, const XubString& rStr, 6981 xub_StrLen nIndex, xub_StrLen nLen, 6982 sal_uInt16 nStyle, MetricVector* pVector, String* pDisplayText ) 6983 { 6984 DBG_TRACE( "OutputDevice::DrawCtrlText()" ); 6985 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 6986 6987 if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.Len()) ) 6988 return; 6989 6990 // better get graphics here because ImplDrawMnemonicLine() will not 6991 // we need a graphics 6992 if( !mpGraphics && !ImplGetGraphics() ) 6993 return; 6994 if( mbInitClipRegion ) 6995 ImplInitClipRegion(); 6996 if ( mbOutputClipped ) 6997 return; 6998 6999 if( nIndex >= rStr.Len() ) 7000 return; 7001 if( (sal_uLong)nIndex+nLen >= rStr.Len() ) 7002 nLen = rStr.Len() - nIndex; 7003 7004 XubString aStr = rStr; 7005 xub_StrLen nMnemonicPos = STRING_NOTFOUND; 7006 7007 long nMnemonicX = 0; 7008 long nMnemonicY = 0; 7009 long nMnemonicWidth = 0; 7010 if ( (nStyle & TEXT_DRAW_MNEMONIC) && nLen > 1 ) 7011 { 7012 aStr = GetNonMnemonicString( aStr, nMnemonicPos ); 7013 if ( nMnemonicPos != STRING_NOTFOUND ) 7014 { 7015 if( nMnemonicPos < nIndex ) 7016 --nIndex; 7017 else if( nLen < STRING_LEN ) 7018 { 7019 if( nMnemonicPos < (nIndex+nLen) ) 7020 --nLen; 7021 DBG_ASSERT( nMnemonicPos < (nIndex+nLen), "Mnemonic underline marker after last character" ); 7022 } 7023 sal_Bool bInvalidPos = sal_False; 7024 7025 if( nMnemonicPos >= nLen ) 7026 { 7027 // #106952# 7028 // may occur in BiDi-Strings: the '~' is sometimes found behind the last char 7029 // due to some strange BiDi text editors 7030 // ->place the underline behind the string to indicate a failure 7031 bInvalidPos = sal_True; 7032 nMnemonicPos = nLen-1; 7033 } 7034 7035 sal_Int32* pCaretXArray = (sal_Int32*)alloca( 2 * sizeof(sal_Int32) * nLen ); 7036 /*sal_Bool bRet =*/ GetCaretPositions( aStr, pCaretXArray, nIndex, nLen ); 7037 long lc_x1 = pCaretXArray[ 2*(nMnemonicPos - nIndex) ]; 7038 long lc_x2 = pCaretXArray[ 2*(nMnemonicPos - nIndex)+1 ]; 7039 nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2)); 7040 7041 Point aTempPos( Min(lc_x1,lc_x2), GetFontMetric().GetAscent() ); 7042 if( bInvalidPos ) // #106952#, place behind the (last) character 7043 aTempPos = Point( Max(lc_x1,lc_x2), GetFontMetric().GetAscent() ); 7044 7045 aTempPos += rPos; 7046 aTempPos = LogicToPixel( aTempPos ); 7047 nMnemonicX = mnOutOffX + aTempPos.X(); 7048 nMnemonicY = mnOutOffY + aTempPos.Y(); 7049 } 7050 } 7051 7052 if ( nStyle & TEXT_DRAW_DISABLE && ! pVector ) 7053 { 7054 Color aOldTextColor; 7055 Color aOldTextFillColor; 7056 sal_Bool bRestoreFillColor; 7057 sal_Bool bHighContrastBlack = sal_False; 7058 sal_Bool bHighContrastWhite = sal_False; 7059 const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() ); 7060 if( rStyleSettings.GetHighContrastMode() ) 7061 { 7062 if( IsBackground() ) 7063 { 7064 Wallpaper aWall = GetBackground(); 7065 Color aCol = aWall.GetColor(); 7066 bHighContrastBlack = aCol.IsDark(); 7067 bHighContrastWhite = aCol.IsBright(); 7068 } 7069 } 7070 7071 aOldTextColor = GetTextColor(); 7072 if ( IsTextFillColor() ) 7073 { 7074 bRestoreFillColor = sal_True; 7075 aOldTextFillColor = GetTextFillColor(); 7076 } 7077 else 7078 bRestoreFillColor = sal_False; 7079 7080 if( bHighContrastBlack ) 7081 SetTextColor( COL_GREEN ); 7082 else if( bHighContrastWhite ) 7083 SetTextColor( COL_LIGHTGREEN ); 7084 else 7085 SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() ); 7086 7087 DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText ); 7088 if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector ) 7089 { 7090 if ( nMnemonicPos != STRING_NOTFOUND ) 7091 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); 7092 } 7093 SetTextColor( aOldTextColor ); 7094 if ( bRestoreFillColor ) 7095 SetTextFillColor( aOldTextFillColor ); 7096 } 7097 else 7098 { 7099 DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText ); 7100 if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector ) 7101 { 7102 if ( nMnemonicPos != STRING_NOTFOUND ) 7103 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth ); 7104 } 7105 } 7106 7107 if( mpAlphaVDev ) 7108 mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText ); 7109 } 7110 7111 // ----------------------------------------------------------------------- 7112 7113 long OutputDevice::GetCtrlTextWidth( const String& rStr, 7114 xub_StrLen nIndex, xub_StrLen nLen, 7115 sal_uInt16 nStyle ) const 7116 { 7117 DBG_TRACE( "OutputDevice::GetCtrlTextSize()" ); 7118 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7119 7120 if ( nStyle & TEXT_DRAW_MNEMONIC ) 7121 { 7122 xub_StrLen nMnemonicPos; 7123 XubString aStr = GetNonMnemonicString( rStr, nMnemonicPos ); 7124 if ( nMnemonicPos != STRING_NOTFOUND ) 7125 { 7126 if ( nMnemonicPos < nIndex ) 7127 nIndex--; 7128 else if ( (nLen < STRING_LEN) && 7129 (nMnemonicPos >= nIndex) && (nMnemonicPos < (sal_uLong)(nIndex+nLen)) ) 7130 nLen--; 7131 } 7132 return GetTextWidth( aStr, nIndex, nLen ); 7133 } 7134 else 7135 return GetTextWidth( rStr, nIndex, nLen ); 7136 } 7137 7138 // ----------------------------------------------------------------------- 7139 7140 String OutputDevice::GetNonMnemonicString( const String& rStr, xub_StrLen& rMnemonicPos ) 7141 { 7142 String aStr = rStr; 7143 xub_StrLen nLen = aStr.Len(); 7144 xub_StrLen i = 0; 7145 7146 rMnemonicPos = STRING_NOTFOUND; 7147 while ( i < nLen ) 7148 { 7149 if ( aStr.GetChar( i ) == '~' ) 7150 { 7151 if ( aStr.GetChar( i+1 ) != '~' ) 7152 { 7153 if ( rMnemonicPos == STRING_NOTFOUND ) 7154 rMnemonicPos = i; 7155 aStr.Erase( i, 1 ); 7156 nLen--; 7157 } 7158 else 7159 { 7160 aStr.Erase( i, 1 ); 7161 nLen--; 7162 i++; 7163 } 7164 } 7165 else 7166 i++; 7167 } 7168 7169 return aStr; 7170 } 7171 7172 // ----------------------------------------------------------------------- 7173 7174 int OutputDevice::GetDevFontCount() const 7175 { 7176 DBG_TRACE( "OutputDevice::GetDevFontCount()" ); 7177 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7178 7179 if( !mpGetDevFontList ) 7180 mpGetDevFontList = mpFontList->GetDevFontList(); 7181 return mpGetDevFontList->Count(); 7182 } 7183 7184 // ----------------------------------------------------------------------- 7185 7186 FontInfo OutputDevice::GetDevFont( int nDevFontIndex ) const 7187 { 7188 DBG_TRACE( "OutputDevice::GetDevFont()" ); 7189 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7190 7191 FontInfo aFontInfo; 7192 7193 ImplInitFontList(); 7194 7195 int nCount = GetDevFontCount(); 7196 if( nDevFontIndex < nCount ) 7197 { 7198 const ImplFontData& rData = *mpGetDevFontList->Get( nDevFontIndex ); 7199 aFontInfo.SetName( rData.maName ); 7200 aFontInfo.SetStyleName( rData.maStyleName ); 7201 aFontInfo.SetCharSet( rData.mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); 7202 aFontInfo.SetFamily( rData.meFamily ); 7203 aFontInfo.SetPitch( rData.mePitch ); 7204 aFontInfo.SetWeight( rData.meWeight ); 7205 aFontInfo.SetItalic( rData.meItalic ); 7206 aFontInfo.SetWidthType( rData.meWidthType ); 7207 if( rData.IsScalable() ) 7208 aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG; 7209 if( rData.mbDevice ) 7210 aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG; 7211 } 7212 7213 return aFontInfo; 7214 } 7215 7216 // ----------------------------------------------------------------------- 7217 7218 sal_Bool OutputDevice::AddTempDevFont( const String& rFileURL, const String& rFontName ) 7219 { 7220 DBG_TRACE( "OutputDevice::AddTempDevFont()" ); 7221 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7222 7223 ImplInitFontList(); 7224 7225 if( !mpGraphics && !ImplGetGraphics() ) 7226 return sal_False; 7227 7228 bool bRC = mpGraphics->AddTempDevFont( mpFontList, rFileURL, rFontName ); 7229 if( !bRC ) 7230 return sal_False; 7231 7232 if( mpAlphaVDev ) 7233 mpAlphaVDev->AddTempDevFont( rFileURL, rFontName ); 7234 7235 mpFontCache->Invalidate(); 7236 return sal_True; 7237 } 7238 7239 // ----------------------------------------------------------------------- 7240 7241 int OutputDevice::GetDevFontSizeCount( const Font& rFont ) const 7242 { 7243 DBG_TRACE( "OutputDevice::GetDevFontSizeCount()" ); 7244 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7245 7246 delete mpGetDevSizeList; 7247 7248 ImplInitFontList(); 7249 mpGetDevSizeList = mpFontList->GetDevSizeList( rFont.GetName() ); 7250 return mpGetDevSizeList->Count(); 7251 } 7252 7253 // ----------------------------------------------------------------------- 7254 7255 Size OutputDevice::GetDevFontSize( const Font& rFont, int nSizeIndex ) const 7256 { 7257 DBG_TRACE( "OutputDevice::GetDevFontSize()" ); 7258 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7259 7260 // check range 7261 int nCount = GetDevFontSizeCount( rFont ); 7262 if ( nSizeIndex >= nCount ) 7263 return Size(); 7264 7265 // when mapping is enabled round to .5 points 7266 Size aSize( 0, mpGetDevSizeList->Get( nSizeIndex ) ); 7267 if ( mbMap ) 7268 { 7269 aSize.Height() *= 10; 7270 MapMode aMap( MAP_10TH_INCH, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) ); 7271 aSize = PixelToLogic( aSize, aMap ); 7272 aSize.Height() += 5; 7273 aSize.Height() /= 10; 7274 long nRound = aSize.Height() % 5; 7275 if ( nRound >= 3 ) 7276 aSize.Height() += (5-nRound); 7277 else 7278 aSize.Height() -= nRound; 7279 aSize.Height() *= 10; 7280 aSize = LogicToPixel( aSize, aMap ); 7281 aSize = PixelToLogic( aSize ); 7282 aSize.Height() += 5; 7283 aSize.Height() /= 10; 7284 } 7285 return aSize; 7286 } 7287 7288 // ----------------------------------------------------------------------- 7289 7290 sal_Bool OutputDevice::IsFontAvailable( const String& rFontName ) const 7291 { 7292 DBG_TRACE( "OutputDevice::IsFontAvailable()" ); 7293 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7294 7295 ImplDevFontListData* pFound = mpFontList->FindFontFamily( rFontName ); 7296 return (pFound != NULL); 7297 } 7298 7299 // ----------------------------------------------------------------------- 7300 7301 FontMetric OutputDevice::GetFontMetric() const 7302 { 7303 DBG_TRACE( "OutputDevice::GetFontMetric()" ); 7304 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7305 7306 FontMetric aMetric; 7307 if( mbNewFont && !ImplNewFont() ) 7308 return aMetric; 7309 7310 ImplFontEntry* pEntry = mpFontEntry; 7311 ImplFontMetricData* pMetric = &(pEntry->maMetric); 7312 7313 // prepare metric 7314 aMetric.Font::operator=( maFont ); 7315 7316 // set aMetric with info from font 7317 aMetric.SetName( maFont.GetName() ); 7318 aMetric.SetStyleName( pMetric->maStyleName ); 7319 aMetric.SetSize( PixelToLogic( Size( pMetric->mnWidth, pMetric->mnAscent+pMetric->mnDescent-pMetric->mnIntLeading ) ) ); 7320 aMetric.SetCharSet( pMetric->mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); 7321 aMetric.SetFamily( pMetric->meFamily ); 7322 aMetric.SetPitch( pMetric->mePitch ); 7323 aMetric.SetWeight( pMetric->meWeight ); 7324 aMetric.SetItalic( pMetric->meItalic ); 7325 aMetric.SetWidthType( pMetric->meWidthType ); 7326 if ( pEntry->mnOwnOrientation ) 7327 aMetric.SetOrientation( pEntry->mnOwnOrientation ); 7328 else 7329 aMetric.SetOrientation( pMetric->mnOrientation ); 7330 if( !pEntry->maMetric.mbKernableFont ) 7331 aMetric.SetKerning( maFont.GetKerning() & ~KERNING_FONTSPECIFIC ); 7332 7333 // set remaining metric fields 7334 aMetric.mpImplMetric->mnMiscFlags = 0; 7335 if( pMetric->mbDevice ) 7336 aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG; 7337 if( pMetric->mbScalableFont ) 7338 aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG; 7339 aMetric.mpImplMetric->mnAscent = ImplDevicePixelToLogicHeight( pMetric->mnAscent+mnEmphasisAscent ); 7340 aMetric.mpImplMetric->mnDescent = ImplDevicePixelToLogicHeight( pMetric->mnDescent+mnEmphasisDescent ); 7341 aMetric.mpImplMetric->mnIntLeading = ImplDevicePixelToLogicHeight( pMetric->mnIntLeading+mnEmphasisAscent ); 7342 aMetric.mpImplMetric->mnExtLeading = ImplDevicePixelToLogicHeight( pMetric->mnExtLeading ); 7343 aMetric.mpImplMetric->mnLineHeight = ImplDevicePixelToLogicHeight( pMetric->mnAscent+pMetric->mnDescent+mnEmphasisAscent+mnEmphasisDescent ); 7344 aMetric.mpImplMetric->mnSlant = ImplDevicePixelToLogicHeight( pMetric->mnSlant ); 7345 7346 #ifdef UNX 7347 // backwards compatible line metrics after fixing #i60945# 7348 if( (meOutDevType == OUTDEV_VIRDEV) 7349 && static_cast<const VirtualDevice*>(this)->ForceZeroExtleadBug() ) 7350 aMetric.mpImplMetric->mnExtLeading = 0; 7351 #endif 7352 7353 return aMetric; 7354 } 7355 7356 // ----------------------------------------------------------------------- 7357 7358 FontMetric OutputDevice::GetFontMetric( const Font& rFont ) const 7359 { 7360 // select font, query metrics, select original font again 7361 Font aOldFont = GetFont(); 7362 const_cast<OutputDevice*>(this)->SetFont( rFont ); 7363 FontMetric aMetric( GetFontMetric() ); 7364 const_cast<OutputDevice*>(this)->SetFont( aOldFont ); 7365 return aMetric; 7366 } 7367 7368 // ----------------------------------------------------------------------- 7369 7370 /** OutputDevice::GetSysFontData 7371 * 7372 * @param nFallbacklevel Fallback font level (0 = best matching font) 7373 * 7374 * Retrieve detailed font information in platform independent structure 7375 * 7376 * @return SystemFontData 7377 **/ 7378 SystemFontData OutputDevice::GetSysFontData(int nFallbacklevel) const 7379 { 7380 SystemFontData aSysFontData; 7381 aSysFontData.nSize = sizeof(aSysFontData); 7382 7383 if (!mpGraphics) ImplGetGraphics(); 7384 if (mpGraphics) aSysFontData = mpGraphics->GetSysFontData(nFallbacklevel); 7385 7386 return aSysFontData; 7387 } 7388 7389 7390 // ----------------------------------------------------------------------- 7391 7392 /** OutputDevice::GetSysTextLayoutData 7393 * 7394 * @param rStartPt Start point of the text 7395 * @param rStr Text string that will be transformed into layout of glyphs 7396 * @param nIndex Position in the string from where layout will be done 7397 * @param nLen Length of the string 7398 * @param pDXAry Custom layout adjustment data 7399 * 7400 * Export finalized glyph layout data as platform independent SystemTextLayoutData 7401 * (see vcl/inc/vcl/sysdata.hxx) 7402 * 7403 * Only parameters rStartPt and rStr are mandatory, the rest is optional 7404 * (default values will be used) 7405 * 7406 * @return SystemTextLayoutData 7407 **/ 7408 SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, const XubString& rStr, xub_StrLen nIndex, xub_StrLen nLen, 7409 const sal_Int32* pDXAry) const 7410 { 7411 DBG_TRACE( "OutputDevice::GetSysTextLayoutData()" ); 7412 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7413 7414 SystemTextLayoutData aSysLayoutData; 7415 aSysLayoutData.nSize = sizeof(aSysLayoutData); 7416 aSysLayoutData.rGlyphData.reserve( 256 ); 7417 7418 if ( mpMetaFile ) { 7419 if (pDXAry) 7420 mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) ); 7421 else 7422 mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) ); 7423 } 7424 7425 if ( !IsDeviceOutputNecessary() ) return aSysLayoutData; 7426 7427 SalLayout* rLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true ); 7428 7429 // setup glyphs 7430 Point aPos; 7431 sal_GlyphId aGlyphId; 7432 for( int nStart = 0; rLayout->GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); ) 7433 { 7434 // NOTE: Windows backend is producing unicode chars (ucs4), so on windows, 7435 // ETO_GLYPH_INDEX is unusable, unless extra glyph conversion is made. 7436 7437 SystemGlyphData aGlyph; 7438 aGlyph.index = static_cast<unsigned long> (aGlyphId & GF_IDXMASK); 7439 aGlyph.x = aPos.X(); 7440 aGlyph.y = aPos.Y(); 7441 int nLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT; 7442 aGlyph.fallbacklevel = nLevel < MAX_FALLBACK ? nLevel : 0; 7443 aSysLayoutData.rGlyphData.push_back(aGlyph); 7444 } 7445 7446 // Get font data 7447 aSysLayoutData.orientation = rLayout->GetOrientation(); 7448 7449 rLayout->Release(); 7450 7451 return aSysLayoutData; 7452 } 7453 7454 // ----------------------------------------------------------------------- 7455 7456 7457 long OutputDevice::GetMinKashida() const 7458 { 7459 DBG_TRACE( "OutputDevice::GetMinKashida()" ); 7460 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7461 if( mbNewFont && !ImplNewFont() ) 7462 return 0; 7463 7464 ImplFontEntry* pEntry = mpFontEntry; 7465 ImplFontMetricData* pMetric = &(pEntry->maMetric); 7466 return ImplDevicePixelToLogicWidth( pMetric->mnMinKashida ); 7467 } 7468 // ----------------------------------------------------------------------- 7469 7470 long OutputDevice::GetMinKashida( const Font& rFont ) const 7471 { 7472 // select font, query Kashida, select original font again 7473 Font aOldFont = GetFont(); 7474 const_cast<OutputDevice*>(this)->SetFont( rFont ); 7475 long aKashida = GetMinKashida(); 7476 const_cast<OutputDevice*>(this)->SetFont( aOldFont ); 7477 return aKashida; 7478 } 7479 7480 // ----------------------------------------------------------------------- 7481 xub_StrLen OutputDevice::ValidateKashidas ( const String& rTxt, 7482 xub_StrLen nIdx, xub_StrLen nLen, 7483 xub_StrLen nKashCount, 7484 const xub_StrLen* pKashidaPos, 7485 xub_StrLen* pKashidaPosDropped ) const 7486 { 7487 // do layout 7488 SalLayout* pSalLayout = ImplLayout( rTxt, nIdx, nLen ); 7489 if( !pSalLayout ) 7490 return 0; 7491 xub_StrLen nDropped = 0; 7492 for( int i = 0; i < nKashCount; ++i ) 7493 { 7494 if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] )) 7495 { 7496 pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ]; 7497 ++nDropped; 7498 } 7499 } 7500 pSalLayout->Release(); 7501 return nDropped; 7502 } 7503 7504 // ----------------------------------------------------------------------- 7505 7506 sal_Bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const String& rStr, 7507 int nIndex, int nLen, int nBase, MetricVector& rVector ) 7508 { 7509 DBG_TRACE( "OutputDevice::GetGlyphBoundRect_CTL()" ); 7510 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7511 7512 rVector.clear(); 7513 7514 if( nLen == STRING_LEN ) 7515 nLen = rStr.Len() - nIndex; 7516 7517 Rectangle aRect; 7518 for( int i = 0; i < nLen; i++ ) 7519 { 7520 if( !GetTextBoundRect( aRect, rStr, sal::static_int_cast<xub_StrLen>(nBase), sal::static_int_cast<xub_StrLen>(nIndex+i), 1 ) ) 7521 break; 7522 aRect.Move( rOrigin.X(), rOrigin.Y() ); 7523 rVector.push_back( aRect ); 7524 } 7525 7526 return (nLen == (int)rVector.size()); 7527 } 7528 7529 // ----------------------------------------------------------------------- 7530 7531 sal_Bool OutputDevice::GetTextBoundRect( Rectangle& rRect, 7532 const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen, 7533 sal_uLong nLayoutWidth, const sal_Int32* pDXAry ) const 7534 { 7535 DBG_TRACE( "OutputDevice::GetTextBoundRect()" ); 7536 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); 7537 7538 sal_Bool bRet = sal_False; 7539 rRect.SetEmpty(); 7540 7541 SalLayout* pSalLayout = NULL; 7542 const Point aPoint; 7543 // calculate offset when nBase!=nIndex 7544 long nXOffset = 0; 7545 if( nBase != nIndex ) 7546 { 7547 xub_StrLen nStart = Min( nBase, nIndex ); 7548 xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart; 7549 pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry ); 7550 if( pSalLayout ) 7551 { 7552 nXOffset = pSalLayout->GetTextWidth(); 7553 nXOffset /= pSalLayout->GetUnitsPerPixel(); 7554 pSalLayout->Release(); 7555 // TODO: fix offset calculation for Bidi case 7556 if( nBase < nIndex) 7557 nXOffset = -nXOffset; 7558 } 7559 } 7560 7561 pSalLayout = ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry ); 7562 Rectangle aPixelRect; 7563 if( pSalLayout ) 7564 { 7565 bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect ); 7566 7567 if( bRet ) 7568 { 7569 int nWidthFactor = pSalLayout->GetUnitsPerPixel(); 7570 7571 if( nWidthFactor > 1 ) 7572 { 7573 double fFactor = 1.0 / nWidthFactor; 7574 aPixelRect.Left() 7575 = static_cast< long >(aPixelRect.Left() * fFactor); 7576 aPixelRect.Right() 7577 = static_cast< long >(aPixelRect.Right() * fFactor); 7578 aPixelRect.Top() 7579 = static_cast< long >(aPixelRect.Top() * fFactor); 7580 aPixelRect.Bottom() 7581 = static_cast< long >(aPixelRect.Bottom() * fFactor); 7582 } 7583 7584 Point aRotatedOfs( mnTextOffX, mnTextOffY ); 7585 aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) ); 7586 aPixelRect += aRotatedOfs; 7587 rRect = PixelToLogic( aPixelRect ); 7588 if( mbMap ) 7589 rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY ); 7590 } 7591 7592 pSalLayout->Release(); 7593 } 7594 7595 if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry ) 7596 return bRet; 7597 7598 // fall back to bitmap method to get the bounding rectangle, 7599 // so we need a monochrome virtual device with matching font 7600 VirtualDevice aVDev( 1 ); 7601 Font aFont( GetFont() ); 7602 aFont.SetShadow( sal_False ); 7603 aFont.SetOutline( sal_False ); 7604 aFont.SetRelief( RELIEF_NONE ); 7605 aFont.SetOrientation( 0 ); 7606 aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) ); 7607 aVDev.SetFont( aFont ); 7608 aVDev.SetTextAlign( ALIGN_TOP ); 7609 7610 // layout the text on the virtual device 7611 pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry ); 7612 if( !pSalLayout ) 7613 return false; 7614 7615 // make the bitmap big enough 7616 // TODO: use factors when it would get too big 7617 long nWidth = pSalLayout->GetTextWidth(); 7618 long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent; 7619 Point aOffset( nWidth/2, 8 ); 7620 Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() ); 7621 if( !nWidth || !aVDev.SetOutputSizePixel( aOutSize ) ) 7622 return false; 7623 7624 // draw text in black 7625 pSalLayout->DrawBase() = aOffset; 7626 aVDev.SetTextColor( Color( COL_BLACK ) ); 7627 aVDev.SetTextFillColor(); 7628 aVDev.ImplInitTextColor(); 7629 aVDev.ImplDrawText( *pSalLayout ); 7630 pSalLayout->Release(); 7631 7632 // find extents using the bitmap 7633 Bitmap aBmp = aVDev.GetBitmap( Point(), aOutSize ); 7634 BitmapReadAccess* pAcc = aBmp.AcquireReadAccess(); 7635 if( !pAcc ) 7636 return sal_False; 7637 const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); 7638 const long nW = pAcc->Width(); 7639 const long nH = pAcc->Height(); 7640 long nLeft = 0; 7641 long nRight = 0; 7642 7643 // find top left point 7644 long nTop = 0; 7645 for(; nTop < nH; ++nTop ) 7646 { 7647 for( nLeft = 0; nLeft < nW; ++nLeft ) 7648 if( pAcc->GetPixel( nTop, nLeft ) == aBlack ) 7649 break; 7650 if( nLeft < nW ) 7651 break; 7652 } 7653 7654 // find bottom right point 7655 long nBottom = nH; 7656 while( --nBottom >= nTop ) 7657 { 7658 for( nRight = nW; --nRight >= 0; ) 7659 if( pAcc->GetPixel( nBottom, nRight ) == aBlack ) 7660 break; 7661 if( nRight >= 0 ) 7662 break; 7663 } 7664 if( nRight < nLeft ) 7665 { 7666 long nX = nRight; 7667 nRight = nLeft; 7668 nLeft = nX; 7669 } 7670 7671 for( long nY = nTop; nY <= nBottom; ++nY ) 7672 { 7673 // find leftmost point 7674 long nX; 7675 for( nX = 0; nX < nLeft; ++nX ) 7676 if( pAcc->GetPixel( nY, nX ) == aBlack ) 7677 break; 7678 nLeft = nX; 7679 7680 // find rightmost point 7681 for( nX = nW; --nX > nRight; ) 7682 if( pAcc->GetPixel( nY, nX ) == aBlack ) 7683 break; 7684 nRight = nX; 7685 } 7686 7687 aBmp.ReleaseAccess( pAcc ); 7688 7689 if( nTop <= nBottom ) 7690 { 7691 Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 ); 7692 Point aTopLeft( nLeft, nTop ); 7693 aTopLeft -= aOffset; 7694 // adjust to text alignment 7695 aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent); 7696 // convert to logical coordinates 7697 aSize = PixelToLogic( aSize ); 7698 aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() ); 7699 aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() ); 7700 rRect = Rectangle( aTopLeft, aSize ); 7701 return sal_True; 7702 } 7703 7704 return sal_False; 7705 } 7706 7707 // ----------------------------------------------------------------------- 7708 7709 sal_Bool OutputDevice::GetTextOutlines( ::basegfx::B2DPolyPolygonVector& rVector, 7710 const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen, 7711 sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const 7712 { 7713 // the fonts need to be initialized 7714 if( mbNewFont ) 7715 ImplNewFont(); 7716 if( mbInitFont ) 7717 ImplInitFont(); 7718 if( !mpFontEntry ) 7719 return sal_False; 7720 7721 sal_Bool bRet = sal_False; 7722 rVector.clear(); 7723 if( nLen == STRING_LEN ) 7724 nLen = rStr.Len() - nIndex; 7725 rVector.reserve( nLen ); 7726 7727 // we want to get the Rectangle in logical units, so to 7728 // avoid rounding errors we just size the font in logical units 7729 sal_Bool bOldMap = mbMap; 7730 if( bOldMap ) 7731 { 7732 const_cast<OutputDevice&>(*this).mbMap = sal_False; 7733 const_cast<OutputDevice&>(*this).mbNewFont = sal_True; 7734 } 7735 7736 SalLayout* pSalLayout = NULL; 7737 7738 // calculate offset when nBase!=nIndex 7739 long nXOffset = 0; 7740 if( nBase != nIndex ) 7741 { 7742 xub_StrLen nStart = Min( nBase, nIndex ); 7743 xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart; 7744 pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nTWidth, pDXArray ); 7745 if( pSalLayout ) 7746 { 7747 nXOffset = pSalLayout->GetTextWidth(); 7748 pSalLayout->Release(); 7749 // TODO: fix offset calculation for Bidi case 7750 if( nBase > nIndex) 7751 nXOffset = -nXOffset; 7752 } 7753 } 7754 7755 pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray ); 7756 if( pSalLayout ) 7757 { 7758 bRet = pSalLayout->GetOutline( *mpGraphics, rVector ); 7759 if( bRet ) 7760 { 7761 // transform polygon to pixel units 7762 ::basegfx::B2DHomMatrix aMatrix; 7763 7764 int nWidthFactor = pSalLayout->GetUnitsPerPixel(); 7765 if( nXOffset | mnTextOffX | mnTextOffY ) 7766 { 7767 Point aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor ); 7768 aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) ); 7769 aMatrix.translate( aRotatedOfs.X(), aRotatedOfs.Y() ); 7770 } 7771 7772 if( nWidthFactor > 1 ) 7773 { 7774 double fFactor = 1.0 / nWidthFactor; 7775 aMatrix.scale( fFactor, fFactor ); 7776 } 7777 7778 if( !aMatrix.isIdentity() ) 7779 { 7780 ::basegfx::B2DPolyPolygonVector::iterator aIt = rVector.begin(); 7781 for(; aIt != rVector.end(); ++aIt ) 7782 (*aIt).transform( aMatrix ); 7783 } 7784 } 7785 7786 pSalLayout->Release(); 7787 } 7788 7789 if( bOldMap ) 7790 { 7791 // restore original font size and map mode 7792 const_cast<OutputDevice&>(*this).mbMap = bOldMap; 7793 const_cast<OutputDevice&>(*this).mbNewFont = sal_True; 7794 } 7795 7796 if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry ) 7797 return bRet; 7798 7799 // fall back to bitmap conversion ------------------------------------------ 7800 7801 // Here, we can savely assume that the mapping between characters and glyphs 7802 // is one-to-one. This is most probably valid for the old bitmap fonts. 7803 7804 // fall back to bitmap method to get the bounding rectangle, 7805 // so we need a monochrome virtual device with matching font 7806 pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray ); 7807 if (pSalLayout == 0) 7808 return false; 7809 long nOrgWidth = pSalLayout->GetTextWidth(); 7810 long nOrgHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent 7811 + mnEmphasisDescent; 7812 pSalLayout->Release(); 7813 7814 VirtualDevice aVDev(1); 7815 7816 Font aFont(GetFont()); 7817 aFont.SetShadow(false); 7818 aFont.SetOutline(false); 7819 aFont.SetRelief(RELIEF_NONE); 7820 aFont.SetOrientation(0); 7821 if( bOptimize ) 7822 { 7823 aFont.SetSize( Size( 0, GLYPH_FONT_HEIGHT ) ); 7824 aVDev.SetMapMode( MAP_PIXEL ); 7825 } 7826 aVDev.SetFont( aFont ); 7827 aVDev.SetTextAlign( ALIGN_TOP ); 7828 aVDev.SetTextColor( Color(COL_BLACK) ); 7829 aVDev.SetTextFillColor(); 7830 7831 pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray ); 7832 if (pSalLayout == 0) 7833 return false; 7834 long nWidth = pSalLayout->GetTextWidth(); 7835 long nHeight = ((OutputDevice*)&aVDev)->mpFontEntry->mnLineHeight + ((OutputDevice*)&aVDev)->mnEmphasisAscent 7836 + ((OutputDevice*)&aVDev)->mnEmphasisDescent; 7837 pSalLayout->Release(); 7838 7839 if( !nWidth || !nHeight ) 7840 return sal_True; 7841 double fScaleX = static_cast< double >(nOrgWidth) / nWidth; 7842 double fScaleY = static_cast< double >(nOrgHeight) / nHeight; 7843 7844 // calculate offset when nBase!=nIndex 7845 // TODO: fix offset calculation for Bidi case 7846 nXOffset = 0; 7847 if( nBase != nIndex ) 7848 { 7849 xub_StrLen nStart = ((nBase < nIndex) ? nBase : nIndex); 7850 xub_StrLen nLength = ((nBase > nIndex) ? nBase : nIndex) - nStart; 7851 pSalLayout = aVDev.ImplLayout( rStr, nStart, nLength, Point(0,0), nTWidth, pDXArray ); 7852 if( pSalLayout ) 7853 { 7854 nXOffset = pSalLayout->GetTextWidth(); 7855 pSalLayout->Release(); 7856 if( nBase > nIndex) 7857 nXOffset = -nXOffset; 7858 } 7859 } 7860 7861 bRet = true; 7862 bool bRTL = false; 7863 String aStr( rStr ); // prepare for e.g. localized digits 7864 ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL ); 7865 for( int nCharPos = -1; aLayoutArgs.GetNextPos( &nCharPos, &bRTL);) 7866 { 7867 bool bSuccess = false; 7868 7869 // draw character into virtual device 7870 pSalLayout = aVDev.ImplLayout( rStr, static_cast< xub_StrLen >(nCharPos), 1, Point(0,0), nTWidth, pDXArray ); 7871 if (pSalLayout == 0) 7872 return false; 7873 long nCharWidth = pSalLayout->GetTextWidth(); 7874 7875 Point aOffset(nCharWidth / 2, 8); 7876 Size aSize(nCharWidth + 2 * aOffset.X(), nHeight + 2 * aOffset.Y()); 7877 bSuccess = (bool)aVDev.SetOutputSizePixel(aSize); 7878 if( bSuccess ) 7879 { 7880 // draw glyph into virtual device 7881 aVDev.Erase(); 7882 pSalLayout->DrawBase() += aOffset; 7883 pSalLayout->DrawBase() += Point( ((OutputDevice*)&aVDev)->mnTextOffX, ((OutputDevice*)&aVDev)->mnTextOffY ); 7884 pSalLayout->DrawText( *((OutputDevice*)&aVDev)->mpGraphics ); 7885 pSalLayout->Release(); 7886 7887 // convert character image into outline 7888 Bitmap aBmp( aVDev.GetBitmap(Point(0, 0), aSize)); 7889 7890 PolyPolygon aPolyPoly; 7891 bool bVectorized = aBmp.Vectorize(aPolyPoly, BMP_VECTORIZE_OUTER | BMP_VECTORIZE_REDUCE_EDGES); 7892 if( !bVectorized ) 7893 bSuccess = false; 7894 else 7895 { 7896 // convert units to logical width 7897 for (sal_uInt16 j = 0; j < aPolyPoly.Count(); ++j) 7898 { 7899 Polygon& rPoly = aPolyPoly[j]; 7900 for (sal_uInt16 k = 0; k < rPoly.GetSize(); ++k) 7901 { 7902 Point& rPt = rPoly[k]; 7903 rPt -= aOffset; 7904 int nPixelX = rPt.X() - ((OutputDevice&)aVDev).mnTextOffX + nXOffset; 7905 int nPixelY = rPt.Y() - ((OutputDevice&)aVDev).mnTextOffY; 7906 rPt.X() = ImplDevicePixelToLogicWidth( nPixelX ); 7907 rPt.Y() = ImplDevicePixelToLogicHeight( nPixelY ); 7908 } 7909 } 7910 7911 7912 // ignore "empty" glyphs: 7913 if( aPolyPoly.Count() > 0 ) 7914 { 7915 // convert to B2DPolyPolygon 7916 // TODO: get rid of intermediate tool's PolyPolygon 7917 ::basegfx::B2DPolyPolygon aB2DPolyPoly = aPolyPoly.getB2DPolyPolygon(); 7918 ::basegfx::B2DHomMatrix aMatrix; 7919 aMatrix.scale( fScaleX, fScaleY ); 7920 int nAngle = GetFont().GetOrientation(); 7921 if( nAngle ) 7922 aMatrix.rotate( nAngle * F_PI1800 ); 7923 aB2DPolyPoly.transform( aMatrix ); 7924 rVector.push_back( aB2DPolyPoly ); 7925 } 7926 } 7927 } 7928 7929 nXOffset += nCharWidth; 7930 bRet = bRet && bSuccess; 7931 } 7932 7933 return bRet; 7934 } 7935 7936 // ----------------------------------------------------------------------- 7937 7938 sal_Bool OutputDevice::GetTextOutlines( PolyPolyVector& rResultVector, 7939 const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, 7940 xub_StrLen nLen, sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const 7941 { 7942 rResultVector.clear(); 7943 7944 // get the basegfx polypolygon vector 7945 ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector; 7946 if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen, 7947 bOptimize, nTWidth, pDXArray ) ) 7948 return sal_False; 7949 7950 // convert to a tool polypolygon vector 7951 rResultVector.reserve( aB2DPolyPolyVector.size() ); 7952 ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin(); 7953 for(; aIt != aB2DPolyPolyVector.end(); ++aIt ) 7954 rResultVector.push_back(PolyPolygon(*aIt)); // #i76339# 7955 7956 return sal_True; 7957 } 7958 7959 // ----------------------------------------------------------------------- 7960 7961 sal_Bool OutputDevice::GetTextOutline( PolyPolygon& rPolyPoly, 7962 const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen, 7963 sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const 7964 { 7965 rPolyPoly.Clear(); 7966 7967 // get the basegfx polypolygon vector 7968 ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector; 7969 if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen, 7970 bOptimize, nTWidth, pDXArray ) ) 7971 return sal_False; 7972 7973 // convert and merge into a tool polypolygon 7974 ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin(); 7975 for(; aIt != aB2DPolyPolyVector.end(); ++aIt ) 7976 for( unsigned int i = 0; i < aIt->count(); ++i ) 7977 rPolyPoly.Insert(Polygon((*aIt).getB2DPolygon( i ))); // #i76339# 7978 7979 return sal_True; 7980 } 7981 7982 // ----------------------------------------------------------------------- 7983 7984 sal_Bool OutputDevice::GetFontCharMap( FontCharMap& rFontCharMap ) const 7985 { 7986 rFontCharMap.Reset(); 7987 7988 // we need a graphics 7989 if( !mpGraphics && !ImplGetGraphics() ) 7990 return sal_False; 7991 7992 if( mbNewFont ) 7993 ImplNewFont(); 7994 if( mbInitFont ) 7995 ImplInitFont(); 7996 if( !mpFontEntry ) 7997 return sal_False; 7998 7999 #ifdef ENABLE_IFC_CACHE // a little font charmap cache helps considerably 8000 static const int NMAXITEMS = 16; 8001 static int nUsedItems = 0, nCurItem = 0; 8002 8003 struct CharMapCacheItem { const ImplFontData* mpFontData; FontCharMap maCharMap; }; 8004 static CharMapCacheItem aCache[ NMAXITEMS ]; 8005 8006 const ImplFontData* pFontData = mpFontEntry->maFontSelData.mpFontData; 8007 8008 int i; 8009 for( i = nUsedItems; --i >= 0; ) 8010 if( pFontData == aCache[i].mpFontData ) 8011 break; 8012 if( i >= 0 ) // found in cache 8013 { 8014 rFontCharMap.Reset( aCache[i].maCharMap.mpImpl ); 8015 } 8016 else // need to cache 8017 #endif // ENABLE_IFC_CACHE 8018 { 8019 const ImplFontCharMap* pNewMap = mpGraphics->GetImplFontCharMap(); 8020 rFontCharMap.Reset( pNewMap ); 8021 8022 #ifdef ENABLE_IFC_CACHE 8023 // manage cache round-robin and insert data 8024 CharMapCacheItem& rItem = aCache[ nCurItem ]; 8025 rItem.mpFontData = pFontData; 8026 rItem.maCharMap.Reset( pNewMap ); 8027 8028 if( ++nCurItem >= NMAXITEMS ) 8029 nCurItem = 0; 8030 8031 if( ++nUsedItems >= NMAXITEMS ) 8032 nUsedItems = NMAXITEMS; 8033 #endif // ENABLE_IFC_CACHE 8034 } 8035 8036 if( rFontCharMap.IsDefaultMap() ) 8037 return sal_False; 8038 return sal_True; 8039 } 8040 8041 // ----------------------------------------------------------------------- 8042 8043 xub_StrLen OutputDevice::HasGlyphs( const Font& rTempFont, const String& rStr, 8044 xub_StrLen nIndex, xub_StrLen nLen ) const 8045 { 8046 if( nIndex >= rStr.Len() ) 8047 return nIndex; 8048 xub_StrLen nEnd = nIndex + nLen; 8049 if( (sal_uLong)nIndex+nLen > rStr.Len() ) 8050 nEnd = rStr.Len(); 8051 8052 DBG_ASSERT( nIndex < nEnd, "StartPos >= EndPos?" ); 8053 DBG_ASSERT( nEnd <= rStr.Len(), "String too short" ); 8054 8055 // to get the map temporarily set font 8056 const Font aOrigFont = GetFont(); 8057 const_cast<OutputDevice&>(*this).SetFont( rTempFont ); 8058 FontCharMap aFontCharMap; 8059 sal_Bool bRet = GetFontCharMap( aFontCharMap ); 8060 const_cast<OutputDevice&>(*this).SetFont( aOrigFont ); 8061 8062 // if fontmap is unknown assume it doesn't have the glyphs 8063 if( bRet == sal_False ) 8064 return nIndex; 8065 8066 const sal_Unicode* pStr = rStr.GetBuffer(); 8067 for( pStr += nIndex; nIndex < nEnd; ++pStr, ++nIndex ) 8068 if( ! aFontCharMap.HasChar( *pStr ) ) 8069 return nIndex; 8070 8071 return STRING_LEN; 8072 } 8073 8074 // ----------------------------------------------------------------------- 8075