bump product version to 25.8.0.0.alpha0+
[LibreOffice.git] / unotools / source / config / fontcfg.cxx
blobc24e3710aeea26a635cd1294e5de4c653ef3e909
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/configuration.hxx>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/propertysequence.hxx>
29 #include <com/sun/star/uno/Any.hxx>
30 #include <com/sun/star/uno/Sequence.hxx>
31 #include <com/sun/star/configuration/theDefaultProvider.hpp>
32 #include <com/sun/star/container/XNameAccess.hpp>
33 #include <unotools/syslocale.hxx>
34 #include <rtl/ustrbuf.hxx>
35 #include <osl/diagnose.h>
36 #include <sal/log.hxx>
38 #include <string.h>
39 #include <algorithm>
41 using namespace utl;
42 using namespace com::sun::star::uno;
43 using namespace com::sun::star::lang;
44 using namespace com::sun::star::container;
45 using namespace com::sun::star::configuration;
48 * DefaultFontConfiguration
51 static OUString getKeyType( DefaultFontType nKeyType )
53 switch( nKeyType )
55 case DefaultFontType::CJK_DISPLAY: return u"CJK_DISPLAY"_ustr;
56 case DefaultFontType::CJK_HEADING: return u"CJK_HEADING"_ustr;
57 case DefaultFontType::CJK_PRESENTATION: return u"CJK_PRESENTATION"_ustr;
58 case DefaultFontType::CJK_SPREADSHEET: return u"CJK_SPREADSHEET"_ustr;
59 case DefaultFontType::CJK_TEXT: return u"CJK_TEXT"_ustr;
60 case DefaultFontType::CTL_DISPLAY: return u"CTL_DISPLAY"_ustr;
61 case DefaultFontType::CTL_HEADING: return u"CTL_HEADING"_ustr;
62 case DefaultFontType::CTL_PRESENTATION: return u"CTL_PRESENTATION"_ustr;
63 case DefaultFontType::CTL_SPREADSHEET: return u"CTL_SPREADSHEET"_ustr;
64 case DefaultFontType::CTL_TEXT: return u"CTL_TEXT"_ustr;
65 case DefaultFontType::FIXED: return u"FIXED"_ustr;
66 case DefaultFontType::LATIN_DISPLAY: return u"LATIN_DISPLAY"_ustr;
67 case DefaultFontType::LATIN_FIXED: return u"LATIN_FIXED"_ustr;
68 case DefaultFontType::LATIN_HEADING: return u"LATIN_HEADING"_ustr;
69 case DefaultFontType::LATIN_PRESENTATION: return u"LATIN_PRESENTATION"_ustr;
70 case DefaultFontType::LATIN_SPREADSHEET: return u"LATIN_SPREADSHEET"_ustr;
71 case DefaultFontType::LATIN_TEXT: return u"LATIN_TEXT"_ustr;
72 case DefaultFontType::SANS: return u"SANS"_ustr;
73 case DefaultFontType::SANS_UNICODE: return u"SANS_UNICODE"_ustr;
74 case DefaultFontType::SERIF: return u"SERIF"_ustr;
75 case DefaultFontType::SYMBOL: return u"SYMBOL"_ustr;
76 case DefaultFontType::UI_FIXED: return u"UI_FIXED"_ustr;
77 case DefaultFontType::UI_SANS: return u"UI_SANS"_ustr;
78 default:
79 OSL_FAIL( "unmatched type" );
80 return u""_ustr;
84 DefaultFontConfiguration& DefaultFontConfiguration::get()
86 static DefaultFontConfiguration theDefaultFontConfiguration;
87 return theDefaultFontConfiguration;
90 DefaultFontConfiguration::DefaultFontConfiguration()
92 if (comphelper::IsFuzzing())
93 return;
94 // create configuration hierarchical access name
95 try
97 // get service provider
98 m_xConfigProvider = theDefaultProvider::get(comphelper::getProcessComponentContext());
99 Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
101 {"nodepath", Any(u"/org.openoffice.VCL/DefaultFonts"_ustr)}
102 }));
103 m_xConfigAccess =
104 Reference< XNameAccess >(
105 m_xConfigProvider->createInstanceWithArguments( u"com.sun.star.configuration.ConfigurationAccess"_ustr,
106 aArgs ),
107 UNO_QUERY );
108 if( m_xConfigAccess.is() )
110 const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
111 // fill config hash with empty interfaces
112 for( const OUString& rLocaleString : aLocales )
114 // Feed through LanguageTag for casing.
115 OUString aLoc( LanguageTag( rLocaleString, true).getBcp47( false));
116 m_aConfig[ aLoc ] = LocaleAccess();
117 m_aConfig[ aLoc ].aConfigLocaleString = rLocaleString;
121 catch (const Exception&)
123 // configuration is awry
124 m_xConfigProvider.clear();
125 m_xConfigAccess.clear();
127 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
128 << ", config access: " << m_xConfigAccess.is());
131 DefaultFontConfiguration::~DefaultFontConfiguration()
133 // release all nodes
134 m_aConfig.clear();
135 // release top node
136 m_xConfigAccess.clear();
137 // release config provider
138 m_xConfigProvider.clear();
141 OUString DefaultFontConfiguration::tryLocale( const OUString& rBcp47, const OUString& rType ) const
143 OUString aRet;
145 std::unordered_map< OUString, LocaleAccess >::const_iterator it = m_aConfig.find( rBcp47 );
146 if( it != m_aConfig.end() )
148 if( !it->second.xAccess.is() )
152 Reference< XNameAccess > xNode;
153 if ( m_xConfigAccess->hasByName( it->second.aConfigLocaleString ) )
155 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
156 if( aAny >>= xNode )
157 it->second.xAccess = std::move(xNode);
160 catch (const NoSuchElementException&)
163 catch (const WrappedTargetException&)
167 if( it->second.xAccess.is() )
171 if ( it->second.xAccess->hasByName( rType ) )
173 Any aAny = it->second.xAccess->getByName( rType );
174 aAny >>= aRet;
177 catch (const NoSuchElementException&)
180 catch (const WrappedTargetException&)
186 return aRet;
189 OUString DefaultFontConfiguration::getDefaultFont( const LanguageTag& rLanguageTag, DefaultFontType nType ) const
191 OUString aType = getKeyType( nType );
192 // Try the simple cases first without constructing fallbacks.
193 OUString aRet = tryLocale( rLanguageTag.getBcp47(), aType );
194 if (aRet.isEmpty())
196 if (rLanguageTag.isIsoLocale())
198 if (!rLanguageTag.getCountry().isEmpty())
200 aRet = tryLocale( rLanguageTag.getLanguage(), aType );
203 else
205 ::std::vector< OUString > aFallbacks( rLanguageTag.getFallbackStrings( false));
206 for (const auto& rFallback : aFallbacks)
208 aRet = tryLocale( rFallback, aType );
209 if (!aRet.isEmpty())
210 break;
214 if( aRet.isEmpty() )
216 aRet = tryLocale( u"en"_ustr, aType );
218 return aRet;
221 OUString DefaultFontConfiguration::getUserInterfaceFont( const LanguageTag& rLanguageTag ) const
223 LanguageTag aLanguageTag( rLanguageTag);
224 if( aLanguageTag.isSystemLocale() )
225 aLanguageTag = SvtSysLocale().GetUILanguageTag();
227 OUString aUIFont = getDefaultFont( aLanguageTag, DefaultFontType::UI_SANS );
229 if( !aUIFont.isEmpty() )
230 return aUIFont;
232 // fallback mechanism (either no configuration or no entry in configuration
234 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS = u"Andale Sans UI;Albany;Albany AMT;Tahoma;Arial Unicode MS;Arial;Nimbus Sans L;Bitstream Vera Sans;gnu-unifont;Interface User;Geneva;WarpSans;Dialog;Swiss;Lucida;Helvetica;Charcoal;Chicago;MS Sans Serif;Helv;Times;Times New Roman;Interface System";
235 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_LATIN2 = u"Andale Sans UI;Albany;Albany AMT;Tahoma;Arial Unicode MS;Arial;Nimbus Sans L;Luxi Sans;Bitstream Vera Sans;Interface User;Geneva;WarpSans;Dialog;Swiss;Lucida;Helvetica;Charcoal;Chicago;MS Sans Serif;Helv;Times;Times New Roman;Interface System";
236 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_ARABIC = u"Tahoma;Traditional Arabic;Simplified Arabic;Lucidasans;Lucida Sans;Supplement;Andale Sans UI;clearlyU;Interface User;Arial Unicode MS;Lucida Sans Unicode;WarpSans;Geneva;MS Sans Serif;Helv;Dialog;Albany;Lucida;Helvetica;Charcoal;Chicago;Arial;Helmet;Interface System;Sans Serif";
237 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_THAI = u"OONaksit;Tahoma;Lucidasans;Arial Unicode MS";
238 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_KOREAN = u"Noto Sans KR;Noto Sans CJK KR;Noto Serif KR;Noto Serif CJK KR;Source Han Sans KR;NanumGothic;NanumBarunGothic;NanumBarunGothic YetHangul;KoPubWorld Dotum;Malgun Gothic;Apple SD Gothic Neo;Dotum;DotumChe;Gulim;GulimChe;Batang;BatangChe;Apple Gothic;UnDotum;Baekmuk Gulim;Arial Unicode MS;Lucida Sans Unicode;gnu-unifont;Andale Sans UI";
239 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_JAPANESE = u"Noto Sans CJK JP;Noto Sans JP;Source Han Sans;Source Han Sans JP;Yu Gothic UI;Yu Gothic;YuGothic;Hiragino Sans;Hiragino Kaku Gothic ProN;Hiragino Kaku Gothic Pro;Hiragino Kaku Gothic StdN;Meiryo UI;Meiryo;IPAexGothic;IPAPGothic;IPAGothic;MS UI Gothic;MS PGothic;MS Gothic;Osaka;Unifont;gnu-unifont;Arial Unicode MS;Interface System";
240 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_CHINSIM = u"Andale Sans UI;Arial Unicode MS;ZYSong18030;AR PL SungtiL GB;AR PL KaitiM GB;SimSun;Lucida Sans Unicode;Fangsong;Hei;Song;Kai;Ming;gnu-unifont;Interface User;";
241 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_CHINTRD = u"Andale Sans UI;Arial Unicode MS;AR PL Mingti2L Big5;AR PL KaitiM Big5;Kai;PMingLiU;MingLiU;Ming;Lucida Sans Unicode;gnu-unifont;Interface User;";
243 const OUString aLanguage( aLanguageTag.getLanguage());
245 // optimize font list for some locales, as long as Andale Sans UI does not support them
246 if( aLanguage == "ar" || aLanguage == "he" || aLanguage == "iw" )
248 return FALLBACKFONT_UI_SANS_ARABIC;
250 else if ( aLanguage == "th" )
252 return FALLBACKFONT_UI_SANS_THAI;
254 else if ( aLanguage == "ko" )
256 return FALLBACKFONT_UI_SANS_KOREAN;
258 else if ( aLanguage == "ja" )
260 return FALLBACKFONT_UI_SANS_JAPANESE;
262 else if( aLanguage == "cs" ||
263 aLanguage == "hu" ||
264 aLanguage == "pl" ||
265 aLanguage == "ro" ||
266 aLanguage == "rm" ||
267 aLanguage == "hr" ||
268 aLanguage == "sk" ||
269 aLanguage == "sl" ||
270 aLanguage == "sb")
272 return FALLBACKFONT_UI_SANS_LATIN2;
274 else
276 const Locale& aLocale( aLanguageTag.getLocale());
277 if (MsLangId::isTraditionalChinese(aLocale))
278 return FALLBACKFONT_UI_SANS_CHINTRD;
279 else if (MsLangId::isSimplifiedChinese(aLocale))
280 return FALLBACKFONT_UI_SANS_CHINSIM;
283 return FALLBACKFONT_UI_SANS;
287 * FontSubstConfigItem::get
290 FontSubstConfiguration& FontSubstConfiguration::get()
292 static FontSubstConfiguration theFontSubstConfiguration;
293 return theFontSubstConfiguration;
297 * FontSubstConfigItem::FontSubstConfigItem
300 FontSubstConfiguration::FontSubstConfiguration() :
301 maSubstHash( 300 ),
302 maLanguageTag(u"en"_ustr)
304 if (comphelper::IsFuzzing())
305 return;
308 // get service provider
309 const Reference< XComponentContext >& xContext( comphelper::getProcessComponentContext() );
310 // create configuration hierarchical access name
311 m_xConfigProvider = theDefaultProvider::get( xContext );
312 Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
314 {"nodepath", Any(u"/org.openoffice.VCL/FontSubstitutions"_ustr)}
315 }));
316 m_xConfigAccess =
317 Reference< XNameAccess >(
318 m_xConfigProvider->createInstanceWithArguments( u"com.sun.star.configuration.ConfigurationAccess"_ustr,
319 aArgs ),
320 UNO_QUERY );
321 if( m_xConfigAccess.is() )
323 const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
324 // fill config hash with empty interfaces
325 for( const OUString& rLocaleString : aLocales )
327 // Feed through LanguageTag for casing.
328 OUString aLoc( LanguageTag( rLocaleString, true).getBcp47( false));
329 m_aSubst[ aLoc ] = LocaleSubst();
330 m_aSubst[ aLoc ].aConfigLocaleString = rLocaleString;
334 catch (const Exception&)
336 // configuration is awry
337 m_xConfigProvider.clear();
338 m_xConfigAccess.clear();
340 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
341 << ", config access: " << m_xConfigAccess.is());
343 if( maLanguageTag.isSystemLocale() )
344 maLanguageTag = SvtSysLocale().GetUILanguageTag();
348 * FontSubstConfigItem::~FontSubstConfigItem
351 FontSubstConfiguration::~FontSubstConfiguration()
353 // release config access
354 m_xConfigAccess.clear();
355 // release config provider
356 m_xConfigProvider.clear();
360 * FontSubstConfigItem::getMapName
363 const char* const aImplKillLeadingList[] =
365 "microsoft",
366 "monotype",
367 "linotype",
368 "baekmuk",
369 "adobe",
370 "nimbus",
371 "zycjk",
372 "itc",
373 "sun",
374 "amt",
375 "ms",
376 "mt",
377 "cg",
378 "hg",
379 "fz",
380 "ipa",
381 "sazanami",
382 "kochi",
383 nullptr
386 const char* const aImplKillTrailingList[] =
388 "microsoft",
389 "monotype",
390 "linotype",
391 "adobe",
392 "nimbus",
393 "itc",
394 "sun",
395 "amt",
396 "ms",
397 "mt",
398 "clm",
399 // Scripts, for compatibility with older versions
400 "we",
401 "cyr",
402 "tur",
403 "wt",
404 "greek",
405 "wl",
406 // CJK extensions
407 "gb",
408 "big5",
409 "pro",
410 "z01",
411 "z02",
412 "z03",
413 "z13",
414 "b01",
415 "w3x12",
416 // Old Printer Fontnames
417 "5cpi",
418 "6cpi",
419 "7cpi",
420 "8cpi",
421 "9cpi",
422 "10cpi",
423 "11cpi",
424 "12cpi",
425 "13cpi",
426 "14cpi",
427 "15cpi",
428 "16cpi",
429 "18cpi",
430 "24cpi",
431 "scale",
432 "pc",
433 nullptr
436 const char* const aImplKillTrailingWithExceptionsList[] =
438 "ce", "monospace", "oldface", nullptr,
439 "ps", "caps", nullptr,
440 nullptr
443 namespace {
445 struct ImplFontAttrWeightSearchData
447 const char* mpStr;
448 FontWeight meWeight;
453 ImplFontAttrWeightSearchData const aImplWeightAttrSearchList[] =
455 // the attribute names are ordered by "first match wins"
456 // e.g. "semilight" should wins over "semi"
457 { "extrablack", WEIGHT_BLACK },
458 { "ultrablack", WEIGHT_BLACK },
459 { "ultrabold", WEIGHT_ULTRABOLD },
460 { "semibold", WEIGHT_SEMIBOLD },
461 { "semilight", WEIGHT_SEMILIGHT },
462 { "semi", WEIGHT_SEMIBOLD },
463 { "demi", WEIGHT_SEMIBOLD },
464 { "black", WEIGHT_BLACK },
465 { "bold", WEIGHT_BOLD },
466 { "heavy", WEIGHT_BLACK },
467 { "ultralight", WEIGHT_ULTRALIGHT },
468 { "light", WEIGHT_LIGHT },
469 { "medium", WEIGHT_MEDIUM },
470 { nullptr, WEIGHT_DONTKNOW },
473 namespace {
475 struct ImplFontAttrWidthSearchData
477 const char* mpStr;
478 FontWidth meWidth;
483 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 namespace {
500 struct ImplFontAttrTypeSearchData
502 const char* mpStr;
503 ImplFontAttrs mnType;
508 ImplFontAttrTypeSearchData const aImplTypeAttrSearchList[] =
510 { "monotype", ImplFontAttrs::None },
511 { "linotype", ImplFontAttrs::None },
512 { "titling", ImplFontAttrs::Titling },
513 { "capitals", ImplFontAttrs::Capitals },
514 { "capital", ImplFontAttrs::Capitals },
515 { "caps", ImplFontAttrs::Capitals },
516 { "italic", ImplFontAttrs::Italic },
517 { "oblique", ImplFontAttrs::Italic },
518 { "rounded", ImplFontAttrs::Rounded },
519 { "outline", ImplFontAttrs::Outline },
520 { "shadow", ImplFontAttrs::Shadow },
521 { "handwriting", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
522 { "hand", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
523 { "signet", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
524 { "script", ImplFontAttrs::BrushScript | ImplFontAttrs::Script },
525 { "calligraphy", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
526 { "chancery", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
527 { "corsiva", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
528 { "gothic", ImplFontAttrs::SansSerif | ImplFontAttrs::Gothic },
529 { "schoolbook", ImplFontAttrs::Serif | ImplFontAttrs::Schoolbook },
530 { "schlbk", ImplFontAttrs::Serif | ImplFontAttrs::Schoolbook },
531 { "typewriter", ImplFontAttrs::Typewriter | ImplFontAttrs::Fixed },
532 { "lineprinter", ImplFontAttrs::Typewriter | ImplFontAttrs::Fixed },
533 { "monospaced", ImplFontAttrs::Fixed },
534 { "monospace", ImplFontAttrs::Fixed },
535 { "mono", ImplFontAttrs::Fixed },
536 { "fixed", ImplFontAttrs::Fixed },
537 { "sansserif", ImplFontAttrs::SansSerif },
538 { "sans", ImplFontAttrs::SansSerif },
539 { "swiss", ImplFontAttrs::SansSerif },
540 { "serif", ImplFontAttrs::Serif },
541 { "bright", ImplFontAttrs::Serif },
542 { "symbols", ImplFontAttrs::Symbol },
543 { "symbol", ImplFontAttrs::Symbol },
544 { "dingbats", ImplFontAttrs::Symbol },
545 { "dings", ImplFontAttrs::Symbol },
546 { "ding", ImplFontAttrs::Symbol },
547 { "bats", ImplFontAttrs::Symbol },
548 { "math", ImplFontAttrs::Symbol },
549 { "oldstyle", ImplFontAttrs::OtherStyle },
550 { "oldface", ImplFontAttrs::OtherStyle },
551 { "old", ImplFontAttrs::OtherStyle },
552 { "new", ImplFontAttrs::None },
553 { "modern", ImplFontAttrs::None },
554 { "lucida", ImplFontAttrs::None },
555 { "regular", ImplFontAttrs::None },
556 { "extended", ImplFontAttrs::None },
557 { "extra", ImplFontAttrs::OtherStyle },
558 { "ext", ImplFontAttrs::None },
559 { "scalable", ImplFontAttrs::None },
560 { "scale", ImplFontAttrs::None },
561 { "nimbus", ImplFontAttrs::None },
562 { "adobe", ImplFontAttrs::None },
563 { "itc", ImplFontAttrs::None },
564 { "amt", ImplFontAttrs::None },
565 { "mt", ImplFontAttrs::None },
566 { "ms", ImplFontAttrs::None },
567 { "cpi", ImplFontAttrs::None },
568 { "no", ImplFontAttrs::None },
569 { nullptr, ImplFontAttrs::None },
572 static bool ImplKillLeading( OUString& rName, const char* const* ppStr )
574 for(; *ppStr; ++ppStr )
576 const char* pStr = *ppStr;
577 const sal_Unicode* pNameStr = rName.getStr();
578 while ( (*pNameStr == static_cast<sal_Unicode>(static_cast<unsigned char>(*pStr))) && *pStr )
580 pNameStr++;
581 pStr++;
583 if ( !*pStr )
585 sal_Int32 nLen = static_cast<sal_Int32>(pNameStr - rName.getStr());
586 rName = rName.copy(nLen);
587 return true;
591 // special case for Baekmuk
592 // TODO: allow non-ASCII KillLeading list
593 const sal_Unicode* pNameStr = rName.getStr();
594 if( (pNameStr[0]==0xBC31) && (pNameStr[1]==0xBC35) )
596 sal_Int32 nLen = (pNameStr[2]==0x0020) ? 3 : 2;
597 rName = rName.copy(nLen);
598 return true;
601 return false;
604 static sal_Int32 ImplIsTrailing( std::u16string_view rName, const char* pStr )
606 size_t nStrLen = strlen( pStr );
607 if( nStrLen >= rName.size() )
608 return 0;
610 const sal_Unicode* pEndName = rName.data() + rName.size();
611 const sal_Unicode* pNameStr = pEndName - nStrLen;
612 do if( *(pNameStr++) != *(pStr++) )
613 return 0;
614 while( *pStr );
616 return nStrLen;
619 static bool ImplKillTrailing( OUString& rName, const char* const* ppStr )
621 for(; *ppStr; ++ppStr )
623 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
624 if( nTrailLen )
626 rName = rName.copy(0, rName.getLength() - nTrailLen );
627 return true;
631 return false;
634 static bool ImplKillTrailingWithExceptions( OUString& rName, const char* const* ppStr )
636 for(; *ppStr; ++ppStr )
638 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
639 if( nTrailLen )
641 // check string match against string exceptions
642 while( *++ppStr )
643 if( ImplIsTrailing( rName, *ppStr ) )
644 return false;
646 rName = rName.copy(0, rName.getLength() - nTrailLen );
647 return true;
649 else
651 // skip exception strings
652 while( *++ppStr ) {}
656 return false;
659 static bool ImplFindAndErase( OUString& rName, const char* pStr )
661 sal_Int32 nLen = static_cast<sal_Int32>(strlen(pStr));
662 sal_Int32 nPos = rName.indexOfAsciiL(pStr, nLen );
663 if ( nPos < 0 )
664 return false;
666 OUStringBuffer sBuff(rName);
667 sBuff.remove(nPos, nLen);
668 rName = sBuff.makeStringAndClear();
669 return true;
672 void FontSubstConfiguration::getMapName( const OUString& rOrgName, OUString& rShortName,
673 OUString& rFamilyName, FontWeight& rWeight,
674 FontWidth& rWidth, ImplFontAttrs& rType )
676 rShortName = rOrgName;
678 // TODO: get rid of the crazy O(N*strlen) searches below
679 // they should be possible in O(strlen)
681 // Kill leading vendor names and other unimportant data
682 ImplKillLeading( rShortName, aImplKillLeadingList );
684 // Kill trailing vendor names and other unimportant data
685 ImplKillTrailing( rShortName, aImplKillTrailingList );
686 ImplKillTrailingWithExceptions( rShortName, aImplKillTrailingWithExceptionsList );
688 rFamilyName = rShortName;
690 // Kill attributes from the name and update the data
691 // Weight
692 const ImplFontAttrWeightSearchData* pWeightList = aImplWeightAttrSearchList;
693 while ( pWeightList->mpStr )
695 if ( ImplFindAndErase( rFamilyName, pWeightList->mpStr ) )
697 if ( (rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL) )
698 rWeight = pWeightList->meWeight;
699 break;
701 pWeightList++;
704 // Width
705 const ImplFontAttrWidthSearchData* pWidthList = aImplWidthAttrSearchList;
706 while ( pWidthList->mpStr )
708 if ( ImplFindAndErase( rFamilyName, pWidthList->mpStr ) )
710 if ( (rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL) )
711 rWidth = pWidthList->meWidth;
712 break;
714 pWidthList++;
717 // Type
718 rType = ImplFontAttrs::None;
719 const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
720 while ( pTypeList->mpStr )
722 if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
723 rType |= pTypeList->mnType;
724 pTypeList++;
727 // Remove numbers
728 // TODO: also remove localized and fullwidth digits
729 sal_Int32 i = 0;
730 OUStringBuffer sBuff(rFamilyName);
731 while ( i < sBuff.getLength() )
733 sal_Unicode c = sBuff[ i ];
734 if ( (c >= 0x0030) && (c <= 0x0039) )
735 sBuff.remove(i, 1);
736 else
737 i++;
741 namespace {
743 struct StrictStringSort
745 bool operator()( const FontNameAttr& rLeft, const FontNameAttr& rRight )
746 { return rLeft.Name.compareTo( rRight.Name ) < 0; }
751 // The entries in this table must match the bits in the ImplFontAttrs enum.
753 const char* const pAttribNames[] =
755 "default",
756 "standard",
757 "normal",
758 "symbol",
759 "fixed",
760 "sansserif",
761 "serif",
762 "decorative",
763 "special",
764 "italic",
765 "title",
766 "capitals",
767 "cjk",
768 "cjk_jp",
769 "cjk_sc",
770 "cjk_tc",
771 "cjk_kr",
772 "ctl",
773 "nonelatin",
774 "full",
775 "outline",
776 "shadow",
777 "rounded",
778 "typewriter",
779 "script",
780 "handwriting",
781 "chancery",
782 "comic",
783 "brushscript",
784 "gothic",
785 "schoolbook",
786 "other"
789 namespace {
791 struct enum_convert
793 const char* pName;
794 int nEnum;
799 const enum_convert pWeightNames[] =
801 { "normal", WEIGHT_NORMAL },
802 { "medium", WEIGHT_MEDIUM },
803 { "bold", WEIGHT_BOLD },
804 { "black", WEIGHT_BLACK },
805 { "semibold", WEIGHT_SEMIBOLD },
806 { "light", WEIGHT_LIGHT },
807 { "semilight", WEIGHT_SEMILIGHT },
808 { "ultrabold", WEIGHT_ULTRABOLD },
809 { "semi", WEIGHT_SEMIBOLD },
810 { "demi", WEIGHT_SEMIBOLD },
811 { "heavy", WEIGHT_BLACK },
812 { "unknown", WEIGHT_DONTKNOW },
813 { "thin", WEIGHT_THIN },
814 { "ultralight", WEIGHT_ULTRALIGHT }
817 const enum_convert pWidthNames[] =
819 { "normal", WIDTH_NORMAL },
820 { "condensed", WIDTH_CONDENSED },
821 { "expanded", WIDTH_EXPANDED },
822 { "unknown", WIDTH_DONTKNOW },
823 { "ultracondensed", WIDTH_ULTRA_CONDENSED },
824 { "extracondensed", WIDTH_EXTRA_CONDENSED },
825 { "semicondensed", WIDTH_SEMI_CONDENSED },
826 { "semiexpanded", WIDTH_SEMI_EXPANDED },
827 { "extraexpanded", WIDTH_EXTRA_EXPANDED },
828 { "ultraexpanded", WIDTH_ULTRA_EXPANDED }
831 void FontSubstConfiguration::fillSubstVector( const css::uno::Reference< XNameAccess >& rFont,
832 const OUString& rType,
833 std::vector< OUString >& rSubstVector ) const
837 Any aAny = rFont->getByName( rType );
838 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
840 sal_Int32 nLength = pLine->getLength();
841 if( nLength )
843 const sal_Unicode* pStr = pLine->getStr();
844 sal_Int32 nTokens = 0;
845 // count tokens
846 while( nLength-- )
848 if( *pStr++ == ';' )
849 nTokens++;
851 rSubstVector.clear();
852 // optimize performance, heap fragmentation
853 rSubstVector.reserve( nTokens );
854 sal_Int32 nIndex = 0;
855 while( nIndex != -1 )
857 OUString aSubst( pLine->getToken( 0, ';', nIndex ) );
858 if( !aSubst.isEmpty() )
860 auto itPair = maSubstHash.insert( aSubst );
861 if (!itPair.second)
862 aSubst = *itPair.first;
863 rSubstVector.push_back( aSubst );
869 catch (const NoSuchElementException&)
872 catch (const WrappedTargetException&)
877 // static
878 FontWeight FontSubstConfiguration::getSubstWeight( const css::uno::Reference< XNameAccess >& rFont,
879 const OUString& rType )
881 int weight = -1;
884 Any aAny = rFont->getByName( rType );
885 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
887 if( !pLine->isEmpty() )
889 for( weight=std::size(pWeightNames)-1; weight >= 0; weight-- )
890 if( pLine->equalsIgnoreAsciiCaseAscii( pWeightNames[weight].pName ) )
891 break;
893 SAL_WARN_IF(weight < 0, "unotools.config", "Error: invalid weight " << *pLine);
896 catch (const NoSuchElementException&)
899 catch (const WrappedTargetException&)
902 return static_cast<FontWeight>( weight >= 0 ? pWeightNames[weight].nEnum : WEIGHT_DONTKNOW );
905 // static
906 FontWidth FontSubstConfiguration::getSubstWidth( const css::uno::Reference< XNameAccess >& rFont,
907 const OUString& rType )
909 int width = -1;
912 Any aAny = rFont->getByName( rType );
913 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
915 if( !pLine->isEmpty() )
917 for( width=std::size(pWidthNames)-1; width >= 0; width-- )
918 if( pLine->equalsIgnoreAsciiCaseAscii( pWidthNames[width].pName ) )
919 break;
921 SAL_WARN_IF( width < 0, "unotools.config", "Error: invalid width " << *pLine);
924 catch (const NoSuchElementException&)
927 catch (const WrappedTargetException&)
930 return static_cast<FontWidth>( width >= 0 ? pWidthNames[width].nEnum : WIDTH_DONTKNOW );
933 // static
934 ImplFontAttrs FontSubstConfiguration::getSubstType( const css::uno::Reference< XNameAccess >& rFont,
935 const OUString& rType )
937 sal_uInt32 type = 0;
940 Any aAny = rFont->getByName( rType );
941 auto pLine = o3tl::tryAccess<OUString>(aAny);
942 if( !pLine )
943 return ImplFontAttrs::None;
944 if( pLine->isEmpty() )
945 return ImplFontAttrs::None;
946 sal_Int32 nIndex = 0;
947 while( nIndex != -1 )
949 OUString aToken( pLine->getToken( 0, ',', nIndex ) );
950 for( int k = 0; k < 32; k++ )
951 if( aToken.equalsIgnoreAsciiCaseAscii( pAttribNames[k] ) )
953 type |= sal_uInt32(1) << k;
954 break;
957 assert(((type & ~o3tl::typed_flags<ImplFontAttrs>::mask) == 0) && "invalid font attributes");
959 catch (const NoSuchElementException&)
962 catch (const WrappedTargetException&)
966 return static_cast<ImplFontAttrs>(type);
969 void FontSubstConfiguration::readLocaleSubst( const OUString& rBcp47 ) const
971 std::unordered_map< OUString, LocaleSubst >::const_iterator it = m_aSubst.find( rBcp47 );
972 if( it == m_aSubst.end() )
973 return;
975 if( it->second.bConfigRead )
976 return;
978 it->second.bConfigRead = true;
979 Reference< XNameAccess > xNode;
982 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
983 aAny >>= xNode;
985 catch (const NoSuchElementException&)
988 catch (const WrappedTargetException&)
991 if( !xNode.is() )
992 return;
994 const Sequence< OUString > aFonts = xNode->getElementNames();
995 int nFonts = aFonts.getLength();
996 // improve performance, heap fragmentation
997 it->second.aSubstAttributes.reserve( nFonts );
999 // strings for subst retrieval, construct only once
1000 static constexpr OUStringLiteral aSubstFontsStr ( u"SubstFonts" );
1001 static constexpr OUStringLiteral aSubstFontsMSStr( u"SubstFontsMS" );
1002 static constexpr OUStringLiteral aSubstWeightStr ( u"FontWeight" );
1003 static constexpr OUStringLiteral aSubstWidthStr ( u"FontWidth" );
1004 static constexpr OUStringLiteral aSubstTypeStr ( u"FontType" );
1005 for( const OUString& rFontName : aFonts )
1007 Reference< XNameAccess > xFont;
1010 Any aAny = xNode->getByName( rFontName );
1011 aAny >>= xFont;
1013 catch (const NoSuchElementException&)
1016 catch (const WrappedTargetException&)
1019 if( ! xFont.is() )
1021 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName);
1022 continue;
1025 FontNameAttr aAttr;
1026 // read subst attributes from config
1027 aAttr.Name = rFontName;
1028 fillSubstVector( xFont, aSubstFontsStr, aAttr.Substitutions );
1029 fillSubstVector( xFont, aSubstFontsMSStr, aAttr.MSSubstitutions );
1030 aAttr.Weight = getSubstWeight( xFont, aSubstWeightStr );
1031 aAttr.Width = getSubstWidth( xFont, aSubstWidthStr );
1032 aAttr.Type = getSubstType( xFont, aSubstTypeStr );
1034 // finally insert this entry
1035 it->second.aSubstAttributes.push_back( aAttr );
1037 std::sort( it->second.aSubstAttributes.begin(), it->second.aSubstAttributes.end(), StrictStringSort() );
1040 const FontNameAttr* FontSubstConfiguration::getSubstInfo( const OUString& rFontName ) const
1042 if( rFontName.isEmpty() )
1043 return nullptr;
1045 // search if a (language dep.) replacement table for the given font exists
1046 // fallback is english
1047 OUString aSearchFont( rFontName.toAsciiLowerCase() );
1048 FontNameAttr aSearchAttr;
1049 aSearchAttr.Name = aSearchFont;
1051 ::std::vector< OUString > aFallbacks( maLanguageTag.getFallbackStrings( true));
1052 if (maLanguageTag.getLanguage() != "en")
1053 aFallbacks.emplace_back("en");
1055 for (const auto& rFallback : aFallbacks)
1057 std::unordered_map< OUString, LocaleSubst >::const_iterator lang = m_aSubst.find( rFallback );
1058 if( lang != m_aSubst.end() )
1060 if( ! lang->second.bConfigRead )
1061 readLocaleSubst( rFallback );
1062 // try to find an exact match
1063 // because the list is sorted this will also find fontnames of the form searchfontname*
1064 std::vector< FontNameAttr >::const_iterator it = ::std::lower_bound( lang->second.aSubstAttributes.begin(), lang->second.aSubstAttributes.end(), aSearchAttr, StrictStringSort() );
1065 if( it != lang->second.aSubstAttributes.end())
1067 const FontNameAttr& rFoundAttr = *it;
1068 // a search for "abcblack" may match with an entry for "abc"
1069 // the reverse is not a good idea (e.g. #i112731# alba->albani)
1070 if( rFoundAttr.Name.getLength() <= aSearchFont.getLength() )
1071 if( aSearchFont.startsWith( rFoundAttr.Name))
1072 return &rFoundAttr;
1076 return nullptr;
1079 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */