build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / font / fontcache.cxx
blob216283263931b503edb12470737f5899f777a6b9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "svdata.hxx"
22 #include "fontinstance.hxx"
23 #include "PhysicalFontCollection.hxx"
24 #include "PhysicalFontFace.hxx"
25 #include "PhysicalFontFamily.hxx"
27 size_t ImplFontCache::IFSD_Hash::operator()( const FontSelectPattern& rFSD ) const
29 return rFSD.hashCode();
32 bool ImplFontCache::IFSD_Equal::operator()(const FontSelectPattern& rA, const FontSelectPattern& rB) const
34 // check normalized font family name
35 if( rA.maSearchName != rB.maSearchName )
36 return false;
38 // check font transformation
39 if( (rA.mnHeight != rB.mnHeight)
40 || (rA.mnWidth != rB.mnWidth)
41 || (rA.mnOrientation != rB.mnOrientation) )
42 return false;
44 // check mapping relevant attributes
45 if( (rA.mbVertical != rB.mbVertical)
46 || (rA.meLanguage != rB.meLanguage) )
47 return false;
49 // check font face attributes
50 if( (rA.GetWeight() != rB.GetWeight())
51 || (rA.GetItalic() != rB.GetItalic())
52 // || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member
53 || (rA.GetPitch() != rB.GetPitch()) )
54 return false;
56 // check style name
57 if( rA.GetStyleName() != rB.GetStyleName() )
58 return false;
60 // Symbol fonts may recode from one type to another So they are only
61 // safely equivalent for equal targets
62 if (
63 (rA.mpFontData && rA.mpFontData->IsSymbolFont()) ||
64 (rB.mpFontData && rB.mpFontData->IsSymbolFont())
67 if (rA.maTargetName != rB.maTargetName)
68 return false;
71 // check for features
72 if ((rA.maTargetName.indexOf(FontSelectPatternAttributes::FEAT_PREFIX)
73 != -1 ||
74 rB.maTargetName.indexOf(FontSelectPatternAttributes::FEAT_PREFIX)
75 != -1) && rA.maTargetName != rB.maTargetName)
76 return false;
78 if (rA.mbEmbolden != rB.mbEmbolden)
79 return false;
81 if (rA.maItalicMatrix != rB.maItalicMatrix)
82 return false;
84 return true;
87 ImplFontCache::ImplFontCache()
88 : mpFirstEntry( nullptr ),
89 mnRef0Count( 0 )
92 ImplFontCache::~ImplFontCache()
94 FontInstanceList::iterator it = maFontInstanceList.begin();
95 for(; it != maFontInstanceList.end(); ++it )
97 LogicalFontInstance* pFontInstance = (*it).second;
98 delete pFontInstance;
102 LogicalFontInstance* ImplFontCache::GetFontInstance( PhysicalFontCollection* pFontList,
103 const vcl::Font& rFont, const Size& rSize, float fExactHeight )
105 const OUString& aSearchName = rFont.GetFamilyName();
107 // initialize internal font request object
108 FontSelectPattern aFontSelData( rFont, aSearchName, rSize, fExactHeight );
109 return GetFontInstance( pFontList, aFontSelData );
112 LogicalFontInstance* ImplFontCache::GetFontInstance( PhysicalFontCollection* pFontList,
113 FontSelectPattern& aFontSelData )
115 // check if a directly matching logical font instance is already cached,
116 // the most recently used font usually has a hit rate of >50%
117 LogicalFontInstance *pFontInstance = nullptr;
118 PhysicalFontFamily* pFontFamily = nullptr;
119 IFSD_Equal aIFSD_Equal;
120 if( mpFirstEntry && aIFSD_Equal( aFontSelData, mpFirstEntry->maFontSelData ) )
121 pFontInstance = mpFirstEntry;
122 else
124 FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
125 if( it != maFontInstanceList.end() )
126 pFontInstance = (*it).second;
129 if( !pFontInstance ) // no direct cache hit
131 // find the best matching logical font family and update font selector accordingly
132 pFontFamily = pFontList->FindFontFamily( aFontSelData );
133 SAL_WARN_IF( (pFontFamily == nullptr), "vcl", "ImplFontCache::Get() No logical font found!" );
134 if( pFontFamily )
135 aFontSelData.maSearchName = pFontFamily->GetSearchName();
137 // check if an indirectly matching logical font instance is already cached
138 FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
139 if( it != maFontInstanceList.end() )
141 // we have an indirect cache hit
142 pFontInstance = (*it).second;
146 PhysicalFontFace* pFontData = nullptr;
148 if (!pFontInstance && pFontFamily)// no cache hit => find the best matching physical font face
150 bool bOrigWasSymbol = aFontSelData.mpFontData && aFontSelData.mpFontData->IsSymbolFont();
151 pFontData = pFontFamily->FindBestFontFace( aFontSelData );
152 aFontSelData.mpFontData = pFontData;
153 bool bNewIsSymbol = aFontSelData.mpFontData && aFontSelData.mpFontData->IsSymbolFont();
155 if (bNewIsSymbol != bOrigWasSymbol)
157 // it is possible, though generally unlikely, that at this point we
158 // will attempt to use a symbol font as a last-ditch fallback for a
159 // non-symbol font request or vice versa, and by changing
160 // aFontSelData.mpFontData to/from a symbol font we may now find
161 // something in the cache that can be reused which previously
162 // wasn't a candidate
163 FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
164 if( it != maFontInstanceList.end() )
165 pFontInstance = (*it).second;
169 if( pFontInstance ) // cache hit => use existing font instance
171 // increase the font instance's reference count
172 Acquire(pFontInstance);
175 if (!pFontInstance && pFontData)// still no cache hit => create a new font instance
177 // create a new logical font instance from this physical font face
178 pFontInstance = pFontData->CreateFontInstance( aFontSelData );
179 pFontInstance->mpFontCache = this;
181 // if we're subtituting from or to a symbol font we may need a symbol
182 // conversion table
183 if( pFontData->IsSymbolFont() || aFontSelData.IsSymbolFont() )
185 if( aFontSelData.maTargetName != aFontSelData.maSearchName )
186 pFontInstance->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
189 #ifdef MACOSX
190 //It might be better to dig out the font version of the target font
191 //to see if it's a modern re-coded apple symbol font in case that
192 //font shows up on a different platform
193 if (!pFontInstance->mpConversion &&
194 aFontSelData.maTargetName.equalsIgnoreAsciiCase("symbol") &&
195 aFontSelData.maSearchName.equalsIgnoreAsciiCase("symbol"))
197 pFontInstance->mpConversion = ConvertChar::GetRecodeData( "Symbol", "AppleSymbol" );
199 #endif
201 // add the new entry to the cache
202 maFontInstanceList[ aFontSelData ] = pFontInstance;
205 mpFirstEntry = pFontInstance;
206 return pFontInstance;
209 LogicalFontInstance* ImplFontCache::GetGlyphFallbackFont( PhysicalFontCollection* pFontCollection,
210 FontSelectPattern& rFontSelData, int nFallbackLevel, OUString& rMissingCodes )
212 // get a candidate font for glyph fallback
213 // unless the previously selected font got a device specific substitution
214 // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
215 if( nFallbackLevel >= 1)
217 PhysicalFontFamily* pFallbackData = nullptr;
219 //fdo#33898 If someone has EUDC installed then they really want that to
220 //be used as the first-choice glyph fallback seeing as it's filled with
221 //private area codes with don't make any sense in any other font so
222 //prioritise it here if it's available. Ideally we would remove from
223 //rMissingCodes all the glyphs which it is able to resolve as an
224 //optimization, but that's tricky to achieve cross-platform without
225 //sufficient heavy-weight code that's likely to undo the value of the
226 //optimization
227 if (nFallbackLevel == 1)
228 pFallbackData = pFontCollection->FindFontFamily("EUDC");
229 if (!pFallbackData)
230 pFallbackData = pFontCollection->GetGlyphFallbackFont(rFontSelData, rMissingCodes, nFallbackLevel-1);
231 // escape when there are no font candidates
232 if( !pFallbackData )
233 return nullptr;
234 // override the font name
235 rFontSelData.SetFamilyName( pFallbackData->GetFamilyName() );
236 // clear the cached normalized name
237 rFontSelData.maSearchName.clear();
240 LogicalFontInstance* pFallbackFont = GetFontInstance( pFontCollection, rFontSelData );
241 return pFallbackFont;
244 void ImplFontCache::Acquire(LogicalFontInstance* pFontInstance)
246 assert(pFontInstance->mpFontCache == this);
248 if (0 == pFontInstance->mnRefCount++)
249 --mnRef0Count;
252 void ImplFontCache::Release(LogicalFontInstance* pFontInstance)
254 static const int FONTCACHE_MAX = getenv("LO_TESTNAME") ? 1 : 50;
256 assert(pFontInstance->mnRefCount > 0 && "ImplFontCache::Release() - font refcount underflow");
257 if( --pFontInstance->mnRefCount > 0 )
258 return;
260 if (++mnRef0Count < FONTCACHE_MAX)
261 return;
263 assert(CountUnreferencedEntries() == mnRef0Count);
265 // remove unused entries from font instance cache
266 FontInstanceList::iterator it_next = maFontInstanceList.begin();
267 while( it_next != maFontInstanceList.end() )
269 FontInstanceList::iterator it = it_next++;
270 LogicalFontInstance* pFontEntry = (*it).second;
271 if( pFontEntry->mnRefCount > 0 )
272 continue;
274 maFontInstanceList.erase( it );
275 delete pFontEntry;
276 --mnRef0Count;
277 assert(mnRef0Count>=0 && "ImplFontCache::Release() - refcount0 underflow");
279 if( mpFirstEntry == pFontEntry )
280 mpFirstEntry = nullptr;
283 assert(mnRef0Count==0 && "ImplFontCache::Release() - refcount0 mismatch");
286 int ImplFontCache::CountUnreferencedEntries() const
288 size_t nCount = 0;
289 // count unreferenced entries
290 for (FontInstanceList::const_iterator it = maFontInstanceList.begin();
291 it != maFontInstanceList.end(); ++it)
293 const LogicalFontInstance* pFontEntry = it->second;
294 if (pFontEntry->mnRefCount > 0)
295 continue;
296 ++nCount;
298 return nCount;
301 void ImplFontCache::Invalidate()
303 assert(CountUnreferencedEntries() == mnRef0Count);
305 // delete unreferenced entries
306 FontInstanceList::iterator it = maFontInstanceList.begin();
307 for(; it != maFontInstanceList.end(); ++it )
309 LogicalFontInstance* pFontEntry = (*it).second;
310 if( pFontEntry->mnRefCount > 0 )
311 continue;
313 delete pFontEntry;
314 --mnRef0Count;
317 // #112304# make sure the font cache is really clean
318 mpFirstEntry = nullptr;
319 maFontInstanceList.clear();
321 assert(mnRef0Count==0 && "ImplFontCache::Invalidate() - mnRef0Count non-zero");
325 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */