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", This, ch, exists); + TRACE("%p, %#x, %p.\n", iface, ch, exists); - return font_has_character(This, ch, exists); + *exists = dwritefont_has_character(font, ch); + + 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;