114 {
115
116
117 QMap<QString, KoOpenTypeFeatureInfo>
featureInfo = previousFeatureInfo;
119 hb_tag_t table = gpos? HB_OT_TAG_GPOS: HB_OT_TAG_GSUB;
120 uint targetLanguageIndex = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
121
123 Q_FOREACH(const QString locale, locales) {
124 QByteArray l(locale.split("_").join("-").toLatin1());
125 localeTags.append(hb_language_from_string(l.data(), l.size()));
126 }
127
128 hb_language_t languageTag = lang.isEmpty()? HB_LANGUAGE_INVALID: hb_language_from_string(lang.data(), lang.size());
129 uint scriptCount = hb_ot_layout_table_get_script_tags(hbFace.data(), table, 0,
nullptr,
nullptr);
132 for (
uint script = 0; script < scriptCount; script++) {
133 uint scriptCount = 1;
134 hb_tag_t scriptTag;
136 hb_ot_layout_table_get_script_tags(hbFace.data(), table, script, &scriptCount, &scriptTag);
137 if (!hb_ot_layout_table_select_script(hbFace.data(), table, 1, &scriptTag, &scriptIndex, &scriptTag)) {
138 continue;
139 }
140 scriptIndices.append(scriptIndex);
141
142 uint languageCount = hb_ot_layout_script_get_language_tags(hbFace.data(), table, scriptIndex, 0,
nullptr,
nullptr);
143
144 bool foundLanguage = false;
145
146 for(
uint j = 0; j < languageCount; j++) {
147 hb_tag_t langTag;
149 hb_ot_layout_script_get_language_tags(hbFace.data(), table, scriptIndex, j, &count, &langTag);
150 if (count < 1) continue;
151 if (hb_ot_tag_to_language(langTag) == languageTag) {
153 if (hb_ot_layout_script_select_language(hbFace.data(), table, scriptIndex, count, &langTag, &languageIndex)) {
154 targetLanguageIndex = languageIndex;
155 foundLanguage = true;
156 break;
157 }
158 }
159 }
160 if (foundLanguage) break;
161 }
162
163 QHash<quint32, int> glyphToCodepoint;
164 for (
int i = 0; i<
charMap.size(); i++) {
165 glyphToCodepoint.insert(
charMap.at(i).glyphIndex, i);
166 }
167
168 for (auto scriptIt = scriptIndices.begin(); scriptIt != scriptIndices.end(); scriptIt++) {
169 uint targetScriptIndex = *scriptIt;
170 uint featureCount = hb_ot_layout_language_get_feature_tags(hbFace.data(),
171 table,
172 targetScriptIndex,
173 targetLanguageIndex,
174 0, nullptr, nullptr);
175 hb_ot_layout_language_get_feature_tags(hbFace.data(), table, targetScriptIndex,
176 targetLanguageIndex,
177 0, nullptr, nullptr);
178 for(
uint k = 0; k < featureCount; k++) {
180 hb_tag_t features;
181 hb_ot_layout_language_get_feature_tags(hbFace.data(), table, targetScriptIndex,
182 targetLanguageIndex,
183 k, &count, &features);
184 if (count < 1) continue;
185 tags.append(features);
186 }
187
188
189
193 for (auto tagIt = tags.begin(); tagIt != tags.end(); tagIt++) {
194 char c[4];
195 hb_tag_to_string(*tagIt, c);
196 const QByteArray tagName(c, 4);
198
199
200 bool found = hb_ot_layout_language_find_feature (hbFace.data(), table,
201 targetScriptIndex,
202 targetLanguageIndex,
203 *tagIt,
204 &featureIndex );
205
206 if (!found || featureIndicesProcessed.contains(featureIndex)) {
207 continue;
208 }
209 featureIndicesProcessed.append(featureIndex);
210
213 hb_ot_name_id_t labelId = HB_OT_NAME_ID_INVALID;
214 hb_ot_name_id_t toolTipId = HB_OT_NAME_ID_INVALID;
215 hb_ot_name_id_t sampleId = HB_OT_NAME_ID_INVALID;
216 uint namedParameters;
217 hb_ot_name_id_t firstParamId = HB_OT_NAME_ID_INVALID;
218
219 if (hb_ot_layout_feature_get_name_ids(hbFace.data(), table, featureIndex, &labelId, &toolTipId, &sampleId, &namedParameters, &firstParamId)) {
221 if (firstParamId != HB_OT_NAME_ID_INVALID) {
222 for (
uint i = 0; i < namedParameters; i++) {
223 nameIds += firstParamId + i;
224 }
225 }
226
227 for(auto nameId = nameIds.begin(); nameId != nameIds.end(); nameId++) {
228 if (*nameId == HB_OT_NAME_ID_INVALID) {
229 continue;
230 }
233 if (*nameId == sampleId) {
234 testLang.append(languageTag);
235 } else {
236 testLang = localeTags;
237 }
238 testLang.append(HB_LANGUAGE_INVALID);
239 for (auto tag = testLang.begin(); tag != testLang.end(); tag++) {
240 length = hb_ot_name_get_utf8(hbFace.data(), *nameId, *tag,
nullptr,
nullptr);
243 break;
244 }
245 }
246
247 std::vector<char> buff(
length);
248 hb_ot_name_get_utf8(hbFace.data(), *nameId, languageTag, &
length, buff.data());
250 const QString nameString = QString::fromUtf8(buff.data(),
length);
251 if (*nameId == labelId) {
252 info.
name = nameString;
253 } else if (*nameId == toolTipId) {
255 } else if (*nameId == sampleId) {
257 } else {
259 }
260 }
261 }
262 }
263
265 }
267 continue;
268 }
269
271 int lookupCount = hb_ot_layout_feature_get_lookups (hbFace.data(), table,
272 featureIndex,
273 0,
274 nullptr,
275 nullptr );
276 for (int i = 0; i < lookupCount; ++i) {
278 uint lookUpIndex = 0;
279 hb_ot_layout_feature_get_lookups (hbFace.data(), table,
280 featureIndex,
281 i,
282 &maxCount,
283 &lookUpIndex );
284 if (maxCount < 1 || lookUpsProcessed.contains(lookUpIndex)) {
285 continue;
286 }
287
288
289
290
291
296
297 hb_ot_layout_lookup_collect_glyphs (hbFace.data(), table,
298 lookUpIndex,
299 glyphsBefore.data(),
300 glyphsInput.data(),
301 glyphsAfter.data(),
302 glyphsOutput.data() );
303
304 GlyphInfo gci;
306 gci.baseString = tagName;
307
308 hb_codepoint_t currentGlyph = HB_SET_VALUE_INVALID;
309 while(hb_set_next(glyphsInput.data(), ¤tGlyph)) {
310 if (!glyphToCodepoint.contains(currentGlyph)) continue;
311 const int codePointLocation = glyphToCodepoint.value(currentGlyph);
312 CodePointInfo codePointInfo =
charMap.at(codePointLocation);
313 gci.ucs = codePointInfo.ucs;
314 bool addSample = false;
315
316 uint alt_count = hb_ot_layout_lookup_get_glyph_alternates (hbFace.data(),
317 lookUpIndex, currentGlyph,
318 0,
319 nullptr, nullptr);
320
321 if (alt_count > 0) {
322
323 for(
uint j = 1; j < alt_count; ++j) {
324 gci.featureIndex = j;
325
326 bool addToGlyphs = codePointInfo.addToGlyphsIfNotAlready(gci);
327 if (addToGlyphs && !addSample) {
328 addSample = true;
329 }
330 }
332 } else {
333 gci.featureIndex = 1;
334 addSample = codePointInfo.addToGlyphsIfNotAlready(gci);
335 }
336 charMap[codePointLocation] = codePointInfo;
337 if (samples.size() < 6 && addSample) {
338 samples.append(QString::fromUcs4(&gci.ucs, 1));
339 }
340 if (samples.size() >= 6 && samplesOnly) {
341 break;
342 }
343 }
344
345 lookUpsProcessed.append(lookUpIndex);
346 if (info.
sample.isEmpty() && !samples.isEmpty()) {
347 info.
sample = samples.join(
" ");
348 }
350
351 }
352
353 }
354 }
355
357 }
qreal length(const QPointF &vec)
QMap< QString, KoOpenTypeFeatureInfo > featureInfo() const
featureInfo
static QVector< CodePointInfo > charMap(FT_FaceSP face)
The KoOpenTypeFeatureInfoFactory class.
KoOpenTypeFeatureInfo infoByTag(const QByteArray &tag) const
infoByTag
QString description
Description of the feature.
QString name
User-friendly name.
bool glyphPalette
Whether the feature should be visible in the glyph palette.
QStringList namedParameters
QString sample
Sample of the feature, if any. Only used by CVXX features and retrieved from the font.
int maxValue
The maximum value possible, this is by default 1 (on), but for alternate substitution(gsub 3),...