Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/dwrite_private.h | 27 +++++ dlls/dwrite/opentype.c | 201 +++++++++++++++++++++++++++-------- 2 files changed, 183 insertions(+), 45 deletions(-)
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index d992abda2a..b7d5d4583c 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -76,6 +76,33 @@ static inline const char *debugstr_matrix(const DWRITE_MATRIX *m) m->dx, m->dy); }
+static inline BOOL dwrite_array_reserve(void **elements, size_t *capacity, size_t count, size_t size) +{ + size_t new_capacity, max_capacity; + void *new_elements; + + if (count <= *capacity) + return TRUE; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return FALSE; + + new_capacity = max(4, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = max_capacity; + + if (!(new_elements = heap_realloc(*elements, new_capacity * size))) + return FALSE; + + *elements = new_elements; + *capacity = new_capacity; + + return TRUE; +} + static inline const char *debugstr_tag(DWORD tag) { return debugstr_an((char *)&tag, 4); diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 7d3db16049..02825c2d78 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -312,8 +312,6 @@ struct gasp_header struct gasp_range ranges[1]; };
-#include "poppack.h" - enum OS2_FSSELECTION { OS2_FSSELECTION_ITALIC = 1 << 0, OS2_FSSELECTION_UNDERSCORE = 1 << 1, @@ -373,22 +371,25 @@ struct vdmx_group struct vdmx_vtable entries[1]; };
-typedef struct { - CHAR FeatureTag[4]; - WORD Feature; -} OT_FeatureRecord; +struct ot_feature_record +{ + DWORD tag; + WORD offset; +};
-typedef struct { - WORD FeatureCount; - OT_FeatureRecord FeatureRecord[1]; -} OT_FeatureList; +struct ot_feature_list +{ + WORD feature_count; + struct ot_feature_record features[1]; +};
-typedef struct { - WORD LookupOrder; /* Reserved */ - WORD ReqFeatureIndex; - WORD FeatureCount; - WORD FeatureIndex[1]; -} OT_LangSys; +struct ot_langsys +{ + WORD lookup_order; /* Reserved */ + WORD required_feature_index; + WORD feature_count; + WORD feature_index[1]; +};
struct ot_langsys_record { @@ -432,11 +433,12 @@ enum OPENTYPE_PLATFORM_ID OPENTYPE_PLATFORM_CUSTOM };
-typedef struct { - WORD FeatureParams; - WORD LookupCount; - WORD LookupListIndex[1]; -} OT_Feature; +struct ot_feature +{ + WORD feature_params; + WORD lookup_count; + WORD lookuplist_index[1]; +};
typedef struct { WORD LookupCount; @@ -469,10 +471,12 @@ typedef struct { DWORD ExtensionOffset; } GSUB_ExtensionPosFormat1;
-enum OPENTYPE_GPOS_LOOKUPS +#include "poppack.h" + +enum gsub_lookup_type { - OPENTYPE_GPOS_SINGLE_SUBST = 1, - OPENTYPE_GPOS_EXTENSION_SUBST = 7 + GSUB_LOOKUP_SINGLE_SUBST = 1, + GSUB_LOOKUP_EXTENSION_SUBST = 7, };
enum TT_NAME_WINDOWS_ENCODING_ID @@ -1852,31 +1856,30 @@ static inline const struct ot_script *opentype_get_script(const struct ot_script return NULL; }
-static inline const OT_LangSys *opentype_get_langsys(const struct ot_script *script, UINT32 languagetag) +static inline const struct ot_langsys *opentype_get_langsys(const struct ot_script *script, UINT32 languagetag) { UINT16 j;
for (j = 0; j < GET_BE_WORD(script->langsys_count); j++) { const char *tag = script->langsys[j].tag; if (languagetag == DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3])) - return (OT_LangSys*)((BYTE*)script + GET_BE_WORD(script->langsys[j].langsys)); + return (struct ot_langsys *)((BYTE*)script + GET_BE_WORD(script->langsys[j].langsys)); }
return NULL; }
-static void opentype_add_font_features(const struct gpos_gsub_header *header, const OT_LangSys *langsys, +static void opentype_add_font_features(const struct gpos_gsub_header *header, const struct ot_langsys *langsys, UINT32 max_tagcount, UINT32 *count, DWRITE_FONT_FEATURE_TAG *tags) { - const OT_FeatureList *features = (const OT_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->feature_list)); + const struct ot_feature_list *features = (const struct ot_feature_list *)((const BYTE*)header + GET_BE_WORD(header->feature_list)); UINT16 j;
- for (j = 0; j < GET_BE_WORD(langsys->FeatureCount); j++) { - const OT_FeatureRecord *feature = &features->FeatureRecord[langsys->FeatureIndex[j]]; - const char *tag = feature->FeatureTag; + for (j = 0; j < GET_BE_WORD(langsys->feature_count); j++) { + const struct ot_feature_record *feature = &features->features[langsys->feature_index[j]];
if (*count < max_tagcount) - tags[*count] = DWRITE_MAKE_OPENTYPE_TAG(tag[0], tag[1], tag[2], tag[3]); + tags[*count] = GET_BE_DWORD(feature->tag);
(*count)++; } @@ -1912,7 +1915,7 @@ HRESULT opentype_get_typographic_features(IDWriteFontFace *fontface, UINT32 scri
script = opentype_get_script(scriptlist, scripttag); if (script) { - const OT_LangSys *langsys = opentype_get_langsys(script, languagetag); + const struct ot_langsys *langsys = opentype_get_langsys(script, languagetag); if (langsys) opentype_add_font_features(header, langsys, max_tagcount, count, tags); } @@ -2162,27 +2165,27 @@ void opentype_colr_next_glyph(const struct dwrite_fonttable *colr, struct dwrite BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) { const struct gpos_gsub_header *header; - const OT_FeatureList *featurelist; + const struct ot_feature_list *featurelist; const OT_LookupList *lookup_list; BOOL exists = FALSE, ret = FALSE; + unsigned int i, j; const void *data; void *context; UINT32 size; HRESULT hr; - UINT16 i;
hr = IDWriteFontFace4_TryGetFontTable(fontface, MS_GSUB_TAG, &data, &size, &context, &exists); if (FAILED(hr) || !exists) return FALSE;
header = data; - featurelist = (OT_FeatureList*)((BYTE*)header + GET_BE_WORD(header->feature_list)); + featurelist = (struct ot_feature_list *)((BYTE*)header + GET_BE_WORD(header->feature_list)); lookup_list = (const OT_LookupList*)((BYTE*)header + GET_BE_WORD(header->lookup_list));
- for (i = 0; i < GET_BE_WORD(featurelist->FeatureCount); i++) { - if (*(UINT32*)featurelist->FeatureRecord[i].FeatureTag == DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING) { - const OT_Feature *feature = (const OT_Feature*)((BYTE*)featurelist + GET_BE_WORD(featurelist->FeatureRecord[i].Feature)); - UINT16 lookup_count = GET_BE_WORD(feature->LookupCount), i, index, count, type; + for (i = 0; i < GET_BE_WORD(featurelist->feature_count); i++) { + if (featurelist->features[i].tag == DWRITE_FONT_FEATURE_TAG_VERTICAL_WRITING) { + const struct ot_feature *feature = (const struct ot_feature*)((BYTE*)featurelist + GET_BE_WORD(featurelist->features[i].offset)); + UINT16 lookup_count = GET_BE_WORD(feature->lookup_count), index, count, type; const GSUB_SingleSubstFormat2 *subst2; const OT_LookupTable *lookup_table; UINT32 offset; @@ -2190,13 +2193,13 @@ BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) if (lookup_count == 0) continue;
- for (i = 0; i < lookup_count; i++) { + for (j = 0; j < lookup_count; ++j) { /* check if lookup is empty */ - index = GET_BE_WORD(feature->LookupListIndex[i]); + index = GET_BE_WORD(feature->lookuplist_index[j]); lookup_table = (const OT_LookupTable*)((BYTE*)lookup_list + GET_BE_WORD(lookup_list->Lookup[index]));
type = GET_BE_WORD(lookup_table->LookupType); - if (type != OPENTYPE_GPOS_SINGLE_SUBST && type != OPENTYPE_GPOS_EXTENSION_SUBST) + if (type != GSUB_LOOKUP_SINGLE_SUBST && type != GSUB_LOOKUP_EXTENSION_SUBST) continue;
count = GET_BE_WORD(lookup_table->SubTableCount); @@ -2204,7 +2207,7 @@ BOOL opentype_has_vertical_variants(IDWriteFontFace4 *fontface) continue;
offset = GET_BE_WORD(lookup_table->SubTable[0]); - if (type == OPENTYPE_GPOS_EXTENSION_SUBST) { + if (type == GSUB_LOOKUP_EXTENSION_SUBST) { const GSUB_ExtensionPosFormat1 *ext = (const GSUB_ExtensionPosFormat1 *)((const BYTE *)lookup_table + offset); if (GET_BE_WORD(ext->SubstFormat) == 1) offset += GET_BE_DWORD(ext->ExtensionOffset); @@ -2494,8 +2497,116 @@ DWORD opentype_layout_find_language(const struct scriptshaping_cache *cache, DWO return 0; }
+static void opentype_layout_apply_gpos_lookup(struct scriptshaping_context *context, int lookup_index) +{ + /* FIXME: stub */ +} + +struct lookups +{ + int *indexes; + size_t capacity; + size_t count; +}; + +static int lookups_sorting_compare(const void *left, const void *right) +{ + return *(int *)left - *(int *)right; +}; + void opentype_layout_apply_gpos_features(struct scriptshaping_context *context, unsigned int script_index, unsigned int language_index, const struct shaping_features *features) { - /* FIXME: stub */ + WORD table_offset, langsys_offset, script_feature_count, total_feature_count, total_lookup_count; + struct scriptshaping_cache *cache = context->cache; + const struct ot_feature_list *feature_list; + struct lookups lookups = { 0 }; + unsigned int i, j, l; + + /* ScriptTable offset. */ + table_offset = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + + FIELD_OFFSET(struct ot_script_list, scripts) + script_index * sizeof(struct ot_script_record) + + FIELD_OFFSET(struct ot_script_record, script)); + if (!table_offset) + return; + + if (language_index == ~0u) + langsys_offset = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset); + else + langsys_offset = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset + + FIELD_OFFSET(struct ot_script, langsys) + language_index * sizeof(struct ot_langsys_record) + + FIELD_OFFSET(struct ot_langsys_record, langsys)); + + script_feature_count = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset + + langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_count)); + if (!script_feature_count) + return; + + total_feature_count = table_read_be_word(&cache->gpos.table, cache->gpos.feature_list); + if (!total_feature_count) + return; + + total_lookup_count = table_read_be_word(&cache->gpos.table, cache->gpos.lookup_list); + if (!total_lookup_count) + return; + + feature_list = table_read_ensure(&cache->gpos.table, cache->gpos.feature_list, + FIELD_OFFSET(struct ot_feature_list, features[total_feature_count])); + if (!feature_list) + return; + + /* Collect lookups for all given features. */ + for (i = 0; i < features->count; ++i) + { + for (j = 0; j < script_feature_count; ++j) + { + WORD feature_index = table_read_be_word(&cache->gpos.table, cache->gpos.script_list + table_offset + + langsys_offset + FIELD_OFFSET(struct ot_langsys, feature_index[j])); + if (feature_index >= total_feature_count) + continue; + + if (feature_list->features[feature_index].tag == features->tags[i]) + { + WORD feature_offset = GET_BE_WORD(feature_list->features[feature_index].offset); + WORD lookup_count; + + lookup_count = table_read_be_word(&cache->gpos.table, cache->gpos.feature_list + feature_offset + + FIELD_OFFSET(struct ot_feature, lookup_count)); + if (!lookup_count) + continue; + + if (!dwrite_array_reserve((void **)&lookups.indexes, &lookups.capacity, lookups.count + lookup_count, + sizeof(*lookups.indexes))) + { + heap_free(lookups.indexes); + return; + } + + for (l = 0; l < lookup_count; ++l) + { + WORD lookup_index = table_read_be_word(&cache->gpos.table, cache->gpos.feature_list + + feature_offset + FIELD_OFFSET(struct ot_feature, lookuplist_index[l])); + + if (lookup_index >= total_lookup_count) + continue; + + lookups.indexes[lookups.count++] = lookup_index; + } + } + } + } + + /* Sort lookups. */ + qsort(lookups.indexes, lookups.count, sizeof(*lookups.indexes), lookups_sorting_compare); + + for (l = 0; l < lookups.count; ++l) + { + /* Skip duplicates. */ + if (l && lookups.indexes[l] == lookups.indexes[l - 1]) + continue; + + opentype_layout_apply_gpos_lookup(context, lookups.indexes[l]); + } + + heap_free(lookups.indexes); }