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:
parent
fb5eec2c1e
commit
745452ee94
|
@ -195,6 +195,47 @@ enum font_flags
|
||||||
FONTFACE_HAS_VERTICAL_VARIANTS = 1 << 4
|
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
|
struct dwrite_fontface
|
||||||
{
|
{
|
||||||
IDWriteFontFace5 IDWriteFontFace5_iface;
|
IDWriteFontFace5 IDWriteFontFace5_iface;
|
||||||
|
|
|
@ -80,6 +80,7 @@ struct dwrite_font_data
|
||||||
FONTSIGNATURE fontsig;
|
FONTSIGNATURE fontsig;
|
||||||
UINT32 flags; /* enum font_flags */
|
UINT32 flags; /* enum font_flags */
|
||||||
struct dwrite_font_propvec propvec;
|
struct dwrite_font_propvec propvec;
|
||||||
|
struct dwrite_cmap cmap;
|
||||||
/* Static axis for weight/width/italic. */
|
/* Static axis for weight/width/italic. */
|
||||||
DWRITE_FONT_AXIS_VALUE axis[3];
|
DWRITE_FONT_AXIS_VALUE axis[3];
|
||||||
|
|
||||||
|
@ -469,6 +470,7 @@ static void release_font_data(struct dwrite_font_data *data)
|
||||||
if (data->family_names)
|
if (data->family_names)
|
||||||
IDWriteLocalizedStrings_Release(data->family_names);
|
IDWriteLocalizedStrings_Release(data->family_names);
|
||||||
|
|
||||||
|
dwrite_cmap_release(&data->cmap);
|
||||||
IDWriteFontFile_Release(data->file);
|
IDWriteFontFile_Release(data->file);
|
||||||
heap_free(data->facename);
|
heap_free(data->facename);
|
||||||
heap_free(data);
|
heap_free(data);
|
||||||
|
@ -1958,35 +1960,23 @@ static void WINAPI dwritefont_GetMetrics(IDWriteFont3 *iface, DWRITE_FONT_METRIC
|
||||||
memcpy(metrics, &This->data->metrics, sizeof(*metrics));
|
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 glyph;
|
||||||
UINT16 index;
|
dwrite_cmap_init(&font->data->cmap, font->data->file, font->data->face_index, font->data->face_type);
|
||||||
HRESULT hr;
|
glyph = opentype_cmap_get_glyph(&font->data->cmap, ch);
|
||||||
|
return glyph != 0;
|
||||||
*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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI dwritefont_HasCharacter(IDWriteFont3 *iface, UINT32 ch, BOOL *exists)
|
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)
|
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)
|
static BOOL WINAPI dwritefont3_HasCharacter(IDWriteFont3 *iface, UINT32 ch)
|
||||||
{
|
{
|
||||||
struct dwrite_font *This = impl_from_IDWriteFont3(iface);
|
struct dwrite_font *font = impl_from_IDWriteFont3(iface);
|
||||||
BOOL ret;
|
|
||||||
|
|
||||||
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)
|
static DWRITE_LOCALITY WINAPI dwritefont3_GetLocality(IDWriteFont3 *iface)
|
||||||
|
|
|
@ -1565,6 +1565,263 @@ static HRESULT opentype_get_font_table(const struct file_stream_desc *stream_des
|
||||||
* CMAP
|
* 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)
|
static unsigned int opentype_cmap_get_unicode_ranges_count(const struct dwrite_fonttable *cmap)
|
||||||
{
|
{
|
||||||
unsigned int i, num_tables, count = 0;
|
unsigned int i, num_tables, count = 0;
|
||||||
|
|
Loading…
Reference in New Issue