116 {
117
118
119 QMap<QString, KoOpenTypeFeatureInfo>
featureInfo = previousFeatureInfo;
121 hb_tag_t table = gpos? HB_OT_TAG_GPOS: HB_OT_TAG_GSUB;
122 uint targetLanguageIndex = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
123
125 Q_FOREACH(const QString locale, locales) {
126 QByteArray l(locale.split("_").join("-").toLatin1());
127 localeTags.append(hb_language_from_string(l.data(), l.size()));
128 }
129
130 hb_language_t languageTag = lang.isEmpty()? HB_LANGUAGE_INVALID: hb_language_from_string(lang.data(), lang.size());
131 uint scriptCount = hb_ot_layout_table_get_script_tags(hbFace.data(), table, 0,
nullptr,
nullptr);
134 for (
uint script = 0; script < scriptCount; script++) {
135 uint scriptCount = 1;
136 hb_tag_t scriptTag;
138 hb_ot_layout_table_get_script_tags(hbFace.data(), table, script, &scriptCount, &scriptTag);
139 if (!hb_ot_layout_table_select_script(hbFace.data(), table, 1, &scriptTag, &scriptIndex, &scriptTag)) {
140 continue;
141 }
142 scriptIndices.append(scriptIndex);
143
144 uint languageCount = hb_ot_layout_script_get_language_tags(hbFace.data(), table, scriptIndex, 0,
nullptr,
nullptr);
145
146 bool foundLanguage = false;
147
148 for(
uint j = 0; j < languageCount; j++) {
149 hb_tag_t langTag;
151 hb_ot_layout_script_get_language_tags(hbFace.data(), table, scriptIndex, j, &count, &langTag);
152 if (count < 1) continue;
153 if (hb_ot_tag_to_language(langTag) == languageTag) {
155 if (hb_ot_layout_script_select_language(hbFace.data(), table, scriptIndex, count, &langTag, &languageIndex)) {
156 targetLanguageIndex = languageIndex;
157 foundLanguage = true;
158 break;
159 }
160 }
161 }
162 if (foundLanguage) break;
163 }
164
165 QHash<quint32, int> glyphToCodepoint;
166 for (
int i = 0; i<
charMap.size(); i++) {
167 glyphToCodepoint.insert(
charMap.at(i).glyphIndex, i);
168 }
169
170 for (auto scriptIt = scriptIndices.begin(); scriptIt != scriptIndices.end(); scriptIt++) {
171 uint targetScriptIndex = *scriptIt;
172 uint featureCount = hb_ot_layout_language_get_feature_tags(hbFace.data(),
173 table,
174 targetScriptIndex,
175 targetLanguageIndex,
176 0, nullptr, nullptr);
177 hb_ot_layout_language_get_feature_tags(hbFace.data(), table, targetScriptIndex,
178 targetLanguageIndex,
179 0, nullptr, nullptr);
180 for(
uint k = 0; k < featureCount; k++) {
182 hb_tag_t features;
183 hb_ot_layout_language_get_feature_tags(hbFace.data(), table, targetScriptIndex,
184 targetLanguageIndex,
185 k, &count, &features);
186 if (count < 1) continue;
187 tags.append(features);
188 }
189
192 for (auto tagIt = tags.begin(); tagIt != tags.end(); tagIt++) {
193 char c[4];
194 hb_tag_to_string(*tagIt, c);
195 const QByteArray tagName(c, 4);
197
198
199 bool found = hb_ot_layout_language_find_feature (hbFace.data(), table,
200 targetScriptIndex,
201 targetLanguageIndex,
202 *tagIt,
203 &featureIndex );
204
205 if (!found || featureIndicesProcessed.contains(featureIndex)) {
206 continue;
207 }
208 featureIndicesProcessed.append(featureIndex);
209
212 hb_ot_name_id_t labelId = HB_OT_NAME_ID_INVALID;
213 hb_ot_name_id_t toolTipId = HB_OT_NAME_ID_INVALID;
214 hb_ot_name_id_t sampleId = HB_OT_NAME_ID_INVALID;
215 uint namedParameters;
216 hb_ot_name_id_t firstParamId = HB_OT_NAME_ID_INVALID;
217
218 if (hb_ot_layout_feature_get_name_ids(hbFace.data(), table, featureIndex, &labelId, &toolTipId, &sampleId, &namedParameters, &firstParamId)) {
220 if (firstParamId != HB_OT_NAME_ID_INVALID) {
221 for (
uint i = 0; i < namedParameters; i++) {
222 nameIds += firstParamId + i;
223 }
224 }
225
226 for(auto nameId = nameIds.begin(); nameId != nameIds.end(); nameId++) {
227 if (*nameId == HB_OT_NAME_ID_INVALID) {
228 continue;
229 }
232 if (*nameId == sampleId) {
233 testLang.append(languageTag);
234 } else {
235 testLang = localeTags;
236 }
237 testLang.append(HB_LANGUAGE_INVALID);
238 for (auto tag = testLang.begin(); tag != testLang.end(); tag++) {
239 length = hb_ot_name_get_utf8(hbFace.data(), *nameId, *tag,
nullptr,
nullptr);
242 break;
243 }
244 }
245
246 std::vector<char> buff(
length);
247 hb_ot_name_get_utf8(hbFace.data(), *nameId, languageTag, &
length, buff.data());
249 const QString nameString = QString::fromUtf8(buff.data(),
length);
250 if (*nameId == labelId) {
251 info.
name = nameString;
252 } else if (*nameId == toolTipId) {
254 } else if (*nameId == sampleId) {
256 } else {
258 }
259 }
260 }
261 }
262
264 }
266 continue;
267 }
268
270 int lookupCount = hb_ot_layout_feature_get_lookups (hbFace.data(), table,
271 featureIndex,
272 0,
273 nullptr,
274 nullptr );
275 for (int i = 0; i < lookupCount; ++i) {
277 uint lookUpIndex = 0;
278 hb_ot_layout_feature_get_lookups (hbFace.data(), table,
279 featureIndex,
280 i,
281 &maxCount,
282 &lookUpIndex );
283 if (maxCount < 1 || lookUpsProcessed.contains(lookUpIndex)) {
284 continue;
285 }
286
287
288
289
290
295
296 hb_ot_layout_lookup_collect_glyphs (hbFace.data(), table,
297 lookUpIndex,
298 glyphsBefore.data(),
299 glyphsInput.data(),
300 glyphsAfter.data(),
301 glyphsOutput.data() );
302
303 GlyphInfo gci;
305 gci.baseString = tagName;
306
307 hb_codepoint_t currentGlyph = HB_SET_VALUE_INVALID;
308 while(hb_set_next(glyphsInput.data(), ¤tGlyph)) {
309 if (!glyphToCodepoint.contains(currentGlyph)) continue;
310 const int codePointLocation = glyphToCodepoint.value(currentGlyph);
311 CodePointInfo codePointInfo =
charMap.at(codePointLocation);
312 gci.ucs = codePointInfo.ucs;
313 bool addSample = false;
314
315 uint alt_count = hb_ot_layout_lookup_get_glyph_alternates (hbFace.data(),
316 lookUpIndex, currentGlyph,
317 0,
318 nullptr, nullptr);
319
320 if (alt_count > 0) {
321
322 for(
uint j = 1; j < alt_count; ++j) {
323 gci.featureIndex = j;
324
325 bool addToGlyphs = codePointInfo.addToGlyphsIfNotAlready(gci);
326 if (addToGlyphs && !addSample) {
327 addSample = true;
328 }
329 }
331 } else {
332 gci.featureIndex = 1;
333 addSample = codePointInfo.addToGlyphsIfNotAlready(gci);
334 }
335 charMap[codePointLocation] = codePointInfo;
336 if (samples.size() < 6 && addSample) {
337 samples.append(QString::fromUcs4(&gci.ucs, 1));
338 }
339 if (samples.size() >= 6 && samplesOnly) {
340 break;
341 }
342 }
343
344 lookUpsProcessed.append(lookUpIndex);
345 if (info.
sample.isEmpty() && !samples.isEmpty()) {
346 info.
sample = samples.join(
" ");
347 }
349
350 }
351
352 }
353 }
354
356 }
qreal length(const QPointF &vec)
QMap< QString, KoOpenTypeFeatureInfo > featureInfo() const
featureInfo
static QVector< CodePointInfo > charMap(FT_FaceSP face)
KoOpenTypeFeatureInfoFactory openTypeFeaturesFactory
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),...