[PATCH] dwrite/tests: Add some tests for GetUnicodeRanges().

Nikolay Sivov nsivov at codeweavers.com
Mon Nov 26 02:43:09 CST 2018


Signed-off-by: Nikolay Sivov <nsivov at 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);
 }
-- 
2.19.2




More information about the wine-devel mailing list