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 <i18nlangtag/languagetag.hxx>
22 #include <o3tl/any.hxx>
23 #include <unotools/configmgr.hxx>
24 #include <unotools/fontcfg.hxx>
25 #include <unotools/fontdefs.hxx>
26 #include <comphelper/configuration.hxx>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/propertysequence.hxx>
29 #include <com/sun/star/uno/Any.hxx>
30 #include <com/sun/star/uno/Sequence.hxx>
31 #include <com/sun/star/configuration/theDefaultProvider.hpp>
32 #include <com/sun/star/container/XNameAccess.hpp>
33 #include <unotools/syslocale.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <osl/diagnose.h>
36 #include <sal/log.hxx>
42 using namespace com::sun::star::uno
;
43 using namespace com::sun::star::lang
;
44 using namespace com::sun::star::container
;
45 using namespace com::sun::star::configuration
;
48 * DefaultFontConfiguration
51 static OUString
getKeyType( DefaultFontType nKeyType
)
55 case DefaultFontType::CJK_DISPLAY
: return u
"CJK_DISPLAY"_ustr
;
56 case DefaultFontType::CJK_HEADING
: return u
"CJK_HEADING"_ustr
;
57 case DefaultFontType::CJK_PRESENTATION
: return u
"CJK_PRESENTATION"_ustr
;
58 case DefaultFontType::CJK_SPREADSHEET
: return u
"CJK_SPREADSHEET"_ustr
;
59 case DefaultFontType::CJK_TEXT
: return u
"CJK_TEXT"_ustr
;
60 case DefaultFontType::CTL_DISPLAY
: return u
"CTL_DISPLAY"_ustr
;
61 case DefaultFontType::CTL_HEADING
: return u
"CTL_HEADING"_ustr
;
62 case DefaultFontType::CTL_PRESENTATION
: return u
"CTL_PRESENTATION"_ustr
;
63 case DefaultFontType::CTL_SPREADSHEET
: return u
"CTL_SPREADSHEET"_ustr
;
64 case DefaultFontType::CTL_TEXT
: return u
"CTL_TEXT"_ustr
;
65 case DefaultFontType::FIXED
: return u
"FIXED"_ustr
;
66 case DefaultFontType::LATIN_DISPLAY
: return u
"LATIN_DISPLAY"_ustr
;
67 case DefaultFontType::LATIN_FIXED
: return u
"LATIN_FIXED"_ustr
;
68 case DefaultFontType::LATIN_HEADING
: return u
"LATIN_HEADING"_ustr
;
69 case DefaultFontType::LATIN_PRESENTATION
: return u
"LATIN_PRESENTATION"_ustr
;
70 case DefaultFontType::LATIN_SPREADSHEET
: return u
"LATIN_SPREADSHEET"_ustr
;
71 case DefaultFontType::LATIN_TEXT
: return u
"LATIN_TEXT"_ustr
;
72 case DefaultFontType::SANS
: return u
"SANS"_ustr
;
73 case DefaultFontType::SANS_UNICODE
: return u
"SANS_UNICODE"_ustr
;
74 case DefaultFontType::SERIF
: return u
"SERIF"_ustr
;
75 case DefaultFontType::SYMBOL
: return u
"SYMBOL"_ustr
;
76 case DefaultFontType::UI_FIXED
: return u
"UI_FIXED"_ustr
;
77 case DefaultFontType::UI_SANS
: return u
"UI_SANS"_ustr
;
79 OSL_FAIL( "unmatched type" );
84 DefaultFontConfiguration
& DefaultFontConfiguration::get()
86 static DefaultFontConfiguration theDefaultFontConfiguration
;
87 return theDefaultFontConfiguration
;
90 DefaultFontConfiguration::DefaultFontConfiguration()
92 if (comphelper::IsFuzzing())
94 // create configuration hierarchical access name
97 // get service provider
98 m_xConfigProvider
= theDefaultProvider::get(comphelper::getProcessComponentContext());
99 Sequence
<Any
> aArgs(comphelper::InitAnyPropertySequence(
101 {"nodepath", Any(u
"/org.openoffice.VCL/DefaultFonts"_ustr
)}
104 Reference
< XNameAccess
>(
105 m_xConfigProvider
->createInstanceWithArguments( u
"com.sun.star.configuration.ConfigurationAccess"_ustr
,
108 if( m_xConfigAccess
.is() )
110 const Sequence
< OUString
> aLocales
= m_xConfigAccess
->getElementNames();
111 // fill config hash with empty interfaces
112 for( const OUString
& rLocaleString
: aLocales
)
114 // Feed through LanguageTag for casing.
115 OUString
aLoc( LanguageTag( rLocaleString
, true).getBcp47( false));
116 m_aConfig
[ aLoc
] = LocaleAccess();
117 m_aConfig
[ aLoc
].aConfigLocaleString
= rLocaleString
;
121 catch (const Exception
&)
123 // configuration is awry
124 m_xConfigProvider
.clear();
125 m_xConfigAccess
.clear();
127 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider
.is()
128 << ", config access: " << m_xConfigAccess
.is());
131 DefaultFontConfiguration::~DefaultFontConfiguration()
136 m_xConfigAccess
.clear();
137 // release config provider
138 m_xConfigProvider
.clear();
141 OUString
DefaultFontConfiguration::tryLocale( const OUString
& rBcp47
, const OUString
& rType
) const
145 std::unordered_map
< OUString
, LocaleAccess
>::const_iterator it
= m_aConfig
.find( rBcp47
);
146 if( it
!= m_aConfig
.end() )
148 if( !it
->second
.xAccess
.is() )
152 Reference
< XNameAccess
> xNode
;
153 if ( m_xConfigAccess
->hasByName( it
->second
.aConfigLocaleString
) )
155 Any aAny
= m_xConfigAccess
->getByName( it
->second
.aConfigLocaleString
);
157 it
->second
.xAccess
= std::move(xNode
);
160 catch (const NoSuchElementException
&)
163 catch (const WrappedTargetException
&)
167 if( it
->second
.xAccess
.is() )
171 if ( it
->second
.xAccess
->hasByName( rType
) )
173 Any aAny
= it
->second
.xAccess
->getByName( rType
);
177 catch (const NoSuchElementException
&)
180 catch (const WrappedTargetException
&)
189 OUString
DefaultFontConfiguration::getDefaultFont( const LanguageTag
& rLanguageTag
, DefaultFontType nType
) const
191 OUString aType
= getKeyType( nType
);
192 // Try the simple cases first without constructing fallbacks.
193 OUString aRet
= tryLocale( rLanguageTag
.getBcp47(), aType
);
196 if (rLanguageTag
.isIsoLocale())
198 if (!rLanguageTag
.getCountry().isEmpty())
200 aRet
= tryLocale( rLanguageTag
.getLanguage(), aType
);
205 ::std::vector
< OUString
> aFallbacks( rLanguageTag
.getFallbackStrings( false));
206 for (const auto& rFallback
: aFallbacks
)
208 aRet
= tryLocale( rFallback
, aType
);
216 aRet
= tryLocale( u
"en"_ustr
, aType
);
221 OUString
DefaultFontConfiguration::getUserInterfaceFont( const LanguageTag
& rLanguageTag
) const
223 LanguageTag
aLanguageTag( rLanguageTag
);
224 if( aLanguageTag
.isSystemLocale() )
225 aLanguageTag
= SvtSysLocale().GetUILanguageTag();
227 OUString aUIFont
= getDefaultFont( aLanguageTag
, DefaultFontType::UI_SANS
);
229 if( !aUIFont
.isEmpty() )
232 // fallback mechanism (either no configuration or no entry in configuration
234 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS
= u
"Andale Sans UI;Albany;Albany AMT;Tahoma;Arial Unicode MS;Arial;Nimbus Sans L;Bitstream Vera Sans;gnu-unifont;Interface User;Geneva;WarpSans;Dialog;Swiss;Lucida;Helvetica;Charcoal;Chicago;MS Sans Serif;Helv;Times;Times New Roman;Interface System";
235 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_LATIN2
= u
"Andale Sans UI;Albany;Albany AMT;Tahoma;Arial Unicode MS;Arial;Nimbus Sans L;Luxi Sans;Bitstream Vera Sans;Interface User;Geneva;WarpSans;Dialog;Swiss;Lucida;Helvetica;Charcoal;Chicago;MS Sans Serif;Helv;Times;Times New Roman;Interface System";
236 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_ARABIC
= u
"Tahoma;Traditional Arabic;Simplified Arabic;Lucidasans;Lucida Sans;Supplement;Andale Sans UI;clearlyU;Interface User;Arial Unicode MS;Lucida Sans Unicode;WarpSans;Geneva;MS Sans Serif;Helv;Dialog;Albany;Lucida;Helvetica;Charcoal;Chicago;Arial;Helmet;Interface System;Sans Serif";
237 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_THAI
= u
"OONaksit;Tahoma;Lucidasans;Arial Unicode MS";
238 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_KOREAN
= u
"Noto Sans KR;Noto Sans CJK KR;Noto Serif KR;Noto Serif CJK KR;Source Han Sans KR;NanumGothic;NanumBarunGothic;NanumBarunGothic YetHangul;KoPubWorld Dotum;Malgun Gothic;Apple SD Gothic Neo;Dotum;DotumChe;Gulim;GulimChe;Batang;BatangChe;Apple Gothic;UnDotum;Baekmuk Gulim;Arial Unicode MS;Lucida Sans Unicode;gnu-unifont;Andale Sans UI";
239 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_JAPANESE
= u
"Noto Sans CJK JP;Noto Sans JP;Source Han Sans;Source Han Sans JP;Yu Gothic UI;Yu Gothic;YuGothic;Hiragino Sans;Hiragino Kaku Gothic ProN;Hiragino Kaku Gothic Pro;Hiragino Kaku Gothic StdN;Meiryo UI;Meiryo;IPAexGothic;IPAPGothic;IPAGothic;MS UI Gothic;MS PGothic;MS Gothic;Osaka;Unifont;gnu-unifont;Arial Unicode MS;Interface System";
240 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_CHINSIM
= u
"Andale Sans UI;Arial Unicode MS;ZYSong18030;AR PL SungtiL GB;AR PL KaitiM GB;SimSun;Lucida Sans Unicode;Fangsong;Hei;Song;Kai;Ming;gnu-unifont;Interface User;";
241 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_CHINTRD
= u
"Andale Sans UI;Arial Unicode MS;AR PL Mingti2L Big5;AR PL KaitiM Big5;Kai;PMingLiU;MingLiU;Ming;Lucida Sans Unicode;gnu-unifont;Interface User;";
243 const OUString
aLanguage( aLanguageTag
.getLanguage());
245 // optimize font list for some locales, as long as Andale Sans UI does not support them
246 if( aLanguage
== "ar" || aLanguage
== "he" || aLanguage
== "iw" )
248 return FALLBACKFONT_UI_SANS_ARABIC
;
250 else if ( aLanguage
== "th" )
252 return FALLBACKFONT_UI_SANS_THAI
;
254 else if ( aLanguage
== "ko" )
256 return FALLBACKFONT_UI_SANS_KOREAN
;
258 else if ( aLanguage
== "ja" )
260 return FALLBACKFONT_UI_SANS_JAPANESE
;
262 else if( aLanguage
== "cs" ||
272 return FALLBACKFONT_UI_SANS_LATIN2
;
276 const Locale
& aLocale( aLanguageTag
.getLocale());
277 if (MsLangId::isTraditionalChinese(aLocale
))
278 return FALLBACKFONT_UI_SANS_CHINTRD
;
279 else if (MsLangId::isSimplifiedChinese(aLocale
))
280 return FALLBACKFONT_UI_SANS_CHINSIM
;
283 return FALLBACKFONT_UI_SANS
;
287 * FontSubstConfigItem::get
290 FontSubstConfiguration
& FontSubstConfiguration::get()
292 static FontSubstConfiguration theFontSubstConfiguration
;
293 return theFontSubstConfiguration
;
297 * FontSubstConfigItem::FontSubstConfigItem
300 FontSubstConfiguration::FontSubstConfiguration() :
302 maLanguageTag(u
"en"_ustr
)
304 if (comphelper::IsFuzzing())
308 // get service provider
309 const Reference
< XComponentContext
>& xContext( comphelper::getProcessComponentContext() );
310 // create configuration hierarchical access name
311 m_xConfigProvider
= theDefaultProvider::get( xContext
);
312 Sequence
<Any
> aArgs(comphelper::InitAnyPropertySequence(
314 {"nodepath", Any(u
"/org.openoffice.VCL/FontSubstitutions"_ustr
)}
317 Reference
< XNameAccess
>(
318 m_xConfigProvider
->createInstanceWithArguments( u
"com.sun.star.configuration.ConfigurationAccess"_ustr
,
321 if( m_xConfigAccess
.is() )
323 const Sequence
< OUString
> aLocales
= m_xConfigAccess
->getElementNames();
324 // fill config hash with empty interfaces
325 for( const OUString
& rLocaleString
: aLocales
)
327 // Feed through LanguageTag for casing.
328 OUString
aLoc( LanguageTag( rLocaleString
, true).getBcp47( false));
329 m_aSubst
[ aLoc
] = LocaleSubst();
330 m_aSubst
[ aLoc
].aConfigLocaleString
= rLocaleString
;
334 catch (const Exception
&)
336 // configuration is awry
337 m_xConfigProvider
.clear();
338 m_xConfigAccess
.clear();
340 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider
.is()
341 << ", config access: " << m_xConfigAccess
.is());
343 if( maLanguageTag
.isSystemLocale() )
344 maLanguageTag
= SvtSysLocale().GetUILanguageTag();
348 * FontSubstConfigItem::~FontSubstConfigItem
351 FontSubstConfiguration::~FontSubstConfiguration()
353 // release config access
354 m_xConfigAccess
.clear();
355 // release config provider
356 m_xConfigProvider
.clear();
360 * FontSubstConfigItem::getMapName
363 const char* const aImplKillLeadingList
[] =
386 const char* const aImplKillTrailingList
[] =
399 // Scripts, for compatibility with older versions
416 // Old Printer Fontnames
436 const char* const aImplKillTrailingWithExceptionsList
[] =
438 "ce", "monospace", "oldface", nullptr,
439 "ps", "caps", nullptr,
445 struct ImplFontAttrWeightSearchData
453 ImplFontAttrWeightSearchData
const aImplWeightAttrSearchList
[] =
455 // the attribute names are ordered by "first match wins"
456 // e.g. "semilight" should wins over "semi"
457 { "extrablack", WEIGHT_BLACK
},
458 { "ultrablack", WEIGHT_BLACK
},
459 { "ultrabold", WEIGHT_ULTRABOLD
},
460 { "semibold", WEIGHT_SEMIBOLD
},
461 { "semilight", WEIGHT_SEMILIGHT
},
462 { "semi", WEIGHT_SEMIBOLD
},
463 { "demi", WEIGHT_SEMIBOLD
},
464 { "black", WEIGHT_BLACK
},
465 { "bold", WEIGHT_BOLD
},
466 { "heavy", WEIGHT_BLACK
},
467 { "ultralight", WEIGHT_ULTRALIGHT
},
468 { "light", WEIGHT_LIGHT
},
469 { "medium", WEIGHT_MEDIUM
},
470 { nullptr, WEIGHT_DONTKNOW
},
475 struct ImplFontAttrWidthSearchData
483 ImplFontAttrWidthSearchData
const aImplWidthAttrSearchList
[] =
485 { "narrow", WIDTH_CONDENSED
},
486 { "semicondensed", WIDTH_SEMI_CONDENSED
},
487 { "ultracondensed", WIDTH_ULTRA_CONDENSED
},
488 { "semiexpanded", WIDTH_SEMI_EXPANDED
},
489 { "ultraexpanded", WIDTH_ULTRA_EXPANDED
},
490 { "expanded", WIDTH_EXPANDED
},
491 { "wide", WIDTH_ULTRA_EXPANDED
},
492 { "condensed", WIDTH_CONDENSED
},
493 { "cond", WIDTH_CONDENSED
},
494 { "cn", WIDTH_CONDENSED
},
495 { nullptr, WIDTH_DONTKNOW
},
500 struct ImplFontAttrTypeSearchData
503 ImplFontAttrs mnType
;
508 ImplFontAttrTypeSearchData
const aImplTypeAttrSearchList
[] =
510 { "monotype", ImplFontAttrs::None
},
511 { "linotype", ImplFontAttrs::None
},
512 { "titling", ImplFontAttrs::Titling
},
513 { "capitals", ImplFontAttrs::Capitals
},
514 { "capital", ImplFontAttrs::Capitals
},
515 { "caps", ImplFontAttrs::Capitals
},
516 { "italic", ImplFontAttrs::Italic
},
517 { "oblique", ImplFontAttrs::Italic
},
518 { "rounded", ImplFontAttrs::Rounded
},
519 { "outline", ImplFontAttrs::Outline
},
520 { "shadow", ImplFontAttrs::Shadow
},
521 { "handwriting", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
522 { "hand", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
523 { "signet", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
524 { "script", ImplFontAttrs::BrushScript
| ImplFontAttrs::Script
},
525 { "calligraphy", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
526 { "chancery", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
527 { "corsiva", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
528 { "gothic", ImplFontAttrs::SansSerif
| ImplFontAttrs::Gothic
},
529 { "schoolbook", ImplFontAttrs::Serif
| ImplFontAttrs::Schoolbook
},
530 { "schlbk", ImplFontAttrs::Serif
| ImplFontAttrs::Schoolbook
},
531 { "typewriter", ImplFontAttrs::Typewriter
| ImplFontAttrs::Fixed
},
532 { "lineprinter", ImplFontAttrs::Typewriter
| ImplFontAttrs::Fixed
},
533 { "monospaced", ImplFontAttrs::Fixed
},
534 { "monospace", ImplFontAttrs::Fixed
},
535 { "mono", ImplFontAttrs::Fixed
},
536 { "fixed", ImplFontAttrs::Fixed
},
537 { "sansserif", ImplFontAttrs::SansSerif
},
538 { "sans", ImplFontAttrs::SansSerif
},
539 { "swiss", ImplFontAttrs::SansSerif
},
540 { "serif", ImplFontAttrs::Serif
},
541 { "bright", ImplFontAttrs::Serif
},
542 { "symbols", ImplFontAttrs::Symbol
},
543 { "symbol", ImplFontAttrs::Symbol
},
544 { "dingbats", ImplFontAttrs::Symbol
},
545 { "dings", ImplFontAttrs::Symbol
},
546 { "ding", ImplFontAttrs::Symbol
},
547 { "bats", ImplFontAttrs::Symbol
},
548 { "math", ImplFontAttrs::Symbol
},
549 { "oldstyle", ImplFontAttrs::OtherStyle
},
550 { "oldface", ImplFontAttrs::OtherStyle
},
551 { "old", ImplFontAttrs::OtherStyle
},
552 { "new", ImplFontAttrs::None
},
553 { "modern", ImplFontAttrs::None
},
554 { "lucida", ImplFontAttrs::None
},
555 { "regular", ImplFontAttrs::None
},
556 { "extended", ImplFontAttrs::None
},
557 { "extra", ImplFontAttrs::OtherStyle
},
558 { "ext", ImplFontAttrs::None
},
559 { "scalable", ImplFontAttrs::None
},
560 { "scale", ImplFontAttrs::None
},
561 { "nimbus", ImplFontAttrs::None
},
562 { "adobe", ImplFontAttrs::None
},
563 { "itc", ImplFontAttrs::None
},
564 { "amt", ImplFontAttrs::None
},
565 { "mt", ImplFontAttrs::None
},
566 { "ms", ImplFontAttrs::None
},
567 { "cpi", ImplFontAttrs::None
},
568 { "no", ImplFontAttrs::None
},
569 { nullptr, ImplFontAttrs::None
},
572 static bool ImplKillLeading( OUString
& rName
, const char* const* ppStr
)
574 for(; *ppStr
; ++ppStr
)
576 const char* pStr
= *ppStr
;
577 const sal_Unicode
* pNameStr
= rName
.getStr();
578 while ( (*pNameStr
== static_cast<sal_Unicode
>(static_cast<unsigned char>(*pStr
))) && *pStr
)
585 sal_Int32 nLen
= static_cast<sal_Int32
>(pNameStr
- rName
.getStr());
586 rName
= rName
.copy(nLen
);
591 // special case for Baekmuk
592 // TODO: allow non-ASCII KillLeading list
593 const sal_Unicode
* pNameStr
= rName
.getStr();
594 if( (pNameStr
[0]==0xBC31) && (pNameStr
[1]==0xBC35) )
596 sal_Int32 nLen
= (pNameStr
[2]==0x0020) ? 3 : 2;
597 rName
= rName
.copy(nLen
);
604 static sal_Int32
ImplIsTrailing( std::u16string_view rName
, const char* pStr
)
606 size_t nStrLen
= strlen( pStr
);
607 if( nStrLen
>= rName
.size() )
610 const sal_Unicode
* pEndName
= rName
.data() + rName
.size();
611 const sal_Unicode
* pNameStr
= pEndName
- nStrLen
;
612 do if( *(pNameStr
++) != *(pStr
++) )
619 static bool ImplKillTrailing( OUString
& rName
, const char* const* ppStr
)
621 for(; *ppStr
; ++ppStr
)
623 sal_Int32 nTrailLen
= ImplIsTrailing( rName
, *ppStr
);
626 rName
= rName
.copy(0, rName
.getLength() - nTrailLen
);
634 static bool ImplKillTrailingWithExceptions( OUString
& rName
, const char* const* ppStr
)
636 for(; *ppStr
; ++ppStr
)
638 sal_Int32 nTrailLen
= ImplIsTrailing( rName
, *ppStr
);
641 // check string match against string exceptions
643 if( ImplIsTrailing( rName
, *ppStr
) )
646 rName
= rName
.copy(0, rName
.getLength() - nTrailLen
);
651 // skip exception strings
659 static bool ImplFindAndErase( OUString
& rName
, const char* pStr
)
661 sal_Int32 nLen
= static_cast<sal_Int32
>(strlen(pStr
));
662 sal_Int32 nPos
= rName
.indexOfAsciiL(pStr
, nLen
);
666 OUStringBuffer
sBuff(rName
);
667 sBuff
.remove(nPos
, nLen
);
668 rName
= sBuff
.makeStringAndClear();
672 void FontSubstConfiguration::getMapName( const OUString
& rOrgName
, OUString
& rShortName
,
673 OUString
& rFamilyName
, FontWeight
& rWeight
,
674 FontWidth
& rWidth
, ImplFontAttrs
& rType
)
676 rShortName
= rOrgName
;
678 // TODO: get rid of the crazy O(N*strlen) searches below
679 // they should be possible in O(strlen)
681 // Kill leading vendor names and other unimportant data
682 ImplKillLeading( rShortName
, aImplKillLeadingList
);
684 // Kill trailing vendor names and other unimportant data
685 ImplKillTrailing( rShortName
, aImplKillTrailingList
);
686 ImplKillTrailingWithExceptions( rShortName
, aImplKillTrailingWithExceptionsList
);
688 rFamilyName
= rShortName
;
690 // Kill attributes from the name and update the data
692 const ImplFontAttrWeightSearchData
* pWeightList
= aImplWeightAttrSearchList
;
693 while ( pWeightList
->mpStr
)
695 if ( ImplFindAndErase( rFamilyName
, pWeightList
->mpStr
) )
697 if ( (rWeight
== WEIGHT_DONTKNOW
) || (rWeight
== WEIGHT_NORMAL
) )
698 rWeight
= pWeightList
->meWeight
;
705 const ImplFontAttrWidthSearchData
* pWidthList
= aImplWidthAttrSearchList
;
706 while ( pWidthList
->mpStr
)
708 if ( ImplFindAndErase( rFamilyName
, pWidthList
->mpStr
) )
710 if ( (rWidth
== WIDTH_DONTKNOW
) || (rWidth
== WIDTH_NORMAL
) )
711 rWidth
= pWidthList
->meWidth
;
718 rType
= ImplFontAttrs::None
;
719 const ImplFontAttrTypeSearchData
* pTypeList
= aImplTypeAttrSearchList
;
720 while ( pTypeList
->mpStr
)
722 if ( ImplFindAndErase( rFamilyName
, pTypeList
->mpStr
) )
723 rType
|= pTypeList
->mnType
;
728 // TODO: also remove localized and fullwidth digits
730 OUStringBuffer
sBuff(rFamilyName
);
731 while ( i
< sBuff
.getLength() )
733 sal_Unicode c
= sBuff
[ i
];
734 if ( (c
>= 0x0030) && (c
<= 0x0039) )
743 struct StrictStringSort
745 bool operator()( const FontNameAttr
& rLeft
, const FontNameAttr
& rRight
)
746 { return rLeft
.Name
.compareTo( rRight
.Name
) < 0; }
751 // The entries in this table must match the bits in the ImplFontAttrs enum.
753 const char* const pAttribNames
[] =
799 const enum_convert pWeightNames
[] =
801 { "normal", WEIGHT_NORMAL
},
802 { "medium", WEIGHT_MEDIUM
},
803 { "bold", WEIGHT_BOLD
},
804 { "black", WEIGHT_BLACK
},
805 { "semibold", WEIGHT_SEMIBOLD
},
806 { "light", WEIGHT_LIGHT
},
807 { "semilight", WEIGHT_SEMILIGHT
},
808 { "ultrabold", WEIGHT_ULTRABOLD
},
809 { "semi", WEIGHT_SEMIBOLD
},
810 { "demi", WEIGHT_SEMIBOLD
},
811 { "heavy", WEIGHT_BLACK
},
812 { "unknown", WEIGHT_DONTKNOW
},
813 { "thin", WEIGHT_THIN
},
814 { "ultralight", WEIGHT_ULTRALIGHT
}
817 const enum_convert pWidthNames
[] =
819 { "normal", WIDTH_NORMAL
},
820 { "condensed", WIDTH_CONDENSED
},
821 { "expanded", WIDTH_EXPANDED
},
822 { "unknown", WIDTH_DONTKNOW
},
823 { "ultracondensed", WIDTH_ULTRA_CONDENSED
},
824 { "extracondensed", WIDTH_EXTRA_CONDENSED
},
825 { "semicondensed", WIDTH_SEMI_CONDENSED
},
826 { "semiexpanded", WIDTH_SEMI_EXPANDED
},
827 { "extraexpanded", WIDTH_EXTRA_EXPANDED
},
828 { "ultraexpanded", WIDTH_ULTRA_EXPANDED
}
831 void FontSubstConfiguration::fillSubstVector( const css::uno::Reference
< XNameAccess
>& rFont
,
832 const OUString
& rType
,
833 std::vector
< OUString
>& rSubstVector
) const
837 Any aAny
= rFont
->getByName( rType
);
838 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
840 sal_Int32 nLength
= pLine
->getLength();
843 const sal_Unicode
* pStr
= pLine
->getStr();
844 sal_Int32 nTokens
= 0;
851 rSubstVector
.clear();
852 // optimize performance, heap fragmentation
853 rSubstVector
.reserve( nTokens
);
854 sal_Int32 nIndex
= 0;
855 while( nIndex
!= -1 )
857 OUString
aSubst( pLine
->getToken( 0, ';', nIndex
) );
858 if( !aSubst
.isEmpty() )
860 auto itPair
= maSubstHash
.insert( aSubst
);
862 aSubst
= *itPair
.first
;
863 rSubstVector
.push_back( aSubst
);
869 catch (const NoSuchElementException
&)
872 catch (const WrappedTargetException
&)
878 FontWeight
FontSubstConfiguration::getSubstWeight( const css::uno::Reference
< XNameAccess
>& rFont
,
879 const OUString
& rType
)
884 Any aAny
= rFont
->getByName( rType
);
885 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
887 if( !pLine
->isEmpty() )
889 for( weight
=std::size(pWeightNames
)-1; weight
>= 0; weight
-- )
890 if( pLine
->equalsIgnoreAsciiCaseAscii( pWeightNames
[weight
].pName
) )
893 SAL_WARN_IF(weight
< 0, "unotools.config", "Error: invalid weight " << *pLine
);
896 catch (const NoSuchElementException
&)
899 catch (const WrappedTargetException
&)
902 return static_cast<FontWeight
>( weight
>= 0 ? pWeightNames
[weight
].nEnum
: WEIGHT_DONTKNOW
);
906 FontWidth
FontSubstConfiguration::getSubstWidth( const css::uno::Reference
< XNameAccess
>& rFont
,
907 const OUString
& rType
)
912 Any aAny
= rFont
->getByName( rType
);
913 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
915 if( !pLine
->isEmpty() )
917 for( width
=std::size(pWidthNames
)-1; width
>= 0; width
-- )
918 if( pLine
->equalsIgnoreAsciiCaseAscii( pWidthNames
[width
].pName
) )
921 SAL_WARN_IF( width
< 0, "unotools.config", "Error: invalid width " << *pLine
);
924 catch (const NoSuchElementException
&)
927 catch (const WrappedTargetException
&)
930 return static_cast<FontWidth
>( width
>= 0 ? pWidthNames
[width
].nEnum
: WIDTH_DONTKNOW
);
934 ImplFontAttrs
FontSubstConfiguration::getSubstType( const css::uno::Reference
< XNameAccess
>& rFont
,
935 const OUString
& rType
)
940 Any aAny
= rFont
->getByName( rType
);
941 auto pLine
= o3tl::tryAccess
<OUString
>(aAny
);
943 return ImplFontAttrs::None
;
944 if( pLine
->isEmpty() )
945 return ImplFontAttrs::None
;
946 sal_Int32 nIndex
= 0;
947 while( nIndex
!= -1 )
949 OUString
aToken( pLine
->getToken( 0, ',', nIndex
) );
950 for( int k
= 0; k
< 32; k
++ )
951 if( aToken
.equalsIgnoreAsciiCaseAscii( pAttribNames
[k
] ) )
953 type
|= sal_uInt32(1) << k
;
957 assert(((type
& ~o3tl::typed_flags
<ImplFontAttrs
>::mask
) == 0) && "invalid font attributes");
959 catch (const NoSuchElementException
&)
962 catch (const WrappedTargetException
&)
966 return static_cast<ImplFontAttrs
>(type
);
969 void FontSubstConfiguration::readLocaleSubst( const OUString
& rBcp47
) const
971 std::unordered_map
< OUString
, LocaleSubst
>::const_iterator it
= m_aSubst
.find( rBcp47
);
972 if( it
== m_aSubst
.end() )
975 if( it
->second
.bConfigRead
)
978 it
->second
.bConfigRead
= true;
979 Reference
< XNameAccess
> xNode
;
982 Any aAny
= m_xConfigAccess
->getByName( it
->second
.aConfigLocaleString
);
985 catch (const NoSuchElementException
&)
988 catch (const WrappedTargetException
&)
994 const Sequence
< OUString
> aFonts
= xNode
->getElementNames();
995 int nFonts
= aFonts
.getLength();
996 // improve performance, heap fragmentation
997 it
->second
.aSubstAttributes
.reserve( nFonts
);
999 // strings for subst retrieval, construct only once
1000 static constexpr OUStringLiteral
aSubstFontsStr ( u
"SubstFonts" );
1001 static constexpr OUStringLiteral
aSubstFontsMSStr( u
"SubstFontsMS" );
1002 static constexpr OUStringLiteral
aSubstWeightStr ( u
"FontWeight" );
1003 static constexpr OUStringLiteral
aSubstWidthStr ( u
"FontWidth" );
1004 static constexpr OUStringLiteral
aSubstTypeStr ( u
"FontType" );
1005 for( const OUString
& rFontName
: aFonts
)
1007 Reference
< XNameAccess
> xFont
;
1010 Any aAny
= xNode
->getByName( rFontName
);
1013 catch (const NoSuchElementException
&)
1016 catch (const WrappedTargetException
&)
1021 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName
);
1026 // read subst attributes from config
1027 aAttr
.Name
= rFontName
;
1028 fillSubstVector( xFont
, aSubstFontsStr
, aAttr
.Substitutions
);
1029 fillSubstVector( xFont
, aSubstFontsMSStr
, aAttr
.MSSubstitutions
);
1030 aAttr
.Weight
= getSubstWeight( xFont
, aSubstWeightStr
);
1031 aAttr
.Width
= getSubstWidth( xFont
, aSubstWidthStr
);
1032 aAttr
.Type
= getSubstType( xFont
, aSubstTypeStr
);
1034 // finally insert this entry
1035 it
->second
.aSubstAttributes
.push_back( aAttr
);
1037 std::sort( it
->second
.aSubstAttributes
.begin(), it
->second
.aSubstAttributes
.end(), StrictStringSort() );
1040 const FontNameAttr
* FontSubstConfiguration::getSubstInfo( const OUString
& rFontName
) const
1042 if( rFontName
.isEmpty() )
1045 // search if a (language dep.) replacement table for the given font exists
1046 // fallback is english
1047 OUString
aSearchFont( rFontName
.toAsciiLowerCase() );
1048 FontNameAttr aSearchAttr
;
1049 aSearchAttr
.Name
= aSearchFont
;
1051 ::std::vector
< OUString
> aFallbacks( maLanguageTag
.getFallbackStrings( true));
1052 if (maLanguageTag
.getLanguage() != "en")
1053 aFallbacks
.emplace_back("en");
1055 for (const auto& rFallback
: aFallbacks
)
1057 std::unordered_map
< OUString
, LocaleSubst
>::const_iterator lang
= m_aSubst
.find( rFallback
);
1058 if( lang
!= m_aSubst
.end() )
1060 if( ! lang
->second
.bConfigRead
)
1061 readLocaleSubst( rFallback
);
1062 // try to find an exact match
1063 // because the list is sorted this will also find fontnames of the form searchfontname*
1064 std::vector
< FontNameAttr
>::const_iterator it
= ::std::lower_bound( lang
->second
.aSubstAttributes
.begin(), lang
->second
.aSubstAttributes
.end(), aSearchAttr
, StrictStringSort() );
1065 if( it
!= lang
->second
.aSubstAttributes
.end())
1067 const FontNameAttr
& rFoundAttr
= *it
;
1068 // a search for "abcblack" may match with an entry for "abc"
1069 // the reverse is not a good idea (e.g. #i112731# alba->albani)
1070 if( rFoundAttr
.Name
.getLength() <= aSearchFont
.getLength() )
1071 if( aSearchFont
.startsWith( rFoundAttr
.Name
))
1079 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */