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