Coverage Report

Created: 2026-09-28 10:59

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
22.8k
{
292
22.8k
    static FontSubstConfiguration theFontSubstConfiguration;
293
22.8k
    return theFontSubstConfiguration;
294
22.8k
}
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
22.8k
{
574
434k
    for(; *ppStr; ++ppStr )
575
411k
    {
576
411k
        const char*         pStr = *ppStr;
577
411k
        const sal_Unicode*  pNameStr = rName.getStr();
578
411k
        while ( (*pNameStr == static_cast<sal_Unicode>(static_cast<unsigned char>(*pStr))) && *pStr )
579
389
        {
580
389
            pNameStr++;
581
389
            pStr++;
582
389
        }
583
411k
        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
411k
    }
590
591
    // special case for Baekmuk
592
    // TODO: allow non-ASCII KillLeading list
593
22.8k
    const sal_Unicode* pNameStr = rName.getStr();
594
22.8k
    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
22.8k
    return false;
602
22.8k
}
603
604
static sal_Int32 ImplIsTrailing( std::u16string_view rName, const char* pStr )
605
1.00M
{
606
1.00M
    size_t nStrLen = strlen( pStr );
607
1.00M
    if( nStrLen >= rName.size() )
608
987k
        return 0;
609
610
17.3k
    const sal_Unicode* pEndName = rName.data() + rName.size();
611
17.3k
    const sal_Unicode* pNameStr = pEndName - nStrLen;
612
18.2k
    do if( *(pNameStr++) != *(pStr++) )
613
17.3k
        return 0;
614
17.3k
    while( *pStr );
615
616
0
    return nStrLen;
617
17.3k
}
618
619
static bool ImplKillTrailing( OUString& rName, const char* const* ppStr )
620
22.8k
{
621
982k
    for(; *ppStr; ++ppStr )
622
959k
    {
623
959k
        sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
624
959k
        if( nTrailLen )
625
0
        {
626
0
            rName = rName.copy(0, rName.getLength() - nTrailLen );
627
0
            return true;
628
0
        }
629
959k
    }
630
631
22.8k
    return false;
632
22.8k
}
633
634
static bool ImplKillTrailingWithExceptions( OUString& rName, const char* const* ppStr )
635
22.8k
{
636
68.5k
    for(; *ppStr; ++ppStr )
637
45.6k
    {
638
45.6k
        sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
639
45.6k
        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
45.6k
        else
650
45.6k
        {
651
            // skip exception strings
652
114k
            while( *++ppStr ) {}
653
45.6k
        }
654
45.6k
    }
655
656
22.8k
    return false;
657
22.8k
}
658
659
static bool ImplFindAndErase( OUString& rName, const char* pStr )
660
1.87M
{
661
1.87M
    sal_Int32 nLen = static_cast<sal_Int32>(strlen(pStr));
662
1.87M
    sal_Int32 nPos = rName.indexOfAsciiL(pStr, nLen );
663
1.87M
    if ( nPos < 0 )
664
1.87M
        return false;
665
666
389
    OUStringBuffer sBuff(rName);
667
389
    sBuff.remove(nPos, nLen);
668
389
    rName = sBuff.makeStringAndClear();
669
389
    return true;
670
1.87M
}
671
672
void FontSubstConfiguration::getMapName( const OUString& rOrgName, OUString& rShortName,
673
                                         OUString& rFamilyName, FontWeight& rWeight,
674
                                         FontWidth& rWidth, ImplFontAttrs& rType )
675
22.8k
{
676
22.8k
    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
22.8k
    ImplKillLeading( rShortName, aImplKillLeadingList );
683
684
    // Kill trailing vendor names and other unimportant data
685
22.8k
    ImplKillTrailing( rShortName, aImplKillTrailingList );
686
22.8k
    ImplKillTrailingWithExceptions( rShortName, aImplKillTrailingWithExceptionsList );
687
688
22.8k
    rFamilyName = rShortName;
689
690
    // Kill attributes from the name and update the data
691
    // Weight
692
22.8k
    const ImplFontAttrWeightSearchData* pWeightList = aImplWeightAttrSearchList;
693
319k
    while ( pWeightList->mpStr )
694
297k
    {
695
297k
        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
297k
        pWeightList++;
702
297k
    }
703
704
    // Width
705
22.8k
    const ImplFontAttrWidthSearchData* pWidthList = aImplWidthAttrSearchList;
706
251k
    while ( pWidthList->mpStr )
707
228k
    {
708
228k
        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
228k
        pWidthList++;
715
228k
    }
716
717
    // Type
718
22.8k
    rType = ImplFontAttrs::None;
719
22.8k
    const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
720
1.37M
    while ( pTypeList->mpStr )
721
1.34M
    {
722
1.34M
        if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
723
389
            rType |= pTypeList->mnType;
724
1.34M
        pTypeList++;
725
1.34M
    }
726
727
    // Remove numbers
728
    // TODO: also remove localized and fullwidth digits
729
22.8k
    sal_Int32 i = 0;
730
22.8k
    OUStringBuffer sBuff(rFamilyName);
731
26.0k
    while ( i < sBuff.getLength() )
732
3.16k
    {
733
3.16k
        sal_Unicode c = sBuff[ i ];
734
3.16k
        if ( (c >= 0x0030) && (c <= 0x0039) )
735
0
            sBuff.remove(i, 1);
736
3.16k
        else
737
3.16k
            i++;
738
3.16k
    }
739
22.8k
}
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
22.8k
{
1042
22.8k
    if( rFontName.isEmpty() )
1043
22.4k
        return nullptr;
1044
1045
    // search if a  (language dep.) replacement table for the given font exists
1046
    // fallback is english
1047
398
    OUString aSearchFont( rFontName.toAsciiLowerCase() );
1048
398
    FontNameAttr aSearchAttr;
1049
398
    aSearchAttr.Name = aSearchFont;
1050
1051
398
    ::std::vector< OUString > aFallbacks( maLanguageTag.getFallbackStrings( true));
1052
398
    if (maLanguageTag.getLanguage() != "en")
1053
0
        aFallbacks.emplace_back("en");
1054
1055
398
    for (const auto& rFallback : aFallbacks)
1056
398
    {
1057
398
        std::unordered_map< OUString, LocaleSubst >::const_iterator lang = m_aSubst.find( rFallback );
1058
398
        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
398
    }
1076
398
    return nullptr;
1077
398
}
1078
1079
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */