Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/vcl/source/font/FeatureCollector.cxx
Line
Count
Source (jump to first uncovered line)
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
10
#include <font/FeatureCollector.hxx>
11
#include <font/OpenTypeFeatureDefinitionList.hxx>
12
#include <i18nlangtag/languagetag.hxx>
13
14
#include <font/OpenTypeFeatureStrings.hrc>
15
#include <svdata.hxx>
16
17
#include <hb-aat.h>
18
#include <hb-ot.h>
19
#include <hb-graphite2.h>
20
21
namespace vcl::font
22
{
23
bool FeatureCollector::collectGraphite()
24
0
{
25
0
    gr_face* grFace = hb_graphite2_face_get_gr_face(m_pHbFace);
26
27
0
    if (grFace == nullptr)
28
0
        return false;
29
30
0
    gr_uint16 nUILanguage = gr_uint16(m_rLanguageTag.getLanguageType());
31
32
0
    gr_uint16 nNumberOfFeatures = gr_face_n_fref(grFace);
33
0
    gr_feature_val* pfeatureValues
34
0
        = gr_face_featureval_for_lang(grFace, 0); // shame we don't know which lang
35
36
0
    for (gr_uint16 i = 0; i < nNumberOfFeatures; ++i)
37
0
    {
38
0
        const gr_feature_ref* pFeatureRef = gr_face_fref(grFace, i);
39
0
        gr_uint32 nFeatureCode = gr_fref_id(pFeatureRef);
40
41
0
        if (nFeatureCode == 0) // illegal feature code - skip
42
0
            continue;
43
44
0
        gr_uint16 nValue = gr_fref_feature_value(pFeatureRef, pfeatureValues);
45
0
        gr_uint32 nLabelLength = 0;
46
0
        void* pLabel = gr_fref_label(pFeatureRef, &nUILanguage, gr_utf8, &nLabelLength);
47
0
        OUString sLabel(OUString::createFromAscii(static_cast<char*>(pLabel)));
48
0
        gr_label_destroy(pLabel);
49
50
0
        std::vector<vcl::font::FeatureParameter> aParameters;
51
0
        gr_uint16 nNumberOfValues = gr_fref_n_values(pFeatureRef);
52
53
0
        if (nNumberOfValues > 0)
54
0
        {
55
0
            for (gr_uint16 j = 0; j < nNumberOfValues; ++j)
56
0
            {
57
0
                gr_uint32 nValueLabelLength = 0;
58
0
                void* pValueLabel = gr_fref_value_label(pFeatureRef, j, &nUILanguage, gr_utf8,
59
0
                                                        &nValueLabelLength);
60
0
                OUString sValueLabel(OUString::createFromAscii(static_cast<char*>(pValueLabel)));
61
0
                gr_uint16 nParamValue = gr_fref_value(pFeatureRef, j);
62
0
                aParameters.emplace_back(sal_uInt32(nParamValue), sValueLabel);
63
0
                gr_label_destroy(pValueLabel);
64
0
            }
65
66
0
            auto eFeatureParameterType = vcl::font::FeatureParameterType::ENUM;
67
68
            // Check if the parameters are boolean
69
0
            if (aParameters.size() == 2
70
0
                && (aParameters[0].getDescription() == "True"
71
0
                    || aParameters[0].getDescription() == "False"))
72
0
            {
73
0
                eFeatureParameterType = vcl::font::FeatureParameterType::BOOL;
74
0
                aParameters.clear();
75
0
            }
76
77
0
            m_rFontFeatures.emplace_back(nFeatureCode, vcl::font::FeatureType::Graphite);
78
0
            vcl::font::Feature& rFeature = m_rFontFeatures.back();
79
0
            rFeature.m_aDefinition = vcl::font::FeatureDefinition(
80
0
                nFeatureCode, std::move(sLabel), eFeatureParameterType, std::move(aParameters),
81
0
                int32_t(nValue));
82
0
        }
83
0
    }
84
0
    gr_featureval_destroy(pfeatureValues);
85
0
    return true;
86
0
}
87
88
void FeatureCollector::collectForTable(hb_tag_t aTableTag)
89
0
{
90
0
    unsigned int nFeatureCount
91
0
        = hb_ot_layout_table_get_feature_tags(m_pHbFace, aTableTag, 0, nullptr, nullptr);
92
0
    std::vector<hb_tag_t> aFeatureTags(nFeatureCount);
93
0
    hb_ot_layout_table_get_feature_tags(m_pHbFace, aTableTag, 0, &nFeatureCount,
94
0
                                        aFeatureTags.data());
95
0
    aFeatureTags.resize(nFeatureCount);
96
97
0
    for (hb_tag_t aFeatureTag : aFeatureTags)
98
0
    {
99
0
        if (OpenTypeFeatureDefinitionList().isRequired(aFeatureTag))
100
0
            continue;
101
102
0
        m_rFontFeatures.emplace_back();
103
0
        vcl::font::Feature& rFeature = m_rFontFeatures.back();
104
0
        rFeature.m_nCode = aFeatureTag;
105
106
0
        FeatureDefinition aDefinition = OpenTypeFeatureDefinitionList().getDefinition(rFeature);
107
0
        std::vector<vcl::font::FeatureParameter> aParameters{
108
0
            { 0, VclResId(STR_FONT_FEATURE_PARAM_NONE) }
109
0
        };
110
111
0
        unsigned int nFeatureIdx;
112
0
        if (hb_ot_layout_language_find_feature(m_pHbFace, aTableTag, 0,
113
0
                                               HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, aFeatureTag,
114
0
                                               &nFeatureIdx))
115
0
        {
116
            // ssXX and cvXX can have name ID defined for them, check for
117
            // them and use as appropriate.
118
0
            hb_ot_name_id_t aLabelID;
119
0
            hb_ot_name_id_t aFirstParameterID;
120
0
            unsigned nNamedParameters;
121
0
            if (hb_ot_layout_feature_get_name_ids(m_pHbFace, aTableTag, nFeatureIdx, &aLabelID,
122
0
                                                  nullptr, nullptr, &nNamedParameters,
123
0
                                                  &aFirstParameterID))
124
0
            {
125
0
                OUString sLabel = m_pFace->GetName(NameID(aLabelID), m_rLanguageTag);
126
0
                if (!sLabel.isEmpty())
127
0
                    aDefinition = vcl::font::FeatureDefinition(aFeatureTag, sLabel);
128
129
                // cvXX features can have parameters name IDs, check for
130
                // them and populate feature parameters as appropriate.
131
0
                for (unsigned i = 0; i < nNamedParameters; i++)
132
0
                {
133
0
                    hb_ot_name_id_t aNameID = aFirstParameterID + i;
134
0
                    OUString sName = m_pFace->GetName(NameID(aNameID), m_rLanguageTag);
135
0
                    if (!sName.isEmpty())
136
0
                        aParameters.emplace_back(uint32_t(i + 1), sName);
137
0
                    else
138
0
                        aParameters.emplace_back(uint32_t(i + 1), OUString::number(i + 1));
139
0
                }
140
0
            }
141
142
0
            unsigned int nAlternates = 0;
143
0
            if (aTableTag == HB_OT_TAG_GSUB)
144
0
            {
145
                // Collect lookups in this feature, and input glyphs for each
146
                // lookup, and calculate the max number of alternates they have.
147
0
                unsigned int nLookups = hb_ot_layout_feature_get_lookups(
148
0
                    m_pHbFace, aTableTag, nFeatureIdx, 0, nullptr, nullptr);
149
0
                std::vector<unsigned int> aLookups(nLookups);
150
0
                hb_ot_layout_feature_get_lookups(m_pHbFace, aTableTag, nFeatureIdx, 0, &nLookups,
151
0
                                                 aLookups.data());
152
153
0
                hb_set_t* pGlyphs = hb_set_create();
154
0
                for (unsigned int nLookupIdx : aLookups)
155
0
                {
156
0
                    hb_set_clear(pGlyphs);
157
0
                    hb_ot_layout_lookup_collect_glyphs(m_pHbFace, aTableTag, nLookupIdx, nullptr,
158
0
                                                       pGlyphs, nullptr, nullptr);
159
0
                    hb_codepoint_t nGlyphIdx = HB_SET_VALUE_INVALID;
160
0
                    while (hb_set_next(pGlyphs, &nGlyphIdx))
161
0
                    {
162
0
                        nAlternates
163
0
                            = std::max(nAlternates,
164
0
                                       hb_ot_layout_lookup_get_glyph_alternates(
165
0
                                           m_pHbFace, nLookupIdx, nGlyphIdx, 0, nullptr, nullptr));
166
0
                    }
167
0
                }
168
0
                hb_set_destroy(pGlyphs);
169
0
            }
170
171
            // Append the alternates to the feature parameters, keeping any
172
            // existing ones calculated from cvXX features above.
173
0
            for (unsigned int i = aParameters.size() - 1; i < nAlternates; i++)
174
0
                aParameters.emplace_back(uint32_t(i + 1), OUString::number(i + 1));
175
176
0
            if (aParameters.size() > 1)
177
0
            {
178
0
                aDefinition = vcl::font::FeatureDefinition(
179
0
                    aFeatureTag, aDefinition.getDescription(),
180
0
                    vcl::font::FeatureParameterType::ENUM, std::move(aParameters), 0);
181
0
            }
182
0
        }
183
184
0
        if (aDefinition)
185
0
            rFeature.m_aDefinition = std::move(aDefinition);
186
0
    }
187
0
}
188
189
bool FeatureCollector::collect()
190
0
{
191
0
    gr_face* grFace = hb_graphite2_face_get_gr_face(m_pHbFace);
192
193
0
    if (grFace)
194
0
    {
195
0
        return collectGraphite();
196
0
    }
197
0
    else
198
0
    {
199
        // tdf#163213: Font Features dialog should not show OpenType features if the font has "morx" table
200
0
        if (!hb_aat_layout_has_substitution(m_pHbFace))
201
0
            collectForTable(HB_OT_TAG_GSUB); // substitution
202
0
        collectForTable(HB_OT_TAG_GPOS); // positioning
203
0
        return true;
204
0
    }
205
0
}
206
207
} // end namespace vcl::font
208
209
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */