Signed-off-by: Nikolay Sivov nsivov@codeweavers.com ---
Tests are disabled on Wine for now.
dlls/dwrite/tests/font.c | 356 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 355 insertions(+), 1 deletion(-)
diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index 86efb8cc05..f752afd08e 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -396,6 +396,66 @@ struct WOFFHeader2 ULONG privLength; };
+struct cmap_encoding_record +{ + WORD platformID; + WORD encodingID; + DWORD offset; +}; + +struct cmap_header +{ + WORD version; + WORD numTables; + struct cmap_encoding_record tables[1]; +}; + +struct cmap_segmented_coverage_group +{ + DWORD startCharCode; + DWORD endCharCode; + DWORD startGlyphID; +}; + +struct cmap_segmented_coverage +{ + WORD format; + WORD reserved; + DWORD length; + DWORD language; + DWORD nGroups; + struct cmap_segmented_coverage_group groups[1]; +}; + +struct cmap_segmented_mapping_0 +{ + WORD format; + WORD length; + WORD language; + WORD segCountX2; + WORD searchRange; + WORD entrySelector; + WORD rangeShift; + WORD endCode[1]; +}; + +enum opentype_cmap_table_platform +{ + OPENTYPE_CMAP_TABLE_PLATFORM_WIN = 3, +}; + +enum opentype_cmap_table_encoding +{ + OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_BMP = 1, + OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_FULL = 10, +}; + +enum opentype_cmap_table_format +{ + OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING = 4, + OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE = 12, +}; + #include "poppack.h"
static void *create_factory_iid(REFIID riid) @@ -3318,14 +3378,220 @@ static void test_shared_isolated(void) ok(ref == 0, "factory not released, %u\n", ref); }
+struct dwrite_fonttable +{ + BYTE *data; + void *context; + UINT32 size; +}; + +static WORD table_read_be_word(const struct dwrite_fonttable *table, void *ptr, DWORD offset) +{ + if (!ptr) + ptr = table->data; + + if ((BYTE *)ptr < table->data || (BYTE *)ptr - table->data >= table->size) + return 0; + + if (offset > table->size - sizeof(WORD)) + return 0; + + return GET_BE_WORD(*(WORD *)((BYTE *)ptr + offset)); +} + +static DWORD table_read_be_dword(const struct dwrite_fonttable *table, void *ptr, DWORD offset) +{ + if (!ptr) + ptr = table->data; + + if ((BYTE *)ptr < table->data || (BYTE *)ptr - table->data >= table->size) + return 0; + + if (offset > table->size - sizeof(WORD)) + return 0; + + return GET_BE_DWORD(*(DWORD *)((BYTE *)ptr + offset)); +} + +static void 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; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return; + + 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; + + *elements = new_elements; + *capacity = new_capacity; +} + +static void opentype_cmap_read_table(const struct dwrite_fonttable *table, UINT16 cmap_index, UINT32 *count, + size_t *capacity, DWRITE_UNICODE_RANGE **ranges) +{ + const BYTE *tables = table->data + FIELD_OFFSET(struct cmap_header, tables); + struct cmap_encoding_record *record; + DWORD table_offset; + WORD format; + int j; + + record = (struct cmap_encoding_record *)(tables + cmap_index * sizeof(*record)); + + if (!(table_offset = table_read_be_dword(table, record, FIELD_OFFSET(struct cmap_encoding_record, offset)))) + return; + + format = table_read_be_word(table, NULL, table_offset); + switch (format) + { + case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING: + { + UINT16 segment_count = table_read_be_word(table, NULL, table_offset + + FIELD_OFFSET(struct cmap_segmented_mapping_0, segCountX2)) / 2; + DWORD start_code_offset = table_offset + sizeof(struct cmap_segmented_mapping_0) + + sizeof(WORD) * segment_count; + + for (j = 0; j < segment_count; ++j) { + WORD endcode = table_read_be_word(table, NULL, table_offset + + FIELD_OFFSET(struct cmap_segmented_mapping_0, endCode) + j * sizeof(WORD)); + WORD first; + + if (endcode == 0xffff) + break; + + first = table_read_be_word(table, NULL, start_code_offset + j * sizeof(WORD)); + + array_reserve((void **)ranges, capacity, *count + 1, sizeof(**ranges)); + (*ranges)[*count].first = first; + (*ranges)[*count].last = endcode; + (*count)++; + } + break; + } + case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE: + { + DWORD num_groups = table_read_be_dword(table, NULL, table_offset + + FIELD_OFFSET(struct cmap_segmented_coverage, nGroups)); + + for (j = 0; j < num_groups; ++j) { + DWORD group_offset = table_offset + FIELD_OFFSET(struct cmap_segmented_coverage, groups) + + j * sizeof(struct cmap_segmented_coverage_group); + DWORD first = table_read_be_dword(table, NULL, group_offset + + FIELD_OFFSET(struct cmap_segmented_coverage_group, startCharCode)); + DWORD last = table_read_be_dword(table, NULL, group_offset + + FIELD_OFFSET(struct cmap_segmented_coverage_group, endCharCode)); + + array_reserve((void **)ranges, capacity, *count + 1, sizeof(**ranges)); + (*ranges)[*count].first = first; + (*ranges)[*count].last = last; + (*count)++; + } + break; + } + default: + ok(0, "%u table format %#x unhandled.\n", cmap_index, format); + } +} + +static UINT32 opentype_cmap_get_unicode_ranges(const struct dwrite_fonttable *table, DWRITE_UNICODE_RANGE **ranges) +{ + int index_full = -1, index_bmp = -1; + unsigned int i, count = 0; + size_t capacity = 0; + const BYTE *tables; + WORD num_tables; + + *ranges = NULL; + + num_tables = table_read_be_word(table, 0, FIELD_OFFSET(struct cmap_header, numTables)); + tables = table->data + FIELD_OFFSET(struct cmap_header, tables); + + for (i = 0; i < num_tables; ++i) + { + struct cmap_encoding_record *record = (struct cmap_encoding_record *)(tables + i * sizeof(*record)); + WORD platform, encoding; + + platform = table_read_be_word(table, record, FIELD_OFFSET(struct cmap_encoding_record, platformID)); + encoding = table_read_be_word(table, record, FIELD_OFFSET(struct cmap_encoding_record, encodingID)); + + if (platform == OPENTYPE_CMAP_TABLE_PLATFORM_WIN) + { + if (encoding == OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_FULL) + { + index_full = i; + break; + } + else if (encoding == OPENTYPE_CMAP_TABLE_ENCODING_UNICODE_BMP) + index_bmp = i; + } + } + + if (index_full != -1) + opentype_cmap_read_table(table, index_full, &count, &capacity, ranges); + else if (index_bmp != -1) + opentype_cmap_read_table(table, index_bmp, &count, &capacity, ranges); + + return count; +} + +static UINT32 fontface_get_expected_unicode_ranges(IDWriteFontFace1 *fontface, DWRITE_UNICODE_RANGE **out) +{ + struct dwrite_fonttable cmap; + DWRITE_UNICODE_RANGE *ranges; + UINT32 i, j, count; + BOOL exists; + HRESULT hr; + + *out = NULL; + + hr = IDWriteFontFace1_TryGetFontTable(fontface, MS_CMAP_TAG, (const void **)&cmap.data, + &cmap.size, &cmap.context, &exists); + if (FAILED(hr) || !exists) + return 0; + + count = opentype_cmap_get_unicode_ranges(&cmap, &ranges); + IDWriteFontFace1_ReleaseFontTable(fontface, cmap.context); + + *out = heap_alloc(count * sizeof(**out)); + + /* Eliminate duplicates and merge ranges together. */ + for (i = 0, j = 0; i < count; ++i) { + if (j) { + DWRITE_UNICODE_RANGE *prev = &(*out)[j-1]; + /* Merge adjacent ranges. */ + if (ranges[i].first == prev->last + 1) { + prev->last = ranges[i].last; + continue; + } + } + (*out)[j++] = ranges[i]; + } + + heap_free(ranges); + + return j; +} + static void test_GetUnicodeRanges(void) { + IDWriteFontCollection *syscollection; DWRITE_UNICODE_RANGE *ranges, r; IDWriteFontFile *ffile = NULL; IDWriteFontFace1 *fontface1; IDWriteFontFace *fontface; IDWriteFactory *factory; - UINT32 count; + UINT32 count, i; HRESULT hr; HRSRC font; ULONG ref; @@ -3383,6 +3649,94 @@ static void test_GetUnicodeRanges(void) ok(hr == S_OK, "got 0x%08x\n", hr);
IDWriteFontFace1_Release(fontface1); + +if (strcmp(winetest_platform, "wine")) { + + hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE); + ok(hr == S_OK, "Failed to get system collection, hr %#x.\n", hr); + + count = IDWriteFontCollection_GetFontFamilyCount(syscollection); + + for (i = 0; i < count; i++) { + WCHAR familynameW[256], facenameW[128]; + IDWriteLocalizedStrings *names; + IDWriteFontFamily *family; + UINT32 j, k, fontcount; + IDWriteFont *font; + + hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family); + ok(hr == S_OK, "Failed to get font family, hr %#x.\n", hr); + + hr = IDWriteFontFamily_GetFamilyNames(family, &names); + ok(hr == S_OK, "Failed to get family names, hr %#x.\n", hr); + + get_enus_string(names, familynameW, ARRAY_SIZE(familynameW)); + IDWriteLocalizedStrings_Release(names); + + fontcount = IDWriteFontFamily_GetFontCount(family); + for (j = 0; j < fontcount; j++) { + DWRITE_UNICODE_RANGE *expected_ranges = NULL; + UINT32 range_count, expected_count; + + hr = IDWriteFontFamily_GetFont(family, j, &font); + ok(hr == S_OK, "Failed to get font, hr %#x.\n", hr); + + hr = IDWriteFont_CreateFontFace(font, &fontface); + ok(hr == S_OK, "Failed to create fontface, hr %#x.\n", hr); + + hr = IDWriteFont_GetFaceNames(font, &names); + ok(hr == S_OK, "Failed to get face names, hr %#x.\n", hr); + IDWriteFont_Release(font); + + get_enus_string(names, facenameW, ARRAY_SIZE(facenameW)); + + IDWriteLocalizedStrings_Release(names); + + if (IDWriteFontFace_IsSymbolFont(fontface)) { + skip("Skipping for symbol font %s %s.\n", wine_dbgstr_w(familynameW), wine_dbgstr_w(facenameW)); + IDWriteFontFace_Release(fontface); + continue; + } + + IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void **)&fontface1); + + hr = IDWriteFontFace1_GetUnicodeRanges(fontface1, 0, NULL, &range_count); + ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#x.\n", hr); + + ranges = heap_alloc(range_count * sizeof(*ranges)); + + hr = IDWriteFontFace1_GetUnicodeRanges(fontface1, range_count, ranges, &range_count); + ok(hr == S_OK, "Failed to get ranges, hr %#x.\n", hr); + + expected_count = fontface_get_expected_unicode_ranges(fontface1, &expected_ranges); + ok(expected_count == range_count, "%s - %s: unexpected range count %u, expected %u.\n", + wine_dbgstr_w(familynameW), wine_dbgstr_w(facenameW), range_count, expected_count); + + if (expected_count == range_count) { + if (memcmp(expected_ranges, ranges, expected_count * sizeof(*ranges))) { + for (k = 0; k < expected_count; ++k) { + BOOL failed = memcmp(&expected_ranges[k], &ranges[k], sizeof(*ranges)); + ok(!failed, "%u: %s - %s mismatching range [%#x, %#x] vs [%#x, %#x].\n", k, + wine_dbgstr_w(familynameW), wine_dbgstr_w(facenameW), ranges[k].first, ranges[k].last, + expected_ranges[k].first, expected_ranges[k].last); + if (failed) + break; + } + } + } + + heap_free(expected_ranges); + heap_free(ranges); + + IDWriteFontFace1_Release(fontface1); + IDWriteFontFace_Release(fontface); + } + + IDWriteFontFamily_Release(family); + } + + IDWriteFontCollection_Release(syscollection); +} ref = IDWriteFactory_Release(factory); ok(ref == 0, "factory not released, %u\n", ref); }