[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