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