cid#1640468 Dereference after null check
[LibreOffice.git] / vcl / source / font / fontcache.cxx
blob70f29d37074ab152de1c2297a83f1f86d77ed9e6
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 <sal/config.h>
22 #include <sal/log.hxx>
24 #include <font/PhysicalFontCollection.hxx>
25 #include <font/PhysicalFontFace.hxx>
26 #include <font/PhysicalFontFamily.hxx>
27 #include <font/LogicalFontInstance.hxx>
28 #include <o3tl/test_info.hxx>
29 #include <tools/debug.hxx>
30 #include <impfontcache.hxx>
32 using namespace vcl::font;
34 size_t ImplFontCache::IFSD_Hash::operator()( const vcl::font::FontSelectPattern& rFSD ) const
36 return rFSD.hashCode();
39 bool ImplFontCache::IFSD_Equal::operator()(const vcl::font::FontSelectPattern& rA, const vcl::font::FontSelectPattern& rB) const
41 // check normalized font family name
42 if( rA.maSearchName != rB.maSearchName )
43 return false;
45 // check font transformation
46 if( (rA.mnHeight != rB.mnHeight)
47 || (rA.mnWidth != rB.mnWidth)
48 || (rA.mnOrientation != rB.mnOrientation) )
49 return false;
51 // check mapping relevant attributes
52 if( (rA.mbVertical != rB.mbVertical)
53 || (rA.meLanguage != rB.meLanguage) )
54 return false;
56 // check font face attributes
57 if( (rA.GetWeight() != rB.GetWeight())
58 || (rA.GetItalic() != rB.GetItalic())
59 // || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member
60 || (rA.GetPitch() != rB.GetPitch()) )
61 return false;
63 // check style name
64 if( rA.GetStyleName() != rB.GetStyleName() )
65 return false;
67 // Symbol fonts may recode from one type to another So they are only
68 // safely equivalent for equal targets
69 if (rA.IsMicrosoftSymbolEncoded() || rB.IsMicrosoftSymbolEncoded())
71 if (rA.maTargetName != rB.maTargetName)
72 return false;
75 // check for features
76 if ((rA.maTargetName.indexOf(vcl::font::FontSelectPattern::FEAT_PREFIX)
77 != -1 ||
78 rB.maTargetName.indexOf(vcl::font::FontSelectPattern::FEAT_PREFIX)
79 != -1) && rA.maTargetName != rB.maTargetName)
80 return false;
82 if (rA.mbEmbolden != rB.mbEmbolden)
83 return false;
85 if (rA.maItalicMatrix != rB.maItalicMatrix)
86 return false;
88 return true;
91 ImplFontCache::ImplFontCache()
92 : mpLastHitCacheEntry( nullptr )
93 , maFontInstanceList(std::numeric_limits<size_t>::max()) // "unlimited", i.e. no cleanup
94 // The cache limit is set by the rough number of characters needed to read your average Asian newspaper.
95 , m_aBoundRectCache(3000)
98 ImplFontCache::~ImplFontCache()
100 DBG_TESTSOLARMUTEX();
101 for (const auto & rLFI : maFontInstanceList)
103 rLFI.second->mpFontCache = nullptr;
107 rtl::Reference<LogicalFontInstance> ImplFontCache::GetFontInstance( PhysicalFontCollection const * pFontList,
108 const vcl::Font& rFont, const Size& rSize, float fExactHeight, bool bNonAntialias )
110 // initialize internal font request object
111 vcl::font::FontSelectPattern aFontSelData(rFont, rFont.GetFamilyName(), rSize, fExactHeight, bNonAntialias);
112 return GetFontInstance( pFontList, aFontSelData );
115 rtl::Reference<LogicalFontInstance> ImplFontCache::GetFontInstance( PhysicalFontCollection const * pFontList,
116 vcl::font::FontSelectPattern& aFontSelData )
118 DBG_TESTSOLARMUTEX();
119 rtl::Reference<LogicalFontInstance> pFontInstance;
120 PhysicalFontFamily* pFontFamily = nullptr;
122 // check if a directly matching logical font instance is already cached,
123 // the most recently used font usually has a hit rate of >50%
124 if (mpLastHitCacheEntry && IFSD_Equal()(aFontSelData, mpLastHitCacheEntry->GetFontSelectPattern()))
125 pFontInstance = mpLastHitCacheEntry;
126 else
128 FontInstanceList::const_iterator it = maFontInstanceList.find( aFontSelData );
129 if( it != maFontInstanceList.end() )
130 pFontInstance = (*it).second;
133 if( !pFontInstance ) // no direct cache hit
135 // find the best matching logical font family and update font selector accordingly
136 pFontFamily = pFontList->FindFontFamily( aFontSelData );
137 SAL_WARN_IF( (pFontFamily == nullptr), "vcl", "ImplFontCache::Get() No logical font found!" );
138 if( pFontFamily )
140 aFontSelData.maSearchName = pFontFamily->GetSearchName();
142 // check if an indirectly matching logical font instance is already cached
143 FontInstanceList::const_iterator it = maFontInstanceList.find( aFontSelData );
144 if( it != maFontInstanceList.end() )
145 pFontInstance = (*it).second;
149 if( !pFontInstance && pFontFamily) // still no cache hit => create a new font instance
151 vcl::font::PhysicalFontFace* pFontData = pFontFamily->FindBestFontFace(aFontSelData);
153 // create a new logical font instance from this physical font face
154 pFontInstance = pFontData->CreateFontInstance( aFontSelData );
155 pFontInstance->mpFontCache = this;
157 // if we're substituting from or to a symbol font we may need a symbol
158 // conversion table
159 if( pFontData->IsMicrosoftSymbolEncoded() || aFontSelData.IsMicrosoftSymbolEncoded() || IsOpenSymbol(aFontSelData.maSearchName) )
161 if( aFontSelData.maTargetName != aFontSelData.maSearchName )
162 pFontInstance->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
165 #ifdef MACOSX
166 //It might be better to dig out the font version of the target font
167 //to see if it's a modern re-coded apple symbol font in case that
168 //font shows up on a different platform
169 if (!pFontInstance->mpConversion &&
170 aFontSelData.maTargetName.equalsIgnoreAsciiCase("symbol") &&
171 aFontSelData.maSearchName.equalsIgnoreAsciiCase("symbol"))
173 pFontInstance->mpConversion = ConvertChar::GetRecodeData( u"Symbol", u"AppleSymbol" );
175 #endif
177 static const size_t FONTCACHE_MAX = o3tl::IsRunningUnitTest() ? 1 : 50;
179 if (maFontInstanceList.size() >= FONTCACHE_MAX)
181 struct limit_exception : public std::exception {};
184 maFontInstanceList.remove_if([this] (FontInstanceList::key_value_pair_t const& rFontPair)
186 if (maFontInstanceList.size() < FONTCACHE_MAX)
187 throw limit_exception();
188 LogicalFontInstance* pFontEntry = rFontPair.second.get();
189 if (pFontEntry->m_nCount > 1)
190 return false;
191 m_aBoundRectCache.remove_if([&pFontEntry] (GlyphBoundRectCache::key_value_pair_t const& rGlyphPair)
192 { return rGlyphPair.first.m_pFont == pFontEntry; });
193 if (mpLastHitCacheEntry == pFontEntry)
194 mpLastHitCacheEntry = nullptr;
195 return true;
198 catch (limit_exception&) {}
201 assert(pFontInstance);
202 // add the new entry to the cache
203 maFontInstanceList.insert({aFontSelData, pFontInstance.get()});
206 mpLastHitCacheEntry = pFontInstance.get();
207 return pFontInstance;
210 rtl::Reference<LogicalFontInstance> ImplFontCache::GetGlyphFallbackFont( PhysicalFontCollection const * pFontCollection,
211 vcl::font::FontSelectPattern& rFontSelData, LogicalFontInstance* pFontInstance, int nFallbackLevel, OUString& rMissingCodes )
213 // get a candidate font for glyph fallback
214 // unless the previously selected font got a device specific substitution
215 // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
216 if( nFallbackLevel >= 1)
218 PhysicalFontFamily* pFallbackData = nullptr;
220 //fdo#33898 If someone has EUDC installed then they really want that to
221 //be used as the first-choice glyph fallback seeing as it's filled with
222 //private area codes with don't make any sense in any other font so
223 //prioritize it here if it's available. Ideally we would remove from
224 //rMissingCodes all the glyphs which it is able to resolve as an
225 //optimization, but that's tricky to achieve cross-platform without
226 //sufficient heavy-weight code that's likely to undo the value of the
227 //optimization
228 if (nFallbackLevel == 1)
229 pFallbackData = pFontCollection->FindFontFamily(u"EUDC");
230 if (!pFallbackData)
231 pFallbackData = pFontCollection->GetGlyphFallbackFont(rFontSelData, pFontInstance, rMissingCodes, nFallbackLevel-1);
232 // escape when there are no font candidates
233 if( !pFallbackData )
234 return nullptr;
235 // override the font name
236 rFontSelData.SetFamilyName( pFallbackData->GetFamilyName() );
237 // clear the cached normalized name
238 rFontSelData.maSearchName.clear();
241 rtl::Reference<LogicalFontInstance> pFallbackFont = GetFontInstance( pFontCollection, rFontSelData );
242 return pFallbackFont;
245 void ImplFontCache::Invalidate()
247 DBG_TESTSOLARMUTEX();
248 // #112304# make sure the font cache is really clean
249 mpLastHitCacheEntry = nullptr;
250 for (auto const & pair : maFontInstanceList)
251 pair.second->mpFontCache = nullptr;
252 maFontInstanceList.clear();
253 m_aBoundRectCache.clear();
256 bool ImplFontCache::GetCachedGlyphBoundRect(const LogicalFontInstance *pFont, sal_GlyphId nID, basegfx::B2DRectangle &rRect)
258 if (!pFont->GetFontCache())
259 return false;
260 assert(pFont->GetFontCache() == this);
261 if (pFont->GetFontCache() != this)
262 return false;
264 auto it = m_aBoundRectCache.find({pFont, nID});
265 if (it != m_aBoundRectCache.end())
267 rRect = it->second;
268 return true;
270 return false;
273 void ImplFontCache::CacheGlyphBoundRect(const LogicalFontInstance *pFont, sal_GlyphId nID, basegfx::B2DRectangle &rRect)
275 if (!pFont->GetFontCache())
276 return;
277 assert(pFont->GetFontCache() == this);
278 if (pFont->GetFontCache() != this)
279 return;
281 m_aBoundRectCache.insert({{pFont, nID}, rRect});
284 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */