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