Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/font/PhysicalFontFamily.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 <sal/config.h>
21
22
#include <rtl/ustring.hxx>
23
#include <unotools/fontdefs.hxx>
24
25
#include <font/PhysicalFontFaceCollection.hxx>
26
#include <font/PhysicalFontCollection.hxx>
27
#include <utility>
28
29
namespace vcl::font
30
{
31
32
void PhysicalFontFamily::CalcType( ImplFontAttrs& rType, FontWeight& rWeight, FontWidth& rWidth,
33
                                   FontFamily eFamily, const utl::FontNameAttr* pFontAttr )
34
0
{
35
0
    if ( eFamily != FAMILY_DONTKNOW )
36
0
    {
37
0
        if ( eFamily == FAMILY_SWISS )
38
0
            rType |= ImplFontAttrs::SansSerif;
39
0
        else if ( eFamily == FAMILY_ROMAN )
40
0
            rType |= ImplFontAttrs::Serif;
41
0
        else if ( eFamily == FAMILY_SCRIPT )
42
0
            rType |= ImplFontAttrs::Script;
43
0
        else if ( eFamily == FAMILY_MODERN )
44
0
            rType |= ImplFontAttrs::Fixed;
45
0
        else if ( eFamily == FAMILY_DECORATIVE )
46
0
            rType |= ImplFontAttrs::Decorative;
47
0
    }
48
49
0
    if ( pFontAttr )
50
0
    {
51
0
        rType |= pFontAttr->Type;
52
53
0
        if ( ((rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL)) &&
54
0
             (pFontAttr->Weight != WEIGHT_DONTKNOW) )
55
0
            rWeight = pFontAttr->Weight;
56
0
        if ( ((rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL)) &&
57
0
             (pFontAttr->Width != WIDTH_DONTKNOW) )
58
0
            rWidth = pFontAttr->Width;
59
0
    }
60
0
}
61
62
static ImplFontAttrs lcl_IsCJKFont( std::u16string_view rFontName )
63
0
{
64
    // Test, if Fontname includes CJK characters --> In this case we
65
    // mention that it is a CJK font
66
0
    for (const sal_Unicode& ch : rFontName)
67
0
    {
68
        // japanese
69
0
        if ( ((ch >= 0x3040) && (ch <= 0x30FF)) ||
70
0
             ((ch >= 0x3190) && (ch <= 0x319F)) )
71
0
            return ImplFontAttrs::CJK|ImplFontAttrs::CJK_JP;
72
73
        // korean
74
0
        if ( ((ch >= 0xAC00) && (ch <= 0xD7AF)) ||
75
0
             ((ch >= 0xA960) && (ch <= 0xA97F)) ||
76
0
             ((ch >= 0xD7B0) && (ch <= 0xD7FF)) ||
77
0
             ((ch >= 0x3130) && (ch <= 0x318F)) ||
78
0
             ((ch >= 0x1100) && (ch <= 0x11FF)) )
79
0
            return ImplFontAttrs::CJK|ImplFontAttrs::CJK_KR;
80
81
        // chinese
82
0
        if ( (ch >= 0x3400) && (ch <= 0x9FFF) )
83
0
            return ImplFontAttrs::CJK|ImplFontAttrs::CJK_TC|ImplFontAttrs::CJK_SC;
84
85
        // cjk
86
0
        if ( ((ch >= 0x3000) && (ch <= 0xD7AF)) ||
87
0
             ((ch >= 0xFF00) && (ch <= 0xFFEE)) )
88
0
            return ImplFontAttrs::CJK;
89
90
0
    }
91
92
0
    return ImplFontAttrs::None;
93
0
}
94
95
PhysicalFontFamily::PhysicalFontFamily( OUString aSearchName )
96
338k
:   maSearchName(std::move( aSearchName )),
97
338k
    mnTypeFaces( FontTypeFaces::NONE ),
98
338k
    meFamily( FAMILY_DONTKNOW ),
99
338k
    mePitch( PITCH_DONTKNOW ),
100
338k
    mnMinQuality( -1 ),
101
338k
    mnMatchType( ImplFontAttrs::None ),
102
338k
    meMatchWeight( WEIGHT_DONTKNOW ),
103
338k
    meMatchWidth( WIDTH_DONTKNOW )
104
338k
{}
105
106
PhysicalFontFamily::~PhysicalFontFamily()
107
338k
{
108
338k
}
109
110
void PhysicalFontFamily::AddFontFace( PhysicalFontFace* pNewFontFace )
111
1.36M
{
112
1.36M
    if( maFontFaces.empty() )
113
338k
    {
114
338k
        maFamilyName   = pNewFontFace->GetFamilyName();
115
338k
        meFamily       = pNewFontFace->GetFamilyType();
116
338k
        mePitch        = pNewFontFace->GetPitch();
117
338k
        mnMinQuality   = pNewFontFace->GetQuality();
118
338k
    }
119
1.02M
    else
120
1.02M
    {
121
1.02M
        if( meFamily == FAMILY_DONTKNOW )
122
1.02M
            meFamily = pNewFontFace->GetFamilyType();
123
1.02M
        if( mePitch == PITCH_DONTKNOW )
124
0
            mePitch = pNewFontFace->GetPitch();
125
1.02M
        if( mnMinQuality > pNewFontFace->GetQuality() )
126
2.84k
            mnMinQuality = pNewFontFace->GetQuality();
127
1.02M
    }
128
129
    // set attributes for attribute based font matching
130
1.36M
    mnTypeFaces |= FontTypeFaces::Scalable;
131
132
1.36M
    if( pNewFontFace->IsMicrosoftSymbolEncoded() )
133
0
        mnTypeFaces |= FontTypeFaces::Symbol;
134
1.36M
    else
135
1.36M
        mnTypeFaces |= FontTypeFaces::NoneSymbol;
136
137
1.36M
    if( pNewFontFace->GetWeight() != WEIGHT_DONTKNOW )
138
1.36M
    {
139
1.36M
        if( pNewFontFace->GetWeight() >= WEIGHT_SEMIBOLD )
140
681k
            mnTypeFaces |= FontTypeFaces::Bold;
141
681k
        else if( pNewFontFace->GetWeight() <= WEIGHT_SEMILIGHT )
142
0
            mnTypeFaces |= FontTypeFaces::Light;
143
681k
        else
144
681k
            mnTypeFaces |= FontTypeFaces::Normal;
145
1.36M
    }
146
147
1.36M
    if( pNewFontFace->GetItalic() == ITALIC_NONE )
148
681k
        mnTypeFaces |= FontTypeFaces::NoneItalic;
149
681k
    else if( (pNewFontFace->GetItalic() == ITALIC_NORMAL)
150
0
         ||  (pNewFontFace->GetItalic() == ITALIC_OBLIQUE) )
151
681k
        mnTypeFaces |= FontTypeFaces::Italic;
152
153
    // reassign name (sharing saves memory)
154
1.36M
    if( pNewFontFace->GetFamilyName() == GetFamilyName() )
155
1.36M
        pNewFontFace->SetFamilyName( GetFamilyName() );
156
157
    // add the new physical font face, replacing existing font face if necessary
158
    // TODO: get rid of linear search?
159
1.36M
    auto it(maFontFaces.begin());
160
3.40M
    for (; it != maFontFaces.end(); ++it)
161
2.04M
    {
162
2.04M
        PhysicalFontFace* pFoundFontFace = it->get();
163
2.04M
        sal_Int32 eComp = pNewFontFace->CompareIgnoreSize( *pFoundFontFace );
164
2.04M
        if( eComp > 0 )
165
2.03M
            continue;
166
10.6k
        if( eComp < 0 )
167
1.89k
            break;
168
169
        // ignore duplicate if its quality is worse
170
8.74k
        if( pNewFontFace->GetQuality() < pFoundFontFace->GetQuality() )
171
0
            return;
172
173
        // keep the device font if its quality is good enough
174
8.74k
        if( pNewFontFace->GetQuality() == pFoundFontFace->GetQuality() )
175
8.74k
            return;
176
177
        // replace existing font face with a better one
178
0
        *it = pNewFontFace; // insert at sort position
179
0
        return;
180
8.74k
    }
181
182
1.35M
    maFontFaces.emplace(it, pNewFontFace); // insert at sort position
183
1.35M
}
184
185
// get font attributes using the normalized font family name
186
void PhysicalFontFamily::InitMatchData( const utl::FontSubstConfiguration& rFontSubst,
187
    const OUString& rSearchName )
188
0
{
189
0
    OUString aShortName;
190
0
    OUString aMatchFamilyName(maMatchFamilyName);
191
    // get font attributes from the decorated font name
192
0
    utl::FontSubstConfiguration::getMapName( rSearchName, aShortName, aMatchFamilyName,
193
0
                            meMatchWeight, meMatchWidth, mnMatchType );
194
0
    maMatchFamilyName = aMatchFamilyName;
195
0
    const utl::FontNameAttr* pFontAttr = rFontSubst.getSubstInfo( rSearchName );
196
    // eventually use the stripped name
197
0
    if( !pFontAttr )
198
0
        if( aShortName != rSearchName )
199
0
            pFontAttr = rFontSubst.getSubstInfo( aShortName );
200
0
    CalcType( mnMatchType, meMatchWeight, meMatchWidth, meFamily, pFontAttr );
201
0
    mnMatchType |= lcl_IsCJKFont( maFamilyName );
202
0
}
203
204
PhysicalFontFace* PhysicalFontFamily::FindBestFontFace( const vcl::font::FontSelectPattern& rFSD ) const
205
132k
{
206
132k
    if( maFontFaces.empty() )
207
0
        return nullptr;
208
132k
    if( maFontFaces.size() == 1)
209
0
        return maFontFaces[0].get();
210
211
    // TODO: linear search improve!
212
132k
    PhysicalFontFace* pBestFontFace = maFontFaces[0].get();
213
132k
    int nBestMatch = 0;
214
132k
    for (auto const& font : maFontFaces)
215
531k
    {
216
531k
        PhysicalFontFace* pFoundFontFace = font.get();
217
531k
        if( pFoundFontFace->IsBetterMatch( rFSD, nBestMatch ) )
218
148k
            pBestFontFace = pFoundFontFace;
219
531k
    }
220
221
132k
    return pBestFontFace;
222
132k
}
223
224
// update device font list with unique font faces, with uniqueness
225
// meaning different font attributes, but not different fonts sizes
226
void PhysicalFontFamily::UpdateDevFontList( vcl::font::PhysicalFontFaceCollection& rDevFontList ) const
227
369k
{
228
369k
    PhysicalFontFace* pPrevFace = nullptr;
229
369k
    for (auto const& font : maFontFaces)
230
1.47M
    {
231
1.47M
        PhysicalFontFace* pFoundFontFace = font.get();
232
1.47M
        if( !pPrevFace || pFoundFontFace->CompareIgnoreSize( *pPrevFace ) )
233
1.47M
            rDevFontList.Add( pFoundFontFace );
234
1.47M
        pPrevFace = pFoundFontFace;
235
1.47M
    }
236
369k
}
237
238
void PhysicalFontFamily::UpdateCloneFontList(vcl::font::PhysicalFontCollection& rFontCollection) const
239
337k
{
240
337k
    OUString aFamilyName = GetEnglishSearchFontName( GetFamilyName() );
241
337k
    PhysicalFontFamily* pFamily(nullptr);
242
243
337k
    for (auto const& font : maFontFaces)
244
1.34M
    {
245
1.34M
        PhysicalFontFace* pFoundFontFace = font.get();
246
247
1.34M
        if (!pFamily)
248
337k
        { // tdf#98989 lazy create as family without faces won't work
249
337k
            pFamily = std::get<0>(rFontCollection.FindOrCreateFontFamily(aFamilyName));
250
337k
        }
251
        assert(pFamily);
252
1.34M
        pFamily->AddFontFace(pFoundFontFace);
253
1.34M
    }
254
337k
}
255
}
256
257
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */