1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
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.
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).
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.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
34 #include <vcl/salbtype.hxx>
35 #include <gcach_ftyp.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vcl/bitmap.hxx>
39 #include <vcl/outfont.hxx>
41 #ifdef ENABLE_GRAPHITE
42 #include <vcl/graphite_features.hxx>
45 #include <rtl/ustring.hxx> // used only for string=>hashvalue
46 #include <osl/file.hxx>
47 #include <tools/debug.hxx>
49 // =======================================================================
51 // =======================================================================
53 static GlyphCache
* pInstance
= NULL
;
55 GlyphCache::GlyphCache( GlyphCachePeer
& rPeer
)
58 mnBytesUsed(sizeof(GlyphCache
)),
61 mpCurrentGCFont(NULL
),
65 mpFtManager
= new FreetypeManager
;
68 // -----------------------------------------------------------------------
70 GlyphCache::~GlyphCache()
73 // for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
74 // delete const_cast<ServerFont*>( it->second );
80 // -----------------------------------------------------------------------
85 size_t GlyphCache::IFSD_Hash::operator()( const ImplFontSelectData
& rFontSelData
) const
87 // TODO: is it worth to improve this hash function?
88 sal_IntPtr nFontId
= reinterpret_cast<sal_IntPtr
>( rFontSelData
.mpFontData
);
89 #ifdef ENABLE_GRAPHITE
90 if (rFontSelData
.maTargetName
.Search(grutils::GrFeatureParser::FEAT_PREFIX
)
93 rtl::OString aFeatName
= rtl::OUStringToOString( rFontSelData
.maTargetName
, RTL_TEXTENCODING_UTF8
);
94 nFontId
^= aFeatName
.hashCode();
97 size_t nHash
= nFontId
<< 8;
98 nHash
+= rFontSelData
.mnHeight
;
99 nHash
+= rFontSelData
.mnOrientation
;
100 nHash
+= rFontSelData
.mbVertical
;
101 nHash
+= rFontSelData
.meItalic
;
102 nHash
+= rFontSelData
.meWeight
;
103 #ifdef ENABLE_GRAPHITE
104 nHash
+= rFontSelData
.meLanguage
;
109 // -----------------------------------------------------------------------
111 bool GlyphCache::IFSD_Equal::operator()( const ImplFontSelectData
& rA
, const ImplFontSelectData
& rB
) const
114 sal_IntPtr nFontIdA
= reinterpret_cast<sal_IntPtr
>( rA
.mpFontData
);
115 sal_IntPtr nFontIdB
= reinterpret_cast<sal_IntPtr
>( rB
.mpFontData
);
116 if( nFontIdA
!= nFontIdB
)
119 // compare with the requested metrics
120 if( (rA
.mnHeight
!= rB
.mnHeight
)
121 || (rA
.mnOrientation
!= rB
.mnOrientation
)
122 || (rA
.mbVertical
!= rB
.mbVertical
)
123 || (rA
.mbNonAntialiased
!= rB
.mbNonAntialiased
) )
126 if( (rA
.meItalic
!= rB
.meItalic
)
127 || (rA
.meWeight
!= rB
.meWeight
) )
130 // NOTE: ignoring meFamily deliberately
132 // compare with the requested width, allow default width
133 if( (rA
.mnWidth
!= rB
.mnWidth
)
134 && ((rA
.mnHeight
!= rB
.mnWidth
) || (rA
.mnWidth
!= 0)) )
136 #ifdef ENABLE_GRAPHITE
137 if (rA
.meLanguage
!= rB
.meLanguage
)
139 // check for features
140 if ((rA
.maTargetName
.Search(grutils::GrFeatureParser::FEAT_PREFIX
)
141 != STRING_NOTFOUND
||
142 rB
.maTargetName
.Search(grutils::GrFeatureParser::FEAT_PREFIX
)
143 != STRING_NOTFOUND
) && rA
.maTargetName
!= rB
.maTargetName
)
149 // -----------------------------------------------------------------------
151 GlyphCache
& GlyphCache::GetInstance()
156 // -----------------------------------------------------------------------
158 void GlyphCache::LoadFonts()
160 if( const char* pFontPath
= ::getenv( "SAL_FONTPATH_PRIVATE" ) )
161 AddFontPath( String::CreateFromAscii( pFontPath
) );
162 const String
& rFontPath
= Application::GetFontPath();
163 if( rFontPath
.Len() > 0 )
164 AddFontPath( rFontPath
);
167 // -----------------------------------------------------------------------
169 void GlyphCache::ClearFontPath()
172 mpFtManager
->ClearFontList();
175 // -----------------------------------------------------------------------
177 void GlyphCache::AddFontPath( const String
& rFontPath
)
182 for( xub_StrLen nBreaker1
= 0, nBreaker2
= 0; nBreaker2
!= STRING_LEN
; nBreaker1
= nBreaker2
+ 1 )
184 nBreaker2
= rFontPath
.Search( ';', nBreaker1
);
185 if( nBreaker2
== STRING_NOTFOUND
)
186 nBreaker2
= STRING_LEN
;
188 ::rtl::OUString aUrlName
;
189 osl::FileBase::getFileURLFromSystemPath( rFontPath
.Copy( nBreaker1
, nBreaker2
), aUrlName
);
190 mpFtManager
->AddFontDir( aUrlName
);
194 // -----------------------------------------------------------------------
196 void GlyphCache::AddFontFile( const rtl::OString
& rNormalizedName
, int nFaceNum
,
197 sal_IntPtr nFontId
, const ImplDevFontAttributes
& rDFA
, const ExtraKernInfo
* pExtraKern
)
200 mpFtManager
->AddFontFile( rNormalizedName
, nFaceNum
, nFontId
, rDFA
, pExtraKern
);
203 // -----------------------------------------------------------------------
205 void GlyphCache::AnnounceFonts( ImplDevFontList
* pList
) const
208 mpFtManager
->AnnounceFonts( pList
);
209 // VirtDevServerFont::AnnounceFonts( pList );
212 // -----------------------------------------------------------------------
214 ServerFont
* GlyphCache::CacheFont( const ImplFontSelectData
& rFontSelData
)
216 // a serverfont request has pFontData
217 if( rFontSelData
.mpFontData
== NULL
)
219 // a serverfont request has a fontid > 0
220 sal_IntPtr nFontId
= rFontSelData
.mpFontData
->GetFontId();
224 // the FontList's key mpFontData member is reinterpreted as font id
225 ImplFontSelectData aFontSelData
= rFontSelData
;
226 aFontSelData
.mpFontData
= reinterpret_cast<ImplFontData
*>( nFontId
);
227 FontList::iterator it
= maFontList
.find( aFontSelData
);
228 if( it
!= maFontList
.end() )
230 ServerFont
* pFound
= it
->second
;
236 // font not cached yet => create new font item
237 ServerFont
* pNew
= NULL
;
239 pNew
= mpFtManager
->CreateFont( aFontSelData
);
240 // TODO: pNew = VirtDevServerFont::CreateFont( aFontSelData );
244 maFontList
[ aFontSelData
] = pNew
;
245 mnBytesUsed
+= pNew
->GetByteCount();
247 // enable garbage collection for new font
248 if( !mpCurrentGCFont
)
250 mpCurrentGCFont
= pNew
;
251 pNew
->mpNextGCFont
= pNew
;
252 pNew
->mpPrevGCFont
= pNew
;
256 pNew
->mpNextGCFont
= mpCurrentGCFont
;
257 pNew
->mpPrevGCFont
= mpCurrentGCFont
->mpPrevGCFont
;
258 pNew
->mpPrevGCFont
->mpNextGCFont
= pNew
;
259 mpCurrentGCFont
->mpPrevGCFont
= pNew
;
266 // -----------------------------------------------------------------------
268 void GlyphCache::UncacheFont( ServerFont
& rServerFont
)
270 // the interface for rServerFont must be const because a
271 // user who wants to release it only got const ServerFonts.
272 // The caching algorithm needs a non-const object
273 ServerFont
* pFont
= const_cast<ServerFont
*>( &rServerFont
);
274 if( (pFont
->Release() <= 0)
275 && (mnMaxSize
<= (mnBytesUsed
+ mrPeer
.GetByteCount())) )
277 mpCurrentGCFont
= pFont
;
282 // -----------------------------------------------------------------------
284 ULONG
GlyphCache::CalcByteCount() const
286 ULONG nCacheSize
= sizeof(*this);
287 for( FontList::const_iterator it
= maFontList
.begin(); it
!= maFontList
.end(); ++it
)
289 const ServerFont
* pSF
= it
->second
;
291 nCacheSize
+= pSF
->GetByteCount();
293 // TODO: also account something for hashtable management
297 // -----------------------------------------------------------------------
299 void GlyphCache::GarbageCollect()
301 // when current GC font has been destroyed get another one
302 if( !mpCurrentGCFont
)
304 FontList::iterator it
= maFontList
.begin();
305 if( it
!= maFontList
.end() )
306 mpCurrentGCFont
= it
->second
;
309 // unless there is no other font to collect
310 if( !mpCurrentGCFont
)
313 // prepare advance to next font for garbage collection
314 ServerFont
* const pServerFont
= mpCurrentGCFont
;
315 mpCurrentGCFont
= pServerFont
->mpNextGCFont
;
317 if( (pServerFont
== mpCurrentGCFont
) // no other fonts
318 || (pServerFont
->GetRefCount() > 0) ) // font still used
320 // try to garbage collect at least a few bytes
321 pServerFont
->GarbageCollect( mnLruIndex
- mnGlyphCount
/2 );
323 else // current GC font is unreferenced
325 DBG_ASSERT( (pServerFont
->GetRefCount() == 0),
326 "GlyphCache::GC detected RefCount underflow" );
328 // free all pServerFont related data
329 pServerFont
->GarbageCollect( mnLruIndex
+0x10000000 );
330 if( pServerFont
== mpCurrentGCFont
)
331 mpCurrentGCFont
= NULL
;
332 const ImplFontSelectData
& rIFSD
= pServerFont
->GetFontSelData();
333 maFontList
.erase( rIFSD
);
334 mrPeer
.RemovingFont( *pServerFont
);
335 mnBytesUsed
-= pServerFont
->GetByteCount();
337 // remove font from list of garbage collected fonts
338 if( pServerFont
->mpPrevGCFont
)
339 pServerFont
->mpPrevGCFont
->mpNextGCFont
= pServerFont
->mpNextGCFont
;
340 if( pServerFont
->mpNextGCFont
)
341 pServerFont
->mpNextGCFont
->mpPrevGCFont
= pServerFont
->mpPrevGCFont
;
342 if( pServerFont
== mpCurrentGCFont
)
343 mpCurrentGCFont
= NULL
;
349 // -----------------------------------------------------------------------
351 inline void GlyphCache::UsingGlyph( ServerFont
&, GlyphData
& rGlyphData
)
353 rGlyphData
.SetLruValue( mnLruIndex
++ );
356 // -----------------------------------------------------------------------
358 inline void GlyphCache::AddedGlyph( ServerFont
& rServerFont
, GlyphData
& rGlyphData
)
361 mnBytesUsed
+= sizeof( rGlyphData
);
362 UsingGlyph( rServerFont
, rGlyphData
);
366 // -----------------------------------------------------------------------
368 void GlyphCache::GrowNotify()
370 if( (mnBytesUsed
+ mrPeer
.GetByteCount()) > mnMaxSize
)
374 // -----------------------------------------------------------------------
376 inline void GlyphCache::RemovingGlyph( ServerFont
& rSF
, GlyphData
& rGD
, int nGlyphIndex
)
378 mrPeer
.RemovingGlyph( rSF
, rGD
, nGlyphIndex
);
379 mnBytesUsed
-= sizeof( GlyphData
);
383 // =======================================================================
385 // =======================================================================
387 ServerFont::ServerFont( const ImplFontSelectData
& rFSD
)
392 mnBytesUsed( sizeof(ServerFont
) ),
393 mpPrevGCFont( NULL
),
394 mpNextGCFont( NULL
),
399 mbCollectedZW( false )
401 // TODO: move update of mpFontEntry into FontEntry class when
402 // it becomes reponsible for the ServerFont instantiation
403 ((ImplServerFontEntry
*)rFSD
.mpFontEntry
)->SetServerFont( this );
405 if( rFSD
.mnOrientation
!= 0 )
407 const double dRad
= rFSD
.mnOrientation
* ( F_2PI
/ 3600.0 );
408 mnCos
= static_cast<long>( 0x10000 * cos( dRad
) + 0.5 );
409 mnSin
= static_cast<long>( 0x10000 * sin( dRad
) + 0.5 );
413 // -----------------------------------------------------------------------
415 ServerFont::~ServerFont()
417 ReleaseFromGarbageCollect();
420 // -----------------------------------------------------------------------
422 void ServerFont::ReleaseFromGarbageCollect()
424 // remove from GC list
425 ServerFont
* pPrev
= mpPrevGCFont
;
426 ServerFont
* pNext
= mpNextGCFont
;
427 if( pPrev
) pPrev
->mpNextGCFont
= pNext
;
428 if( pNext
) pNext
->mpPrevGCFont
= pPrev
;
433 // -----------------------------------------------------------------------
435 long ServerFont::Release() const
437 DBG_ASSERT( mnRefCount
> 0, "ServerFont: RefCount underflow" );
441 // -----------------------------------------------------------------------
443 GlyphData
& ServerFont::GetGlyphData( int nGlyphIndex
)
445 // usually the GlyphData is cached
446 GlyphList::iterator it
= maGlyphList
.find( nGlyphIndex
);
447 if( it
!= maGlyphList
.end() ) {
448 GlyphData
& rGlyphData
= it
->second
;
449 GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData
);
453 // sometimes not => we need to create and initialize it ourselves
454 GlyphData
& rGlyphData
= maGlyphList
[ nGlyphIndex
];
455 mnBytesUsed
+= sizeof( GlyphData
);
456 InitGlyphData( nGlyphIndex
, rGlyphData
);
457 GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData
);
461 // -----------------------------------------------------------------------
463 void ServerFont::GarbageCollect( long nMinLruIndex
)
465 GlyphList::iterator it_next
= maGlyphList
.begin();
466 while( it_next
!= maGlyphList
.end() )
468 GlyphList::iterator it
= it_next
++;
469 GlyphData
& rGD
= it
->second
;
470 if( (nMinLruIndex
- rGD
.GetLruValue()) > 0 )
472 OSL_ASSERT( mnBytesUsed
>= sizeof(GlyphData
) );
473 mnBytesUsed
-= sizeof( GlyphData
);
474 GlyphCache::GetInstance().RemovingGlyph( *this, rGD
, it
->first
);
475 maGlyphList
.erase( it
);
476 it_next
= maGlyphList
.begin();
481 // -----------------------------------------------------------------------
483 Point
ServerFont::TransformPoint( const Point
& rPoint
) const
485 if( mnCos
== 0x10000 )
487 // TODO: use 32x32=>64bit intermediate
488 const double dCos
= mnCos
* (1.0 / 0x10000);
489 const double dSin
= mnSin
* (1.0 / 0x10000);
490 long nX
= (long)(rPoint
.X() * dCos
+ rPoint
.Y() * dSin
);
491 long nY
= (long)(rPoint
.Y() * dCos
- rPoint
.X() * dSin
);
492 return Point( nX
, nY
);
495 bool ServerFont::IsGlyphInvisible( int nGlyphIndex
)
499 mnZWJ
= GetGlyphIndex( 0x200D );
500 mnZWNJ
= GetGlyphIndex( 0x200C );
501 mbCollectedZW
= true;
504 if( !nGlyphIndex
) // don't hide the NotDef glyph
506 if( (nGlyphIndex
== mnZWNJ
) || (nGlyphIndex
== mnZWJ
) )
512 // =======================================================================
514 ImplServerFontEntry::ImplServerFontEntry( ImplFontSelectData
& rFSD
)
515 : ImplFontEntry( rFSD
),
519 // -----------------------------------------------------------------------
521 ImplServerFontEntry::~ImplServerFontEntry()
523 // TODO: remove the ServerFont here instead of in the GlyphCache
526 // =======================================================================
528 ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId
)
529 : mbInitialized( false ),
531 maUnicodeKernPairs( 0 )
534 //--------------------------------------------------------------------------
536 bool ExtraKernInfo::HasKernPairs() const
540 return !maUnicodeKernPairs
.empty();
543 //--------------------------------------------------------------------------
545 int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData
** ppKernPairs
) const
550 // return early if no kerning available
551 if( maUnicodeKernPairs
.empty() )
554 // allocate kern pair table
555 int nKernCount
= maUnicodeKernPairs
.size();
556 *ppKernPairs
= new ImplKernPairData
[ nKernCount
];
558 // fill in unicode kern pairs with the kern value scaled to the font width
559 ImplKernPairData
* pKernData
= *ppKernPairs
;
560 UnicodeKernPairs::const_iterator it
= maUnicodeKernPairs
.begin();
561 for(; it
!= maUnicodeKernPairs
.end(); ++it
)
562 *(pKernData
++) = *it
;
567 //--------------------------------------------------------------------------
569 int ExtraKernInfo::GetUnscaledKernValue( sal_Unicode cLeft
, sal_Unicode cRight
) const
574 if( maUnicodeKernPairs
.empty() )
577 ImplKernPairData aKernPair
= { cLeft
, cRight
, 0 };
578 UnicodeKernPairs::const_iterator it
= maUnicodeKernPairs
.find( aKernPair
);
579 if( it
== maUnicodeKernPairs
.end() )
582 int nUnscaledValue
= (*it
).mnKern
;
583 return nUnscaledValue
;
586 // =======================================================================