1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
37
38 #include <string.h>
39 #include <algorithm>
40
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;
46
47 /*
48 * DefaultFontConfiguration
49 */
50
getKeyType(DefaultFontType nKeyType)51 static OUString getKeyType( DefaultFontType nKeyType )
52 {
53 switch( nKeyType )
54 {
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;
81 }
82 }
83
get()84 DefaultFontConfiguration& DefaultFontConfiguration::get()
85 {
86 static DefaultFontConfiguration theDefaultFontConfiguration;
87 return theDefaultFontConfiguration;
88 }
89
DefaultFontConfiguration()90 DefaultFontConfiguration::DefaultFontConfiguration()
91 {
92 if (comphelper::IsFuzzing())
93 return;
94 // create configuration hierarchical access name
95 try
96 {
97 // get service provider
98 m_xConfigProvider = theDefaultProvider::get(comphelper::getProcessComponentContext());
99 Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
100 {
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() )
109 {
110 const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
111 // fill config hash with empty interfaces
112 for( const OUString& rLocaleString : aLocales )
113 {
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;
118 }
119 }
120 }
121 catch (const Exception&)
122 {
123 // configuration is awry
124 m_xConfigProvider.clear();
125 m_xConfigAccess.clear();
126 }
127 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
128 << ", config access: " << m_xConfigAccess.is());
129 }
130
~DefaultFontConfiguration()131 DefaultFontConfiguration::~DefaultFontConfiguration()
132 {
133 // release all nodes
134 m_aConfig.clear();
135 // release top node
136 m_xConfigAccess.clear();
137 // release config provider
138 m_xConfigProvider.clear();
139 }
140
tryLocale(const OUString & rBcp47,const OUString & rType) const141 OUString DefaultFontConfiguration::tryLocale( const OUString& rBcp47, const OUString& rType ) const
142 {
143 OUString aRet;
144
145 std::unordered_map< OUString, LocaleAccess >::const_iterator it = m_aConfig.find( rBcp47 );
146 if( it != m_aConfig.end() )
147 {
148 if( !it->second.xAccess.is() )
149 {
150 try
151 {
152 Reference< XNameAccess > xNode;
153 if ( m_xConfigAccess->hasByName( it->second.aConfigLocaleString ) )
154 {
155 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
156 if( aAny >>= xNode )
157 it->second.xAccess = std::move(xNode);
158 }
159 }
160 catch (const NoSuchElementException&)
161 {
162 }
163 catch (const WrappedTargetException&)
164 {
165 }
166 }
167 if( it->second.xAccess.is() )
168 {
169 try
170 {
171 if ( it->second.xAccess->hasByName( rType ) )
172 {
173 Any aAny = it->second.xAccess->getByName( rType );
174 aAny >>= aRet;
175 }
176 }
177 catch (const NoSuchElementException&)
178 {
179 }
180 catch (const WrappedTargetException&)
181 {
182 }
183 }
184 }
185
186 return aRet;
187 }
188
getDefaultFont(const LanguageTag & rLanguageTag,DefaultFontType nType) const189 OUString DefaultFontConfiguration::getDefaultFont( const LanguageTag& rLanguageTag, DefaultFontType nType ) const
190 {
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())
195 {
196 if (rLanguageTag.isIsoLocale())
197 {
198 if (!rLanguageTag.getCountry().isEmpty())
199 {
200 aRet = tryLocale( rLanguageTag.getLanguage(), aType );
201 }
202 }
203 else
204 {
205 ::std::vector< OUString > aFallbacks( rLanguageTag.getFallbackStrings( false));
206 for (const auto& rFallback : aFallbacks)
207 {
208 aRet = tryLocale( rFallback, aType );
209 if (!aRet.isEmpty())
210 break;
211 }
212 }
213 }
214 if( aRet.isEmpty() )
215 {
216 aRet = tryLocale( u"en"_ustr, aType );
217 }
218 return aRet;
219 }
220
getUserInterfaceFont(const LanguageTag & rLanguageTag) const221 OUString DefaultFontConfiguration::getUserInterfaceFont( const LanguageTag& rLanguageTag ) const
222 {
223 LanguageTag aLanguageTag( rLanguageTag);
224 if( aLanguageTag.isSystemLocale() )
225 aLanguageTag = SvtSysLocale().GetUILanguageTag();
226
227 OUString aUIFont = getDefaultFont( aLanguageTag, DefaultFontType::UI_SANS );
228
229 if( !aUIFont.isEmpty() )
230 return aUIFont;
231
232 // fallback mechanism (either no configuration or no entry in configuration
233
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;";
242
243 const OUString aLanguage( aLanguageTag.getLanguage());
244
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" )
247 {
248 return FALLBACKFONT_UI_SANS_ARABIC;
249 }
250 else if ( aLanguage == "th" )
251 {
252 return FALLBACKFONT_UI_SANS_THAI;
253 }
254 else if ( aLanguage == "ko" )
255 {
256 return FALLBACKFONT_UI_SANS_KOREAN;
257 }
258 else if ( aLanguage == "ja" )
259 {
260 return FALLBACKFONT_UI_SANS_JAPANESE;
261 }
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")
271 {
272 return FALLBACKFONT_UI_SANS_LATIN2;
273 }
274 else
275 {
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;
281 }
282
283 return FALLBACKFONT_UI_SANS;
284 }
285
286 /*
287 * FontSubstConfigItem::get
288 */
289
get()290 FontSubstConfiguration& FontSubstConfiguration::get()
291 {
292 static FontSubstConfiguration theFontSubstConfiguration;
293 return theFontSubstConfiguration;
294 }
295
296 /*
297 * FontSubstConfigItem::FontSubstConfigItem
298 */
299
FontSubstConfiguration()300 FontSubstConfiguration::FontSubstConfiguration() :
301 maSubstHash( 300 ),
302 maLanguageTag(u"en"_ustr)
303 {
304 if (comphelper::IsFuzzing())
305 return;
306 try
307 {
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(
313 {
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() )
322 {
323 const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
324 // fill config hash with empty interfaces
325 for( const OUString& rLocaleString : aLocales )
326 {
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;
331 }
332 }
333 }
334 catch (const Exception&)
335 {
336 // configuration is awry
337 m_xConfigProvider.clear();
338 m_xConfigAccess.clear();
339 }
340 SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
341 << ", config access: " << m_xConfigAccess.is());
342
343 if( maLanguageTag.isSystemLocale() )
344 maLanguageTag = SvtSysLocale().GetUILanguageTag();
345 }
346
347 /*
348 * FontSubstConfigItem::~FontSubstConfigItem
349 */
350
~FontSubstConfiguration()351 FontSubstConfiguration::~FontSubstConfiguration()
352 {
353 // release config access
354 m_xConfigAccess.clear();
355 // release config provider
356 m_xConfigProvider.clear();
357 }
358
359 /*
360 * FontSubstConfigItem::getMapName
361 */
362
363 const char* const aImplKillLeadingList[] =
364 {
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
384 };
385
386 const char* const aImplKillTrailingList[] =
387 {
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
434 };
435
436 const char* const aImplKillTrailingWithExceptionsList[] =
437 {
438 "ce", "monospace", "oldface", nullptr,
439 "ps", "caps", nullptr,
440 nullptr
441 };
442
443 namespace {
444
445 struct ImplFontAttrWeightSearchData
446 {
447 const char* mpStr;
448 FontWeight meWeight;
449 };
450
451 }
452
453 ImplFontAttrWeightSearchData const aImplWeightAttrSearchList[] =
454 {
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 },
471 };
472
473 namespace {
474
475 struct ImplFontAttrWidthSearchData
476 {
477 const char* mpStr;
478 FontWidth meWidth;
479 };
480
481 }
482
483 ImplFontAttrWidthSearchData const aImplWidthAttrSearchList[] =
484 {
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 },
496 };
497
498 namespace {
499
500 struct ImplFontAttrTypeSearchData
501 {
502 const char* mpStr;
503 ImplFontAttrs mnType;
504 };
505
506 }
507
508 ImplFontAttrTypeSearchData const aImplTypeAttrSearchList[] =
509 {
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 },
570 };
571
ImplKillLeading(OUString & rName,const char * const * ppStr)572 static bool ImplKillLeading( OUString& rName, const char* const* ppStr )
573 {
574 for(; *ppStr; ++ppStr )
575 {
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 )
579 {
580 pNameStr++;
581 pStr++;
582 }
583 if ( !*pStr )
584 {
585 sal_Int32 nLen = static_cast<sal_Int32>(pNameStr - rName.getStr());
586 rName = rName.copy(nLen);
587 return true;
588 }
589 }
590
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) )
595 {
596 sal_Int32 nLen = (pNameStr[2]==0x0020) ? 3 : 2;
597 rName = rName.copy(nLen);
598 return true;
599 }
600
601 return false;
602 }
603
ImplIsTrailing(std::u16string_view rName,const char * pStr)604 static sal_Int32 ImplIsTrailing( std::u16string_view rName, const char* pStr )
605 {
606 size_t nStrLen = strlen( pStr );
607 if( nStrLen >= rName.size() )
608 return 0;
609
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 );
615
616 return nStrLen;
617 }
618
ImplKillTrailing(OUString & rName,const char * const * ppStr)619 static bool ImplKillTrailing( OUString& rName, const char* const* ppStr )
620 {
621 for(; *ppStr; ++ppStr )
622 {
623 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
624 if( nTrailLen )
625 {
626 rName = rName.copy(0, rName.getLength() - nTrailLen );
627 return true;
628 }
629 }
630
631 return false;
632 }
633
ImplKillTrailingWithExceptions(OUString & rName,const char * const * ppStr)634 static bool ImplKillTrailingWithExceptions( OUString& rName, const char* const* ppStr )
635 {
636 for(; *ppStr; ++ppStr )
637 {
638 sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
639 if( nTrailLen )
640 {
641 // check string match against string exceptions
642 while( *++ppStr )
643 if( ImplIsTrailing( rName, *ppStr ) )
644 return false;
645
646 rName = rName.copy(0, rName.getLength() - nTrailLen );
647 return true;
648 }
649 else
650 {
651 // skip exception strings
652 while( *++ppStr ) {}
653 }
654 }
655
656 return false;
657 }
658
ImplFindAndErase(OUString & rName,const char * pStr)659 static bool ImplFindAndErase( OUString& rName, const char* pStr )
660 {
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;
665
666 OUStringBuffer sBuff(rName);
667 sBuff.remove(nPos, nLen);
668 rName = sBuff.makeStringAndClear();
669 return true;
670 }
671
getMapName(const OUString & rOrgName,OUString & rShortName,OUString & rFamilyName,FontWeight & rWeight,FontWidth & rWidth,ImplFontAttrs & rType)672 void FontSubstConfiguration::getMapName( const OUString& rOrgName, OUString& rShortName,
673 OUString& rFamilyName, FontWeight& rWeight,
674 FontWidth& rWidth, ImplFontAttrs& rType )
675 {
676 rShortName = rOrgName;
677
678 // TODO: get rid of the crazy O(N*strlen) searches below
679 // they should be possible in O(strlen)
680
681 // Kill leading vendor names and other unimportant data
682 ImplKillLeading( rShortName, aImplKillLeadingList );
683
684 // Kill trailing vendor names and other unimportant data
685 ImplKillTrailing( rShortName, aImplKillTrailingList );
686 ImplKillTrailingWithExceptions( rShortName, aImplKillTrailingWithExceptionsList );
687
688 rFamilyName = rShortName;
689
690 // Kill attributes from the name and update the data
691 // Weight
692 const ImplFontAttrWeightSearchData* pWeightList = aImplWeightAttrSearchList;
693 while ( pWeightList->mpStr )
694 {
695 if ( ImplFindAndErase( rFamilyName, pWeightList->mpStr ) )
696 {
697 if ( (rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL) )
698 rWeight = pWeightList->meWeight;
699 break;
700 }
701 pWeightList++;
702 }
703
704 // Width
705 const ImplFontAttrWidthSearchData* pWidthList = aImplWidthAttrSearchList;
706 while ( pWidthList->mpStr )
707 {
708 if ( ImplFindAndErase( rFamilyName, pWidthList->mpStr ) )
709 {
710 if ( (rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL) )
711 rWidth = pWidthList->meWidth;
712 break;
713 }
714 pWidthList++;
715 }
716
717 // Type
718 rType = ImplFontAttrs::None;
719 const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
720 while ( pTypeList->mpStr )
721 {
722 if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
723 rType |= pTypeList->mnType;
724 pTypeList++;
725 }
726
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() )
732 {
733 sal_Unicode c = sBuff[ i ];
734 if ( (c >= 0x0030) && (c <= 0x0039) )
735 sBuff.remove(i, 1);
736 else
737 i++;
738 }
739 }
740
741 namespace {
742
743 struct StrictStringSort
744 {
operator ()__anonbf4f69d40411::StrictStringSort745 bool operator()( const FontNameAttr& rLeft, const FontNameAttr& rRight )
746 { return rLeft.Name.compareTo( rRight.Name ) < 0; }
747 };
748
749 }
750
751 // The entries in this table must match the bits in the ImplFontAttrs enum.
752
753 const char* const pAttribNames[] =
754 {
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"
787 };
788
789 namespace {
790
791 struct enum_convert
792 {
793 const char* pName;
794 int nEnum;
795 };
796
797 }
798
799 const enum_convert pWeightNames[] =
800 {
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 }
815 };
816
817 const enum_convert pWidthNames[] =
818 {
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 }
829 };
830
fillSubstVector(const css::uno::Reference<XNameAccess> & rFont,const OUString & rType,std::vector<OUString> & rSubstVector) const831 void FontSubstConfiguration::fillSubstVector( const css::uno::Reference< XNameAccess >& rFont,
832 const OUString& rType,
833 std::vector< OUString >& rSubstVector ) const
834 {
835 try
836 {
837 Any aAny = rFont->getByName( rType );
838 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
839 {
840 sal_Int32 nLength = pLine->getLength();
841 if( nLength )
842 {
843 const sal_Unicode* pStr = pLine->getStr();
844 sal_Int32 nTokens = 0;
845 // count tokens
846 while( nLength-- )
847 {
848 if( *pStr++ == ';' )
849 nTokens++;
850 }
851 rSubstVector.clear();
852 // optimize performance, heap fragmentation
853 rSubstVector.reserve( nTokens );
854 sal_Int32 nIndex = 0;
855 while( nIndex != -1 )
856 {
857 OUString aSubst( pLine->getToken( 0, ';', nIndex ) );
858 if( !aSubst.isEmpty() )
859 {
860 auto itPair = maSubstHash.insert( aSubst );
861 if (!itPair.second)
862 aSubst = *itPair.first;
863 rSubstVector.push_back( aSubst );
864 }
865 }
866 }
867 }
868 }
869 catch (const NoSuchElementException&)
870 {
871 }
872 catch (const WrappedTargetException&)
873 {
874 }
875 }
876
877 // static
getSubstWeight(const css::uno::Reference<XNameAccess> & rFont,const OUString & rType)878 FontWeight FontSubstConfiguration::getSubstWeight( const css::uno::Reference< XNameAccess >& rFont,
879 const OUString& rType )
880 {
881 int weight = -1;
882 try
883 {
884 Any aAny = rFont->getByName( rType );
885 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
886 {
887 if( !pLine->isEmpty() )
888 {
889 for( weight=std::size(pWeightNames)-1; weight >= 0; weight-- )
890 if( pLine->equalsIgnoreAsciiCaseAscii( pWeightNames[weight].pName ) )
891 break;
892 }
893 SAL_WARN_IF(weight < 0, "unotools.config", "Error: invalid weight " << *pLine);
894 }
895 }
896 catch (const NoSuchElementException&)
897 {
898 }
899 catch (const WrappedTargetException&)
900 {
901 }
902 return static_cast<FontWeight>( weight >= 0 ? pWeightNames[weight].nEnum : WEIGHT_DONTKNOW );
903 }
904
905 // static
getSubstWidth(const css::uno::Reference<XNameAccess> & rFont,const OUString & rType)906 FontWidth FontSubstConfiguration::getSubstWidth( const css::uno::Reference< XNameAccess >& rFont,
907 const OUString& rType )
908 {
909 int width = -1;
910 try
911 {
912 Any aAny = rFont->getByName( rType );
913 if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
914 {
915 if( !pLine->isEmpty() )
916 {
917 for( width=std::size(pWidthNames)-1; width >= 0; width-- )
918 if( pLine->equalsIgnoreAsciiCaseAscii( pWidthNames[width].pName ) )
919 break;
920 }
921 SAL_WARN_IF( width < 0, "unotools.config", "Error: invalid width " << *pLine);
922 }
923 }
924 catch (const NoSuchElementException&)
925 {
926 }
927 catch (const WrappedTargetException&)
928 {
929 }
930 return static_cast<FontWidth>( width >= 0 ? pWidthNames[width].nEnum : WIDTH_DONTKNOW );
931 }
932
933 // static
getSubstType(const css::uno::Reference<XNameAccess> & rFont,const OUString & rType)934 ImplFontAttrs FontSubstConfiguration::getSubstType( const css::uno::Reference< XNameAccess >& rFont,
935 const OUString& rType )
936 {
937 sal_uInt32 type = 0;
938 try
939 {
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 )
948 {
949 OUString aToken( pLine->getToken( 0, ',', nIndex ) );
950 for( int k = 0; k < 32; k++ )
951 if( aToken.equalsIgnoreAsciiCaseAscii( pAttribNames[k] ) )
952 {
953 type |= sal_uInt32(1) << k;
954 break;
955 }
956 }
957 assert(((type & ~o3tl::typed_flags<ImplFontAttrs>::mask) == 0) && "invalid font attributes");
958 }
959 catch (const NoSuchElementException&)
960 {
961 }
962 catch (const WrappedTargetException&)
963 {
964 }
965
966 return static_cast<ImplFontAttrs>(type);
967 }
968
readLocaleSubst(const OUString & rBcp47) const969 void FontSubstConfiguration::readLocaleSubst( const OUString& rBcp47 ) const
970 {
971 std::unordered_map< OUString, LocaleSubst >::const_iterator it = m_aSubst.find( rBcp47 );
972 if( it == m_aSubst.end() )
973 return;
974
975 if( it->second.bConfigRead )
976 return;
977
978 it->second.bConfigRead = true;
979 Reference< XNameAccess > xNode;
980 try
981 {
982 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
983 aAny >>= xNode;
984 }
985 catch (const NoSuchElementException&)
986 {
987 }
988 catch (const WrappedTargetException&)
989 {
990 }
991 if( !xNode.is() )
992 return;
993
994 const Sequence< OUString > aFonts = xNode->getElementNames();
995 int nFonts = aFonts.getLength();
996 // improve performance, heap fragmentation
997 it->second.aSubstAttributes.reserve( nFonts );
998
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 )
1006 {
1007 Reference< XNameAccess > xFont;
1008 try
1009 {
1010 Any aAny = xNode->getByName( rFontName );
1011 aAny >>= xFont;
1012 }
1013 catch (const NoSuchElementException&)
1014 {
1015 }
1016 catch (const WrappedTargetException&)
1017 {
1018 }
1019 if( ! xFont.is() )
1020 {
1021 SAL_WARN("unotools.config", "did not get font attributes for " << rFontName);
1022 continue;
1023 }
1024
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 );
1033
1034 // finally insert this entry
1035 it->second.aSubstAttributes.push_back(std::move(aAttr));
1036 }
1037 std::sort( it->second.aSubstAttributes.begin(), it->second.aSubstAttributes.end(), StrictStringSort() );
1038 }
1039
getSubstInfo(const OUString & rFontName) const1040 const FontNameAttr* FontSubstConfiguration::getSubstInfo( const OUString& rFontName ) const
1041 {
1042 if( rFontName.isEmpty() )
1043 return nullptr;
1044
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;
1050
1051 ::std::vector< OUString > aFallbacks( maLanguageTag.getFallbackStrings( true));
1052 if (maLanguageTag.getLanguage() != "en")
1053 aFallbacks.emplace_back("en");
1054
1055 for (const auto& rFallback : aFallbacks)
1056 {
1057 std::unordered_map< OUString, LocaleSubst >::const_iterator lang = m_aSubst.find( rFallback );
1058 if( lang != m_aSubst.end() )
1059 {
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())
1066 {
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;
1073 }
1074 }
1075 }
1076 return nullptr;
1077 }
1078
1079 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1080