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 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"
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 const char* const aImplKillLeadingList
[] =
394 const char* const aImplKillTrailingList
[] =
407 // Scripts, for compatibility with older versions
424 // Old Printer Fontnames
444 const char* const aImplKillTrailingWithExceptionsList
[] =
446 "ce", "monospace", "oldface", nullptr,
447 "ps", "caps", nullptr,
453 struct ImplFontAttrWeightSearchData
461 ImplFontAttrWeightSearchData
const aImplWeightAttrSearchList
[] =
463 // the attribute names are ordered by "first match wins"
464 // e.g. "semilight" should wins over "semi"
465 { "extrablack", WEIGHT_BLACK
},
466 { "ultrablack", WEIGHT_BLACK
},
467 { "ultrabold", WEIGHT_ULTRABOLD
},
468 { "semibold", WEIGHT_SEMIBOLD
},
469 { "semilight", WEIGHT_SEMILIGHT
},
470 { "semi", WEIGHT_SEMIBOLD
},
471 { "demi", WEIGHT_SEMIBOLD
},
472 { "black", WEIGHT_BLACK
},
473 { "bold", WEIGHT_BOLD
},
474 { "heavy", WEIGHT_BLACK
},
475 { "ultralight", WEIGHT_ULTRALIGHT
},
476 { "light", WEIGHT_LIGHT
},
477 { "medium", WEIGHT_MEDIUM
},
478 { nullptr, WEIGHT_DONTKNOW
},
483 struct ImplFontAttrWidthSearchData
491 ImplFontAttrWidthSearchData
const aImplWidthAttrSearchList
[] =
493 { "narrow", WIDTH_CONDENSED
},
494 { "semicondensed", WIDTH_SEMI_CONDENSED
},
495 { "ultracondensed", WIDTH_ULTRA_CONDENSED
},
496 { "semiexpanded", WIDTH_SEMI_EXPANDED
},
497 { "ultraexpanded", WIDTH_ULTRA_EXPANDED
},
498 { "expanded", WIDTH_EXPANDED
},
499 { "wide", WIDTH_ULTRA_EXPANDED
},
500 { "condensed", WIDTH_CONDENSED
},
501 { "cond", WIDTH_CONDENSED
},
502 { "cn", WIDTH_CONDENSED
},
503 { nullptr, WIDTH_DONTKNOW
},
508 struct ImplFontAttrTypeSearchData
511 ImplFontAttrs mnType
;
516 ImplFontAttrTypeSearchData
const aImplTypeAttrSearchList
[] =
518 { "monotype", ImplFontAttrs::None
},
519 { "linotype", ImplFontAttrs::None
},
520 { "titling", ImplFontAttrs::Titling
},
521 { "captitals", ImplFontAttrs::Capitals
},
522 { "captital", ImplFontAttrs::Capitals
},
523 { "caps", ImplFontAttrs::Capitals
},
524 { "italic", ImplFontAttrs::Italic
},
525 { "oblique", ImplFontAttrs::Italic
},
526 { "rounded", ImplFontAttrs::Rounded
},
527 { "outline", ImplFontAttrs::Outline
},
528 { "shadow", ImplFontAttrs::Shadow
},
529 { "handwriting", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
530 { "hand", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
531 { "signet", ImplFontAttrs::Handwriting
| ImplFontAttrs::Script
},
532 { "script", ImplFontAttrs::BrushScript
| ImplFontAttrs::Script
},
533 { "calligraphy", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
534 { "chancery", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
535 { "corsiva", ImplFontAttrs::Chancery
| ImplFontAttrs::Script
},
536 { "gothic", ImplFontAttrs::SansSerif
| ImplFontAttrs::Gothic
},
537 { "schoolbook", ImplFontAttrs::Serif
| ImplFontAttrs::Schoolbook
},
538 { "schlbk", ImplFontAttrs::Serif
| ImplFontAttrs::Schoolbook
},
539 { "typewriter", ImplFontAttrs::Typewriter
| ImplFontAttrs::Fixed
},
540 { "lineprinter", ImplFontAttrs::Typewriter
| ImplFontAttrs::Fixed
},
541 { "monospaced", ImplFontAttrs::Fixed
},
542 { "monospace", ImplFontAttrs::Fixed
},
543 { "mono", ImplFontAttrs::Fixed
},
544 { "fixed", ImplFontAttrs::Fixed
},
545 { "sansserif", ImplFontAttrs::SansSerif
},
546 { "sans", ImplFontAttrs::SansSerif
},
547 { "swiss", ImplFontAttrs::SansSerif
},
548 { "serif", ImplFontAttrs::Serif
},
549 { "bright", ImplFontAttrs::Serif
},
550 { "symbols", ImplFontAttrs::Symbol
},
551 { "symbol", ImplFontAttrs::Symbol
},
552 { "dingbats", ImplFontAttrs::Symbol
},
553 { "dings", ImplFontAttrs::Symbol
},
554 { "ding", ImplFontAttrs::Symbol
},
555 { "bats", ImplFontAttrs::Symbol
},
556 { "math", ImplFontAttrs::Symbol
},
557 { "oldstyle", ImplFontAttrs::OtherStyle
},
558 { "oldface", ImplFontAttrs::OtherStyle
},
559 { "old", ImplFontAttrs::OtherStyle
},
560 { "new", ImplFontAttrs::None
},
561 { "modern", ImplFontAttrs::None
},
562 { "lucida", ImplFontAttrs::None
},
563 { "regular", ImplFontAttrs::None
},
564 { "extended", ImplFontAttrs::None
},
565 { "extra", ImplFontAttrs::OtherStyle
},
566 { "ext", ImplFontAttrs::None
},
567 { "scalable", ImplFontAttrs::None
},
568 { "scale", ImplFontAttrs::None
},
569 { "nimbus", ImplFontAttrs::None
},
570 { "adobe", ImplFontAttrs::None
},
571 { "itc", ImplFontAttrs::None
},
572 { "amt", ImplFontAttrs::None
},
573 { "mt", ImplFontAttrs::None
},
574 { "ms", ImplFontAttrs::None
},
575 { "cpi", ImplFontAttrs::None
},
576 { "no", ImplFontAttrs::None
},
577 { nullptr, ImplFontAttrs::None
},
580 static bool ImplKillLeading( OUString
& rName
, const char* const* ppStr
)
582 for(; *ppStr
; ++ppStr
)
584 const char* pStr
= *ppStr
;
585 const sal_Unicode
* pNameStr
= rName
.getStr();
586 while ( (*pNameStr
== static_cast<sal_Unicode
>(static_cast<unsigned char>(*pStr
))) && *pStr
)
593 sal_Int32 nLen
= static_cast<sal_Int32
>(pNameStr
- rName
.getStr());
594 rName
= rName
.copy(nLen
);
599 // special case for Baekmuk
600 // TODO: allow non-ASCII KillLeading list
601 const sal_Unicode
* pNameStr
= rName
.getStr();
602 if( (pNameStr
[0]==0xBC31) && (pNameStr
[1]==0xBC35) )
604 sal_Int32 nLen
= (pNameStr
[2]==0x0020) ? 3 : 2;
605 rName
= rName
.copy(nLen
);
612 static sal_Int32
ImplIsTrailing( const OUString
& rName
, const char* pStr
)
614 sal_Int32 nStrLen
= static_cast<sal_Int32
>(strlen( pStr
));
615 if( nStrLen
>= rName
.getLength() )
618 const sal_Unicode
* pEndName
= rName
.getStr() + rName
.getLength();
619 const sal_Unicode
* pNameStr
= pEndName
- nStrLen
;
620 do if( *(pNameStr
++) != *(pStr
++) )
627 static bool ImplKillTrailing( OUString
& rName
, const char* const* ppStr
)
629 for(; *ppStr
; ++ppStr
)
631 sal_Int32 nTrailLen
= ImplIsTrailing( rName
, *ppStr
);
634 rName
= rName
.copy(0, rName
.getLength() - nTrailLen
);
642 static bool ImplKillTrailingWithExceptions( OUString
& rName
, const char* const* ppStr
)
644 for(; *ppStr
; ++ppStr
)
646 sal_Int32 nTrailLen
= ImplIsTrailing( rName
, *ppStr
);
649 // check string match against string exceptions
651 if( ImplIsTrailing( rName
, *ppStr
) )
654 rName
= rName
.copy(0, rName
.getLength() - nTrailLen
);
659 // skip exception strings
667 static bool ImplFindAndErase( OUString
& rName
, const char* pStr
)
669 sal_Int32 nLen
= static_cast<sal_Int32
>(strlen(pStr
));
670 sal_Int32 nPos
= rName
.indexOfAsciiL(pStr
, nLen
);
674 OUStringBuffer
sBuff(rName
);
675 sBuff
.remove(nPos
, nLen
);
676 rName
= sBuff
.makeStringAndClear();
680 void FontSubstConfiguration::getMapName( const OUString
& rOrgName
, OUString
& rShortName
,
681 OUString
& rFamilyName
, FontWeight
& rWeight
,
682 FontWidth
& rWidth
, ImplFontAttrs
& rType
)
684 rShortName
= rOrgName
;
686 // TODO: get rid of the crazy O(N*strlen) searches below
687 // they should be possible in O(strlen)
689 // Kill leading vendor names and other unimportant data
690 ImplKillLeading( rShortName
, aImplKillLeadingList
);
692 // Kill trailing vendor names and other unimportant data
693 ImplKillTrailing( rShortName
, aImplKillTrailingList
);
694 ImplKillTrailingWithExceptions( rShortName
, aImplKillTrailingWithExceptionsList
);
696 rFamilyName
= rShortName
;
698 // Kill attributes from the name and update the data
700 const ImplFontAttrWeightSearchData
* pWeightList
= aImplWeightAttrSearchList
;
701 while ( pWeightList
->mpStr
)
703 if ( ImplFindAndErase( rFamilyName
, pWeightList
->mpStr
) )
705 if ( (rWeight
== WEIGHT_DONTKNOW
) || (rWeight
== WEIGHT_NORMAL
) )
706 rWeight
= pWeightList
->meWeight
;
713 const ImplFontAttrWidthSearchData
* pWidthList
= aImplWidthAttrSearchList
;
714 while ( pWidthList
->mpStr
)
716 if ( ImplFindAndErase( rFamilyName
, pWidthList
->mpStr
) )
718 if ( (rWidth
== WIDTH_DONTKNOW
) || (rWidth
== WIDTH_NORMAL
) )
719 rWidth
= pWidthList
->meWidth
;
726 rType
= ImplFontAttrs::None
;
727 const ImplFontAttrTypeSearchData
* pTypeList
= aImplTypeAttrSearchList
;
728 while ( pTypeList
->mpStr
)
730 if ( ImplFindAndErase( rFamilyName
, pTypeList
->mpStr
) )
731 rType
|= pTypeList
->mnType
;
736 // TODO: also remove localized and fullwidth digits
738 OUStringBuffer
sBuff(rFamilyName
);
739 while ( i
< sBuff
.getLength() )
741 sal_Unicode c
= sBuff
[ i
];
742 if ( (c
>= 0x0030) && (c
<= 0x0039) )
751 struct StrictStringSort
753 bool operator()( const FontNameAttr
& rLeft
, const FontNameAttr
& rRight
)
754 { return rLeft
.Name
.compareTo( rRight
.Name
) < 0; }
759 // The entries in this table must match the bits in the ImplFontAttrs enum.
761 const char* const pAttribNames
[] =
807 const enum_convert pWeightNames
[] =
809 { "normal", WEIGHT_NORMAL
},
810 { "medium", WEIGHT_MEDIUM
},
811 { "bold", WEIGHT_BOLD
},
812 { "black", WEIGHT_BLACK
},
813 { "semibold", WEIGHT_SEMIBOLD
},
814 { "light", WEIGHT_LIGHT
},
815 { "semilight", WEIGHT_SEMILIGHT
},
816 { "ultrabold", WEIGHT_ULTRABOLD
},
817 { "semi", WEIGHT_SEMIBOLD
},
818 { "demi", WEIGHT_SEMIBOLD
},
819 { "heavy", WEIGHT_BLACK
},
820 { "unknown", WEIGHT_DONTKNOW
},
821 { "thin", WEIGHT_THIN
},
822 { "ultralight", WEIGHT_ULTRALIGHT
}
825 const enum_convert pWidthNames
[] =
827 { "normal", WIDTH_NORMAL
},
828 { "condensed", WIDTH_CONDENSED
},
829 { "expanded", WIDTH_EXPANDED
},
830 { "unknown", WIDTH_DONTKNOW
},
831 { "ultracondensed", WIDTH_ULTRA_CONDENSED
},
832 { "extracondensed", WIDTH_EXTRA_CONDENSED
},
833 { "semicondensed", WIDTH_SEMI_CONDENSED
},
834 { "semiexpanded", WIDTH_SEMI_EXPANDED
},
835 { "extraexpanded", WIDTH_EXTRA_EXPANDED
},
836 { "ultraexpanded", WIDTH_ULTRA_EXPANDED
}
839 void FontSubstConfiguration::fillSubstVector( const css::uno::Reference
< XNameAccess
>& rFont
,
840 const OUString
& rType
,
841 std::vector
< OUString
>& rSubstVector
) const
845 Any aAny
= rFont
->getByName( rType
);
846 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
848 sal_Int32 nLength
= pLine
->getLength();
851 const sal_Unicode
* pStr
= pLine
->getStr();
852 sal_Int32 nTokens
= 0;
859 rSubstVector
.clear();
860 // optimize performance, heap fragmentation
861 rSubstVector
.reserve( nTokens
);
862 sal_Int32 nIndex
= 0;
863 while( nIndex
!= -1 )
865 OUString
aSubst( pLine
->getToken( 0, ';', nIndex
) );
866 if( !aSubst
.isEmpty() )
868 auto itPair
= maSubstHash
.insert( aSubst
);
870 aSubst
= *itPair
.first
;
871 rSubstVector
.push_back( aSubst
);
877 catch (const NoSuchElementException
&)
880 catch (const WrappedTargetException
&)
885 FontWeight
FontSubstConfiguration::getSubstWeight( const css::uno::Reference
< XNameAccess
>& rFont
,
886 const OUString
& rType
) const
891 Any aAny
= rFont
->getByName( rType
);
892 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
894 if( !pLine
->isEmpty() )
896 for( weight
=SAL_N_ELEMENTS(pWeightNames
)-1; weight
>= 0; weight
-- )
897 if( pLine
->equalsIgnoreAsciiCaseAscii( pWeightNames
[weight
].pName
) )
900 SAL_WARN_IF(weight
< 0, "unotools.config", "Error: invalid weight " << *pLine
);
903 catch (const NoSuchElementException
&)
906 catch (const WrappedTargetException
&)
909 return static_cast<FontWeight
>( weight
>= 0 ? pWeightNames
[weight
].nEnum
: WEIGHT_DONTKNOW
);
912 FontWidth
FontSubstConfiguration::getSubstWidth( const css::uno::Reference
< XNameAccess
>& rFont
,
913 const OUString
& rType
) const
918 Any aAny
= rFont
->getByName( rType
);
919 if( auto pLine
= o3tl::tryAccess
<OUString
>(aAny
) )
921 if( !pLine
->isEmpty() )
923 for( width
=SAL_N_ELEMENTS(pWidthNames
)-1; width
>= 0; width
-- )
924 if( pLine
->equalsIgnoreAsciiCaseAscii( pWidthNames
[width
].pName
) )
927 SAL_WARN_IF( width
< 0, "unotools.config", "Error: invalid width " << *pLine
);
930 catch (const NoSuchElementException
&)
933 catch (const WrappedTargetException
&)
936 return static_cast<FontWidth
>( width
>= 0 ? pWidthNames
[width
].nEnum
: WIDTH_DONTKNOW
);
939 ImplFontAttrs
FontSubstConfiguration::getSubstType( const css::uno::Reference
< XNameAccess
>& rFont
,
940 const OUString
& rType
) const
945 Any aAny
= rFont
->getByName( rType
);
946 auto pLine
= o3tl::tryAccess
<OUString
>(aAny
);
948 return ImplFontAttrs::None
;
949 if( pLine
->isEmpty() )
950 return ImplFontAttrs::None
;
951 sal_Int32 nIndex
= 0;
952 while( nIndex
!= -1 )
954 OUString
aToken( pLine
->getToken( 0, ',', nIndex
) );
955 for( int k
= 0; k
< 32; k
++ )
956 if( aToken
.equalsIgnoreAsciiCaseAscii( pAttribNames
[k
] ) )
958 type
|= sal_uLong(1) << k
;
962 assert(((type
& ~o3tl::typed_flags
<ImplFontAttrs
>::mask
) == 0) && "invalid font attributes");
964 catch (const NoSuchElementException
&)
967 catch (const WrappedTargetException
&)
971 return static_cast<ImplFontAttrs
>(type
);
974 void FontSubstConfiguration::readLocaleSubst( const OUString
& rBcp47
) const
976 std::unordered_map
< OUString
, LocaleSubst
>::const_iterator it
= m_aSubst
.find( rBcp47
);
977 if( it
== m_aSubst
.end() )
980 if( it
->second
.bConfigRead
)
983 it
->second
.bConfigRead
= true;
984 Reference
< XNameAccess
> xNode
;
987 Any aAny
= m_xConfigAccess
->getByName( it
->second
.aConfigLocaleString
);
990 catch (const NoSuchElementException
&)
993 catch (const WrappedTargetException
&)
999 const Sequence
< OUString
> aFonts
= xNode
->getElementNames();
1000 int nFonts
= aFonts
.getLength();
1001 // improve performance, heap fragmentation
1002 it
->second
.aSubstAttributes
.reserve( nFonts
);
1004 // strings for subst retrieval, construct only once
1005 OUString
const aSubstFontsStr ( "SubstFonts" );
1006 OUString
const aSubstFontsMSStr ( "SubstFontsMS" );
1007 OUString
const aSubstWeightStr ( "FontWeight" );
1008 OUString
const aSubstWidthStr ( "FontWidth" );
1009 OUString
const aSubstTypeStr ( "FontType" );
1010 for( const OUString
& rFontName
: aFonts
)
1012 Reference
< XNameAccess
> xFont
;
1015 Any aAny
= xNode
->getByName( rFontName
);
1018 catch (const NoSuchElementException
&)
1021 catch (const WrappedTargetException
&)
1026 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName
);
1031 // read subst attributes from config
1032 aAttr
.Name
= rFontName
;
1033 fillSubstVector( xFont
, aSubstFontsStr
, aAttr
.Substitutions
);
1034 fillSubstVector( xFont
, aSubstFontsMSStr
, aAttr
.MSSubstitutions
);
1035 aAttr
.Weight
= getSubstWeight( xFont
, aSubstWeightStr
);
1036 aAttr
.Width
= getSubstWidth( xFont
, aSubstWidthStr
);
1037 aAttr
.Type
= getSubstType( xFont
, aSubstTypeStr
);
1039 // finally insert this entry
1040 it
->second
.aSubstAttributes
.push_back( aAttr
);
1042 std::sort( it
->second
.aSubstAttributes
.begin(), it
->second
.aSubstAttributes
.end(), StrictStringSort() );
1045 const FontNameAttr
* FontSubstConfiguration::getSubstInfo( const OUString
& rFontName
) const
1047 if( rFontName
.isEmpty() )
1050 // search if a (language dep.) replacement table for the given font exists
1051 // fallback is english
1052 OUString
aSearchFont( rFontName
.toAsciiLowerCase() );
1053 FontNameAttr aSearchAttr
;
1054 aSearchAttr
.Name
= aSearchFont
;
1056 LanguageTag
aLanguageTag("en");
1058 if( aLanguageTag
.isSystemLocale() )
1059 aLanguageTag
= SvtSysLocale().GetUILanguageTag();
1061 ::std::vector
< OUString
> aFallbacks( aLanguageTag
.getFallbackStrings( true));
1062 if (aLanguageTag
.getLanguage() != "en")
1063 aFallbacks
.emplace_back("en");
1065 for (const auto& rFallback
: aFallbacks
)
1067 std::unordered_map
< OUString
, LocaleSubst
>::const_iterator lang
= m_aSubst
.find( rFallback
);
1068 if( lang
!= m_aSubst
.end() )
1070 if( ! lang
->second
.bConfigRead
)
1071 readLocaleSubst( rFallback
);
1072 // try to find an exact match
1073 // because the list is sorted this will also find fontnames of the form searchfontname*
1074 std::vector
< FontNameAttr
>::const_iterator it
= ::std::lower_bound( lang
->second
.aSubstAttributes
.begin(), lang
->second
.aSubstAttributes
.end(), aSearchAttr
, StrictStringSort() );
1075 if( it
!= lang
->second
.aSubstAttributes
.end())
1077 const FontNameAttr
& rFoundAttr
= *it
;
1078 // a search for "abcblack" may match with an entry for "abc"
1079 // the reverse is not a good idea (e.g. #i112731# alba->albani)
1080 if( rFoundAttr
.Name
.getLength() <= aSearchFont
.getLength() )
1081 if( aSearchFont
.startsWith( rFoundAttr
.Name
))
1089 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */