| /* | |
| * Font and collections | |
| * | |
| * Copyright 2011 Huw Davies | |
| * Copyright 2012, 2014-2017 Nikolay Sivov for CodeWeavers | |
| * Copyright 2014 Aric Stewart for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include <assert.h> | |
| #include <math.h> | |
| #define COBJMACROS | |
| #include "dwrite_private.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(dwrite); | |
| WINE_DECLARE_DEBUG_CHANNEL(dwrite_file); | |
| #define MS_HEAD_TAG DWRITE_MAKE_OPENTYPE_TAG('h','e','a','d') | |
| #define MS_OS2_TAG DWRITE_MAKE_OPENTYPE_TAG('O','S','/','2') | |
| #define MS_CMAP_TAG DWRITE_MAKE_OPENTYPE_TAG('c','m','a','p') | |
| #define MS_NAME_TAG DWRITE_MAKE_OPENTYPE_TAG('n','a','m','e') | |
| #define MS_VDMX_TAG DWRITE_MAKE_OPENTYPE_TAG('V','D','M','X') | |
| #define MS_GASP_TAG DWRITE_MAKE_OPENTYPE_TAG('g','a','s','p') | |
| #define MS_CPAL_TAG DWRITE_MAKE_OPENTYPE_TAG('C','P','A','L') | |
| #define MS_COLR_TAG DWRITE_MAKE_OPENTYPE_TAG('C','O','L','R') | |
| static const IID IID_issystemcollection = {0x14d88047,0x331f,0x4cd3,{0xbc,0xa8,0x3e,0x67,0x99,0xaf,0x34,0x75}}; | |
| static const FLOAT RECOMMENDED_OUTLINE_AA_THRESHOLD = 100.0f; | |
| static const FLOAT RECOMMENDED_OUTLINE_A_THRESHOLD = 350.0f; | |
| static const FLOAT RECOMMENDED_NATURAL_PPEM = 20.0f; | |
| static const WCHAR extraW[] = {'e','x','t','r','a',0}; | |
| static const WCHAR ultraW[] = {'u','l','t','r','a',0}; | |
| static const WCHAR semiW[] = {'s','e','m','i',0}; | |
| static const WCHAR extW[] = {'e','x','t',0}; | |
| static const WCHAR thinW[] = {'t','h','i','n',0}; | |
| static const WCHAR lightW[] = {'l','i','g','h','t',0}; | |
| static const WCHAR mediumW[] = {'m','e','d','i','u','m',0}; | |
| static const WCHAR blackW[] = {'b','l','a','c','k',0}; | |
| static const WCHAR condensedW[] = {'c','o','n','d','e','n','s','e','d',0}; | |
| static const WCHAR expandedW[] = {'e','x','p','a','n','d','e','d',0}; | |
| static const WCHAR italicW[] = {'i','t','a','l','i','c',0}; | |
| static const WCHAR boldW[] = {'B','o','l','d',0}; | |
| static const WCHAR obliqueW[] = {'O','b','l','i','q','u','e',0}; | |
| static const WCHAR regularW[] = {'R','e','g','u','l','a','r',0}; | |
| static const WCHAR demiW[] = {'d','e','m','i',0}; | |
| static const WCHAR spaceW[] = {' ',0}; | |
| static const WCHAR enusW[] = {'e','n','-','u','s',0}; | |
| struct dwrite_font_propvec { | |
| FLOAT stretch; | |
| FLOAT style; | |
| FLOAT weight; | |
| }; | |
| struct dwrite_font_data { | |
| LONG ref; | |
| DWRITE_FONT_STYLE style; | |
| DWRITE_FONT_STRETCH stretch; | |
| DWRITE_FONT_WEIGHT weight; | |
| DWRITE_PANOSE panose; | |
| FONTSIGNATURE fontsig; | |
| struct dwrite_font_propvec propvec; | |
| DWRITE_FONT_METRICS1 metrics; | |
| IDWriteLocalizedStrings *info_strings[DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME+1]; | |
| IDWriteLocalizedStrings *names; | |
| /* data needed to create fontface instance */ | |
| DWRITE_FONT_FACE_TYPE face_type; | |
| IDWriteFontFile *file; | |
| UINT32 face_index; | |
| WCHAR *facename; | |
| USHORT simulations; | |
| LOGFONTW lf; | |
| /* used to mark font as tested when scanning for simulation candidate */ | |
| BOOL bold_sim_tested : 1; | |
| BOOL oblique_sim_tested : 1; | |
| }; | |
| struct dwrite_fontfamily_data { | |
| LONG ref; | |
| IDWriteLocalizedStrings *familyname; | |
| struct dwrite_font_data **fonts; | |
| UINT32 font_count; | |
| UINT32 font_alloc; | |
| BOOL has_normal_face : 1; | |
| BOOL has_oblique_face : 1; | |
| BOOL has_italic_face : 1; | |
| }; | |
| struct dwrite_fontcollection { | |
| IDWriteFontCollection1 IDWriteFontCollection1_iface; | |
| LONG ref; | |
| IDWriteFactory5 *factory; | |
| struct dwrite_fontfamily_data **family_data; | |
| UINT32 family_count; | |
| UINT32 family_alloc; | |
| }; | |
| struct dwrite_fontfamily { | |
| IDWriteFontFamily1 IDWriteFontFamily1_iface; | |
| LONG ref; | |
| struct dwrite_fontfamily_data *data; | |
| struct dwrite_fontcollection *collection; | |
| }; | |
| struct dwrite_fontlist { | |
| IDWriteFontList1 IDWriteFontList1_iface; | |
| LONG ref; | |
| struct dwrite_font_data **fonts; | |
| UINT32 font_count; | |
| struct dwrite_fontfamily *family; | |
| }; | |
| struct dwrite_font { | |
| IDWriteFont3 IDWriteFont3_iface; | |
| LONG ref; | |
| DWRITE_FONT_STYLE style; | |
| struct dwrite_font_data *data; | |
| struct dwrite_fontfamily *family; | |
| }; | |
| struct dwrite_fonttable { | |
| void *data; | |
| void *context; | |
| UINT32 size; | |
| BOOL exists; | |
| }; | |
| enum runanalysis_flags { | |
| RUNANALYSIS_BOUNDS_READY = 1 << 0, | |
| RUNANALYSIS_BITMAP_READY = 1 << 1, | |
| RUNANALYSIS_USE_TRANSFORM = 1 << 2 | |
| }; | |
| struct dwrite_glyphrunanalysis { | |
| IDWriteGlyphRunAnalysis IDWriteGlyphRunAnalysis_iface; | |
| LONG ref; | |
| DWRITE_RENDERING_MODE1 rendering_mode; | |
| DWRITE_TEXTURE_TYPE texture_type; /* derived from rendering mode specified on creation */ | |
| DWRITE_GLYPH_RUN run; /* glyphAdvances and glyphOffsets are not used */ | |
| DWRITE_MATRIX m; | |
| UINT16 *glyphs; | |
| D2D_POINT_2F *origins; | |
| UINT8 flags; | |
| RECT bounds; | |
| BYTE *bitmap; | |
| UINT32 max_glyph_bitmap_size; | |
| }; | |
| struct dwrite_colorglyphenum { | |
| IDWriteColorGlyphRunEnumerator IDWriteColorGlyphRunEnumerator_iface; | |
| LONG ref; | |
| FLOAT origin_x; /* original run origin */ | |
| FLOAT origin_y; | |
| IDWriteFontFace4 *fontface; /* for convenience */ | |
| DWRITE_COLOR_GLYPH_RUN colorrun; /* returned with GetCurrentRun() */ | |
| DWRITE_GLYPH_RUN run; /* base run */ | |
| UINT32 palette; /* palette index to get layer color from */ | |
| FLOAT *advances; /* original or measured advances for base glyphs */ | |
| FLOAT *color_advances; /* returned color run points to this */ | |
| DWRITE_GLYPH_OFFSET *offsets; /* original offsets, or NULL */ | |
| DWRITE_GLYPH_OFFSET *color_offsets; /* returned color run offsets, or NULL */ | |
| UINT16 *glyphindices; /* returned color run points to this */ | |
| struct dwrite_colorglyph *glyphs; /* current glyph color info */ | |
| BOOL has_regular_glyphs; /* TRUE if there's any glyph without a color */ | |
| UINT16 current_layer; /* enumerator position, updated with MoveNext */ | |
| UINT16 max_layer_num; /* max number of layers for this run */ | |
| struct dwrite_fonttable colr; /* used to access layers */ | |
| }; | |
| #define GLYPH_BLOCK_SHIFT 8 | |
| #define GLYPH_BLOCK_SIZE (1UL << GLYPH_BLOCK_SHIFT) | |
| #define GLYPH_BLOCK_MASK (GLYPH_BLOCK_SIZE - 1) | |
| #define GLYPH_MAX 65536 | |
| enum fontface_flags { | |
| FONTFACE_IS_SYMBOL = 1 << 0, | |
| FONTFACE_IS_MONOSPACED = 1 << 1, | |
| FONTFACE_HAS_KERNING_PAIRS = 1 << 2, | |
| FONTFACE_HAS_VERTICAL_VARIANTS = 1 << 3 | |
| }; | |
| struct dwrite_fontface { | |
| IDWriteFontFace4 IDWriteFontFace4_iface; | |
| LONG ref; | |
| IDWriteFontFileStream **streams; | |
| IDWriteFontFile **files; | |
| UINT32 file_count; | |
| UINT32 index; | |
| IDWriteFactory5 *factory; | |
| struct fontfacecached *cached; | |
| USHORT simulations; | |
| DWRITE_FONT_FACE_TYPE type; | |
| DWRITE_FONT_METRICS1 metrics; | |
| DWRITE_CARET_METRICS caret; | |
| INT charmap; | |
| UINT16 flags; | |
| struct dwrite_fonttable cmap; | |
| struct dwrite_fonttable vdmx; | |
| struct dwrite_fonttable gasp; | |
| struct dwrite_fonttable cpal; | |
| struct dwrite_fonttable colr; | |
| DWRITE_GLYPH_METRICS *glyphs[GLYPH_MAX/GLYPH_BLOCK_SIZE]; | |
| DWRITE_FONT_STYLE style; | |
| DWRITE_FONT_STRETCH stretch; | |
| DWRITE_FONT_WEIGHT weight; | |
| DWRITE_PANOSE panose; | |
| FONTSIGNATURE fontsig; | |
| UINT32 glyph_image_formats; | |
| LOGFONTW lf; | |
| }; | |
| struct dwrite_fontfile { | |
| IDWriteFontFile IDWriteFontFile_iface; | |
| LONG ref; | |
| IDWriteFontFileLoader *loader; | |
| void *reference_key; | |
| UINT32 key_size; | |
| IDWriteFontFileStream *stream; | |
| }; | |
| struct dwrite_fontfacereference { | |
| IDWriteFontFaceReference IDWriteFontFaceReference_iface; | |
| LONG ref; | |
| IDWriteFontFile *file; | |
| UINT32 index; | |
| USHORT simulations; | |
| IDWriteFactory5 *factory; | |
| }; | |
| static inline struct dwrite_fontface *impl_from_IDWriteFontFace4(IDWriteFontFace4 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_fontface, IDWriteFontFace4_iface); | |
| } | |
| static inline struct dwrite_font *impl_from_IDWriteFont3(IDWriteFont3 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_font, IDWriteFont3_iface); | |
| } | |
| static inline struct dwrite_fontfile *impl_from_IDWriteFontFile(IDWriteFontFile *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_fontfile, IDWriteFontFile_iface); | |
| } | |
| static inline struct dwrite_fontfamily *impl_from_IDWriteFontFamily1(IDWriteFontFamily1 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_fontfamily, IDWriteFontFamily1_iface); | |
| } | |
| static inline struct dwrite_fontcollection *impl_from_IDWriteFontCollection1(IDWriteFontCollection1 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_fontcollection, IDWriteFontCollection1_iface); | |
| } | |
| static inline struct dwrite_glyphrunanalysis *impl_from_IDWriteGlyphRunAnalysis(IDWriteGlyphRunAnalysis *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_glyphrunanalysis, IDWriteGlyphRunAnalysis_iface); | |
| } | |
| static inline struct dwrite_colorglyphenum *impl_from_IDWriteColorGlyphRunEnumerator(IDWriteColorGlyphRunEnumerator *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_colorglyphenum, IDWriteColorGlyphRunEnumerator_iface); | |
| } | |
| static inline struct dwrite_fontlist *impl_from_IDWriteFontList1(IDWriteFontList1 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_fontlist, IDWriteFontList1_iface); | |
| } | |
| static inline struct dwrite_fontfacereference *impl_from_IDWriteFontFaceReference(IDWriteFontFaceReference *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_fontfacereference, IDWriteFontFaceReference_iface); | |
| } | |
| static inline const char *debugstr_tag(UINT32 tag) | |
| { | |
| return debugstr_an((char*)&tag, 4); | |
| } | |
| static HRESULT get_cached_glyph_metrics(struct dwrite_fontface *fontface, UINT16 glyph, DWRITE_GLYPH_METRICS *metrics) | |
| { | |
| static const DWRITE_GLYPH_METRICS nil; | |
| DWRITE_GLYPH_METRICS *block = fontface->glyphs[glyph >> GLYPH_BLOCK_SHIFT]; | |
| if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(DWRITE_GLYPH_METRICS))) return S_FALSE; | |
| memcpy(metrics, &block[glyph & GLYPH_BLOCK_MASK], sizeof(*metrics)); | |
| return S_OK; | |
| } | |
| static HRESULT set_cached_glyph_metrics(struct dwrite_fontface *fontface, UINT16 glyph, DWRITE_GLYPH_METRICS *metrics) | |
| { | |
| DWRITE_GLYPH_METRICS **block = &fontface->glyphs[glyph >> GLYPH_BLOCK_SHIFT]; | |
| if (!*block) { | |
| /* start new block */ | |
| *block = heap_alloc_zero(sizeof(*metrics) * GLYPH_BLOCK_SIZE); | |
| if (!*block) | |
| return E_OUTOFMEMORY; | |
| } | |
| memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], metrics, sizeof(*metrics)); | |
| return S_OK; | |
| } | |
| static void* get_fontface_table(IDWriteFontFace4 *fontface, UINT32 tag, struct dwrite_fonttable *table) | |
| { | |
| HRESULT hr; | |
| if (table->data || !table->exists) | |
| return table->data; | |
| table->exists = FALSE; | |
| hr = IDWriteFontFace4_TryGetFontTable(fontface, tag, (const void**)&table->data, &table->size, &table->context, | |
| &table->exists); | |
| if (FAILED(hr) || !table->exists) { | |
| TRACE("Font does not have %s table\n", debugstr_tag(tag)); | |
| return NULL; | |
| } | |
| return table->data; | |
| } | |
| static void init_font_prop_vec(DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STRETCH stretch, DWRITE_FONT_STYLE style, | |
| struct dwrite_font_propvec *vec) | |
| { | |
| vec->stretch = ((INT32)stretch - DWRITE_FONT_STRETCH_NORMAL) * 11.0f; | |
| vec->style = style * 7.0f; | |
| vec->weight = ((INT32)weight - DWRITE_FONT_WEIGHT_NORMAL) / 100.0f * 5.0f; | |
| } | |
| static FLOAT get_font_prop_vec_distance(const struct dwrite_font_propvec *left, const struct dwrite_font_propvec *right) | |
| { | |
| return powf(left->stretch - right->stretch, 2) + powf(left->style - right->style, 2) + powf(left->weight - right->weight, 2); | |
| } | |
| static FLOAT get_font_prop_vec_dotproduct(const struct dwrite_font_propvec *left, const struct dwrite_font_propvec *right) | |
| { | |
| return left->stretch * right->stretch + left->style * right->style + left->weight * right->weight; | |
| } | |
| static inline void* get_fontface_cmap(struct dwrite_fontface *fontface) | |
| { | |
| return get_fontface_table(&fontface->IDWriteFontFace4_iface, MS_CMAP_TAG, &fontface->cmap); | |
| } | |
| static inline void* get_fontface_vdmx(struct dwrite_fontface *fontface) | |
| { | |
| return get_fontface_table(&fontface->IDWriteFontFace4_iface, MS_VDMX_TAG, &fontface->vdmx); | |
| } | |
| static inline void* get_fontface_gasp(struct dwrite_fontface *fontface, UINT32 *size) | |
| { | |
| void *ptr = get_fontface_table(&fontface->IDWriteFontFace4_iface, MS_GASP_TAG, &fontface->gasp); | |
| *size = fontface->gasp.size; | |
| return ptr; | |
| } | |
| static inline void* get_fontface_cpal(struct dwrite_fontface *fontface) | |
| { | |
| return get_fontface_table(&fontface->IDWriteFontFace4_iface, MS_CPAL_TAG, &fontface->cpal); | |
| } | |
| static inline void* get_fontface_colr(struct dwrite_fontface *fontface) | |
| { | |
| return get_fontface_table(&fontface->IDWriteFontFace4_iface, MS_COLR_TAG, &fontface->colr); | |
| } | |
| static void addref_font_data(struct dwrite_font_data *data) | |
| { | |
| InterlockedIncrement(&data->ref); | |
| } | |
| static void release_font_data(struct dwrite_font_data *data) | |
| { | |
| int i; | |
| if (InterlockedDecrement(&data->ref) > 0) | |
| return; | |
| for (i = DWRITE_INFORMATIONAL_STRING_NONE; i < sizeof(data->info_strings)/sizeof(data->info_strings[0]); i++) { | |
| if (data->info_strings[i]) | |
| IDWriteLocalizedStrings_Release(data->info_strings[i]); | |
| } | |
| if (data->names) | |
| IDWriteLocalizedStrings_Release(data->names); | |
| IDWriteFontFile_Release(data->file); | |
| heap_free(data->facename); | |
| heap_free(data); | |
| } | |
| static void release_fontfamily_data(struct dwrite_fontfamily_data *data) | |
| { | |
| int i; | |
| if (InterlockedDecrement(&data->ref) > 0) | |
| return; | |
| for (i = 0; i < data->font_count; i++) | |
| release_font_data(data->fonts[i]); | |
| heap_free(data->fonts); | |
| IDWriteLocalizedStrings_Release(data->familyname); | |
| heap_free(data); | |
| } | |
| void fontface_detach_from_cache(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *fontface = impl_from_IDWriteFontFace4(iface); | |
| fontface->cached = NULL; | |
| } | |
| static HRESULT WINAPI dwritefontface_QueryInterface(IDWriteFontFace4 *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFontFace4) || | |
| IsEqualIID(riid, &IID_IDWriteFontFace3) || | |
| IsEqualIID(riid, &IID_IDWriteFontFace2) || | |
| IsEqualIID(riid, &IID_IDWriteFontFace1) || | |
| IsEqualIID(riid, &IID_IDWriteFontFace) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| if (InterlockedIncrement(&This->ref) == 1) { | |
| InterlockedDecrement(&This->ref); | |
| *obj = NULL; | |
| return E_FAIL; | |
| } | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI dwritefontface_AddRef(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI dwritefontface_Release(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| if (!ref) { | |
| UINT32 i; | |
| if (This->cached) { | |
| factory_lock(This->factory); | |
| list_remove(&This->cached->entry); | |
| factory_unlock(This->factory); | |
| heap_free(This->cached); | |
| } | |
| if (This->cmap.context) | |
| IDWriteFontFace4_ReleaseFontTable(iface, This->cmap.context); | |
| if (This->vdmx.context) | |
| IDWriteFontFace4_ReleaseFontTable(iface, This->vdmx.context); | |
| if (This->gasp.context) | |
| IDWriteFontFace4_ReleaseFontTable(iface, This->gasp.context); | |
| if (This->cpal.context) | |
| IDWriteFontFace4_ReleaseFontTable(iface, This->cpal.context); | |
| if (This->colr.context) | |
| IDWriteFontFace4_ReleaseFontTable(iface, This->colr.context); | |
| for (i = 0; i < This->file_count; i++) { | |
| if (This->streams[i]) | |
| IDWriteFontFileStream_Release(This->streams[i]); | |
| if (This->files[i]) | |
| IDWriteFontFile_Release(This->files[i]); | |
| } | |
| heap_free(This->streams); | |
| heap_free(This->files); | |
| for (i = 0; i < sizeof(This->glyphs)/sizeof(This->glyphs[0]); i++) | |
| heap_free(This->glyphs[i]); | |
| freetype_notify_cacheremove(iface); | |
| IDWriteFactory5_Release(This->factory); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static DWRITE_FONT_FACE_TYPE WINAPI dwritefontface_GetType(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return This->type; | |
| } | |
| static HRESULT WINAPI dwritefontface_GetFiles(IDWriteFontFace4 *iface, UINT32 *number_of_files, | |
| IDWriteFontFile **fontfiles) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| int i; | |
| TRACE("(%p)->(%p %p)\n", This, number_of_files, fontfiles); | |
| if (fontfiles == NULL) | |
| { | |
| *number_of_files = This->file_count; | |
| return S_OK; | |
| } | |
| if (*number_of_files < This->file_count) | |
| return E_INVALIDARG; | |
| for (i = 0; i < This->file_count; i++) | |
| { | |
| IDWriteFontFile_AddRef(This->files[i]); | |
| fontfiles[i] = This->files[i]; | |
| } | |
| return S_OK; | |
| } | |
| static UINT32 WINAPI dwritefontface_GetIndex(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return This->index; | |
| } | |
| static DWRITE_FONT_SIMULATIONS WINAPI dwritefontface_GetSimulations(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return This->simulations; | |
| } | |
| static BOOL WINAPI dwritefontface_IsSymbolFont(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return !!(This->flags & FONTFACE_IS_SYMBOL); | |
| } | |
| static void WINAPI dwritefontface_GetMetrics(IDWriteFontFace4 *iface, DWRITE_FONT_METRICS *metrics) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%p)\n", This, metrics); | |
| memcpy(metrics, &This->metrics, sizeof(*metrics)); | |
| } | |
| static UINT16 WINAPI dwritefontface_GetGlyphCount(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return freetype_get_glyphcount(iface); | |
| } | |
| static HRESULT WINAPI dwritefontface_GetDesignGlyphMetrics(IDWriteFontFace4 *iface, | |
| UINT16 const *glyphs, UINT32 glyph_count, DWRITE_GLYPH_METRICS *ret, BOOL is_sideways) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| HRESULT hr; | |
| UINT32 i; | |
| TRACE("(%p)->(%p %u %p %d)\n", This, glyphs, glyph_count, ret, is_sideways); | |
| if (!glyphs) | |
| return E_INVALIDARG; | |
| if (is_sideways) | |
| FIXME("sideways metrics are not supported.\n"); | |
| for (i = 0; i < glyph_count; i++) { | |
| DWRITE_GLYPH_METRICS metrics; | |
| hr = get_cached_glyph_metrics(This, glyphs[i], &metrics); | |
| if (hr != S_OK) { | |
| freetype_get_design_glyph_metrics(iface, This->metrics.designUnitsPerEm, glyphs[i], &metrics); | |
| hr = set_cached_glyph_metrics(This, glyphs[i], &metrics); | |
| if (FAILED(hr)) | |
| return hr; | |
| } | |
| ret[i] = metrics; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT fontface_get_glyphs(struct dwrite_fontface *fontface, UINT32 const *codepoints, | |
| UINT32 count, UINT16 *glyphs) | |
| { | |
| if (!glyphs) | |
| return E_INVALIDARG; | |
| if (!codepoints) { | |
| memset(glyphs, 0, count * sizeof(*glyphs)); | |
| return E_INVALIDARG; | |
| } | |
| freetype_get_glyphs(&fontface->IDWriteFontFace4_iface, fontface->charmap, codepoints, count, glyphs); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefontface_GetGlyphIndices(IDWriteFontFace4 *iface, UINT32 const *codepoints, | |
| UINT32 count, UINT16 *glyphs) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%p %u %p)\n", This, codepoints, count, glyphs); | |
| return fontface_get_glyphs(This, codepoints, count, glyphs); | |
| } | |
| static HRESULT WINAPI dwritefontface_TryGetFontTable(IDWriteFontFace4 *iface, UINT32 table_tag, | |
| const void **table_data, UINT32 *table_size, void **context, BOOL *exists) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| struct file_stream_desc stream_desc; | |
| TRACE("(%p)->(%s %p %p %p %p)\n", This, debugstr_tag(table_tag), table_data, table_size, context, exists); | |
| stream_desc.stream = This->streams[0]; | |
| stream_desc.face_type = This->type; | |
| stream_desc.face_index = This->index; | |
| return opentype_get_font_table(&stream_desc, table_tag, table_data, context, table_size, exists); | |
| } | |
| static void WINAPI dwritefontface_ReleaseFontTable(IDWriteFontFace4 *iface, void *table_context) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%p)\n", This, table_context); | |
| IDWriteFontFileStream_ReleaseFileFragment(This->streams[0], table_context); | |
| } | |
| static HRESULT WINAPI dwritefontface_GetGlyphRunOutline(IDWriteFontFace4 *iface, FLOAT emSize, | |
| UINT16 const *glyphs, FLOAT const* advances, DWRITE_GLYPH_OFFSET const *offsets, | |
| UINT32 count, BOOL is_sideways, BOOL is_rtl, IDWriteGeometrySink *sink) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%.2f %p %p %p %u %d %d %p)\n", This, emSize, glyphs, advances, offsets, | |
| count, is_sideways, is_rtl, sink); | |
| if (!glyphs || !sink) | |
| return E_INVALIDARG; | |
| if (is_sideways) | |
| FIXME("sideways mode is not supported.\n"); | |
| return freetype_get_glyphrun_outline(iface, emSize, glyphs, advances, offsets, count, is_rtl, sink); | |
| } | |
| static DWRITE_RENDERING_MODE fontface_renderingmode_from_measuringmode(DWRITE_MEASURING_MODE measuring, | |
| FLOAT ppem, WORD gasp) | |
| { | |
| DWRITE_RENDERING_MODE mode = DWRITE_RENDERING_MODE_DEFAULT; | |
| switch (measuring) | |
| { | |
| case DWRITE_MEASURING_MODE_NATURAL: | |
| { | |
| if (!(gasp & GASP_SYMMETRIC_SMOOTHING) && (ppem <= RECOMMENDED_NATURAL_PPEM)) | |
| mode = DWRITE_RENDERING_MODE_NATURAL; | |
| else | |
| mode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; | |
| break; | |
| } | |
| case DWRITE_MEASURING_MODE_GDI_CLASSIC: | |
| mode = DWRITE_RENDERING_MODE_GDI_CLASSIC; | |
| break; | |
| case DWRITE_MEASURING_MODE_GDI_NATURAL: | |
| mode = DWRITE_RENDERING_MODE_GDI_NATURAL; | |
| break; | |
| default: | |
| ; | |
| } | |
| return mode; | |
| } | |
| static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace4 *iface, FLOAT emSize, | |
| FLOAT ppdip, DWRITE_MEASURING_MODE measuring, IDWriteRenderingParams *params, DWRITE_RENDERING_MODE *mode) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| WORD gasp, *ptr; | |
| UINT32 size; | |
| FLOAT ppem; | |
| TRACE("(%p)->(%.2f %.2f %d %p %p)\n", This, emSize, ppdip, measuring, params, mode); | |
| if (!params) { | |
| *mode = DWRITE_RENDERING_MODE_DEFAULT; | |
| return E_INVALIDARG; | |
| } | |
| *mode = IDWriteRenderingParams_GetRenderingMode(params); | |
| if (*mode != DWRITE_RENDERING_MODE_DEFAULT) | |
| return S_OK; | |
| ppem = emSize * ppdip; | |
| if (ppem >= RECOMMENDED_OUTLINE_AA_THRESHOLD) { | |
| *mode = DWRITE_RENDERING_MODE_OUTLINE; | |
| return S_OK; | |
| } | |
| ptr = get_fontface_gasp(This, &size); | |
| gasp = opentype_get_gasp_flags(ptr, size, ppem); | |
| *mode = fontface_renderingmode_from_measuringmode(measuring, ppem, gasp); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefontface_GetGdiCompatibleMetrics(IDWriteFontFace4 *iface, FLOAT emSize, FLOAT pixels_per_dip, | |
| DWRITE_MATRIX const *transform, DWRITE_FONT_METRICS *metrics) | |
| { | |
| DWRITE_FONT_METRICS1 metrics1; | |
| HRESULT hr = IDWriteFontFace4_GetGdiCompatibleMetrics(iface, emSize, pixels_per_dip, transform, &metrics1); | |
| memcpy(metrics, &metrics1, sizeof(*metrics)); | |
| return hr; | |
| } | |
| static inline int round_metric(FLOAT metric) | |
| { | |
| return (int)floorf(metric + 0.5f); | |
| } | |
| static UINT32 fontface_get_horz_metric_adjustment(const struct dwrite_fontface *fontface) | |
| { | |
| if (!(fontface->simulations & DWRITE_FONT_SIMULATIONS_BOLD)) | |
| return 0; | |
| return (fontface->metrics.designUnitsPerEm + 49) / 50; | |
| } | |
| static HRESULT WINAPI dwritefontface_GetGdiCompatibleGlyphMetrics(IDWriteFontFace4 *iface, FLOAT emSize, FLOAT ppdip, | |
| DWRITE_MATRIX const *m, BOOL use_gdi_natural, UINT16 const *glyphs, UINT32 glyph_count, | |
| DWRITE_GLYPH_METRICS *metrics, BOOL is_sideways) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| UINT32 adjustment = fontface_get_horz_metric_adjustment(This); | |
| DWRITE_MEASURING_MODE mode; | |
| FLOAT scale, size; | |
| HRESULT hr; | |
| UINT32 i; | |
| TRACE("(%p)->(%.2f %.2f %p %d %p %u %p %d)\n", This, emSize, ppdip, m, use_gdi_natural, glyphs, | |
| glyph_count, metrics, is_sideways); | |
| if (m && memcmp(m, &identity, sizeof(*m))) | |
| FIXME("transform is not supported, %s\n", debugstr_matrix(m)); | |
| size = emSize * ppdip; | |
| scale = size / This->metrics.designUnitsPerEm; | |
| mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC; | |
| for (i = 0; i < glyph_count; i++) { | |
| DWRITE_GLYPH_METRICS *ret = metrics + i; | |
| DWRITE_GLYPH_METRICS design; | |
| BOOL has_contours; | |
| hr = IDWriteFontFace4_GetDesignGlyphMetrics(iface, glyphs + i, 1, &design, is_sideways); | |
| if (FAILED(hr)) | |
| return hr; | |
| ret->advanceWidth = freetype_get_glyph_advance(iface, size, glyphs[i], mode, &has_contours); | |
| if (has_contours) | |
| ret->advanceWidth = round_metric(ret->advanceWidth * This->metrics.designUnitsPerEm / size + adjustment); | |
| else | |
| ret->advanceWidth = round_metric(ret->advanceWidth * This->metrics.designUnitsPerEm / size); | |
| #define SCALE_METRIC(x) ret->x = round_metric(round_metric((design.x) * scale) / scale) | |
| SCALE_METRIC(leftSideBearing); | |
| SCALE_METRIC(rightSideBearing); | |
| SCALE_METRIC(topSideBearing); | |
| SCALE_METRIC(advanceHeight); | |
| SCALE_METRIC(bottomSideBearing); | |
| SCALE_METRIC(verticalOriginY); | |
| #undef SCALE_METRIC | |
| } | |
| return S_OK; | |
| } | |
| static void WINAPI dwritefontface1_GetMetrics(IDWriteFontFace4 *iface, DWRITE_FONT_METRICS1 *metrics) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%p)\n", This, metrics); | |
| *metrics = This->metrics; | |
| } | |
| static HRESULT WINAPI dwritefontface1_GetGdiCompatibleMetrics(IDWriteFontFace4 *iface, FLOAT em_size, FLOAT pixels_per_dip, | |
| const DWRITE_MATRIX *m, DWRITE_FONT_METRICS1 *metrics) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| const DWRITE_FONT_METRICS1 *design = &This->metrics; | |
| UINT16 ascent, descent; | |
| FLOAT scale; | |
| TRACE("(%p)->(%.2f %.2f %p %p)\n", This, em_size, pixels_per_dip, m, metrics); | |
| if (em_size <= 0.0f || pixels_per_dip <= 0.0f) { | |
| memset(metrics, 0, sizeof(*metrics)); | |
| return E_INVALIDARG; | |
| } | |
| em_size *= pixels_per_dip; | |
| if (m && m->m22 != 0.0f) | |
| em_size *= fabs(m->m22); | |
| scale = em_size / design->designUnitsPerEm; | |
| if (!opentype_get_vdmx_size(get_fontface_vdmx(This), em_size, &ascent, &descent)) { | |
| ascent = round_metric(design->ascent * scale); | |
| descent = round_metric(design->descent * scale); | |
| } | |
| #define SCALE_METRIC(x) metrics->x = round_metric(round_metric((design->x) * scale) / scale) | |
| metrics->designUnitsPerEm = design->designUnitsPerEm; | |
| metrics->ascent = round_metric(ascent / scale); | |
| metrics->descent = round_metric(descent / scale); | |
| SCALE_METRIC(lineGap); | |
| SCALE_METRIC(capHeight); | |
| SCALE_METRIC(xHeight); | |
| SCALE_METRIC(underlinePosition); | |
| SCALE_METRIC(underlineThickness); | |
| SCALE_METRIC(strikethroughPosition); | |
| SCALE_METRIC(strikethroughThickness); | |
| SCALE_METRIC(glyphBoxLeft); | |
| SCALE_METRIC(glyphBoxTop); | |
| SCALE_METRIC(glyphBoxRight); | |
| SCALE_METRIC(glyphBoxBottom); | |
| SCALE_METRIC(subscriptPositionX); | |
| SCALE_METRIC(subscriptPositionY); | |
| SCALE_METRIC(subscriptSizeX); | |
| SCALE_METRIC(subscriptSizeY); | |
| SCALE_METRIC(superscriptPositionX); | |
| SCALE_METRIC(superscriptPositionY); | |
| SCALE_METRIC(superscriptSizeX); | |
| SCALE_METRIC(superscriptSizeY); | |
| metrics->hasTypographicMetrics = design->hasTypographicMetrics; | |
| #undef SCALE_METRIC | |
| return S_OK; | |
| } | |
| static void WINAPI dwritefontface1_GetCaretMetrics(IDWriteFontFace4 *iface, DWRITE_CARET_METRICS *metrics) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%p)\n", This, metrics); | |
| *metrics = This->caret; | |
| } | |
| static HRESULT WINAPI dwritefontface1_GetUnicodeRanges(IDWriteFontFace4 *iface, UINT32 max_count, | |
| DWRITE_UNICODE_RANGE *ranges, UINT32 *count) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%u %p %p)\n", This, max_count, ranges, count); | |
| *count = 0; | |
| if (max_count && !ranges) | |
| return E_INVALIDARG; | |
| return opentype_cmap_get_unicode_ranges(get_fontface_cmap(This), max_count, ranges, count); | |
| } | |
| static BOOL WINAPI dwritefontface1_IsMonospacedFont(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return !!(This->flags & FONTFACE_IS_MONOSPACED); | |
| } | |
| static HRESULT WINAPI dwritefontface1_GetDesignGlyphAdvances(IDWriteFontFace4 *iface, | |
| UINT32 glyph_count, UINT16 const *glyphs, INT32 *advances, BOOL is_sideways) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| UINT32 adjustment = fontface_get_horz_metric_adjustment(This); | |
| UINT32 i; | |
| TRACE("(%p)->(%u %p %p %d)\n", This, glyph_count, glyphs, advances, is_sideways); | |
| if (is_sideways) | |
| FIXME("sideways mode not supported\n"); | |
| for (i = 0; i < glyph_count; i++) { | |
| BOOL has_contours; | |
| advances[i] = freetype_get_glyph_advance(iface, This->metrics.designUnitsPerEm, glyphs[i], | |
| DWRITE_MEASURING_MODE_NATURAL, &has_contours); | |
| if (has_contours) | |
| advances[i] += adjustment; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefontface1_GetGdiCompatibleGlyphAdvances(IDWriteFontFace4 *iface, | |
| FLOAT em_size, FLOAT ppdip, const DWRITE_MATRIX *m, BOOL use_gdi_natural, | |
| BOOL is_sideways, UINT32 glyph_count, UINT16 const *glyphs, INT32 *advances) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| UINT32 adjustment = fontface_get_horz_metric_adjustment(This); | |
| DWRITE_MEASURING_MODE mode; | |
| UINT32 i; | |
| TRACE("(%p)->(%.2f %.2f %p %d %d %u %p %p)\n", This, em_size, ppdip, m, | |
| use_gdi_natural, is_sideways, glyph_count, glyphs, advances); | |
| if (em_size < 0.0f || ppdip <= 0.0f) { | |
| memset(advances, 0, sizeof(*advances) * glyph_count); | |
| return E_INVALIDARG; | |
| } | |
| em_size *= ppdip; | |
| if (em_size == 0.0f) { | |
| memset(advances, 0, sizeof(*advances) * glyph_count); | |
| return S_OK; | |
| } | |
| if (m && memcmp(m, &identity, sizeof(*m))) | |
| FIXME("transform is not supported, %s\n", debugstr_matrix(m)); | |
| mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC; | |
| for (i = 0; i < glyph_count; i++) { | |
| BOOL has_contours; | |
| advances[i] = freetype_get_glyph_advance(iface, em_size, glyphs[i], mode, &has_contours); | |
| if (has_contours) | |
| advances[i] = round_metric(advances[i] * This->metrics.designUnitsPerEm / em_size + adjustment); | |
| else | |
| advances[i] = round_metric(advances[i] * This->metrics.designUnitsPerEm / em_size); | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefontface1_GetKerningPairAdjustments(IDWriteFontFace4 *iface, UINT32 count, | |
| const UINT16 *indices, INT32 *adjustments) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| UINT32 i; | |
| TRACE("(%p)->(%u %p %p)\n", This, count, indices, adjustments); | |
| if (!(indices || adjustments) || !count) | |
| return E_INVALIDARG; | |
| if (!indices || count == 1) { | |
| memset(adjustments, 0, count*sizeof(INT32)); | |
| return E_INVALIDARG; | |
| } | |
| if (!(This->flags & FONTFACE_HAS_KERNING_PAIRS)) { | |
| memset(adjustments, 0, count*sizeof(INT32)); | |
| return S_OK; | |
| } | |
| for (i = 0; i < count-1; i++) | |
| adjustments[i] = freetype_get_kerning_pair_adjustment(iface, indices[i], indices[i+1]); | |
| adjustments[count-1] = 0; | |
| return S_OK; | |
| } | |
| static BOOL WINAPI dwritefontface1_HasKerningPairs(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return !!(This->flags & FONTFACE_HAS_KERNING_PAIRS); | |
| } | |
| static HRESULT WINAPI dwritefontface1_GetRecommendedRenderingMode(IDWriteFontFace4 *iface, | |
| FLOAT font_emsize, FLOAT dpiX, FLOAT dpiY, const DWRITE_MATRIX *transform, BOOL is_sideways, | |
| DWRITE_OUTLINE_THRESHOLD threshold, DWRITE_MEASURING_MODE measuring_mode, DWRITE_RENDERING_MODE *rendering_mode) | |
| { | |
| DWRITE_GRID_FIT_MODE gridfitmode; | |
| return IDWriteFontFace2_GetRecommendedRenderingMode((IDWriteFontFace2*)iface, font_emsize, dpiX, dpiY, transform, is_sideways, | |
| threshold, measuring_mode, NULL, rendering_mode, &gridfitmode); | |
| } | |
| static HRESULT WINAPI dwritefontface1_GetVerticalGlyphVariants(IDWriteFontFace4 *iface, UINT32 glyph_count, | |
| const UINT16 *nominal_indices, UINT16 *vertical_indices) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%u %p %p): stub\n", This, glyph_count, nominal_indices, vertical_indices); | |
| return E_NOTIMPL; | |
| } | |
| static BOOL WINAPI dwritefontface1_HasVerticalGlyphVariants(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return !!(This->flags & FONTFACE_HAS_VERTICAL_VARIANTS); | |
| } | |
| static BOOL WINAPI dwritefontface2_IsColorFont(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return get_fontface_cpal(This) && get_fontface_colr(This); | |
| } | |
| static UINT32 WINAPI dwritefontface2_GetColorPaletteCount(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return opentype_get_cpal_palettecount(get_fontface_cpal(This)); | |
| } | |
| static UINT32 WINAPI dwritefontface2_GetPaletteEntryCount(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return opentype_get_cpal_paletteentrycount(get_fontface_cpal(This)); | |
| } | |
| static HRESULT WINAPI dwritefontface2_GetPaletteEntries(IDWriteFontFace4 *iface, UINT32 palette_index, | |
| UINT32 first_entry_index, UINT32 entry_count, DWRITE_COLOR_F *entries) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%u %u %u %p)\n", This, palette_index, first_entry_index, entry_count, entries); | |
| return opentype_get_cpal_entries(get_fontface_cpal(This), palette_index, first_entry_index, entry_count, entries); | |
| } | |
| static HRESULT WINAPI dwritefontface2_GetRecommendedRenderingMode(IDWriteFontFace4 *iface, FLOAT emSize, | |
| FLOAT dpiX, FLOAT dpiY, DWRITE_MATRIX const *m, BOOL is_sideways, DWRITE_OUTLINE_THRESHOLD threshold, | |
| DWRITE_MEASURING_MODE measuringmode, IDWriteRenderingParams *params, DWRITE_RENDERING_MODE *renderingmode, | |
| DWRITE_GRID_FIT_MODE *gridfitmode) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FLOAT emthreshold; | |
| WORD gasp, *ptr; | |
| UINT32 size; | |
| TRACE("(%p)->(%.2f %.2f %.2f %p %d %d %d %p %p %p)\n", This, emSize, dpiX, dpiY, m, is_sideways, threshold, | |
| measuringmode, params, renderingmode, gridfitmode); | |
| if (m) | |
| FIXME("transform not supported %s\n", debugstr_matrix(m)); | |
| if (is_sideways) | |
| FIXME("sideways mode not supported\n"); | |
| emSize *= max(dpiX, dpiY) / 96.0f; | |
| *renderingmode = DWRITE_RENDERING_MODE_DEFAULT; | |
| *gridfitmode = DWRITE_GRID_FIT_MODE_DEFAULT; | |
| if (params) { | |
| IDWriteRenderingParams2 *params2; | |
| HRESULT hr; | |
| hr = IDWriteRenderingParams_QueryInterface(params, &IID_IDWriteRenderingParams2, (void**)¶ms2); | |
| if (hr == S_OK) { | |
| *renderingmode = IDWriteRenderingParams2_GetRenderingMode(params2); | |
| *gridfitmode = IDWriteRenderingParams2_GetGridFitMode(params2); | |
| IDWriteRenderingParams2_Release(params2); | |
| } | |
| else | |
| *renderingmode = IDWriteRenderingParams_GetRenderingMode(params); | |
| } | |
| emthreshold = threshold == DWRITE_OUTLINE_THRESHOLD_ANTIALIASED ? RECOMMENDED_OUTLINE_AA_THRESHOLD : RECOMMENDED_OUTLINE_A_THRESHOLD; | |
| ptr = get_fontface_gasp(This, &size); | |
| gasp = opentype_get_gasp_flags(ptr, size, emSize); | |
| if (*renderingmode == DWRITE_RENDERING_MODE_DEFAULT) { | |
| if (emSize >= emthreshold) | |
| *renderingmode = DWRITE_RENDERING_MODE_OUTLINE; | |
| else | |
| *renderingmode = fontface_renderingmode_from_measuringmode(measuringmode, emSize, gasp); | |
| } | |
| if (*gridfitmode == DWRITE_GRID_FIT_MODE_DEFAULT) { | |
| if (emSize >= emthreshold) | |
| *gridfitmode = DWRITE_GRID_FIT_MODE_DISABLED; | |
| else if (measuringmode == DWRITE_MEASURING_MODE_GDI_CLASSIC || measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL) | |
| *gridfitmode = DWRITE_GRID_FIT_MODE_ENABLED; | |
| else | |
| *gridfitmode = (gasp & (GASP_GRIDFIT|GASP_SYMMETRIC_GRIDFIT)) ? DWRITE_GRID_FIT_MODE_ENABLED : DWRITE_GRID_FIT_MODE_DISABLED; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefontface3_GetFontFaceReference(IDWriteFontFace4 *iface, IDWriteFontFaceReference **ref) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%p): stub\n", This, ref); | |
| return E_NOTIMPL; | |
| } | |
| static void WINAPI dwritefontface3_GetPanose(IDWriteFontFace4 *iface, DWRITE_PANOSE *panose) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)->(%p)\n", This, panose); | |
| *panose = This->panose; | |
| } | |
| static DWRITE_FONT_WEIGHT WINAPI dwritefontface3_GetWeight(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return This->weight; | |
| } | |
| static DWRITE_FONT_STRETCH WINAPI dwritefontface3_GetStretch(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return This->stretch; | |
| } | |
| static DWRITE_FONT_STYLE WINAPI dwritefontface3_GetStyle(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return This->style; | |
| } | |
| static HRESULT WINAPI dwritefontface3_GetFamilyNames(IDWriteFontFace4 *iface, IDWriteLocalizedStrings **names) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%p): stub\n", This, names); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI dwritefontface3_GetFaceNames(IDWriteFontFace4 *iface, IDWriteLocalizedStrings **names) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%p): stub\n", This, names); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI dwritefontface3_GetInformationalStrings(IDWriteFontFace4 *iface, DWRITE_INFORMATIONAL_STRING_ID stringid, | |
| IDWriteLocalizedStrings **strings, BOOL *exists) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%u %p %p): stub\n", This, stringid, strings, exists); | |
| return E_NOTIMPL; | |
| } | |
| static BOOL WINAPI dwritefontface3_HasCharacter(IDWriteFontFace4 *iface, UINT32 ch) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| UINT16 index; | |
| TRACE("(%p)->(%#x)\n", This, ch); | |
| index = 0; | |
| if (FAILED(fontface_get_glyphs(This, &ch, 1, &index))) | |
| return FALSE; | |
| return index != 0; | |
| } | |
| static HRESULT WINAPI dwritefontface3_GetRecommendedRenderingMode(IDWriteFontFace4 *iface, FLOAT emSize, FLOAT dpiX, FLOAT dpiY, | |
| DWRITE_MATRIX const *m, BOOL is_sideways, DWRITE_OUTLINE_THRESHOLD threshold, DWRITE_MEASURING_MODE measuring_mode, | |
| IDWriteRenderingParams *params, DWRITE_RENDERING_MODE1 *rendering_mode, DWRITE_GRID_FIT_MODE *gridfit_mode) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FLOAT emthreshold; | |
| WORD gasp, *ptr; | |
| UINT32 size; | |
| TRACE("(%p)->(%.2f %.2f %.2f %p %d %d %d %p %p %p)\n", This, emSize, dpiX, dpiY, m, is_sideways, threshold, | |
| measuring_mode, params, rendering_mode, gridfit_mode); | |
| if (m) | |
| FIXME("transform not supported %s\n", debugstr_matrix(m)); | |
| if (is_sideways) | |
| FIXME("sideways mode not supported\n"); | |
| emSize *= max(dpiX, dpiY) / 96.0f; | |
| *rendering_mode = DWRITE_RENDERING_MODE1_DEFAULT; | |
| *gridfit_mode = DWRITE_GRID_FIT_MODE_DEFAULT; | |
| if (params) { | |
| IDWriteRenderingParams3 *params3; | |
| HRESULT hr; | |
| hr = IDWriteRenderingParams_QueryInterface(params, &IID_IDWriteRenderingParams3, (void**)¶ms3); | |
| if (hr == S_OK) { | |
| *rendering_mode = IDWriteRenderingParams3_GetRenderingMode1(params3); | |
| *gridfit_mode = IDWriteRenderingParams3_GetGridFitMode(params3); | |
| IDWriteRenderingParams3_Release(params3); | |
| } | |
| else | |
| *rendering_mode = IDWriteRenderingParams_GetRenderingMode(params); | |
| } | |
| emthreshold = threshold == DWRITE_OUTLINE_THRESHOLD_ANTIALIASED ? RECOMMENDED_OUTLINE_AA_THRESHOLD : RECOMMENDED_OUTLINE_A_THRESHOLD; | |
| ptr = get_fontface_gasp(This, &size); | |
| gasp = opentype_get_gasp_flags(ptr, size, emSize); | |
| if (*rendering_mode == DWRITE_RENDERING_MODE1_DEFAULT) { | |
| if (emSize >= emthreshold) | |
| *rendering_mode = DWRITE_RENDERING_MODE1_OUTLINE; | |
| else | |
| *rendering_mode = fontface_renderingmode_from_measuringmode(measuring_mode, emSize, gasp); | |
| } | |
| if (*gridfit_mode == DWRITE_GRID_FIT_MODE_DEFAULT) { | |
| if (emSize >= emthreshold) | |
| *gridfit_mode = DWRITE_GRID_FIT_MODE_DISABLED; | |
| else if (measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC || measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) | |
| *gridfit_mode = DWRITE_GRID_FIT_MODE_ENABLED; | |
| else | |
| *gridfit_mode = (gasp & (GASP_GRIDFIT|GASP_SYMMETRIC_GRIDFIT)) ? DWRITE_GRID_FIT_MODE_ENABLED : DWRITE_GRID_FIT_MODE_DISABLED; | |
| } | |
| return S_OK; | |
| } | |
| static BOOL WINAPI dwritefontface3_IsCharacterLocal(IDWriteFontFace4 *iface, UINT32 ch) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(0x%x): stub\n", This, ch); | |
| return FALSE; | |
| } | |
| static BOOL WINAPI dwritefontface3_IsGlyphLocal(IDWriteFontFace4 *iface, UINT16 glyph) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%u): stub\n", This, glyph); | |
| return FALSE; | |
| } | |
| static HRESULT WINAPI dwritefontface3_AreCharactersLocal(IDWriteFontFace4 *iface, WCHAR const *text, | |
| UINT32 count, BOOL enqueue_if_not, BOOL *are_local) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%s:%u %d %p): stub\n", This, debugstr_wn(text, count), count, enqueue_if_not, are_local); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI dwritefontface3_AreGlyphsLocal(IDWriteFontFace4 *iface, UINT16 const *glyphs, | |
| UINT32 count, BOOL enqueue_if_not, BOOL *are_local) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%p %u %d %p): stub\n", This, glyphs, count, enqueue_if_not, are_local); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI dwritefontface4_GetGlyphImageFormats_(IDWriteFontFace4 *iface, UINT16 glyph, | |
| UINT32 ppem_first, UINT32 ppem_last, DWRITE_GLYPH_IMAGE_FORMATS *formats) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%u %u %u %p): stub\n", This, glyph, ppem_first, ppem_last, formats); | |
| return E_NOTIMPL; | |
| } | |
| static DWRITE_GLYPH_IMAGE_FORMATS WINAPI dwritefontface4_GetGlyphImageFormats(IDWriteFontFace4 *iface) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| TRACE("(%p)\n", This); | |
| return This->glyph_image_formats; | |
| } | |
| static HRESULT WINAPI dwritefontface4_GetGlyphImageData(IDWriteFontFace4 *iface, UINT16 glyph, | |
| UINT32 ppem, DWRITE_GLYPH_IMAGE_FORMATS format, DWRITE_GLYPH_IMAGE_DATA *data, void **context) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%u %u %d %p %p): stub\n", This, glyph, ppem, format, data, context); | |
| return E_NOTIMPL; | |
| } | |
| static void WINAPI dwritefontface4_ReleaseGlyphImageData(IDWriteFontFace4 *iface, void *context) | |
| { | |
| struct dwrite_fontface *This = impl_from_IDWriteFontFace4(iface); | |
| FIXME("(%p)->(%p): stub\n", This, context); | |
| } | |
| static const IDWriteFontFace4Vtbl dwritefontfacevtbl = { | |
| dwritefontface_QueryInterface, | |
| dwritefontface_AddRef, | |
| dwritefontface_Release, | |
| dwritefontface_GetType, | |
| dwritefontface_GetFiles, | |
| dwritefontface_GetIndex, | |
| dwritefontface_GetSimulations, | |
| dwritefontface_IsSymbolFont, | |
| dwritefontface_GetMetrics, | |
| dwritefontface_GetGlyphCount, | |
| dwritefontface_GetDesignGlyphMetrics, | |
| dwritefontface_GetGlyphIndices, | |
| dwritefontface_TryGetFontTable, | |
| dwritefontface_ReleaseFontTable, | |
| dwritefontface_GetGlyphRunOutline, | |
| dwritefontface_GetRecommendedRenderingMode, | |
| dwritefontface_GetGdiCompatibleMetrics, | |
| dwritefontface_GetGdiCompatibleGlyphMetrics, | |
| dwritefontface1_GetMetrics, | |
| dwritefontface1_GetGdiCompatibleMetrics, | |
| dwritefontface1_GetCaretMetrics, | |
| dwritefontface1_GetUnicodeRanges, | |
| dwritefontface1_IsMonospacedFont, | |
| dwritefontface1_GetDesignGlyphAdvances, | |
| dwritefontface1_GetGdiCompatibleGlyphAdvances, | |
| dwritefontface1_GetKerningPairAdjustments, | |
| dwritefontface1_HasKerningPairs, | |
| dwritefontface1_GetRecommendedRenderingMode, | |
| dwritefontface1_GetVerticalGlyphVariants, | |
| dwritefontface1_HasVerticalGlyphVariants, | |
| dwritefontface2_IsColorFont, | |
| dwritefontface2_GetColorPaletteCount, | |
| dwritefontface2_GetPaletteEntryCount, | |
| dwritefontface2_GetPaletteEntries, | |
| dwritefontface2_GetRecommendedRenderingMode, | |
| dwritefontface3_GetFontFaceReference, | |
| dwritefontface3_GetPanose, | |
| dwritefontface3_GetWeight, | |
| dwritefontface3_GetStretch, | |
| dwritefontface3_GetStyle, | |
| dwritefontface3_GetFamilyNames, | |
| dwritefontface3_GetFaceNames, | |
| dwritefontface3_GetInformationalStrings, | |
| dwritefontface3_HasCharacter, | |
| dwritefontface3_GetRecommendedRenderingMode, | |
| dwritefontface3_IsCharacterLocal, | |
| dwritefontface3_IsGlyphLocal, | |
| dwritefontface3_AreCharactersLocal, | |
| dwritefontface3_AreGlyphsLocal, | |
| dwritefontface4_GetGlyphImageFormats_, | |
| dwritefontface4_GetGlyphImageFormats, | |
| dwritefontface4_GetGlyphImageData, | |
| dwritefontface4_ReleaseGlyphImageData | |
| }; | |
| static HRESULT get_fontface_from_font(struct dwrite_font *font, IDWriteFontFace4 **fontface) | |
| { | |
| struct dwrite_font_data *data = font->data; | |
| struct fontface_desc desc; | |
| struct list *cached_list; | |
| HRESULT hr; | |
| *fontface = NULL; | |
| hr = factory_get_cached_fontface(font->family->collection->factory, &data->file, data->face_index, | |
| font->data->simulations, &cached_list, &IID_IDWriteFontFace4, (void **)fontface); | |
| if (hr == S_OK) | |
| return hr; | |
| desc.factory = font->family->collection->factory; | |
| desc.face_type = data->face_type; | |
| desc.files = &data->file; | |
| desc.files_number = 1; | |
| desc.index = data->face_index; | |
| desc.simulations = data->simulations; | |
| desc.font_data = data; | |
| return create_fontface(&desc, cached_list, fontface); | |
| } | |
| static HRESULT WINAPI dwritefont_QueryInterface(IDWriteFont3 *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFont3) || | |
| IsEqualIID(riid, &IID_IDWriteFont2) || | |
| IsEqualIID(riid, &IID_IDWriteFont1) || | |
| IsEqualIID(riid, &IID_IDWriteFont) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| IDWriteFont3_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI dwritefont_AddRef(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI dwritefont_Release(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| if (!ref) { | |
| IDWriteFontFamily1_Release(&This->family->IDWriteFontFamily1_iface); | |
| release_font_data(This->data); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI dwritefont_GetFontFamily(IDWriteFont3 *iface, IDWriteFontFamily **family) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%p)\n", This, family); | |
| *family = (IDWriteFontFamily*)This->family; | |
| IDWriteFontFamily_AddRef(*family); | |
| return S_OK; | |
| } | |
| static DWRITE_FONT_WEIGHT WINAPI dwritefont_GetWeight(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)\n", This); | |
| return This->data->weight; | |
| } | |
| static DWRITE_FONT_STRETCH WINAPI dwritefont_GetStretch(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)\n", This); | |
| return This->data->stretch; | |
| } | |
| static DWRITE_FONT_STYLE WINAPI dwritefont_GetStyle(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)\n", This); | |
| return This->style; | |
| } | |
| static BOOL WINAPI dwritefont_IsSymbolFont(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| IDWriteFontFace4 *fontface; | |
| HRESULT hr; | |
| BOOL ret; | |
| TRACE("(%p)\n", This); | |
| hr = get_fontface_from_font(This, &fontface); | |
| if (FAILED(hr)) | |
| return FALSE; | |
| ret = IDWriteFontFace4_IsSymbolFont(fontface); | |
| IDWriteFontFace4_Release(fontface); | |
| return ret; | |
| } | |
| static HRESULT WINAPI dwritefont_GetFaceNames(IDWriteFont3 *iface, IDWriteLocalizedStrings **names) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%p)\n", This, names); | |
| return clone_localizedstring(This->data->names, names); | |
| } | |
| static HRESULT WINAPI dwritefont_GetInformationalStrings(IDWriteFont3 *iface, | |
| DWRITE_INFORMATIONAL_STRING_ID stringid, IDWriteLocalizedStrings **strings, BOOL *exists) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| struct dwrite_font_data *data = This->data; | |
| HRESULT hr; | |
| TRACE("(%p)->(%d %p %p)\n", This, stringid, strings, exists); | |
| *exists = FALSE; | |
| *strings = NULL; | |
| if (stringid > DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME || stringid == DWRITE_INFORMATIONAL_STRING_NONE) | |
| return S_OK; | |
| if (!data->info_strings[stringid]) { | |
| IDWriteFontFace4 *fontface; | |
| const void *table_data; | |
| BOOL table_exists; | |
| void *context; | |
| UINT32 size; | |
| hr = get_fontface_from_font(This, &fontface); | |
| if (FAILED(hr)) | |
| return hr; | |
| table_exists = FALSE; | |
| hr = IDWriteFontFace4_TryGetFontTable(fontface, MS_NAME_TAG, &table_data, &size, &context, &table_exists); | |
| if (FAILED(hr) || !table_exists) | |
| WARN("no NAME table found.\n"); | |
| if (table_exists) { | |
| hr = opentype_get_font_info_strings(table_data, stringid, &data->info_strings[stringid]); | |
| IDWriteFontFace4_ReleaseFontTable(fontface, context); | |
| if (FAILED(hr) || !data->info_strings[stringid]) | |
| return hr; | |
| } | |
| IDWriteFontFace4_Release(fontface); | |
| } | |
| hr = clone_localizedstring(data->info_strings[stringid], strings); | |
| if (FAILED(hr)) | |
| return hr; | |
| *exists = TRUE; | |
| return S_OK; | |
| } | |
| static DWRITE_FONT_SIMULATIONS WINAPI dwritefont_GetSimulations(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)\n", This); | |
| return This->data->simulations; | |
| } | |
| static void WINAPI dwritefont_GetMetrics(IDWriteFont3 *iface, DWRITE_FONT_METRICS *metrics) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%p)\n", This, metrics); | |
| memcpy(metrics, &This->data->metrics, sizeof(*metrics)); | |
| } | |
| static HRESULT font_has_character(struct dwrite_font *font, UINT32 ch, BOOL *exists) | |
| { | |
| IDWriteFontFace4 *fontface; | |
| UINT16 index; | |
| HRESULT hr; | |
| *exists = FALSE; | |
| hr = get_fontface_from_font(font, &fontface); | |
| if (FAILED(hr)) | |
| return hr; | |
| index = 0; | |
| hr = IDWriteFontFace4_GetGlyphIndices(fontface, &ch, 1, &index); | |
| IDWriteFontFace4_Release(fontface); | |
| if (FAILED(hr)) | |
| return hr; | |
| *exists = index != 0; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefont_HasCharacter(IDWriteFont3 *iface, UINT32 ch, BOOL *exists) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%#x %p)\n", This, ch, exists); | |
| return font_has_character(This, ch, exists); | |
| } | |
| static HRESULT WINAPI dwritefont_CreateFontFace(IDWriteFont3 *iface, IDWriteFontFace **fontface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%p)\n", This, fontface); | |
| return IDWriteFont3_CreateFontFace(iface, (IDWriteFontFace3**)fontface); | |
| } | |
| static void WINAPI dwritefont1_GetMetrics(IDWriteFont3 *iface, DWRITE_FONT_METRICS1 *metrics) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%p)\n", This, metrics); | |
| *metrics = This->data->metrics; | |
| } | |
| static void WINAPI dwritefont1_GetPanose(IDWriteFont3 *iface, DWRITE_PANOSE *panose) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%p)\n", This, panose); | |
| *panose = This->data->panose; | |
| } | |
| static HRESULT WINAPI dwritefont1_GetUnicodeRanges(IDWriteFont3 *iface, UINT32 max_count, DWRITE_UNICODE_RANGE *ranges, UINT32 *count) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| IDWriteFontFace4 *fontface; | |
| HRESULT hr; | |
| TRACE("(%p)->(%u %p %p)\n", This, max_count, ranges, count); | |
| hr = get_fontface_from_font(This, &fontface); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFontFace4_GetUnicodeRanges(fontface, max_count, ranges, count); | |
| IDWriteFontFace4_Release(fontface); | |
| return hr; | |
| } | |
| static BOOL WINAPI dwritefont1_IsMonospacedFont(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| IDWriteFontFace4 *fontface; | |
| HRESULT hr; | |
| BOOL ret; | |
| TRACE("(%p)\n", This); | |
| hr = get_fontface_from_font(This, &fontface); | |
| if (FAILED(hr)) | |
| return FALSE; | |
| ret = IDWriteFontFace4_IsMonospacedFont(fontface); | |
| IDWriteFontFace4_Release(fontface); | |
| return ret; | |
| } | |
| static BOOL WINAPI dwritefont2_IsColorFont(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| IDWriteFontFace4 *fontface; | |
| HRESULT hr; | |
| BOOL ret; | |
| TRACE("(%p)\n", This); | |
| hr = get_fontface_from_font(This, &fontface); | |
| if (FAILED(hr)) | |
| return FALSE; | |
| ret = IDWriteFontFace4_IsColorFont(fontface); | |
| IDWriteFontFace4_Release(fontface); | |
| return ret; | |
| } | |
| static HRESULT WINAPI dwritefont3_CreateFontFace(IDWriteFont3 *iface, IDWriteFontFace3 **fontface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%p)\n", This, fontface); | |
| return get_fontface_from_font(This, (IDWriteFontFace4 **)fontface); | |
| } | |
| static BOOL WINAPI dwritefont3_Equals(IDWriteFont3 *iface, IDWriteFont *font) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| FIXME("(%p)->(%p): stub\n", This, font); | |
| return FALSE; | |
| } | |
| static HRESULT WINAPI dwritefont3_GetFontFaceReference(IDWriteFont3 *iface, IDWriteFontFaceReference **reference) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| TRACE("(%p)->(%p)\n", This, reference); | |
| return IDWriteFactory5_CreateFontFaceReference_(This->family->collection->factory, This->data->file, | |
| This->data->face_index, This->data->simulations, reference); | |
| } | |
| static BOOL WINAPI dwritefont3_HasCharacter(IDWriteFont3 *iface, UINT32 ch) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| BOOL ret; | |
| TRACE("(%p)->(%#x)\n", This, ch); | |
| return font_has_character(This, ch, &ret) == S_OK && ret; | |
| } | |
| static DWRITE_LOCALITY WINAPI dwritefont3_GetLocality(IDWriteFont3 *iface) | |
| { | |
| struct dwrite_font *This = impl_from_IDWriteFont3(iface); | |
| FIXME("(%p): stub\n", This); | |
| return DWRITE_LOCALITY_LOCAL; | |
| } | |
| static const IDWriteFont3Vtbl dwritefontvtbl = { | |
| dwritefont_QueryInterface, | |
| dwritefont_AddRef, | |
| dwritefont_Release, | |
| dwritefont_GetFontFamily, | |
| dwritefont_GetWeight, | |
| dwritefont_GetStretch, | |
| dwritefont_GetStyle, | |
| dwritefont_IsSymbolFont, | |
| dwritefont_GetFaceNames, | |
| dwritefont_GetInformationalStrings, | |
| dwritefont_GetSimulations, | |
| dwritefont_GetMetrics, | |
| dwritefont_HasCharacter, | |
| dwritefont_CreateFontFace, | |
| dwritefont1_GetMetrics, | |
| dwritefont1_GetPanose, | |
| dwritefont1_GetUnicodeRanges, | |
| dwritefont1_IsMonospacedFont, | |
| dwritefont2_IsColorFont, | |
| dwritefont3_CreateFontFace, | |
| dwritefont3_Equals, | |
| dwritefont3_GetFontFaceReference, | |
| dwritefont3_HasCharacter, | |
| dwritefont3_GetLocality | |
| }; | |
| static struct dwrite_font *unsafe_impl_from_IDWriteFont(IDWriteFont *iface) | |
| { | |
| if (!iface) | |
| return NULL; | |
| assert(iface->lpVtbl == (IDWriteFontVtbl*)&dwritefontvtbl); | |
| return CONTAINING_RECORD(iface, struct dwrite_font, IDWriteFont3_iface); | |
| } | |
| static struct dwrite_fontface *unsafe_impl_from_IDWriteFontFace(IDWriteFontFace *iface) | |
| { | |
| if (!iface) | |
| return NULL; | |
| assert(iface->lpVtbl == (IDWriteFontFaceVtbl*)&dwritefontfacevtbl); | |
| return CONTAINING_RECORD(iface, struct dwrite_fontface, IDWriteFontFace4_iface); | |
| } | |
| void get_logfont_from_font(IDWriteFont *iface, LOGFONTW *lf) | |
| { | |
| struct dwrite_font *font = unsafe_impl_from_IDWriteFont(iface); | |
| *lf = font->data->lf; | |
| } | |
| void get_logfont_from_fontface(IDWriteFontFace *iface, LOGFONTW *lf) | |
| { | |
| struct dwrite_fontface *fontface = unsafe_impl_from_IDWriteFontFace(iface); | |
| *lf = fontface->lf; | |
| } | |
| HRESULT get_fontsig_from_font(IDWriteFont *iface, FONTSIGNATURE *fontsig) | |
| { | |
| struct dwrite_font *font = unsafe_impl_from_IDWriteFont(iface); | |
| *fontsig = font->data->fontsig; | |
| return S_OK; | |
| } | |
| HRESULT get_fontsig_from_fontface(IDWriteFontFace *iface, FONTSIGNATURE *fontsig) | |
| { | |
| struct dwrite_fontface *fontface = unsafe_impl_from_IDWriteFontFace(iface); | |
| *fontsig = fontface->fontsig; | |
| return S_OK; | |
| } | |
| static HRESULT create_font(struct dwrite_fontfamily *family, UINT32 index, IDWriteFont3 **font) | |
| { | |
| struct dwrite_font *This; | |
| *font = NULL; | |
| This = heap_alloc(sizeof(*This)); | |
| if (!This) | |
| return E_OUTOFMEMORY; | |
| This->IDWriteFont3_iface.lpVtbl = &dwritefontvtbl; | |
| This->ref = 1; | |
| This->family = family; | |
| IDWriteFontFamily1_AddRef(&family->IDWriteFontFamily1_iface); | |
| This->data = family->data->fonts[index]; | |
| This->style = This->data->style; | |
| addref_font_data(This->data); | |
| *font = &This->IDWriteFont3_iface; | |
| return S_OK; | |
| } | |
| /* IDWriteFontList1 */ | |
| static HRESULT WINAPI dwritefontlist_QueryInterface(IDWriteFontList1 *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFontList1) || | |
| IsEqualIID(riid, &IID_IDWriteFontList) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| IDWriteFontList1_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI dwritefontlist_AddRef(IDWriteFontList1 *iface) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI dwritefontlist_Release(IDWriteFontList1 *iface) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| if (!ref) { | |
| UINT32 i; | |
| for (i = 0; i < This->font_count; i++) | |
| release_font_data(This->fonts[i]); | |
| IDWriteFontFamily1_Release(&This->family->IDWriteFontFamily1_iface); | |
| heap_free(This->fonts); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI dwritefontlist_GetFontCollection(IDWriteFontList1 *iface, IDWriteFontCollection **collection) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| return IDWriteFontFamily1_GetFontCollection(&This->family->IDWriteFontFamily1_iface, collection); | |
| } | |
| static UINT32 WINAPI dwritefontlist_GetFontCount(IDWriteFontList1 *iface) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| TRACE("(%p)\n", This); | |
| return This->font_count; | |
| } | |
| static HRESULT WINAPI dwritefontlist_GetFont(IDWriteFontList1 *iface, UINT32 index, IDWriteFont **font) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| TRACE("(%p)->(%u %p)\n", This, index, font); | |
| *font = NULL; | |
| if (This->font_count == 0) | |
| return S_FALSE; | |
| if (index >= This->font_count) | |
| return E_INVALIDARG; | |
| return create_font(This->family, index, (IDWriteFont3 **)font); | |
| } | |
| static DWRITE_LOCALITY WINAPI dwritefontlist1_GetFontLocality(IDWriteFontList1 *iface, UINT32 index) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| FIXME("(%p)->(%u): stub\n", This, index); | |
| return DWRITE_LOCALITY_LOCAL; | |
| } | |
| static HRESULT WINAPI dwritefontlist1_GetFont(IDWriteFontList1 *iface, UINT32 index, IDWriteFont3 **font) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| TRACE("(%p)->(%u %p)\n", This, index, font); | |
| *font = NULL; | |
| if (This->font_count == 0) | |
| return S_FALSE; | |
| if (index >= This->font_count) | |
| return E_FAIL; | |
| return create_font(This->family, index, font); | |
| } | |
| static HRESULT WINAPI dwritefontlist1_GetFontFaceReference(IDWriteFontList1 *iface, UINT32 index, | |
| IDWriteFontFaceReference **reference) | |
| { | |
| struct dwrite_fontlist *This = impl_from_IDWriteFontList1(iface); | |
| IDWriteFont3 *font; | |
| HRESULT hr; | |
| TRACE("(%p)->(%u %p)\n", This, index, reference); | |
| *reference = NULL; | |
| hr = IDWriteFontList1_GetFont(iface, index, &font); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFont3_GetFontFaceReference(font, reference); | |
| IDWriteFont3_Release(font); | |
| return hr; | |
| } | |
| static const IDWriteFontList1Vtbl dwritefontlistvtbl = { | |
| dwritefontlist_QueryInterface, | |
| dwritefontlist_AddRef, | |
| dwritefontlist_Release, | |
| dwritefontlist_GetFontCollection, | |
| dwritefontlist_GetFontCount, | |
| dwritefontlist_GetFont, | |
| dwritefontlist1_GetFontLocality, | |
| dwritefontlist1_GetFont, | |
| dwritefontlist1_GetFontFaceReference | |
| }; | |
| static HRESULT WINAPI dwritefontfamily_QueryInterface(IDWriteFontFamily1 *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFontFamily1) || | |
| IsEqualIID(riid, &IID_IDWriteFontFamily) || | |
| IsEqualIID(riid, &IID_IDWriteFontList) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| IDWriteFontFamily1_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI dwritefontfamily_AddRef(IDWriteFontFamily1 *iface) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI dwritefontfamily_Release(IDWriteFontFamily1 *iface) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| if (!ref) | |
| { | |
| IDWriteFontCollection1_Release(&This->collection->IDWriteFontCollection1_iface); | |
| release_fontfamily_data(This->data); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI dwritefontfamily_GetFontCollection(IDWriteFontFamily1 *iface, IDWriteFontCollection **collection) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| TRACE("(%p)->(%p)\n", This, collection); | |
| *collection = (IDWriteFontCollection*)This->collection; | |
| IDWriteFontCollection_AddRef(*collection); | |
| return S_OK; | |
| } | |
| static UINT32 WINAPI dwritefontfamily_GetFontCount(IDWriteFontFamily1 *iface) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| TRACE("(%p)\n", This); | |
| return This->data->font_count; | |
| } | |
| static HRESULT WINAPI dwritefontfamily_GetFont(IDWriteFontFamily1 *iface, UINT32 index, IDWriteFont **font) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| TRACE("(%p)->(%u %p)\n", This, index, font); | |
| *font = NULL; | |
| if (This->data->font_count == 0) | |
| return S_FALSE; | |
| if (index >= This->data->font_count) | |
| return E_INVALIDARG; | |
| return create_font(This, index, (IDWriteFont3 **)font); | |
| } | |
| static HRESULT WINAPI dwritefontfamily_GetFamilyNames(IDWriteFontFamily1 *iface, IDWriteLocalizedStrings **names) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| return clone_localizedstring(This->data->familyname, names); | |
| } | |
| static BOOL is_better_font_match(const struct dwrite_font_propvec *next, const struct dwrite_font_propvec *cur, | |
| const struct dwrite_font_propvec *req) | |
| { | |
| FLOAT cur_to_req = get_font_prop_vec_distance(cur, req); | |
| FLOAT next_to_req = get_font_prop_vec_distance(next, req); | |
| FLOAT cur_req_prod, next_req_prod; | |
| if (next_to_req < cur_to_req) | |
| return TRUE; | |
| if (next_to_req > cur_to_req) | |
| return FALSE; | |
| cur_req_prod = get_font_prop_vec_dotproduct(cur, req); | |
| next_req_prod = get_font_prop_vec_dotproduct(next, req); | |
| if (next_req_prod > cur_req_prod) | |
| return TRUE; | |
| if (next_req_prod < cur_req_prod) | |
| return FALSE; | |
| if (next->stretch > cur->stretch) | |
| return TRUE; | |
| if (next->stretch < cur->stretch) | |
| return FALSE; | |
| if (next->style > cur->style) | |
| return TRUE; | |
| if (next->style < cur->style) | |
| return FALSE; | |
| if (next->weight > cur->weight) | |
| return TRUE; | |
| if (next->weight < cur->weight) | |
| return FALSE; | |
| /* full match, no reason to prefer new variant */ | |
| return FALSE; | |
| } | |
| static HRESULT WINAPI dwritefontfamily_GetFirstMatchingFont(IDWriteFontFamily1 *iface, DWRITE_FONT_WEIGHT weight, | |
| DWRITE_FONT_STRETCH stretch, DWRITE_FONT_STYLE style, IDWriteFont **font) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| struct dwrite_font_propvec req; | |
| UINT32 i, match; | |
| TRACE("(%p)->(%d %d %d %p)\n", This, weight, stretch, style, font); | |
| if (This->data->font_count == 0) { | |
| *font = NULL; | |
| return DWRITE_E_NOFONT; | |
| } | |
| init_font_prop_vec(weight, stretch, style, &req); | |
| match = 0; | |
| for (i = 1; i < This->data->font_count; i++) { | |
| if (is_better_font_match(&This->data->fonts[i]->propvec, &This->data->fonts[match]->propvec, &req)) | |
| match = i; | |
| } | |
| return create_font(This, match, (IDWriteFont3 **)font); | |
| } | |
| typedef BOOL (*matching_filter_func)(const struct dwrite_font_data*); | |
| static BOOL is_font_acceptable_for_normal(const struct dwrite_font_data *font) | |
| { | |
| return font->style == DWRITE_FONT_STYLE_NORMAL || font->style == DWRITE_FONT_STYLE_ITALIC; | |
| } | |
| static BOOL is_font_acceptable_for_oblique_italic(const struct dwrite_font_data *font) | |
| { | |
| return font->style == DWRITE_FONT_STYLE_OBLIQUE || font->style == DWRITE_FONT_STYLE_ITALIC; | |
| } | |
| static void matchingfonts_sort(struct dwrite_fontlist *fonts, const struct dwrite_font_propvec *req) | |
| { | |
| UINT32 b = fonts->font_count - 1, j, t; | |
| while (1) { | |
| t = b; | |
| for (j = 0; j < b; j++) { | |
| if (is_better_font_match(&fonts->fonts[j+1]->propvec, &fonts->fonts[j]->propvec, req)) { | |
| struct dwrite_font_data *s = fonts->fonts[j]; | |
| fonts->fonts[j] = fonts->fonts[j+1]; | |
| fonts->fonts[j+1] = s; | |
| t = j; | |
| } | |
| } | |
| if (t == b) | |
| break; | |
| b = t; | |
| }; | |
| } | |
| static HRESULT WINAPI dwritefontfamily_GetMatchingFonts(IDWriteFontFamily1 *iface, DWRITE_FONT_WEIGHT weight, | |
| DWRITE_FONT_STRETCH stretch, DWRITE_FONT_STYLE style, IDWriteFontList **ret) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| matching_filter_func func = NULL; | |
| struct dwrite_font_propvec req; | |
| struct dwrite_fontlist *fonts; | |
| UINT32 i; | |
| TRACE("(%p)->(%d %d %d %p)\n", This, weight, stretch, style, ret); | |
| *ret = NULL; | |
| fonts = heap_alloc(sizeof(*fonts)); | |
| if (!fonts) | |
| return E_OUTOFMEMORY; | |
| /* Allocate as many as family has, not all of them will be necessary used. */ | |
| fonts->fonts = heap_alloc(sizeof(*fonts->fonts) * This->data->font_count); | |
| if (!fonts->fonts) { | |
| heap_free(fonts); | |
| return E_OUTOFMEMORY; | |
| } | |
| fonts->IDWriteFontList1_iface.lpVtbl = &dwritefontlistvtbl; | |
| fonts->ref = 1; | |
| fonts->family = This; | |
| IDWriteFontFamily1_AddRef(&fonts->family->IDWriteFontFamily1_iface); | |
| fonts->font_count = 0; | |
| /* Normal style accepts Normal or Italic, Oblique and Italic - both Oblique and Italic styles */ | |
| if (style == DWRITE_FONT_STYLE_NORMAL) { | |
| if (This->data->has_normal_face || This->data->has_italic_face) | |
| func = is_font_acceptable_for_normal; | |
| } | |
| else /* requested oblique or italic */ { | |
| if (This->data->has_oblique_face || This->data->has_italic_face) | |
| func = is_font_acceptable_for_oblique_italic; | |
| } | |
| for (i = 0; i < This->data->font_count; i++) { | |
| if (!func || func(This->data->fonts[i])) { | |
| fonts->fonts[fonts->font_count] = This->data->fonts[i]; | |
| addref_font_data(This->data->fonts[i]); | |
| fonts->font_count++; | |
| } | |
| } | |
| /* now potential matches are sorted using same criteria GetFirstMatchingFont uses */ | |
| init_font_prop_vec(weight, stretch, style, &req); | |
| matchingfonts_sort(fonts, &req); | |
| *ret = (IDWriteFontList*)&fonts->IDWriteFontList1_iface; | |
| return S_OK; | |
| } | |
| static DWRITE_LOCALITY WINAPI dwritefontfamily1_GetFontLocality(IDWriteFontFamily1 *iface, UINT32 index) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| FIXME("(%p)->(%u): stub\n", This, index); | |
| return DWRITE_LOCALITY_LOCAL; | |
| } | |
| static HRESULT WINAPI dwritefontfamily1_GetFont(IDWriteFontFamily1 *iface, UINT32 index, IDWriteFont3 **font) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| TRACE("(%p)->(%u %p)\n", This, index, font); | |
| *font = NULL; | |
| if (This->data->font_count == 0) | |
| return S_FALSE; | |
| if (index >= This->data->font_count) | |
| return E_FAIL; | |
| return create_font(This, index, font); | |
| } | |
| static HRESULT WINAPI dwritefontfamily1_GetFontFaceReference(IDWriteFontFamily1 *iface, UINT32 index, | |
| IDWriteFontFaceReference **reference) | |
| { | |
| struct dwrite_fontfamily *This = impl_from_IDWriteFontFamily1(iface); | |
| IDWriteFont3 *font; | |
| HRESULT hr; | |
| TRACE("(%p)->(%u %p)\n", This, index, reference); | |
| *reference = NULL; | |
| hr = IDWriteFontFamily1_GetFont(iface, index, &font); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFont3_GetFontFaceReference(font, reference); | |
| IDWriteFont3_Release(font); | |
| return hr; | |
| } | |
| static const IDWriteFontFamily1Vtbl fontfamilyvtbl = { | |
| dwritefontfamily_QueryInterface, | |
| dwritefontfamily_AddRef, | |
| dwritefontfamily_Release, | |
| dwritefontfamily_GetFontCollection, | |
| dwritefontfamily_GetFontCount, | |
| dwritefontfamily_GetFont, | |
| dwritefontfamily_GetFamilyNames, | |
| dwritefontfamily_GetFirstMatchingFont, | |
| dwritefontfamily_GetMatchingFonts, | |
| dwritefontfamily1_GetFontLocality, | |
| dwritefontfamily1_GetFont, | |
| dwritefontfamily1_GetFontFaceReference | |
| }; | |
| static HRESULT create_fontfamily(struct dwrite_fontcollection *collection, UINT32 index, IDWriteFontFamily1 **family) | |
| { | |
| struct dwrite_fontfamily *This; | |
| *family = NULL; | |
| This = heap_alloc(sizeof(struct dwrite_fontfamily)); | |
| if (!This) return E_OUTOFMEMORY; | |
| This->IDWriteFontFamily1_iface.lpVtbl = &fontfamilyvtbl; | |
| This->ref = 1; | |
| This->collection = collection; | |
| IDWriteFontCollection1_AddRef(&collection->IDWriteFontCollection1_iface); | |
| This->data = collection->family_data[index]; | |
| InterlockedIncrement(&This->data->ref); | |
| *family = &This->IDWriteFontFamily1_iface; | |
| return S_OK; | |
| } | |
| BOOL is_system_collection(IDWriteFontCollection *collection) | |
| { | |
| void *obj; | |
| return IDWriteFontCollection_QueryInterface(collection, &IID_issystemcollection, (void**)&obj) == S_OK; | |
| } | |
| static HRESULT WINAPI dwritesystemfontcollection_QueryInterface(IDWriteFontCollection1 *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFontCollection1) || | |
| IsEqualIID(riid, &IID_IDWriteFontCollection) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| IDWriteFontCollection1_AddRef(iface); | |
| return S_OK; | |
| } | |
| *obj = NULL; | |
| if (IsEqualIID(riid, &IID_issystemcollection)) | |
| return S_OK; | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| static HRESULT WINAPI dwritefontcollection_QueryInterface(IDWriteFontCollection1 *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFontCollection1) || | |
| IsEqualIID(riid, &IID_IDWriteFontCollection) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| IDWriteFontCollection1_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI dwritefontcollection_AddRef(IDWriteFontCollection1 *iface) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI dwritefontcollection_Release(IDWriteFontCollection1 *iface) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| if (!ref) { | |
| int i; | |
| factory_detach_fontcollection(This->factory, iface); | |
| for (i = 0; i < This->family_count; i++) | |
| release_fontfamily_data(This->family_data[i]); | |
| heap_free(This->family_data); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static UINT32 WINAPI dwritefontcollection_GetFontFamilyCount(IDWriteFontCollection1 *iface) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| TRACE("(%p)\n", This); | |
| return This->family_count; | |
| } | |
| static HRESULT WINAPI dwritefontcollection_GetFontFamily(IDWriteFontCollection1 *iface, UINT32 index, IDWriteFontFamily **family) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| TRACE("(%p)->(%u %p)\n", This, index, family); | |
| if (index >= This->family_count) { | |
| *family = NULL; | |
| return E_FAIL; | |
| } | |
| return create_fontfamily(This, index, (IDWriteFontFamily1 **)family); | |
| } | |
| static UINT32 collection_find_family(struct dwrite_fontcollection *collection, const WCHAR *name) | |
| { | |
| UINT32 i; | |
| for (i = 0; i < collection->family_count; i++) { | |
| IDWriteLocalizedStrings *family_name = collection->family_data[i]->familyname; | |
| UINT32 j, count = IDWriteLocalizedStrings_GetCount(family_name); | |
| HRESULT hr; | |
| for (j = 0; j < count; j++) { | |
| WCHAR buffer[255]; | |
| hr = IDWriteLocalizedStrings_GetString(family_name, j, buffer, 255); | |
| if (SUCCEEDED(hr) && !strcmpiW(buffer, name)) | |
| return i; | |
| } | |
| } | |
| return ~0u; | |
| } | |
| static HRESULT WINAPI dwritefontcollection_FindFamilyName(IDWriteFontCollection1 *iface, const WCHAR *name, UINT32 *index, BOOL *exists) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| TRACE("(%p)->(%s %p %p)\n", This, debugstr_w(name), index, exists); | |
| *index = collection_find_family(This, name); | |
| *exists = *index != ~0u; | |
| return S_OK; | |
| } | |
| static BOOL is_same_fontfile(IDWriteFontFile *left, IDWriteFontFile *right) | |
| { | |
| UINT32 left_key_size, right_key_size; | |
| const void *left_key, *right_key; | |
| HRESULT hr; | |
| if (left == right) | |
| return TRUE; | |
| hr = IDWriteFontFile_GetReferenceKey(left, &left_key, &left_key_size); | |
| if (FAILED(hr)) | |
| return FALSE; | |
| hr = IDWriteFontFile_GetReferenceKey(right, &right_key, &right_key_size); | |
| if (FAILED(hr)) | |
| return FALSE; | |
| if (left_key_size != right_key_size) | |
| return FALSE; | |
| return !memcmp(left_key, right_key, left_key_size); | |
| } | |
| static HRESULT WINAPI dwritefontcollection_GetFontFromFontFace(IDWriteFontCollection1 *iface, IDWriteFontFace *face, IDWriteFont **font) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| IDWriteFontFamily1 *family; | |
| UINT32 i, j, face_index; | |
| BOOL found_font = FALSE; | |
| IDWriteFontFile *file; | |
| HRESULT hr; | |
| TRACE("(%p)->(%p %p)\n", This, face, font); | |
| *font = NULL; | |
| if (!face) | |
| return E_INVALIDARG; | |
| i = 1; | |
| hr = IDWriteFontFace_GetFiles(face, &i, &file); | |
| if (FAILED(hr)) | |
| return hr; | |
| face_index = IDWriteFontFace_GetIndex(face); | |
| found_font = FALSE; | |
| for (i = 0; i < This->family_count; i++) { | |
| struct dwrite_fontfamily_data *family_data = This->family_data[i]; | |
| for (j = 0; j < family_data->font_count; j++) { | |
| struct dwrite_font_data *font_data = family_data->fonts[j]; | |
| if (face_index == font_data->face_index && is_same_fontfile(file, font_data->file)) { | |
| found_font = TRUE; | |
| break; | |
| } | |
| } | |
| if (found_font) | |
| break; | |
| } | |
| IDWriteFontFile_Release(file); | |
| if (!found_font) | |
| return DWRITE_E_NOFONT; | |
| hr = create_fontfamily(This, i, &family); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = create_font(impl_from_IDWriteFontFamily1(family), j, (IDWriteFont3 **)font); | |
| IDWriteFontFamily1_Release(family); | |
| return hr; | |
| } | |
| static HRESULT WINAPI dwritefontcollection1_GetFontSet(IDWriteFontCollection1 *iface, IDWriteFontSet **fontset) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| FIXME("(%p)->(%p): stub\n", This, fontset); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI dwritefontcollection1_GetFontFamily(IDWriteFontCollection1 *iface, UINT32 index, IDWriteFontFamily1 **family) | |
| { | |
| struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection1(iface); | |
| TRACE("(%p)->(%u %p)\n", This, index, family); | |
| if (index >= This->family_count) { | |
| *family = NULL; | |
| return E_FAIL; | |
| } | |
| return create_fontfamily(This, index, family); | |
| } | |
| static const IDWriteFontCollection1Vtbl fontcollectionvtbl = { | |
| dwritefontcollection_QueryInterface, | |
| dwritefontcollection_AddRef, | |
| dwritefontcollection_Release, | |
| dwritefontcollection_GetFontFamilyCount, | |
| dwritefontcollection_GetFontFamily, | |
| dwritefontcollection_FindFamilyName, | |
| dwritefontcollection_GetFontFromFontFace, | |
| dwritefontcollection1_GetFontSet, | |
| dwritefontcollection1_GetFontFamily | |
| }; | |
| static const IDWriteFontCollection1Vtbl systemfontcollectionvtbl = { | |
| dwritesystemfontcollection_QueryInterface, | |
| dwritefontcollection_AddRef, | |
| dwritefontcollection_Release, | |
| dwritefontcollection_GetFontFamilyCount, | |
| dwritefontcollection_GetFontFamily, | |
| dwritefontcollection_FindFamilyName, | |
| dwritefontcollection_GetFontFromFontFace, | |
| dwritefontcollection1_GetFontSet, | |
| dwritefontcollection1_GetFontFamily | |
| }; | |
| static HRESULT fontfamily_add_font(struct dwrite_fontfamily_data *family_data, struct dwrite_font_data *font_data) | |
| { | |
| if (family_data->font_count + 1 >= family_data->font_alloc) { | |
| struct dwrite_font_data **new_list; | |
| UINT32 new_alloc; | |
| new_alloc = family_data->font_alloc * 2; | |
| new_list = heap_realloc(family_data->fonts, sizeof(*family_data->fonts) * new_alloc); | |
| if (!new_list) | |
| return E_OUTOFMEMORY; | |
| family_data->fonts = new_list; | |
| family_data->font_alloc = new_alloc; | |
| } | |
| family_data->fonts[family_data->font_count] = font_data; | |
| family_data->font_count++; | |
| if (font_data->style == DWRITE_FONT_STYLE_NORMAL) | |
| family_data->has_normal_face = 1; | |
| else if (font_data->style == DWRITE_FONT_STYLE_OBLIQUE) | |
| family_data->has_oblique_face = 1; | |
| else | |
| family_data->has_italic_face = 1; | |
| return S_OK; | |
| } | |
| static HRESULT fontcollection_add_family(struct dwrite_fontcollection *collection, struct dwrite_fontfamily_data *family) | |
| { | |
| if (collection->family_alloc < collection->family_count + 1) { | |
| struct dwrite_fontfamily_data **new_list; | |
| UINT32 new_alloc; | |
| new_alloc = collection->family_alloc * 2; | |
| new_list = heap_realloc(collection->family_data, sizeof(*new_list) * new_alloc); | |
| if (!new_list) | |
| return E_OUTOFMEMORY; | |
| collection->family_alloc = new_alloc; | |
| collection->family_data = new_list; | |
| } | |
| collection->family_data[collection->family_count++] = family; | |
| return S_OK; | |
| } | |
| static HRESULT init_font_collection(struct dwrite_fontcollection *collection, BOOL is_system) | |
| { | |
| collection->IDWriteFontCollection1_iface.lpVtbl = is_system ? &systemfontcollectionvtbl : &fontcollectionvtbl; | |
| collection->ref = 1; | |
| collection->family_count = 0; | |
| collection->family_alloc = is_system ? 30 : 5; | |
| collection->family_data = heap_alloc(sizeof(*collection->family_data) * collection->family_alloc); | |
| if (!collection->family_data) | |
| return E_OUTOFMEMORY; | |
| return S_OK; | |
| } | |
| HRESULT get_filestream_from_file(IDWriteFontFile *file, IDWriteFontFileStream **stream) | |
| { | |
| IDWriteFontFileLoader *loader; | |
| const void *key; | |
| UINT32 key_size; | |
| HRESULT hr; | |
| *stream = NULL; | |
| hr = IDWriteFontFile_GetReferenceKey(file, &key, &key_size); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFontFile_GetLoader(file, &loader); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFontFileLoader_CreateStreamFromKey(loader, key, key_size, stream); | |
| IDWriteFontFileLoader_Release(loader); | |
| if (FAILED(hr)) | |
| return hr; | |
| return hr; | |
| } | |
| static void fontstrings_get_en_string(IDWriteLocalizedStrings *strings, WCHAR *buffer, UINT32 size) | |
| { | |
| BOOL exists = FALSE; | |
| UINT32 index; | |
| HRESULT hr; | |
| buffer[0] = 0; | |
| hr = IDWriteLocalizedStrings_FindLocaleName(strings, enusW, &index, &exists); | |
| if (FAILED(hr) || !exists) | |
| return; | |
| IDWriteLocalizedStrings_GetString(strings, index, buffer, size); | |
| } | |
| static int trim_spaces(WCHAR *in, WCHAR *ret) | |
| { | |
| int len; | |
| while (isspaceW(*in)) | |
| in++; | |
| ret[0] = 0; | |
| if (!(len = strlenW(in))) | |
| return 0; | |
| while (isspaceW(in[len-1])) | |
| len--; | |
| memcpy(ret, in, len*sizeof(WCHAR)); | |
| ret[len] = 0; | |
| return len; | |
| } | |
| struct name_token { | |
| struct list entry; | |
| const WCHAR *ptr; | |
| INT len; /* token length */ | |
| INT fulllen; /* full length including following separators */ | |
| }; | |
| static inline BOOL is_name_separator_char(WCHAR ch) | |
| { | |
| return ch == ' ' || ch == '.' || ch == '-' || ch == '_'; | |
| } | |
| struct name_pattern { | |
| const WCHAR *part1; /* NULL indicates end of list */ | |
| const WCHAR *part2; /* optional, if not NULL should point to non-empty string */ | |
| }; | |
| static BOOL match_pattern_list(struct list *tokens, const struct name_pattern *patterns, struct name_token *match) | |
| { | |
| const struct name_pattern *pattern; | |
| struct name_token *token; | |
| int i = 0; | |
| while ((pattern = &patterns[i++])->part1) { | |
| int len_part1 = strlenW(pattern->part1); | |
| int len_part2 = pattern->part2 ? strlenW(pattern->part2) : 0; | |
| LIST_FOR_EACH_ENTRY(token, tokens, struct name_token, entry) { | |
| if (len_part2 == 0) { | |
| /* simple case with single part pattern */ | |
| if (token->len != len_part1) | |
| continue; | |
| if (!strncmpiW(token->ptr, pattern->part1, len_part1)) { | |
| if (match) *match = *token; | |
| list_remove(&token->entry); | |
| heap_free(token); | |
| return TRUE; | |
| } | |
| } | |
| else { | |
| struct name_token *next_token; | |
| struct list *next_entry; | |
| /* pattern parts are stored in reading order, tokens list is reversed */ | |
| if (token->len < len_part2) | |
| continue; | |
| /* it's possible to have combined string as a token, like ExtraCondensed */ | |
| if (token->len == len_part1 + len_part2) { | |
| if (strncmpiW(token->ptr, pattern->part1, len_part1)) | |
| continue; | |
| if (strncmpiW(&token->ptr[len_part1], pattern->part2, len_part2)) | |
| continue; | |
| /* combined string match */ | |
| if (match) *match = *token; | |
| list_remove(&token->entry); | |
| heap_free(token); | |
| return TRUE; | |
| } | |
| /* now it's only possible to have two tokens matched to respective pattern parts */ | |
| if (token->len != len_part2) | |
| continue; | |
| next_entry = list_next(tokens, &token->entry); | |
| if (next_entry) { | |
| next_token = LIST_ENTRY(next_entry, struct name_token, entry); | |
| if (next_token->len != len_part1) | |
| continue; | |
| if (strncmpiW(token->ptr, pattern->part2, len_part2)) | |
| continue; | |
| if (strncmpiW(next_token->ptr, pattern->part1, len_part1)) | |
| continue; | |
| /* both parts matched, remove tokens */ | |
| if (match) { | |
| match->ptr = next_token->ptr; | |
| match->len = (token->ptr - next_token->ptr) + token->len; | |
| } | |
| list_remove(&token->entry); | |
| list_remove(&next_token->entry); | |
| heap_free(next_token); | |
| heap_free(token); | |
| return TRUE; | |
| } | |
| } | |
| } | |
| } | |
| if (match) { | |
| match->ptr = NULL; | |
| match->len = 0; | |
| } | |
| return FALSE; | |
| } | |
| static DWRITE_FONT_STYLE font_extract_style(struct list *tokens, DWRITE_FONT_STYLE style, struct name_token *match) | |
| { | |
| static const WCHAR itaW[] = {'i','t','a',0}; | |
| static const WCHAR italW[] = {'i','t','a','l',0}; | |
| static const WCHAR cursiveW[] = {'c','u','r','s','i','v','e',0}; | |
| static const WCHAR kursivW[] = {'k','u','r','s','i','v',0}; | |
| static const WCHAR inclinedW[] = {'i','n','c','l','i','n','e','d',0}; | |
| static const WCHAR backslantedW[] = {'b','a','c','k','s','l','a','n','t','e','d',0}; | |
| static const WCHAR backslantW[] = {'b','a','c','k','s','l','a','n','t',0}; | |
| static const WCHAR slantedW[] = {'s','l','a','n','t','e','d',0}; | |
| static const struct name_pattern italic_patterns[] = { | |
| { itaW }, | |
| { italW }, | |
| { italicW }, | |
| { cursiveW }, | |
| { kursivW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern oblique_patterns[] = { | |
| { inclinedW }, | |
| { obliqueW }, | |
| { backslantedW }, | |
| { backslantW }, | |
| { slantedW }, | |
| { NULL } | |
| }; | |
| /* italic patterns first */ | |
| if (match_pattern_list(tokens, italic_patterns, match)) | |
| return DWRITE_FONT_STYLE_ITALIC; | |
| /* oblique patterns */ | |
| if (match_pattern_list(tokens, oblique_patterns, match)) | |
| return DWRITE_FONT_STYLE_OBLIQUE; | |
| return style; | |
| } | |
| static DWRITE_FONT_STRETCH font_extract_stretch(struct list *tokens, DWRITE_FONT_STRETCH stretch, | |
| struct name_token *match) | |
| { | |
| static const WCHAR compressedW[] = {'c','o','m','p','r','e','s','s','e','d',0}; | |
| static const WCHAR extendedW[] = {'e','x','t','e','n','d','e','d',0}; | |
| static const WCHAR compactW[] = {'c','o','m','p','a','c','t',0}; | |
| static const WCHAR narrowW[] = {'n','a','r','r','o','w',0}; | |
| static const WCHAR wideW[] = {'w','i','d','e',0}; | |
| static const WCHAR condW[] = {'c','o','n','d',0}; | |
| static const struct name_pattern ultracondensed_patterns[] = { | |
| { extraW, compressedW }, | |
| { extW, compressedW }, | |
| { ultraW, compressedW }, | |
| { ultraW, condensedW }, | |
| { ultraW, condW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern extracondensed_patterns[] = { | |
| { compressedW }, | |
| { extraW, condensedW }, | |
| { extW, condensedW }, | |
| { extraW, condW }, | |
| { extW, condW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern semicondensed_patterns[] = { | |
| { narrowW }, | |
| { compactW }, | |
| { semiW, condensedW }, | |
| { semiW, condW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern semiexpanded_patterns[] = { | |
| { wideW }, | |
| { semiW, expandedW }, | |
| { semiW, extendedW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern extraexpanded_patterns[] = { | |
| { extraW, expandedW }, | |
| { extW, expandedW }, | |
| { extraW, extendedW }, | |
| { extW, extendedW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern ultraexpanded_patterns[] = { | |
| { ultraW, expandedW }, | |
| { ultraW, extendedW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern condensed_patterns[] = { | |
| { condensedW }, | |
| { condW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern expanded_patterns[] = { | |
| { expandedW }, | |
| { extendedW }, | |
| { NULL } | |
| }; | |
| if (match_pattern_list(tokens, ultracondensed_patterns, match)) | |
| return DWRITE_FONT_STRETCH_ULTRA_CONDENSED; | |
| if (match_pattern_list(tokens, extracondensed_patterns, match)) | |
| return DWRITE_FONT_STRETCH_EXTRA_CONDENSED; | |
| if (match_pattern_list(tokens, semicondensed_patterns, match)) | |
| return DWRITE_FONT_STRETCH_SEMI_CONDENSED; | |
| if (match_pattern_list(tokens, semiexpanded_patterns, match)) | |
| return DWRITE_FONT_STRETCH_SEMI_EXPANDED; | |
| if (match_pattern_list(tokens, extraexpanded_patterns, match)) | |
| return DWRITE_FONT_STRETCH_EXTRA_EXPANDED; | |
| if (match_pattern_list(tokens, ultraexpanded_patterns, match)) | |
| return DWRITE_FONT_STRETCH_ULTRA_EXPANDED; | |
| if (match_pattern_list(tokens, condensed_patterns, match)) | |
| return DWRITE_FONT_STRETCH_CONDENSED; | |
| if (match_pattern_list(tokens, expanded_patterns, match)) | |
| return DWRITE_FONT_STRETCH_EXPANDED; | |
| return stretch; | |
| } | |
| static DWRITE_FONT_WEIGHT font_extract_weight(struct list *tokens, DWRITE_FONT_WEIGHT weight, | |
| struct name_token *match) | |
| { | |
| static const WCHAR heavyW[] = {'h','e','a','v','y',0}; | |
| static const WCHAR nordW[] = {'n','o','r','d',0}; | |
| static const struct name_pattern thin_patterns[] = { | |
| { extraW, thinW }, | |
| { extW, thinW }, | |
| { ultraW, thinW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern extralight_patterns[] = { | |
| { extraW, lightW }, | |
| { extW, lightW }, | |
| { ultraW, lightW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern semilight_patterns[] = { | |
| { semiW, lightW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern demibold_patterns[] = { | |
| { semiW, boldW }, | |
| { demiW, boldW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern extrabold_patterns[] = { | |
| { extraW, boldW }, | |
| { extW, boldW }, | |
| { ultraW, boldW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern extrablack_patterns[] = { | |
| { extraW, blackW }, | |
| { extW, blackW }, | |
| { ultraW, blackW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern bold_patterns[] = { | |
| { boldW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern thin2_patterns[] = { | |
| { thinW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern light_patterns[] = { | |
| { lightW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern medium_patterns[] = { | |
| { mediumW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern black_patterns[] = { | |
| { blackW }, | |
| { heavyW }, | |
| { nordW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern demibold2_patterns[] = { | |
| { demiW }, | |
| { NULL } | |
| }; | |
| static const struct name_pattern extrabold2_patterns[] = { | |
| { ultraW }, | |
| { NULL } | |
| }; | |
| /* FIXME: allow optional 'face' suffix, separated or not. It's removed together with | |
| matching pattern. */ | |
| if (match_pattern_list(tokens, thin_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_THIN; | |
| if (match_pattern_list(tokens, extralight_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_EXTRA_LIGHT; | |
| if (match_pattern_list(tokens, semilight_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_SEMI_LIGHT; | |
| if (match_pattern_list(tokens, demibold_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_DEMI_BOLD; | |
| if (match_pattern_list(tokens, extrabold_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_EXTRA_BOLD; | |
| if (match_pattern_list(tokens, extrablack_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_EXTRA_BLACK; | |
| if (match_pattern_list(tokens, bold_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_BOLD; | |
| if (match_pattern_list(tokens, thin2_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_THIN; | |
| if (match_pattern_list(tokens, light_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_LIGHT; | |
| if (match_pattern_list(tokens, medium_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_MEDIUM; | |
| if (match_pattern_list(tokens, black_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_BLACK; | |
| if (match_pattern_list(tokens, black_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_BLACK; | |
| if (match_pattern_list(tokens, demibold2_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_DEMI_BOLD; | |
| if (match_pattern_list(tokens, extrabold2_patterns, match)) | |
| return DWRITE_FONT_WEIGHT_EXTRA_BOLD; | |
| /* FIXME: use abbreviated names to extract weight */ | |
| return weight; | |
| } | |
| struct knownweight_entry { | |
| const WCHAR *nameW; | |
| DWRITE_FONT_WEIGHT weight; | |
| }; | |
| static int compare_knownweights(const void *a, const void* b) | |
| { | |
| DWRITE_FONT_WEIGHT target = *(DWRITE_FONT_WEIGHT*)a; | |
| const struct knownweight_entry *entry = (struct knownweight_entry*)b; | |
| int ret = 0; | |
| if (target > entry->weight) | |
| ret = 1; | |
| else if (target < entry->weight) | |
| ret = -1; | |
| return ret; | |
| } | |
| static BOOL is_known_weight_value(DWRITE_FONT_WEIGHT weight, WCHAR *nameW) | |
| { | |
| static const WCHAR extralightW[] = {'E','x','t','r','a',' ','L','i','g','h','t',0}; | |
| static const WCHAR semilightW[] = {'S','e','m','i',' ','L','i','g','h','t',0}; | |
| static const WCHAR extrablackW[] = {'E','x','t','r','a',' ','B','l','a','c','k',0}; | |
| static const WCHAR extraboldW[] = {'E','x','t','r','a',' ','B','o','l','d',0}; | |
| static const WCHAR demiboldW[] = {'D','e','m','i',' ','B','o','l','d',0}; | |
| const struct knownweight_entry *ptr; | |
| static const struct knownweight_entry knownweights[] = { | |
| { thinW, DWRITE_FONT_WEIGHT_THIN }, | |
| { extralightW, DWRITE_FONT_WEIGHT_EXTRA_LIGHT }, | |
| { lightW, DWRITE_FONT_WEIGHT_LIGHT }, | |
| { semilightW, DWRITE_FONT_WEIGHT_SEMI_LIGHT }, | |
| { mediumW, DWRITE_FONT_WEIGHT_MEDIUM }, | |
| { demiboldW, DWRITE_FONT_WEIGHT_DEMI_BOLD }, | |
| { boldW, DWRITE_FONT_WEIGHT_BOLD }, | |
| { extraboldW, DWRITE_FONT_WEIGHT_EXTRA_BOLD }, | |
| { blackW, DWRITE_FONT_WEIGHT_BLACK }, | |
| { extrablackW, DWRITE_FONT_WEIGHT_EXTRA_BLACK } | |
| }; | |
| ptr = bsearch(&weight, knownweights, sizeof(knownweights)/sizeof(knownweights[0]), sizeof(knownweights[0]), | |
| compare_knownweights); | |
| if (!ptr) { | |
| nameW[0] = 0; | |
| return FALSE; | |
| } | |
| strcpyW(nameW, ptr->nameW); | |
| return TRUE; | |
| } | |
| static inline void font_name_token_to_str(const struct name_token *name, WCHAR *strW) | |
| { | |
| memcpy(strW, name->ptr, name->len * sizeof(WCHAR)); | |
| strW[name->len] = 0; | |
| } | |
| /* Modifies facenameW string, and returns pointer to regular term that was removed */ | |
| static const WCHAR *facename_remove_regular_term(WCHAR *facenameW, INT len) | |
| { | |
| static const WCHAR bookW[] = {'B','o','o','k',0}; | |
| static const WCHAR normalW[] = {'N','o','r','m','a','l',0}; | |
| static const WCHAR regularW[] = {'R','e','g','u','l','a','r',0}; | |
| static const WCHAR romanW[] = {'R','o','m','a','n',0}; | |
| static const WCHAR uprightW[] = {'U','p','r','i','g','h','t',0}; | |
| static const WCHAR *regular_patterns[] = { | |
| bookW, | |
| normalW, | |
| regularW, | |
| romanW, | |
| uprightW, | |
| NULL | |
| }; | |
| const WCHAR *regular_ptr = NULL, *ptr; | |
| int i = 0; | |
| if (len == -1) | |
| len = strlenW(facenameW); | |
| /* remove rightmost regular variant from face name */ | |
| while (!regular_ptr && (ptr = regular_patterns[i++])) { | |
| int pattern_len = strlenW(ptr); | |
| WCHAR *src; | |
| if (pattern_len > len) | |
| continue; | |
| src = facenameW + len - pattern_len; | |
| while (src >= facenameW) { | |
| if (!strncmpiW(src, ptr, pattern_len)) { | |
| memmove(src, src + pattern_len, (len - pattern_len - (src - facenameW) + 1)*sizeof(WCHAR)); | |
| len = strlenW(facenameW); | |
| regular_ptr = ptr; | |
| break; | |
| } | |
| else | |
| src--; | |
| } | |
| } | |
| return regular_ptr; | |
| } | |
| static void fontname_tokenize(struct list *tokens, const WCHAR *nameW) | |
| { | |
| const WCHAR *ptr; | |
| list_init(tokens); | |
| ptr = nameW; | |
| while (*ptr) { | |
| struct name_token *token = heap_alloc(sizeof(*token)); | |
| token->ptr = ptr; | |
| token->len = 0; | |
| token->fulllen = 0; | |
| while (*ptr && !is_name_separator_char(*ptr)) { | |
| token->len++; | |
| token->fulllen++; | |
| ptr++; | |
| } | |
| /* skip separators */ | |
| while (is_name_separator_char(*ptr)) { | |
| token->fulllen++; | |
| ptr++; | |
| } | |
| list_add_head(tokens, &token->entry); | |
| } | |
| } | |
| static void fontname_tokens_to_str(struct list *tokens, WCHAR *nameW) | |
| { | |
| struct name_token *token, *token2; | |
| LIST_FOR_EACH_ENTRY_SAFE_REV(token, token2, tokens, struct name_token, entry) { | |
| int len; | |
| list_remove(&token->entry); | |
| /* don't include last separator */ | |
| len = list_empty(tokens) ? token->len : token->fulllen; | |
| memcpy(nameW, token->ptr, len * sizeof(WCHAR)); | |
| nameW += len; | |
| heap_free(token); | |
| } | |
| *nameW = 0; | |
| } | |
| static BOOL font_apply_differentiation_rules(struct dwrite_font_data *font, WCHAR *familyW, WCHAR *faceW) | |
| { | |
| struct name_token stretch_name, weight_name, style_name; | |
| WCHAR familynameW[255], facenameW[255], finalW[255]; | |
| WCHAR weightW[32], stretchW[32], styleW[32]; | |
| const WCHAR *regular_ptr = NULL; | |
| DWRITE_FONT_STRETCH stretch; | |
| DWRITE_FONT_WEIGHT weight; | |
| struct list tokens; | |
| int len; | |
| /* remove leading and trailing spaces from family and face name */ | |
| trim_spaces(familyW, familynameW); | |
| len = trim_spaces(faceW, facenameW); | |
| /* remove rightmost regular variant from face name */ | |
| regular_ptr = facename_remove_regular_term(facenameW, len); | |
| /* append face name to family name, FIXME check if face name is a substring of family name */ | |
| if (*facenameW) { | |
| strcatW(familynameW, spaceW); | |
| strcatW(familynameW, facenameW); | |
| } | |
| /* tokenize with " .-_" */ | |
| fontname_tokenize(&tokens, familynameW); | |
| /* extract and resolve style */ | |
| font->style = font_extract_style(&tokens, font->style, &style_name); | |
| /* extract stretch */ | |
| stretch = font_extract_stretch(&tokens, font->stretch, &stretch_name); | |
| /* extract weight */ | |
| weight = font_extract_weight(&tokens, font->weight, &weight_name); | |
| /* resolve weight */ | |
| if (weight != font->weight) { | |
| if (!(weight < DWRITE_FONT_WEIGHT_NORMAL && font->weight < DWRITE_FONT_WEIGHT_NORMAL) && | |
| !(weight > DWRITE_FONT_WEIGHT_MEDIUM && font->weight > DWRITE_FONT_WEIGHT_MEDIUM) && | |
| !((weight == DWRITE_FONT_WEIGHT_NORMAL && font->weight == DWRITE_FONT_WEIGHT_MEDIUM) || | |
| (weight == DWRITE_FONT_WEIGHT_MEDIUM && font->weight == DWRITE_FONT_WEIGHT_NORMAL)) && | |
| !(abs(weight - font->weight) <= 150 && | |
| font->weight != DWRITE_FONT_WEIGHT_NORMAL && | |
| font->weight != DWRITE_FONT_WEIGHT_MEDIUM && | |
| font->weight != DWRITE_FONT_WEIGHT_BOLD)) { | |
| font->weight = weight; | |
| } | |
| } | |
| /* Resolve stretch - extracted stretch can't be normal, it will override specified stretch if | |
| it's leaning in opposite direction from normal comparing to specified stretch or if specified | |
| stretch itself is normal (extracted stretch is never normal). */ | |
| if (stretch != font->stretch) { | |
| if ((font->stretch == DWRITE_FONT_STRETCH_NORMAL) || | |
| (font->stretch < DWRITE_FONT_STRETCH_NORMAL && stretch > DWRITE_FONT_STRETCH_NORMAL) || | |
| (font->stretch > DWRITE_FONT_STRETCH_NORMAL && stretch < DWRITE_FONT_STRETCH_NORMAL)) { | |
| font->stretch = stretch; | |
| } | |
| } | |
| /* FIXME: cleanup face name from possible 2-3 digit prefixes */ | |
| /* get final combined string from what's left in token list, list is released */ | |
| fontname_tokens_to_str(&tokens, finalW); | |
| if (!strcmpW(familyW, finalW)) | |
| return FALSE; | |
| /* construct face name */ | |
| strcpyW(familyW, finalW); | |
| /* resolved weight name */ | |
| if (weight_name.ptr) | |
| font_name_token_to_str(&weight_name, weightW); | |
| /* ignore normal weight */ | |
| else if (font->weight == DWRITE_FONT_WEIGHT_NORMAL) | |
| weightW[0] = 0; | |
| /* for known weight values use appropriate names */ | |
| else if (is_known_weight_value(font->weight, weightW)) { | |
| } | |
| /* use Wnnn format as a fallback in case weight is not one of known values */ | |
| else { | |
| static const WCHAR fmtW[] = {'W','%','d',0}; | |
| sprintfW(weightW, fmtW, font->weight); | |
| } | |
| /* resolved stretch name */ | |
| if (stretch_name.ptr) | |
| font_name_token_to_str(&stretch_name, stretchW); | |
| /* ignore normal stretch */ | |
| else if (font->stretch == DWRITE_FONT_STRETCH_NORMAL) | |
| stretchW[0] = 0; | |
| /* use predefined stretch names */ | |
| else { | |
| static const WCHAR ultracondensedW[] = {'U','l','t','r','a',' ','C','o','n','d','e','n','s','e','d',0}; | |
| static const WCHAR extracondensedW[] = {'E','x','t','r','a',' ','C','o','n','d','e','n','s','e','d',0}; | |
| static const WCHAR semicondensedW[] = {'S','e','m','i',' ','C','o','n','d','e','n','s','e','d',0}; | |
| static const WCHAR semiexpandedW[] = {'S','e','m','i',' ','E','x','p','a','n','d','e','d',0}; | |
| static const WCHAR extraexpandedW[] = {'E','x','t','r','a',' ','E','x','p','a','n','d','e','d',0}; | |
| static const WCHAR ultraexpandedW[] = {'U','l','t','r','a',' ','E','x','p','a','n','d','e','d',0}; | |
| static const WCHAR *stretchnamesW[] = { | |
| NULL, /* DWRITE_FONT_STRETCH_UNDEFINED */ | |
| ultracondensedW, | |
| extracondensedW, | |
| condensedW, | |
| semicondensedW, | |
| NULL, /* DWRITE_FONT_STRETCH_NORMAL */ | |
| semiexpandedW, | |
| expandedW, | |
| extraexpandedW, | |
| ultraexpandedW | |
| }; | |
| strcpyW(stretchW, stretchnamesW[font->stretch]); | |
| } | |
| /* resolved style name */ | |
| if (style_name.ptr) | |
| font_name_token_to_str(&style_name, styleW); | |
| else if (font->style == DWRITE_FONT_STYLE_NORMAL) | |
| styleW[0] = 0; | |
| /* use predefined names */ | |
| else { | |
| if (font->style == DWRITE_FONT_STYLE_ITALIC) | |
| strcpyW(styleW, italicW); | |
| else | |
| strcpyW(styleW, obliqueW); | |
| } | |
| /* use Regular match if it was found initially */ | |
| if (!*weightW && !*stretchW && !*styleW) | |
| strcpyW(faceW, regular_ptr ? regular_ptr : regularW); | |
| else { | |
| faceW[0] = 0; | |
| if (*stretchW) | |
| strcpyW(faceW, stretchW); | |
| if (*weightW) { | |
| if (*faceW) | |
| strcatW(faceW, spaceW); | |
| strcatW(faceW, weightW); | |
| } | |
| if (*styleW) { | |
| if (*faceW) | |
| strcatW(faceW, spaceW); | |
| strcatW(faceW, styleW); | |
| } | |
| } | |
| TRACE("resolved family %s, face %s\n", debugstr_w(familyW), debugstr_w(faceW)); | |
| return TRUE; | |
| } | |
| static HRESULT init_font_data(const struct fontface_desc *desc, IDWriteLocalizedStrings **family_name, struct dwrite_font_data **ret) | |
| { | |
| struct file_stream_desc stream_desc; | |
| struct dwrite_font_props props; | |
| struct dwrite_font_data *data; | |
| IDWriteFontFileStream *stream; | |
| WCHAR familyW[255], faceW[255]; | |
| HRESULT hr; | |
| *ret = NULL; | |
| data = heap_alloc_zero(sizeof(*data)); | |
| if (!data) | |
| return E_OUTOFMEMORY; | |
| hr = get_filestream_from_file(desc->files[0], &stream); | |
| if (FAILED(hr)) { | |
| heap_free(data); | |
| return hr; | |
| } | |
| data->ref = 1; | |
| data->file = desc->files[0]; | |
| data->face_index = desc->index; | |
| data->face_type = desc->face_type; | |
| data->simulations = DWRITE_FONT_SIMULATIONS_NONE; | |
| data->bold_sim_tested = 0; | |
| data->oblique_sim_tested = 0; | |
| IDWriteFontFile_AddRef(data->file); | |
| stream_desc.stream = stream; | |
| stream_desc.face_type = desc->face_type; | |
| stream_desc.face_index = desc->index; | |
| opentype_get_font_properties(&stream_desc, &props); | |
| opentype_get_font_metrics(&stream_desc, &data->metrics, NULL); | |
| opentype_get_font_facename(&stream_desc, props.lf.lfFaceName, &data->names); | |
| /* get family name from font file */ | |
| hr = opentype_get_font_familyname(&stream_desc, family_name); | |
| IDWriteFontFileStream_Release(stream); | |
| if (FAILED(hr)) { | |
| WARN("unable to get family name from font\n"); | |
| release_font_data(data); | |
| return hr; | |
| } | |
| data->style = props.style; | |
| data->stretch = props.stretch; | |
| data->weight = props.weight; | |
| data->panose = props.panose; | |
| data->fontsig = props.fontsig; | |
| data->lf = props.lf; | |
| fontstrings_get_en_string(*family_name, familyW, sizeof(familyW)/sizeof(WCHAR)); | |
| fontstrings_get_en_string(data->names, faceW, sizeof(faceW)/sizeof(WCHAR)); | |
| if (font_apply_differentiation_rules(data, familyW, faceW)) { | |
| set_en_localizedstring(*family_name, familyW); | |
| set_en_localizedstring(data->names, faceW); | |
| } | |
| init_font_prop_vec(data->weight, data->stretch, data->style, &data->propvec); | |
| *ret = data; | |
| return S_OK; | |
| } | |
| static HRESULT init_font_data_from_font(const struct dwrite_font_data *src, DWRITE_FONT_SIMULATIONS sim, | |
| const WCHAR *facenameW, struct dwrite_font_data **ret) | |
| { | |
| struct dwrite_font_data *data; | |
| *ret = NULL; | |
| data = heap_alloc_zero(sizeof(*data)); | |
| if (!data) | |
| return E_OUTOFMEMORY; | |
| *data = *src; | |
| data->ref = 1; | |
| data->simulations |= sim; | |
| if (sim == DWRITE_FONT_SIMULATIONS_BOLD) | |
| data->weight = DWRITE_FONT_WEIGHT_BOLD; | |
| else if (sim == DWRITE_FONT_SIMULATIONS_OBLIQUE) | |
| data->style = DWRITE_FONT_STYLE_OBLIQUE; | |
| memset(data->info_strings, 0, sizeof(data->info_strings)); | |
| data->names = NULL; | |
| IDWriteFontFile_AddRef(data->file); | |
| create_localizedstrings(&data->names); | |
| add_localizedstring(data->names, enusW, facenameW); | |
| init_font_prop_vec(data->weight, data->stretch, data->style, &data->propvec); | |
| *ret = data; | |
| return S_OK; | |
| }; | |
| static HRESULT init_fontfamily_data(IDWriteLocalizedStrings *familyname, struct dwrite_fontfamily_data **ret) | |
| { | |
| struct dwrite_fontfamily_data *data; | |
| data = heap_alloc(sizeof(*data)); | |
| if (!data) | |
| return E_OUTOFMEMORY; | |
| data->ref = 1; | |
| data->font_count = 0; | |
| data->font_alloc = 2; | |
| data->has_normal_face = 0; | |
| data->has_oblique_face = 0; | |
| data->has_italic_face = 0; | |
| data->fonts = heap_alloc(sizeof(*data->fonts)*data->font_alloc); | |
| if (!data->fonts) { | |
| heap_free(data); | |
| return E_OUTOFMEMORY; | |
| } | |
| data->familyname = familyname; | |
| IDWriteLocalizedStrings_AddRef(familyname); | |
| *ret = data; | |
| return S_OK; | |
| } | |
| static void fontfamily_add_bold_simulated_face(struct dwrite_fontfamily_data *family) | |
| { | |
| UINT32 i, j, heaviest; | |
| for (i = 0; i < family->font_count; i++) { | |
| DWRITE_FONT_WEIGHT weight = family->fonts[i]->weight; | |
| heaviest = i; | |
| if (family->fonts[i]->bold_sim_tested) | |
| continue; | |
| family->fonts[i]->bold_sim_tested = 1; | |
| for (j = i; j < family->font_count; j++) { | |
| if (family->fonts[j]->bold_sim_tested) | |
| continue; | |
| if ((family->fonts[i]->style == family->fonts[j]->style) && | |
| (family->fonts[i]->stretch == family->fonts[j]->stretch)) { | |
| if (family->fonts[j]->weight > weight) { | |
| weight = family->fonts[j]->weight; | |
| heaviest = j; | |
| } | |
| family->fonts[j]->bold_sim_tested = 1; | |
| } | |
| } | |
| if (weight >= DWRITE_FONT_WEIGHT_SEMI_LIGHT && weight <= 550) { | |
| static const struct name_pattern weightsim_patterns[] = { | |
| { extraW, lightW }, | |
| { extW, lightW }, | |
| { ultraW, lightW }, | |
| { semiW, lightW }, | |
| { semiW, boldW }, | |
| { demiW, boldW }, | |
| { boldW }, | |
| { thinW }, | |
| { lightW }, | |
| { mediumW }, | |
| { demiW }, | |
| { NULL } | |
| }; | |
| WCHAR facenameW[255], initialW[255]; | |
| struct dwrite_font_data *boldface; | |
| struct list tokens; | |
| /* add Bold simulation based on heaviest face data */ | |
| /* Simulated face name should only contain Bold as weight term, | |
| so remove existing regular and weight terms. */ | |
| fontstrings_get_en_string(family->fonts[heaviest]->names, initialW, sizeof(initialW)/sizeof(WCHAR)); | |
| facename_remove_regular_term(initialW, -1); | |
| /* remove current weight pattern */ | |
| fontname_tokenize(&tokens, initialW); | |
| match_pattern_list(&tokens, weightsim_patterns, NULL); | |
| fontname_tokens_to_str(&tokens, facenameW); | |
| /* Bold suffix for new name */ | |
| if (*facenameW) | |
| strcatW(facenameW, spaceW); | |
| strcatW(facenameW, boldW); | |
| if (init_font_data_from_font(family->fonts[heaviest], DWRITE_FONT_SIMULATIONS_BOLD, facenameW, &boldface) == S_OK) { | |
| boldface->bold_sim_tested = 1; | |
| boldface->lf.lfWeight += (FW_BOLD - FW_REGULAR) / 2 + 1; | |
| fontfamily_add_font(family, boldface); | |
| } | |
| } | |
| } | |
| } | |
| static void fontfamily_add_oblique_simulated_face(struct dwrite_fontfamily_data *family) | |
| { | |
| UINT32 i, j; | |
| for (i = 0; i < family->font_count; i++) { | |
| UINT32 regular = ~0u, oblique = ~0u; | |
| struct dwrite_font_data *obliqueface; | |
| WCHAR facenameW[255]; | |
| if (family->fonts[i]->oblique_sim_tested) | |
| continue; | |
| family->fonts[i]->oblique_sim_tested = 1; | |
| if (family->fonts[i]->style == DWRITE_FONT_STYLE_NORMAL) | |
| regular = i; | |
| else if (family->fonts[i]->style == DWRITE_FONT_STYLE_OBLIQUE) | |
| oblique = i; | |
| /* find regular style with same weight/stretch values */ | |
| for (j = i; j < family->font_count; j++) { | |
| if (family->fonts[j]->oblique_sim_tested) | |
| continue; | |
| if ((family->fonts[i]->weight == family->fonts[j]->weight) && | |
| (family->fonts[i]->stretch == family->fonts[j]->stretch)) { | |
| family->fonts[j]->oblique_sim_tested = 1; | |
| if (regular == ~0 && family->fonts[j]->style == DWRITE_FONT_STYLE_NORMAL) | |
| regular = j; | |
| if (oblique == ~0 && family->fonts[j]->style == DWRITE_FONT_STYLE_OBLIQUE) | |
| oblique = j; | |
| } | |
| if (regular != ~0u && oblique != ~0u) | |
| break; | |
| } | |
| /* no regular variant for this weight/stretch pair, nothing to base simulated face on */ | |
| if (regular == ~0u) | |
| continue; | |
| /* regular face exists, and corresponding oblique is present as well, nothing to do */ | |
| if (oblique != ~0u) | |
| continue; | |
| /* add oblique simulation based on this regular face */ | |
| /* remove regular term if any, append 'Oblique' */ | |
| fontstrings_get_en_string(family->fonts[regular]->names, facenameW, sizeof(facenameW)/sizeof(WCHAR)); | |
| facename_remove_regular_term(facenameW, -1); | |
| if (*facenameW) | |
| strcatW(facenameW, spaceW); | |
| strcatW(facenameW, obliqueW); | |
| if (init_font_data_from_font(family->fonts[regular], DWRITE_FONT_SIMULATIONS_OBLIQUE, facenameW, &obliqueface) == S_OK) { | |
| obliqueface->oblique_sim_tested = 1; | |
| obliqueface->lf.lfItalic = 1; | |
| fontfamily_add_font(family, obliqueface); | |
| } | |
| } | |
| } | |
| static BOOL fontcollection_add_replacement(struct dwrite_fontcollection *collection, const WCHAR *target_name, | |
| const WCHAR *replacement_name) | |
| { | |
| UINT32 i = collection_find_family(collection, replacement_name); | |
| struct dwrite_fontfamily_data *target; | |
| IDWriteLocalizedStrings *strings; | |
| HRESULT hr; | |
| /* replacement does not exist */ | |
| if (i == ~0u) | |
| return FALSE; | |
| hr = create_localizedstrings(&strings); | |
| if (FAILED(hr)) | |
| return FALSE; | |
| /* add a new family with target name, reuse font data from replacement */ | |
| add_localizedstring(strings, enusW, target_name); | |
| hr = init_fontfamily_data(strings, &target); | |
| if (hr == S_OK) { | |
| struct dwrite_fontfamily_data *replacement = collection->family_data[i]; | |
| WCHAR nameW[255]; | |
| for (i = 0; i < replacement->font_count; i++) { | |
| fontfamily_add_font(target, replacement->fonts[i]); | |
| addref_font_data(replacement->fonts[i]); | |
| } | |
| fontcollection_add_family(collection, target); | |
| fontstrings_get_en_string(replacement->familyname, nameW, sizeof(nameW)/sizeof(WCHAR)); | |
| TRACE("replacement %s -> %s\n", debugstr_w(target_name), debugstr_w(nameW)); | |
| } | |
| IDWriteLocalizedStrings_Release(strings); | |
| return TRUE; | |
| } | |
| /* Add family mappings from HKCU\Software\Wine\Fonts\Replacements. This only affects | |
| system font collections. */ | |
| static void fontcollection_add_replacements(struct dwrite_fontcollection *collection) | |
| { | |
| DWORD max_namelen, max_datalen, i = 0, type, datalen, namelen; | |
| WCHAR *name; | |
| void *data; | |
| HKEY hkey; | |
| if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts\\Replacements", &hkey)) | |
| return; | |
| if (RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &max_namelen, &max_datalen, NULL, NULL)) { | |
| RegCloseKey(hkey); | |
| return; | |
| } | |
| max_namelen++; /* returned value doesn't include room for '\0' */ | |
| name = heap_alloc(max_namelen * sizeof(WCHAR)); | |
| data = heap_alloc(max_datalen); | |
| datalen = max_datalen; | |
| namelen = max_namelen; | |
| while (RegEnumValueW(hkey, i++, name, &namelen, NULL, &type, data, &datalen) == ERROR_SUCCESS) { | |
| if (collection_find_family(collection, name) == ~0u) { | |
| if (type == REG_MULTI_SZ) { | |
| WCHAR *replacement = data; | |
| while (*replacement) { | |
| if (fontcollection_add_replacement(collection, name, replacement)) | |
| break; | |
| replacement += strlenW(replacement) + 1; | |
| } | |
| } | |
| else if (type == REG_SZ) | |
| fontcollection_add_replacement(collection, name, data); | |
| } | |
| else | |
| TRACE("%s is available, won't be replaced.\n", debugstr_w(name)); | |
| datalen = max_datalen; | |
| namelen = max_namelen; | |
| } | |
| heap_free(data); | |
| heap_free(name); | |
| RegCloseKey(hkey); | |
| } | |
| HRESULT create_font_collection(IDWriteFactory5 *factory, IDWriteFontFileEnumerator *enumerator, BOOL is_system, | |
| IDWriteFontCollection1 **ret) | |
| { | |
| struct fontfile_enum { | |
| struct list entry; | |
| IDWriteFontFile *file; | |
| }; | |
| struct fontfile_enum *fileenum, *fileenum2; | |
| struct dwrite_fontcollection *collection; | |
| struct list scannedfiles; | |
| BOOL current = FALSE; | |
| HRESULT hr = S_OK; | |
| UINT32 i; | |
| *ret = NULL; | |
| collection = heap_alloc(sizeof(struct dwrite_fontcollection)); | |
| if (!collection) return E_OUTOFMEMORY; | |
| hr = init_font_collection(collection, is_system); | |
| if (FAILED(hr)) { | |
| heap_free(collection); | |
| return hr; | |
| } | |
| *ret = &collection->IDWriteFontCollection1_iface; | |
| TRACE("building font collection:\n"); | |
| list_init(&scannedfiles); | |
| while (hr == S_OK) { | |
| DWRITE_FONT_FACE_TYPE face_type; | |
| DWRITE_FONT_FILE_TYPE file_type; | |
| BOOL supported, same = FALSE; | |
| IDWriteFontFile *file; | |
| UINT32 face_count; | |
| current = FALSE; | |
| hr = IDWriteFontFileEnumerator_MoveNext(enumerator, ¤t); | |
| if (FAILED(hr) || !current) | |
| break; | |
| hr = IDWriteFontFileEnumerator_GetCurrentFontFile(enumerator, &file); | |
| if (FAILED(hr)) | |
| break; | |
| /* check if we've scanned this file already */ | |
| LIST_FOR_EACH_ENTRY(fileenum, &scannedfiles, struct fontfile_enum, entry) { | |
| if ((same = is_same_fontfile(fileenum->file, file))) | |
| break; | |
| } | |
| if (same) { | |
| IDWriteFontFile_Release(file); | |
| continue; | |
| } | |
| /* failed font files are skipped */ | |
| hr = IDWriteFontFile_Analyze(file, &supported, &file_type, &face_type, &face_count); | |
| if (FAILED(hr) || !supported || face_count == 0) { | |
| TRACE("unsupported font (%p, 0x%08x, %d, %u)\n", file, hr, supported, face_count); | |
| IDWriteFontFile_Release(file); | |
| hr = S_OK; | |
| continue; | |
| } | |
| /* add to scanned list */ | |
| fileenum = heap_alloc(sizeof(*fileenum)); | |
| fileenum->file = file; | |
| list_add_tail(&scannedfiles, &fileenum->entry); | |
| for (i = 0; i < face_count; i++) { | |
| IDWriteLocalizedStrings *family_name = NULL; | |
| struct dwrite_font_data *font_data; | |
| struct fontface_desc desc; | |
| WCHAR familyW[255]; | |
| UINT32 index; | |
| desc.factory = factory; | |
| desc.face_type = face_type; | |
| desc.files = &file; | |
| desc.files_number = 1; | |
| desc.index = i; | |
| desc.simulations = DWRITE_FONT_SIMULATIONS_NONE; | |
| desc.font_data = NULL; | |
| /* alloc and init new font data structure */ | |
| hr = init_font_data(&desc, &family_name, &font_data); | |
| if (FAILED(hr)) { | |
| /* move to next one */ | |
| hr = S_OK; | |
| continue; | |
| } | |
| fontstrings_get_en_string(family_name, familyW, sizeof(familyW)/sizeof(WCHAR)); | |
| /* ignore dot named faces */ | |
| if (familyW[0] == '.') { | |
| WARN("Ignoring face %s\n", debugstr_w(familyW)); | |
| IDWriteLocalizedStrings_Release(family_name); | |
| release_font_data(font_data); | |
| continue; | |
| } | |
| index = collection_find_family(collection, familyW); | |
| if (index != ~0u) | |
| hr = fontfamily_add_font(collection->family_data[index], font_data); | |
| else { | |
| struct dwrite_fontfamily_data *family_data; | |
| /* create and init new family */ | |
| hr = init_fontfamily_data(family_name, &family_data); | |
| if (hr == S_OK) { | |
| /* add font to family, family - to collection */ | |
| hr = fontfamily_add_font(family_data, font_data); | |
| if (hr == S_OK) | |
| hr = fontcollection_add_family(collection, family_data); | |
| if (FAILED(hr)) | |
| release_fontfamily_data(family_data); | |
| } | |
| } | |
| IDWriteLocalizedStrings_Release(family_name); | |
| if (FAILED(hr)) | |
| break; | |
| } | |
| } | |
| LIST_FOR_EACH_ENTRY_SAFE(fileenum, fileenum2, &scannedfiles, struct fontfile_enum, entry) { | |
| IDWriteFontFile_Release(fileenum->file); | |
| list_remove(&fileenum->entry); | |
| heap_free(fileenum); | |
| } | |
| for (i = 0; i < collection->family_count; i++) { | |
| fontfamily_add_bold_simulated_face(collection->family_data[i]); | |
| fontfamily_add_oblique_simulated_face(collection->family_data[i]); | |
| } | |
| if (is_system) | |
| fontcollection_add_replacements(collection); | |
| collection->factory = factory; | |
| IDWriteFactory5_AddRef(factory); | |
| return hr; | |
| } | |
| struct system_fontfile_enumerator | |
| { | |
| IDWriteFontFileEnumerator IDWriteFontFileEnumerator_iface; | |
| LONG ref; | |
| IDWriteFactory5 *factory; | |
| HKEY hkey; | |
| int index; | |
| WCHAR *value; | |
| DWORD max_val_count; | |
| }; | |
| static inline struct system_fontfile_enumerator *impl_from_IDWriteFontFileEnumerator(IDWriteFontFileEnumerator* iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct system_fontfile_enumerator, IDWriteFontFileEnumerator_iface); | |
| } | |
| static HRESULT WINAPI systemfontfileenumerator_QueryInterface(IDWriteFontFileEnumerator *iface, REFIID riid, void **obj) | |
| { | |
| if (IsEqualIID(riid, &IID_IDWriteFontFileEnumerator) || IsEqualIID(riid, &IID_IUnknown)) { | |
| IDWriteFontFileEnumerator_AddRef(iface); | |
| *obj = iface; | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI systemfontfileenumerator_AddRef(IDWriteFontFileEnumerator *iface) | |
| { | |
| struct system_fontfile_enumerator *enumerator = impl_from_IDWriteFontFileEnumerator(iface); | |
| return InterlockedIncrement(&enumerator->ref); | |
| } | |
| static ULONG WINAPI systemfontfileenumerator_Release(IDWriteFontFileEnumerator *iface) | |
| { | |
| struct system_fontfile_enumerator *enumerator = impl_from_IDWriteFontFileEnumerator(iface); | |
| ULONG ref = InterlockedDecrement(&enumerator->ref); | |
| if (!ref) { | |
| IDWriteFactory5_Release(enumerator->factory); | |
| RegCloseKey(enumerator->hkey); | |
| heap_free(enumerator->value); | |
| heap_free(enumerator); | |
| } | |
| return ref; | |
| } | |
| static HRESULT create_local_file_reference(IDWriteFactory5 *factory, const WCHAR *filename, IDWriteFontFile **file) | |
| { | |
| HRESULT hr; | |
| /* Fonts installed in 'Fonts' system dir don't get full path in registry font files cache */ | |
| if (!strchrW(filename, '\\')) { | |
| static const WCHAR fontsW[] = {'\\','f','o','n','t','s','\\',0}; | |
| WCHAR fullpathW[MAX_PATH]; | |
| GetWindowsDirectoryW(fullpathW, sizeof(fullpathW)/sizeof(WCHAR)); | |
| strcatW(fullpathW, fontsW); | |
| strcatW(fullpathW, filename); | |
| hr = IDWriteFactory5_CreateFontFileReference(factory, fullpathW, NULL, file); | |
| } | |
| else | |
| hr = IDWriteFactory5_CreateFontFileReference(factory, filename, NULL, file); | |
| return hr; | |
| } | |
| static HRESULT WINAPI systemfontfileenumerator_GetCurrentFontFile(IDWriteFontFileEnumerator *iface, IDWriteFontFile **file) | |
| { | |
| struct system_fontfile_enumerator *enumerator = impl_from_IDWriteFontFileEnumerator(iface); | |
| DWORD ret, type, val_count, count; | |
| WCHAR *value, *filename; | |
| HRESULT hr; | |
| *file = NULL; | |
| if (enumerator->index < 0) | |
| return E_FAIL; | |
| ret = RegQueryInfoKeyW(enumerator->hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &val_count, &count, NULL, NULL); | |
| if (ret != ERROR_SUCCESS) | |
| return E_FAIL; | |
| val_count++; | |
| value = heap_alloc( val_count * sizeof(value[0]) ); | |
| filename = heap_alloc(count); | |
| if (!value || !filename) { | |
| heap_free(value); | |
| heap_free(filename); | |
| return E_OUTOFMEMORY; | |
| } | |
| ret = RegEnumValueW(enumerator->hkey, enumerator->index, value, &val_count, NULL, &type, (BYTE*)filename, &count); | |
| if (ret) { | |
| heap_free(value); | |
| heap_free(filename); | |
| return E_FAIL; | |
| } | |
| hr = create_local_file_reference(enumerator->factory, filename, file); | |
| heap_free(value); | |
| heap_free(filename); | |
| return hr; | |
| } | |
| static HRESULT WINAPI systemfontfileenumerator_MoveNext(IDWriteFontFileEnumerator *iface, BOOL *current) | |
| { | |
| struct system_fontfile_enumerator *enumerator = impl_from_IDWriteFontFileEnumerator(iface); | |
| *current = FALSE; | |
| enumerator->index++; | |
| if (!enumerator->value) { | |
| if (RegQueryInfoKeyW(enumerator->hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
| &enumerator->max_val_count, NULL, NULL, NULL)) | |
| return E_FAIL; | |
| enumerator->max_val_count++; | |
| if (!(enumerator->value = heap_alloc(enumerator->max_val_count * sizeof(*enumerator->value)))) | |
| return E_OUTOFMEMORY; | |
| } | |
| /* iterate until we find next string value */ | |
| for (;;) { | |
| DWORD type = 0, count, val_count; | |
| val_count = enumerator->max_val_count; | |
| *enumerator->value = 0; | |
| if (RegEnumValueW(enumerator->hkey, enumerator->index, enumerator->value, &val_count, | |
| NULL, &type, NULL, &count)) | |
| break; | |
| if (type == REG_SZ && *enumerator->value && *enumerator->value != '@') { | |
| *current = TRUE; | |
| break; | |
| } | |
| enumerator->index++; | |
| } | |
| TRACE("index = %d, current = %d\n", enumerator->index, *current); | |
| return S_OK; | |
| } | |
| static const struct IDWriteFontFileEnumeratorVtbl systemfontfileenumeratorvtbl = | |
| { | |
| systemfontfileenumerator_QueryInterface, | |
| systemfontfileenumerator_AddRef, | |
| systemfontfileenumerator_Release, | |
| systemfontfileenumerator_MoveNext, | |
| systemfontfileenumerator_GetCurrentFontFile | |
| }; | |
| static HRESULT create_system_fontfile_enumerator(IDWriteFactory5 *factory, IDWriteFontFileEnumerator **ret) | |
| { | |
| struct system_fontfile_enumerator *enumerator; | |
| static const WCHAR fontslistW[] = { | |
| 'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', | |
| 'W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', | |
| 'F','o','n','t','s',0 | |
| }; | |
| *ret = NULL; | |
| enumerator = heap_alloc(sizeof(*enumerator)); | |
| if (!enumerator) | |
| return E_OUTOFMEMORY; | |
| enumerator->IDWriteFontFileEnumerator_iface.lpVtbl = &systemfontfileenumeratorvtbl; | |
| enumerator->ref = 1; | |
| enumerator->factory = factory; | |
| enumerator->index = -1; | |
| enumerator->value = NULL; | |
| enumerator->max_val_count = 0; | |
| IDWriteFactory5_AddRef(factory); | |
| if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, fontslistW, 0, GENERIC_READ, &enumerator->hkey)) { | |
| ERR("failed to open fonts list key\n"); | |
| IDWriteFactory5_Release(factory); | |
| heap_free(enumerator); | |
| return E_FAIL; | |
| } | |
| *ret = &enumerator->IDWriteFontFileEnumerator_iface; | |
| return S_OK; | |
| } | |
| HRESULT get_system_fontcollection(IDWriteFactory5 *factory, IDWriteFontCollection1 **collection) | |
| { | |
| IDWriteFontFileEnumerator *enumerator; | |
| HRESULT hr; | |
| *collection = NULL; | |
| hr = create_system_fontfile_enumerator(factory, &enumerator); | |
| if (FAILED(hr)) | |
| return hr; | |
| TRACE("building system font collection for factory %p\n", factory); | |
| hr = create_font_collection(factory, enumerator, TRUE, collection); | |
| IDWriteFontFileEnumerator_Release(enumerator); | |
| return hr; | |
| } | |
| static HRESULT eudc_collection_add_family(IDWriteFactory5 *factory, struct dwrite_fontcollection *collection, | |
| const WCHAR *keynameW, const WCHAR *pathW) | |
| { | |
| static const WCHAR defaultfontW[] = {'S','y','s','t','e','m','D','e','f','a','u','l','t','E','U','D','C','F','o','n','t',0}; | |
| static const WCHAR emptyW[] = {0}; | |
| IDWriteLocalizedStrings *names; | |
| DWRITE_FONT_FACE_TYPE face_type; | |
| DWRITE_FONT_FILE_TYPE file_type; | |
| BOOL supported; | |
| UINT32 face_count, i; | |
| IDWriteFontFile *file; | |
| HRESULT hr; | |
| struct dwrite_fontfamily_data *family_data; | |
| /* create font file from this path */ | |
| hr = create_local_file_reference(factory, pathW, &file); | |
| if (FAILED(hr)) | |
| return S_FALSE; | |
| /* failed font files are skipped */ | |
| hr = IDWriteFontFile_Analyze(file, &supported, &file_type, &face_type, &face_count); | |
| if (FAILED(hr) || !supported || face_count == 0) { | |
| TRACE("unsupported font (%p, 0x%08x, %d, %u)\n", file, hr, supported, face_count); | |
| IDWriteFontFile_Release(file); | |
| return S_FALSE; | |
| } | |
| /* create and init new family */ | |
| /* Family names are added for non-specific locale, represented with empty string. | |
| Default family appears with empty family name. */ | |
| create_localizedstrings(&names); | |
| if (!strcmpiW(keynameW, defaultfontW)) | |
| add_localizedstring(names, emptyW, emptyW); | |
| else | |
| add_localizedstring(names, emptyW, keynameW); | |
| hr = init_fontfamily_data(names, &family_data); | |
| IDWriteLocalizedStrings_Release(names); | |
| if (hr != S_OK) { | |
| IDWriteFontFile_Release(file); | |
| return hr; | |
| } | |
| /* fill with faces */ | |
| for (i = 0; i < face_count; i++) { | |
| struct dwrite_font_data *font_data; | |
| struct fontface_desc desc; | |
| /* alloc and init new font data structure */ | |
| desc.factory = factory; | |
| desc.face_type = face_type; | |
| desc.index = i; | |
| desc.files = &file; | |
| desc.files_number = 1; | |
| desc.simulations = DWRITE_FONT_SIMULATIONS_NONE; | |
| desc.font_data = NULL; | |
| hr = init_font_data(&desc, &names, &font_data); | |
| if (FAILED(hr)) | |
| continue; | |
| IDWriteLocalizedStrings_Release(names); | |
| /* add font to family */ | |
| hr = fontfamily_add_font(family_data, font_data); | |
| if (hr != S_OK) | |
| release_font_data(font_data); | |
| } | |
| /* add family to collection */ | |
| hr = fontcollection_add_family(collection, family_data); | |
| if (FAILED(hr)) | |
| release_fontfamily_data(family_data); | |
| IDWriteFontFile_Release(file); | |
| return hr; | |
| } | |
| HRESULT get_eudc_fontcollection(IDWriteFactory5 *factory, IDWriteFontCollection1 **ret) | |
| { | |
| static const WCHAR eudckeyfmtW[] = {'E','U','D','C','\\','%','u',0}; | |
| struct dwrite_fontcollection *collection; | |
| static const WCHAR emptyW[] = {0}; | |
| WCHAR eudckeypathW[16]; | |
| HKEY eudckey; | |
| DWORD index; | |
| BOOL exists; | |
| LONG retval; | |
| HRESULT hr; | |
| UINT32 i; | |
| TRACE("building EUDC font collection for factory %p, ACP %u\n", factory, GetACP()); | |
| *ret = NULL; | |
| collection = heap_alloc(sizeof(struct dwrite_fontcollection)); | |
| if (!collection) return E_OUTOFMEMORY; | |
| hr = init_font_collection(collection, FALSE); | |
| if (FAILED(hr)) { | |
| heap_free(collection); | |
| return hr; | |
| } | |
| *ret = &collection->IDWriteFontCollection1_iface; | |
| collection->factory = factory; | |
| IDWriteFactory5_AddRef(factory); | |
| /* return empty collection if EUDC fonts are not configured */ | |
| sprintfW(eudckeypathW, eudckeyfmtW, GetACP()); | |
| if (RegOpenKeyExW(HKEY_CURRENT_USER, eudckeypathW, 0, GENERIC_READ, &eudckey)) | |
| return S_OK; | |
| retval = ERROR_SUCCESS; | |
| index = 0; | |
| while (retval != ERROR_NO_MORE_ITEMS) { | |
| WCHAR keynameW[64], pathW[MAX_PATH]; | |
| DWORD type, path_len, name_len; | |
| path_len = sizeof(pathW)/sizeof(*pathW); | |
| name_len = sizeof(keynameW)/sizeof(*keynameW); | |
| retval = RegEnumValueW(eudckey, index++, keynameW, &name_len, NULL, &type, (BYTE*)pathW, &path_len); | |
| if (retval || type != REG_SZ) | |
| continue; | |
| hr = eudc_collection_add_family(factory, collection, keynameW, pathW); | |
| if (hr != S_OK) | |
| WARN("failed to add family %s, path %s\n", debugstr_w(keynameW), debugstr_w(pathW)); | |
| } | |
| RegCloseKey(eudckey); | |
| /* try to add global default if not defined for specific codepage */ | |
| exists = FALSE; | |
| hr = IDWriteFontCollection1_FindFamilyName(&collection->IDWriteFontCollection1_iface, emptyW, | |
| &index, &exists); | |
| if (FAILED(hr) || !exists) { | |
| const WCHAR globaldefaultW[] = {'E','U','D','C','.','T','T','E',0}; | |
| hr = eudc_collection_add_family(factory, collection, emptyW, globaldefaultW); | |
| if (hr != S_OK) | |
| WARN("failed to add global default EUDC font, 0x%08x\n", hr); | |
| } | |
| /* EUDC collection offers simulated faces too */ | |
| for (i = 0; i < collection->family_count; i++) { | |
| fontfamily_add_bold_simulated_face(collection->family_data[i]); | |
| fontfamily_add_oblique_simulated_face(collection->family_data[i]); | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefontfile_QueryInterface(IDWriteFontFile *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_fontfile *This = impl_from_IDWriteFontFile(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteFontFile)) | |
| { | |
| *obj = iface; | |
| IDWriteFontFile_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI dwritefontfile_AddRef(IDWriteFontFile *iface) | |
| { | |
| struct dwrite_fontfile *This = impl_from_IDWriteFontFile(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI dwritefontfile_Release(IDWriteFontFile *iface) | |
| { | |
| struct dwrite_fontfile *This = impl_from_IDWriteFontFile(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| if (!ref) | |
| { | |
| IDWriteFontFileLoader_Release(This->loader); | |
| if (This->stream) IDWriteFontFileStream_Release(This->stream); | |
| heap_free(This->reference_key); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI dwritefontfile_GetReferenceKey(IDWriteFontFile *iface, const void **fontFileReferenceKey, UINT32 *fontFileReferenceKeySize) | |
| { | |
| struct dwrite_fontfile *This = impl_from_IDWriteFontFile(iface); | |
| TRACE("(%p)->(%p, %p)\n", This, fontFileReferenceKey, fontFileReferenceKeySize); | |
| *fontFileReferenceKey = This->reference_key; | |
| *fontFileReferenceKeySize = This->key_size; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefontfile_GetLoader(IDWriteFontFile *iface, IDWriteFontFileLoader **fontFileLoader) | |
| { | |
| struct dwrite_fontfile *This = impl_from_IDWriteFontFile(iface); | |
| TRACE("(%p)->(%p)\n", This, fontFileLoader); | |
| *fontFileLoader = This->loader; | |
| IDWriteFontFileLoader_AddRef(This->loader); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI dwritefontfile_Analyze(IDWriteFontFile *iface, BOOL *isSupportedFontType, DWRITE_FONT_FILE_TYPE *fontFileType, | |
| DWRITE_FONT_FACE_TYPE *fontFaceType, UINT32 *numberOfFaces) | |
| { | |
| struct dwrite_fontfile *This = impl_from_IDWriteFontFile(iface); | |
| IDWriteFontFileStream *stream; | |
| HRESULT hr; | |
| TRACE("(%p)->(%p, %p, %p, %p)\n", This, isSupportedFontType, fontFileType, fontFaceType, numberOfFaces); | |
| *isSupportedFontType = FALSE; | |
| *fontFileType = DWRITE_FONT_FILE_TYPE_UNKNOWN; | |
| if (fontFaceType) | |
| *fontFaceType = DWRITE_FONT_FACE_TYPE_UNKNOWN; | |
| *numberOfFaces = 0; | |
| hr = IDWriteFontFileLoader_CreateStreamFromKey(This->loader, This->reference_key, This->key_size, &stream); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = opentype_analyze_font(stream, numberOfFaces, fontFileType, fontFaceType, isSupportedFontType); | |
| /* TODO: Further Analysis */ | |
| IDWriteFontFileStream_Release(stream); | |
| return S_OK; | |
| } | |
| static const IDWriteFontFileVtbl dwritefontfilevtbl = { | |
| dwritefontfile_QueryInterface, | |
| dwritefontfile_AddRef, | |
| dwritefontfile_Release, | |
| dwritefontfile_GetReferenceKey, | |
| dwritefontfile_GetLoader, | |
| dwritefontfile_Analyze, | |
| }; | |
| HRESULT create_font_file(IDWriteFontFileLoader *loader, const void *reference_key, UINT32 key_size, | |
| IDWriteFontFile **ret) | |
| { | |
| struct dwrite_fontfile *file; | |
| void *key; | |
| *ret = NULL; | |
| file = heap_alloc(sizeof(*file)); | |
| key = heap_alloc(key_size); | |
| if (!file || !key) { | |
| heap_free(file); | |
| heap_free(key); | |
| return E_OUTOFMEMORY; | |
| } | |
| file->IDWriteFontFile_iface.lpVtbl = &dwritefontfilevtbl; | |
| file->ref = 1; | |
| IDWriteFontFileLoader_AddRef(loader); | |
| file->loader = loader; | |
| file->stream = NULL; | |
| file->reference_key = key; | |
| memcpy(file->reference_key, reference_key, key_size); | |
| file->key_size = key_size; | |
| *ret = &file->IDWriteFontFile_iface; | |
| return S_OK; | |
| } | |
| static HRESULT get_stream_from_file(IDWriteFontFile *file, IDWriteFontFileStream **stream) | |
| { | |
| IDWriteFontFileLoader *loader; | |
| UINT32 key_size; | |
| const void *key; | |
| HRESULT hr; | |
| *stream = NULL; | |
| hr = IDWriteFontFile_GetLoader(file, &loader); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFontFile_GetReferenceKey(file, &key, &key_size); | |
| if (FAILED(hr)) { | |
| IDWriteFontFileLoader_Release(loader); | |
| return hr; | |
| } | |
| hr = IDWriteFontFileLoader_CreateStreamFromKey(loader, key, key_size, stream); | |
| IDWriteFontFileLoader_Release(loader); | |
| return hr; | |
| } | |
| HRESULT create_fontface(const struct fontface_desc *desc, struct list *cached_list, IDWriteFontFace4 **ret) | |
| { | |
| struct file_stream_desc stream_desc; | |
| struct dwrite_fontface *fontface; | |
| HRESULT hr = S_OK; | |
| BOOL is_symbol; | |
| int i; | |
| *ret = NULL; | |
| fontface = heap_alloc_zero(sizeof(struct dwrite_fontface)); | |
| if (!fontface) | |
| return E_OUTOFMEMORY; | |
| fontface->files = heap_alloc_zero(sizeof(*fontface->files) * desc->files_number); | |
| fontface->streams = heap_alloc_zero(sizeof(*fontface->streams) * desc->files_number); | |
| if (!fontface->files || !fontface->streams) { | |
| heap_free(fontface->files); | |
| heap_free(fontface->streams); | |
| heap_free(fontface); | |
| return E_OUTOFMEMORY; | |
| } | |
| fontface->IDWriteFontFace4_iface.lpVtbl = &dwritefontfacevtbl; | |
| fontface->ref = 1; | |
| fontface->type = desc->face_type; | |
| fontface->file_count = desc->files_number; | |
| fontface->cmap.exists = TRUE; | |
| fontface->vdmx.exists = TRUE; | |
| fontface->gasp.exists = TRUE; | |
| fontface->cpal.exists = TRUE; | |
| fontface->colr.exists = TRUE; | |
| fontface->index = desc->index; | |
| fontface->simulations = desc->simulations; | |
| IDWriteFactory5_AddRef(fontface->factory = desc->factory); | |
| for (i = 0; i < fontface->file_count; i++) { | |
| hr = get_stream_from_file(desc->files[i], &fontface->streams[i]); | |
| if (FAILED(hr)) { | |
| IDWriteFontFace4_Release(&fontface->IDWriteFontFace4_iface); | |
| return hr; | |
| } | |
| fontface->files[i] = desc->files[i]; | |
| IDWriteFontFile_AddRef(fontface->files[i]); | |
| } | |
| stream_desc.stream = fontface->streams[0]; | |
| stream_desc.face_type = desc->face_type; | |
| stream_desc.face_index = desc->index; | |
| opentype_get_font_metrics(&stream_desc, &fontface->metrics, &fontface->caret); | |
| if (desc->simulations & DWRITE_FONT_SIMULATIONS_OBLIQUE) { | |
| /* TODO: test what happens if caret is already slanted */ | |
| if (fontface->caret.slopeRise == 1) { | |
| fontface->caret.slopeRise = fontface->metrics.designUnitsPerEm; | |
| fontface->caret.slopeRun = fontface->caret.slopeRise / 3; | |
| } | |
| } | |
| fontface->charmap = freetype_get_charmap_index(&fontface->IDWriteFontFace4_iface, &is_symbol); | |
| if (is_symbol) | |
| fontface->flags |= FONTFACE_IS_SYMBOL; | |
| if (freetype_has_kerning_pairs(&fontface->IDWriteFontFace4_iface)) | |
| fontface->flags |= FONTFACE_HAS_KERNING_PAIRS; | |
| if (freetype_is_monospaced(&fontface->IDWriteFontFace4_iface)) | |
| fontface->flags |= FONTFACE_IS_MONOSPACED; | |
| if (opentype_has_vertical_variants(&fontface->IDWriteFontFace4_iface)) | |
| fontface->flags |= FONTFACE_HAS_VERTICAL_VARIANTS; | |
| fontface->glyph_image_formats = opentype_get_glyph_image_formats(&fontface->IDWriteFontFace4_iface); | |
| /* Font properties are reused from font object when 'normal' face creation path is used: | |
| collection -> family -> matching font -> fontface. | |
| If face is created directly from factory we have to go through properties resolution. | |
| */ | |
| if (desc->font_data) { | |
| fontface->weight = desc->font_data->weight; | |
| fontface->style = desc->font_data->style; | |
| fontface->stretch = desc->font_data->stretch; | |
| fontface->panose = desc->font_data->panose; | |
| fontface->fontsig = desc->font_data->fontsig; | |
| fontface->lf = desc->font_data->lf; | |
| } | |
| else { | |
| IDWriteLocalizedStrings *names; | |
| struct dwrite_font_data *data; | |
| hr = init_font_data(desc, &names, &data); | |
| if (FAILED(hr)) { | |
| IDWriteFontFace4_Release(&fontface->IDWriteFontFace4_iface); | |
| return hr; | |
| } | |
| fontface->weight = data->weight; | |
| fontface->style = data->style; | |
| fontface->stretch = data->stretch; | |
| fontface->panose = data->panose; | |
| fontface->fontsig = data->fontsig; | |
| fontface->lf = data->lf; | |
| IDWriteLocalizedStrings_Release(names); | |
| release_font_data(data); | |
| } | |
| fontface->cached = factory_cache_fontface(fontface->factory, cached_list, &fontface->IDWriteFontFace4_iface); | |
| *ret = &fontface->IDWriteFontFace4_iface; | |
| return S_OK; | |
| } | |
| /* IDWriteLocalFontFileLoader and its required IDWriteFontFileStream */ | |
| struct local_refkey | |
| { | |
| FILETIME writetime; | |
| WCHAR name[1]; | |
| }; | |
| struct local_cached_stream | |
| { | |
| struct list entry; | |
| IDWriteFontFileStream *stream; | |
| struct local_refkey *key; | |
| UINT32 key_size; | |
| }; | |
| struct dwrite_localfontfilestream | |
| { | |
| IDWriteFontFileStream IDWriteFontFileStream_iface; | |
| LONG ref; | |
| struct local_cached_stream *entry; | |
| const void *file_ptr; | |
| UINT64 size; | |
| }; | |
| struct dwrite_localfontfileloader { | |
| IDWriteLocalFontFileLoader IDWriteLocalFontFileLoader_iface; | |
| LONG ref; | |
| struct list streams; | |
| CRITICAL_SECTION cs; | |
| }; | |
| static struct dwrite_localfontfileloader local_fontfile_loader; | |
| struct dwrite_inmemory_stream_data | |
| { | |
| LONG ref; | |
| IUnknown *owner; | |
| void *data; | |
| UINT32 size; | |
| }; | |
| struct dwrite_inmemory_filestream | |
| { | |
| IDWriteFontFileStream IDWriteFontFileStream_iface; | |
| LONG ref; | |
| struct dwrite_inmemory_stream_data *data; | |
| }; | |
| struct dwrite_inmemory_fileloader | |
| { | |
| IDWriteInMemoryFontFileLoader IDWriteInMemoryFontFileLoader_iface; | |
| LONG ref; | |
| struct dwrite_inmemory_stream_data **streams; | |
| UINT32 filecount; | |
| UINT32 capacity; | |
| }; | |
| static inline struct dwrite_localfontfileloader *impl_from_IDWriteLocalFontFileLoader(IDWriteLocalFontFileLoader *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_localfontfileloader, IDWriteLocalFontFileLoader_iface); | |
| } | |
| static inline struct dwrite_localfontfilestream *impl_from_IDWriteFontFileStream(IDWriteFontFileStream *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_localfontfilestream, IDWriteFontFileStream_iface); | |
| } | |
| static inline struct dwrite_inmemory_fileloader *impl_from_IDWriteInMemoryFontFileLoader(IDWriteInMemoryFontFileLoader *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_inmemory_fileloader, IDWriteInMemoryFontFileLoader_iface); | |
| } | |
| static inline struct dwrite_inmemory_filestream *inmemory_impl_from_IDWriteFontFileStream(IDWriteFontFileStream *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct dwrite_inmemory_filestream, IDWriteFontFileStream_iface); | |
| } | |
| static void release_inmemory_stream(struct dwrite_inmemory_stream_data *stream) | |
| { | |
| if (InterlockedDecrement(&stream->ref) == 0) { | |
| if (stream->owner) | |
| IUnknown_Release(stream->owner); | |
| else | |
| heap_free(stream->data); | |
| heap_free(stream); | |
| } | |
| } | |
| static HRESULT WINAPI localfontfilestream_QueryInterface(IDWriteFontFileStream *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_localfontfilestream *This = impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFontFileStream) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| if (InterlockedIncrement(&This->ref) == 1) { | |
| InterlockedDecrement(&This->ref); | |
| *obj = NULL; | |
| return E_FAIL; | |
| } | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI localfontfilestream_AddRef(IDWriteFontFileStream *iface) | |
| { | |
| struct dwrite_localfontfilestream *This = impl_from_IDWriteFontFileStream(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE_(dwrite_file)("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static inline void release_cached_stream(struct local_cached_stream *stream) | |
| { | |
| list_remove(&stream->entry); | |
| heap_free(stream->key); | |
| heap_free(stream); | |
| } | |
| static ULONG WINAPI localfontfilestream_Release(IDWriteFontFileStream *iface) | |
| { | |
| struct dwrite_localfontfilestream *This = impl_from_IDWriteFontFileStream(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE_(dwrite_file)("(%p)->(%d)\n", This, ref); | |
| if (!ref) { | |
| UnmapViewOfFile(This->file_ptr); | |
| EnterCriticalSection(&local_fontfile_loader.cs); | |
| release_cached_stream(This->entry); | |
| LeaveCriticalSection(&local_fontfile_loader.cs); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI localfontfilestream_ReadFileFragment(IDWriteFontFileStream *iface, void const **fragment_start, | |
| UINT64 offset, UINT64 fragment_size, void **fragment_context) | |
| { | |
| struct dwrite_localfontfilestream *This = impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%p, 0x%s, 0x%s, %p)\n", This, fragment_start, | |
| wine_dbgstr_longlong(offset), wine_dbgstr_longlong(fragment_size), fragment_context); | |
| *fragment_context = NULL; | |
| if ((offset >= This->size - 1) || (fragment_size > This->size - offset)) { | |
| *fragment_start = NULL; | |
| return E_FAIL; | |
| } | |
| *fragment_start = (char*)This->file_ptr + offset; | |
| return S_OK; | |
| } | |
| static void WINAPI localfontfilestream_ReleaseFileFragment(IDWriteFontFileStream *iface, void *fragment_context) | |
| { | |
| struct dwrite_localfontfilestream *This = impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%p)\n", This, fragment_context); | |
| } | |
| static HRESULT WINAPI localfontfilestream_GetFileSize(IDWriteFontFileStream *iface, UINT64 *size) | |
| { | |
| struct dwrite_localfontfilestream *This = impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%p)\n", This, size); | |
| *size = This->size; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI localfontfilestream_GetLastWriteTime(IDWriteFontFileStream *iface, UINT64 *last_writetime) | |
| { | |
| struct dwrite_localfontfilestream *This = impl_from_IDWriteFontFileStream(iface); | |
| ULARGE_INTEGER li; | |
| TRACE_(dwrite_file)("(%p)->(%p)\n", This, last_writetime); | |
| li.u.LowPart = This->entry->key->writetime.dwLowDateTime; | |
| li.u.HighPart = This->entry->key->writetime.dwHighDateTime; | |
| *last_writetime = li.QuadPart; | |
| return S_OK; | |
| } | |
| static const IDWriteFontFileStreamVtbl localfontfilestreamvtbl = | |
| { | |
| localfontfilestream_QueryInterface, | |
| localfontfilestream_AddRef, | |
| localfontfilestream_Release, | |
| localfontfilestream_ReadFileFragment, | |
| localfontfilestream_ReleaseFileFragment, | |
| localfontfilestream_GetFileSize, | |
| localfontfilestream_GetLastWriteTime | |
| }; | |
| static HRESULT create_localfontfilestream(const void *file_ptr, UINT64 size, struct local_cached_stream *entry, IDWriteFontFileStream **ret) | |
| { | |
| struct dwrite_localfontfilestream *This; | |
| *ret = NULL; | |
| This = heap_alloc(sizeof(struct dwrite_localfontfilestream)); | |
| if (!This) | |
| return E_OUTOFMEMORY; | |
| This->IDWriteFontFileStream_iface.lpVtbl = &localfontfilestreamvtbl; | |
| This->ref = 1; | |
| This->file_ptr = file_ptr; | |
| This->size = size; | |
| This->entry = entry; | |
| *ret = &This->IDWriteFontFileStream_iface; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI localfontfileloader_QueryInterface(IDWriteLocalFontFileLoader *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_localfontfileloader *This = impl_from_IDWriteLocalFontFileLoader(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteLocalFontFileLoader) || | |
| IsEqualIID(riid, &IID_IDWriteFontFileLoader) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| IDWriteLocalFontFileLoader_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI localfontfileloader_AddRef(IDWriteLocalFontFileLoader *iface) | |
| { | |
| struct dwrite_localfontfileloader *This = impl_from_IDWriteLocalFontFileLoader(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI localfontfileloader_Release(IDWriteLocalFontFileLoader *iface) | |
| { | |
| struct dwrite_localfontfileloader *This = impl_from_IDWriteLocalFontFileLoader(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static HRESULT create_local_cached_stream(const void *key, UINT32 key_size, struct local_cached_stream **ret) | |
| { | |
| const struct local_refkey *refkey = key; | |
| struct local_cached_stream *stream; | |
| IDWriteFontFileStream *filestream; | |
| HANDLE file, mapping; | |
| LARGE_INTEGER size; | |
| void *file_ptr; | |
| HRESULT hr = S_OK; | |
| *ret = NULL; | |
| file = CreateFileW(refkey->name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, | |
| NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
| if (file == INVALID_HANDLE_VALUE) { | |
| WARN_(dwrite_file)("Failed to open the file %s, error %d.\n", debugstr_w(refkey->name), GetLastError()); | |
| return E_FAIL; | |
| } | |
| GetFileSizeEx(file, &size); | |
| mapping = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL); | |
| CloseHandle(file); | |
| if (!mapping) | |
| return E_FAIL; | |
| file_ptr = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); | |
| CloseHandle(mapping); | |
| if (!file_ptr) { | |
| ERR("mapping failed, file size %s, error %d\n", wine_dbgstr_longlong(size.QuadPart), GetLastError()); | |
| return E_FAIL; | |
| } | |
| stream = heap_alloc(sizeof(*stream)); | |
| if (!stream) { | |
| UnmapViewOfFile(file_ptr); | |
| return E_OUTOFMEMORY; | |
| } | |
| stream->key = heap_alloc(key_size); | |
| if (!stream->key) { | |
| UnmapViewOfFile(file_ptr); | |
| heap_free(stream); | |
| return E_OUTOFMEMORY; | |
| } | |
| stream->key_size = key_size; | |
| memcpy(stream->key, key, key_size); | |
| hr = create_localfontfilestream(file_ptr, size.QuadPart, stream, &filestream); | |
| if (FAILED(hr)) { | |
| UnmapViewOfFile(file_ptr); | |
| heap_free(stream->key); | |
| heap_free(stream); | |
| return hr; | |
| } | |
| stream->stream = filestream; | |
| *ret = stream; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI localfontfileloader_CreateStreamFromKey(IDWriteLocalFontFileLoader *iface, const void *key, | |
| UINT32 key_size, IDWriteFontFileStream **ret) | |
| { | |
| struct dwrite_localfontfileloader *This = impl_from_IDWriteLocalFontFileLoader(iface); | |
| const struct local_refkey *refkey = key; | |
| struct local_cached_stream *stream; | |
| HRESULT hr = S_OK; | |
| TRACE("(%p)->(%p, %u, %p)\n", This, key, key_size, ret); | |
| TRACE("name: %s\n", debugstr_w(refkey->name)); | |
| EnterCriticalSection(&This->cs); | |
| *ret = NULL; | |
| /* search cache first */ | |
| LIST_FOR_EACH_ENTRY(stream, &This->streams, struct local_cached_stream, entry) { | |
| if (key_size == stream->key_size && !memcmp(stream->key, key, key_size)) { | |
| IDWriteFontFileStream_QueryInterface(stream->stream, &IID_IDWriteFontFileStream, (void **)ret); | |
| break; | |
| } | |
| } | |
| if (*ret == NULL && (hr = create_local_cached_stream(key, key_size, &stream)) == S_OK) { | |
| list_add_head(&This->streams, &stream->entry); | |
| *ret = stream->stream; | |
| } | |
| LeaveCriticalSection(&This->cs); | |
| return hr; | |
| } | |
| static HRESULT WINAPI localfontfileloader_GetFilePathLengthFromKey(IDWriteLocalFontFileLoader *iface, void const *key, UINT32 key_size, UINT32 *length) | |
| { | |
| struct dwrite_localfontfileloader *This = impl_from_IDWriteLocalFontFileLoader(iface); | |
| const struct local_refkey *refkey = key; | |
| TRACE("(%p)->(%p, %i, %p)\n", This, key, key_size, length); | |
| *length = strlenW(refkey->name); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI localfontfileloader_GetFilePathFromKey(IDWriteLocalFontFileLoader *iface, void const *key, UINT32 key_size, WCHAR *path, UINT32 length) | |
| { | |
| struct dwrite_localfontfileloader *This = impl_from_IDWriteLocalFontFileLoader(iface); | |
| const struct local_refkey *refkey = key; | |
| TRACE("(%p)->(%p, %i, %p, %i)\n", This, key, key_size, path, length); | |
| if (length < strlenW(refkey->name)) | |
| return E_INVALIDARG; | |
| strcpyW(path, refkey->name); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI localfontfileloader_GetLastWriteTimeFromKey(IDWriteLocalFontFileLoader *iface, void const *key, | |
| UINT32 key_size, FILETIME *writetime) | |
| { | |
| struct dwrite_localfontfileloader *This = impl_from_IDWriteLocalFontFileLoader(iface); | |
| const struct local_refkey *refkey = key; | |
| TRACE("(%p)->(%p, %u, %p)\n", This, key, key_size, writetime); | |
| *writetime = refkey->writetime; | |
| return S_OK; | |
| } | |
| static const struct IDWriteLocalFontFileLoaderVtbl localfontfileloadervtbl = { | |
| localfontfileloader_QueryInterface, | |
| localfontfileloader_AddRef, | |
| localfontfileloader_Release, | |
| localfontfileloader_CreateStreamFromKey, | |
| localfontfileloader_GetFilePathLengthFromKey, | |
| localfontfileloader_GetFilePathFromKey, | |
| localfontfileloader_GetLastWriteTimeFromKey | |
| }; | |
| void init_local_fontfile_loader(void) | |
| { | |
| local_fontfile_loader.IDWriteLocalFontFileLoader_iface.lpVtbl = &localfontfileloadervtbl; | |
| local_fontfile_loader.ref = 1; | |
| list_init(&local_fontfile_loader.streams); | |
| InitializeCriticalSection(&local_fontfile_loader.cs); | |
| local_fontfile_loader.cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": localfileloader.lock"); | |
| } | |
| IDWriteFontFileLoader *get_local_fontfile_loader(void) | |
| { | |
| return (IDWriteFontFileLoader *)&local_fontfile_loader.IDWriteLocalFontFileLoader_iface; | |
| } | |
| HRESULT get_local_refkey(const WCHAR *path, const FILETIME *writetime, void **key, UINT32 *size) | |
| { | |
| struct local_refkey *refkey; | |
| if (!path) | |
| return E_INVALIDARG; | |
| *size = FIELD_OFFSET(struct local_refkey, name) + (strlenW(path)+1)*sizeof(WCHAR); | |
| *key = NULL; | |
| refkey = heap_alloc(*size); | |
| if (!refkey) | |
| return E_OUTOFMEMORY; | |
| if (writetime) | |
| refkey->writetime = *writetime; | |
| else { | |
| WIN32_FILE_ATTRIBUTE_DATA info; | |
| if (GetFileAttributesExW(path, GetFileExInfoStandard, &info)) | |
| refkey->writetime = info.ftLastWriteTime; | |
| else | |
| memset(&refkey->writetime, 0, sizeof(refkey->writetime)); | |
| } | |
| strcpyW(refkey->name, path); | |
| *key = refkey; | |
| return S_OK; | |
| } | |
| /* IDWriteGlyphRunAnalysis */ | |
| static HRESULT WINAPI glyphrunanalysis_QueryInterface(IDWriteGlyphRunAnalysis *iface, REFIID riid, void **ppv) | |
| { | |
| struct dwrite_glyphrunanalysis *This = impl_from_IDWriteGlyphRunAnalysis(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); | |
| if (IsEqualIID(riid, &IID_IDWriteGlyphRunAnalysis) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *ppv = iface; | |
| IDWriteGlyphRunAnalysis_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *ppv = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI glyphrunanalysis_AddRef(IDWriteGlyphRunAnalysis *iface) | |
| { | |
| struct dwrite_glyphrunanalysis *This = impl_from_IDWriteGlyphRunAnalysis(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI glyphrunanalysis_Release(IDWriteGlyphRunAnalysis *iface) | |
| { | |
| struct dwrite_glyphrunanalysis *This = impl_from_IDWriteGlyphRunAnalysis(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| if (!ref) { | |
| if (This->run.fontFace) | |
| IDWriteFontFace_Release(This->run.fontFace); | |
| heap_free(This->glyphs); | |
| heap_free(This->origins); | |
| heap_free(This->bitmap); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static BOOL is_natural_rendering_mode(DWRITE_RENDERING_MODE1 mode) | |
| { | |
| switch (mode) | |
| { | |
| case DWRITE_RENDERING_MODE1_NATURAL: | |
| case DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC: | |
| case DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC_DOWNSAMPLED: | |
| return TRUE; | |
| default: | |
| return FALSE; | |
| } | |
| } | |
| static UINT32 get_glyph_bitmap_pitch(DWRITE_RENDERING_MODE1 rendering_mode, INT width) | |
| { | |
| return rendering_mode == DWRITE_RENDERING_MODE1_ALIASED ? ((width + 31) >> 5) << 2 : (width + 3) / 4 * 4; | |
| } | |
| static void glyphrunanalysis_get_texturebounds(struct dwrite_glyphrunanalysis *analysis, RECT *bounds) | |
| { | |
| struct dwrite_glyphbitmap glyph_bitmap; | |
| IDWriteFontFace4 *fontface; | |
| HRESULT hr; | |
| UINT32 i; | |
| if (analysis->flags & RUNANALYSIS_BOUNDS_READY) { | |
| *bounds = analysis->bounds; | |
| return; | |
| } | |
| if (analysis->run.isSideways) | |
| FIXME("sideways runs are not supported.\n"); | |
| hr = IDWriteFontFace_QueryInterface(analysis->run.fontFace, &IID_IDWriteFontFace4, (void **)&fontface); | |
| if (FAILED(hr)) | |
| WARN("failed to get IDWriteFontFace4, 0x%08x\n", hr); | |
| memset(&glyph_bitmap, 0, sizeof(glyph_bitmap)); | |
| glyph_bitmap.fontface = fontface; | |
| glyph_bitmap.simulations = IDWriteFontFace4_GetSimulations(fontface); | |
| glyph_bitmap.emsize = analysis->run.fontEmSize; | |
| glyph_bitmap.nohint = is_natural_rendering_mode(analysis->rendering_mode); | |
| if (analysis->flags & RUNANALYSIS_USE_TRANSFORM) | |
| glyph_bitmap.m = &analysis->m; | |
| for (i = 0; i < analysis->run.glyphCount; i++) { | |
| RECT *bbox = &glyph_bitmap.bbox; | |
| UINT32 bitmap_size; | |
| glyph_bitmap.index = analysis->run.glyphIndices[i]; | |
| freetype_get_glyph_bbox(&glyph_bitmap); | |
| bitmap_size = get_glyph_bitmap_pitch(analysis->rendering_mode, bbox->right - bbox->left) * | |
| (bbox->bottom - bbox->top); | |
| if (bitmap_size > analysis->max_glyph_bitmap_size) | |
| analysis->max_glyph_bitmap_size = bitmap_size; | |
| OffsetRect(bbox, analysis->origins[i].x, analysis->origins[i].y); | |
| UnionRect(&analysis->bounds, &analysis->bounds, bbox); | |
| } | |
| IDWriteFontFace4_Release(fontface); | |
| analysis->flags |= RUNANALYSIS_BOUNDS_READY; | |
| *bounds = analysis->bounds; | |
| } | |
| static HRESULT WINAPI glyphrunanalysis_GetAlphaTextureBounds(IDWriteGlyphRunAnalysis *iface, DWRITE_TEXTURE_TYPE type, RECT *bounds) | |
| { | |
| struct dwrite_glyphrunanalysis *This = impl_from_IDWriteGlyphRunAnalysis(iface); | |
| TRACE("(%p)->(%d %p)\n", This, type, bounds); | |
| if ((UINT32)type > DWRITE_TEXTURE_CLEARTYPE_3x1) { | |
| SetRectEmpty(bounds); | |
| return E_INVALIDARG; | |
| } | |
| if (type != This->texture_type) { | |
| SetRectEmpty(bounds); | |
| return S_OK; | |
| } | |
| glyphrunanalysis_get_texturebounds(This, bounds); | |
| return S_OK; | |
| } | |
| static inline int get_dib_stride( int width, int bpp ) | |
| { | |
| return ((width * bpp + 31) >> 3) & ~3; | |
| } | |
| static inline BYTE *get_pixel_ptr(BYTE *ptr, DWRITE_TEXTURE_TYPE type, const RECT *runbounds, const RECT *bounds) | |
| { | |
| if (type == DWRITE_TEXTURE_CLEARTYPE_3x1) | |
| return ptr + (runbounds->top - bounds->top) * (bounds->right - bounds->left) * 3 + | |
| (runbounds->left - bounds->left) * 3; | |
| else | |
| return ptr + (runbounds->top - bounds->top) * (bounds->right - bounds->left) + | |
| runbounds->left - bounds->left; | |
| } | |
| static HRESULT glyphrunanalysis_render(struct dwrite_glyphrunanalysis *analysis) | |
| { | |
| static const BYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; | |
| struct dwrite_glyphbitmap glyph_bitmap; | |
| IDWriteFontFace4 *fontface; | |
| D2D_POINT_2F origin; | |
| UINT32 i, size; | |
| HRESULT hr; | |
| RECT *bbox; | |
| hr = IDWriteFontFace_QueryInterface(analysis->run.fontFace, &IID_IDWriteFontFace4, (void **)&fontface); | |
| if (FAILED(hr)) { | |
| WARN("failed to get IDWriteFontFace4, 0x%08x\n", hr); | |
| return hr; | |
| } | |
| size = (analysis->bounds.right - analysis->bounds.left)*(analysis->bounds.bottom - analysis->bounds.top); | |
| if (analysis->texture_type == DWRITE_TEXTURE_CLEARTYPE_3x1) | |
| size *= 3; | |
| if (!(analysis->bitmap = heap_alloc_zero(size))) { | |
| WARN("Failed to allocate run bitmap, %s, type %s.\n", wine_dbgstr_rect(&analysis->bounds), | |
| analysis->texture_type == DWRITE_TEXTURE_CLEARTYPE_3x1 ? "3x1" : "1x1"); | |
| IDWriteFontFace4_Release(fontface); | |
| return E_OUTOFMEMORY; | |
| } | |
| origin.x = origin.y = 0.0f; | |
| memset(&glyph_bitmap, 0, sizeof(glyph_bitmap)); | |
| glyph_bitmap.fontface = fontface; | |
| glyph_bitmap.simulations = IDWriteFontFace4_GetSimulations(fontface); | |
| glyph_bitmap.emsize = analysis->run.fontEmSize; | |
| glyph_bitmap.nohint = is_natural_rendering_mode(analysis->rendering_mode); | |
| glyph_bitmap.aliased = analysis->rendering_mode == DWRITE_RENDERING_MODE1_ALIASED; | |
| if (analysis->flags & RUNANALYSIS_USE_TRANSFORM) | |
| glyph_bitmap.m = &analysis->m; | |
| if (!(glyph_bitmap.buf = heap_alloc(analysis->max_glyph_bitmap_size))) { | |
| IDWriteFontFace4_Release(fontface); | |
| return E_OUTOFMEMORY; | |
| } | |
| bbox = &glyph_bitmap.bbox; | |
| for (i = 0; i < analysis->run.glyphCount; i++) { | |
| BYTE *src = glyph_bitmap.buf, *dst; | |
| int x, y, width, height; | |
| BOOL is_1bpp; | |
| glyph_bitmap.index = analysis->run.glyphIndices[i]; | |
| freetype_get_glyph_bbox(&glyph_bitmap); | |
| if (IsRectEmpty(bbox)) | |
| continue; | |
| width = bbox->right - bbox->left; | |
| height = bbox->bottom - bbox->top; | |
| glyph_bitmap.pitch = get_glyph_bitmap_pitch(analysis->rendering_mode, width); | |
| memset(src, 0, height * glyph_bitmap.pitch); | |
| is_1bpp = freetype_get_glyph_bitmap(&glyph_bitmap); | |
| OffsetRect(bbox, analysis->origins[i].x, analysis->origins[i].y); | |
| /* blit to analysis bitmap */ | |
| dst = get_pixel_ptr(analysis->bitmap, analysis->texture_type, bbox, &analysis->bounds); | |
| if (is_1bpp) { | |
| /* convert 1bpp to 8bpp/24bpp */ | |
| if (analysis->texture_type == DWRITE_TEXTURE_CLEARTYPE_3x1) { | |
| for (y = 0; y < height; y++) { | |
| for (x = 0; x < width; x++) | |
| if (src[x / 8] & masks[x % 8]) | |
| dst[3*x] = dst[3*x+1] = dst[3*x+2] = DWRITE_ALPHA_MAX; | |
| src += glyph_bitmap.pitch; | |
| dst += (analysis->bounds.right - analysis->bounds.left) * 3; | |
| } | |
| } | |
| else { | |
| for (y = 0; y < height; y++) { | |
| for (x = 0; x < width; x++) | |
| if (src[x / 8] & masks[x % 8]) | |
| dst[x] = DWRITE_ALPHA_MAX; | |
| src += glyph_bitmap.pitch; | |
| dst += analysis->bounds.right - analysis->bounds.left; | |
| } | |
| } | |
| } | |
| else { | |
| if (analysis->texture_type == DWRITE_TEXTURE_CLEARTYPE_3x1) { | |
| for (y = 0; y < height; y++) { | |
| for (x = 0; x < width; x++) | |
| dst[3*x] = dst[3*x+1] = dst[3*x+2] = src[x] | dst[3*x]; | |
| src += glyph_bitmap.pitch; | |
| dst += (analysis->bounds.right - analysis->bounds.left) * 3; | |
| } | |
| } | |
| else { | |
| for (y = 0; y < height; y++) { | |
| for (x = 0; x < width; x++) | |
| dst[x] |= src[x]; | |
| src += glyph_bitmap.pitch; | |
| dst += analysis->bounds.right - analysis->bounds.left; | |
| } | |
| } | |
| } | |
| } | |
| heap_free(glyph_bitmap.buf); | |
| IDWriteFontFace4_Release(fontface); | |
| analysis->flags |= RUNANALYSIS_BITMAP_READY; | |
| /* we don't need this anymore */ | |
| heap_free(analysis->glyphs); | |
| heap_free(analysis->origins); | |
| IDWriteFontFace_Release(analysis->run.fontFace); | |
| analysis->glyphs = NULL; | |
| analysis->origins = NULL; | |
| analysis->run.glyphIndices = NULL; | |
| analysis->run.fontFace = NULL; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI glyphrunanalysis_CreateAlphaTexture(IDWriteGlyphRunAnalysis *iface, DWRITE_TEXTURE_TYPE type, | |
| RECT const *bounds, BYTE *bitmap, UINT32 size) | |
| { | |
| struct dwrite_glyphrunanalysis *This = impl_from_IDWriteGlyphRunAnalysis(iface); | |
| UINT32 required; | |
| RECT runbounds; | |
| TRACE("(%p)->(%d %s %p %u)\n", This, type, wine_dbgstr_rect(bounds), bitmap, size); | |
| if (!bounds || !bitmap || (UINT32)type > DWRITE_TEXTURE_CLEARTYPE_3x1) | |
| return E_INVALIDARG; | |
| /* make sure buffer is large enough for requested texture type */ | |
| required = (bounds->right - bounds->left) * (bounds->bottom - bounds->top); | |
| if (This->texture_type == DWRITE_TEXTURE_CLEARTYPE_3x1) | |
| required *= 3; | |
| if (size < required) | |
| return E_NOT_SUFFICIENT_BUFFER; | |
| /* validate requested texture type */ | |
| if (This->texture_type != type) | |
| return DWRITE_E_UNSUPPORTEDOPERATION; | |
| memset(bitmap, 0, size); | |
| glyphrunanalysis_get_texturebounds(This, &runbounds); | |
| if (IntersectRect(&runbounds, &runbounds, bounds)) { | |
| int pixel_size = type == DWRITE_TEXTURE_CLEARTYPE_3x1 ? 3 : 1; | |
| int src_width = (This->bounds.right - This->bounds.left) * pixel_size; | |
| int dst_width = (bounds->right - bounds->left) * pixel_size; | |
| int draw_width = (runbounds.right - runbounds.left) * pixel_size; | |
| BYTE *src, *dst; | |
| int y; | |
| if (!(This->flags & RUNANALYSIS_BITMAP_READY)) { | |
| HRESULT hr; | |
| if (FAILED(hr = glyphrunanalysis_render(This))) | |
| return hr; | |
| } | |
| src = get_pixel_ptr(This->bitmap, type, &runbounds, &This->bounds); | |
| dst = get_pixel_ptr(bitmap, type, &runbounds, bounds); | |
| for (y = 0; y < runbounds.bottom - runbounds.top; y++) { | |
| memcpy(dst, src, draw_width); | |
| src += src_width; | |
| dst += dst_width; | |
| } | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI glyphrunanalysis_GetAlphaBlendParams(IDWriteGlyphRunAnalysis *iface, IDWriteRenderingParams *params, | |
| FLOAT *gamma, FLOAT *contrast, FLOAT *cleartypelevel) | |
| { | |
| struct dwrite_glyphrunanalysis *This = impl_from_IDWriteGlyphRunAnalysis(iface); | |
| TRACE("(%p)->(%p %p %p %p)\n", This, params, gamma, contrast, cleartypelevel); | |
| if (!params) | |
| return E_INVALIDARG; | |
| switch (This->rendering_mode) | |
| { | |
| case DWRITE_RENDERING_MODE1_GDI_CLASSIC: | |
| case DWRITE_RENDERING_MODE1_GDI_NATURAL: | |
| { | |
| UINT value = 0; | |
| SystemParametersInfoW(SPI_GETFONTSMOOTHINGCONTRAST, 0, &value, 0); | |
| *gamma = (FLOAT)value / 1000.0f; | |
| *contrast = 0.0f; | |
| *cleartypelevel = 1.0f; | |
| break; | |
| } | |
| case DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC_DOWNSAMPLED: | |
| WARN("NATURAL_SYMMETRIC_DOWNSAMPLED mode is ignored.\n"); | |
| /* fallthrough */ | |
| case DWRITE_RENDERING_MODE1_ALIASED: | |
| case DWRITE_RENDERING_MODE1_NATURAL: | |
| case DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC: | |
| *gamma = IDWriteRenderingParams_GetGamma(params); | |
| *contrast = IDWriteRenderingParams_GetEnhancedContrast(params); | |
| *cleartypelevel = IDWriteRenderingParams_GetClearTypeLevel(params); | |
| break; | |
| default: | |
| ; | |
| } | |
| return S_OK; | |
| } | |
| static const struct IDWriteGlyphRunAnalysisVtbl glyphrunanalysisvtbl = { | |
| glyphrunanalysis_QueryInterface, | |
| glyphrunanalysis_AddRef, | |
| glyphrunanalysis_Release, | |
| glyphrunanalysis_GetAlphaTextureBounds, | |
| glyphrunanalysis_CreateAlphaTexture, | |
| glyphrunanalysis_GetAlphaBlendParams | |
| }; | |
| static inline void transform_point(D2D_POINT_2F *point, const DWRITE_MATRIX *m) | |
| { | |
| D2D_POINT_2F ret; | |
| ret.x = point->x * m->m11 + point->y * m->m21 + m->dx; | |
| ret.y = point->x * m->m12 + point->y * m->m22 + m->dy; | |
| *point = ret; | |
| } | |
| HRESULT create_glyphrunanalysis(const struct glyphrunanalysis_desc *desc, IDWriteGlyphRunAnalysis **ret) | |
| { | |
| struct dwrite_glyphrunanalysis *analysis; | |
| DWRITE_FONT_METRICS metrics; | |
| IDWriteFontFace1 *fontface1; | |
| D2D_POINT_2F origin; | |
| FLOAT rtl_factor; | |
| HRESULT hr; | |
| UINT32 i; | |
| *ret = NULL; | |
| /* Check rendering, antialising, measuring, and grid fitting modes. */ | |
| if ((UINT32)desc->rendering_mode >= DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC_DOWNSAMPLED || | |
| desc->rendering_mode == DWRITE_RENDERING_MODE1_OUTLINE || | |
| desc->rendering_mode == DWRITE_RENDERING_MODE1_DEFAULT) | |
| return E_INVALIDARG; | |
| if ((UINT32)desc->aa_mode > DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE) | |
| return E_INVALIDARG; | |
| if ((UINT32)desc->gridfit_mode > DWRITE_GRID_FIT_MODE_ENABLED) | |
| return E_INVALIDARG; | |
| if ((UINT32)desc->measuring_mode > DWRITE_MEASURING_MODE_GDI_NATURAL) | |
| return E_INVALIDARG; | |
| analysis = heap_alloc(sizeof(*analysis)); | |
| if (!analysis) | |
| return E_OUTOFMEMORY; | |
| analysis->IDWriteGlyphRunAnalysis_iface.lpVtbl = &glyphrunanalysisvtbl; | |
| analysis->ref = 1; | |
| analysis->rendering_mode = desc->rendering_mode; | |
| if (desc->rendering_mode == DWRITE_RENDERING_MODE1_ALIASED | |
| || desc->aa_mode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE) | |
| analysis->texture_type = DWRITE_TEXTURE_ALIASED_1x1; | |
| else | |
| analysis->texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; | |
| analysis->flags = 0; | |
| analysis->bitmap = NULL; | |
| analysis->max_glyph_bitmap_size = 0; | |
| SetRectEmpty(&analysis->bounds); | |
| analysis->run = *desc->run; | |
| analysis->run.fontEmSize *= desc->ppdip; | |
| IDWriteFontFace_AddRef(analysis->run.fontFace); | |
| analysis->glyphs = heap_alloc(desc->run->glyphCount * sizeof(*analysis->glyphs)); | |
| analysis->origins = heap_alloc(desc->run->glyphCount * sizeof(*analysis->origins)); | |
| if (!analysis->glyphs || !analysis->origins) { | |
| heap_free(analysis->glyphs); | |
| heap_free(analysis->origins); | |
| analysis->glyphs = NULL; | |
| analysis->origins = NULL; | |
| IDWriteGlyphRunAnalysis_Release(&analysis->IDWriteGlyphRunAnalysis_iface); | |
| return E_OUTOFMEMORY; | |
| } | |
| /* check if transform is usable */ | |
| if (desc->transform && memcmp(desc->transform, &identity, sizeof(*desc->transform))) { | |
| analysis->m = *desc->transform; | |
| analysis->flags |= RUNANALYSIS_USE_TRANSFORM; | |
| } | |
| else | |
| memset(&analysis->m, 0, sizeof(analysis->m)); | |
| analysis->run.glyphIndices = analysis->glyphs; | |
| analysis->run.glyphAdvances = NULL; | |
| analysis->run.glyphOffsets = NULL; | |
| rtl_factor = desc->run->bidiLevel & 1 ? -1.0f : 1.0f; | |
| memcpy(analysis->glyphs, desc->run->glyphIndices, desc->run->glyphCount*sizeof(*desc->run->glyphIndices)); | |
| IDWriteFontFace_GetMetrics(desc->run->fontFace, &metrics); | |
| if (FAILED(hr = IDWriteFontFace_QueryInterface(desc->run->fontFace, &IID_IDWriteFontFace1, (void **)&fontface1))) | |
| WARN("Failed to get IDWriteFontFace1, %#x.\n", hr); | |
| origin.x = desc->origin_x * desc->ppdip; | |
| origin.y = desc->origin_y * desc->ppdip; | |
| for (i = 0; i < desc->run->glyphCount; i++) { | |
| FLOAT advance; | |
| /* Use nominal advances if not provided by caller. */ | |
| if (desc->run->glyphAdvances) | |
| advance = rtl_factor * desc->run->glyphAdvances[i] * desc->ppdip; | |
| else { | |
| INT32 a; | |
| advance = 0.0f; | |
| switch (desc->measuring_mode) | |
| { | |
| case DWRITE_MEASURING_MODE_NATURAL: | |
| if (SUCCEEDED(IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, desc->run->glyphIndices + i, &a, | |
| desc->run->isSideways))) | |
| advance = rtl_factor * get_scaled_advance_width(a, desc->run->fontEmSize, &metrics) * desc->ppdip; | |
| break; | |
| case DWRITE_MEASURING_MODE_GDI_CLASSIC: | |
| case DWRITE_MEASURING_MODE_GDI_NATURAL: | |
| if (SUCCEEDED(IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, desc->run->fontEmSize, | |
| desc->ppdip, desc->transform, desc->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL, | |
| desc->run->isSideways, 1, desc->run->glyphIndices + i, &a))) | |
| advance = rtl_factor * floorf(a * desc->run->fontEmSize * desc->ppdip / metrics.designUnitsPerEm + 0.5f); | |
| break; | |
| default: | |
| ; | |
| } | |
| } | |
| analysis->origins[i] = origin; | |
| /* Offsets are optional, appled to pre-transformed origin. */ | |
| if (desc->run->glyphOffsets) { | |
| FLOAT advanceoffset = rtl_factor * desc->run->glyphOffsets[i].advanceOffset * desc->ppdip; | |
| FLOAT ascenderoffset = -desc->run->glyphOffsets[i].ascenderOffset * desc->ppdip; | |
| if (desc->run->isSideways) { | |
| analysis->origins[i].x += ascenderoffset; | |
| analysis->origins[i].y += advanceoffset; | |
| } | |
| else { | |
| analysis->origins[i].x += advanceoffset; | |
| analysis->origins[i].y += ascenderoffset; | |
| } | |
| } | |
| if (analysis->flags & RUNANALYSIS_USE_TRANSFORM) | |
| transform_point(analysis->origins + i, &analysis->m); | |
| if (desc->run->isSideways) | |
| origin.y += advance; | |
| else | |
| origin.x += advance; | |
| } | |
| IDWriteFontFace1_Release(fontface1); | |
| *ret = &analysis->IDWriteGlyphRunAnalysis_iface; | |
| return S_OK; | |
| } | |
| /* IDWriteColorGlyphRunEnumerator */ | |
| static HRESULT WINAPI colorglyphenum_QueryInterface(IDWriteColorGlyphRunEnumerator *iface, REFIID riid, void **ppv) | |
| { | |
| struct dwrite_colorglyphenum *This = impl_from_IDWriteColorGlyphRunEnumerator(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); | |
| if (IsEqualIID(riid, &IID_IDWriteColorGlyphRunEnumerator) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *ppv = iface; | |
| IDWriteColorGlyphRunEnumerator_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *ppv = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI colorglyphenum_AddRef(IDWriteColorGlyphRunEnumerator *iface) | |
| { | |
| struct dwrite_colorglyphenum *This = impl_from_IDWriteColorGlyphRunEnumerator(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI colorglyphenum_Release(IDWriteColorGlyphRunEnumerator *iface) | |
| { | |
| struct dwrite_colorglyphenum *This = impl_from_IDWriteColorGlyphRunEnumerator(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| if (!ref) { | |
| heap_free(This->advances); | |
| heap_free(This->color_advances); | |
| heap_free(This->offsets); | |
| heap_free(This->color_offsets); | |
| heap_free(This->glyphindices); | |
| heap_free(This->glyphs); | |
| if (This->colr.context) | |
| IDWriteFontFace4_ReleaseFontTable(This->fontface, This->colr.context); | |
| IDWriteFontFace4_Release(This->fontface); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static FLOAT get_glyph_origin(const struct dwrite_colorglyphenum *glyphenum, UINT32 g) | |
| { | |
| BOOL is_rtl = glyphenum->run.bidiLevel & 1; | |
| FLOAT origin = 0.0f; | |
| if (g == 0) | |
| return 0.0f; | |
| while (g--) | |
| origin += is_rtl ? -glyphenum->advances[g] : glyphenum->advances[g]; | |
| return origin; | |
| } | |
| static BOOL colorglyphenum_build_color_run(struct dwrite_colorglyphenum *glyphenum) | |
| { | |
| DWRITE_COLOR_GLYPH_RUN *colorrun = &glyphenum->colorrun; | |
| FLOAT advance_adj = 0.0f; | |
| BOOL got_palette_index; | |
| UINT32 g; | |
| /* start with regular glyphs */ | |
| if (glyphenum->current_layer == 0 && glyphenum->has_regular_glyphs) { | |
| UINT32 first_glyph = 0; | |
| for (g = 0; g < glyphenum->run.glyphCount; g++) { | |
| if (glyphenum->glyphs[g].num_layers == 0) { | |
| glyphenum->glyphindices[g] = glyphenum->glyphs[g].glyph; | |
| first_glyph = min(first_glyph, g); | |
| } | |
| else | |
| glyphenum->glyphindices[g] = 1; | |
| glyphenum->color_advances[g] = glyphenum->advances[g]; | |
| if (glyphenum->color_offsets) | |
| glyphenum->color_offsets[g] = glyphenum->offsets[g]; | |
| } | |
| colorrun->baselineOriginX = glyphenum->origin_x + get_glyph_origin(glyphenum, first_glyph); | |
| colorrun->baselineOriginY = glyphenum->origin_y; | |
| colorrun->glyphRun.glyphCount = glyphenum->run.glyphCount; | |
| colorrun->paletteIndex = 0xffff; | |
| memset(&colorrun->runColor, 0, sizeof(colorrun->runColor)); | |
| glyphenum->has_regular_glyphs = FALSE; | |
| return TRUE; | |
| } | |
| else { | |
| colorrun->glyphRun.glyphCount = 0; | |
| got_palette_index = FALSE; | |
| } | |
| advance_adj = 0.0f; | |
| for (g = 0; g < glyphenum->run.glyphCount; g++) { | |
| glyphenum->glyphindices[g] = 1; | |
| /* all glyph layers were returned */ | |
| if (glyphenum->glyphs[g].layer == glyphenum->glyphs[g].num_layers) { | |
| advance_adj += glyphenum->advances[g]; | |
| continue; | |
| } | |
| if (glyphenum->current_layer == glyphenum->glyphs[g].layer && (!got_palette_index || colorrun->paletteIndex == glyphenum->glyphs[g].palette_index)) { | |
| UINT32 index = colorrun->glyphRun.glyphCount; | |
| if (!got_palette_index) { | |
| colorrun->paletteIndex = glyphenum->glyphs[g].palette_index; | |
| /* use foreground color or request one from the font */ | |
| memset(&colorrun->runColor, 0, sizeof(colorrun->runColor)); | |
| if (colorrun->paletteIndex != 0xffff) { | |
| HRESULT hr = IDWriteFontFace4_GetPaletteEntries(glyphenum->fontface, glyphenum->palette, colorrun->paletteIndex, | |
| 1, &colorrun->runColor); | |
| if (FAILED(hr)) | |
| WARN("failed to get palette entry, fontface %p, palette %u, index %u, 0x%08x\n", glyphenum->fontface, | |
| glyphenum->palette, colorrun->paletteIndex, hr); | |
| } | |
| /* found a glyph position new color run starts from, origin is "original origin + distance to this glyph" */ | |
| colorrun->baselineOriginX = glyphenum->origin_x + get_glyph_origin(glyphenum, g); | |
| colorrun->baselineOriginY = glyphenum->origin_y; | |
| glyphenum->color_advances[index] = glyphenum->advances[g]; | |
| got_palette_index = TRUE; | |
| } | |
| glyphenum->glyphindices[index] = glyphenum->glyphs[g].glyph; | |
| /* offsets are relative to glyph origin, nothing to fix up */ | |
| if (glyphenum->color_offsets) | |
| glyphenum->color_offsets[index] = glyphenum->offsets[g]; | |
| opentype_colr_next_glyph(glyphenum->colr.data, glyphenum->glyphs + g); | |
| if (index) | |
| glyphenum->color_advances[index-1] += advance_adj; | |
| colorrun->glyphRun.glyphCount++; | |
| advance_adj = 0.0f; | |
| } | |
| else | |
| advance_adj += glyphenum->advances[g]; | |
| } | |
| /* reset last advance */ | |
| if (colorrun->glyphRun.glyphCount) | |
| glyphenum->color_advances[colorrun->glyphRun.glyphCount-1] = 0.0f; | |
| return colorrun->glyphRun.glyphCount > 0; | |
| } | |
| static HRESULT WINAPI colorglyphenum_MoveNext(IDWriteColorGlyphRunEnumerator *iface, BOOL *has_run) | |
| { | |
| struct dwrite_colorglyphenum *This = impl_from_IDWriteColorGlyphRunEnumerator(iface); | |
| TRACE("(%p)->(%p)\n", This, has_run); | |
| *has_run = FALSE; | |
| This->colorrun.glyphRun.glyphCount = 0; | |
| while (This->current_layer < This->max_layer_num) { | |
| if (colorglyphenum_build_color_run(This)) | |
| break; | |
| else | |
| This->current_layer++; | |
| } | |
| *has_run = This->colorrun.glyphRun.glyphCount > 0; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI colorglyphenum_GetCurrentRun(IDWriteColorGlyphRunEnumerator *iface, DWRITE_COLOR_GLYPH_RUN const **run) | |
| { | |
| struct dwrite_colorglyphenum *This = impl_from_IDWriteColorGlyphRunEnumerator(iface); | |
| TRACE("(%p)->(%p)\n", This, run); | |
| if (This->colorrun.glyphRun.glyphCount == 0) { | |
| *run = NULL; | |
| return E_NOT_VALID_STATE; | |
| } | |
| *run = &This->colorrun; | |
| return S_OK; | |
| } | |
| static const IDWriteColorGlyphRunEnumeratorVtbl colorglyphenumvtbl = { | |
| colorglyphenum_QueryInterface, | |
| colorglyphenum_AddRef, | |
| colorglyphenum_Release, | |
| colorglyphenum_MoveNext, | |
| colorglyphenum_GetCurrentRun | |
| }; | |
| HRESULT create_colorglyphenum(FLOAT originX, FLOAT originY, const DWRITE_GLYPH_RUN *run, const DWRITE_GLYPH_RUN_DESCRIPTION *rundescr, | |
| DWRITE_MEASURING_MODE measuring_mode, const DWRITE_MATRIX *transform, UINT32 palette, IDWriteColorGlyphRunEnumerator **ret) | |
| { | |
| struct dwrite_colorglyphenum *colorglyphenum; | |
| BOOL colorfont, has_colored_glyph; | |
| IDWriteFontFace4 *fontface; | |
| HRESULT hr; | |
| UINT32 i; | |
| *ret = NULL; | |
| hr = IDWriteFontFace_QueryInterface(run->fontFace, &IID_IDWriteFontFace4, (void**)&fontface); | |
| if (FAILED(hr)) { | |
| WARN("failed to get IDWriteFontFace4, 0x%08x\n", hr); | |
| return hr; | |
| } | |
| colorfont = IDWriteFontFace4_IsColorFont(fontface) && IDWriteFontFace4_GetColorPaletteCount(fontface) > palette; | |
| if (!colorfont) { | |
| hr = DWRITE_E_NOCOLOR; | |
| goto failed; | |
| } | |
| colorglyphenum = heap_alloc_zero(sizeof(*colorglyphenum)); | |
| if (!colorglyphenum) { | |
| hr = E_OUTOFMEMORY; | |
| goto failed; | |
| } | |
| colorglyphenum->IDWriteColorGlyphRunEnumerator_iface.lpVtbl = &colorglyphenumvtbl; | |
| colorglyphenum->ref = 1; | |
| colorglyphenum->origin_x = originX; | |
| colorglyphenum->origin_y = originY; | |
| colorglyphenum->fontface = fontface; | |
| colorglyphenum->glyphs = NULL; | |
| colorglyphenum->run = *run; | |
| colorglyphenum->run.glyphIndices = NULL; | |
| colorglyphenum->run.glyphAdvances = NULL; | |
| colorglyphenum->run.glyphOffsets = NULL; | |
| colorglyphenum->palette = palette; | |
| memset(&colorglyphenum->colr, 0, sizeof(colorglyphenum->colr)); | |
| colorglyphenum->colr.exists = TRUE; | |
| get_fontface_table(fontface, MS_COLR_TAG, &colorglyphenum->colr); | |
| colorglyphenum->current_layer = 0; | |
| colorglyphenum->max_layer_num = 0; | |
| colorglyphenum->glyphs = heap_alloc_zero(run->glyphCount * sizeof(*colorglyphenum->glyphs)); | |
| has_colored_glyph = FALSE; | |
| colorglyphenum->has_regular_glyphs = FALSE; | |
| for (i = 0; i < run->glyphCount; i++) { | |
| if (opentype_get_colr_glyph(colorglyphenum->colr.data, run->glyphIndices[i], colorglyphenum->glyphs + i) == S_OK) { | |
| colorglyphenum->max_layer_num = max(colorglyphenum->max_layer_num, colorglyphenum->glyphs[i].num_layers); | |
| has_colored_glyph = TRUE; | |
| } | |
| if (colorglyphenum->glyphs[i].num_layers == 0) | |
| colorglyphenum->has_regular_glyphs = TRUE; | |
| } | |
| /* It's acceptable to have a subset of glyphs mapped to color layers, for regular runs client | |
| is supposed to proceed normally, like if font had no color info at all. */ | |
| if (!has_colored_glyph) { | |
| IDWriteColorGlyphRunEnumerator_Release(&colorglyphenum->IDWriteColorGlyphRunEnumerator_iface); | |
| return DWRITE_E_NOCOLOR; | |
| } | |
| colorglyphenum->advances = heap_alloc(run->glyphCount * sizeof(FLOAT)); | |
| colorglyphenum->color_advances = heap_alloc(run->glyphCount * sizeof(FLOAT)); | |
| colorglyphenum->glyphindices = heap_alloc(run->glyphCount * sizeof(UINT16)); | |
| if (run->glyphOffsets) { | |
| colorglyphenum->offsets = heap_alloc(run->glyphCount * sizeof(*colorglyphenum->offsets)); | |
| colorglyphenum->color_offsets = heap_alloc(run->glyphCount * sizeof(*colorglyphenum->color_offsets)); | |
| memcpy(colorglyphenum->offsets, run->glyphOffsets, run->glyphCount * sizeof(*run->glyphOffsets)); | |
| } | |
| colorglyphenum->colorrun.glyphRun.fontFace = (IDWriteFontFace*)fontface; | |
| colorglyphenum->colorrun.glyphRun.fontEmSize = run->fontEmSize; | |
| colorglyphenum->colorrun.glyphRun.glyphIndices = colorglyphenum->glyphindices; | |
| colorglyphenum->colorrun.glyphRun.glyphAdvances = colorglyphenum->color_advances; | |
| colorglyphenum->colorrun.glyphRun.glyphOffsets = colorglyphenum->color_offsets; | |
| colorglyphenum->colorrun.glyphRunDescription = NULL; /* FIXME */ | |
| if (run->glyphAdvances) | |
| memcpy(colorglyphenum->advances, run->glyphAdvances, run->glyphCount * sizeof(FLOAT)); | |
| else { | |
| DWRITE_FONT_METRICS metrics; | |
| IDWriteFontFace_GetMetrics(run->fontFace, &metrics); | |
| for (i = 0; i < run->glyphCount; i++) { | |
| HRESULT hr; | |
| INT32 a; | |
| switch (measuring_mode) | |
| { | |
| case DWRITE_MEASURING_MODE_NATURAL: | |
| hr = IDWriteFontFace4_GetDesignGlyphAdvances(fontface, 1, run->glyphIndices + i, &a, run->isSideways); | |
| if (FAILED(hr)) | |
| a = 0; | |
| colorglyphenum->advances[i] = get_scaled_advance_width(a, run->fontEmSize, &metrics); | |
| break; | |
| case DWRITE_MEASURING_MODE_GDI_CLASSIC: | |
| case DWRITE_MEASURING_MODE_GDI_NATURAL: | |
| hr = IDWriteFontFace4_GetGdiCompatibleGlyphAdvances(fontface, run->fontEmSize, 1.0f, transform, | |
| measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->isSideways, 1, run->glyphIndices + i, &a); | |
| if (FAILED(hr)) | |
| colorglyphenum->advances[i] = 0.0f; | |
| else | |
| colorglyphenum->advances[i] = floorf(a * run->fontEmSize / metrics.designUnitsPerEm + 0.5f); | |
| break; | |
| default: | |
| ; | |
| } | |
| } | |
| } | |
| *ret = &colorglyphenum->IDWriteColorGlyphRunEnumerator_iface; | |
| return S_OK; | |
| failed: | |
| IDWriteFontFace4_Release(fontface); | |
| return hr; | |
| } | |
| /* IDWriteFontFaceReference */ | |
| static HRESULT WINAPI fontfacereference_QueryInterface(IDWriteFontFaceReference *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFontFaceReference) || IsEqualIID(riid, &IID_IUnknown)) { | |
| *obj = iface; | |
| IDWriteFontFaceReference_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI fontfacereference_AddRef(IDWriteFontFaceReference *iface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI fontfacereference_Release(IDWriteFontFaceReference *iface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%u)\n", This, ref); | |
| if (!ref) { | |
| IDWriteFontFile_Release(This->file); | |
| IDWriteFactory5_Release(This->factory); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI fontfacereference_CreateFontFace(IDWriteFontFaceReference *iface, IDWriteFontFace3 **fontface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| TRACE("(%p)->(%p)\n", This, fontface); | |
| return IDWriteFontFaceReference_CreateFontFaceWithSimulations(iface, This->simulations, fontface); | |
| } | |
| static HRESULT WINAPI fontfacereference_CreateFontFaceWithSimulations(IDWriteFontFaceReference *iface, | |
| DWRITE_FONT_SIMULATIONS simulations, IDWriteFontFace3 **ret) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| DWRITE_FONT_FILE_TYPE file_type; | |
| DWRITE_FONT_FACE_TYPE face_type; | |
| IDWriteFontFace *fontface; | |
| BOOL is_supported; | |
| UINT32 face_num; | |
| HRESULT hr; | |
| TRACE("(%p)->(%d %p)\n", This, simulations, ret); | |
| hr = IDWriteFontFile_Analyze(This->file, &is_supported, &file_type, &face_type, &face_num); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFactory5_CreateFontFace(This->factory, face_type, 1, &This->file, This->index, simulations, &fontface); | |
| if (SUCCEEDED(hr)) { | |
| hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace3, (void **)ret); | |
| IDWriteFontFace_Release(fontface); | |
| } | |
| return hr; | |
| } | |
| static BOOL WINAPI fontfacereference_Equals(IDWriteFontFaceReference *iface, IDWriteFontFaceReference *ref) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| IDWriteFontFile *file; | |
| BOOL ret; | |
| TRACE("(%p)->(%p)\n", This, ref); | |
| if (FAILED(IDWriteFontFaceReference_GetFontFile(ref, &file))) | |
| return FALSE; | |
| ret = is_same_fontfile(This->file, file) && | |
| This->index == IDWriteFontFaceReference_GetFontFaceIndex(ref) && | |
| This->simulations == IDWriteFontFaceReference_GetSimulations(ref); | |
| IDWriteFontFile_Release(file); | |
| return ret; | |
| } | |
| static UINT32 WINAPI fontfacereference_GetFontFaceIndex(IDWriteFontFaceReference *iface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| TRACE("(%p)\n", This); | |
| return This->index; | |
| } | |
| static DWRITE_FONT_SIMULATIONS WINAPI fontfacereference_GetSimulations(IDWriteFontFaceReference *iface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| TRACE("(%p)\n", This); | |
| return This->simulations; | |
| } | |
| static HRESULT WINAPI fontfacereference_GetFontFile(IDWriteFontFaceReference *iface, IDWriteFontFile **file) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| IDWriteFontFileLoader *loader; | |
| const void *key; | |
| UINT32 key_size; | |
| HRESULT hr; | |
| TRACE("(%p)->(%p)\n", This, file); | |
| hr = IDWriteFontFile_GetReferenceKey(This->file, &key, &key_size); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFontFile_GetLoader(This->file, &loader); | |
| if (FAILED(hr)) | |
| return hr; | |
| hr = IDWriteFactory5_CreateCustomFontFileReference(This->factory, key, key_size, loader, file); | |
| IDWriteFontFileLoader_Release(loader); | |
| return hr; | |
| } | |
| static UINT64 WINAPI fontfacereference_GetLocalFileSize(IDWriteFontFaceReference *iface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| FIXME("(%p): stub\n", This); | |
| return 0; | |
| } | |
| static UINT64 WINAPI fontfacereference_GetFileSize(IDWriteFontFaceReference *iface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| FIXME("(%p): stub\n", This); | |
| return 0; | |
| } | |
| static HRESULT WINAPI fontfacereference_GetFileTime(IDWriteFontFaceReference *iface, FILETIME *writetime) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| FIXME("(%p)->(%p): stub\n", This, writetime); | |
| return E_NOTIMPL; | |
| } | |
| static DWRITE_LOCALITY WINAPI fontfacereference_GetLocality(IDWriteFontFaceReference *iface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| FIXME("(%p): stub\n", This); | |
| return DWRITE_LOCALITY_LOCAL; | |
| } | |
| static HRESULT WINAPI fontfacereference_EnqueueFontDownloadRequest(IDWriteFontFaceReference *iface) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| FIXME("(%p): stub\n", This); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI fontfacereference_EnqueueCharacterDownloadRequest(IDWriteFontFaceReference *iface, WCHAR const *chars, | |
| UINT32 count) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| FIXME("(%p)->(%s:%u): stub\n", This, debugstr_wn(chars, count), count); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI fontfacereference_EnqueueGlyphDownloadRequest(IDWriteFontFaceReference *iface, UINT16 const *glyphs, | |
| UINT32 count) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| FIXME("(%p)->(%p %u): stub\n", This, glyphs, count); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI fontfacereference_EnqueueFileFragmentDownloadRequest(IDWriteFontFaceReference *iface, | |
| UINT64 offset, UINT64 size) | |
| { | |
| struct dwrite_fontfacereference *This = impl_from_IDWriteFontFaceReference(iface); | |
| FIXME("(%p)->(0x%s 0x%s): stub\n", This, wine_dbgstr_longlong(offset), wine_dbgstr_longlong(size)); | |
| return E_NOTIMPL; | |
| } | |
| static const IDWriteFontFaceReferenceVtbl fontfacereferencevtbl = { | |
| fontfacereference_QueryInterface, | |
| fontfacereference_AddRef, | |
| fontfacereference_Release, | |
| fontfacereference_CreateFontFace, | |
| fontfacereference_CreateFontFaceWithSimulations, | |
| fontfacereference_Equals, | |
| fontfacereference_GetFontFaceIndex, | |
| fontfacereference_GetSimulations, | |
| fontfacereference_GetFontFile, | |
| fontfacereference_GetLocalFileSize, | |
| fontfacereference_GetFileSize, | |
| fontfacereference_GetFileTime, | |
| fontfacereference_GetLocality, | |
| fontfacereference_EnqueueFontDownloadRequest, | |
| fontfacereference_EnqueueCharacterDownloadRequest, | |
| fontfacereference_EnqueueGlyphDownloadRequest, | |
| fontfacereference_EnqueueFileFragmentDownloadRequest | |
| }; | |
| HRESULT create_fontfacereference(IDWriteFactory5 *factory, IDWriteFontFile *file, UINT32 index, | |
| DWRITE_FONT_SIMULATIONS simulations, IDWriteFontFaceReference **ret) | |
| { | |
| struct dwrite_fontfacereference *ref; | |
| *ret = NULL; | |
| if (!is_simulation_valid(simulations)) | |
| return E_INVALIDARG; | |
| ref = heap_alloc(sizeof(*ref)); | |
| if (!ref) | |
| return E_OUTOFMEMORY; | |
| ref->IDWriteFontFaceReference_iface.lpVtbl = &fontfacereferencevtbl; | |
| ref->ref = 1; | |
| ref->factory = factory; | |
| IDWriteFactory5_AddRef(ref->factory); | |
| ref->file = file; | |
| IDWriteFontFile_AddRef(ref->file); | |
| ref->index = index; | |
| ref->simulations = simulations; | |
| *ret = &ref->IDWriteFontFaceReference_iface; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI inmemoryfilestream_QueryInterface(IDWriteFontFileStream *iface, REFIID riid, void **obj) | |
| { | |
| struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%s, %p)\n", stream, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteFontFileStream) || IsEqualIID(riid, &IID_IUnknown)) { | |
| *obj = iface; | |
| IDWriteFontFileStream_AddRef(iface); | |
| return S_OK; | |
| } | |
| *obj = NULL; | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI inmemoryfilestream_AddRef(IDWriteFontFileStream *iface) | |
| { | |
| struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); | |
| ULONG ref = InterlockedIncrement(&stream->ref); | |
| TRACE_(dwrite_file)("(%p)->(%u)\n", stream, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI inmemoryfilestream_Release(IDWriteFontFileStream *iface) | |
| { | |
| struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); | |
| ULONG ref = InterlockedDecrement(&stream->ref); | |
| TRACE_(dwrite_file)("(%p)->(%u)\n", stream, ref); | |
| if (!ref) { | |
| release_inmemory_stream(stream->data); | |
| heap_free(stream); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI inmemoryfilestream_ReadFileFragment(IDWriteFontFileStream *iface, void const **fragment_start, | |
| UINT64 offset, UINT64 fragment_size, void **fragment_context) | |
| { | |
| struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%p, 0x%s, 0x%s, %p)\n", stream, fragment_start, | |
| wine_dbgstr_longlong(offset), wine_dbgstr_longlong(fragment_size), fragment_context); | |
| *fragment_context = NULL; | |
| if ((offset >= stream->data->size - 1) || (fragment_size > stream->data->size - offset)) { | |
| *fragment_start = NULL; | |
| return E_FAIL; | |
| } | |
| *fragment_start = (char *)stream->data->data + offset; | |
| return S_OK; | |
| } | |
| static void WINAPI inmemoryfilestream_ReleaseFileFragment(IDWriteFontFileStream *iface, void *fragment_context) | |
| { | |
| struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%p)\n", stream, fragment_context); | |
| } | |
| static HRESULT WINAPI inmemoryfilestream_GetFileSize(IDWriteFontFileStream *iface, UINT64 *size) | |
| { | |
| struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%p)\n", stream, size); | |
| *size = stream->data->size; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI inmemoryfilestream_GetLastWriteTime(IDWriteFontFileStream *iface, UINT64 *last_writetime) | |
| { | |
| struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); | |
| TRACE_(dwrite_file)("(%p)->(%p)\n", stream, last_writetime); | |
| *last_writetime = 0; | |
| return E_NOTIMPL; | |
| } | |
| static const IDWriteFontFileStreamVtbl inmemoryfilestreamvtbl = { | |
| inmemoryfilestream_QueryInterface, | |
| inmemoryfilestream_AddRef, | |
| inmemoryfilestream_Release, | |
| inmemoryfilestream_ReadFileFragment, | |
| inmemoryfilestream_ReleaseFileFragment, | |
| inmemoryfilestream_GetFileSize, | |
| inmemoryfilestream_GetLastWriteTime, | |
| }; | |
| static HRESULT WINAPI inmemoryfontfileloader_QueryInterface(IDWriteInMemoryFontFileLoader *iface, | |
| REFIID riid, void **obj) | |
| { | |
| struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); | |
| TRACE("(%p)->(%s, %p)\n", loader, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IDWriteInMemoryFontFileLoader) || | |
| IsEqualIID(riid, &IID_IDWriteFontFileLoader) || | |
| IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| IDWriteInMemoryFontFileLoader_AddRef(iface); | |
| return S_OK; | |
| } | |
| WARN("%s not implemented.\n", debugstr_guid(riid)); | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI inmemoryfontfileloader_AddRef(IDWriteInMemoryFontFileLoader *iface) | |
| { | |
| struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); | |
| ULONG ref = InterlockedIncrement(&loader->ref); | |
| TRACE("(%p)->(%u)\n", loader, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI inmemoryfontfileloader_Release(IDWriteInMemoryFontFileLoader *iface) | |
| { | |
| struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); | |
| ULONG ref = InterlockedDecrement(&loader->ref); | |
| TRACE("(%p)->(%u)\n", loader, ref); | |
| if (!ref) { | |
| UINT32 i; | |
| for (i = 0; i < loader->filecount; i++) | |
| release_inmemory_stream(loader->streams[i]); | |
| heap_free(loader->streams); | |
| heap_free(loader); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI inmemoryfontfileloader_CreateStreamFromKey(IDWriteInMemoryFontFileLoader *iface, | |
| void const *key, UINT32 key_size, IDWriteFontFileStream **ret) | |
| { | |
| struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); | |
| struct dwrite_inmemory_filestream *stream; | |
| DWORD index; | |
| TRACE("(%p)->(%p, %u, %p)\n", loader, key, key_size, ret); | |
| *ret = NULL; | |
| if (key_size != sizeof(DWORD)) | |
| return E_INVALIDARG; | |
| index = *(DWORD *)key; | |
| if (index >= loader->filecount) | |
| return E_INVALIDARG; | |
| if (!(stream = heap_alloc(sizeof(*stream)))) | |
| return E_OUTOFMEMORY; | |
| stream->IDWriteFontFileStream_iface.lpVtbl = &inmemoryfilestreamvtbl; | |
| stream->ref = 1; | |
| stream->data = loader->streams[index]; | |
| InterlockedIncrement(&stream->data->ref); | |
| *ret = &stream->IDWriteFontFileStream_iface; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI inmemoryfontfileloader_CreateInMemoryFontFileReference(IDWriteInMemoryFontFileLoader *iface, | |
| IDWriteFactory *factory, void const *data, UINT32 data_size, IUnknown *owner, IDWriteFontFile **fontfile) | |
| { | |
| struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); | |
| struct dwrite_inmemory_stream_data *stream; | |
| DWORD key; | |
| TRACE("(%p)->(%p, %p, %u, %p, %p)\n", loader, factory, data, data_size, owner, fontfile); | |
| *fontfile = NULL; | |
| if (loader->filecount == loader->capacity) { | |
| if (loader->streams) { | |
| struct dwrite_inmemory_stream_data **ptr; | |
| if (!(ptr = heap_realloc(loader->streams, 2 * loader->capacity * sizeof(*loader->streams)))) | |
| return E_OUTOFMEMORY; | |
| loader->streams = ptr; | |
| loader->capacity *= 2; | |
| } | |
| else { | |
| loader->capacity = 16; | |
| loader->streams = heap_alloc(loader->capacity * sizeof(*loader->streams)); | |
| } | |
| } | |
| if (!(stream = heap_alloc(sizeof(*stream)))) | |
| return E_OUTOFMEMORY; | |
| stream->ref = 1; | |
| stream->size = data_size; | |
| stream->owner = owner; | |
| if (stream->owner) { | |
| IUnknown_AddRef(stream->owner); | |
| stream->data = (void *)data; | |
| } | |
| else { | |
| if (!(stream->data = heap_alloc(data_size))) { | |
| heap_free(stream); | |
| return E_OUTOFMEMORY; | |
| } | |
| memcpy(stream->data, data, data_size); | |
| } | |
| key = loader->filecount; | |
| loader->streams[loader->filecount++] = stream; | |
| return IDWriteFactory_CreateCustomFontFileReference(factory, &key, sizeof(key), | |
| (IDWriteFontFileLoader *)&loader->IDWriteInMemoryFontFileLoader_iface, fontfile); | |
| } | |
| static UINT32 WINAPI inmemoryfontfileloader_GetFileCount(IDWriteInMemoryFontFileLoader *iface) | |
| { | |
| struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); | |
| TRACE("(%p)\n", loader); | |
| return loader->filecount; | |
| } | |
| static const IDWriteInMemoryFontFileLoaderVtbl inmemoryfontfileloadervtbl = | |
| { | |
| inmemoryfontfileloader_QueryInterface, | |
| inmemoryfontfileloader_AddRef, | |
| inmemoryfontfileloader_Release, | |
| inmemoryfontfileloader_CreateStreamFromKey, | |
| inmemoryfontfileloader_CreateInMemoryFontFileReference, | |
| inmemoryfontfileloader_GetFileCount, | |
| }; | |
| HRESULT create_inmemory_fileloader(IDWriteFontFileLoader **ret) | |
| { | |
| struct dwrite_inmemory_fileloader *loader; | |
| *ret = NULL; | |
| loader = heap_alloc_zero(sizeof(*loader)); | |
| if (!loader) | |
| return E_OUTOFMEMORY; | |
| loader->IDWriteInMemoryFontFileLoader_iface.lpVtbl = &inmemoryfontfileloadervtbl; | |
| loader->ref = 1; | |
| *ret = (IDWriteFontFileLoader *)&loader->IDWriteInMemoryFontFileLoader_iface; | |
| return S_OK; | |
| } |