LibreOffice Module vcl (master) 1
fontcache.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <sal/log.hxx>
23
28#include <tools/debug.hxx>
29#include <impfontcache.hxx>
30
31using namespace vcl::font;
32
34{
35 return rFSD.hashCode();
36}
37
39{
40 // check normalized font family name
41 if( rA.maSearchName != rB.maSearchName )
42 return false;
43
44 // check font transformation
45 if( (rA.mnHeight != rB.mnHeight)
46 || (rA.mnWidth != rB.mnWidth)
47 || (rA.mnOrientation != rB.mnOrientation) )
48 return false;
49
50 // check mapping relevant attributes
51 if( (rA.mbVertical != rB.mbVertical)
52 || (rA.meLanguage != rB.meLanguage) )
53 return false;
54
55 // check font face attributes
56 if( (rA.GetWeight() != rB.GetWeight())
57 || (rA.GetItalic() != rB.GetItalic())
58// || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member
59 || (rA.GetPitch() != rB.GetPitch()) )
60 return false;
61
62 // check style name
63 if( rA.GetStyleName() != rB.GetStyleName() )
64 return false;
65
66 // Symbol fonts may recode from one type to another So they are only
67 // safely equivalent for equal targets
69 {
70 if (rA.maTargetName != rB.maTargetName)
71 return false;
72 }
73
74 // check for features
76 != -1 ||
78 != -1) && rA.maTargetName != rB.maTargetName)
79 return false;
80
81 if (rA.mbEmbolden != rB.mbEmbolden)
82 return false;
83
84 if (rA.maItalicMatrix != rB.maItalicMatrix)
85 return false;
86
87 return true;
88}
89
91 : mpLastHitCacheEntry( nullptr )
92 , maFontInstanceList(std::numeric_limits<size_t>::max()) // "unlimited", i.e. no cleanup
93 // The cache limit is set by the rough number of characters needed to read your average Asian newspaper.
94 , m_aBoundRectCache(3000)
95{}
96
98{
100 for (const auto & rLFI : maFontInstanceList)
101 {
102 rLFI.second->mpFontCache = nullptr;
103 }
104}
105
107 const vcl::Font& rFont, const Size& rSize, float fExactHeight, bool bNonAntialias )
108{
109 // initialize internal font request object
110 vcl::font::FontSelectPattern aFontSelData(rFont, rFont.GetFamilyName(), rSize, fExactHeight, bNonAntialias);
111 return GetFontInstance( pFontList, aFontSelData );
112}
113
115 vcl::font::FontSelectPattern& aFontSelData )
116{
119 PhysicalFontFamily* pFontFamily = nullptr;
120
121 // check if a directly matching logical font instance is already cached,
122 // the most recently used font usually has a hit rate of >50%
124 pFontInstance = mpLastHitCacheEntry;
125 else
126 {
128 if( it != maFontInstanceList.end() )
129 pFontInstance = (*it).second;
130 }
131
132 if( !pFontInstance ) // no direct cache hit
133 {
134 // find the best matching logical font family and update font selector accordingly
135 pFontFamily = pFontList->FindFontFamily( aFontSelData );
136 SAL_WARN_IF( (pFontFamily == nullptr), "vcl", "ImplFontCache::Get() No logical font found!" );
137 if( pFontFamily )
138 {
139 aFontSelData.maSearchName = pFontFamily->GetSearchName();
140
141 // check if an indirectly matching logical font instance is already cached
143 if( it != maFontInstanceList.end() )
144 pFontInstance = (*it).second;
145 }
146 }
147
148 if( !pFontInstance && pFontFamily) // still no cache hit => create a new font instance
149 {
150 vcl::font::PhysicalFontFace* pFontData = pFontFamily->FindBestFontFace(aFontSelData);
151
152 // create a new logical font instance from this physical font face
153 pFontInstance = pFontData->CreateFontInstance( aFontSelData );
154 pFontInstance->mpFontCache = this;
155
156 // if we're substituting from or to a symbol font we may need a symbol
157 // conversion table
158 if( pFontData->IsMicrosoftSymbolEncoded() || aFontSelData.IsMicrosoftSymbolEncoded() || IsOpenSymbol(aFontSelData.maSearchName) )
159 {
160 if( aFontSelData.maTargetName != aFontSelData.maSearchName )
161 pFontInstance->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
162 }
163
164#ifdef MACOSX
165 //It might be better to dig out the font version of the target font
166 //to see if it's a modern re-coded apple symbol font in case that
167 //font shows up on a different platform
168 if (!pFontInstance->mpConversion &&
169 aFontSelData.maTargetName.equalsIgnoreAsciiCase("symbol") &&
170 aFontSelData.maSearchName.equalsIgnoreAsciiCase("symbol"))
171 {
172 pFontInstance->mpConversion = ConvertChar::GetRecodeData( u"Symbol", u"AppleSymbol" );
173 }
174#endif
175
176 static const size_t FONTCACHE_MAX = getenv("LO_TESTNAME") ? 1 : 50;
177
178 if (maFontInstanceList.size() >= FONTCACHE_MAX)
179 {
180 struct limit_exception : public std::exception {};
181 try
182 {
184 {
185 if (maFontInstanceList.size() < FONTCACHE_MAX)
186 throw limit_exception();
187 LogicalFontInstance* pFontEntry = rFontPair.second.get();
188 if (pFontEntry->m_nCount > 1)
189 return false;
191 { return rGlyphPair.first.m_pFont == pFontEntry; });
192 if (mpLastHitCacheEntry == pFontEntry)
193 mpLastHitCacheEntry = nullptr;
194 return true;
195 });
196 }
197 catch (limit_exception&) {}
198 }
199
200 assert(pFontInstance);
201 // add the new entry to the cache
202 maFontInstanceList.insert({aFontSelData, pFontInstance.get()});
203 }
204
205 mpLastHitCacheEntry = pFontInstance.get();
206 return pFontInstance;
207}
208
210 vcl::font::FontSelectPattern& rFontSelData, LogicalFontInstance* pFontInstance, int nFallbackLevel, OUString& rMissingCodes )
211{
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)
216 {
217 PhysicalFontFamily* pFallbackData = nullptr;
218
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 //prioritize 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(u"EUDC");
229 if (!pFallbackData)
230 pFallbackData = pFontCollection->GetGlyphFallbackFont(rFontSelData, pFontInstance, 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();
238 }
239
240 rtl::Reference<LogicalFontInstance> pFallbackFont = GetFontInstance( pFontCollection, rFontSelData );
241 return pFallbackFont;
242}
243
245{
247 // #112304# make sure the font cache is really clean
248 mpLastHitCacheEntry = nullptr;
249 for (auto const & pair : maFontInstanceList)
250 pair.second->mpFontCache = nullptr;
253}
254
256{
257 if (!pFont->GetFontCache())
258 return false;
259 assert(pFont->GetFontCache() == this);
260 if (pFont->GetFontCache() != this)
261 return false;
262
263 auto it = m_aBoundRectCache.find({pFont, nID});
264 if (it != m_aBoundRectCache.end())
265 {
266 rRect = it->second;
267 return true;
268 }
269 return false;
270}
271
273{
274 if (!pFont->GetFontCache())
275 return;
276 assert(pFont->GetFontCache() == this);
277 if (pFont->GetFontCache() != this)
278 return;
279
280 m_aBoundRectCache.insert({{pFont, nID}, rRect});
281}
282
283/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsMicrosoftSymbolEncoded() const
FontItalic GetItalic() const
FontWeight GetWeight() const
void SetFamilyName(const OUString &sFamilyName)
FontPitch GetPitch() const
const OUString & GetStyleName() const
LogicalFontInstance * mpLastHitCacheEntry
keeps the last hit cache entry
rtl::Reference< LogicalFontInstance > GetFontInstance(vcl::font::PhysicalFontCollection const *, vcl::font::FontSelectPattern &)
Definition: fontcache.cxx:114
bool GetCachedGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:255
FontInstanceList maFontInstanceList
GlyphBoundRectCache m_aBoundRectCache
void CacheGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:272
rtl::Reference< LogicalFontInstance > GetGlyphFallbackFont(vcl::font::PhysicalFontCollection const *, vcl::font::FontSelectPattern &, LogicalFontInstance *pLogicalFont, int nFallbackLevel, OUString &rMissingCodes)
Definition: fontcache.cxx:209
void Invalidate()
Definition: fontcache.cxx:244
const ImplFontCache * GetFontCache() const
const vcl::font::FontSelectPattern & GetFontSelectPattern() const
size_t size() const
void insert(key_value_pair_t &rPair)
list_const_iterator_t end() const
void remove_if(UnaryPredicate pred)
list_const_iterator_t find(const Key &key)
oslInterlockedCount m_nCount
const OUString & GetFamilyName() const
Definition: font/font.cxx:904
vcl::font::PhysicalFontFamily * FindFontFamily(std::u16string_view rFontName) const
vcl::font::PhysicalFontFamily * GetGlyphFallbackFont(vcl::font::FontSelectPattern &, LogicalFontInstance *pLogicalFont, OUString &rMissingCodes, int nFallbackLevel) const
abstract base class for physical font faces
virtual rtl::Reference< LogicalFontInstance > CreateFontInstance(const vcl::font::FontSelectPattern &) const =0
PhysicalFontFace * FindBestFontFace(const vcl::font::FontSelectPattern &rFSD) const
const OUString & GetSearchName() const
const OUString & GetFamilyName() const
#define DBG_TESTSOLARMUTEX()
float u
#define max(a, b)
UNOTOOLS_DLLPUBLIC bool IsOpenSymbol(std::u16string_view rFontName)
uint32_t sal_GlyphId
Definition: glyphid.hxx:24
#define SAL_WARN_IF(condition, area, stream)
if(aStr !=aBuf) UpdateName_Impl(m_xFollowLb.get()
A PhysicalFontFaceCollection is created by a PhysicalFontCollection and becomes invalid when original...
bool operator()(const vcl::font::FontSelectPattern &, const vcl::font::FontSelectPattern &) const
Definition: fontcache.cxx:38
size_t operator()(const vcl::font::FontSelectPattern &) const
Definition: fontcache.cxx:33