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::beans
;
45 using namespace com::sun::star::container
;
46 using namespace com::sun::star::configuration
;
49 * DefaultFontConfiguration
52 static const char* getKeyType( DefaultFontType nKeyType
)
56 case DefaultFontType::CJK_DISPLAY
: return "CJK_DISPLAY";
57 case DefaultFontType::CJK_HEADING
: return "CJK_HEADING";
58 case DefaultFontType::CJK_PRESENTATION
: return "CJK_PRESENTATION";
59 case DefaultFontType::CJK_SPREADSHEET
: return "CJK_SPREADSHEET";
60 case DefaultFontType::CJK_TEXT
: return "CJK_TEXT";
61 case DefaultFontType::CTL_DISPLAY
: return "CTL_DISPLAY";
62 case DefaultFontType::CTL_HEADING
: return "CTL_HEADING";
63 case DefaultFontType::CTL_PRESENTATION
: return "CTL_PRESENTATION";
64 case DefaultFontType::CTL_SPREADSHEET
: return "CTL_SPREADSHEET";
65 case DefaultFontType::CTL_TEXT
: return "CTL_TEXT";
66 case DefaultFontType::FIXED
: return "FIXED";
67 case DefaultFontType::LATIN_DISPLAY
: return "LATIN_DISPLAY";
68 case DefaultFontType::LATIN_FIXED
: return "LATIN_FIXED";
69 case DefaultFontType::LATIN_HEADING
: return "LATIN_HEADING";
70 case DefaultFontType::LATIN_PRESENTATION
: return "LATIN_PRESENTATION";
71 case DefaultFontType::LATIN_SPREADSHEET
: return "LATIN_SPREADSHEET";
72 case DefaultFontType::LATIN_TEXT
: return "LATIN_TEXT";
73 case DefaultFontType::SANS
: return "SANS";
74 case DefaultFontType::SANS_UNICODE
: return "SANS_UNICODE";
75 case DefaultFontType::SERIF
: return "SERIF";
76 case DefaultFontType::SYMBOL
: return "SYMBOL";
77 case DefaultFontType::UI_FIXED
: return "UI_FIXED";
78 case DefaultFontType::UI_SANS
: return "UI_SANS";
80 OSL_FAIL( "unmatched type" );
85 DefaultFontConfiguration
& DefaultFontConfiguration::get()
87 static DefaultFontConfiguration theDefaultFontConfiguration
;
88 return theDefaultFontConfiguration
;
91 DefaultFontConfiguration::DefaultFontConfiguration()
93 if (comphelper::IsFuzzing())
95 // create configuration hierarchical access name
98 // get service provider
99 m_xConfigProvider
= theDefaultProvider::get(comphelper::getProcessComponentContext());
100 Sequence
<Any
> aArgs(comphelper::InitAnyPropertySequence(
102 {"nodepath", Any(OUString( "/org.openoffice.VCL/DefaultFonts" ))}
105 Reference
< XNameAccess
>(
106 m_xConfigProvider
->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
109 if( m_xConfigAccess
.is() )
111 const Sequence
< OUString
> aLocales
= m_xConfigAccess
->getElementNames();
112 // fill config hash with empty interfaces
113 for( const OUString
& rLocaleString
: aLocales
)
115 // Feed through LanguageTag for casing.
116 OUString
aLoc( LanguageTag( rLocaleString
, true).getBcp47( false));
117 m_aConfig
[ aLoc
] = LocaleAccess();
118 m_aConfig
[ aLoc
].aConfigLocaleString
= rLocaleString
;
122 catch (const Exception
&)
124 // configuration is awry
125 m_xConfigProvider
.clear();
126 m_xConfigAccess
.clear();
128 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider
.is()
129 << ", config access: " << m_xConfigAccess
.is());
132 DefaultFontConfiguration::~DefaultFontConfiguration()
137 m_xConfigAccess
.clear();
138 // release config provider
139 m_xConfigProvider
.clear();
142 OUString
DefaultFontConfiguration::tryLocale( const OUString
& rBcp47
, const OUString
& rType
) const
146 std::unordered_map
< OUString
, LocaleAccess
>::const_iterator it
= m_aConfig
.find( rBcp47
);
147 if( it
!= m_aConfig
.end() )
149 if( !it
->second
.xAccess
.is() )
153 Reference
< XNameAccess
> xNode
;
154 if ( m_xConfigAccess
->hasByName( it
->second
.aConfigLocaleString
) )
156 Any aAny
= m_xConfigAccess
->getByName( it
->second
.aConfigLocaleString
);
158 it
->second
.xAccess
= xNode
;
161 catch (const NoSuchElementException
&)
164 catch (const WrappedTargetException
&)
168 if( it
->second
.xAccess
.is() )
172 if ( it
->second
.xAccess
->hasByName( rType
) )
174 Any aAny
= it
->second
.xAccess
->getByName( rType
);
178 catch (const NoSuchElementException
&)
181 catch (const WrappedTargetException
&)
190 OUString
DefaultFontConfiguration::getDefaultFont( const LanguageTag
& rLanguageTag
, DefaultFontType nType
) const
192 OUString aType
= OUString::createFromAscii( getKeyType( nType
) );
193 // Try the simple cases first without constructing fallbacks.
194 OUString aRet
= tryLocale( rLanguageTag
.getBcp47(), aType
);
197 if (rLanguageTag
.isIsoLocale())
199 if (!rLanguageTag
.getCountry().isEmpty())
201 aRet
= tryLocale( rLanguageTag
.getLanguage(), aType
);
206 ::std::vector
< OUString
> aFallbacks( rLanguageTag
.getFallbackStrings( false));
207 for (const auto& rFallback
: aFallbacks
)
209 aRet
= tryLocale( rFallback
, aType
);
217 aRet
= tryLocale( "en", aType
);
222 OUString
DefaultFontConfiguration::getUserInterfaceFont( const LanguageTag
& rLanguageTag
) const
224 LanguageTag
aLanguageTag( rLanguageTag
);
225 if( aLanguageTag
.isSystemLocale() )
226 aLanguageTag
= SvtSysLocale().GetUILanguageTag();
228 OUString aUIFont
= getDefaultFont( aLanguageTag
, DefaultFontType::UI_SANS
);
230 if( !aUIFont
.isEmpty() )
233 // fallback mechanism (either no configuration or no entry in configuration
235 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";
236 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";
237 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";
238 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_THAI
= u
"OONaksit;Tahoma;Lucidasans;Arial Unicode MS";
239 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";
240 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";
241 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;";
242 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;";
244 const OUString
aLanguage( aLanguageTag
.getLanguage());
246 // optimize font list for some locales, as long as Andale Sans UI does not support them
247 if( aLanguage
== "ar" || aLanguage
== "he" || aLanguage
== "iw" )
249 return FALLBACKFONT_UI_SANS_ARABIC
;
251 else if ( aLanguage
== "th" )
253 return FALLBACKFONT_UI_SANS_THAI
;
255 else if ( aLanguage
== "ko" )
257 return FALLBACKFONT_UI_SANS_KOREAN
;
259 else if ( aLanguage
== "ja" )
261 return FALLBACKFONT_UI_SANS_JAPANESE
;
263 else if( aLanguage
== "cs" ||
273 return FALLBACKFONT_UI_SANS_LATIN2
;
277 const Locale
& aLocale( aLanguageTag
.getLocale());
278 if (MsLangId::isTraditionalChinese(aLocale
))
279 return FALLBACKFONT_UI_SANS_CHINTRD
;
280 else if (MsLangId::isSimplifiedChinese(aLocale
))
281 return FALLBACKFONT_UI_SANS_CHINSIM
;
284 return FALLBACKFONT_UI_SANS
;
288 * FontSubstConfigItem::get
291 FontSubstConfiguration
& FontSubstConfiguration::get()
293 static FontSubstConfiguration theFontSubstConfiguration
;
294 return theFontSubstConfiguration
;
298 * FontSubstConfigItem::FontSubstConfigItem
301 FontSubstConfiguration::FontSubstConfiguration() :
305 if (comphelper::IsFuzzing())
309 // get service provider
310 Reference
< XComponentContext
> xContext( comphelper::getProcessComponentContext() );
311 // create configuration hierarchical access name
312 m_xConfigProvider
= theDefaultProvider::get( xContext
);
313 Sequence
<Any
> aArgs(comphelper::InitAnyPropertySequence(
315 {"nodepath", Any(OUString( "/org.openoffice.VCL/FontSubstitutions" ))}
318 Reference
< XNameAccess
>(
319 m_xConfigProvider
->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
322 if( m_xConfigAccess
.is() )
324 const Sequence
< OUString
> aLocales
= m_xConfigAccess
->getElementNames();
325 // fill config hash with empty interfaces
326 for( const OUString
& rLocaleString
: aLocales
)
328 // Feed through LanguageTag for casing.
329 OUString
aLoc( LanguageTag( rLocaleString
, true).getBcp47( false));
330 m_aSubst
[ aLoc
] = LocaleSubst();
331 m_aSubst
[ aLoc
].aConfigLocaleString
= rLocaleString
;
335 catch (const Exception
&)
337 // configuration is awry
338 m_xConfigProvider
.clear();
339 m_xConfigAccess
.clear();
341 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider
.is()
342 << ", config access: " << m_xConfigAccess
.is());
344 if( maLanguageTag
.isSystemLocale() )
345 maLanguageTag
= SvtSysLocale().GetUILanguageTag();
349 * FontSubstConfigItem::~FontSubstConfigItem
352 FontSubstConfiguration::~FontSubstConfiguration()
354 // release config access
355 m_xConfigAccess
.clear();
356 // release config provider
357 m_xConfigProvider
.clear();
361 * FontSubstConfigItem::getMapName
364 const char* const aImplKillLeadingList
[] =
387 const char* const aImplKillTrailingList
[] =
400 // Scripts, for compatibility with older versions
417 // Old Printer Fontnames
437 const char* const aImplKillTrailingWithExceptionsList
[] =
439 "ce", "monospace", "oldface", nullptr,
440 "ps", "caps", nullptr,
446 struct ImplFontAttrWeightSearchData
454 ImplFontAttrWeightSearchData
const aImplWeightAttrSearchList
[] =
456 // the attribute names are ordered by "first match wins"
457 // e.g. "semilight" should wins over "semi"
458 { "extrablack", WEIGHT_BLACK
},
459 { "ultrablack", WEIGHT_BLACK
},
460 { "ultrabold", WEIGHT_ULTRABOLD
},
461 { "semibold", WEIGHT_SEMIBOLD
},
462 { "semilight", WEIGHT_SEMILIGHT
},
463 { "semi", WEIGHT_SEMIBOLD
},
464 { "demi", WEIGHT_SEMIBOLD
},
465 { "black", WEIGHT_BLACK
},
466 { "bold", WEIGHT_BOLD
},
467 { "heavy", WEIGHT_BLACK
},
468 { "ultralight", WEIGHT_ULTRALIGHT
},
469 { "light", WEIGHT_LIGHT
},
470 { "medium", WEIGHT_MEDIUM
},
471 { nullptr, WEIGHT_DONTKNOW
},
476 struct ImplFontAttrWidthSearchData
484 ImplFontAttrWidthSearchData
const aImplWidthAttrSearchList
[] =
486 { "narrow", WIDTH_CONDENSED
},
487 { "semicondensed", WIDTH_SEMI_CONDENSED
},
488 { "ultracondensed", WIDTH_ULTRA_CONDENSED
},
489 { "semiexpanded", WIDTH_SEMI_EXPANDED
},
490 { "ultraexpanded", WIDTH_ULTRA_EXPANDED
},
491 { "expanded", WIDTH_EXPANDED
},
492 { "wide", WIDTH_ULTRA_EXPANDED
},
493 { "condensed", WIDTH_CONDENSED
},
494 { "cond", WIDTH_CONDENSED
},
495 { "cn", WIDTH_CONDENSED
},
496 { nullptr, WIDTH_DONTKNOW
},
501 struct ImplFontAttrTypeSearchData
504 ImplFontAttrs mnType
;
509 ImplFontAttrTypeSearchData
const aImplTypeAttrSearchList
[] =
511 { "monotype", ImplFontAttrs::None
},
512 { "linotype", ImplFontAttrs::None
},
513 { "titling", ImplFontAttrs::Titling
},
514 { "captitals", ImplFontAttrs::Capitals
},
515 { "captital", ImplFontAttrs::Capitals
},
516 { "caps", ImplFontAttrs::Capitals
},
517 { "italic", ImplFontAttrs::Italic
},
518 { "oblique", ImplFontAttrs::Italic
},
519 { "rounded", ImplFontAttrs::Rounded
},
520 { "outline", ImplFontAttrs::Outline
},
521 { "shadow", ImplFontAttrs::Shadow
},
522 { "handwriting", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
523 { "hand", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
524 { "signet", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
525 { "script", ImplFontAttrs::BrushScript
| ImplFontAttrs::Script
},
526 { "calligraphy", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
527 { "chancery", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
528 { "corsiva", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
529 { "gothic", ImplFontAttrs::SansSerif
| ImplFontAttrs::Gothic
},
530 { "schoolbook", ImplFontAttrs::Serif
| ImplFontAttrs::Schoolbook
},
531 { "schlbk", ImplFontAttrs::Serif
| ImplFontAttrs::Schoolbook
},
532 { "typewriter", ImplFontAttrs::Typewriter
| ImplFontAttrs::Fixed
},
533 { "lineprinter", ImplFontAttrs::Typewriter
| ImplFontAttrs::Fixed
},
534 { "monospaced", ImplFontAttrs::Fixed
},
535 { "monospace", ImplFontAttrs::Fixed
},
536 { "mono", ImplFontAttrs::Fixed
},
537 { "fixed", ImplFontAttrs::Fixed
},
538 { "sansserif", ImplFontAttrs::SansSerif
},
539 { "sans", ImplFontAttrs::SansSerif
},
540 { "swiss", ImplFontAttrs::SansSerif
},
541 { "serif", ImplFontAttrs::Serif
},
542 { "bright", ImplFontAttrs::Serif
},
543 { "symbols", ImplFontAttrs::Symbol
},
544 { "symbol", ImplFontAttrs::Symbol
},
545 { "dingbats", ImplFontAttrs::Symbol
},
546 { "dings", ImplFontAttrs::Symbol
},
547 { "ding", ImplFontAttrs::Symbol
},
548 { "bats", ImplFontAttrs::Symbol
},
549 { "math", ImplFontAttrs::Symbol
},
550 { "oldstyle", ImplFontAttrs::OtherStyle
},
551 { "oldface", ImplFontAttrs::OtherStyle
},
552 { "old", ImplFontAttrs::OtherStyle
},
553 { "new", ImplFontAttrs::None
},
554 { "modern", ImplFontAttrs::None
},
555 { "lucida", ImplFontAttrs::None
},
556 { "regular", ImplFontAttrs::None
},
557 { "extended", ImplFontAttrs::None
},
558 { "extra", ImplFontAttrs::OtherStyle
},
559 { "ext", ImplFontAttrs::None
},
560 { "scalable", ImplFontAttrs::None
},
561 { "scale", ImplFontAttrs::None
},
562 { "nimbus", ImplFontAttrs::None
},
563 { "adobe", ImplFontAttrs::None
},
564 { "itc", ImplFontAttrs::None
},
565 { "amt", ImplFontAttrs::None
},
566 { "mt", ImplFontAttrs::None
},
567 { "ms", ImplFontAttrs::None
},
568 { "cpi", ImplFontAttrs::None
},
569 { "no", ImplFontAttrs::None
},
570 { nullptr, ImplFontAttrs::None
},
573 static bool ImplKillLeading( OUString
& rName
, const char* const* ppStr
)
575 for(; *ppStr
; ++ppStr
)
577 const char* pStr
= *ppStr
;
578 const sal_Unicode
* pNameStr
= rName
.getStr();
579 while ( (*pNameStr
== static_cast<sal_Unicode
>(static_cast<unsigned char>(*pStr
))) && *pStr
)
586 sal_Int32 nLen
= static_cast<sal_Int32
>(pNameStr
- rName
.getStr());
587 rName
= rName
.copy(nLen
);
592 // special case for Baekmuk
593 // TODO: allow non-ASCII KillLeading list
594 const sal_Unicode
* pNameStr
= rName
.getStr();
595 if( (pNameStr
[0]==0xBC31) && (pNameStr
[1]==0xBC35) )
597 sal_Int32 nLen
= (pNameStr
[2]==0x0020) ? 3 : 2;
598 rName
= rName
.copy(nLen
);
605 static sal_Int32
ImplIsTrailing( std::u16string_view rName
, const char* pStr
)
607 size_t nStrLen
= strlen( pStr
);
608 if( nStrLen
>= rName
.size() )
611 const sal_Unicode
* pEndName
= rName
.data() + rName
.size();
612 const sal_Unicode
* pNameStr
= pEndName
- nStrLen
;
613 do if( *(pNameStr
++) != *(pStr
++) )
620 static bool ImplKillTrailing( OUString
& rName
, const char* const* ppStr
)
622 for(; *ppStr
; ++ppStr
)
624 sal_Int32 nTrailLen
= ImplIsTrailing( rName
, *ppStr
);
627 rName
= rName
.copy(0, rName
.getLength() - nTrailLen
);
635 static bool ImplKillTrailingWithExceptions( OUString
& rName
, const char* const* ppStr
)
637 for(; *ppStr
; ++ppStr
)
639 sal_Int32 nTrailLen
= ImplIsTrailing( rName
, *ppStr
);
642 // check string match against string exceptions
644 if( ImplIsTrailing( rName
, *ppStr
) )
647 rName
= rName
.copy(0, rName
.getLength() - nTrailLen
);
652 // skip exception strings
660 static bool ImplFindAndErase( OUString
& rName
, const char* pStr
)
662 sal_Int32 nLen
= static_cast<sal_Int32
>(strlen(pStr
));
663 sal_Int32 nPos
= rName
.indexOfAsciiL(pStr
, nLen
);
667 OUStringBuffer
sBuff(rName
);
668 sBuff
.remove(nPos
, nLen
);
669 rName
= sBuff
.makeStringAndClear();
673 void FontSubstConfiguration::getMapName( const OUString
& rOrgName
, OUString
& rShortName
,
674 OUString
& rFamilyName
, FontWeight
& rWeight
,
675 FontWidth
& rWidth
, ImplFontAttrs
& rType
)
677 rShortName
= rOrgName
;
679 // TODO: get rid of the crazy O(N*strlen) searches below
680 // they should be possible in O(strlen)
682 // Kill leading vendor names and other unimportant data
683 ImplKillLeading( rShortName
, aImplKillLeadingList
);
685 // Kill trailing vendor names and other unimportant data
686 ImplKillTrailing( rShortName
, aImplKillTrailingList
);
687 ImplKillTrailingWithExceptions( rShortName
, aImplKillTrailingWithExceptionsList
);
689 rFamilyName
= rShortName
;
691 // Kill attributes from the name and update the data
693 const ImplFontAttrWeightSearchData
* pWeightList
= aImplWeightAttrSearchList
;
694 while ( pWeightList
->mpStr
)
696 if ( ImplFindAndErase( rFamilyName
, pWeightList
->mpStr
) )
698 if ( (rWeight
== WEIGHT_DONTKNOW
) || (rWeight
== WEIGHT_NORMAL
) )
699 rWeight
= pWeightList
->meWeight
;
706 const ImplFontAttrWidthSearchData
* pWidthList
= aImplWidthAttrSearchList
;
707 while ( pWidthList
->mpStr
)
709 if ( ImplFindAndErase( rFamilyName
, pWidthList
->mpStr
) )
711 if ( (rWidth
== WIDTH_DONTKNOW
) || (rWidth
== WIDTH_NORMAL
) )
712 rWidth
= pWidthList
->meWidth
;
719 rType
= ImplFontAttrs::None
;
720 const ImplFontAttrTypeSearchData
* pTypeList
= aImplTypeAttrSearchList
;
721 while ( pTypeList
->mpStr
)
723 if ( ImplFindAndErase( rFamilyName
, pTypeList
->mpStr
) )
724 rType
|= pTypeList
->mnType
;
729 // TODO: also remove localized and fullwidth digits
731 OUStringBuffer
sBuff(rFamilyName
);
732 while ( i
< sBuff
.getLength() )
734 sal_Unicode c
= sBuff
[ i
];
735 if ( (c
>= 0x0030) && (c
<= 0x0039) )
744 struct StrictStringSort
746 bool operator()( const FontNameAttr
& rLeft
, const FontNameAttr
& rRight
)
747 { return rLeft
.Name
.compareTo( rRight
.Name
) < 0; }
752 // The entries in this table must match the bits in the ImplFontAttrs enum.
754 const char* const pAttribNames
[] =
800 const enum_convert pWeightNames
[] =
802 { "normal", WEIGHT_NORMAL
},
803 { "medium", WEIGHT_MEDIUM
},
804 { "bold", WEIGHT_BOLD
},
805 { "black", WEIGHT_BLACK
},
806 { "semibold", WEIGHT_SEMIBOLD
},
807 { "light", WEIGHT_LIGHT
},
808 { "semilight", WEIGHT_SEMILIGHT
},
809 { "ultrabold", WEIGHT_ULTRABOLD
},
810 { "semi", WEIGHT_SEMIBOLD
},
811 { "demi", WEIGHT_SEMIBOLD
},
812 { "heavy", WEIGHT_BLACK
},
813 { "unknown", WEIGHT_DONTKNOW
},
814 { "thin", WEIGHT_THIN
},
815 { "ultralight", WEIGHT_ULTRALIGHT
}
818 const enum_convert pWidthNames
[] =
820 { "normal", WIDTH_NORMAL
},
821 { "condensed", WIDTH_CONDENSED
},
822 { "expanded", WIDTH_EXPANDED
},
823 { "unknown", WIDTH_DONTKNOW
},
824 { "ultracondensed", WIDTH_ULTRA_CONDENSED
},
825 { "extracondensed", WIDTH_EXTRA_CONDENSED
},
826 { "semicondensed", WIDTH_SEMI_CONDENSED
},
827 { "semiexpanded", WIDTH_SEMI_EXPANDED
},
828 { "extraexpanded", WIDTH_EXTRA_EXPANDED
},
829 { "ultraexpanded", WIDTH_ULTRA_EXPANDED
}
832 void FontSubstConfiguration::fillSubstVector( const css::uno::Reference
< XNameAccess
>& rFont
,
833 const OUString
& rType
,
834 std::vector
< OUString
>& rSubstVector
) const
838 Any aAny
= rFont
->getByName( rType
);
839 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
841 sal_Int32 nLength
= pLine
->getLength();
844 const sal_Unicode
* pStr
= pLine
->getStr();
845 sal_Int32 nTokens
= 0;
852 rSubstVector
.clear();
853 // optimize performance, heap fragmentation
854 rSubstVector
.reserve( nTokens
);
855 sal_Int32 nIndex
= 0;
856 while( nIndex
!= -1 )
858 OUString
aSubst( pLine
->getToken( 0, ';', nIndex
) );
859 if( !aSubst
.isEmpty() )
861 auto itPair
= maSubstHash
.insert( aSubst
);
863 aSubst
= *itPair
.first
;
864 rSubstVector
.push_back( aSubst
);
870 catch (const NoSuchElementException
&)
873 catch (const WrappedTargetException
&)
879 FontWeight
FontSubstConfiguration::getSubstWeight( const css::uno::Reference
< XNameAccess
>& rFont
,
880 const OUString
& rType
)
885 Any aAny
= rFont
->getByName( rType
);
886 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
888 if( !pLine
->isEmpty() )
890 for( weight
=std::size(pWeightNames
)-1; weight
>= 0; weight
-- )
891 if( pLine
->equalsIgnoreAsciiCaseAscii( pWeightNames
[weight
].pName
) )
894 SAL_WARN_IF(weight
< 0, "unotools.config", "Error: invalid weight " << *pLine
);
897 catch (const NoSuchElementException
&)
900 catch (const WrappedTargetException
&)
903 return static_cast<FontWeight
>( weight
>= 0 ? pWeightNames
[weight
].nEnum
: WEIGHT_DONTKNOW
);
907 FontWidth
FontSubstConfiguration::getSubstWidth( const css::uno::Reference
< XNameAccess
>& rFont
,
908 const OUString
& rType
)
913 Any aAny
= rFont
->getByName( rType
);
914 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
916 if( !pLine
->isEmpty() )
918 for( width
=std::size(pWidthNames
)-1; width
>= 0; width
-- )
919 if( pLine
->equalsIgnoreAsciiCaseAscii( pWidthNames
[width
].pName
) )
922 SAL_WARN_IF( width
< 0, "unotools.config", "Error: invalid width " << *pLine
);
925 catch (const NoSuchElementException
&)
928 catch (const WrappedTargetException
&)
931 return static_cast<FontWidth
>( width
>= 0 ? pWidthNames
[width
].nEnum
: WIDTH_DONTKNOW
);
935 ImplFontAttrs
FontSubstConfiguration::getSubstType( const css::uno::Reference
< XNameAccess
>& rFont
,
936 const OUString
& rType
)
941 Any aAny
= rFont
->getByName( rType
);
942 auto pLine
= o3tl::tryAccess
<OUString
>(aAny
);
944 return ImplFontAttrs::None
;
945 if( pLine
->isEmpty() )
946 return ImplFontAttrs::None
;
947 sal_Int32 nIndex
= 0;
948 while( nIndex
!= -1 )
950 OUString
aToken( pLine
->getToken( 0, ',', nIndex
) );
951 for( int k
= 0; k
< 32; k
++ )
952 if( aToken
.equalsIgnoreAsciiCaseAscii( pAttribNames
[k
] ) )
954 type
|= sal_uInt32(1) << k
;
958 assert(((type
& ~o3tl::typed_flags
<ImplFontAttrs
>::mask
) == 0) && "invalid font attributes");
960 catch (const NoSuchElementException
&)
963 catch (const WrappedTargetException
&)
967 return static_cast<ImplFontAttrs
>(type
);
970 void FontSubstConfiguration::readLocaleSubst( const OUString
& rBcp47
) const
972 std::unordered_map
< OUString
, LocaleSubst
>::const_iterator it
= m_aSubst
.find( rBcp47
);
973 if( it
== m_aSubst
.end() )
976 if( it
->second
.bConfigRead
)
979 it
->second
.bConfigRead
= true;
980 Reference
< XNameAccess
> xNode
;
983 Any aAny
= m_xConfigAccess
->getByName( it
->second
.aConfigLocaleString
);
986 catch (const NoSuchElementException
&)
989 catch (const WrappedTargetException
&)
995 const Sequence
< OUString
> aFonts
= xNode
->getElementNames();
996 int nFonts
= aFonts
.getLength();
997 // improve performance, heap fragmentation
998 it
->second
.aSubstAttributes
.reserve( nFonts
);
1000 // strings for subst retrieval, construct only once
1001 static constexpr OUStringLiteral
aSubstFontsStr ( u
"SubstFonts" );
1002 static constexpr OUStringLiteral
aSubstFontsMSStr( u
"SubstFontsMS" );
1003 static constexpr OUStringLiteral
aSubstWeightStr ( u
"FontWeight" );
1004 static constexpr OUStringLiteral
aSubstWidthStr ( u
"FontWidth" );
1005 static constexpr OUStringLiteral
aSubstTypeStr ( u
"FontType" );
1006 for( const OUString
& rFontName
: aFonts
)
1008 Reference
< XNameAccess
> xFont
;
1011 Any aAny
= xNode
->getByName( rFontName
);
1014 catch (const NoSuchElementException
&)
1017 catch (const WrappedTargetException
&)
1022 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName
);
1027 // read subst attributes from config
1028 aAttr
.Name
= rFontName
;
1029 fillSubstVector( xFont
, aSubstFontsStr
, aAttr
.Substitutions
);
1030 fillSubstVector( xFont
, aSubstFontsMSStr
, aAttr
.MSSubstitutions
);
1031 aAttr
.Weight
= getSubstWeight( xFont
, aSubstWeightStr
);
1032 aAttr
.Width
= getSubstWidth( xFont
, aSubstWidthStr
);
1033 aAttr
.Type
= getSubstType( xFont
, aSubstTypeStr
);
1035 // finally insert this entry
1036 it
->second
.aSubstAttributes
.push_back( aAttr
);
1038 std::sort( it
->second
.aSubstAttributes
.begin(), it
->second
.aSubstAttributes
.end(), StrictStringSort() );
1041 const FontNameAttr
* FontSubstConfiguration::getSubstInfo( const OUString
& rFontName
) const
1043 if( rFontName
.isEmpty() )
1046 // search if a (language dep.) replacement table for the given font exists
1047 // fallback is english
1048 OUString
aSearchFont( rFontName
.toAsciiLowerCase() );
1049 FontNameAttr aSearchAttr
;
1050 aSearchAttr
.Name
= aSearchFont
;
1052 ::std::vector
< OUString
> aFallbacks( maLanguageTag
.getFallbackStrings( true));
1053 if (maLanguageTag
.getLanguage() != "en")
1054 aFallbacks
.emplace_back("en");
1056 for (const auto& rFallback
: aFallbacks
)
1058 std::unordered_map
< OUString
, LocaleSubst
>::const_iterator lang
= m_aSubst
.find( rFallback
);
1059 if( lang
!= m_aSubst
.end() )
1061 if( ! lang
->second
.bConfigRead
)
1062 readLocaleSubst( rFallback
);
1063 // try to find an exact match
1064 // because the list is sorted this will also find fontnames of the form searchfontname*
1065 std::vector
< FontNameAttr
>::const_iterator it
= ::std::lower_bound( lang
->second
.aSubstAttributes
.begin(), lang
->second
.aSubstAttributes
.end(), aSearchAttr
, StrictStringSort() );
1066 if( it
!= lang
->second
.aSubstAttributes
.end())
1068 const FontNameAttr
& rFoundAttr
= *it
;
1069 // a search for "abcblack" may match with an entry for "abc"
1070 // the reverse is not a good idea (e.g. #i112731# alba->albani)
1071 if( rFoundAttr
.Name
.getLength() <= aSearchFont
.getLength() )
1072 if( aSearchFont
.startsWith( rFoundAttr
.Name
))
1080 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */