Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/unotools/source/config/fontcfg.cxx
Line
Count
Source
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
51
static OUString getKeyType( DefaultFontType nKeyType )
52
0
{
53
0
    switch( nKeyType )
54
0
    {
55
0
    case DefaultFontType::CJK_DISPLAY: return u"CJK_DISPLAY"_ustr;
56
0
    case DefaultFontType::CJK_HEADING: return u"CJK_HEADING"_ustr;
57
0
    case DefaultFontType::CJK_PRESENTATION: return u"CJK_PRESENTATION"_ustr;
58
0
    case DefaultFontType::CJK_SPREADSHEET: return u"CJK_SPREADSHEET"_ustr;
59
0
    case DefaultFontType::CJK_TEXT: return u"CJK_TEXT"_ustr;
60
0
    case DefaultFontType::CTL_DISPLAY: return u"CTL_DISPLAY"_ustr;
61
0
    case DefaultFontType::CTL_HEADING: return u"CTL_HEADING"_ustr;
62
0
    case DefaultFontType::CTL_PRESENTATION: return u"CTL_PRESENTATION"_ustr;
63
0
    case DefaultFontType::CTL_SPREADSHEET: return u"CTL_SPREADSHEET"_ustr;
64
0
    case DefaultFontType::CTL_TEXT: return u"CTL_TEXT"_ustr;
65
0
    case DefaultFontType::FIXED: return u"FIXED"_ustr;
66
0
    case DefaultFontType::LATIN_DISPLAY: return u"LATIN_DISPLAY"_ustr;
67
0
    case DefaultFontType::LATIN_FIXED: return u"LATIN_FIXED"_ustr;
68
0
    case DefaultFontType::LATIN_HEADING: return u"LATIN_HEADING"_ustr;
69
0
    case DefaultFontType::LATIN_PRESENTATION: return u"LATIN_PRESENTATION"_ustr;
70
0
    case DefaultFontType::LATIN_SPREADSHEET: return u"LATIN_SPREADSHEET"_ustr;
71
0
    case DefaultFontType::LATIN_TEXT: return u"LATIN_TEXT"_ustr;
72
0
    case DefaultFontType::SANS: return u"SANS"_ustr;
73
0
    case DefaultFontType::SANS_UNICODE: return u"SANS_UNICODE"_ustr;
74
0
    case DefaultFontType::SERIF: return u"SERIF"_ustr;
75
0
    case DefaultFontType::SYMBOL: return u"SYMBOL"_ustr;
76
0
    case DefaultFontType::UI_FIXED: return u"UI_FIXED"_ustr;
77
0
    case DefaultFontType::UI_SANS: return u"UI_SANS"_ustr;
78
0
    default:
79
0
        OSL_FAIL( "unmatched type" );
80
0
        return u""_ustr;
81
0
    }
82
0
}
83
84
DefaultFontConfiguration& DefaultFontConfiguration::get()
85
0
{
86
0
    static DefaultFontConfiguration theDefaultFontConfiguration;
87
0
    return theDefaultFontConfiguration;
88
0
}
89
90
DefaultFontConfiguration::DefaultFontConfiguration()
91
0
{
92
0
    if (comphelper::IsFuzzing())
93
0
        return;
94
    // create configuration hierarchical access name
95
0
    try
96
0
    {
97
        // get service provider
98
0
        m_xConfigProvider = theDefaultProvider::get(comphelper::getProcessComponentContext());
99
0
        Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
100
0
        {
101
0
            {"nodepath", Any(u"/org.openoffice.VCL/DefaultFonts"_ustr)}
102
0
        }));
103
0
        m_xConfigAccess =
104
0
            Reference< XNameAccess >(
105
0
                m_xConfigProvider->createInstanceWithArguments( u"com.sun.star.configuration.ConfigurationAccess"_ustr,
106
0
                                                                aArgs ),
107
0
                UNO_QUERY );
108
0
        if( m_xConfigAccess.is() )
109
0
        {
110
0
            const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
111
            // fill config hash with empty interfaces
112
0
            for( const OUString& rLocaleString : aLocales )
113
0
            {
114
                // Feed through LanguageTag for casing.
115
0
                OUString aLoc( LanguageTag( rLocaleString, true).getBcp47( false));
116
0
                m_aConfig[ aLoc ] = LocaleAccess();
117
0
                m_aConfig[ aLoc ].aConfigLocaleString = rLocaleString;
118
0
            }
119
0
        }
120
0
    }
121
0
    catch (const Exception&)
122
0
    {
123
        // configuration is awry
124
0
        m_xConfigProvider.clear();
125
0
        m_xConfigAccess.clear();
126
0
    }
127
0
    SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
128
0
            << ", config access: " << m_xConfigAccess.is());
129
0
}
130
131
DefaultFontConfiguration::~DefaultFontConfiguration()
132
0
{
133
    // release all nodes
134
0
    m_aConfig.clear();
135
    // release top node
136
0
    m_xConfigAccess.clear();
137
    // release config provider
138
0
    m_xConfigProvider.clear();
139
0
}
140
141
OUString DefaultFontConfiguration::tryLocale( const OUString& rBcp47, const OUString& rType ) const
142
0
{
143
0
    OUString aRet;
144
145
0
    std::unordered_map< OUString, LocaleAccess >::const_iterator it = m_aConfig.find( rBcp47 );
146
0
    if( it != m_aConfig.end() )
147
0
    {
148
0
        if( !it->second.xAccess.is() )
149
0
        {
150
0
            try
151
0
            {
152
0
                Reference< XNameAccess > xNode;
153
0
                if ( m_xConfigAccess->hasByName( it->second.aConfigLocaleString ) )
154
0
                {
155
0
                    Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
156
0
                    if( aAny >>= xNode )
157
0
                        it->second.xAccess = std::move(xNode);
158
0
                }
159
0
            }
160
0
            catch (const NoSuchElementException&)
161
0
            {
162
0
            }
163
0
            catch (const WrappedTargetException&)
164
0
            {
165
0
            }
166
0
        }
167
0
        if( it->second.xAccess.is() )
168
0
        {
169
0
            try
170
0
            {
171
0
                if ( it->second.xAccess->hasByName( rType ) )
172
0
                {
173
0
                    Any aAny = it->second.xAccess->getByName( rType );
174
0
                    aAny >>= aRet;
175
0
                }
176
0
            }
177
0
            catch (const NoSuchElementException&)
178
0
            {
179
0
            }
180
0
            catch (const WrappedTargetException&)
181
0
            {
182
0
            }
183
0
        }
184
0
    }
185
186
0
    return aRet;
187
0
}
188
189
OUString DefaultFontConfiguration::getDefaultFont( const LanguageTag& rLanguageTag, DefaultFontType nType ) const
190
0
{
191
0
    OUString aType = getKeyType( nType );
192
    // Try the simple cases first without constructing fallbacks.
193
0
    OUString aRet = tryLocale( rLanguageTag.getBcp47(), aType );
194
0
    if (aRet.isEmpty())
195
0
    {
196
0
        if (rLanguageTag.isIsoLocale())
197
0
        {
198
0
            if (!rLanguageTag.getCountry().isEmpty())
199
0
            {
200
0
                aRet = tryLocale( rLanguageTag.getLanguage(), aType );
201
0
            }
202
0
        }
203
0
        else
204
0
        {
205
0
            ::std::vector< OUString > aFallbacks( rLanguageTag.getFallbackStrings( false));
206
0
            for (const auto& rFallback : aFallbacks)
207
0
            {
208
0
                aRet = tryLocale( rFallback, aType );
209
0
                if (!aRet.isEmpty())
210
0
                    break;
211
0
            }
212
0
        }
213
0
    }
214
0
    if( aRet.isEmpty() )
215
0
    {
216
0
        aRet = tryLocale( u"en"_ustr, aType );
217
0
    }
218
0
    return aRet;
219
0
}
220
221
OUString DefaultFontConfiguration::getUserInterfaceFont( const LanguageTag& rLanguageTag ) const
222
0
{
223
0
    LanguageTag aLanguageTag( rLanguageTag);
224
0
    if( aLanguageTag.isSystemLocale() )
225
0
        aLanguageTag = SvtSysLocale().GetUILanguageTag();
226
227
0
    OUString aUIFont = getDefaultFont( aLanguageTag, DefaultFontType::UI_SANS );
228
229
0
    if( !aUIFont.isEmpty() )
230
0
        return aUIFont;
231
232
    // fallback mechanism (either no configuration or no entry in configuration
233
234
0
    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
0
    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
0
    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
0
    static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_THAI = u"OONaksit;Tahoma;Lucidasans;Arial Unicode MS";
238
0
    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
0
    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
0
    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
0
    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
0
    const OUString aLanguage( aLanguageTag.getLanguage());
244
245
    // optimize font list for some locales, as long as Andale Sans UI does not support them
246
0
    if( aLanguage == "ar" || aLanguage == "he" || aLanguage == "iw"  )
247
0
    {
248
0
        return FALLBACKFONT_UI_SANS_ARABIC;
249
0
    }
250
0
    else if ( aLanguage == "th" )
251
0
    {
252
0
        return FALLBACKFONT_UI_SANS_THAI;
253
0
    }
254
0
    else if ( aLanguage == "ko" )
255
0
    {
256
0
        return FALLBACKFONT_UI_SANS_KOREAN;
257
0
    }
258
0
    else if ( aLanguage == "ja" )
259
0
    {
260
0
        return FALLBACKFONT_UI_SANS_JAPANESE;
261
0
    }
262
0
    else if( aLanguage == "cs" ||
263
0
             aLanguage == "hu" ||
264
0
             aLanguage == "pl" ||
265
0
             aLanguage == "ro" ||
266
0
             aLanguage == "rm" ||
267
0
             aLanguage == "hr" ||
268
0
             aLanguage == "sk" ||
269
0
             aLanguage == "sl" ||
270
0
             aLanguage == "sb")
271
0
    {
272
0
        return FALLBACKFONT_UI_SANS_LATIN2;
273
0
    }
274
0
    else
275
0
    {
276
0
        const Locale& aLocale( aLanguageTag.getLocale());
277
0
        if (MsLangId::isTraditionalChinese(aLocale))
278
0
            return FALLBACKFONT_UI_SANS_CHINTRD;
279
0
        else if (MsLangId::isSimplifiedChinese(aLocale))
280
0
            return FALLBACKFONT_UI_SANS_CHINSIM;
281
0
    }
282
283
0
    return FALLBACKFONT_UI_SANS;
284
0
}
285
286
/*
287
 *  FontSubstConfigItem::get
288
 */
289
290
FontSubstConfiguration& FontSubstConfiguration::get()
291
12.6k
{
292
12.6k
    static FontSubstConfiguration theFontSubstConfiguration;
293
12.6k
    return theFontSubstConfiguration;
294
12.6k
}
295
296
/*
297
 *  FontSubstConfigItem::FontSubstConfigItem
298
 */
299
300
FontSubstConfiguration::FontSubstConfiguration() :
301
2
    maSubstHash( 300 ),
302
2
    maLanguageTag(u"en"_ustr)
303
2
{
304
2
    if (comphelper::IsFuzzing())
305
2
        return;
306
0
    try
307
0
    {
308
        // get service provider
309
0
        const Reference< XComponentContext >& xContext( comphelper::getProcessComponentContext() );
310
        // create configuration hierarchical access name
311
0
        m_xConfigProvider = theDefaultProvider::get( xContext );
312
0
        Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
313
0
        {
314
0
            {"nodepath", Any(u"/org.openoffice.VCL/FontSubstitutions"_ustr)}
315
0
        }));
316
0
        m_xConfigAccess =
317
0
            Reference< XNameAccess >(
318
0
                m_xConfigProvider->createInstanceWithArguments( u"com.sun.star.configuration.ConfigurationAccess"_ustr,
319
0
                                                                aArgs ),
320
0
                UNO_QUERY );
321
0
        if( m_xConfigAccess.is() )
322
0
        {
323
0
            const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
324
            // fill config hash with empty interfaces
325
0
            for( const OUString& rLocaleString : aLocales )
326
0
            {
327
                // Feed through LanguageTag for casing.
328
0
                OUString aLoc( LanguageTag( rLocaleString, true).getBcp47( false));
329
0
                m_aSubst[ aLoc ] = LocaleSubst();
330
0
                m_aSubst[ aLoc ].aConfigLocaleString = rLocaleString;
331
0
            }
332
0
        }
333
0
    }
334
0
    catch (const Exception&)
335
0
    {
336
        // configuration is awry
337
0
        m_xConfigProvider.clear();
338
0
        m_xConfigAccess.clear();
339
0
    }
340
0
    SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
341
0
            << ", config access: " << m_xConfigAccess.is());
342
343
0
    if( maLanguageTag.isSystemLocale() )
344
0
        maLanguageTag = SvtSysLocale().GetUILanguageTag();
345
0
}
346
347
/*
348
 *  FontSubstConfigItem::~FontSubstConfigItem
349
 */
350
351
FontSubstConfiguration::~FontSubstConfiguration()
352
2
{
353
    // release config access
354
2
    m_xConfigAccess.clear();
355
    // release config provider
356
2
    m_xConfigProvider.clear();
357
2
}
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
572
static bool ImplKillLeading( OUString& rName, const char* const* ppStr )
573
12.6k
{
574
239k
    for(; *ppStr; ++ppStr )
575
227k
    {
576
227k
        const char*         pStr = *ppStr;
577
227k
        const sal_Unicode*  pNameStr = rName.getStr();
578
227k
        while ( (*pNameStr == static_cast<sal_Unicode>(static_cast<unsigned char>(*pStr))) && *pStr )
579
402
        {
580
402
            pNameStr++;
581
402
            pStr++;
582
402
        }
583
227k
        if ( !*pStr )
584
0
        {
585
0
            sal_Int32 nLen = static_cast<sal_Int32>(pNameStr - rName.getStr());
586
0
            rName = rName.copy(nLen);
587
0
            return true;
588
0
        }
589
227k
    }
590
591
    // special case for Baekmuk
592
    // TODO: allow non-ASCII KillLeading list
593
12.6k
    const sal_Unicode* pNameStr = rName.getStr();
594
12.6k
    if( (pNameStr[0]==0xBC31) && (pNameStr[1]==0xBC35) )
595
0
    {
596
0
        sal_Int32 nLen = (pNameStr[2]==0x0020) ? 3 : 2;
597
0
        rName = rName.copy(nLen);
598
0
        return true;
599
0
    }
600
601
12.6k
    return false;
602
12.6k
}
603
604
static sal_Int32 ImplIsTrailing( std::u16string_view rName, const char* pStr )
605
555k
{
606
555k
    size_t nStrLen = strlen( pStr );
607
555k
    if( nStrLen >= rName.size() )
608
537k
        return 0;
609
610
17.9k
    const sal_Unicode* pEndName = rName.data() + rName.size();
611
17.9k
    const sal_Unicode* pNameStr = pEndName - nStrLen;
612
18.8k
    do if( *(pNameStr++) != *(pStr++) )
613
17.9k
        return 0;
614
17.9k
    while( *pStr );
615
616
0
    return nStrLen;
617
17.9k
}
618
619
static bool ImplKillTrailing( OUString& rName, const char* const* ppStr )
620
12.6k
{
621
542k
    for(; *ppStr; ++ppStr )
622
530k
    {
623
530k
        sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
624
530k
        if( nTrailLen )
625
0
        {
626
0
            rName = rName.copy(0, rName.getLength() - nTrailLen );
627
0
            return true;
628
0
        }
629
530k
    }
630
631
12.6k
    return false;
632
12.6k
}
633
634
static bool ImplKillTrailingWithExceptions( OUString& rName, const char* const* ppStr )
635
12.6k
{
636
37.8k
    for(; *ppStr; ++ppStr )
637
25.2k
    {
638
25.2k
        sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
639
25.2k
        if( nTrailLen )
640
0
        {
641
            // check string match against string exceptions
642
0
            while( *++ppStr )
643
0
                if( ImplIsTrailing( rName, *ppStr ) )
644
0
                    return false;
645
646
0
            rName = rName.copy(0, rName.getLength() - nTrailLen );
647
0
            return true;
648
0
        }
649
25.2k
        else
650
25.2k
        {
651
            // skip exception strings
652
63.1k
            while( *++ppStr ) {}
653
25.2k
        }
654
25.2k
    }
655
656
12.6k
    return false;
657
12.6k
}
658
659
static bool ImplFindAndErase( OUString& rName, const char* pStr )
660
1.03M
{
661
1.03M
    sal_Int32 nLen = static_cast<sal_Int32>(strlen(pStr));
662
1.03M
    sal_Int32 nPos = rName.indexOfAsciiL(pStr, nLen );
663
1.03M
    if ( nPos < 0 )
664
1.03M
        return false;
665
666
402
    OUStringBuffer sBuff(rName);
667
402
    sBuff.remove(nPos, nLen);
668
402
    rName = sBuff.makeStringAndClear();
669
402
    return true;
670
1.03M
}
671
672
void FontSubstConfiguration::getMapName( const OUString& rOrgName, OUString& rShortName,
673
                                         OUString& rFamilyName, FontWeight& rWeight,
674
                                         FontWidth& rWidth, ImplFontAttrs& rType )
675
12.6k
{
676
12.6k
    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
12.6k
    ImplKillLeading( rShortName, aImplKillLeadingList );
683
684
    // Kill trailing vendor names and other unimportant data
685
12.6k
    ImplKillTrailing( rShortName, aImplKillTrailingList );
686
12.6k
    ImplKillTrailingWithExceptions( rShortName, aImplKillTrailingWithExceptionsList );
687
688
12.6k
    rFamilyName = rShortName;
689
690
    // Kill attributes from the name and update the data
691
    // Weight
692
12.6k
    const ImplFontAttrWeightSearchData* pWeightList = aImplWeightAttrSearchList;
693
176k
    while ( pWeightList->mpStr )
694
164k
    {
695
164k
        if ( ImplFindAndErase( rFamilyName, pWeightList->mpStr ) )
696
0
        {
697
0
            if ( (rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL) )
698
0
                rWeight = pWeightList->meWeight;
699
0
            break;
700
0
        }
701
164k
        pWeightList++;
702
164k
    }
703
704
    // Width
705
12.6k
    const ImplFontAttrWidthSearchData* pWidthList = aImplWidthAttrSearchList;
706
138k
    while ( pWidthList->mpStr )
707
126k
    {
708
126k
        if ( ImplFindAndErase( rFamilyName, pWidthList->mpStr ) )
709
0
        {
710
0
            if ( (rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL) )
711
0
                rWidth = pWidthList->meWidth;
712
0
            break;
713
0
        }
714
126k
        pWidthList++;
715
126k
    }
716
717
    // Type
718
12.6k
    rType = ImplFontAttrs::None;
719
12.6k
    const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
720
757k
    while ( pTypeList->mpStr )
721
744k
    {
722
744k
        if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
723
402
            rType |= pTypeList->mnType;
724
744k
        pTypeList++;
725
744k
    }
726
727
    // Remove numbers
728
    // TODO: also remove localized and fullwidth digits
729
12.6k
    sal_Int32 i = 0;
730
12.6k
    OUStringBuffer sBuff(rFamilyName);
731
15.8k
    while ( i < sBuff.getLength() )
732
3.26k
    {
733
3.26k
        sal_Unicode c = sBuff[ i ];
734
3.26k
        if ( (c >= 0x0030) && (c <= 0x0039) )
735
0
            sBuff.remove(i, 1);
736
3.26k
        else
737
3.26k
            i++;
738
3.26k
    }
739
12.6k
}
740
741
namespace {
742
743
struct StrictStringSort
744
{
745
    bool operator()( const FontNameAttr& rLeft, const FontNameAttr& rRight )
746
0
    { 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
831
void FontSubstConfiguration::fillSubstVector( const css::uno::Reference< XNameAccess >& rFont,
832
                                              const OUString& rType,
833
                                              std::vector< OUString >& rSubstVector ) const
834
0
{
835
0
    try
836
0
    {
837
0
        Any aAny = rFont->getByName( rType );
838
0
        if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
839
0
        {
840
0
            sal_Int32 nLength = pLine->getLength();
841
0
            if( nLength )
842
0
            {
843
0
                const sal_Unicode* pStr = pLine->getStr();
844
0
                sal_Int32 nTokens = 0;
845
                // count tokens
846
0
                while( nLength-- )
847
0
                {
848
0
                    if( *pStr++ == ';' )
849
0
                        nTokens++;
850
0
                }
851
0
                rSubstVector.clear();
852
                // optimize performance, heap fragmentation
853
0
                rSubstVector.reserve( nTokens );
854
0
                sal_Int32 nIndex = 0;
855
0
                while( nIndex != -1 )
856
0
                {
857
0
                    OUString aSubst( pLine->getToken( 0, ';', nIndex ) );
858
0
                    if( !aSubst.isEmpty() )
859
0
                    {
860
0
                        auto itPair = maSubstHash.insert( aSubst );
861
0
                        if (!itPair.second)
862
0
                            aSubst = *itPair.first;
863
0
                        rSubstVector.push_back( aSubst );
864
0
                    }
865
0
                }
866
0
            }
867
0
        }
868
0
    }
869
0
    catch (const NoSuchElementException&)
870
0
    {
871
0
    }
872
0
    catch (const WrappedTargetException&)
873
0
    {
874
0
    }
875
0
}
876
877
// static
878
FontWeight FontSubstConfiguration::getSubstWeight( const css::uno::Reference< XNameAccess >& rFont,
879
                                                   const OUString& rType )
880
0
{
881
0
    int weight = -1;
882
0
    try
883
0
    {
884
0
        Any aAny = rFont->getByName( rType );
885
0
        if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
886
0
        {
887
0
            if( !pLine->isEmpty() )
888
0
            {
889
0
                for( weight=std::size(pWeightNames)-1; weight >= 0; weight-- )
890
0
                    if( pLine->equalsIgnoreAsciiCaseAscii( pWeightNames[weight].pName ) )
891
0
                        break;
892
0
            }
893
0
            SAL_WARN_IF(weight < 0, "unotools.config", "Error: invalid weight " << *pLine);
894
0
        }
895
0
    }
896
0
    catch (const NoSuchElementException&)
897
0
    {
898
0
    }
899
0
    catch (const WrappedTargetException&)
900
0
    {
901
0
    }
902
0
    return static_cast<FontWeight>( weight >= 0 ? pWeightNames[weight].nEnum : WEIGHT_DONTKNOW );
903
0
}
904
905
// static
906
FontWidth FontSubstConfiguration::getSubstWidth( const css::uno::Reference< XNameAccess >& rFont,
907
                                                 const OUString& rType )
908
0
{
909
0
    int width = -1;
910
0
    try
911
0
    {
912
0
        Any aAny = rFont->getByName( rType );
913
0
        if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
914
0
        {
915
0
            if( !pLine->isEmpty() )
916
0
            {
917
0
                for( width=std::size(pWidthNames)-1; width >= 0; width-- )
918
0
                    if( pLine->equalsIgnoreAsciiCaseAscii( pWidthNames[width].pName ) )
919
0
                        break;
920
0
            }
921
0
            SAL_WARN_IF( width < 0, "unotools.config", "Error: invalid width " << *pLine);
922
0
        }
923
0
    }
924
0
    catch (const NoSuchElementException&)
925
0
    {
926
0
    }
927
0
    catch (const WrappedTargetException&)
928
0
    {
929
0
    }
930
0
    return static_cast<FontWidth>( width >= 0 ? pWidthNames[width].nEnum : WIDTH_DONTKNOW );
931
0
}
932
933
// static
934
ImplFontAttrs FontSubstConfiguration::getSubstType( const css::uno::Reference< XNameAccess >& rFont,
935
                                                    const OUString& rType )
936
0
{
937
0
    sal_uInt32 type = 0;
938
0
    try
939
0
    {
940
0
        Any aAny = rFont->getByName( rType );
941
0
        auto pLine = o3tl::tryAccess<OUString>(aAny);
942
0
        if( !pLine )
943
0
            return ImplFontAttrs::None;
944
0
        if( pLine->isEmpty() )
945
0
            return ImplFontAttrs::None;
946
0
        sal_Int32 nIndex = 0;
947
0
        while( nIndex != -1 )
948
0
        {
949
0
            OUString aToken( pLine->getToken( 0, ',', nIndex ) );
950
0
            for( int k = 0; k < 32; k++ )
951
0
                if( aToken.equalsIgnoreAsciiCaseAscii( pAttribNames[k] ) )
952
0
                {
953
0
                    type |= sal_uInt32(1) << k;
954
0
                    break;
955
0
                }
956
0
        }
957
0
        assert(((type & ~o3tl::typed_flags<ImplFontAttrs>::mask) == 0) && "invalid font attributes");
958
0
    }
959
0
    catch (const NoSuchElementException&)
960
0
    {
961
0
    }
962
0
    catch (const WrappedTargetException&)
963
0
    {
964
0
    }
965
966
0
    return static_cast<ImplFontAttrs>(type);
967
0
}
968
969
void FontSubstConfiguration::readLocaleSubst( const OUString& rBcp47 ) const
970
0
{
971
0
    std::unordered_map< OUString, LocaleSubst >::const_iterator it = m_aSubst.find( rBcp47 );
972
0
    if( it == m_aSubst.end() )
973
0
        return;
974
975
0
    if(  it->second.bConfigRead )
976
0
        return;
977
978
0
    it->second.bConfigRead = true;
979
0
    Reference< XNameAccess > xNode;
980
0
    try
981
0
    {
982
0
        Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
983
0
        aAny >>= xNode;
984
0
    }
985
0
    catch (const NoSuchElementException&)
986
0
    {
987
0
    }
988
0
    catch (const WrappedTargetException&)
989
0
    {
990
0
    }
991
0
    if( !xNode.is() )
992
0
        return;
993
994
0
    const Sequence< OUString > aFonts = xNode->getElementNames();
995
0
    int nFonts = aFonts.getLength();
996
    // improve performance, heap fragmentation
997
0
    it->second.aSubstAttributes.reserve( nFonts );
998
999
    // strings for subst retrieval, construct only once
1000
0
    static constexpr OUStringLiteral aSubstFontsStr  ( u"SubstFonts" );
1001
0
    static constexpr OUStringLiteral aSubstFontsMSStr( u"SubstFontsMS" );
1002
0
    static constexpr OUStringLiteral aSubstWeightStr ( u"FontWeight" );
1003
0
    static constexpr OUStringLiteral aSubstWidthStr  ( u"FontWidth" );
1004
0
    static constexpr OUStringLiteral aSubstTypeStr   ( u"FontType" );
1005
0
    for( const OUString& rFontName : aFonts )
1006
0
    {
1007
0
        Reference< XNameAccess > xFont;
1008
0
        try
1009
0
        {
1010
0
            Any aAny = xNode->getByName( rFontName );
1011
0
            aAny >>= xFont;
1012
0
        }
1013
0
        catch (const NoSuchElementException&)
1014
0
        {
1015
0
        }
1016
0
        catch (const WrappedTargetException&)
1017
0
        {
1018
0
        }
1019
0
        if( ! xFont.is() )
1020
0
        {
1021
0
            SAL_WARN("unotools.config", "did not get font attributes for " << rFontName);
1022
0
            continue;
1023
0
        }
1024
1025
0
        FontNameAttr aAttr;
1026
        // read subst attributes from config
1027
0
        aAttr.Name = rFontName;
1028
0
        fillSubstVector( xFont, aSubstFontsStr, aAttr.Substitutions );
1029
0
        fillSubstVector( xFont, aSubstFontsMSStr, aAttr.MSSubstitutions );
1030
0
        aAttr.Weight = getSubstWeight( xFont, aSubstWeightStr );
1031
0
        aAttr.Width = getSubstWidth( xFont, aSubstWidthStr );
1032
0
        aAttr.Type = getSubstType( xFont, aSubstTypeStr );
1033
1034
        // finally insert this entry
1035
0
        it->second.aSubstAttributes.push_back(std::move(aAttr));
1036
0
    }
1037
0
    std::sort( it->second.aSubstAttributes.begin(), it->second.aSubstAttributes.end(), StrictStringSort() );
1038
0
}
1039
1040
const FontNameAttr* FontSubstConfiguration::getSubstInfo( const OUString& rFontName ) const
1041
12.6k
{
1042
12.6k
    if( rFontName.isEmpty() )
1043
12.2k
        return nullptr;
1044
1045
    // search if a  (language dep.) replacement table for the given font exists
1046
    // fallback is english
1047
411
    OUString aSearchFont( rFontName.toAsciiLowerCase() );
1048
411
    FontNameAttr aSearchAttr;
1049
411
    aSearchAttr.Name = aSearchFont;
1050
1051
411
    ::std::vector< OUString > aFallbacks( maLanguageTag.getFallbackStrings( true));
1052
411
    if (maLanguageTag.getLanguage() != "en")
1053
0
        aFallbacks.emplace_back("en");
1054
1055
411
    for (const auto& rFallback : aFallbacks)
1056
411
    {
1057
411
        std::unordered_map< OUString, LocaleSubst >::const_iterator lang = m_aSubst.find( rFallback );
1058
411
        if( lang != m_aSubst.end() )
1059
0
        {
1060
0
            if( ! lang->second.bConfigRead )
1061
0
                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
0
            std::vector< FontNameAttr >::const_iterator it = ::std::lower_bound( lang->second.aSubstAttributes.begin(), lang->second.aSubstAttributes.end(), aSearchAttr, StrictStringSort() );
1065
0
            if( it != lang->second.aSubstAttributes.end())
1066
0
            {
1067
0
                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
0
                if( rFoundAttr.Name.getLength() <= aSearchFont.getLength() )
1071
0
                    if( aSearchFont.startsWith( rFoundAttr.Name))
1072
0
                        return &rFoundAttr;
1073
0
            }
1074
0
        }
1075
411
    }
1076
411
    return nullptr;
1077
411
}
1078
1079
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */