nss: upgrade to release 3.73
[LibreOffice.git] / unotools / source / config / fontcfg.cxx
blobd94a7bfdc661d36454e0af9ddc4b0c1d4af5c67c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
39 #include <string.h>
40 #include <algorithm>
42 using namespace utl;
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 )
55 switch( 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";
80 default:
81 OSL_FAIL( "unmatched type" );
82 return "";
86 namespace
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())
103 return;
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" ))}
112 }));
113 m_xConfigAccess =
114 Reference< XNameAccess >(
115 m_xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
116 aArgs ),
117 UNO_QUERY );
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()
143 // release all nodes
144 m_aConfig.clear();
145 // release top node
146 m_xConfigAccess.clear();
147 // release config provider
148 m_xConfigProvider.clear();
151 OUString DefaultFontConfiguration::tryLocale( const OUString& rBcp47, const OUString& rType ) const
153 OUString aRet;
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 );
166 if( aAny >>= xNode )
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 );
184 aAny >>= aRet;
187 catch (const NoSuchElementException&)
190 catch (const WrappedTargetException&)
196 return aRet;
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 );
204 if (aRet.isEmpty())
206 if (rLanguageTag.isIsoLocale())
208 if (!rLanguageTag.getCountry().isEmpty())
210 aRet = tryLocale( rLanguageTag.getLanguage(), aType );
213 else
215 ::std::vector< OUString > aFallbacks( rLanguageTag.getFallbackStrings( false));
216 for (const auto& rFallback : aFallbacks)
218 aRet = tryLocale( rFallback, aType );
219 if (!aRet.isEmpty())
220 break;
224 if( aRet.isEmpty() )
226 aRet = tryLocale( "en", aType );
228 return aRet;
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() )
240 return aUIFont;
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" ||
268 aLanguage == "hu" ||
269 aLanguage == "pl" ||
270 aLanguage == "ro" ||
271 aLanguage == "rm" ||
272 aLanguage == "hr" ||
273 aLanguage == "sk" ||
274 aLanguage == "sl" ||
275 aLanguage == "sb")
277 return FALLBACKFONT_UI_SANS_LATIN2;
279 else
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
295 namespace
297 class theFontSubstConfiguration
298 : public rtl::Static<FontSubstConfiguration, theFontSubstConfiguration>
303 FontSubstConfiguration& FontSubstConfiguration::get()
305 return theFontSubstConfiguration::get();
309 * FontSubstConfigItem::FontSubstConfigItem
312 FontSubstConfiguration::FontSubstConfiguration() :
313 maSubstHash( 300 )
315 if (utl::ConfigManager::IsFuzzing())
316 return;
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" ))}
326 }));
327 m_xConfigAccess =
328 Reference< XNameAccess >(
329 m_xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
330 aArgs ),
331 UNO_QUERY );
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[] =
373 "microsoft",
374 "monotype",
375 "linotype",
376 "baekmuk",
377 "adobe",
378 "nimbus",
379 "zycjk",
380 "itc",
381 "sun",
382 "amt",
383 "ms",
384 "mt",
385 "cg",
386 "hg",
387 "fz",
388 "ipa",
389 "sazanami",
390 "kochi",
391 nullptr
394 const char* const aImplKillTrailingList[] =
396 "microsoft",
397 "monotype",
398 "linotype",
399 "adobe",
400 "nimbus",
401 "itc",
402 "sun",
403 "amt",
404 "ms",
405 "mt",
406 "clm",
407 // Scripts, for compatibility with older versions
408 "we",
409 "cyr",
410 "tur",
411 "wt",
412 "greek",
413 "wl",
414 // CJK extensions
415 "gb",
416 "big5",
417 "pro",
418 "z01",
419 "z02",
420 "z03",
421 "z13",
422 "b01",
423 "w3x12",
424 // Old Printer Fontnames
425 "5cpi",
426 "6cpi",
427 "7cpi",
428 "8cpi",
429 "9cpi",
430 "10cpi",
431 "11cpi",
432 "12cpi",
433 "13cpi",
434 "14cpi",
435 "15cpi",
436 "16cpi",
437 "18cpi",
438 "24cpi",
439 "scale",
440 "pc",
441 nullptr
444 const char* const aImplKillTrailingWithExceptionsList[] =
446 "ce", "monospace", "oldface", nullptr,
447 "ps", "caps", nullptr,
448 nullptr
451 namespace {
453 struct ImplFontAttrWeightSearchData
455 const char* mpStr;
456 FontWeight meWeight;
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 },
481 namespace {
483 struct ImplFontAttrWidthSearchData
485 const char* mpStr;
486 FontWidth meWidth;
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 },
506 namespace {
508 struct ImplFontAttrTypeSearchData
510 const char* mpStr;
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 )
588 pNameStr++;
589 pStr++;
591 if ( !*pStr )
593 sal_Int32 nLen = static_cast<sal_Int32>(pNameStr - rName.getStr());
594 rName = rName.copy(nLen);
595 return true;
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);
606 return true;
609 return false;
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() )
616 return 0;
618 const sal_Unicode* pEndName = rName.getStr() + rName.getLength();
619 const sal_Unicode* pNameStr = pEndName - nStrLen;
620 do if( *(pNameStr++) != *(pStr++) )
621 return 0;
622 while( *pStr );
624 return nStrLen;
627 static bool ImplKillTrailing( OUString& rName, const char* const* ppStr )
629 for(; *ppStr; ++ppStr )
631 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
632 if( nTrailLen )
634 rName = rName.copy(0, rName.getLength() - nTrailLen );
635 return true;
639 return false;
642 static bool ImplKillTrailingWithExceptions( OUString& rName, const char* const* ppStr )
644 for(; *ppStr; ++ppStr )
646 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
647 if( nTrailLen )
649 // check string match against string exceptions
650 while( *++ppStr )
651 if( ImplIsTrailing( rName, *ppStr ) )
652 return false;
654 rName = rName.copy(0, rName.getLength() - nTrailLen );
655 return true;
657 else
659 // skip exception strings
660 while( *++ppStr ) {}
664 return false;
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 );
671 if ( nPos < 0 )
672 return false;
674 OUStringBuffer sBuff(rName);
675 sBuff.remove(nPos, nLen);
676 rName = sBuff.makeStringAndClear();
677 return true;
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
699 // Weight
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;
707 break;
709 pWeightList++;
712 // Width
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;
720 break;
722 pWidthList++;
725 // Type
726 rType = ImplFontAttrs::None;
727 const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
728 while ( pTypeList->mpStr )
730 if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
731 rType |= pTypeList->mnType;
732 pTypeList++;
735 // Remove numbers
736 // TODO: also remove localized and fullwidth digits
737 sal_Int32 i = 0;
738 OUStringBuffer sBuff(rFamilyName);
739 while ( i < sBuff.getLength() )
741 sal_Unicode c = sBuff[ i ];
742 if ( (c >= 0x0030) && (c <= 0x0039) )
743 sBuff.remove(i, 1);
744 else
745 i++;
749 namespace {
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[] =
763 "default",
764 "standard",
765 "normal",
766 "symbol",
767 "fixed",
768 "sansserif",
769 "serif",
770 "decorative",
771 "special",
772 "italic",
773 "title",
774 "capitals",
775 "cjk",
776 "cjk_jp",
777 "cjk_sc",
778 "cjk_tc",
779 "cjk_kr",
780 "ctl",
781 "nonelatin",
782 "full",
783 "outline",
784 "shadow",
785 "rounded",
786 "typewriter",
787 "script",
788 "handwriting",
789 "chancery",
790 "comic",
791 "brushscript",
792 "gothic",
793 "schoolbook",
794 "other"
797 namespace {
799 struct enum_convert
801 const char* pName;
802 int nEnum;
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();
849 if( nLength )
851 const sal_Unicode* pStr = pLine->getStr();
852 sal_Int32 nTokens = 0;
853 // count tokens
854 while( nLength-- )
856 if( *pStr++ == ';' )
857 nTokens++;
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 );
869 if (!itPair.second)
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
888 int weight = -1;
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 ) )
898 break;
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
915 int width = -1;
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 ) )
925 break;
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
942 sal_uLong type = 0;
945 Any aAny = rFont->getByName( rType );
946 auto pLine = o3tl::tryAccess<OUString>(aAny);
947 if( !pLine )
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;
959 break;
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() )
978 return;
980 if( it->second.bConfigRead )
981 return;
983 it->second.bConfigRead = true;
984 Reference< XNameAccess > xNode;
987 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
988 aAny >>= xNode;
990 catch (const NoSuchElementException&)
993 catch (const WrappedTargetException&)
996 if( !xNode.is() )
997 return;
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 );
1016 aAny >>= xFont;
1018 catch (const NoSuchElementException&)
1021 catch (const WrappedTargetException&)
1024 if( ! xFont.is() )
1026 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName);
1027 continue;
1030 FontNameAttr aAttr;
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() )
1048 return nullptr;
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))
1082 return &rFoundAttr;
1086 return nullptr;
1089 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */