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 .
20 #include <i18nlangtag/mslangid.hxx>
21 #include <unotools/configmgr.hxx>
22 #include <unotools/fontdefs.hxx>
25 #include "PhysicalFontCollection.hxx"
27 static ImplFontAttrs
lcl_IsCJKFont( const OUString
& rFontName
)
29 // Test, if Fontname includes CJK characters --> In this case we
30 // mention that it is a CJK font
31 for(int i
= 0; i
< rFontName
.getLength(); i
++)
33 const sal_Unicode ch
= rFontName
[i
];
35 if ( ((ch
>= 0x3040) && (ch
<= 0x30FF)) ||
36 ((ch
>= 0x3190) && (ch
<= 0x319F)) )
37 return ImplFontAttrs::CJK
|ImplFontAttrs::CJK_JP
;
40 if ( ((ch
>= 0xAC00) && (ch
<= 0xD7AF)) ||
41 ((ch
>= 0x3130) && (ch
<= 0x318F)) ||
42 ((ch
>= 0x1100) && (ch
<= 0x11FF)) )
43 return ImplFontAttrs::CJK
|ImplFontAttrs::CJK_KR
;
46 if ( ((ch
>= 0x3400) && (ch
<= 0x9FFF)) )
47 return ImplFontAttrs::CJK
|ImplFontAttrs::CJK_TC
|ImplFontAttrs::CJK_SC
;
50 if ( ((ch
>= 0x3000) && (ch
<= 0xD7AF)) ||
51 ((ch
>= 0xFF00) && (ch
<= 0xFFEE)) )
52 return ImplFontAttrs::CJK
;
56 return ImplFontAttrs::None
;
59 PhysicalFontCollection::PhysicalFontCollection()
60 : mbMatchData( false )
62 , mpPreMatchHook( nullptr )
63 , mpFallbackHook( nullptr )
64 , mpFallbackList( nullptr )
65 , mnFallbackCount( -1 )
68 PhysicalFontCollection::~PhysicalFontCollection()
73 void PhysicalFontCollection::SetPreMatchHook( ImplPreMatchFontSubstitution
* pHook
)
75 mpPreMatchHook
= pHook
;
78 void PhysicalFontCollection::SetFallbackHook( ImplGlyphFallbackFontSubstitution
* pHook
)
80 mpFallbackHook
= pHook
;
83 void PhysicalFontCollection::Clear()
85 // remove fallback lists
86 delete[] mpFallbackList
;
87 mpFallbackList
= nullptr;
90 // clear all entries in the device font list
91 PhysicalFontFamilies::iterator it
= maPhysicalFontFamilies
.begin();
92 for(; it
!= maPhysicalFontFamilies
.end(); ++it
)
94 PhysicalFontFamily
* pEntry
= (*it
).second
;
98 maPhysicalFontFamilies
.clear();
100 // match data must be recalculated too
104 void PhysicalFontCollection::ImplInitGenericGlyphFallback() const
106 // normalized family names of fonts suited for glyph fallback
107 // if a font is available related fonts can be ignored
108 // TODO: implement dynamic lists
109 static const char* aGlyphFallbackList
[] = {
110 // empty strings separate the names of unrelated fonts
112 "arialunicodems", "cyberbit", "code2000", "",
114 "starsymbol", "opensymbol", "",
115 "msmincho", "fzmingti", "fzheiti", "ipamincho", "sazanamimincho", "kochimincho", "",
116 "sunbatang", "sundotum", "baekmukdotum", "gulim", "batang", "dotum", "",
117 "hgmincholightj", "msunglightsc", "msunglighttc", "hymyeongjolightk", "",
118 "tahoma", "dejavusans", "timesnewroman", "liberationsans", "",
119 "shree", "mangal", "",
120 "raavi", "shruti", "tunga", "",
121 "latha", "gautami", "kartika", "vrinda", "",
122 "shayyalmt", "naskmt", "scheherazade", "",
123 "david", "nachlieli", "lucidagrande", "",
124 "norasi", "angsanaupc", "",
128 "padauk", "pinlonmyanmar", "",
129 "iskoolapota", "lklug", "",
133 bool bHasEudc
= false;
135 int nBestQuality
= 0;
136 PhysicalFontFamily
** pFallbackList
= nullptr;
138 for( const char** ppNames
= &aGlyphFallbackList
[0];; ++ppNames
)
140 // advance to next sub-list when end-of-sublist marker
141 if( !**ppNames
) // #i46456# check for empty string, i.e., deref string itself not only ptr to it
143 if( nBestQuality
> 0 )
144 if( ++nMaxLevel
>= MAX_GLYPHFALLBACK
)
154 // test if the glyph fallback candidate font is available and scalable
155 OUString
aTokenName( *ppNames
, strlen(*ppNames
), RTL_TEXTENCODING_UTF8
);
156 PhysicalFontFamily
* pFallbackFont
= FindFontFamily( aTokenName
);
161 // keep the best font of the glyph fallback sub-list
162 if( nBestQuality
< pFallbackFont
->GetMinQuality() )
164 nBestQuality
= pFallbackFont
->GetMinQuality();
165 // store available glyph fallback fonts
167 pFallbackList
= new PhysicalFontFamily
*[ MAX_GLYPHFALLBACK
];
169 pFallbackList
[ nMaxLevel
] = pFallbackFont
;
170 if( !bHasEudc
&& !nMaxLevel
)
171 bHasEudc
= !strncmp( *ppNames
, "eudc", 5 );
175 mnFallbackCount
= nMaxLevel
;
176 mpFallbackList
= pFallbackList
;
179 PhysicalFontFamily
* PhysicalFontCollection::GetGlyphFallbackFont( FontSelectPattern
& rFontSelData
,
180 OUString
& rMissingCodes
,
181 int nFallbackLevel
) const
183 PhysicalFontFamily
* pFallbackData
= nullptr;
185 // find a matching font candidate for platform specific glyph fallback
188 // check cache for the first matching entry
189 // to avoid calling the expensive fallback hook (#i83491#)
192 sal_Int32 nStrIndex
= 0;
193 while( nStrIndex
< rMissingCodes
.getLength() )
195 cChar
= rMissingCodes
.iterateCodePoints( &nStrIndex
);
196 bCached
= rFontSelData
.mpFontInstance
->GetFallbackForUnicode( cChar
, rFontSelData
.GetWeight(), &rFontSelData
.maSearchName
);
198 // ignore entries which don't have a fallback
199 if( !bCached
|| !rFontSelData
.maSearchName
.isEmpty() )
205 // there is a matching fallback in the cache
206 // so update rMissingCodes with codepoints not yet resolved by this fallback
207 int nRemainingLength
= 0;
208 std::unique_ptr
<sal_UCS4
[]> const pRemainingCodes(new sal_UCS4
[rMissingCodes
.getLength()]);
211 while( nStrIndex
< rMissingCodes
.getLength() )
213 cChar
= rMissingCodes
.iterateCodePoints( &nStrIndex
);
214 bCached
= rFontSelData
.mpFontInstance
->GetFallbackForUnicode( cChar
, rFontSelData
.GetWeight(), &aFontName
);
215 if( !bCached
|| (rFontSelData
.maSearchName
!= aFontName
) )
216 pRemainingCodes
[ nRemainingLength
++ ] = cChar
;
218 rMissingCodes
= OUString( pRemainingCodes
.get(), nRemainingLength
);
222 OUString aOldMissingCodes
= rMissingCodes
;
224 // call the hook to query the best matching glyph fallback font
225 if( mpFallbackHook
->FindFontSubstitute( rFontSelData
, rMissingCodes
) )
226 // apply outdev3.cxx specific fontname normalization
227 rFontSelData
.maSearchName
= GetEnglishSearchFontName( rFontSelData
.maSearchName
);
229 rFontSelData
.maSearchName
.clear();
231 // See fdo#32665 for an example. FreeSerif that has glyphs in normal
232 // font, but not in the italic or bold version
233 bool bSubSetOfFontRequiresPropertyFaking
= rFontSelData
.mbEmbolden
|| rFontSelData
.maItalicMatrix
!= ItalicMatrix();
235 // Cache the result even if there was no match, unless its from part of a font for which the properties need
236 // to be faked. We need to rework this cache to take into account that fontconfig can return different fonts
237 // for different input sizes, weights, etc. Basically the cache is way to naive
238 if (!bSubSetOfFontRequiresPropertyFaking
)
242 if( !rFontSelData
.mpFontInstance
->GetFallbackForUnicode( cChar
, rFontSelData
.GetWeight(), &rFontSelData
.maSearchName
) )
243 rFontSelData
.mpFontInstance
->AddFallbackForUnicode( cChar
, rFontSelData
.GetWeight(), rFontSelData
.maSearchName
);
244 if( nStrIndex
>= aOldMissingCodes
.getLength() )
246 cChar
= aOldMissingCodes
.iterateCodePoints( &nStrIndex
);
248 if( !rFontSelData
.maSearchName
.isEmpty() )
250 // remove cache entries that were still not resolved
251 for( nStrIndex
= 0; nStrIndex
< rMissingCodes
.getLength(); )
253 cChar
= rMissingCodes
.iterateCodePoints( &nStrIndex
);
254 rFontSelData
.mpFontInstance
->IgnoreFallbackForUnicode( cChar
, rFontSelData
.GetWeight(), rFontSelData
.maSearchName
);
260 // find the matching device font
261 if( !rFontSelData
.maSearchName
.isEmpty() )
262 pFallbackData
= FindFontFamily( rFontSelData
.maSearchName
);
265 // else find a matching font candidate for generic glyph fallback
268 // initialize font candidates for generic glyph fallback if needed
269 if( mnFallbackCount
< 0 )
270 ImplInitGenericGlyphFallback();
272 // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook
273 if( nFallbackLevel
< mnFallbackCount
)
274 pFallbackData
= mpFallbackList
[ nFallbackLevel
];
277 return pFallbackData
;
280 void PhysicalFontCollection::Add( PhysicalFontFace
* pNewData
)
282 OUString aSearchName
= GetEnglishSearchFontName( pNewData
->GetFamilyName() );
284 PhysicalFontFamily
* pFoundData
= FindOrCreateFontFamily( aSearchName
);
286 bool bKeepNewData
= pFoundData
->AddFontFace( pNewData
);
292 // find the font from the normalized font family name
293 PhysicalFontFamily
* PhysicalFontCollection::ImplFindFontFamilyBySearchName( const OUString
& rSearchName
) const
295 // must be called with a normalized name.
296 assert( GetEnglishSearchFontName( rSearchName
) == rSearchName
);
298 PhysicalFontFamilies::const_iterator it
= maPhysicalFontFamilies
.find( rSearchName
);
299 if( it
== maPhysicalFontFamilies
.end() )
302 PhysicalFontFamily
* pFoundData
= (*it
).second
;
306 PhysicalFontFamily
* PhysicalFontCollection::ImplFindFontFamilyByAliasName(const OUString
& rSearchName
,
307 const OUString
& rShortName
) const
309 // short circuit for impossible font name alias
310 if (rSearchName
.isEmpty())
313 // short circuit if no alias names are available
317 // use the font's alias names to find the font
318 // TODO: get rid of linear search
319 PhysicalFontFamilies::const_iterator it
= maPhysicalFontFamilies
.begin();
320 while( it
!= maPhysicalFontFamilies
.end() )
322 PhysicalFontFamily
* pData
= (*it
).second
;
323 if( pData
->GetAliasNames().isEmpty() )
326 // if one alias name matches we found a matching font
328 sal_Int32 nIndex
= 0;
332 aTempName
= GetNextFontToken( pData
->GetAliasNames(), nIndex
);
333 // Test, if the Font name match with one of the mapping names
334 if ( (aTempName
== rSearchName
) || (aTempName
== rShortName
) )
337 while ( nIndex
!= -1 );
343 PhysicalFontFamily
* PhysicalFontCollection::FindFontFamily( const OUString
& rFontName
) const
345 return ImplFindFontFamilyBySearchName( GetEnglishSearchFontName( rFontName
) );
348 PhysicalFontFamily
*PhysicalFontCollection::FindOrCreateFontFamily( const OUString
&rFamilyName
)
350 PhysicalFontFamilies::const_iterator it
= maPhysicalFontFamilies
.find( rFamilyName
);
351 PhysicalFontFamily
* pFoundData
= nullptr;
353 if( it
!= maPhysicalFontFamilies
.end() )
354 pFoundData
= (*it
).second
;
358 pFoundData
= new PhysicalFontFamily( rFamilyName
);
359 maPhysicalFontFamilies
[ rFamilyName
] = pFoundData
;
365 PhysicalFontFamily
* PhysicalFontCollection::FindFontFamilyByTokenNames(const OUString
& rTokenStr
) const
367 PhysicalFontFamily
* pFoundData
= nullptr;
369 // use normalized font name tokens to find the font
370 for( sal_Int32 nTokenPos
= 0; nTokenPos
!= -1; )
372 OUString aFamilyName
= GetNextFontToken( rTokenStr
, nTokenPos
);
373 if( aFamilyName
.isEmpty() )
376 pFoundData
= FindFontFamily( aFamilyName
);
385 PhysicalFontFamily
* PhysicalFontCollection::ImplFindFontFamilyBySubstFontAttr( const utl::FontNameAttr
& rFontAttr
) const
387 PhysicalFontFamily
* pFoundData
= nullptr;
389 // use the font substitutions suggested by the FontNameAttr to find the font
390 ::std::vector
< OUString
>::const_iterator it
= rFontAttr
.Substitutions
.begin();
391 for(; it
!= rFontAttr
.Substitutions
.end(); ++it
)
393 pFoundData
= FindFontFamily( *it
);
398 // use known attributes from the configuration to find a matching substitute
399 const ImplFontAttrs nSearchType
= rFontAttr
.Type
;
400 if( nSearchType
!= ImplFontAttrs::None
)
402 const FontWeight eSearchWeight
= rFontAttr
.Weight
;
403 const FontWidth eSearchWidth
= rFontAttr
.Width
;
404 const FontItalic eSearchSlant
= ITALIC_DONTKNOW
;
405 const OUString aSearchName
;
407 pFoundData
= FindFontFamilyByAttributes( nSearchType
,
408 eSearchWeight
, eSearchWidth
, eSearchSlant
, aSearchName
);
417 void PhysicalFontCollection::ImplInitMatchData() const
419 // short circuit if already done
424 if (utl::ConfigManager::IsAvoidConfig())
427 // calculate MatchData for all entries
428 const utl::FontSubstConfiguration
& rFontSubst
= utl::FontSubstConfiguration::get();
430 PhysicalFontFamilies::const_iterator it
= maPhysicalFontFamilies
.begin();
431 for(; it
!= maPhysicalFontFamilies
.end(); ++it
)
433 const OUString
& rSearchName
= (*it
).first
;
434 PhysicalFontFamily
* pEntry
= (*it
).second
;
436 pEntry
->InitMatchData( rFontSubst
, rSearchName
);
440 PhysicalFontFamily
* PhysicalFontCollection::FindFontFamilyByAttributes( ImplFontAttrs nSearchType
,
441 FontWeight eSearchWeight
,
442 FontWidth eSearchWidth
,
443 FontItalic eSearchItalic
,
444 const OUString
& rSearchFamilyName
) const
446 if( (eSearchItalic
!= ITALIC_NONE
) && (eSearchItalic
!= ITALIC_DONTKNOW
) )
447 nSearchType
|= ImplFontAttrs::Italic
;
449 // don't bother to match attributes if the attributes aren't worth matching
450 if( nSearchType
== ImplFontAttrs::None
451 && ((eSearchWeight
== WEIGHT_DONTKNOW
) || (eSearchWeight
== WEIGHT_NORMAL
))
452 && ((eSearchWidth
== WIDTH_DONTKNOW
) || (eSearchWidth
== WIDTH_NORMAL
)) )
456 PhysicalFontFamily
* pFoundData
= nullptr;
458 long nBestMatch
= 40000;
459 ImplFontAttrs nBestType
= ImplFontAttrs::None
;
461 PhysicalFontFamilies::const_iterator it
= maPhysicalFontFamilies
.begin();
462 for(; it
!= maPhysicalFontFamilies
.end(); ++it
)
464 PhysicalFontFamily
* pData
= (*it
).second
;
466 // Get all information about the matching font
467 ImplFontAttrs nMatchType
= pData
->GetMatchType();
468 FontWeight eMatchWeight
= pData
->GetMatchWeight();
469 FontWidth eMatchWidth
= pData
->GetMatchWidth();
471 // Calculate Match Value
474 // 10000000 CJK, CTL, None-Latin, Symbol
475 // 1000000 FamilyName, Script, Fixed, -Special, -Decorative,
476 // Titling, Capitals, Outline, Shadow
477 // 100000 Match FamilyName, Serif, SansSerif, Italic,
479 // 10000 Scalable, Standard, Default,
480 // full, Normal, Knownfont,
481 // Otherstyle, +Special, +Decorative,
482 // 1000 Typewriter, Rounded, Gothic, Schollbook
486 // test CJK script attributes
487 if ( nSearchType
& ImplFontAttrs::CJK
)
490 if( ImplFontAttrs::None
== ((nSearchType
^ nMatchType
) & ImplFontAttrs::CJK_AllLang
) )
491 nTestMatch
+= 10000000*3;
492 if( nMatchType
& ImplFontAttrs::CJK
)
493 nTestMatch
+= 10000000*2;
494 if( nMatchType
& ImplFontAttrs::Full
)
495 nTestMatch
+= 10000000;
497 else if ( nMatchType
& ImplFontAttrs::CJK
)
499 nTestMatch
-= 10000000;
502 // test CTL script attributes
503 if( nSearchType
& ImplFontAttrs::CTL
)
505 if( nMatchType
& ImplFontAttrs::CTL
)
506 nTestMatch
+= 10000000*2;
507 if( nMatchType
& ImplFontAttrs::Full
)
508 nTestMatch
+= 10000000;
510 else if ( nMatchType
& ImplFontAttrs::CTL
)
512 nTestMatch
-= 10000000;
515 // test LATIN script attributes
516 if( nSearchType
& ImplFontAttrs::NoneLatin
)
518 if( nMatchType
& ImplFontAttrs::NoneLatin
)
519 nTestMatch
+= 10000000*2;
520 if( nMatchType
& ImplFontAttrs::Full
)
521 nTestMatch
+= 10000000;
524 // test SYMBOL attributes
525 if ( nSearchType
& ImplFontAttrs::Symbol
)
527 const OUString
& rSearchName
= it
->first
;
528 // prefer some special known symbol fonts
529 if ( rSearchName
== "starsymbol" )
531 nTestMatch
+= 10000000*6+(10000*3);
533 else if ( rSearchName
== "opensymbol" )
535 nTestMatch
+= 10000000*6;
537 else if ( rSearchName
== "starbats" ||
538 rSearchName
== "wingdings" ||
539 rSearchName
== "monotypesorts" ||
540 rSearchName
== "dingbats" ||
541 rSearchName
== "zapfdingbats" )
543 nTestMatch
+= 10000000*5;
545 else if ( pData
->GetTypeFaces() & FontTypeFaces::Symbol
)
547 nTestMatch
+= 10000000*4;
551 if( nMatchType
& ImplFontAttrs::Symbol
)
552 nTestMatch
+= 10000000*2;
553 if( nMatchType
& ImplFontAttrs::Full
)
554 nTestMatch
+= 10000000;
557 else if ( (pData
->GetTypeFaces() & (FontTypeFaces::Symbol
| FontTypeFaces::NoneSymbol
)) == FontTypeFaces::Symbol
)
559 nTestMatch
-= 10000000;
561 else if ( nMatchType
& ImplFontAttrs::Symbol
)
566 // match stripped family name
567 if( !rSearchFamilyName
.isEmpty() && (rSearchFamilyName
== pData
->GetMatchFamilyName()) )
569 nTestMatch
+= 1000000*3;
572 // match ALLSCRIPT? attribute
573 if( nSearchType
& ImplFontAttrs::AllScript
)
575 if( nMatchType
& ImplFontAttrs::AllScript
)
577 nTestMatch
+= 1000000*2;
579 if( nSearchType
& ImplFontAttrs::AllSubscript
)
581 if( ImplFontAttrs::None
== ((nSearchType
^ nMatchType
) & ImplFontAttrs::AllSubscript
) )
582 nTestMatch
+= 1000000*2;
583 if( ImplFontAttrs::None
!= ((nSearchType
^ nMatchType
) & ImplFontAttrs::BrushScript
) )
584 nTestMatch
-= 1000000;
587 else if( nMatchType
& ImplFontAttrs::AllScript
)
589 nTestMatch
-= 1000000;
592 // test MONOSPACE+TYPEWRITER attributes
593 if( nSearchType
& ImplFontAttrs::Fixed
)
595 if( nMatchType
& ImplFontAttrs::Fixed
)
596 nTestMatch
+= 1000000*2;
597 // a typewriter attribute is even better
598 if( ImplFontAttrs::None
== ((nSearchType
^ nMatchType
) & ImplFontAttrs::Typewriter
) )
599 nTestMatch
+= 10000*2;
601 else if( nMatchType
& ImplFontAttrs::Fixed
)
603 nTestMatch
-= 1000000;
606 // test SPECIAL attribute
607 if( nSearchType
& ImplFontAttrs::Special
)
609 if( nMatchType
& ImplFontAttrs::Special
)
613 else if( !(nSearchType
& ImplFontAttrs::AllSerifStyle
) )
615 if( nMatchType
& ImplFontAttrs::Serif
)
617 nTestMatch
+= 1000*2;
619 else if( nMatchType
& ImplFontAttrs::SansSerif
)
625 else if( (nMatchType
& ImplFontAttrs::Special
) && !(nSearchType
& ImplFontAttrs::Symbol
) )
627 nTestMatch
-= 1000000;
630 // test DECORATIVE attribute
631 if( nSearchType
& ImplFontAttrs::Decorative
)
633 if( nMatchType
& ImplFontAttrs::Decorative
)
637 else if( !(nSearchType
& ImplFontAttrs::AllSerifStyle
) )
639 if( nMatchType
& ImplFontAttrs::Serif
)
640 nTestMatch
+= 1000*2;
641 else if ( nMatchType
& ImplFontAttrs::SansSerif
)
645 else if( nMatchType
& ImplFontAttrs::Decorative
)
647 nTestMatch
-= 1000000;
650 // test TITLE+CAPITALS attributes
651 if( nSearchType
& (ImplFontAttrs::Titling
| ImplFontAttrs::Capitals
) )
653 if( nMatchType
& (ImplFontAttrs::Titling
| ImplFontAttrs::Capitals
) )
655 nTestMatch
+= 1000000*2;
657 if( ImplFontAttrs::None
== ((nSearchType
^nMatchType
) & ImplFontAttrs(ImplFontAttrs::Titling
| ImplFontAttrs::Capitals
)))
659 nTestMatch
+= 1000000;
661 else if( (nMatchType
& (ImplFontAttrs::Titling
| ImplFontAttrs::Capitals
)) &&
662 (nMatchType
& (ImplFontAttrs::Standard
| ImplFontAttrs::Default
)) )
664 nTestMatch
+= 1000000;
667 else if( nMatchType
& (ImplFontAttrs::Titling
| ImplFontAttrs::Capitals
) )
669 nTestMatch
-= 1000000;
672 // test OUTLINE+SHADOW attributes
673 if( nSearchType
& (ImplFontAttrs::Outline
| ImplFontAttrs::Shadow
) )
675 if( nMatchType
& (ImplFontAttrs::Outline
| ImplFontAttrs::Shadow
) )
677 nTestMatch
+= 1000000*2;
679 if( ImplFontAttrs::None
== ((nSearchType
^ nMatchType
) & ImplFontAttrs(ImplFontAttrs::Outline
| ImplFontAttrs::Shadow
)) )
681 nTestMatch
+= 1000000;
683 else if( (nMatchType
& (ImplFontAttrs::Outline
| ImplFontAttrs::Shadow
)) &&
684 (nMatchType
& (ImplFontAttrs::Standard
| ImplFontAttrs::Default
)) )
686 nTestMatch
+= 1000000;
689 else if ( nMatchType
& (ImplFontAttrs::Outline
| ImplFontAttrs::Shadow
) )
691 nTestMatch
-= 1000000;
694 // test font name substrings
695 // TODO: calculate name matching score using e.g. Levenstein distance
696 if( (rSearchFamilyName
.getLength() >= 4) &&
697 (pData
->GetMatchFamilyName().getLength() >= 4) &&
698 ((rSearchFamilyName
.indexOf( pData
->GetMatchFamilyName() ) != -1) ||
699 (pData
->GetMatchFamilyName().indexOf( rSearchFamilyName
) != -1)) )
703 // test SERIF attribute
704 if( nSearchType
& ImplFontAttrs::Serif
)
706 if( nMatchType
& ImplFontAttrs::Serif
)
707 nTestMatch
+= 1000000*2;
708 else if( nMatchType
& ImplFontAttrs::SansSerif
)
709 nTestMatch
-= 1000000;
712 // test SANSERIF attribute
713 if( nSearchType
& ImplFontAttrs::SansSerif
)
715 if( nMatchType
& ImplFontAttrs::SansSerif
)
716 nTestMatch
+= 1000000;
717 else if ( nMatchType
& ImplFontAttrs::Serif
)
718 nTestMatch
-= 1000000;
721 // test ITALIC attribute
722 if( nSearchType
& ImplFontAttrs::Italic
)
724 if( pData
->GetTypeFaces() & FontTypeFaces::Italic
)
725 nTestMatch
+= 1000000*3;
726 if( nMatchType
& ImplFontAttrs::Italic
)
727 nTestMatch
+= 1000000;
729 else if( !(nSearchType
& ImplFontAttrs::AllScript
) &&
730 ((nMatchType
& ImplFontAttrs::Italic
) ||
731 !(pData
->GetTypeFaces() & FontTypeFaces::NoneItalic
)) )
733 nTestMatch
-= 1000000*2;
736 // test WIDTH attribute
737 if( (eSearchWidth
!= WIDTH_DONTKNOW
) && (eSearchWidth
!= WIDTH_NORMAL
) )
739 if( eSearchWidth
< WIDTH_NORMAL
)
741 if( eSearchWidth
== eMatchWidth
)
742 nTestMatch
+= 1000000*3;
743 else if( (eMatchWidth
< WIDTH_NORMAL
) && (eMatchWidth
!= WIDTH_DONTKNOW
) )
744 nTestMatch
+= 1000000;
748 if( eSearchWidth
== eMatchWidth
)
749 nTestMatch
+= 1000000*3;
750 else if( eMatchWidth
> WIDTH_NORMAL
)
751 nTestMatch
+= 1000000;
754 else if( (eMatchWidth
!= WIDTH_DONTKNOW
) && (eMatchWidth
!= WIDTH_NORMAL
) )
756 nTestMatch
-= 1000000;
759 // test WEIGHT attribute
760 if( (eSearchWeight
!= WEIGHT_DONTKNOW
) &&
761 (eSearchWeight
!= WEIGHT_NORMAL
) &&
762 (eSearchWeight
!= WEIGHT_MEDIUM
) )
764 if( eSearchWeight
< WEIGHT_NORMAL
)
766 if( pData
->GetTypeFaces() & FontTypeFaces::Light
)
767 nTestMatch
+= 1000000;
768 if( (eMatchWeight
< WEIGHT_NORMAL
) && (eMatchWeight
!= WEIGHT_DONTKNOW
) )
769 nTestMatch
+= 1000000;
773 if( pData
->GetTypeFaces() & FontTypeFaces::Bold
)
774 nTestMatch
+= 1000000;
775 if( eMatchWeight
> WEIGHT_BOLD
)
776 nTestMatch
+= 1000000;
779 else if( ((eMatchWeight
!= WEIGHT_DONTKNOW
) &&
780 (eMatchWeight
!= WEIGHT_NORMAL
) &&
781 (eMatchWeight
!= WEIGHT_MEDIUM
)) ||
782 !(pData
->GetTypeFaces() & FontTypeFaces::Normal
) )
784 nTestMatch
-= 1000000;
787 // prefer scalable fonts
788 if( pData
->GetTypeFaces() & FontTypeFaces::Scalable
)
789 nTestMatch
+= 10000*4;
791 nTestMatch
-= 10000*4;
793 // test STANDARD+DEFAULT+FULL+NORMAL attributes
794 if( nMatchType
& ImplFontAttrs::Standard
)
795 nTestMatch
+= 10000*2;
796 if( nMatchType
& ImplFontAttrs::Default
)
798 if( nMatchType
& ImplFontAttrs::Full
)
800 if( nMatchType
& ImplFontAttrs::Normal
)
803 // test OTHERSTYLE attribute
804 if( ((nSearchType
^ nMatchType
) & ImplFontAttrs::OtherStyle
) != ImplFontAttrs::None
)
809 // test ROUNDED attribute
810 if( ImplFontAttrs::None
== ((nSearchType
^ nMatchType
) & ImplFontAttrs::Rounded
) )
813 // test TYPEWRITER attribute
814 if( ImplFontAttrs::None
== ((nSearchType
^ nMatchType
) & ImplFontAttrs::Typewriter
) )
817 // test GOTHIC attribute
818 if( nSearchType
& ImplFontAttrs::Gothic
)
820 if( nMatchType
& ImplFontAttrs::Gothic
)
821 nTestMatch
+= 1000*3;
822 if( nMatchType
& ImplFontAttrs::SansSerif
)
823 nTestMatch
+= 1000*2;
826 // test SCHOOLBOOK attribute
827 if( nSearchType
& ImplFontAttrs::Schoolbook
)
829 if( nMatchType
& ImplFontAttrs::Schoolbook
)
830 nTestMatch
+= 1000*3;
831 if( nMatchType
& ImplFontAttrs::Serif
)
832 nTestMatch
+= 1000*2;
835 // compare with best matching font yet
836 if ( nTestMatch
> nBestMatch
)
839 nBestMatch
= nTestMatch
;
840 nBestType
= nMatchType
;
842 else if( nTestMatch
== nBestMatch
)
844 // some fonts are more suitable defaults
845 if( nMatchType
& ImplFontAttrs::Default
)
848 nBestType
= nMatchType
;
850 else if( (nMatchType
& ImplFontAttrs::Standard
) &&
851 !(nBestType
& ImplFontAttrs::Default
) )
854 nBestType
= nMatchType
;
862 PhysicalFontFamily
* PhysicalFontCollection::ImplFindFontFamilyOfDefaultFont() const
864 // try to find one of the default fonts of the
865 // UNICODE, SANSSERIF, SERIF or FIXED default font lists
866 PhysicalFontFamily
* pFoundData
= nullptr;
867 if (!utl::ConfigManager::IsAvoidConfig())
869 const utl::DefaultFontConfiguration
& rDefaults
= utl::DefaultFontConfiguration::get();
870 LanguageTag
aLanguageTag( OUString( "en"));
871 OUString aFontname
= rDefaults
.getDefaultFont( aLanguageTag
, DefaultFontType::SANS_UNICODE
);
872 pFoundData
= FindFontFamilyByTokenNames( aFontname
);
877 aFontname
= rDefaults
.getDefaultFont( aLanguageTag
, DefaultFontType::SANS
);
878 pFoundData
= FindFontFamilyByTokenNames( aFontname
);
882 aFontname
= rDefaults
.getDefaultFont( aLanguageTag
, DefaultFontType::SERIF
);
883 pFoundData
= FindFontFamilyByTokenNames( aFontname
);
887 aFontname
= rDefaults
.getDefaultFont( aLanguageTag
, DefaultFontType::FIXED
);
888 pFoundData
= FindFontFamilyByTokenNames( aFontname
);
893 // now try to find a reasonable non-symbol font
897 PhysicalFontFamilies::const_iterator it
= maPhysicalFontFamilies
.begin();
898 for(; it
!= maPhysicalFontFamilies
.end(); ++it
)
900 PhysicalFontFamily
* pData
= (*it
).second
;
901 if( pData
->GetMatchType() & ImplFontAttrs::Symbol
)
905 if( pData
->GetMatchType() & (ImplFontAttrs::Default
|ImplFontAttrs::Standard
) )
911 // finding any font is better than finding no font at all
912 it
= maPhysicalFontFamilies
.begin();
913 if( it
!= maPhysicalFontFamilies
.end() )
914 pFoundData
= (*it
).second
;
919 PhysicalFontCollection
* PhysicalFontCollection::Clone() const
921 PhysicalFontCollection
* pClonedCollection
= new PhysicalFontCollection
;
922 pClonedCollection
->mbMapNames
= mbMapNames
;
923 pClonedCollection
->mpPreMatchHook
= mpPreMatchHook
;
924 pClonedCollection
->mpFallbackHook
= mpFallbackHook
;
926 // TODO: clone the config-font attributes too?
927 pClonedCollection
->mbMatchData
= false;
929 PhysicalFontFamilies::const_iterator it
= maPhysicalFontFamilies
.begin();
930 for(; it
!= maPhysicalFontFamilies
.end(); ++it
)
932 const PhysicalFontFamily
* pFontFace
= (*it
).second
;
933 pFontFace
->UpdateCloneFontList(*pClonedCollection
);
936 return pClonedCollection
;
939 ImplDeviceFontList
* PhysicalFontCollection::GetDeviceFontList() const
941 ImplDeviceFontList
* pDeviceFontList
= new ImplDeviceFontList
;
943 PhysicalFontFamilies::const_iterator it
= maPhysicalFontFamilies
.begin();
944 for(; it
!= maPhysicalFontFamilies
.end(); ++it
)
946 const PhysicalFontFamily
* pFontFamily
= (*it
).second
;
947 pFontFamily
->UpdateDevFontList( *pDeviceFontList
);
950 return pDeviceFontList
;
953 ImplDeviceFontSizeList
* PhysicalFontCollection::GetDeviceFontSizeList( const OUString
& rFontName
) const
955 ImplDeviceFontSizeList
* pDeviceFontSizeList
= new ImplDeviceFontSizeList
;
957 PhysicalFontFamily
* pFontFamily
= FindFontFamily( rFontName
);
958 if( pFontFamily
!= nullptr )
960 std::set
<int> rHeights
;
961 pFontFamily
->GetFontHeights( rHeights
);
963 std::set
<int>::const_iterator it
= rHeights
.begin();
964 for(; it
!= rHeights
.begin(); ++it
)
965 pDeviceFontSizeList
->Add( *it
);
968 return pDeviceFontSizeList
;
971 PhysicalFontFamily
* PhysicalFontCollection::FindFontFamily( FontSelectPattern
& rFSD
) const
973 // give up if no fonts are available
977 bool bMultiToken
= false;
978 sal_Int32 nTokenPos
= 0;
979 OUString
& aSearchName
= rFSD
.maSearchName
; // TODO: get rid of reference
982 rFSD
.maTargetName
= GetNextFontToken( rFSD
.GetFamilyName(), nTokenPos
);
983 aSearchName
= rFSD
.maTargetName
;
985 // Until features are properly supported, they are appended to the
986 // font name, so we need to strip them off so the font is found.
987 sal_Int32 nFeat
= aSearchName
.indexOf(FontSelectPatternAttributes::FEAT_PREFIX
);
988 OUString aOrigName
= rFSD
.maTargetName
;
989 OUString aBaseFontName
= aSearchName
.copy( 0, (nFeat
!= -1) ? nFeat
: aSearchName
.getLength() );
993 aSearchName
= aBaseFontName
;
994 rFSD
.maTargetName
= aBaseFontName
;
997 aSearchName
= GetEnglishSearchFontName( aSearchName
);
998 ImplFontSubstitute( aSearchName
);
999 // #114999# special emboldening for Ricoh fonts
1000 // TODO: smarter check for special cases by using PreMatch infrastructure?
1001 if( (rFSD
.GetWeight() > WEIGHT_MEDIUM
) &&
1002 aSearchName
.startsWithIgnoreAsciiCase( "hg" ) )
1005 if( aSearchName
.startsWithIgnoreAsciiCase( "hggothicb" ) )
1006 aBoldName
= "hggothice";
1007 else if( aSearchName
.startsWithIgnoreAsciiCase( "hgpgothicb" ) )
1008 aBoldName
= "hgpgothice";
1009 else if( aSearchName
.startsWithIgnoreAsciiCase( "hgminchol" ) )
1010 aBoldName
= "hgminchob";
1011 else if( aSearchName
.startsWithIgnoreAsciiCase( "hgpminchol" ) )
1012 aBoldName
= "hgpminchob";
1013 else if( aSearchName
.equalsIgnoreAsciiCase( "hgminchob" ) )
1014 aBoldName
= "hgminchoe";
1015 else if( aSearchName
.equalsIgnoreAsciiCase( "hgpminchob" ) )
1016 aBoldName
= "hgpminchoe";
1018 if( !aBoldName
.isEmpty() && ImplFindFontFamilyBySearchName( aBoldName
) )
1020 // the other font is available => use it
1021 aSearchName
= aBoldName
;
1022 // prevent synthetic emboldening of bold version
1023 rFSD
.SetWeight(WEIGHT_DONTKNOW
);
1027 // restore the features to make the font selection data unique
1028 rFSD
.maTargetName
= aOrigName
;
1030 // check if the current font name token or its substitute is valid
1031 PhysicalFontFamily
* pFoundData
= ImplFindFontFamilyBySearchName( aSearchName
);
1035 // some systems provide special customization
1036 // e.g. they suggest "serif" as UI-font, but this name cannot be used directly
1037 // because the system wants to map it to another font first, e.g. "Helvetica"
1039 // use the target name to search in the prematch hook
1040 rFSD
.maTargetName
= aBaseFontName
;
1042 // Related: fdo#49271 RTF files often contain weird-ass
1043 // Win 3.1/Win95 style fontnames which attempt to put the
1044 // charset encoding into the filename
1045 // http://www.webcenter.ru/~kazarn/eng/fonts_ttf.htm
1046 OUString sStrippedName
= StripScriptFromName(rFSD
.maTargetName
);
1047 if (sStrippedName
!= rFSD
.maTargetName
)
1049 rFSD
.maTargetName
= sStrippedName
;
1050 aSearchName
= GetEnglishSearchFontName(rFSD
.maTargetName
);
1051 pFoundData
= ImplFindFontFamilyBySearchName(aSearchName
);
1056 if( mpPreMatchHook
)
1058 if( mpPreMatchHook
->FindFontSubstitute( rFSD
) )
1059 aSearchName
= GetEnglishSearchFontName( aSearchName
);
1062 // the prematch hook uses the target name to search, but we now need
1063 // to restore the features to make the font selection data unique
1064 rFSD
.maTargetName
= aOrigName
;
1066 pFoundData
= ImplFindFontFamilyBySearchName( aSearchName
);
1070 // break after last font name token was checked unsuccessfully
1071 if( nTokenPos
== -1)
1076 // if the first font was not available find the next available font in
1077 // the semicolon separated list of font names. A font is also considered
1078 // available when there is a matching entry in the Tools->Options->Fonts
1079 // dialog with neither ALWAYS nor SCREENONLY flags set and the substitution
1080 // font is available
1081 for( nTokenPos
= 0; nTokenPos
!= -1; )
1085 rFSD
.maTargetName
= GetNextFontToken( rFSD
.GetFamilyName(), nTokenPos
);
1086 aSearchName
= GetEnglishSearchFontName( rFSD
.maTargetName
);
1090 if( mpPreMatchHook
)
1091 if( mpPreMatchHook
->FindFontSubstitute( rFSD
) )
1092 aSearchName
= GetEnglishSearchFontName( aSearchName
);
1093 ImplFontSubstitute( aSearchName
);
1094 PhysicalFontFamily
* pFoundData
= ImplFindFontFamilyBySearchName( aSearchName
);
1099 // if no font with a directly matching name is available use the
1100 // first font name token and get its attributes to find a replacement
1104 rFSD
.maTargetName
= GetNextFontToken( rFSD
.GetFamilyName(), nTokenPos
);
1105 aSearchName
= GetEnglishSearchFontName( rFSD
.maTargetName
);
1108 OUString aSearchShortName
;
1109 OUString aSearchFamilyName
;
1110 FontWeight eSearchWeight
= rFSD
.GetWeight();
1111 FontWidth eSearchWidth
= rFSD
.GetWidthType();
1112 ImplFontAttrs nSearchType
= ImplFontAttrs::None
;
1113 utl::FontSubstConfiguration::getMapName( aSearchName
, aSearchShortName
, aSearchFamilyName
,
1114 eSearchWeight
, eSearchWidth
, nSearchType
);
1116 // note: the search name was already translated to english (if possible)
1117 // use the font's shortened name if needed
1118 if ( aSearchShortName
!= aSearchName
)
1120 PhysicalFontFamily
* pFoundData
= ImplFindFontFamilyBySearchName( aSearchShortName
);
1124 /* #96738# don't use mincho as an replacement for "MS Mincho" on X11: Mincho is
1125 a korean bitmap font that is not suitable here. Use the font replacement table,
1126 that automatically leads to the desired "HG Mincho Light J". Same story for
1127 MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */
1128 if ((aSearchName
!= "msmincho") && (aSearchName
!= "msgothic"))
1129 // TODO: add heuristic to only throw out the fake ms* fonts
1137 // use font fallback
1138 const utl::FontNameAttr
* pFontAttr
= nullptr;
1139 if (!aSearchName
.isEmpty() && !utl::ConfigManager::IsAvoidConfig())
1141 // get fallback info using FontSubstConfiguration and
1142 // the target name, it's shortened name and family name in that order
1143 const utl::FontSubstConfiguration
& rFontSubst
= utl::FontSubstConfiguration::get();
1144 pFontAttr
= rFontSubst
.getSubstInfo( aSearchName
);
1145 if ( !pFontAttr
&& (aSearchShortName
!= aSearchName
) )
1146 pFontAttr
= rFontSubst
.getSubstInfo( aSearchShortName
);
1147 if ( !pFontAttr
&& (aSearchFamilyName
!= aSearchShortName
) )
1148 pFontAttr
= rFontSubst
.getSubstInfo( aSearchFamilyName
);
1150 // try the font substitutions suggested by the fallback info
1153 PhysicalFontFamily
* pFoundData
= ImplFindFontFamilyBySubstFontAttr( *pFontAttr
);
1159 // if a target symbol font is not available use a default symbol font
1160 if( rFSD
.IsSymbolFont() )
1162 LanguageTag
aDefaultLanguageTag( OUString( "en"));
1163 if (utl::ConfigManager::IsAvoidConfig())
1164 aSearchName
= "OpenSymbol";
1166 aSearchName
= utl::DefaultFontConfiguration::get().getDefaultFont( aDefaultLanguageTag
, DefaultFontType::SYMBOL
);
1167 PhysicalFontFamily
* pFoundData
= FindFontFamilyByTokenNames( aSearchName
);
1172 // now try the other font name tokens
1173 while( nTokenPos
!= -1 )
1175 rFSD
.maTargetName
= GetNextFontToken( rFSD
.GetFamilyName(), nTokenPos
);
1176 if( rFSD
.maTargetName
.isEmpty() )
1179 aSearchName
= GetEnglishSearchFontName( rFSD
.maTargetName
);
1181 OUString aTempShortName
;
1182 OUString aTempFamilyName
;
1183 ImplFontAttrs nTempType
= ImplFontAttrs::None
;
1184 FontWeight eTempWeight
= rFSD
.GetWeight();
1185 FontWidth eTempWidth
= WIDTH_DONTKNOW
;
1186 utl::FontSubstConfiguration::getMapName( aSearchName
, aTempShortName
, aTempFamilyName
,
1187 eTempWeight
, eTempWidth
, nTempType
);
1189 // use a shortend token name if available
1190 if( aTempShortName
!= aSearchName
)
1192 PhysicalFontFamily
* pFoundData
= ImplFindFontFamilyBySearchName( aTempShortName
);
1197 const utl::FontNameAttr
* pTempFontAttr
= nullptr;
1198 if (!utl::ConfigManager::IsAvoidConfig())
1200 // use a font name from font fallback list to determine font attributes
1201 // get fallback info using FontSubstConfiguration and
1202 // the target name, it's shortened name and family name in that order
1203 const utl::FontSubstConfiguration
& rFontSubst
= utl::FontSubstConfiguration::get();
1204 pTempFontAttr
= rFontSubst
.getSubstInfo( aSearchName
);
1206 if ( !pTempFontAttr
&& (aTempShortName
!= aSearchName
) )
1207 pTempFontAttr
= rFontSubst
.getSubstInfo( aTempShortName
);
1209 if ( !pTempFontAttr
&& (aTempFamilyName
!= aTempShortName
) )
1210 pTempFontAttr
= rFontSubst
.getSubstInfo( aTempFamilyName
);
1213 // try the font substitutions suggested by the fallback info
1216 PhysicalFontFamily
* pFoundData
= ImplFindFontFamilyBySubstFontAttr( *pTempFontAttr
);
1220 pFontAttr
= pTempFontAttr
;
1224 // if still needed use the alias names of the installed fonts
1227 PhysicalFontFamily
* pFoundData
= ImplFindFontFamilyByAliasName( rFSD
.maTargetName
, aSearchShortName
);
1232 // if still needed use the font request's attributes to find a good match
1233 if (MsLangId::isSimplifiedChinese(rFSD
.meLanguage
))
1234 nSearchType
|= ImplFontAttrs::CJK
| ImplFontAttrs::CJK_SC
;
1235 else if (MsLangId::isTraditionalChinese(rFSD
.meLanguage
))
1236 nSearchType
|= ImplFontAttrs::CJK
| ImplFontAttrs::CJK_TC
;
1237 else if (MsLangId::isKorean(rFSD
.meLanguage
))
1238 nSearchType
|= ImplFontAttrs::CJK
| ImplFontAttrs::CJK_KR
;
1239 else if (rFSD
.meLanguage
== LANGUAGE_JAPANESE
)
1240 nSearchType
|= ImplFontAttrs::CJK
| ImplFontAttrs::CJK_JP
;
1243 nSearchType
|= lcl_IsCJKFont( rFSD
.GetFamilyName() );
1244 if( rFSD
.IsSymbolFont() )
1245 nSearchType
|= ImplFontAttrs::Symbol
;
1248 PhysicalFontFamily::CalcType( nSearchType
, eSearchWeight
, eSearchWidth
, rFSD
.GetFamilyType(), pFontAttr
);
1249 PhysicalFontFamily
* pFoundData
= FindFontFamilyByAttributes( nSearchType
,
1250 eSearchWeight
, eSearchWidth
, rFSD
.GetItalic(), aSearchFamilyName
);
1254 // overwrite font selection attributes using info from the typeface flags
1255 if( (eSearchWeight
>= WEIGHT_BOLD
) &&
1256 (eSearchWeight
> rFSD
.GetWeight()) &&
1257 (pFoundData
->GetTypeFaces() & FontTypeFaces::Bold
) )
1259 rFSD
.SetWeight( eSearchWeight
);
1261 else if( (eSearchWeight
< WEIGHT_NORMAL
) &&
1262 (eSearchWeight
< rFSD
.GetWeight()) &&
1263 (eSearchWeight
!= WEIGHT_DONTKNOW
) &&
1264 (pFoundData
->GetTypeFaces() & FontTypeFaces::Light
) )
1266 rFSD
.SetWeight( eSearchWeight
);
1269 if( (nSearchType
& ImplFontAttrs::Italic
) &&
1270 ((rFSD
.GetItalic() == ITALIC_DONTKNOW
) ||
1271 (rFSD
.GetItalic() == ITALIC_NONE
)) &&
1272 (pFoundData
->GetTypeFaces() & FontTypeFaces::Italic
) )
1274 rFSD
.SetItalic( ITALIC_NORMAL
);
1279 // if still needed fall back to default fonts
1280 pFoundData
= ImplFindFontFamilyOfDefaultFont();
1286 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */