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/processfactory.hxx>
27 #include <com/sun/star/uno/Any.hxx>
28 #include <com/sun/star/uno/Sequence.hxx>
29 #include <com/sun/star/configuration/theDefaultProvider.hpp>
30 #include <com/sun/star/container/XNameAccess.hpp>
31 #include <comphelper/propertysequence.hxx>
32 #include <unotools/syslocale.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <rtl/instance.hxx>
35 #include <osl/diagnose.h>
36 #include <sal/macros.h>
37 #include <sal/log.hxx>
43 using namespace com::sun::star::uno
;
44 using namespace com::sun::star::lang
;
45 using namespace com::sun::star::beans
;
46 using namespace com::sun::star::container
;
47 using namespace com::sun::star::configuration
;
50 * DefaultFontConfiguration
53 static const char* getKeyType( DefaultFontType nKeyType
)
57 case DefaultFontType::CJK_DISPLAY
: return "CJK_DISPLAY";
58 case DefaultFontType::CJK_HEADING
: return "CJK_HEADING";
59 case DefaultFontType::CJK_PRESENTATION
: return "CJK_PRESENTATION";
60 case DefaultFontType::CJK_SPREADSHEET
: return "CJK_SPREADSHEET";
61 case DefaultFontType::CJK_TEXT
: return "CJK_TEXT";
62 case DefaultFontType::CTL_DISPLAY
: return "CTL_DISPLAY";
63 case DefaultFontType::CTL_HEADING
: return "CTL_HEADING";
64 case DefaultFontType::CTL_PRESENTATION
: return "CTL_PRESENTATION";
65 case DefaultFontType::CTL_SPREADSHEET
: return "CTL_SPREADSHEET";
66 case DefaultFontType::CTL_TEXT
: return "CTL_TEXT";
67 case DefaultFontType::FIXED
: return "FIXED";
68 case DefaultFontType::LATIN_DISPLAY
: return "LATIN_DISPLAY";
69 case DefaultFontType::LATIN_FIXED
: return "LATIN_FIXED";
70 case DefaultFontType::LATIN_HEADING
: return "LATIN_HEADING";
71 case DefaultFontType::LATIN_PRESENTATION
: return "LATIN_PRESENTATION";
72 case DefaultFontType::LATIN_SPREADSHEET
: return "LATIN_SPREADSHEET";
73 case DefaultFontType::LATIN_TEXT
: return "LATIN_TEXT";
74 case DefaultFontType::SANS
: return "SANS";
75 case DefaultFontType::SANS_UNICODE
: return "SANS_UNICODE";
76 case DefaultFontType::SERIF
: return "SERIF";
77 case DefaultFontType::SYMBOL
: return "SYMBOL";
78 case DefaultFontType::UI_FIXED
: return "UI_FIXED";
79 case DefaultFontType::UI_SANS
: return "UI_SANS";
81 OSL_FAIL( "unmatched type" );
88 class theDefaultFontConfiguration
89 : public rtl::Static
<DefaultFontConfiguration
,
90 theDefaultFontConfiguration
>
95 DefaultFontConfiguration
& DefaultFontConfiguration::get()
97 return theDefaultFontConfiguration::get();
100 DefaultFontConfiguration::DefaultFontConfiguration()
102 if (utl::ConfigManager::IsFuzzing())
104 // create configuration hierarchical access name
107 // get service provider
108 m_xConfigProvider
= theDefaultProvider::get(comphelper::getProcessComponentContext());
109 Sequence
<Any
> aArgs(comphelper::InitAnyPropertySequence(
111 {"nodepath", Any(OUString( "/org.openoffice.VCL/DefaultFonts" ))}
114 Reference
< XNameAccess
>(
115 m_xConfigProvider
->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
118 if( m_xConfigAccess
.is() )
120 const Sequence
< OUString
> aLocales
= m_xConfigAccess
->getElementNames();
121 // fill config hash with empty interfaces
122 for( const OUString
& rLocaleString
: aLocales
)
124 // Feed through LanguageTag for casing.
125 OUString
aLoc( LanguageTag( rLocaleString
, true).getBcp47( false));
126 m_aConfig
[ aLoc
] = LocaleAccess();
127 m_aConfig
[ aLoc
].aConfigLocaleString
= rLocaleString
;
131 catch (const Exception
&)
133 // configuration is awry
134 m_xConfigProvider
.clear();
135 m_xConfigAccess
.clear();
137 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider
.is()
138 << ", config access: " << m_xConfigAccess
.is());
141 DefaultFontConfiguration::~DefaultFontConfiguration()
146 m_xConfigAccess
.clear();
147 // release config provider
148 m_xConfigProvider
.clear();
151 OUString
DefaultFontConfiguration::tryLocale( const OUString
& rBcp47
, const OUString
& rType
) const
155 std::unordered_map
< OUString
, LocaleAccess
>::const_iterator it
= m_aConfig
.find( rBcp47
);
156 if( it
!= m_aConfig
.end() )
158 if( !it
->second
.xAccess
.is() )
162 Reference
< XNameAccess
> xNode
;
163 if ( m_xConfigAccess
->hasByName( it
->second
.aConfigLocaleString
) )
165 Any aAny
= m_xConfigAccess
->getByName( it
->second
.aConfigLocaleString
);
167 it
->second
.xAccess
= xNode
;
170 catch (const NoSuchElementException
&)
173 catch (const WrappedTargetException
&)
177 if( it
->second
.xAccess
.is() )
181 if ( it
->second
.xAccess
->hasByName( rType
) )
183 Any aAny
= it
->second
.xAccess
->getByName( rType
);
187 catch (const NoSuchElementException
&)
190 catch (const WrappedTargetException
&)
199 OUString
DefaultFontConfiguration::getDefaultFont( const LanguageTag
& rLanguageTag
, DefaultFontType nType
) const
201 OUString aType
= OUString::createFromAscii( getKeyType( nType
) );
202 // Try the simple cases first without constructing fallbacks.
203 OUString aRet
= tryLocale( rLanguageTag
.getBcp47(), aType
);
206 if (rLanguageTag
.isIsoLocale())
208 if (!rLanguageTag
.getCountry().isEmpty())
210 aRet
= tryLocale( rLanguageTag
.getLanguage(), aType
);
215 ::std::vector
< OUString
> aFallbacks( rLanguageTag
.getFallbackStrings( false));
216 for (const auto& rFallback
: aFallbacks
)
218 aRet
= tryLocale( rFallback
, aType
);
226 aRet
= tryLocale( "en", aType
);
231 OUString
DefaultFontConfiguration::getUserInterfaceFont( const LanguageTag
& rLanguageTag
) const
233 LanguageTag
aLanguageTag( rLanguageTag
);
234 if( aLanguageTag
.isSystemLocale() )
235 aLanguageTag
= SvtSysLocale().GetUILanguageTag();
237 OUString aUIFont
= getDefaultFont( aLanguageTag
, DefaultFontType::UI_SANS
);
239 if( !aUIFont
.isEmpty() )
242 // fallback mechanism (either no configuration or no entry in configuration
244 #define FALLBACKFONT_UI_SANS "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"
245 #define FALLBACKFONT_UI_SANS_LATIN2 "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"
246 #define FALLBACKFONT_UI_SANS_ARABIC "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"
247 #define FALLBACKFONT_UI_SANS_THAI "OONaksit;Tahoma;Lucidasans;Arial Unicode MS"
248 #define FALLBACKFONT_UI_SANS_KOREAN "Noto Sans CJK KR;Noto Sans KR;Source Han Sans KR;NanumGothic;NanumBarunGothic;NanumBarunGothic YetHangul;KoPubWorld Dotum;Malgun Gothic;Apple SD Gothic Neo;Dotum;Gulim;Apple Gothic;UnDotum;Baekmuk Gulim;Arial Unicode MS;Lucida Sans Unicode;gnu-unifont;Andale Sans UI"
249 #define FALLBACKFONT_UI_SANS_CHINSIM "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;"
250 #define FALLBACKFONT_UI_SANS_CHINTRD "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;"
252 const OUString
aLanguage( aLanguageTag
.getLanguage());
254 // optimize font list for some locales, as long as Andale Sans UI does not support them
255 if( aLanguage
== "ar" || aLanguage
== "he" || aLanguage
== "iw" )
257 return FALLBACKFONT_UI_SANS_ARABIC
;
259 else if ( aLanguage
== "th" )
261 return FALLBACKFONT_UI_SANS_THAI
;
263 else if ( aLanguage
== "ko" )
265 return FALLBACKFONT_UI_SANS_KOREAN
;
267 else if( aLanguage
== "cs" ||
277 return FALLBACKFONT_UI_SANS_LATIN2
;
281 const Locale
& aLocale( aLanguageTag
.getLocale());
282 if (MsLangId::isTraditionalChinese(aLocale
))
283 return FALLBACKFONT_UI_SANS_CHINTRD
;
284 else if (MsLangId::isSimplifiedChinese(aLocale
))
285 return FALLBACKFONT_UI_SANS_CHINSIM
;
288 return FALLBACKFONT_UI_SANS
;
292 * FontSubstConfigItem::get
297 class theFontSubstConfiguration
298 : public rtl::Static
<FontSubstConfiguration
, theFontSubstConfiguration
>
303 FontSubstConfiguration
& FontSubstConfiguration::get()
305 return theFontSubstConfiguration::get();
309 * FontSubstConfigItem::FontSubstConfigItem
312 FontSubstConfiguration::FontSubstConfiguration() :
315 if (utl::ConfigManager::IsFuzzing())
319 // get service provider
320 Reference
< XComponentContext
> xContext( comphelper::getProcessComponentContext() );
321 // create configuration hierarchical access name
322 m_xConfigProvider
= theDefaultProvider::get( xContext
);
323 Sequence
<Any
> aArgs(comphelper::InitAnyPropertySequence(
325 {"nodepath", Any(OUString( "/org.openoffice.VCL/FontSubstitutions" ))}
328 Reference
< XNameAccess
>(
329 m_xConfigProvider
->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
332 if( m_xConfigAccess
.is() )
334 const Sequence
< OUString
> aLocales
= m_xConfigAccess
->getElementNames();
335 // fill config hash with empty interfaces
336 for( const OUString
& rLocaleString
: aLocales
)
338 // Feed through LanguageTag for casing.
339 OUString
aLoc( LanguageTag( rLocaleString
, true).getBcp47( false));
340 m_aSubst
[ aLoc
] = LocaleSubst();
341 m_aSubst
[ aLoc
].aConfigLocaleString
= rLocaleString
;
345 catch (const Exception
&)
347 // configuration is awry
348 m_xConfigProvider
.clear();
349 m_xConfigAccess
.clear();
351 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider
.is()
352 << ", config access: " << m_xConfigAccess
.is());
356 * FontSubstConfigItem::~FontSubstConfigItem
359 FontSubstConfiguration::~FontSubstConfiguration()
361 // release config access
362 m_xConfigAccess
.clear();
363 // release config provider
364 m_xConfigProvider
.clear();
368 * FontSubstConfigItem::getMapName
371 static const char* const aImplKillLeadingList
[] =
394 static const char* const aImplKillTrailingList
[] =
407 // Scripts, for compatibility with older versions
424 // Old Printer Fontnames
444 static const char* const aImplKillTrailingWithExceptionsList
[] =
446 "ce", "monospace", "oldface", nullptr,
447 "ps", "caps", nullptr,
451 struct ImplFontAttrWeightSearchData
454 FontWeight
const meWeight
;
457 static ImplFontAttrWeightSearchData
const aImplWeightAttrSearchList
[] =
459 // the attribute names are ordered by "first match wins"
460 // e.g. "semilight" should wins over "semi"
461 { "extrablack", WEIGHT_BLACK
},
462 { "ultrablack", WEIGHT_BLACK
},
463 { "ultrabold", WEIGHT_ULTRABOLD
},
464 { "semibold", WEIGHT_SEMIBOLD
},
465 { "semilight", WEIGHT_SEMILIGHT
},
466 { "semi", WEIGHT_SEMIBOLD
},
467 { "demi", WEIGHT_SEMIBOLD
},
468 { "black", WEIGHT_BLACK
},
469 { "bold", WEIGHT_BOLD
},
470 { "heavy", WEIGHT_BLACK
},
471 { "ultralight", WEIGHT_ULTRALIGHT
},
472 { "light", WEIGHT_LIGHT
},
473 { "medium", WEIGHT_MEDIUM
},
474 { nullptr, WEIGHT_DONTKNOW
},
477 struct ImplFontAttrWidthSearchData
480 FontWidth
const meWidth
;
483 static 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
},
498 struct ImplFontAttrTypeSearchData
501 ImplFontAttrs
const mnType
;
504 static ImplFontAttrTypeSearchData
const aImplTypeAttrSearchList
[] =
506 { "monotype", ImplFontAttrs::None
},
507 { "linotype", ImplFontAttrs::None
},
508 { "titling", ImplFontAttrs::Titling
},
509 { "captitals", ImplFontAttrs::Capitals
},
510 { "captital", ImplFontAttrs::Capitals
},
511 { "caps", ImplFontAttrs::Capitals
},
512 { "italic", ImplFontAttrs::Italic
},
513 { "oblique", ImplFontAttrs::Italic
},
514 { "rounded", ImplFontAttrs::Rounded
},
515 { "outline", ImplFontAttrs::Outline
},
516 { "shadow", ImplFontAttrs::Shadow
},
517 { "handwriting", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
518 { "hand", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
519 { "signet", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
520 { "script", ImplFontAttrs::BrushScript
| ImplFontAttrs::Script
},
521 { "calligraphy", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
522 { "chancery", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
523 { "corsiva", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
524 { "gothic", ImplFontAttrs::SansSerif
| ImplFontAttrs::Gothic
},
525 { "schoolbook", ImplFontAttrs::Serif
| ImplFontAttrs::Schoolbook
},
526 { "schlbk", ImplFontAttrs::Serif
| ImplFontAttrs::Schoolbook
},
527 { "typewriter", ImplFontAttrs::Typewriter
| ImplFontAttrs::Fixed
},
528 { "lineprinter", ImplFontAttrs::Typewriter
| ImplFontAttrs::Fixed
},
529 { "monospaced", ImplFontAttrs::Fixed
},
530 { "monospace", ImplFontAttrs::Fixed
},
531 { "mono", ImplFontAttrs::Fixed
},
532 { "fixed", ImplFontAttrs::Fixed
},
533 { "sansserif", ImplFontAttrs::SansSerif
},
534 { "sans", ImplFontAttrs::SansSerif
},
535 { "swiss", ImplFontAttrs::SansSerif
},
536 { "serif", ImplFontAttrs::Serif
},
537 { "bright", ImplFontAttrs::Serif
},
538 { "symbols", ImplFontAttrs::Symbol
},
539 { "symbol", ImplFontAttrs::Symbol
},
540 { "dingbats", ImplFontAttrs::Symbol
},
541 { "dings", ImplFontAttrs::Symbol
},
542 { "ding", ImplFontAttrs::Symbol
},
543 { "bats", ImplFontAttrs::Symbol
},
544 { "math", ImplFontAttrs::Symbol
},
545 { "oldstyle", ImplFontAttrs::OtherStyle
},
546 { "oldface", ImplFontAttrs::OtherStyle
},
547 { "old", ImplFontAttrs::OtherStyle
},
548 { "new", ImplFontAttrs::None
},
549 { "modern", ImplFontAttrs::None
},
550 { "lucida", ImplFontAttrs::None
},
551 { "regular", ImplFontAttrs::None
},
552 { "extended", ImplFontAttrs::None
},
553 { "extra", ImplFontAttrs::OtherStyle
},
554 { "ext", ImplFontAttrs::None
},
555 { "scalable", ImplFontAttrs::None
},
556 { "scale", ImplFontAttrs::None
},
557 { "nimbus", ImplFontAttrs::None
},
558 { "adobe", ImplFontAttrs::None
},
559 { "itc", ImplFontAttrs::None
},
560 { "amt", ImplFontAttrs::None
},
561 { "mt", ImplFontAttrs::None
},
562 { "ms", ImplFontAttrs::None
},
563 { "cpi", ImplFontAttrs::None
},
564 { "no", ImplFontAttrs::None
},
565 { nullptr, ImplFontAttrs::None
},
568 static bool ImplKillLeading( OUString
& rName
, const char* const* ppStr
)
570 for(; *ppStr
; ++ppStr
)
572 const char* pStr
= *ppStr
;
573 const sal_Unicode
* pNameStr
= rName
.getStr();
574 while ( (*pNameStr
== static_cast<sal_Unicode
>(static_cast<unsigned char>(*pStr
))) && *pStr
)
581 sal_Int32 nLen
= static_cast<sal_Int32
>(pNameStr
- rName
.getStr());
582 rName
= rName
.copy(nLen
);
587 // special case for Baekmuk
588 // TODO: allow non-ASCII KillLeading list
589 const sal_Unicode
* pNameStr
= rName
.getStr();
590 if( (pNameStr
[0]==0xBC31) && (pNameStr
[1]==0xBC35) )
592 sal_Int32 nLen
= (pNameStr
[2]==0x0020) ? 3 : 2;
593 rName
= rName
.copy(nLen
);
600 static sal_Int32
ImplIsTrailing( const OUString
& rName
, const char* pStr
)
602 sal_Int32 nStrLen
= static_cast<sal_Int32
>(strlen( pStr
));
603 if( nStrLen
>= rName
.getLength() )
606 const sal_Unicode
* pEndName
= rName
.getStr() + rName
.getLength();
607 const sal_Unicode
* pNameStr
= pEndName
- nStrLen
;
608 do if( *(pNameStr
++) != *(pStr
++) )
615 static bool ImplKillTrailing( OUString
& rName
, const char* const* ppStr
)
617 for(; *ppStr
; ++ppStr
)
619 sal_Int32 nTrailLen
= ImplIsTrailing( rName
, *ppStr
);
622 rName
= rName
.copy(0, rName
.getLength() - nTrailLen
);
630 static bool ImplKillTrailingWithExceptions( OUString
& rName
, const char* const* ppStr
)
632 for(; *ppStr
; ++ppStr
)
634 sal_Int32 nTrailLen
= ImplIsTrailing( rName
, *ppStr
);
637 // check string match against string exceptions
639 if( ImplIsTrailing( rName
, *ppStr
) )
642 rName
= rName
.copy(0, rName
.getLength() - nTrailLen
);
647 // skip exception strings
655 static bool ImplFindAndErase( OUString
& rName
, const char* pStr
)
657 sal_Int32 nLen
= static_cast<sal_Int32
>(strlen(pStr
));
658 sal_Int32 nPos
= rName
.indexOfAsciiL(pStr
, nLen
);
662 OUStringBuffer
sBuff(rName
);
663 sBuff
.remove(nPos
, nLen
);
664 rName
= sBuff
.makeStringAndClear();
668 void FontSubstConfiguration::getMapName( const OUString
& rOrgName
, OUString
& rShortName
,
669 OUString
& rFamilyName
, FontWeight
& rWeight
,
670 FontWidth
& rWidth
, ImplFontAttrs
& rType
)
672 rShortName
= rOrgName
;
674 // TODO: get rid of the crazy O(N*strlen) searches below
675 // they should be possible in O(strlen)
677 // Kill leading vendor names and other unimportant data
678 ImplKillLeading( rShortName
, aImplKillLeadingList
);
680 // Kill trailing vendor names and other unimportant data
681 ImplKillTrailing( rShortName
, aImplKillTrailingList
);
682 ImplKillTrailingWithExceptions( rShortName
, aImplKillTrailingWithExceptionsList
);
684 rFamilyName
= rShortName
;
686 // Kill attributes from the name and update the data
688 const ImplFontAttrWeightSearchData
* pWeightList
= aImplWeightAttrSearchList
;
689 while ( pWeightList
->mpStr
)
691 if ( ImplFindAndErase( rFamilyName
, pWeightList
->mpStr
) )
693 if ( (rWeight
== WEIGHT_DONTKNOW
) || (rWeight
== WEIGHT_NORMAL
) )
694 rWeight
= pWeightList
->meWeight
;
701 const ImplFontAttrWidthSearchData
* pWidthList
= aImplWidthAttrSearchList
;
702 while ( pWidthList
->mpStr
)
704 if ( ImplFindAndErase( rFamilyName
, pWidthList
->mpStr
) )
706 if ( (rWidth
== WIDTH_DONTKNOW
) || (rWidth
== WIDTH_NORMAL
) )
707 rWidth
= pWidthList
->meWidth
;
714 rType
= ImplFontAttrs::None
;
715 const ImplFontAttrTypeSearchData
* pTypeList
= aImplTypeAttrSearchList
;
716 while ( pTypeList
->mpStr
)
718 if ( ImplFindAndErase( rFamilyName
, pTypeList
->mpStr
) )
719 rType
|= pTypeList
->mnType
;
724 // TODO: also remove localized and fullwidth digits
726 OUStringBuffer
sBuff(rFamilyName
);
727 while ( i
< sBuff
.getLength() )
729 sal_Unicode c
= sBuff
[ i
];
730 if ( (c
>= 0x0030) && (c
<= 0x0039) )
737 struct StrictStringSort
739 bool operator()( const FontNameAttr
& rLeft
, const FontNameAttr
& rRight
)
740 { return rLeft
.Name
.compareTo( rRight
.Name
) < 0; }
743 // The entries in this table must match the bits in the ImplFontAttrs enum.
745 static const char* const pAttribNames
[] =
787 static const enum_convert pWeightNames
[] =
789 { "normal", WEIGHT_NORMAL
},
790 { "medium", WEIGHT_MEDIUM
},
791 { "bold", WEIGHT_BOLD
},
792 { "black", WEIGHT_BLACK
},
793 { "semibold", WEIGHT_SEMIBOLD
},
794 { "light", WEIGHT_LIGHT
},
795 { "semilight", WEIGHT_SEMILIGHT
},
796 { "ultrabold", WEIGHT_ULTRABOLD
},
797 { "semi", WEIGHT_SEMIBOLD
},
798 { "demi", WEIGHT_SEMIBOLD
},
799 { "heavy", WEIGHT_BLACK
},
800 { "unknown", WEIGHT_DONTKNOW
},
801 { "thin", WEIGHT_THIN
},
802 { "ultralight", WEIGHT_ULTRALIGHT
}
805 static const enum_convert pWidthNames
[] =
807 { "normal", WIDTH_NORMAL
},
808 { "condensed", WIDTH_CONDENSED
},
809 { "expanded", WIDTH_EXPANDED
},
810 { "unknown", WIDTH_DONTKNOW
},
811 { "ultracondensed", WIDTH_ULTRA_CONDENSED
},
812 { "extracondensed", WIDTH_EXTRA_CONDENSED
},
813 { "semicondensed", WIDTH_SEMI_CONDENSED
},
814 { "semiexpanded", WIDTH_SEMI_EXPANDED
},
815 { "extraexpanded", WIDTH_EXTRA_EXPANDED
},
816 { "ultraexpanded", WIDTH_ULTRA_EXPANDED
}
819 void FontSubstConfiguration::fillSubstVector( const css::uno::Reference
< XNameAccess
>& rFont
,
820 const OUString
& rType
,
821 std::vector
< OUString
>& rSubstVector
) const
825 Any aAny
= rFont
->getByName( rType
);
826 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
828 sal_Int32 nLength
= pLine
->getLength();
831 const sal_Unicode
* pStr
= pLine
->getStr();
832 sal_Int32 nTokens
= 0;
839 rSubstVector
.clear();
840 // optimize performance, heap fragmentation
841 rSubstVector
.reserve( nTokens
);
842 sal_Int32 nIndex
= 0;
843 while( nIndex
!= -1 )
845 OUString
aSubst( pLine
->getToken( 0, ';', nIndex
) );
846 if( !aSubst
.isEmpty() )
848 auto itPair
= maSubstHash
.insert( aSubst
);
850 aSubst
= *itPair
.first
;
851 rSubstVector
.push_back( aSubst
);
857 catch (const NoSuchElementException
&)
860 catch (const WrappedTargetException
&)
865 FontWeight
FontSubstConfiguration::getSubstWeight( const css::uno::Reference
< XNameAccess
>& rFont
,
866 const OUString
& rType
) const
871 Any aAny
= rFont
->getByName( rType
);
872 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
874 if( !pLine
->isEmpty() )
876 for( weight
=SAL_N_ELEMENTS(pWeightNames
)-1; weight
>= 0; weight
-- )
877 if( pLine
->equalsIgnoreAsciiCaseAscii( pWeightNames
[weight
].pName
) )
880 SAL_WARN_IF(weight
< 0, "unotools.config", "Error: invalid weight " << *pLine
);
883 catch (const NoSuchElementException
&)
886 catch (const WrappedTargetException
&)
889 return static_cast<FontWeight
>( weight
>= 0 ? pWeightNames
[weight
].nEnum
: WEIGHT_DONTKNOW
);
892 FontWidth
FontSubstConfiguration::getSubstWidth( const css::uno::Reference
< XNameAccess
>& rFont
,
893 const OUString
& rType
) const
898 Any aAny
= rFont
->getByName( rType
);
899 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
901 if( !pLine
->isEmpty() )
903 for( width
=SAL_N_ELEMENTS(pWidthNames
)-1; width
>= 0; width
-- )
904 if( pLine
->equalsIgnoreAsciiCaseAscii( pWidthNames
[width
].pName
) )
907 SAL_WARN_IF( width
< 0, "unotools.config", "Error: invalid width " << *pLine
);
910 catch (const NoSuchElementException
&)
913 catch (const WrappedTargetException
&)
916 return static_cast<FontWidth
>( width
>= 0 ? pWidthNames
[width
].nEnum
: WIDTH_DONTKNOW
);
919 ImplFontAttrs
FontSubstConfiguration::getSubstType( const css::uno::Reference
< XNameAccess
>& rFont
,
920 const OUString
& rType
) const
925 Any aAny
= rFont
->getByName( rType
);
926 auto pLine
= o3tl::tryAccess
<OUString
>(aAny
);
928 return ImplFontAttrs::None
;
929 if( pLine
->isEmpty() )
930 return ImplFontAttrs::None
;
931 sal_Int32 nIndex
= 0;
932 while( nIndex
!= -1 )
934 OUString
aToken( pLine
->getToken( 0, ',', nIndex
) );
935 for( int k
= 0; k
< 32; k
++ )
936 if( aToken
.equalsIgnoreAsciiCaseAscii( pAttribNames
[k
] ) )
938 type
|= sal_uLong(1) << k
;
942 assert(((type
& ~o3tl::typed_flags
<ImplFontAttrs
>::mask
) == 0) && "invalid font attributes");
944 catch (const NoSuchElementException
&)
947 catch (const WrappedTargetException
&)
951 return static_cast<ImplFontAttrs
>(type
);
954 void FontSubstConfiguration::readLocaleSubst( const OUString
& rBcp47
) const
956 std::unordered_map
< OUString
, LocaleSubst
>::const_iterator it
= m_aSubst
.find( rBcp47
);
957 if( it
!= m_aSubst
.end() )
959 if( ! it
->second
.bConfigRead
)
961 it
->second
.bConfigRead
= true;
962 Reference
< XNameAccess
> xNode
;
965 Any aAny
= m_xConfigAccess
->getByName( it
->second
.aConfigLocaleString
);
968 catch (const NoSuchElementException
&)
971 catch (const WrappedTargetException
&)
976 const Sequence
< OUString
> aFonts
= xNode
->getElementNames();
977 int nFonts
= aFonts
.getLength();
978 // improve performance, heap fragmentation
979 it
->second
.aSubstAttributes
.reserve( nFonts
);
981 // strings for subst retrieval, construct only once
982 OUString
const aSubstFontsStr ( "SubstFonts" );
983 OUString
const aSubstFontsMSStr ( "SubstFontsMS" );
984 OUString
const aSubstWeightStr ( "FontWeight" );
985 OUString
const aSubstWidthStr ( "FontWidth" );
986 OUString
const aSubstTypeStr ( "FontType" );
987 for( const OUString
& rFontName
: aFonts
)
989 Reference
< XNameAccess
> xFont
;
992 Any aAny
= xNode
->getByName( rFontName
);
995 catch (const NoSuchElementException
&)
998 catch (const WrappedTargetException
&)
1003 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName
);
1008 // read subst attributes from config
1009 aAttr
.Name
= rFontName
;
1010 fillSubstVector( xFont
, aSubstFontsStr
, aAttr
.Substitutions
);
1011 fillSubstVector( xFont
, aSubstFontsMSStr
, aAttr
.MSSubstitutions
);
1012 aAttr
.Weight
= getSubstWeight( xFont
, aSubstWeightStr
);
1013 aAttr
.Width
= getSubstWidth( xFont
, aSubstWidthStr
);
1014 aAttr
.Type
= getSubstType( xFont
, aSubstTypeStr
);
1016 // finally insert this entry
1017 it
->second
.aSubstAttributes
.push_back( aAttr
);
1019 std::sort( it
->second
.aSubstAttributes
.begin(), it
->second
.aSubstAttributes
.end(), StrictStringSort() );
1025 const FontNameAttr
* FontSubstConfiguration::getSubstInfo( const OUString
& rFontName
) const
1027 if( rFontName
.isEmpty() )
1030 // search if a (language dep.) replacement table for the given font exists
1031 // fallback is english
1032 OUString
aSearchFont( rFontName
.toAsciiLowerCase() );
1033 FontNameAttr aSearchAttr
;
1034 aSearchAttr
.Name
= aSearchFont
;
1036 LanguageTag
aLanguageTag("en");
1038 if( aLanguageTag
.isSystemLocale() )
1039 aLanguageTag
= SvtSysLocale().GetUILanguageTag();
1041 ::std::vector
< OUString
> aFallbacks( aLanguageTag
.getFallbackStrings( true));
1042 if (aLanguageTag
.getLanguage() != "en")
1043 aFallbacks
.emplace_back("en");
1045 for (const auto& rFallback
: aFallbacks
)
1047 std::unordered_map
< OUString
, LocaleSubst
>::const_iterator lang
= m_aSubst
.find( rFallback
);
1048 if( lang
!= m_aSubst
.end() )
1050 if( ! lang
->second
.bConfigRead
)
1051 readLocaleSubst( rFallback
);
1052 // try to find an exact match
1053 // because the list is sorted this will also find fontnames of the form searchfontname*
1054 std::vector
< FontNameAttr
>::const_iterator it
= ::std::lower_bound( lang
->second
.aSubstAttributes
.begin(), lang
->second
.aSubstAttributes
.end(), aSearchAttr
, StrictStringSort() );
1055 if( it
!= lang
->second
.aSubstAttributes
.end())
1057 const FontNameAttr
& rFoundAttr
= *it
;
1058 // a search for "abcblack" may match with an entry for "abc"
1059 // the reverse is not a good idea (e.g. #i112731# alba->albani)
1060 if( rFoundAttr
.Name
.getLength() <= aSearchFont
.getLength() )
1061 if( aSearchFont
.startsWith( rFoundAttr
.Name
))
1069 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */