1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_vcl.hxx" 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <math.h> 34 35 #include <gcach_ftyp.hxx> 36 37 #include <vcl/svapp.hxx> 38 #include <vcl/bitmap.hxx> 39 #include <vcl/salbtype.hxx> 40 41 #include <outfont.hxx> 42 43 #ifdef ENABLE_GRAPHITE 44 #include <graphite_features.hxx> 45 #endif 46 47 #include <rtl/ustring.hxx> // used only for string=>hashvalue 48 #include <osl/file.hxx> 49 #include <tools/debug.hxx> 50 51 // ======================================================================= 52 // GlyphCache 53 // ======================================================================= 54 55 static GlyphCache* pInstance = NULL; 56 57 GlyphCache::GlyphCache( GlyphCachePeer& rPeer ) 58 : mrPeer( rPeer ), 59 mnMaxSize( 1500000 ), 60 mnBytesUsed(sizeof(GlyphCache)), 61 mnLruIndex(0), 62 mnGlyphCount(0), 63 mpCurrentGCFont(NULL), 64 mpFtManager(NULL) 65 { 66 pInstance = this; 67 mpFtManager = new FreetypeManager; 68 } 69 70 // ----------------------------------------------------------------------- 71 72 GlyphCache::~GlyphCache() 73 { 74 InvalidateAllGlyphs(); 75 if( mpFtManager ) 76 delete mpFtManager; 77 } 78 79 // ----------------------------------------------------------------------- 80 81 void GlyphCache::InvalidateAllGlyphs() 82 { 83 // an application about to exit can omit garbage collecting the heap 84 // since it makes things slower and introduces risks if the heap was not perfect 85 // for debugging, for memory grinding or leak checking the env allows to force GC 86 const char* pEnv = getenv( "SAL_FORCE_GC_ON_EXIT" ); 87 if( pEnv && (*pEnv != '0') ) 88 { 89 // uncache of all glyph shapes and metrics 90 for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it ) 91 delete const_cast<ServerFont*>( it->second ); 92 maFontList.clear(); 93 mpCurrentGCFont = NULL; 94 } 95 } 96 97 // ----------------------------------------------------------------------- 98 99 inline 100 size_t GlyphCache::IFSD_Hash::operator()( const ImplFontSelectData& rFontSelData ) const 101 { 102 // TODO: is it worth to improve this hash function? 103 sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData ); 104 #ifdef ENABLE_GRAPHITE 105 if (rFontSelData.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) 106 != STRING_NOTFOUND) 107 { 108 rtl::OString aFeatName = rtl::OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); 109 nFontId ^= aFeatName.hashCode(); 110 } 111 #endif 112 size_t nHash = nFontId << 8; 113 nHash += rFontSelData.mnHeight; 114 nHash += rFontSelData.mnOrientation; 115 nHash += rFontSelData.mbVertical; 116 nHash += rFontSelData.meItalic; 117 nHash += rFontSelData.meWeight; 118 #ifdef ENABLE_GRAPHITE 119 nHash += rFontSelData.meLanguage; 120 #endif 121 return nHash; 122 } 123 124 // ----------------------------------------------------------------------- 125 126 bool GlyphCache::IFSD_Equal::operator()( const ImplFontSelectData& rA, const ImplFontSelectData& rB) const 127 { 128 // check font ids 129 sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData ); 130 sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData ); 131 if( nFontIdA != nFontIdB ) 132 return false; 133 134 // compare with the requested metrics 135 if( (rA.mnHeight != rB.mnHeight) 136 || (rA.mnOrientation != rB.mnOrientation) 137 || (rA.mbVertical != rB.mbVertical) 138 || (rA.mbNonAntialiased != rB.mbNonAntialiased) ) 139 return false; 140 141 if( (rA.meItalic != rB.meItalic) 142 || (rA.meWeight != rB.meWeight) ) 143 return false; 144 145 // NOTE: ignoring meFamily deliberately 146 147 // compare with the requested width, allow default width 148 if( (rA.mnWidth != rB.mnWidth) 149 && ((rA.mnHeight != rB.mnWidth) || (rA.mnWidth != 0)) ) 150 return false; 151 #ifdef ENABLE_GRAPHITE 152 if (rA.meLanguage != rB.meLanguage) 153 return false; 154 // check for features 155 if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) 156 != STRING_NOTFOUND || 157 rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX) 158 != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName) 159 return false; 160 #endif 161 return true; 162 } 163 164 // ----------------------------------------------------------------------- 165 166 GlyphCache& GlyphCache::GetInstance() 167 { 168 return *pInstance; 169 } 170 171 // ----------------------------------------------------------------------- 172 173 void GlyphCache::LoadFonts() 174 { 175 if( const char* pFontPath = ::getenv( "SAL_FONTPATH_PRIVATE" ) ) 176 AddFontPath( String::CreateFromAscii( pFontPath ) ); 177 const String& rFontPath = Application::GetFontPath(); 178 if( rFontPath.Len() > 0 ) 179 AddFontPath( rFontPath ); 180 } 181 182 // ----------------------------------------------------------------------- 183 184 void GlyphCache::ClearFontPath() 185 { 186 if( mpFtManager ) 187 mpFtManager->ClearFontList(); 188 } 189 190 // ----------------------------------------------------------------------- 191 192 void GlyphCache::AddFontPath( const String& rFontPath ) 193 { 194 if( !mpFtManager ) 195 return; 196 197 for( xub_StrLen nBreaker1 = 0, nBreaker2 = 0; nBreaker2 != STRING_LEN; nBreaker1 = nBreaker2 + 1 ) 198 { 199 nBreaker2 = rFontPath.Search( ';', nBreaker1 ); 200 if( nBreaker2 == STRING_NOTFOUND ) 201 nBreaker2 = STRING_LEN; 202 203 ::rtl::OUString aUrlName; 204 osl::FileBase::getFileURLFromSystemPath( rFontPath.Copy( nBreaker1, nBreaker2 ), aUrlName ); 205 mpFtManager->AddFontDir( aUrlName ); 206 } 207 } 208 209 // ----------------------------------------------------------------------- 210 211 void GlyphCache::AddFontFile( const rtl::OString& rNormalizedName, int nFaceNum, 212 sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA, const ExtraKernInfo* pExtraKern ) 213 { 214 if( mpFtManager ) 215 mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA, pExtraKern ); 216 } 217 218 // ----------------------------------------------------------------------- 219 220 void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const 221 { 222 if( mpFtManager ) 223 mpFtManager->AnnounceFonts( pList ); 224 // VirtDevServerFont::AnnounceFonts( pList ); 225 } 226 227 // ----------------------------------------------------------------------- 228 229 ServerFont* GlyphCache::CacheFont( const ImplFontSelectData& rFontSelData ) 230 { 231 // a serverfont request has pFontData 232 if( rFontSelData.mpFontData == NULL ) 233 return NULL; 234 // a serverfont request has a fontid > 0 235 sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId(); 236 if( nFontId <= 0 ) 237 return NULL; 238 239 // the FontList's key mpFontData member is reinterpreted as font id 240 ImplFontSelectData aFontSelData = rFontSelData; 241 aFontSelData.mpFontData = reinterpret_cast<ImplFontData*>( nFontId ); 242 FontList::iterator it = maFontList.find( aFontSelData ); 243 if( it != maFontList.end() ) 244 { 245 ServerFont* pFound = it->second; 246 if( pFound ) 247 pFound->AddRef(); 248 return pFound; 249 } 250 251 // font not cached yet => create new font item 252 ServerFont* pNew = NULL; 253 if( mpFtManager ) 254 pNew = mpFtManager->CreateFont( aFontSelData ); 255 // TODO: pNew = VirtDevServerFont::CreateFont( aFontSelData ); 256 257 if( pNew ) 258 { 259 maFontList[ aFontSelData ] = pNew; 260 mnBytesUsed += pNew->GetByteCount(); 261 262 // enable garbage collection for new font 263 if( !mpCurrentGCFont ) 264 { 265 mpCurrentGCFont = pNew; 266 pNew->mpNextGCFont = pNew; 267 pNew->mpPrevGCFont = pNew; 268 } 269 else 270 { 271 pNew->mpNextGCFont = mpCurrentGCFont; 272 pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont; 273 pNew->mpPrevGCFont->mpNextGCFont = pNew; 274 mpCurrentGCFont->mpPrevGCFont = pNew; 275 } 276 } 277 278 return pNew; 279 } 280 281 // ----------------------------------------------------------------------- 282 283 void GlyphCache::UncacheFont( ServerFont& rServerFont ) 284 { 285 // the interface for rServerFont must be const because a 286 // user who wants to release it only got const ServerFonts. 287 // The caching algorithm needs a non-const object 288 ServerFont* pFont = const_cast<ServerFont*>( &rServerFont ); 289 if( (pFont->Release() <= 0) 290 && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) ) 291 { 292 mpCurrentGCFont = pFont; 293 GarbageCollect(); 294 } 295 } 296 297 // ----------------------------------------------------------------------- 298 299 sal_uLong GlyphCache::CalcByteCount() const 300 { 301 sal_uLong nCacheSize = sizeof(*this); 302 for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it ) 303 { 304 const ServerFont* pSF = it->second; 305 if( pSF ) 306 nCacheSize += pSF->GetByteCount(); 307 } 308 // TODO: also account something for hashtable management 309 return nCacheSize; 310 } 311 312 // ----------------------------------------------------------------------- 313 314 void GlyphCache::GarbageCollect() 315 { 316 // when current GC font has been destroyed get another one 317 if( !mpCurrentGCFont ) 318 { 319 FontList::iterator it = maFontList.begin(); 320 if( it != maFontList.end() ) 321 mpCurrentGCFont = it->second; 322 } 323 324 // unless there is no other font to collect 325 if( !mpCurrentGCFont ) 326 return; 327 328 // prepare advance to next font for garbage collection 329 ServerFont* const pServerFont = mpCurrentGCFont; 330 mpCurrentGCFont = pServerFont->mpNextGCFont; 331 332 if( (pServerFont == mpCurrentGCFont) // no other fonts 333 || (pServerFont->GetRefCount() > 0) ) // font still used 334 { 335 // try to garbage collect at least a few bytes 336 pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 ); 337 } 338 else // current GC font is unreferenced 339 { 340 DBG_ASSERT( (pServerFont->GetRefCount() == 0), 341 "GlyphCache::GC detected RefCount underflow" ); 342 343 // free all pServerFont related data 344 pServerFont->GarbageCollect( mnLruIndex+0x10000000 ); 345 if( pServerFont == mpCurrentGCFont ) 346 mpCurrentGCFont = NULL; 347 const ImplFontSelectData& rIFSD = pServerFont->GetFontSelData(); 348 maFontList.erase( rIFSD ); 349 mrPeer.RemovingFont( *pServerFont ); 350 mnBytesUsed -= pServerFont->GetByteCount(); 351 352 // remove font from list of garbage collected fonts 353 if( pServerFont->mpPrevGCFont ) 354 pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont; 355 if( pServerFont->mpNextGCFont ) 356 pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont; 357 if( pServerFont == mpCurrentGCFont ) 358 mpCurrentGCFont = NULL; 359 360 delete pServerFont; 361 } 362 } 363 364 // ----------------------------------------------------------------------- 365 366 inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData ) 367 { 368 rGlyphData.SetLruValue( mnLruIndex++ ); 369 } 370 371 // ----------------------------------------------------------------------- 372 373 inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData ) 374 { 375 ++mnGlyphCount; 376 mnBytesUsed += sizeof( rGlyphData ); 377 UsingGlyph( rServerFont, rGlyphData ); 378 GrowNotify(); 379 } 380 381 // ----------------------------------------------------------------------- 382 383 void GlyphCache::GrowNotify() 384 { 385 if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize ) 386 GarbageCollect(); 387 } 388 389 // ----------------------------------------------------------------------- 390 391 inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, int nGlyphIndex ) 392 { 393 mrPeer.RemovingGlyph( rSF, rGD, nGlyphIndex ); 394 mnBytesUsed -= sizeof( GlyphData ); 395 --mnGlyphCount; 396 } 397 398 // ======================================================================= 399 // ServerFont 400 // ======================================================================= 401 402 ServerFont::ServerFont( const ImplFontSelectData& rFSD ) 403 : maGlyphList( 0), 404 maFontSelData(rFSD), 405 mnExtInfo(0), 406 mnRefCount(1), 407 mnBytesUsed( sizeof(ServerFont) ), 408 mpPrevGCFont( NULL ), 409 mpNextGCFont( NULL ), 410 mnCos( 0x10000), 411 mnSin( 0 ), 412 mnZWJ( 0 ), 413 mnZWNJ( 0 ), 414 mbCollectedZW( false ) 415 { 416 // TODO: move update of mpFontEntry into FontEntry class when 417 // it becomes reponsible for the ServerFont instantiation 418 ((ImplServerFontEntry*)rFSD.mpFontEntry)->SetServerFont( this ); 419 420 if( rFSD.mnOrientation != 0 ) 421 { 422 const double dRad = rFSD.mnOrientation * ( F_2PI / 3600.0 ); 423 mnCos = static_cast<long>( 0x10000 * cos( dRad ) + 0.5 ); 424 mnSin = static_cast<long>( 0x10000 * sin( dRad ) + 0.5 ); 425 } 426 } 427 428 // ----------------------------------------------------------------------- 429 430 ServerFont::~ServerFont() 431 { 432 ReleaseFromGarbageCollect(); 433 } 434 435 // ----------------------------------------------------------------------- 436 437 void ServerFont::ReleaseFromGarbageCollect() 438 { 439 // remove from GC list 440 ServerFont* pPrev = mpPrevGCFont; 441 ServerFont* pNext = mpNextGCFont; 442 if( pPrev ) pPrev->mpNextGCFont = pNext; 443 if( pNext ) pNext->mpPrevGCFont = pPrev; 444 mpPrevGCFont = NULL; 445 mpNextGCFont = NULL; 446 } 447 448 // ----------------------------------------------------------------------- 449 450 long ServerFont::Release() const 451 { 452 DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" ); 453 return --mnRefCount; 454 } 455 456 // ----------------------------------------------------------------------- 457 458 GlyphData& ServerFont::GetGlyphData( int nGlyphIndex ) 459 { 460 // usually the GlyphData is cached 461 GlyphList::iterator it = maGlyphList.find( nGlyphIndex ); 462 if( it != maGlyphList.end() ) { 463 GlyphData& rGlyphData = it->second; 464 GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData ); 465 return rGlyphData; 466 } 467 468 // sometimes not => we need to create and initialize it ourselves 469 GlyphData& rGlyphData = maGlyphList[ nGlyphIndex ]; 470 mnBytesUsed += sizeof( GlyphData ); 471 InitGlyphData( nGlyphIndex, rGlyphData ); 472 GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData ); 473 return rGlyphData; 474 } 475 476 // ----------------------------------------------------------------------- 477 478 void ServerFont::GarbageCollect( long nMinLruIndex ) 479 { 480 GlyphList::iterator it_next = maGlyphList.begin(); 481 while( it_next != maGlyphList.end() ) 482 { 483 GlyphList::iterator it = it_next++; 484 GlyphData& rGD = it->second; 485 if( (nMinLruIndex - rGD.GetLruValue()) > 0 ) 486 { 487 OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) ); 488 mnBytesUsed -= sizeof( GlyphData ); 489 GlyphCache::GetInstance().RemovingGlyph( *this, rGD, it->first ); 490 maGlyphList.erase( it ); 491 it_next = maGlyphList.begin(); 492 } 493 } 494 } 495 496 // ----------------------------------------------------------------------- 497 498 Point ServerFont::TransformPoint( const Point& rPoint ) const 499 { 500 if( mnCos == 0x10000 ) 501 return rPoint; 502 // TODO: use 32x32=>64bit intermediate 503 const double dCos = mnCos * (1.0 / 0x10000); 504 const double dSin = mnSin * (1.0 / 0x10000); 505 long nX = (long)(rPoint.X() * dCos + rPoint.Y() * dSin); 506 long nY = (long)(rPoint.Y() * dCos - rPoint.X() * dSin); 507 return Point( nX, nY ); 508 } 509 510 bool ServerFont::IsGlyphInvisible( int nGlyphIndex ) 511 { 512 if (!mbCollectedZW) 513 { 514 mnZWJ = GetGlyphIndex( 0x200D ); 515 mnZWNJ = GetGlyphIndex( 0x200C ); 516 mbCollectedZW = true; 517 } 518 519 if( !nGlyphIndex ) // don't hide the NotDef glyph 520 return false; 521 if( (nGlyphIndex == mnZWNJ) || (nGlyphIndex == mnZWJ) ) 522 return true; 523 524 return false; 525 } 526 527 // ======================================================================= 528 529 ImplServerFontEntry::ImplServerFontEntry( ImplFontSelectData& rFSD ) 530 : ImplFontEntry( rFSD ) 531 , mpServerFont( NULL ) 532 , mbGotFontOptions( false ) 533 , mbValidFontOptions( false ) 534 {} 535 536 // ----------------------------------------------------------------------- 537 538 ImplServerFontEntry::~ImplServerFontEntry() 539 { 540 // TODO: remove the ServerFont here instead of in the GlyphCache 541 } 542 543 // ======================================================================= 544 545 ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId ) 546 : mbInitialized( false ), 547 mnFontId( nFontId ), 548 maUnicodeKernPairs( 0 ) 549 {} 550 551 //-------------------------------------------------------------------------- 552 553 bool ExtraKernInfo::HasKernPairs() const 554 { 555 if( !mbInitialized ) 556 Initialize(); 557 return !maUnicodeKernPairs.empty(); 558 } 559 560 //-------------------------------------------------------------------------- 561 562 int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData** ppKernPairs ) const 563 { 564 if( !mbInitialized ) 565 Initialize(); 566 567 // return early if no kerning available 568 if( maUnicodeKernPairs.empty() ) 569 return 0; 570 571 // allocate kern pair table 572 int nKernCount = maUnicodeKernPairs.size(); 573 *ppKernPairs = new ImplKernPairData[ nKernCount ]; 574 575 // fill in unicode kern pairs with the kern value scaled to the font width 576 ImplKernPairData* pKernData = *ppKernPairs; 577 UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.begin(); 578 for(; it != maUnicodeKernPairs.end(); ++it ) 579 *(pKernData++) = *it; 580 581 return nKernCount; 582 } 583 584 //-------------------------------------------------------------------------- 585 586 int ExtraKernInfo::GetUnscaledKernValue( sal_Unicode cLeft, sal_Unicode cRight ) const 587 { 588 if( !mbInitialized ) 589 Initialize(); 590 591 if( maUnicodeKernPairs.empty() ) 592 return 0; 593 594 ImplKernPairData aKernPair = { cLeft, cRight, 0 }; 595 UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.find( aKernPair ); 596 if( it == maUnicodeKernPairs.end() ) 597 return 0; 598 599 int nUnscaledValue = (*it).mnKern; 600 return nUnscaledValue; 601 } 602 603 // ======================================================================= 604 605