dwrite: Do not require fontface object for HasCharacter().

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2020-06-09 12:14:35 +03:00 committed by Alexandre Julliard
parent fb5eec2c1e
commit 745452ee94
3 changed files with 313 additions and 26 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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;