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