1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
22 #include "fontinstance.hxx"
23 #include "PhysicalFontCollection.hxx"
24 #include "PhysicalFontFace.hxx"
25 #include "PhysicalFontFamily.hxx"
27 size_t ImplFontCache::IFSD_Hash::operator()( const FontSelectPattern
& rFSD
) const
29 return rFSD
.hashCode();
32 bool ImplFontCache::IFSD_Equal::operator()(const FontSelectPattern
& rA
, const FontSelectPattern
& rB
) const
34 // check normalized font family name
35 if( rA
.maSearchName
!= rB
.maSearchName
)
38 // check font transformation
39 if( (rA
.mnHeight
!= rB
.mnHeight
)
40 || (rA
.mnWidth
!= rB
.mnWidth
)
41 || (rA
.mnOrientation
!= rB
.mnOrientation
) )
44 // check mapping relevant attributes
45 if( (rA
.mbVertical
!= rB
.mbVertical
)
46 || (rA
.meLanguage
!= rB
.meLanguage
) )
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()) )
57 if( rA
.GetStyleName() != rB
.GetStyleName() )
60 // Symbol fonts may recode from one type to another So they are only
61 // safely equivalent for equal targets
63 (rA
.mpFontData
&& rA
.mpFontData
->IsSymbolFont()) ||
64 (rB
.mpFontData
&& rB
.mpFontData
->IsSymbolFont())
67 if (rA
.maTargetName
!= rB
.maTargetName
)
72 if ((rA
.maTargetName
.indexOf(FontSelectPatternAttributes::FEAT_PREFIX
)
74 rB
.maTargetName
.indexOf(FontSelectPatternAttributes::FEAT_PREFIX
)
75 != -1) && rA
.maTargetName
!= rB
.maTargetName
)
78 if (rA
.mbEmbolden
!= rB
.mbEmbolden
)
81 if (rA
.maItalicMatrix
!= rB
.maItalicMatrix
)
87 ImplFontCache::ImplFontCache()
88 : mpFirstEntry( nullptr ),
92 ImplFontCache::~ImplFontCache()
94 FontInstanceList::iterator it
= maFontInstanceList
.begin();
95 for(; it
!= maFontInstanceList
.end(); ++it
)
97 LogicalFontInstance
* pFontInstance
= (*it
).second
;
102 LogicalFontInstance
* ImplFontCache::GetFontInstance( PhysicalFontCollection
* pFontList
,
103 const vcl::Font
& rFont
, const Size
& rSize
, float fExactHeight
)
105 const OUString
& aSearchName
= rFont
.GetFamilyName();
107 // initialize internal font request object
108 FontSelectPattern
aFontSelData( rFont
, aSearchName
, rSize
, fExactHeight
);
109 return GetFontInstance( pFontList
, aFontSelData
);
112 LogicalFontInstance
* ImplFontCache::GetFontInstance( PhysicalFontCollection
* pFontList
,
113 FontSelectPattern
& aFontSelData
)
115 // check if a directly matching logical font instance is already cached,
116 // the most recently used font usually has a hit rate of >50%
117 LogicalFontInstance
*pFontInstance
= nullptr;
118 PhysicalFontFamily
* pFontFamily
= nullptr;
119 IFSD_Equal aIFSD_Equal
;
120 if( mpFirstEntry
&& aIFSD_Equal( aFontSelData
, mpFirstEntry
->maFontSelData
) )
121 pFontInstance
= mpFirstEntry
;
124 FontInstanceList::iterator it
= maFontInstanceList
.find( aFontSelData
);
125 if( it
!= maFontInstanceList
.end() )
126 pFontInstance
= (*it
).second
;
129 if( !pFontInstance
) // no direct cache hit
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!" );
135 aFontSelData
.maSearchName
= pFontFamily
->GetSearchName();
137 // check if an indirectly matching logical font instance is already cached
138 FontInstanceList::iterator it
= maFontInstanceList
.find( aFontSelData
);
139 if( it
!= maFontInstanceList
.end() )
141 // we have an indirect cache hit
142 pFontInstance
= (*it
).second
;
146 PhysicalFontFace
* pFontData
= nullptr;
148 if (!pFontInstance
&& pFontFamily
)// no cache hit => find the best matching physical font face
150 bool bOrigWasSymbol
= aFontSelData
.mpFontData
&& aFontSelData
.mpFontData
->IsSymbolFont();
151 pFontData
= pFontFamily
->FindBestFontFace( aFontSelData
);
152 aFontSelData
.mpFontData
= pFontData
;
153 bool bNewIsSymbol
= aFontSelData
.mpFontData
&& aFontSelData
.mpFontData
->IsSymbolFont();
155 if (bNewIsSymbol
!= bOrigWasSymbol
)
157 // it is possible, though generally unlikely, that at this point we
158 // will attempt to use a symbol font as a last-ditch fallback for a
159 // non-symbol font request or vice versa, and by changing
160 // aFontSelData.mpFontData to/from a symbol font we may now find
161 // something in the cache that can be reused which previously
162 // wasn't a candidate
163 FontInstanceList::iterator it
= maFontInstanceList
.find( aFontSelData
);
164 if( it
!= maFontInstanceList
.end() )
165 pFontInstance
= (*it
).second
;
169 if( pFontInstance
) // cache hit => use existing font instance
171 // increase the font instance's reference count
172 Acquire(pFontInstance
);
175 if (!pFontInstance
&& pFontData
)// still no cache hit => create a new font instance
177 // create a new logical font instance from this physical font face
178 pFontInstance
= pFontData
->CreateFontInstance( aFontSelData
);
179 pFontInstance
->mpFontCache
= this;
181 // if we're subtituting from or to a symbol font we may need a symbol
183 if( pFontData
->IsSymbolFont() || aFontSelData
.IsSymbolFont() )
185 if( aFontSelData
.maTargetName
!= aFontSelData
.maSearchName
)
186 pFontInstance
->mpConversion
= ConvertChar::GetRecodeData( aFontSelData
.maTargetName
, aFontSelData
.maSearchName
);
190 //It might be better to dig out the font version of the target font
191 //to see if it's a modern re-coded apple symbol font in case that
192 //font shows up on a different platform
193 if (!pFontInstance
->mpConversion
&&
194 aFontSelData
.maTargetName
.equalsIgnoreAsciiCase("symbol") &&
195 aFontSelData
.maSearchName
.equalsIgnoreAsciiCase("symbol"))
197 pFontInstance
->mpConversion
= ConvertChar::GetRecodeData( "Symbol", "AppleSymbol" );
201 // add the new entry to the cache
202 maFontInstanceList
[ aFontSelData
] = pFontInstance
;
205 mpFirstEntry
= pFontInstance
;
206 return pFontInstance
;
209 LogicalFontInstance
* ImplFontCache::GetGlyphFallbackFont( PhysicalFontCollection
* pFontCollection
,
210 FontSelectPattern
& rFontSelData
, int nFallbackLevel
, OUString
& rMissingCodes
)
212 // get a candidate font for glyph fallback
213 // unless the previously selected font got a device specific substitution
214 // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
215 if( nFallbackLevel
>= 1)
217 PhysicalFontFamily
* pFallbackData
= nullptr;
219 //fdo#33898 If someone has EUDC installed then they really want that to
220 //be used as the first-choice glyph fallback seeing as it's filled with
221 //private area codes with don't make any sense in any other font so
222 //prioritise it here if it's available. Ideally we would remove from
223 //rMissingCodes all the glyphs which it is able to resolve as an
224 //optimization, but that's tricky to achieve cross-platform without
225 //sufficient heavy-weight code that's likely to undo the value of the
227 if (nFallbackLevel
== 1)
228 pFallbackData
= pFontCollection
->FindFontFamily("EUDC");
230 pFallbackData
= pFontCollection
->GetGlyphFallbackFont(rFontSelData
, rMissingCodes
, nFallbackLevel
-1);
231 // escape when there are no font candidates
234 // override the font name
235 rFontSelData
.SetFamilyName( pFallbackData
->GetFamilyName() );
236 // clear the cached normalized name
237 rFontSelData
.maSearchName
.clear();
240 LogicalFontInstance
* pFallbackFont
= GetFontInstance( pFontCollection
, rFontSelData
);
241 return pFallbackFont
;
244 void ImplFontCache::Acquire(LogicalFontInstance
* pFontInstance
)
246 assert(pFontInstance
->mpFontCache
== this);
248 if (0 == pFontInstance
->mnRefCount
++)
252 void ImplFontCache::Release(LogicalFontInstance
* pFontInstance
)
254 static const int FONTCACHE_MAX
= getenv("LO_TESTNAME") ? 1 : 50;
256 assert(pFontInstance
->mnRefCount
> 0 && "ImplFontCache::Release() - font refcount underflow");
257 if( --pFontInstance
->mnRefCount
> 0 )
260 if (++mnRef0Count
< FONTCACHE_MAX
)
263 assert(CountUnreferencedEntries() == mnRef0Count
);
265 // remove unused entries from font instance cache
266 FontInstanceList::iterator it_next
= maFontInstanceList
.begin();
267 while( it_next
!= maFontInstanceList
.end() )
269 FontInstanceList::iterator it
= it_next
++;
270 LogicalFontInstance
* pFontEntry
= (*it
).second
;
271 if( pFontEntry
->mnRefCount
> 0 )
274 maFontInstanceList
.erase( it
);
277 assert(mnRef0Count
>=0 && "ImplFontCache::Release() - refcount0 underflow");
279 if( mpFirstEntry
== pFontEntry
)
280 mpFirstEntry
= nullptr;
283 assert(mnRef0Count
==0 && "ImplFontCache::Release() - refcount0 mismatch");
286 int ImplFontCache::CountUnreferencedEntries() const
289 // count unreferenced entries
290 for (FontInstanceList::const_iterator it
= maFontInstanceList
.begin();
291 it
!= maFontInstanceList
.end(); ++it
)
293 const LogicalFontInstance
* pFontEntry
= it
->second
;
294 if (pFontEntry
->mnRefCount
> 0)
301 void ImplFontCache::Invalidate()
303 assert(CountUnreferencedEntries() == mnRef0Count
);
305 // delete unreferenced entries
306 FontInstanceList::iterator it
= maFontInstanceList
.begin();
307 for(; it
!= maFontInstanceList
.end(); ++it
)
309 LogicalFontInstance
* pFontEntry
= (*it
).second
;
310 if( pFontEntry
->mnRefCount
> 0 )
317 // #112304# make sure the font cache is really clean
318 mpFirstEntry
= nullptr;
319 maFontInstanceList
.clear();
321 assert(mnRef0Count
==0 && "ImplFontCache::Invalidate() - mnRef0Count non-zero");
325 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */