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 <impfontcache.hxx>
29
30using namespace vcl::font;
31
33{
34 return rFSD.hashCode();
35}
36
38{
39 // check normalized font family name
40 if( rA.maSearchName != rB.maSearchName )
41 return false;
42
43 // check font transformation
44 if( (rA.mnHeight != rB.mnHeight)
45 || (rA.mnWidth != rB.mnWidth)
46 || (rA.mnOrientation != rB.mnOrientation) )
47 return false;
48
49 // check mapping relevant attributes
50 if( (rA.mbVertical != rB.mbVertical)
51 || (rA.meLanguage != rB.meLanguage) )
52 return false;
53
54 // check font face attributes
55 if( (rA.GetWeight() != rB.GetWeight())
56 || (rA.GetItalic() != rB.GetItalic())
57// || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member
58 || (rA.GetPitch() != rB.GetPitch()) )
59 return false;
60
61 // check style name
62 if( rA.GetStyleName() != rB.GetStyleName() )
63 return false;
64
65 // Symbol fonts may recode from one type to another So they are only
66 // safely equivalent for equal targets
68 {
69 if (rA.maTargetName != rB.maTargetName)
70 return false;
71 }
72
73 // check for features
75 != -1 ||
77 != -1) && rA.maTargetName != rB.maTargetName)
78 return false;
79
80 if (rA.mbEmbolden != rB.mbEmbolden)
81 return false;
82
83 if (rA.maItalicMatrix != rB.maItalicMatrix)
84 return false;
85
86 return true;
87}
88
90 : mpLastHitCacheEntry( nullptr )
91 , maFontInstanceList(std::numeric_limits<size_t>::max()) // "unlimited", i.e. no cleanup
92 // The cache limit is set by the rough number of characters needed to read your average Asian newspaper.
93 , m_aBoundRectCache(3000)
94{}
95
97{
98 for (const auto & rLFI : maFontInstanceList)
99 {
100 rLFI.second->mpFontCache = nullptr;
101 }
102}
103
105 const vcl::Font& rFont, const Size& rSize, float fExactHeight, bool bNonAntialias )
106{
107 // initialize internal font request object
108 vcl::font::FontSelectPattern aFontSelData(rFont, rFont.GetFamilyName(), rSize, fExactHeight, bNonAntialias);
109 return GetFontInstance( pFontList, aFontSelData );
110}
111
113 vcl::font::FontSelectPattern& aFontSelData )
114{
116 PhysicalFontFamily* pFontFamily = nullptr;
117
118 // check if a directly matching logical font instance is already cached,
119 // the most recently used font usually has a hit rate of >50%
121 pFontInstance = mpLastHitCacheEntry;
122 else
123 {
125 if( it != maFontInstanceList.end() )
126 pFontInstance = (*it).second;
127 }
128
129 if( !pFontInstance ) // no direct cache hit
130 {
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 {
136 aFontSelData.maSearchName = pFontFamily->GetSearchName();
137
138 // check if an indirectly matching logical font instance is already cached
140 if( it != maFontInstanceList.end() )
141 pFontInstance = (*it).second;
142 }
143 }
144
145 if( !pFontInstance && pFontFamily) // still no cache hit => create a new font instance
146 {
147 vcl::font::PhysicalFontFace* pFontData = pFontFamily->FindBestFontFace(aFontSelData);
148
149 // create a new logical font instance from this physical font face
150 pFontInstance = pFontData->CreateFontInstance( aFontSelData );
151 pFontInstance->mpFontCache = this;
152
153 // if we're substituting from or to a symbol font we may need a symbol
154 // conversion table
155 if( pFontData->IsMicrosoftSymbolEncoded() || aFontSelData.IsMicrosoftSymbolEncoded() )
156 {
157 if( aFontSelData.maTargetName != aFontSelData.maSearchName )
158 pFontInstance->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
159 }
160
161#ifdef MACOSX
162 //It might be better to dig out the font version of the target font
163 //to see if it's a modern re-coded apple symbol font in case that
164 //font shows up on a different platform
165 if (!pFontInstance->mpConversion &&
166 aFontSelData.maTargetName.equalsIgnoreAsciiCase("symbol") &&
167 aFontSelData.maSearchName.equalsIgnoreAsciiCase("symbol"))
168 {
169 pFontInstance->mpConversion = ConvertChar::GetRecodeData( u"Symbol", u"AppleSymbol" );
170 }
171#endif
172
173 static const size_t FONTCACHE_MAX = getenv("LO_TESTNAME") ? 1 : 50;
174
175 if (maFontInstanceList.size() >= FONTCACHE_MAX)
176 {
177 struct limit_exception : public std::exception {};
178 try
179 {
181 {
182 if (maFontInstanceList.size() < FONTCACHE_MAX)
183 throw limit_exception();
184 LogicalFontInstance* pFontEntry = rFontPair.second.get();
185 if (pFontEntry->m_nCount > 1)
186 return false;
188 { return rGlyphPair.first.m_pFont == pFontEntry; });
189 if (mpLastHitCacheEntry == pFontEntry)
190 mpLastHitCacheEntry = nullptr;
191 return true;
192 });
193 }
194 catch (limit_exception&) {}
195 }
196
197 assert(pFontInstance);
198 // add the new entry to the cache
199 maFontInstanceList.insert({aFontSelData, pFontInstance.get()});
200 }
201
202 mpLastHitCacheEntry = pFontInstance.get();
203 return pFontInstance;
204}
205
207 vcl::font::FontSelectPattern& rFontSelData, LogicalFontInstance* pFontInstance, int nFallbackLevel, OUString& rMissingCodes )
208{
209 // get a candidate font for glyph fallback
210 // unless the previously selected font got a device specific substitution
211 // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
212 if( nFallbackLevel >= 1)
213 {
214 PhysicalFontFamily* pFallbackData = nullptr;
215
216 //fdo#33898 If someone has EUDC installed then they really want that to
217 //be used as the first-choice glyph fallback seeing as it's filled with
218 //private area codes with don't make any sense in any other font so
219 //prioritize it here if it's available. Ideally we would remove from
220 //rMissingCodes all the glyphs which it is able to resolve as an
221 //optimization, but that's tricky to achieve cross-platform without
222 //sufficient heavy-weight code that's likely to undo the value of the
223 //optimization
224 if (nFallbackLevel == 1)
225 pFallbackData = pFontCollection->FindFontFamily(u"EUDC");
226 if (!pFallbackData)
227 pFallbackData = pFontCollection->GetGlyphFallbackFont(rFontSelData, pFontInstance, rMissingCodes, nFallbackLevel-1);
228 // escape when there are no font candidates
229 if( !pFallbackData )
230 return nullptr;
231 // override the font name
232 rFontSelData.SetFamilyName( pFallbackData->GetFamilyName() );
233 // clear the cached normalized name
234 rFontSelData.maSearchName.clear();
235 }
236
237 rtl::Reference<LogicalFontInstance> pFallbackFont = GetFontInstance( pFontCollection, rFontSelData );
238 return pFallbackFont;
239}
240
242{
243 // #112304# make sure the font cache is really clean
244 mpLastHitCacheEntry = nullptr;
245 for (auto const & pair : maFontInstanceList)
246 pair.second->mpFontCache = nullptr;
249}
250
252{
253 if (!pFont->GetFontCache())
254 return false;
255 assert(pFont->GetFontCache() == this);
256 if (pFont->GetFontCache() != this)
257 return false;
258
259 auto it = m_aBoundRectCache.find({pFont, nID});
260 if (it != m_aBoundRectCache.end())
261 {
262 rRect = it->second;
263 return true;
264 }
265 return false;
266}
267
269{
270 if (!pFont->GetFontCache())
271 return;
272 assert(pFont->GetFontCache() == this);
273 if (pFont->GetFontCache() != this)
274 return;
275
276 m_aBoundRectCache.insert({{pFont, nID}, rRect});
277}
278
279/* 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:112
bool GetCachedGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:251
FontInstanceList maFontInstanceList
GlyphBoundRectCache m_aBoundRectCache
void CacheGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:268
rtl::Reference< LogicalFontInstance > GetGlyphFallbackFont(vcl::font::PhysicalFontCollection const *, vcl::font::FontSelectPattern &, LogicalFontInstance *pLogicalFont, int nFallbackLevel, OUString &rMissingCodes)
Definition: fontcache.cxx:206
void Invalidate()
Definition: fontcache.cxx:241
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:903
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
float u
#define max(a, b)
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:37
size_t operator()(const vcl::font::FontSelectPattern &) const
Definition: fontcache.cxx:32