[PATCH 3/5] dwrite: Do not require fontface object for HasCharacter().

Nikolay Sivov nsivov at codeweavers.com
Tue Jun 9 04:14:35 CDT 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/dwrite/dwrite_private.h |  41 ++++++
 dlls/dwrite/font.c           |  41 ++----
 dlls/dwrite/opentype.c       | 257 +++++++++++++++++++++++++++++++++++
 3 files changed, 313 insertions(+), 26 deletions(-)

diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h
index 0bb5455a677..2d736fdec3c 100644
--- a/dlls/dwrite/dwrite_private.h
+++ b/dlls/dwrite/dwrite_private.h
@@ -195,6 +195,47 @@ enum font_flags
     FONTFACE_HAS_VERTICAL_VARIANTS = 1 << 4
 };
 
+struct dwrite_cmap;
+
+typedef UINT16 (*p_cmap_get_glyph_func)(const struct dwrite_cmap *cmap, unsigned int ch);
+
+struct dwrite_cmap
+{
+    const void *data;
+    union
+    {
+        struct
+        {
+            unsigned int seg_count;
+            unsigned int glyph_id_array_len;
+
+            const UINT16 *ends;
+            const UINT16 *starts;
+            const UINT16 *id_delta;
+            const UINT16 *id_range_offset;
+            const UINT16 *glyph_id_array;
+        } format4;
+        struct
+        {
+            unsigned int first;
+            unsigned int last;
+        } format6_10;
+        struct
+        {
+            unsigned int group_count;
+        } format12_13;
+    } u;
+    p_cmap_get_glyph_func get_glyph;
+    unsigned short symbol : 1;
+    IDWriteFontFileStream *stream;
+    void *table_context;
+};
+
+extern void dwrite_cmap_init(struct dwrite_cmap *cmap, IDWriteFontFile *file, unsigned int face_index,
+        DWRITE_FONT_FACE_TYPE face_type) DECLSPEC_HIDDEN;
+extern void dwrite_cmap_release(struct dwrite_cmap *cmap) DECLSPEC_HIDDEN;
+extern UINT16 opentype_cmap_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch) DECLSPEC_HIDDEN;
+
 struct dwrite_fontface
 {
     IDWriteFontFace5 IDWriteFontFace5_iface;
diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c
index 75d2593e07e..dbb44327f26 100644
--- a/dlls/dwrite/font.c
+++ b/dlls/dwrite/font.c
@@ -80,6 +80,7 @@ struct dwrite_font_data
     FONTSIGNATURE fontsig;
     UINT32 flags; /* enum font_flags */
     struct dwrite_font_propvec propvec;
+    struct dwrite_cmap cmap;
     /* Static axis for weight/width/italic. */
     DWRITE_FONT_AXIS_VALUE axis[3];
 
@@ -469,6 +470,7 @@ static void release_font_data(struct dwrite_font_data *data)
     if (data->family_names)
         IDWriteLocalizedStrings_Release(data->family_names);
 
+    dwrite_cmap_release(&data->cmap);
     IDWriteFontFile_Release(data->file);
     heap_free(data->facename);
     heap_free(data);
@@ -1958,35 +1960,23 @@ static void WINAPI dwritefont_GetMetrics(IDWriteFont3 *iface, DWRITE_FONT_METRIC
     memcpy(metrics, &This->data->metrics, sizeof(*metrics));
 }
 
-static HRESULT font_has_character(struct dwrite_font *font, UINT32 ch, BOOL *exists)
+static BOOL dwritefont_has_character(struct dwrite_font *font, UINT32 ch)
 {
-    IDWriteFontFace5 *fontface;
-    UINT16 index;
-    HRESULT hr;
-
-    *exists = FALSE;
-
-    hr = get_fontface_from_font(font, &fontface);
-    if (FAILED(hr))
-        return hr;
-
-    index = 0;
-    hr = IDWriteFontFace5_GetGlyphIndices(fontface, &ch, 1, &index);
-    IDWriteFontFace5_Release(fontface);
-    if (FAILED(hr))
-        return hr;
-
-    *exists = index != 0;
-    return S_OK;
+    UINT16 glyph;
+    dwrite_cmap_init(&font->data->cmap, font->data->file, font->data->face_index, font->data->face_type);
+    glyph = opentype_cmap_get_glyph(&font->data->cmap, ch);
+    return glyph != 0;
 }
 
 static HRESULT WINAPI dwritefont_HasCharacter(IDWriteFont3 *iface, UINT32 ch, BOOL *exists)
 {
-    struct dwrite_font *This = impl_from_IDWriteFont3(iface);
+    struct dwrite_font *font = impl_from_IDWriteFont3(iface);
+
+    TRACE("%p, %#x, %p.\n", iface, ch, exists);
 
-    TRACE("(%p)->(%#x %p)\n", This, ch, exists);
+    *exists = dwritefont_has_character(font, ch);
 
-    return font_has_character(This, ch, exists);
+    return S_OK;
 }
 
 static HRESULT WINAPI dwritefont_CreateFontFace(IDWriteFont3 *iface, IDWriteFontFace **fontface)
@@ -2088,12 +2078,11 @@ static HRESULT WINAPI dwritefont3_GetFontFaceReference(IDWriteFont3 *iface, IDWr
 
 static BOOL WINAPI dwritefont3_HasCharacter(IDWriteFont3 *iface, UINT32 ch)
 {
-    struct dwrite_font *This = impl_from_IDWriteFont3(iface);
-    BOOL ret;
+    struct dwrite_font *font = impl_from_IDWriteFont3(iface);
 
-    TRACE("(%p)->(%#x)\n", This, ch);
+    TRACE("%p, %#x.\n", iface, ch);
 
-    return font_has_character(This, ch, &ret) == S_OK && ret;
+    return dwritefont_has_character(font, ch);
 }
 
 static DWRITE_LOCALITY WINAPI dwritefont3_GetLocality(IDWriteFont3 *iface)
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c
index e502be1e12b..8e507158579 100644
--- a/dlls/dwrite/opentype.c
+++ b/dlls/dwrite/opentype.c
@@ -1565,6 +1565,263 @@ static HRESULT opentype_get_font_table(const struct file_stream_desc *stream_des
  * CMAP
  **********/
 
+static UINT16 opentype_cmap_format0_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
+{
+    const UINT8 *glyphs = cmap->data;
+    return (ch < 0xff) ? glyphs[ch] : 0;
+}
+
+struct cmap_format4_compare_context
+{
+    const struct dwrite_cmap *cmap;
+    unsigned int ch;
+};
+
+static int cmap_format4_compare_range(const void *a, const void *b)
+{
+    const struct cmap_format4_compare_context *key = a;
+    const UINT16 *end = b;
+    unsigned int idx;
+
+    if (key->ch > GET_BE_WORD(*end))
+        return 1;
+
+    idx = end - key->cmap->u.format4.ends;
+    if (key->ch < GET_BE_WORD(key->cmap->u.format4.starts[idx]))
+        return -1;
+
+    return 0;
+}
+
+static UINT16 opentype_cmap_format4_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
+{
+    struct cmap_format4_compare_context key = { .cmap = cmap, .ch = ch };
+    unsigned int glyph, idx, range_offset;
+    const UINT16 *end_found;
+
+    /* Look up range. */
+    end_found = bsearch(&key, cmap->u.format4.ends, cmap->u.format4.seg_count, sizeof(*cmap->u.format4.ends),
+            cmap_format4_compare_range);
+    if (!end_found)
+        return 0;
+
+    idx = end_found - cmap->u.format4.ends;
+
+    range_offset = GET_BE_WORD(cmap->u.format4.id_range_offset[idx]);
+
+    if (!range_offset)
+    {
+        glyph = ch + GET_BE_WORD(cmap->u.format4.id_delta[idx]);
+    }
+    else
+    {
+        unsigned int index = range_offset / 2 + (ch - GET_BE_WORD(cmap->u.format4.starts[idx])) + idx - cmap->u.format4.seg_count;
+        if (index >= cmap->u.format4.glyph_id_array_len)
+            return 0;
+        glyph = GET_BE_WORD(cmap->u.format4.glyph_id_array[index]);
+        if (!glyph)
+            return 0;
+        glyph += GET_BE_WORD(cmap->u.format4.id_delta[idx]);
+    }
+
+    return glyph & 0xffff;
+}
+
+static UINT16 opentype_cmap_format6_10_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
+{
+    const UINT16 *glyphs = cmap->data;
+    if (ch < cmap->u.format6_10.first || ch > cmap->u.format6_10.last) return 0;
+    return glyphs[ch - cmap->u.format6_10.first];
+}
+
+static int cmap_format12_13_compare_group(const void *a, const void *b)
+{
+    const unsigned int *ch = a;
+    const UINT32 *group = b;
+
+    if (*ch > GET_BE_DWORD(group[1]))
+        return 1;
+
+    if (*ch < GET_BE_DWORD(group[0]))
+        return -1;
+
+    return 0;
+}
+
+static UINT16 opentype_cmap_format12_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
+{
+    const UINT32 *groups = cmap->data;
+    const UINT32 *group_found;
+
+    if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
+            cmap_format12_13_compare_group)))
+        return 0;
+
+    return GET_BE_DWORD(group_found[0]) <= GET_BE_DWORD(group_found[1]) ?
+            GET_BE_DWORD(group_found[2]) + (ch - GET_BE_DWORD(group_found[0])) : 0;
+}
+
+static UINT16 opentype_cmap_format13_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
+{
+    const UINT32 *groups = cmap->data;
+    const UINT32 *group_found;
+
+    if (!(group_found = bsearch(&ch, groups, cmap->u.format12_13.group_count, 3 * sizeof(*groups),
+            cmap_format12_13_compare_group)))
+        return 0;
+
+    return GET_BE_DWORD(group_found[2]);
+}
+
+static UINT16 opentype_cmap_dummy_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
+{
+    return 0;
+}
+
+UINT16 opentype_cmap_get_glyph(const struct dwrite_cmap *cmap, unsigned int ch)
+{
+    UINT16 glyph;
+
+    if (!cmap->get_glyph) return 0;
+    glyph = cmap->get_glyph(cmap, ch);
+    if (!glyph && cmap->symbol && ch <= 0xff)
+        glyph = cmap->get_glyph(cmap, ch + 0xf000);
+    return glyph;
+}
+
+static int cmap_header_compare(const void *a, const void *b)
+{
+    const UINT16 *key = a;
+    const UINT16 *record = b;
+
+    /* Platform. */
+    if (key[0] < GET_BE_WORD(record[0])) return -1;
+    if (key[0] > GET_BE_WORD(record[0])) return 1;
+    /* Encoding. */
+    if (key[1] < GET_BE_WORD(record[1])) return -1;
+    if (key[1] > GET_BE_WORD(record[1])) return 1;
+
+    return 0;
+}
+
+void dwrite_cmap_init(struct dwrite_cmap *cmap, IDWriteFontFile *file, unsigned int face_index,
+        DWRITE_FONT_FACE_TYPE face_type)
+{
+    static const UINT16 encodings[][2] =
+    {
+        { 3, 0 }, /* MS Symbol encoding is preferred. */
+        { 3, 10 },
+        { 0, 6 },
+        { 0, 4 },
+        { 3, 1 },
+        { 0, 3 },
+        { 0, 2 },
+        { 0, 1 },
+        { 0, 0 },
+    };
+    const struct cmap_encoding_record *records, *found_record = NULL;
+    unsigned int length, offset, format, count, f, i, num_records;
+    struct file_stream_desc stream_desc;
+    struct dwrite_fonttable table;
+    const UINT16 *pair = NULL;
+    HRESULT hr;
+
+    if (cmap->data) return;
+
+    if (FAILED(hr = get_filestream_from_file(file, &stream_desc.stream)))
+    {
+        WARN("Failed to get file stream, hr %#x.\n", hr);
+        goto failed;
+    }
+
+    stream_desc.face_type = face_type;
+    stream_desc.face_index = face_index;
+
+    opentype_get_font_table(&stream_desc, MS_CMAP_TAG, &table);
+    if (!table.exists)
+        goto failed;
+    cmap->table_context = table.context;
+
+    num_records = table_read_be_word(&table, 2);
+    records = table_read_ensure(&table, 4, sizeof(*records) * num_records);
+
+    for (i = 0; i < ARRAY_SIZE(encodings); ++i)
+    {
+        pair = encodings[i];
+        if ((found_record = bsearch(pair, records, num_records, sizeof(*records), cmap_header_compare)))
+            break;
+    }
+
+    if (!found_record)
+    {
+        WARN("No suitable cmap table were found.\n");
+        goto failed;
+    }
+
+    /* Symbol encoding. */
+    cmap->symbol = pair[0] == 3 && pair[1] == 0;
+    offset = GET_BE_DWORD(found_record->offset);
+
+    format = table_read_be_word(&table, offset);
+
+    switch (format)
+    {
+        case 0:
+            cmap->data = table_read_ensure(&table, offset + 6, 256);
+            cmap->get_glyph = opentype_cmap_format0_get_glyph;
+            break;
+        case 4:
+            length = table_read_be_word(&table, offset + 2);
+            cmap->u.format4.seg_count = count = table_read_be_word(&table, offset + 6) / 2;
+            cmap->u.format4.ends = table_read_ensure(&table, offset + 14, count * 2);
+            cmap->u.format4.starts = cmap->u.format4.ends + count + 1;
+            cmap->u.format4.id_delta = cmap->u.format4.starts + count;
+            cmap->u.format4.id_range_offset = cmap->u.format4.id_delta + count;
+            cmap->u.format4.glyph_id_array = cmap->data = cmap->u.format4.id_range_offset + count;
+            cmap->u.format4.glyph_id_array_len = (length - 16 - 8 * count) / 2;
+            cmap->get_glyph = opentype_cmap_format4_get_glyph;
+            break;
+        case 6:
+        case 10:
+            /* Format 10 uses 4 byte fields. */
+            f = format == 6 ? 1 : 2;
+            cmap->u.format6_10.first = table_read_be_word(&table, offset + f * 6);
+            count = table_read_be_word(&table, offset + f * 8);
+            cmap->u.format6_10.last = cmap->u.format6_10.first + count;
+            cmap->data = table_read_ensure(&table, offset + f * 10, count * 2);
+            cmap->get_glyph = opentype_cmap_format6_10_get_glyph;
+            break;
+        case 12:
+        case 13:
+            cmap->u.format12_13.group_count = count = table_read_be_dword(&table, offset + 12);
+            cmap->data = table_read_ensure(&table, offset + 16, count * 3 * 4);
+            cmap->get_glyph = format == 12 ? opentype_cmap_format12_get_glyph : opentype_cmap_format13_get_glyph;
+            break;
+        default:
+            WARN("Unhandled subtable format %u.\n", format);
+    }
+
+failed:
+
+    if (!cmap->data)
+    {
+        /* Dummy implementation, returns 0 unconditionally. */
+        cmap->data = cmap;
+        cmap->get_glyph = opentype_cmap_dummy_get_glyph;
+    }
+}
+
+void dwrite_cmap_release(struct dwrite_cmap *cmap)
+{
+    if (cmap->stream)
+    {
+        IDWriteFontFileStream_ReleaseFileFragment(cmap->stream, cmap->table_context);
+        IDWriteFontFileStream_Release(cmap->stream);
+    }
+    cmap->data = NULL;
+    cmap->stream = NULL;
+}
+
 static unsigned int opentype_cmap_get_unicode_ranges_count(const struct dwrite_fonttable *cmap)
 {
     unsigned int i, num_tables, count = 0;
-- 
2.26.2




More information about the wine-devel mailing list