Coverage Report

Created: 2025-11-16 09:57

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