Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / unotools / source / config / fontcfg.cxx
blobbd7f7e21330b6615f8d353bfb45cc65f8a59ba05
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 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" ||
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 static 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 static 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 static const char* const aImplKillTrailingWithExceptionsList[] =
446 "ce", "monospace", "oldface", nullptr,
447 "ps", "caps", nullptr,
448 nullptr
451 struct ImplFontAttrWeightSearchData
453 const char* mpStr;
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
479 const char* mpStr;
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
500 const char* mpStr;
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 )
576 pNameStr++;
577 pStr++;
579 if ( !*pStr )
581 sal_Int32 nLen = static_cast<sal_Int32>(pNameStr - rName.getStr());
582 rName = rName.copy(nLen);
583 return true;
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);
594 return true;
597 return false;
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() )
604 return 0;
606 const sal_Unicode* pEndName = rName.getStr() + rName.getLength();
607 const sal_Unicode* pNameStr = pEndName - nStrLen;
608 do if( *(pNameStr++) != *(pStr++) )
609 return 0;
610 while( *pStr );
612 return nStrLen;
615 static bool ImplKillTrailing( OUString& rName, const char* const* ppStr )
617 for(; *ppStr; ++ppStr )
619 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
620 if( nTrailLen )
622 rName = rName.copy(0, rName.getLength() - nTrailLen );
623 return true;
627 return false;
630 static bool ImplKillTrailingWithExceptions( OUString& rName, const char* const* ppStr )
632 for(; *ppStr; ++ppStr )
634 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
635 if( nTrailLen )
637 // check string match against string exceptions
638 while( *++ppStr )
639 if( ImplIsTrailing( rName, *ppStr ) )
640 return false;
642 rName = rName.copy(0, rName.getLength() - nTrailLen );
643 return true;
645 else
647 // skip exception strings
648 while( *++ppStr ) {}
652 return false;
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 );
659 if ( nPos < 0 )
660 return false;
662 OUStringBuffer sBuff(rName);
663 sBuff.remove(nPos, nLen);
664 rName = sBuff.makeStringAndClear();
665 return true;
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
687 // Weight
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;
695 break;
697 pWeightList++;
700 // Width
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;
708 break;
710 pWidthList++;
713 // Type
714 rType = ImplFontAttrs::None;
715 const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
716 while ( pTypeList->mpStr )
718 if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
719 rType |= pTypeList->mnType;
720 pTypeList++;
723 // Remove numbers
724 // TODO: also remove localized and fullwidth digits
725 sal_Int32 i = 0;
726 OUStringBuffer sBuff(rFamilyName);
727 while ( i < sBuff.getLength() )
729 sal_Unicode c = sBuff[ i ];
730 if ( (c >= 0x0030) && (c <= 0x0039) )
731 sBuff.remove(i, 1);
732 else
733 i++;
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[] =
747 "default",
748 "standard",
749 "normal",
750 "symbol",
751 "fixed",
752 "sansserif",
753 "serif",
754 "decorative",
755 "special",
756 "italic",
757 "title",
758 "capitals",
759 "cjk",
760 "cjk_jp",
761 "cjk_sc",
762 "cjk_tc",
763 "cjk_kr",
764 "ctl",
765 "nonelatin",
766 "full",
767 "outline",
768 "shadow",
769 "rounded",
770 "typewriter",
771 "script",
772 "handwriting",
773 "chancery",
774 "comic",
775 "brushscript",
776 "gothic",
777 "schoolbook",
778 "other"
781 struct enum_convert
783 const char* pName;
784 int const nEnum;
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();
829 if( nLength )
831 const sal_Unicode* pStr = pLine->getStr();
832 sal_Int32 nTokens = 0;
833 // count tokens
834 while( nLength-- )
836 if( *pStr++ == ';' )
837 nTokens++;
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 );
849 if (!itPair.second)
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
868 int weight = -1;
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 ) )
878 break;
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
895 int width = -1;
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 ) )
905 break;
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
922 sal_uLong type = 0;
925 Any aAny = rFont->getByName( rType );
926 auto pLine = o3tl::tryAccess<OUString>(aAny);
927 if( !pLine )
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;
939 break;
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 );
966 aAny >>= xNode;
968 catch (const NoSuchElementException&)
971 catch (const WrappedTargetException&)
974 if( xNode.is() )
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 );
993 aAny >>= xFont;
995 catch (const NoSuchElementException&)
998 catch (const WrappedTargetException&)
1001 if( ! xFont.is() )
1003 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName);
1004 continue;
1007 FontNameAttr aAttr;
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() )
1028 return nullptr;
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))
1062 return &rFoundAttr;
1066 return nullptr;
1069 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */