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 
27 #include <fontinstance.hxx>
28 #include <impfontcache.hxx>
29 
30 using 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
67  if (rA.IsSymbolFont() || rB.IsSymbolFont())
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->IsSymbolFont() || aFontSelData.IsSymbolFont() )
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;
187  m_aBoundRectCache.remove_if([&pFontEntry] (GlyphBoundRectCache::key_value_pair_t const& rGlyphPair)
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;
247  maFontInstanceList.clear();
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: */
vcl::font::PhysicalFontFamily * GetGlyphFallbackFont(vcl::font::FontSelectPattern &, LogicalFontInstance *pLogicalFont, OUString &rMissingCodes, int nFallbackLevel) const
const OUString & GetStyleName() const
const OUString & GetFamilyName() const
Definition: font/font.cxx:878
ImplFontCache * mpFontCache
void insert(key_value_pair_t &rPair)
LogicalFontInstance * mpLastHitCacheEntry
keeps the last hit cache entry
#define max(a, b)
bool operator()(const vcl::font::FontSelectPattern &, const vcl::font::FontSelectPattern &) const
Definition: fontcache.cxx:37
rtl::Reference< LogicalFontInstance > GetGlyphFallbackFont(vcl::font::PhysicalFontCollection const *, vcl::font::FontSelectPattern &, LogicalFontInstance *pLogicalFont, int nFallbackLevel, OUString &rMissingCodes)
Definition: fontcache.cxx:206
sal_uInt16 sal_GlyphId
Definition: glyphid.hxx:24
list_const_iterator_t find(const Key &key)
size_t operator()(const vcl::font::FontSelectPattern &) const
Definition: fontcache.cxx:32
const vcl::font::FontSelectPattern & GetFontSelectPattern() const
void remove_if(UnaryPredicate pred)
FontItalic GetItalic() const
FontWeight GetWeight() const
void Invalidate()
Definition: fontcache.cxx:241
void SetFamilyName(const OUString &sFamilyName)
const OUString & GetSearchName() const
A PhysicalFontFaceCollection is created by a PhysicalFontCollection and becomes invalid when original...
float u
PhysicalFontFace * FindBestFontFace(const vcl::font::FontSelectPattern &rFSD) const
size_t size() const
abstract base class for physical font faces
void CacheGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:268
virtual rtl::Reference< LogicalFontInstance > CreateFontInstance(const vcl::font::FontSelectPattern &) const =0
bool GetCachedGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:251
vcl::font::PhysicalFontFamily * FindFontFamily(std::u16string_view rFontName) const
#define SAL_WARN_IF(condition, area, stream)
list_const_iterator_t end() const
const ImplFontCache * GetFontCache() const
oslInterlockedCount m_nCount
FontInstanceList maFontInstanceList
bool IsSymbolFont() const
const OUString & GetFamilyName() const
FontPitch GetPitch() const
rtl::Reference< LogicalFontInstance > GetFontInstance(vcl::font::PhysicalFontCollection const *, vcl::font::FontSelectPattern &)
Definition: fontcache.cxx:112
GlyphBoundRectCache m_aBoundRectCache