Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/unx/generic/font/fontsubst.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 <unx/geninst.h>
23
#include <font/PhysicalFontCollection.hxx>
24
#include <font/fontsubstitution.hxx>
25
#include <unx/font/fontmanager.hxx>
26
#include <unotools/fontdefs.hxx>
27
28
// platform specific font substitution hooks
29
30
namespace {
31
32
class FcPreMatchSubstitution
33
:   public vcl::font::PreMatchFontSubstitution
34
{
35
public:
36
    bool FindFontSubstitute( vcl::font::FontSelectPattern& ) const override;
37
    typedef ::std::pair<vcl::font::FontSelectPattern, vcl::font::FontSelectPattern> value_type;
38
private:
39
    typedef ::std::list<value_type> CachedFontMapType;
40
    mutable CachedFontMapType maCachedFontMap;
41
};
42
43
class FcGlyphFallbackSubstitution
44
:    public vcl::font::GlyphFallbackFontSubstitution
45
{
46
    // TODO: add a cache
47
public:
48
    bool FindFontSubstitute(vcl::font::FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingCodes) const override;
49
};
50
51
}
52
53
void SalGenericInstance::RegisterFontSubstitutors(vcl::font::PhysicalFontCollection* pFontCollection)
54
289
{
55
    // register font fallback substitutions
56
289
    static FcPreMatchSubstitution aSubstPreMatch;
57
289
    pFontCollection->SetPreMatchHook( &aSubstPreMatch );
58
59
    // register glyph fallback substitutions
60
289
    static FcGlyphFallbackSubstitution aSubstFallback;
61
289
    pFontCollection->SetFallbackHook( &aSubstFallback );
62
289
}
63
64
static vcl::font::FontSelectPattern GetFcSubstitute(const vcl::font::FontSelectPattern &rFontSelData, OUString& rMissingCodes)
65
0
{
66
0
    vcl::font::FontSelectPattern aSubstituted(rFontSelData);
67
0
    FontConfigManager& rMgr = FontConfigManager::get();
68
0
    rMgr.Substitute(aSubstituted, rMissingCodes);
69
0
    return aSubstituted;
70
0
}
71
72
namespace
73
{
74
    bool uselessmatch(const vcl::font::FontSelectPattern &rOrig, const vcl::font::FontSelectPattern &rNew)
75
0
    {
76
0
        return
77
0
          (
78
0
            rOrig.maTargetName == rNew.maSearchName &&
79
0
            rOrig.GetWeight() == rNew.GetWeight() &&
80
0
            rOrig.GetItalic() == rNew.GetItalic() &&
81
0
            rOrig.GetPitch() == rNew.GetPitch() &&
82
0
            rOrig.GetWidthType() == rNew.GetWidthType()
83
0
          );
84
0
    }
85
86
    class equal
87
    {
88
    private:
89
        const vcl::font::FontSelectPattern& mrAttributes;
90
    public:
91
        explicit equal(const vcl::font::FontSelectPattern& rAttributes)
92
0
            : mrAttributes(rAttributes)
93
0
        {
94
0
        }
95
        bool operator()(const FcPreMatchSubstitution::value_type& rOther) const
96
0
            { return rOther.first == mrAttributes; }
97
    };
98
}
99
100
bool FcPreMatchSubstitution::FindFontSubstitute(vcl::font::FontSelectPattern &rFontSelData) const
101
0
{
102
    // We don't actually want to talk to Fontconfig at all for symbol fonts
103
0
    if( rFontSelData.IsMicrosoftSymbolEncoded() )
104
0
        return false;
105
    // OpenSymbol is a unicode font, but it still deserves to be treated as a symbol font
106
0
    if ( IsOpenSymbol(rFontSelData.maSearchName) )
107
0
        return false;
108
109
    //see fdo#41556 and fdo#47636
110
    //fontconfig can return e.g. an italic font for a non-italic input and/or
111
    //different fonts depending on fontsize, bold, etc settings so don't cache
112
    //just on the name, cache map all the input and all the output not just map
113
    //from original selection to output fontname
114
0
    vcl::font::FontSelectPattern& rPatternAttributes = rFontSelData;
115
0
    CachedFontMapType &rCachedFontMap = maCachedFontMap;
116
0
    CachedFontMapType::iterator itr = std::find_if(rCachedFontMap.begin(), rCachedFontMap.end(), equal(rPatternAttributes));
117
0
    if (itr != rCachedFontMap.end())
118
0
    {
119
        // Cached substitution
120
0
        rFontSelData = itr->second;
121
0
        if (itr != rCachedFontMap.begin())
122
0
        {
123
            // MRU, move it to the front
124
0
            rCachedFontMap.splice(rCachedFontMap.begin(), rCachedFontMap, itr);
125
0
        }
126
0
        return true;
127
0
    }
128
129
0
    OUString aDummy;
130
0
    vcl::font::FontSelectPattern aOut = GetFcSubstitute( rFontSelData, aDummy );
131
132
0
    if( aOut.maSearchName.isEmpty() )
133
0
        return false;
134
135
0
    const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut );
136
137
#if OSL_DEBUG_LEVEL >= 2
138
    std::ostringstream oss;
139
    oss << "FcPreMatchSubstitution \""
140
        << rFontSelData.maTargetName
141
        << "\" bipw="
142
        << rFontSelData.GetWeight()
143
        << rFontSelData.GetItalic()
144
        << rFontSelData.GetPitch()
145
        << rFontSelData.GetWidthType()
146
        << " -> ";
147
    if( !bHaveSubstitute )
148
        oss << "no substitute available.";
149
    else
150
        oss << "\""
151
            << aOut.maSearchName
152
            << "\" bipw="
153
            << aOut.GetWeight()
154
            << aOut.GetItalic()
155
            << aOut.GetPitch()
156
            << aOut.GetWidthType();
157
    SAL_INFO("vcl.fonts", oss.str());
158
#endif
159
160
0
    if( bHaveSubstitute )
161
0
    {
162
0
        rCachedFontMap.push_front(value_type(rFontSelData, aOut));
163
        // Fairly arbitrary limit in this case, but I recall measuring max 8
164
        // fonts as the typical max amount of fonts in medium sized documents, so make it
165
        // a fair chunk larger to accommodate weird documents./
166
0
        if (rCachedFontMap.size() > 256)
167
0
            rCachedFontMap.pop_back();
168
0
        rFontSelData = std::move(aOut);
169
0
    }
170
171
0
    return bHaveSubstitute;
172
0
}
173
174
bool FcGlyphFallbackSubstitution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData,
175
    LogicalFontInstance* /*pLogicalFont*/,
176
    OUString& rMissingCodes ) const
177
0
{
178
    // We don't actually want to talk to Fontconfig at all for symbol fonts
179
0
    if( rFontSelData.IsMicrosoftSymbolEncoded() )
180
0
        return false;
181
    // OpenSymbol is a unicode font, but it still deserves to be treated as a symbol font
182
0
    if ( IsOpenSymbol(rFontSelData.maSearchName) )
183
0
        return false;
184
185
0
    vcl::font::FontSelectPattern aOut = GetFcSubstitute( rFontSelData, rMissingCodes );
186
    // TODO: cache the unicode + srcfont specific result
187
    // FC doing it would be preferable because it knows the invariables
188
    // e.g. FC knows the FC rule that all Arial gets replaced by LiberationSans
189
    // whereas we would have to check for every size or attribute
190
0
    if( aOut.maSearchName.isEmpty() )
191
0
        return false;
192
193
0
    const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut );
194
195
#if OSL_DEBUG_LEVEL >= 2
196
    std::ostringstream oss;
197
    oss << "FcGFSubstitution \""
198
        << rFontSelData.maTargetName
199
        << "\" bipw="
200
        << rFontSelData.GetWeight()
201
        << rFontSelData.GetItalic()
202
        << rFontSelData.GetPitch()
203
        << rFontSelData.GetWidthType()
204
        << " -> ";
205
    if( !bHaveSubstitute )
206
        oss << "no substitute available.";
207
    else
208
        oss << "\""
209
            << aOut.maSearchName
210
            << "\" bipw="
211
            << aOut.GetWeight()
212
            << aOut.GetItalic()
213
            << aOut.GetPitch()
214
            << aOut.GetWidthType();
215
    SAL_INFO("vcl.fonts", oss.str());
216
#endif
217
218
0
    if( bHaveSubstitute )
219
0
        rFontSelData = std::move(aOut);
220
221
0
    return bHaveSubstitute;
222
0
}
223
224
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */