/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: */ |