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 <fontinstance.hxx>
21 #include <impfontcache.hxx>
23 #include <PhysicalFontFace.hxx>
24 #include <PhysicalFontFamily.hxx>
25 #include <sal/log.hxx>
26 
27 #if !(defined(_WIN32) || defined(MACOSX) || defined(IOS))
28 #include <unx/glyphcache.hxx>
29 #endif
30 
32 {
33  return rFSD.hashCode();
34 }
35 
37 {
38  // check normalized font family name
39  if( rA.maSearchName != rB.maSearchName )
40  return false;
41 
42  // check font transformation
43  if( (rA.mnHeight != rB.mnHeight)
44  || (rA.mnWidth != rB.mnWidth)
45  || (rA.mnOrientation != rB.mnOrientation) )
46  return false;
47 
48  // check mapping relevant attributes
49  if( (rA.mbVertical != rB.mbVertical)
50  || (rA.meLanguage != rB.meLanguage) )
51  return false;
52 
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;
59 
60  // check style name
61  if( rA.GetStyleName() != rB.GetStyleName() )
62  return false;
63 
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())
67  {
68  if (rA.maTargetName != rB.maTargetName)
69  return false;
70  }
71 
72  // check for features
74  != -1 ||
76  != -1) && rA.maTargetName != rB.maTargetName)
77  return false;
78 
79  if (rA.mbEmbolden != rB.mbEmbolden)
80  return false;
81 
82  if (rA.maItalicMatrix != rB.maItalicMatrix)
83  return false;
84 
85  return true;
86 }
87 
89  : mpLastHitCacheEntry( nullptr )
91  // The cache limit is set by the rough number of characters needed to read your average Asian newspaper.
92  , m_aBoundRectCache(3000)
93 {}
94 
96 {
97  for (const auto & rLFI : maFontInstanceList)
98  {
99 #if !(defined(_WIN32) || defined(MACOSX) || defined(IOS))
100  FreetypeManager::get().TryGarbageCollectFont(rLFI.second.get());
101 #endif
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  FontSelectPattern aFontSelData(rFont, rFont.GetFamilyName(), rSize, fExactHeight, bNonAntialias);
111  return GetFontInstance( pFontList, aFontSelData );
112 }
113 
115  FontSelectPattern& aFontSelData )
116 {
118  PhysicalFontFamily* pFontFamily = nullptr;
119 
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%
123  pFontInstance = mpLastHitCacheEntry;
124  else
125  {
127  if( it != maFontInstanceList.end() )
128  pFontInstance = (*it).second;
129  }
130 
131  if( !pFontInstance ) // no direct cache hit
132  {
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 )
137  {
138  aFontSelData.maSearchName = pFontFamily->GetSearchName();
139 
140  // check if an indirectly matching logical font instance is already cached
142  if( it != maFontInstanceList.end() )
143  pFontInstance = (*it).second;
144  }
145  }
146 
147  if( !pFontInstance && pFontFamily) // still no cache hit => create a new font instance
148  {
149  PhysicalFontFace* pFontData = pFontFamily->FindBestFontFace(aFontSelData);
150 
151  // create a new logical font instance from this physical font face
152  pFontInstance = pFontData->CreateFontInstance( aFontSelData );
153  pFontInstance->mpFontCache = this;
154 
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() )
158  {
159  if( aFontSelData.maTargetName != aFontSelData.maSearchName )
160  pFontInstance->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
161  }
162 
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"))
170  {
171  pFontInstance->mpConversion = ConvertChar::GetRecodeData( "Symbol", "AppleSymbol" );
172  }
173 #endif
174 
175  static const size_t FONTCACHE_MAX = getenv("LO_TESTNAME") ? 1 : 50;
176 
177  if (maFontInstanceList.size() >= FONTCACHE_MAX)
178  {
179  struct limit_exception : public std::exception {};
180  try
181  {
182  maFontInstanceList.remove_if([this] (FontInstanceListPair const& rFontPair)
183  {
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] (GlyphBoundRectCachePair const& rGlyphPair)
190  { return rGlyphPair.first.m_pFont == pFontEntry; });
191  if (mpLastHitCacheEntry == pFontEntry)
192  mpLastHitCacheEntry = nullptr;
193  return true;
194  });
195  }
196  catch (limit_exception&) {}
197  }
198 
199  assert(pFontInstance);
200  // add the new entry to the cache
201  maFontInstanceList.insert({aFontSelData, pFontInstance.get()});
202  }
203 
204  mpLastHitCacheEntry = pFontInstance.get();
205  return pFontInstance;
206 }
207 
209  FontSelectPattern& rFontSelData, LogicalFontInstance* pFontInstance, int nFallbackLevel, OUString& rMissingCodes )
210 {
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)
215  {
216  PhysicalFontFamily* pFallbackData = nullptr;
217 
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();
237  }
238 
239  rtl::Reference<LogicalFontInstance> pFallbackFont = GetFontInstance( pFontCollection, rFontSelData );
240  return pFallbackFont;
241 }
242 
244 {
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();
251 }
252 
254 {
255  if (!pFont->GetFontCache())
256  return false;
257  assert(pFont->GetFontCache() == this);
258  if (pFont->GetFontCache() != this)
259  return false;
260 
261  auto it = m_aBoundRectCache.find({pFont, nID});
262  if (it != m_aBoundRectCache.end())
263  {
264  rRect = it->second;
265  return true;
266  }
267  return false;
268 }
269 
271 {
272  if (!pFont->GetFontCache())
273  return;
274  assert(pFont->GetFontCache() == this);
275  if (pFont->GetFontCache() != this)
276  return;
277 
278  m_aBoundRectCache.insert({{pFont, nID}, rRect});
279 }
280 
281 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static FreetypeManager & get()
const OUString & GetStyleName() const
const OUString & GetFamilyName() const
Definition: font/font.cxx:670
ImplFontCache * mpFontCache
list_const_iterator_t end() const
size_t operator()(const FontSelectPattern &) const
Definition: fontcache.cxx:31
PhysicalFontFamily * GetGlyphFallbackFont(FontSelectPattern &, LogicalFontInstance *pLogicalFont, OUString &rMissingCodes, int nFallbackLevel) const
PhysicalFontFace * FindBestFontFace(const FontSelectPattern &rFSD) const
LogicalFontInstance * mpLastHitCacheEntry
keeps the last hit cache entry
size_t hashCode() const
Definition: fontselect.cxx:85
sal_uInt16 sal_GlyphId
Definition: glyphitem.hxx:26
const OUString & GetSearchName() const
ItalicMatrix maItalicMatrix
Definition: fontselect.hxx:68
size_t size() const
FontItalic GetItalic() const
void TryGarbageCollectFont(LogicalFontInstance *)
Try to GarbageCollect an explicit logical font.
abstract base class for physical font faces
FontWeight GetWeight() const
void Invalidate()
Definition: fontcache.cxx:243
virtual rtl::Reference< LogicalFontInstance > CreateFontInstance(const FontSelectPattern &) const =0
void SetFamilyName(const OUString &sFamilyName)
bool operator()(const FontSelectPattern &, const FontSelectPattern &) const
Definition: fontcache.cxx:36
list_const_iterator_t find(const Key &key)
FontInstanceList::key_value_pair_t FontInstanceListPair
PhysicalFontFamily * FindFontFamily(const OUString &rFontName) const
void CacheGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:270
GlyphBoundRectCache::key_value_pair_t GlyphBoundRectCachePair
const FontSelectPattern & GetFontSelectPattern() const
rtl::Reference< LogicalFontInstance > GetGlyphFallbackFont(PhysicalFontCollection const *, FontSelectPattern &, LogicalFontInstance *pLogicalFont, int nFallbackLevel, OUString &rMissingCodes)
Definition: fontcache.cxx:208
bool GetCachedGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:253
static const ConvertChar * GetRecodeData(const OUString &rOrgFontName, const OUString &rMapFontName)
#define SAL_WARN_IF(condition, area, stream)
rtl::Reference< LogicalFontInstance > GetFontInstance(PhysicalFontCollection const *, FontSelectPattern &)
Definition: fontcache.cxx:114
static const char FEAT_PREFIX
Definition: fontselect.hxx:53
const ImplFontCache * GetFontCache() const
void insert(key_value_pair_t &rPair)
oslInterlockedCount m_nCount
LanguageType meLanguage
Definition: fontselect.hxx:63
FontInstanceList maFontInstanceList
bool IsSymbolFont() const
FontPitch GetPitch() const
OUString maSearchName
Definition: fontselect.hxx:58
OUString maTargetName
Definition: fontselect.hxx:57
void remove_if(UnaryPredicate pred)
GlyphBoundRectCache m_aBoundRectCache
const OUString & GetFamilyName() const