bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / font / fontcache.cxx
blobf3f87d36616bc1cdced2e1fec344052419f01360
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 <fontinstance.hxx>
21 #include <impfontcache.hxx>
22 #include <PhysicalFontCollection.hxx>
23 #include <PhysicalFontFace.hxx>
24 #include <PhysicalFontFamily.hxx>
25 #include <sal/log.hxx>
27 #if !(defined(_WIN32) || defined(MACOSX) || defined(IOS))
28 #include <unx/glyphcache.hxx>
29 #endif
31 size_t ImplFontCache::IFSD_Hash::operator()( const FontSelectPattern& rFSD ) const
33 return rFSD.hashCode();
36 bool ImplFontCache::IFSD_Equal::operator()(const FontSelectPattern& rA, const FontSelectPattern& rB) const
38 // check normalized font family name
39 if( rA.maSearchName != rB.maSearchName )
40 return false;
42 // check font transformation
43 if( (rA.mnHeight != rB.mnHeight)
44 || (rA.mnWidth != rB.mnWidth)
45 || (rA.mnOrientation != rB.mnOrientation) )
46 return false;
48 // check mapping relevant attributes
49 if( (rA.mbVertical != rB.mbVertical)
50 || (rA.meLanguage != rB.meLanguage) )
51 return false;
53 // check font face attributes
54 if( (rA.GetWeight() != rB.GetWeight())
55 || (rA.GetItalic() != rB.GetItalic())
56 // || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member
57 || (rA.GetPitch() != rB.GetPitch()) )
58 return false;
60 // check style name
61 if( rA.GetStyleName() != rB.GetStyleName() )
62 return false;
64 // Symbol fonts may recode from one type to another So they are only
65 // safely equivalent for equal targets
66 if (rA.IsSymbolFont() || rB.IsSymbolFont())
68 if (rA.maTargetName != rB.maTargetName)
69 return false;
72 // check for features
73 if ((rA.maTargetName.indexOf(FontSelectPattern::FEAT_PREFIX)
74 != -1 ||
75 rB.maTargetName.indexOf(FontSelectPattern::FEAT_PREFIX)
76 != -1) && rA.maTargetName != rB.maTargetName)
77 return false;
79 if (rA.mbEmbolden != rB.mbEmbolden)
80 return false;
82 if (rA.maItalicMatrix != rB.maItalicMatrix)
83 return false;
85 return true;
88 ImplFontCache::ImplFontCache()
89 : mpLastHitCacheEntry( nullptr )
90 , maFontInstanceList(0)
91 // The cache limit is set by the rough number of characters needed to read your average Asian newspaper.
92 , m_aBoundRectCache(3000)
95 ImplFontCache::~ImplFontCache()
97 for (const auto & rLFI : maFontInstanceList)
99 #if !(defined(_WIN32) || defined(MACOSX) || defined(IOS))
100 GlyphCache::GetInstance().TryGarbageCollectFont(rLFI.second.get());
101 #endif
102 rLFI.second->mpFontCache = nullptr;
106 rtl::Reference<LogicalFontInstance> ImplFontCache::GetFontInstance( PhysicalFontCollection const * pFontList,
107 const vcl::Font& rFont, const Size& rSize, float fExactHeight, bool bNonAntialias )
109 // initialize internal font request object
110 FontSelectPattern aFontSelData(rFont, rFont.GetFamilyName(), rSize, fExactHeight, bNonAntialias);
111 return GetFontInstance( pFontList, aFontSelData );
114 rtl::Reference<LogicalFontInstance> ImplFontCache::GetFontInstance( PhysicalFontCollection const * pFontList,
115 FontSelectPattern& aFontSelData )
117 rtl::Reference<LogicalFontInstance> pFontInstance;
118 PhysicalFontFamily* pFontFamily = nullptr;
120 // check if a directly matching logical font instance is already cached,
121 // the most recently used font usually has a hit rate of >50%
122 if (mpLastHitCacheEntry && IFSD_Equal()(aFontSelData, mpLastHitCacheEntry->GetFontSelectPattern()))
123 pFontInstance = mpLastHitCacheEntry;
124 else
126 FontInstanceList::const_iterator it = maFontInstanceList.find( aFontSelData );
127 if( it != maFontInstanceList.end() )
128 pFontInstance = (*it).second;
131 if( !pFontInstance ) // no direct cache hit
133 // find the best matching logical font family and update font selector accordingly
134 pFontFamily = pFontList->FindFontFamily( aFontSelData );
135 SAL_WARN_IF( (pFontFamily == nullptr), "vcl", "ImplFontCache::Get() No logical font found!" );
136 if( pFontFamily )
138 aFontSelData.maSearchName = pFontFamily->GetSearchName();
140 // check if an indirectly matching logical font instance is already cached
141 FontInstanceList::const_iterator it = maFontInstanceList.find( aFontSelData );
142 if( it != maFontInstanceList.end() )
143 pFontInstance = (*it).second;
147 if( !pFontInstance && pFontFamily) // still no cache hit => create a new font instance
149 PhysicalFontFace* pFontData = pFontFamily->FindBestFontFace(aFontSelData);
151 // create a new logical font instance from this physical font face
152 pFontInstance = pFontData->CreateFontInstance( aFontSelData );
153 pFontInstance->mpFontCache = this;
155 // if we're substituting from or to a symbol font we may need a symbol
156 // conversion table
157 if( pFontData->IsSymbolFont() || aFontSelData.IsSymbolFont() )
159 if( aFontSelData.maTargetName != aFontSelData.maSearchName )
160 pFontInstance->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
163 #ifdef MACOSX
164 //It might be better to dig out the font version of the target font
165 //to see if it's a modern re-coded apple symbol font in case that
166 //font shows up on a different platform
167 if (!pFontInstance->mpConversion &&
168 aFontSelData.maTargetName.equalsIgnoreAsciiCase("symbol") &&
169 aFontSelData.maSearchName.equalsIgnoreAsciiCase("symbol"))
171 pFontInstance->mpConversion = ConvertChar::GetRecodeData( "Symbol", "AppleSymbol" );
173 #endif
175 static const size_t FONTCACHE_MAX = getenv("LO_TESTNAME") ? 1 : 50;
177 if (maFontInstanceList.size() >= FONTCACHE_MAX)
179 struct limit_exception : public std::exception {};
182 maFontInstanceList.remove_if([this] (FontInstanceListPair const& rFontPair)
184 if (maFontInstanceList.size() < FONTCACHE_MAX)
185 throw limit_exception();
186 LogicalFontInstance* pFontEntry = rFontPair.second.get();
187 if (pFontEntry->m_nCount > 1)
188 return false;
189 m_aBoundRectCache.remove_if([&pFontEntry] (GlpyhBoundRectCachePair const& rGlyphPair)
190 { return rGlyphPair.first.m_pFont == pFontEntry; });
191 if (mpLastHitCacheEntry == pFontEntry)
192 mpLastHitCacheEntry = nullptr;
193 return true;
196 catch (limit_exception&) {}
199 assert(pFontInstance);
200 // add the new entry to the cache
201 maFontInstanceList.insert({aFontSelData, pFontInstance.get()});
204 mpLastHitCacheEntry = pFontInstance.get();
205 return pFontInstance;
208 rtl::Reference<LogicalFontInstance> ImplFontCache::GetGlyphFallbackFont( PhysicalFontCollection const * pFontCollection,
209 FontSelectPattern& rFontSelData, LogicalFontInstance* pFontInstance, int nFallbackLevel, OUString& rMissingCodes )
211 // get a candidate font for glyph fallback
212 // unless the previously selected font got a device specific substitution
213 // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
214 if( nFallbackLevel >= 1)
216 PhysicalFontFamily* pFallbackData = nullptr;
218 //fdo#33898 If someone has EUDC installed then they really want that to
219 //be used as the first-choice glyph fallback seeing as it's filled with
220 //private area codes with don't make any sense in any other font so
221 //prioritize it here if it's available. Ideally we would remove from
222 //rMissingCodes all the glyphs which it is able to resolve as an
223 //optimization, but that's tricky to achieve cross-platform without
224 //sufficient heavy-weight code that's likely to undo the value of the
225 //optimization
226 if (nFallbackLevel == 1)
227 pFallbackData = pFontCollection->FindFontFamily("EUDC");
228 if (!pFallbackData)
229 pFallbackData = pFontCollection->GetGlyphFallbackFont(rFontSelData, pFontInstance, rMissingCodes, nFallbackLevel-1);
230 // escape when there are no font candidates
231 if( !pFallbackData )
232 return nullptr;
233 // override the font name
234 rFontSelData.SetFamilyName( pFallbackData->GetFamilyName() );
235 // clear the cached normalized name
236 rFontSelData.maSearchName.clear();
239 rtl::Reference<LogicalFontInstance> pFallbackFont = GetFontInstance( pFontCollection, rFontSelData );
240 return pFallbackFont;
243 void ImplFontCache::Invalidate()
245 // #112304# make sure the font cache is really clean
246 mpLastHitCacheEntry = nullptr;
247 for (auto const & pair : maFontInstanceList)
248 pair.second->mpFontCache = nullptr;
249 maFontInstanceList.clear();
250 m_aBoundRectCache.clear();
253 bool ImplFontCache::GetCachedGlyphBoundRect(const LogicalFontInstance *pFont, sal_GlyphId nID, tools::Rectangle &rRect)
255 if (!pFont->GetFontCache())
256 return false;
257 assert(pFont->GetFontCache() == this);
258 if (pFont->GetFontCache() != this)
259 return false;
261 auto it = m_aBoundRectCache.find({pFont, nID});
262 if (it != m_aBoundRectCache.end())
264 rRect = it->second;
265 return true;
267 return false;
270 void ImplFontCache::CacheGlyphBoundRect(const LogicalFontInstance *pFont, sal_GlyphId nID, tools::Rectangle &rRect)
272 if (!pFont->GetFontCache())
273 return;
274 assert(pFont->GetFontCache() == this);
275 if (pFont->GetFontCache() != this)
276 return;
278 m_aBoundRectCache.insert({{pFont, nID}, rRect});
281 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */