tdf#129905 tdf#160365 sw: don't always draw text boundary on frames
[LibreOffice.git] / unotools / source / config / fontcfg.cxx
blob295a8742ffbbabe8c80dee650289ea46dd5a5a66
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::beans;
45 using namespace com::sun::star::container;
46 using namespace com::sun::star::configuration;
49 * DefaultFontConfiguration
52 static const char* getKeyType( DefaultFontType nKeyType )
54 switch( nKeyType )
56 case DefaultFontType::CJK_DISPLAY: return "CJK_DISPLAY";
57 case DefaultFontType::CJK_HEADING: return "CJK_HEADING";
58 case DefaultFontType::CJK_PRESENTATION: return "CJK_PRESENTATION";
59 case DefaultFontType::CJK_SPREADSHEET: return "CJK_SPREADSHEET";
60 case DefaultFontType::CJK_TEXT: return "CJK_TEXT";
61 case DefaultFontType::CTL_DISPLAY: return "CTL_DISPLAY";
62 case DefaultFontType::CTL_HEADING: return "CTL_HEADING";
63 case DefaultFontType::CTL_PRESENTATION: return "CTL_PRESENTATION";
64 case DefaultFontType::CTL_SPREADSHEET: return "CTL_SPREADSHEET";
65 case DefaultFontType::CTL_TEXT: return "CTL_TEXT";
66 case DefaultFontType::FIXED: return "FIXED";
67 case DefaultFontType::LATIN_DISPLAY: return "LATIN_DISPLAY";
68 case DefaultFontType::LATIN_FIXED: return "LATIN_FIXED";
69 case DefaultFontType::LATIN_HEADING: return "LATIN_HEADING";
70 case DefaultFontType::LATIN_PRESENTATION: return "LATIN_PRESENTATION";
71 case DefaultFontType::LATIN_SPREADSHEET: return "LATIN_SPREADSHEET";
72 case DefaultFontType::LATIN_TEXT: return "LATIN_TEXT";
73 case DefaultFontType::SANS: return "SANS";
74 case DefaultFontType::SANS_UNICODE: return "SANS_UNICODE";
75 case DefaultFontType::SERIF: return "SERIF";
76 case DefaultFontType::SYMBOL: return "SYMBOL";
77 case DefaultFontType::UI_FIXED: return "UI_FIXED";
78 case DefaultFontType::UI_SANS: return "UI_SANS";
79 default:
80 OSL_FAIL( "unmatched type" );
81 return "";
85 DefaultFontConfiguration& DefaultFontConfiguration::get()
87 static DefaultFontConfiguration theDefaultFontConfiguration;
88 return theDefaultFontConfiguration;
91 DefaultFontConfiguration::DefaultFontConfiguration()
93 if (comphelper::IsFuzzing())
94 return;
95 // create configuration hierarchical access name
96 try
98 // get service provider
99 m_xConfigProvider = theDefaultProvider::get(comphelper::getProcessComponentContext());
100 Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
102 {"nodepath", Any(OUString( "/org.openoffice.VCL/DefaultFonts" ))}
103 }));
104 m_xConfigAccess =
105 Reference< XNameAccess >(
106 m_xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
107 aArgs ),
108 UNO_QUERY );
109 if( m_xConfigAccess.is() )
111 const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
112 // fill config hash with empty interfaces
113 for( const OUString& rLocaleString : aLocales )
115 // Feed through LanguageTag for casing.
116 OUString aLoc( LanguageTag( rLocaleString, true).getBcp47( false));
117 m_aConfig[ aLoc ] = LocaleAccess();
118 m_aConfig[ aLoc ].aConfigLocaleString = rLocaleString;
122 catch (const Exception&)
124 // configuration is awry
125 m_xConfigProvider.clear();
126 m_xConfigAccess.clear();
128 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
129 << ", config access: " << m_xConfigAccess.is());
132 DefaultFontConfiguration::~DefaultFontConfiguration()
134 // release all nodes
135 m_aConfig.clear();
136 // release top node
137 m_xConfigAccess.clear();
138 // release config provider
139 m_xConfigProvider.clear();
142 OUString DefaultFontConfiguration::tryLocale( const OUString& rBcp47, const OUString& rType ) const
144 OUString aRet;
146 std::unordered_map< OUString, LocaleAccess >::const_iterator it = m_aConfig.find( rBcp47 );
147 if( it != m_aConfig.end() )
149 if( !it->second.xAccess.is() )
153 Reference< XNameAccess > xNode;
154 if ( m_xConfigAccess->hasByName( it->second.aConfigLocaleString ) )
156 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
157 if( aAny >>= xNode )
158 it->second.xAccess = xNode;
161 catch (const NoSuchElementException&)
164 catch (const WrappedTargetException&)
168 if( it->second.xAccess.is() )
172 if ( it->second.xAccess->hasByName( rType ) )
174 Any aAny = it->second.xAccess->getByName( rType );
175 aAny >>= aRet;
178 catch (const NoSuchElementException&)
181 catch (const WrappedTargetException&)
187 return aRet;
190 OUString DefaultFontConfiguration::getDefaultFont( const LanguageTag& rLanguageTag, DefaultFontType nType ) const
192 OUString aType = OUString::createFromAscii( getKeyType( nType ) );
193 // Try the simple cases first without constructing fallbacks.
194 OUString aRet = tryLocale( rLanguageTag.getBcp47(), aType );
195 if (aRet.isEmpty())
197 if (rLanguageTag.isIsoLocale())
199 if (!rLanguageTag.getCountry().isEmpty())
201 aRet = tryLocale( rLanguageTag.getLanguage(), aType );
204 else
206 ::std::vector< OUString > aFallbacks( rLanguageTag.getFallbackStrings( false));
207 for (const auto& rFallback : aFallbacks)
209 aRet = tryLocale( rFallback, aType );
210 if (!aRet.isEmpty())
211 break;
215 if( aRet.isEmpty() )
217 aRet = tryLocale( "en", aType );
219 return aRet;
222 OUString DefaultFontConfiguration::getUserInterfaceFont( const LanguageTag& rLanguageTag ) const
224 LanguageTag aLanguageTag( rLanguageTag);
225 if( aLanguageTag.isSystemLocale() )
226 aLanguageTag = SvtSysLocale().GetUILanguageTag();
228 OUString aUIFont = getDefaultFont( aLanguageTag, DefaultFontType::UI_SANS );
230 if( !aUIFont.isEmpty() )
231 return aUIFont;
233 // fallback mechanism (either no configuration or no entry in configuration
235 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";
236 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";
237 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";
238 static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_THAI = u"OONaksit;Tahoma;Lucidasans;Arial Unicode MS";
239 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";
240 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";
241 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;";
242 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;";
244 const OUString aLanguage( aLanguageTag.getLanguage());
246 // optimize font list for some locales, as long as Andale Sans UI does not support them
247 if( aLanguage == "ar" || aLanguage == "he" || aLanguage == "iw" )
249 return FALLBACKFONT_UI_SANS_ARABIC;
251 else if ( aLanguage == "th" )
253 return FALLBACKFONT_UI_SANS_THAI;
255 else if ( aLanguage == "ko" )
257 return FALLBACKFONT_UI_SANS_KOREAN;
259 else if ( aLanguage == "ja" )
261 return FALLBACKFONT_UI_SANS_JAPANESE;
263 else if( aLanguage == "cs" ||
264 aLanguage == "hu" ||
265 aLanguage == "pl" ||
266 aLanguage == "ro" ||
267 aLanguage == "rm" ||
268 aLanguage == "hr" ||
269 aLanguage == "sk" ||
270 aLanguage == "sl" ||
271 aLanguage == "sb")
273 return FALLBACKFONT_UI_SANS_LATIN2;
275 else
277 const Locale& aLocale( aLanguageTag.getLocale());
278 if (MsLangId::isTraditionalChinese(aLocale))
279 return FALLBACKFONT_UI_SANS_CHINTRD;
280 else if (MsLangId::isSimplifiedChinese(aLocale))
281 return FALLBACKFONT_UI_SANS_CHINSIM;
284 return FALLBACKFONT_UI_SANS;
288 * FontSubstConfigItem::get
291 FontSubstConfiguration& FontSubstConfiguration::get()
293 static FontSubstConfiguration theFontSubstConfiguration;
294 return theFontSubstConfiguration;
298 * FontSubstConfigItem::FontSubstConfigItem
301 FontSubstConfiguration::FontSubstConfiguration() :
302 maSubstHash( 300 ),
303 maLanguageTag("en")
305 if (comphelper::IsFuzzing())
306 return;
309 // get service provider
310 Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
311 // create configuration hierarchical access name
312 m_xConfigProvider = theDefaultProvider::get( xContext );
313 Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
315 {"nodepath", Any(OUString( "/org.openoffice.VCL/FontSubstitutions" ))}
316 }));
317 m_xConfigAccess =
318 Reference< XNameAccess >(
319 m_xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
320 aArgs ),
321 UNO_QUERY );
322 if( m_xConfigAccess.is() )
324 const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
325 // fill config hash with empty interfaces
326 for( const OUString& rLocaleString : aLocales )
328 // Feed through LanguageTag for casing.
329 OUString aLoc( LanguageTag( rLocaleString, true).getBcp47( false));
330 m_aSubst[ aLoc ] = LocaleSubst();
331 m_aSubst[ aLoc ].aConfigLocaleString = rLocaleString;
335 catch (const Exception&)
337 // configuration is awry
338 m_xConfigProvider.clear();
339 m_xConfigAccess.clear();
341 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
342 << ", config access: " << m_xConfigAccess.is());
344 if( maLanguageTag.isSystemLocale() )
345 maLanguageTag = SvtSysLocale().GetUILanguageTag();
349 * FontSubstConfigItem::~FontSubstConfigItem
352 FontSubstConfiguration::~FontSubstConfiguration()
354 // release config access
355 m_xConfigAccess.clear();
356 // release config provider
357 m_xConfigProvider.clear();
361 * FontSubstConfigItem::getMapName
364 const char* const aImplKillLeadingList[] =
366 "microsoft",
367 "monotype",
368 "linotype",
369 "baekmuk",
370 "adobe",
371 "nimbus",
372 "zycjk",
373 "itc",
374 "sun",
375 "amt",
376 "ms",
377 "mt",
378 "cg",
379 "hg",
380 "fz",
381 "ipa",
382 "sazanami",
383 "kochi",
384 nullptr
387 const char* const aImplKillTrailingList[] =
389 "microsoft",
390 "monotype",
391 "linotype",
392 "adobe",
393 "nimbus",
394 "itc",
395 "sun",
396 "amt",
397 "ms",
398 "mt",
399 "clm",
400 // Scripts, for compatibility with older versions
401 "we",
402 "cyr",
403 "tur",
404 "wt",
405 "greek",
406 "wl",
407 // CJK extensions
408 "gb",
409 "big5",
410 "pro",
411 "z01",
412 "z02",
413 "z03",
414 "z13",
415 "b01",
416 "w3x12",
417 // Old Printer Fontnames
418 "5cpi",
419 "6cpi",
420 "7cpi",
421 "8cpi",
422 "9cpi",
423 "10cpi",
424 "11cpi",
425 "12cpi",
426 "13cpi",
427 "14cpi",
428 "15cpi",
429 "16cpi",
430 "18cpi",
431 "24cpi",
432 "scale",
433 "pc",
434 nullptr
437 const char* const aImplKillTrailingWithExceptionsList[] =
439 "ce", "monospace", "oldface", nullptr,
440 "ps", "caps", nullptr,
441 nullptr
444 namespace {
446 struct ImplFontAttrWeightSearchData
448 const char* mpStr;
449 FontWeight meWeight;
454 ImplFontAttrWeightSearchData const aImplWeightAttrSearchList[] =
456 // the attribute names are ordered by "first match wins"
457 // e.g. "semilight" should wins over "semi"
458 { "extrablack", WEIGHT_BLACK },
459 { "ultrablack", WEIGHT_BLACK },
460 { "ultrabold", WEIGHT_ULTRABOLD },
461 { "semibold", WEIGHT_SEMIBOLD },
462 { "semilight", WEIGHT_SEMILIGHT },
463 { "semi", WEIGHT_SEMIBOLD },
464 { "demi", WEIGHT_SEMIBOLD },
465 { "black", WEIGHT_BLACK },
466 { "bold", WEIGHT_BOLD },
467 { "heavy", WEIGHT_BLACK },
468 { "ultralight", WEIGHT_ULTRALIGHT },
469 { "light", WEIGHT_LIGHT },
470 { "medium", WEIGHT_MEDIUM },
471 { nullptr, WEIGHT_DONTKNOW },
474 namespace {
476 struct ImplFontAttrWidthSearchData
478 const char* mpStr;
479 FontWidth meWidth;
484 ImplFontAttrWidthSearchData const aImplWidthAttrSearchList[] =
486 { "narrow", WIDTH_CONDENSED },
487 { "semicondensed", WIDTH_SEMI_CONDENSED },
488 { "ultracondensed", WIDTH_ULTRA_CONDENSED },
489 { "semiexpanded", WIDTH_SEMI_EXPANDED },
490 { "ultraexpanded", WIDTH_ULTRA_EXPANDED },
491 { "expanded", WIDTH_EXPANDED },
492 { "wide", WIDTH_ULTRA_EXPANDED },
493 { "condensed", WIDTH_CONDENSED },
494 { "cond", WIDTH_CONDENSED },
495 { "cn", WIDTH_CONDENSED },
496 { nullptr, WIDTH_DONTKNOW },
499 namespace {
501 struct ImplFontAttrTypeSearchData
503 const char* mpStr;
504 ImplFontAttrs mnType;
509 ImplFontAttrTypeSearchData const aImplTypeAttrSearchList[] =
511 { "monotype", ImplFontAttrs::None },
512 { "linotype", ImplFontAttrs::None },
513 { "titling", ImplFontAttrs::Titling },
514 { "captitals", ImplFontAttrs::Capitals },
515 { "captital", ImplFontAttrs::Capitals },
516 { "caps", ImplFontAttrs::Capitals },
517 { "italic", ImplFontAttrs::Italic },
518 { "oblique", ImplFontAttrs::Italic },
519 { "rounded", ImplFontAttrs::Rounded },
520 { "outline", ImplFontAttrs::Outline },
521 { "shadow", ImplFontAttrs::Shadow },
522 { "handwriting", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
523 { "hand", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
524 { "signet", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
525 { "script", ImplFontAttrs::BrushScript | ImplFontAttrs::Script },
526 { "calligraphy", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
527 { "chancery", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
528 { "corsiva", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
529 { "gothic", ImplFontAttrs::SansSerif | ImplFontAttrs::Gothic },
530 { "schoolbook", ImplFontAttrs::Serif | ImplFontAttrs::Schoolbook },
531 { "schlbk", ImplFontAttrs::Serif | ImplFontAttrs::Schoolbook },
532 { "typewriter", ImplFontAttrs::Typewriter | ImplFontAttrs::Fixed },
533 { "lineprinter", ImplFontAttrs::Typewriter | ImplFontAttrs::Fixed },
534 { "monospaced", ImplFontAttrs::Fixed },
535 { "monospace", ImplFontAttrs::Fixed },
536 { "mono", ImplFontAttrs::Fixed },
537 { "fixed", ImplFontAttrs::Fixed },
538 { "sansserif", ImplFontAttrs::SansSerif },
539 { "sans", ImplFontAttrs::SansSerif },
540 { "swiss", ImplFontAttrs::SansSerif },
541 { "serif", ImplFontAttrs::Serif },
542 { "bright", ImplFontAttrs::Serif },
543 { "symbols", ImplFontAttrs::Symbol },
544 { "symbol", ImplFontAttrs::Symbol },
545 { "dingbats", ImplFontAttrs::Symbol },
546 { "dings", ImplFontAttrs::Symbol },
547 { "ding", ImplFontAttrs::Symbol },
548 { "bats", ImplFontAttrs::Symbol },
549 { "math", ImplFontAttrs::Symbol },
550 { "oldstyle", ImplFontAttrs::OtherStyle },
551 { "oldface", ImplFontAttrs::OtherStyle },
552 { "old", ImplFontAttrs::OtherStyle },
553 { "new", ImplFontAttrs::None },
554 { "modern", ImplFontAttrs::None },
555 { "lucida", ImplFontAttrs::None },
556 { "regular", ImplFontAttrs::None },
557 { "extended", ImplFontAttrs::None },
558 { "extra", ImplFontAttrs::OtherStyle },
559 { "ext", ImplFontAttrs::None },
560 { "scalable", ImplFontAttrs::None },
561 { "scale", ImplFontAttrs::None },
562 { "nimbus", ImplFontAttrs::None },
563 { "adobe", ImplFontAttrs::None },
564 { "itc", ImplFontAttrs::None },
565 { "amt", ImplFontAttrs::None },
566 { "mt", ImplFontAttrs::None },
567 { "ms", ImplFontAttrs::None },
568 { "cpi", ImplFontAttrs::None },
569 { "no", ImplFontAttrs::None },
570 { nullptr, ImplFontAttrs::None },
573 static bool ImplKillLeading( OUString& rName, const char* const* ppStr )
575 for(; *ppStr; ++ppStr )
577 const char* pStr = *ppStr;
578 const sal_Unicode* pNameStr = rName.getStr();
579 while ( (*pNameStr == static_cast<sal_Unicode>(static_cast<unsigned char>(*pStr))) && *pStr )
581 pNameStr++;
582 pStr++;
584 if ( !*pStr )
586 sal_Int32 nLen = static_cast<sal_Int32>(pNameStr - rName.getStr());
587 rName = rName.copy(nLen);
588 return true;
592 // special case for Baekmuk
593 // TODO: allow non-ASCII KillLeading list
594 const sal_Unicode* pNameStr = rName.getStr();
595 if( (pNameStr[0]==0xBC31) && (pNameStr[1]==0xBC35) )
597 sal_Int32 nLen = (pNameStr[2]==0x0020) ? 3 : 2;
598 rName = rName.copy(nLen);
599 return true;
602 return false;
605 static sal_Int32 ImplIsTrailing( std::u16string_view rName, const char* pStr )
607 size_t nStrLen = strlen( pStr );
608 if( nStrLen >= rName.size() )
609 return 0;
611 const sal_Unicode* pEndName = rName.data() + rName.size();
612 const sal_Unicode* pNameStr = pEndName - nStrLen;
613 do if( *(pNameStr++) != *(pStr++) )
614 return 0;
615 while( *pStr );
617 return nStrLen;
620 static bool ImplKillTrailing( OUString& rName, const char* const* ppStr )
622 for(; *ppStr; ++ppStr )
624 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
625 if( nTrailLen )
627 rName = rName.copy(0, rName.getLength() - nTrailLen );
628 return true;
632 return false;
635 static bool ImplKillTrailingWithExceptions( OUString& rName, const char* const* ppStr )
637 for(; *ppStr; ++ppStr )
639 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
640 if( nTrailLen )
642 // check string match against string exceptions
643 while( *++ppStr )
644 if( ImplIsTrailing( rName, *ppStr ) )
645 return false;
647 rName = rName.copy(0, rName.getLength() - nTrailLen );
648 return true;
650 else
652 // skip exception strings
653 while( *++ppStr ) {}
657 return false;
660 static bool ImplFindAndErase( OUString& rName, const char* pStr )
662 sal_Int32 nLen = static_cast<sal_Int32>(strlen(pStr));
663 sal_Int32 nPos = rName.indexOfAsciiL(pStr, nLen );
664 if ( nPos < 0 )
665 return false;
667 OUStringBuffer sBuff(rName);
668 sBuff.remove(nPos, nLen);
669 rName = sBuff.makeStringAndClear();
670 return true;
673 void FontSubstConfiguration::getMapName( const OUString& rOrgName, OUString& rShortName,
674 OUString& rFamilyName, FontWeight& rWeight,
675 FontWidth& rWidth, ImplFontAttrs& rType )
677 rShortName = rOrgName;
679 // TODO: get rid of the crazy O(N*strlen) searches below
680 // they should be possible in O(strlen)
682 // Kill leading vendor names and other unimportant data
683 ImplKillLeading( rShortName, aImplKillLeadingList );
685 // Kill trailing vendor names and other unimportant data
686 ImplKillTrailing( rShortName, aImplKillTrailingList );
687 ImplKillTrailingWithExceptions( rShortName, aImplKillTrailingWithExceptionsList );
689 rFamilyName = rShortName;
691 // Kill attributes from the name and update the data
692 // Weight
693 const ImplFontAttrWeightSearchData* pWeightList = aImplWeightAttrSearchList;
694 while ( pWeightList->mpStr )
696 if ( ImplFindAndErase( rFamilyName, pWeightList->mpStr ) )
698 if ( (rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL) )
699 rWeight = pWeightList->meWeight;
700 break;
702 pWeightList++;
705 // Width
706 const ImplFontAttrWidthSearchData* pWidthList = aImplWidthAttrSearchList;
707 while ( pWidthList->mpStr )
709 if ( ImplFindAndErase( rFamilyName, pWidthList->mpStr ) )
711 if ( (rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL) )
712 rWidth = pWidthList->meWidth;
713 break;
715 pWidthList++;
718 // Type
719 rType = ImplFontAttrs::None;
720 const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
721 while ( pTypeList->mpStr )
723 if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
724 rType |= pTypeList->mnType;
725 pTypeList++;
728 // Remove numbers
729 // TODO: also remove localized and fullwidth digits
730 sal_Int32 i = 0;
731 OUStringBuffer sBuff(rFamilyName);
732 while ( i < sBuff.getLength() )
734 sal_Unicode c = sBuff[ i ];
735 if ( (c >= 0x0030) && (c <= 0x0039) )
736 sBuff.remove(i, 1);
737 else
738 i++;
742 namespace {
744 struct StrictStringSort
746 bool operator()( const FontNameAttr& rLeft, const FontNameAttr& rRight )
747 { return rLeft.Name.compareTo( rRight.Name ) < 0; }
752 // The entries in this table must match the bits in the ImplFontAttrs enum.
754 const char* const pAttribNames[] =
756 "default",
757 "standard",
758 "normal",
759 "symbol",
760 "fixed",
761 "sansserif",
762 "serif",
763 "decorative",
764 "special",
765 "italic",
766 "title",
767 "capitals",
768 "cjk",
769 "cjk_jp",
770 "cjk_sc",
771 "cjk_tc",
772 "cjk_kr",
773 "ctl",
774 "nonelatin",
775 "full",
776 "outline",
777 "shadow",
778 "rounded",
779 "typewriter",
780 "script",
781 "handwriting",
782 "chancery",
783 "comic",
784 "brushscript",
785 "gothic",
786 "schoolbook",
787 "other"
790 namespace {
792 struct enum_convert
794 const char* pName;
795 int nEnum;
800 const enum_convert pWeightNames[] =
802 { "normal", WEIGHT_NORMAL },
803 { "medium", WEIGHT_MEDIUM },
804 { "bold", WEIGHT_BOLD },
805 { "black", WEIGHT_BLACK },
806 { "semibold", WEIGHT_SEMIBOLD },
807 { "light", WEIGHT_LIGHT },
808 { "semilight", WEIGHT_SEMILIGHT },
809 { "ultrabold", WEIGHT_ULTRABOLD },
810 { "semi", WEIGHT_SEMIBOLD },
811 { "demi", WEIGHT_SEMIBOLD },
812 { "heavy", WEIGHT_BLACK },
813 { "unknown", WEIGHT_DONTKNOW },
814 { "thin", WEIGHT_THIN },
815 { "ultralight", WEIGHT_ULTRALIGHT }
818 const enum_convert pWidthNames[] =
820 { "normal", WIDTH_NORMAL },
821 { "condensed", WIDTH_CONDENSED },
822 { "expanded", WIDTH_EXPANDED },
823 { "unknown", WIDTH_DONTKNOW },
824 { "ultracondensed", WIDTH_ULTRA_CONDENSED },
825 { "extracondensed", WIDTH_EXTRA_CONDENSED },
826 { "semicondensed", WIDTH_SEMI_CONDENSED },
827 { "semiexpanded", WIDTH_SEMI_EXPANDED },
828 { "extraexpanded", WIDTH_EXTRA_EXPANDED },
829 { "ultraexpanded", WIDTH_ULTRA_EXPANDED }
832 void FontSubstConfiguration::fillSubstVector( const css::uno::Reference< XNameAccess >& rFont,
833 const OUString& rType,
834 std::vector< OUString >& rSubstVector ) const
838 Any aAny = rFont->getByName( rType );
839 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
841 sal_Int32 nLength = pLine->getLength();
842 if( nLength )
844 const sal_Unicode* pStr = pLine->getStr();
845 sal_Int32 nTokens = 0;
846 // count tokens
847 while( nLength-- )
849 if( *pStr++ == ';' )
850 nTokens++;
852 rSubstVector.clear();
853 // optimize performance, heap fragmentation
854 rSubstVector.reserve( nTokens );
855 sal_Int32 nIndex = 0;
856 while( nIndex != -1 )
858 OUString aSubst( pLine->getToken( 0, ';', nIndex ) );
859 if( !aSubst.isEmpty() )
861 auto itPair = maSubstHash.insert( aSubst );
862 if (!itPair.second)
863 aSubst = *itPair.first;
864 rSubstVector.push_back( aSubst );
870 catch (const NoSuchElementException&)
873 catch (const WrappedTargetException&)
878 // static
879 FontWeight FontSubstConfiguration::getSubstWeight( const css::uno::Reference< XNameAccess >& rFont,
880 const OUString& rType )
882 int weight = -1;
885 Any aAny = rFont->getByName( rType );
886 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
888 if( !pLine->isEmpty() )
890 for( weight=std::size(pWeightNames)-1; weight >= 0; weight-- )
891 if( pLine->equalsIgnoreAsciiCaseAscii( pWeightNames[weight].pName ) )
892 break;
894 SAL_WARN_IF(weight < 0, "unotools.config", "Error: invalid weight " << *pLine);
897 catch (const NoSuchElementException&)
900 catch (const WrappedTargetException&)
903 return static_cast<FontWeight>( weight >= 0 ? pWeightNames[weight].nEnum : WEIGHT_DONTKNOW );
906 // static
907 FontWidth FontSubstConfiguration::getSubstWidth( const css::uno::Reference< XNameAccess >& rFont,
908 const OUString& rType )
910 int width = -1;
913 Any aAny = rFont->getByName( rType );
914 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
916 if( !pLine->isEmpty() )
918 for( width=std::size(pWidthNames)-1; width >= 0; width-- )
919 if( pLine->equalsIgnoreAsciiCaseAscii( pWidthNames[width].pName ) )
920 break;
922 SAL_WARN_IF( width < 0, "unotools.config", "Error: invalid width " << *pLine);
925 catch (const NoSuchElementException&)
928 catch (const WrappedTargetException&)
931 return static_cast<FontWidth>( width >= 0 ? pWidthNames[width].nEnum : WIDTH_DONTKNOW );
934 // static
935 ImplFontAttrs FontSubstConfiguration::getSubstType( const css::uno::Reference< XNameAccess >& rFont,
936 const OUString& rType )
938 sal_uInt32 type = 0;
941 Any aAny = rFont->getByName( rType );
942 auto pLine = o3tl::tryAccess<OUString>(aAny);
943 if( !pLine )
944 return ImplFontAttrs::None;
945 if( pLine->isEmpty() )
946 return ImplFontAttrs::None;
947 sal_Int32 nIndex = 0;
948 while( nIndex != -1 )
950 OUString aToken( pLine->getToken( 0, ',', nIndex ) );
951 for( int k = 0; k < 32; k++ )
952 if( aToken.equalsIgnoreAsciiCaseAscii( pAttribNames[k] ) )
954 type |= sal_uInt32(1) << k;
955 break;
958 assert(((type & ~o3tl::typed_flags<ImplFontAttrs>::mask) == 0) && "invalid font attributes");
960 catch (const NoSuchElementException&)
963 catch (const WrappedTargetException&)
967 return static_cast<ImplFontAttrs>(type);
970 void FontSubstConfiguration::readLocaleSubst( const OUString& rBcp47 ) const
972 std::unordered_map< OUString, LocaleSubst >::const_iterator it = m_aSubst.find( rBcp47 );
973 if( it == m_aSubst.end() )
974 return;
976 if( it->second.bConfigRead )
977 return;
979 it->second.bConfigRead = true;
980 Reference< XNameAccess > xNode;
983 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
984 aAny >>= xNode;
986 catch (const NoSuchElementException&)
989 catch (const WrappedTargetException&)
992 if( !xNode.is() )
993 return;
995 const Sequence< OUString > aFonts = xNode->getElementNames();
996 int nFonts = aFonts.getLength();
997 // improve performance, heap fragmentation
998 it->second.aSubstAttributes.reserve( nFonts );
1000 // strings for subst retrieval, construct only once
1001 static constexpr OUStringLiteral aSubstFontsStr ( u"SubstFonts" );
1002 static constexpr OUStringLiteral aSubstFontsMSStr( u"SubstFontsMS" );
1003 static constexpr OUStringLiteral aSubstWeightStr ( u"FontWeight" );
1004 static constexpr OUStringLiteral aSubstWidthStr ( u"FontWidth" );
1005 static constexpr OUStringLiteral aSubstTypeStr ( u"FontType" );
1006 for( const OUString& rFontName : aFonts )
1008 Reference< XNameAccess > xFont;
1011 Any aAny = xNode->getByName( rFontName );
1012 aAny >>= xFont;
1014 catch (const NoSuchElementException&)
1017 catch (const WrappedTargetException&)
1020 if( ! xFont.is() )
1022 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName);
1023 continue;
1026 FontNameAttr aAttr;
1027 // read subst attributes from config
1028 aAttr.Name = rFontName;
1029 fillSubstVector( xFont, aSubstFontsStr, aAttr.Substitutions );
1030 fillSubstVector( xFont, aSubstFontsMSStr, aAttr.MSSubstitutions );
1031 aAttr.Weight = getSubstWeight( xFont, aSubstWeightStr );
1032 aAttr.Width = getSubstWidth( xFont, aSubstWidthStr );
1033 aAttr.Type = getSubstType( xFont, aSubstTypeStr );
1035 // finally insert this entry
1036 it->second.aSubstAttributes.push_back( aAttr );
1038 std::sort( it->second.aSubstAttributes.begin(), it->second.aSubstAttributes.end(), StrictStringSort() );
1041 const FontNameAttr* FontSubstConfiguration::getSubstInfo( const OUString& rFontName ) const
1043 if( rFontName.isEmpty() )
1044 return nullptr;
1046 // search if a (language dep.) replacement table for the given font exists
1047 // fallback is english
1048 OUString aSearchFont( rFontName.toAsciiLowerCase() );
1049 FontNameAttr aSearchAttr;
1050 aSearchAttr.Name = aSearchFont;
1052 ::std::vector< OUString > aFallbacks( maLanguageTag.getFallbackStrings( true));
1053 if (maLanguageTag.getLanguage() != "en")
1054 aFallbacks.emplace_back("en");
1056 for (const auto& rFallback : aFallbacks)
1058 std::unordered_map< OUString, LocaleSubst >::const_iterator lang = m_aSubst.find( rFallback );
1059 if( lang != m_aSubst.end() )
1061 if( ! lang->second.bConfigRead )
1062 readLocaleSubst( rFallback );
1063 // try to find an exact match
1064 // because the list is sorted this will also find fontnames of the form searchfontname*
1065 std::vector< FontNameAttr >::const_iterator it = ::std::lower_bound( lang->second.aSubstAttributes.begin(), lang->second.aSubstAttributes.end(), aSearchAttr, StrictStringSort() );
1066 if( it != lang->second.aSubstAttributes.end())
1068 const FontNameAttr& rFoundAttr = *it;
1069 // a search for "abcblack" may match with an entry for "abc"
1070 // the reverse is not a good idea (e.g. #i112731# alba->albani)
1071 if( rFoundAttr.Name.getLength() <= aSearchFont.getLength() )
1072 if( aSearchFont.startsWith( rFoundAttr.Name))
1073 return &rFoundAttr;
1077 return nullptr;
1080 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */