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  rLFI.second->mpFontCache = nullptr;
100  }
101 }
102 
104  const vcl::Font& rFont, const Size& rSize, float fExactHeight, bool bNonAntialias )
105 {
106  // initialize internal font request object
107  FontSelectPattern aFontSelData(rFont, rFont.GetFamilyName(), rSize, fExactHeight, bNonAntialias);
108  return GetFontInstance( pFontList, aFontSelData );
109 }
110 
112  FontSelectPattern& aFontSelData )
113 {
115  PhysicalFontFamily* pFontFamily = nullptr;
116 
117  // check if a directly matching logical font instance is already cached,
118  // the most recently used font usually has a hit rate of >50%
120  pFontInstance = mpLastHitCacheEntry;
121  else
122  {
124  if( it != maFontInstanceList.end() )
125  pFontInstance = (*it).second;
126  }
127 
128  if( !pFontInstance ) // no direct cache hit
129  {
130  // find the best matching logical font family and update font selector accordingly
131  pFontFamily = pFontList->FindFontFamily( aFontSelData );
132  SAL_WARN_IF( (pFontFamily == nullptr), "vcl", "ImplFontCache::Get() No logical font found!" );
133  if( pFontFamily )
134  {
135  aFontSelData.maSearchName = pFontFamily->GetSearchName();
136 
137  // check if an indirectly matching logical font instance is already cached
139  if( it != maFontInstanceList.end() )
140  pFontInstance = (*it).second;
141  }
142  }
143 
144  if( !pFontInstance && pFontFamily) // still no cache hit => create a new font instance
145  {
146  PhysicalFontFace* pFontData = pFontFamily->FindBestFontFace(aFontSelData);
147 
148  // create a new logical font instance from this physical font face
149  pFontInstance = pFontData->CreateFontInstance( aFontSelData );
150  pFontInstance->mpFontCache = this;
151 
152  // if we're substituting from or to a symbol font we may need a symbol
153  // conversion table
154  if( pFontData->IsSymbolFont() || aFontSelData.IsSymbolFont() )
155  {
156  if( aFontSelData.maTargetName != aFontSelData.maSearchName )
157  pFontInstance->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
158  }
159 
160 #ifdef MACOSX
161  //It might be better to dig out the font version of the target font
162  //to see if it's a modern re-coded apple symbol font in case that
163  //font shows up on a different platform
164  if (!pFontInstance->mpConversion &&
165  aFontSelData.maTargetName.equalsIgnoreAsciiCase("symbol") &&
166  aFontSelData.maSearchName.equalsIgnoreAsciiCase("symbol"))
167  {
168  pFontInstance->mpConversion = ConvertChar::GetRecodeData( "Symbol", "AppleSymbol" );
169  }
170 #endif
171 
172  static const size_t FONTCACHE_MAX = getenv("LO_TESTNAME") ? 1 : 50;
173 
174  if (maFontInstanceList.size() >= FONTCACHE_MAX)
175  {
176  struct limit_exception : public std::exception {};
177  try
178  {
180  {
181  if (maFontInstanceList.size() < FONTCACHE_MAX)
182  throw limit_exception();
183  LogicalFontInstance* pFontEntry = rFontPair.second.get();
184  if (pFontEntry->m_nCount > 1)
185  return false;
186  m_aBoundRectCache.remove_if([&pFontEntry] (GlyphBoundRectCache::key_value_pair_t const& rGlyphPair)
187  { return rGlyphPair.first.m_pFont == pFontEntry; });
188  if (mpLastHitCacheEntry == pFontEntry)
189  mpLastHitCacheEntry = nullptr;
190  return true;
191  });
192  }
193  catch (limit_exception&) {}
194  }
195 
196  assert(pFontInstance);
197  // add the new entry to the cache
198  maFontInstanceList.insert({aFontSelData, pFontInstance.get()});
199  }
200 
201  mpLastHitCacheEntry = pFontInstance.get();
202  return pFontInstance;
203 }
204 
206  FontSelectPattern& rFontSelData, LogicalFontInstance* pFontInstance, int nFallbackLevel, OUString& rMissingCodes )
207 {
208  // get a candidate font for glyph fallback
209  // unless the previously selected font got a device specific substitution
210  // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
211  if( nFallbackLevel >= 1)
212  {
213  PhysicalFontFamily* pFallbackData = nullptr;
214 
215  //fdo#33898 If someone has EUDC installed then they really want that to
216  //be used as the first-choice glyph fallback seeing as it's filled with
217  //private area codes with don't make any sense in any other font so
218  //prioritize it here if it's available. Ideally we would remove from
219  //rMissingCodes all the glyphs which it is able to resolve as an
220  //optimization, but that's tricky to achieve cross-platform without
221  //sufficient heavy-weight code that's likely to undo the value of the
222  //optimization
223  if (nFallbackLevel == 1)
224  pFallbackData = pFontCollection->FindFontFamily("EUDC");
225  if (!pFallbackData)
226  pFallbackData = pFontCollection->GetGlyphFallbackFont(rFontSelData, pFontInstance, rMissingCodes, nFallbackLevel-1);
227  // escape when there are no font candidates
228  if( !pFallbackData )
229  return nullptr;
230  // override the font name
231  rFontSelData.SetFamilyName( pFallbackData->GetFamilyName() );
232  // clear the cached normalized name
233  rFontSelData.maSearchName.clear();
234  }
235 
236  rtl::Reference<LogicalFontInstance> pFallbackFont = GetFontInstance( pFontCollection, rFontSelData );
237  return pFallbackFont;
238 }
239 
241 {
242  // #112304# make sure the font cache is really clean
243  mpLastHitCacheEntry = nullptr;
244  for (auto const & pair : maFontInstanceList)
245  pair.second->mpFontCache = nullptr;
246  maFontInstanceList.clear();
248 }
249 
251 {
252  if (!pFont->GetFontCache())
253  return false;
254  assert(pFont->GetFontCache() == this);
255  if (pFont->GetFontCache() != this)
256  return false;
257 
258  auto it = m_aBoundRectCache.find({pFont, nID});
259  if (it != m_aBoundRectCache.end())
260  {
261  rRect = it->second;
262  return true;
263  }
264  return false;
265 }
266 
268 {
269  if (!pFont->GetFontCache())
270  return;
271  assert(pFont->GetFontCache() == this);
272  if (pFont->GetFontCache() != this)
273  return;
274 
275  m_aBoundRectCache.insert({{pFont, nID}, rRect});
276 }
277 
278 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const OUString & GetStyleName() const
const OUString & GetFamilyName() const
Definition: font/font.cxx:671
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
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
ItalicMatrix maItalicMatrix
Definition: fontselect.hxx:69
size_t size() const
FontItalic GetItalic() const
abstract base class for physical font faces
Degree10 mnOrientation
Definition: fontselect.hxx:63
FontWeight GetWeight() const
void Invalidate()
Definition: fontcache.cxx:240
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)
PhysicalFontFamily * FindFontFamily(const OUString &rFontName) const
void CacheGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:267
const FontSelectPattern & GetFontSelectPattern() const
rtl::Reference< LogicalFontInstance > GetGlyphFallbackFont(PhysicalFontCollection const *, FontSelectPattern &, LogicalFontInstance *pLogicalFont, int nFallbackLevel, OUString &rMissingCodes)
Definition: fontcache.cxx:205
bool GetCachedGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:250
#define SAL_WARN_IF(condition, area, stream)
rtl::Reference< LogicalFontInstance > GetFontInstance(PhysicalFontCollection const *, FontSelectPattern &)
Definition: fontcache.cxx:111
static const char FEAT_PREFIX
Definition: fontselect.hxx:54
const ImplFontCache * GetFontCache() const
void insert(key_value_pair_t &rPair)
oslInterlockedCount m_nCount
LanguageType meLanguage
Definition: fontselect.hxx:64
FontInstanceList maFontInstanceList
bool IsSymbolFont() const
FontPitch GetPitch() const
OUString maSearchName
Definition: fontselect.hxx:59
OUString maTargetName
Definition: fontselect.hxx:58
void remove_if(UnaryPredicate pred)
GlyphBoundRectCache m_aBoundRectCache
const OUString & GetFamilyName() const