diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 3619e530cd7..a102e1426a1 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -139,6 +139,7 @@ extern void opentype_get_font_properties(IDWriteFontFileStream*,DWRITE_FONT_FACE extern void opentype_get_font_metrics(IDWriteFontFileStream*,DWRITE_FONT_FACE_TYPE,UINT32,DWRITE_FONT_METRICS1*,DWRITE_CARET_METRICS*) DECLSPEC_HIDDEN; extern HRESULT opentype_get_font_strings_from_id(const void*,DWRITE_INFORMATIONAL_STRING_ID,IDWriteLocalizedStrings**) DECLSPEC_HIDDEN; extern HRESULT opentype_get_typographic_features(IDWriteFontFace*,UINT32,UINT32,UINT32,UINT32*,DWRITE_FONT_FEATURE_TAG*) DECLSPEC_HIDDEN; +extern BOOL opentype_get_vdmx_size(const void*,INT,UINT16*,UINT16*) DECLSPEC_HIDDEN; /* BiDi helpers */ extern HRESULT bidi_computelevels(const WCHAR*,UINT32,UINT8,UINT8*,UINT8*) DECLSPEC_HIDDEN; diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index a0539caeef5..13d3ff5849d 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -18,6 +18,7 @@ * 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 #define COBJMACROS @@ -30,6 +31,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dwrite); #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') static const IID IID_issystemcollection = {0x14d88047,0x331f,0x4cd3,{0xbc,0xa8,0x3e,0x67,0x99,0xaf,0x34,0x75}}; @@ -96,6 +98,7 @@ struct dwrite_fonttable { void *data; void *context; UINT32 size; + BOOL exists; }; struct dwrite_glyphrunanalysis { @@ -123,6 +126,7 @@ struct dwrite_fontface { DWRITE_CARET_METRICS caret; struct dwrite_fonttable cmap; + struct dwrite_fonttable vdmx; DWRITE_GLYPH_METRICS *glyphs[GLYPH_MAX/GLYPH_BLOCK_SIZE]; }; @@ -166,6 +170,11 @@ static inline struct dwrite_glyphrunanalysis *impl_from_IDWriteGlyphRunAnalysis( return CONTAINING_RECORD(iface, struct dwrite_glyphrunanalysis, IDWriteGlyphRunAnalysis_iface); } +static inline const char *debugstr_tag(UINT32 tag) +{ + return wine_dbg_sprintf("%c%c%c%c", tag >> 24, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff); +} + static HRESULT get_cached_glyph_metrics(struct dwrite_fontface *fontface, UINT16 glyph, DWRITE_GLYPH_METRICS *metrics) { static const DWRITE_GLYPH_METRICS nil; @@ -191,22 +200,32 @@ static HRESULT set_cached_glyph_metrics(struct dwrite_fontface *fontface, UINT16 return S_OK; } -static inline void* get_fontface_cmap(struct dwrite_fontface *fontface) +static void* get_fontface_table(struct dwrite_fontface *fontface, UINT32 tag, struct dwrite_fonttable *table) { - BOOL exists = FALSE; HRESULT hr; - if (fontface->cmap.data) - return fontface->cmap.data; + if (table->data || !table->exists) + return table->data; - hr = IDWriteFontFace2_TryGetFontTable(&fontface->IDWriteFontFace2_iface, MS_CMAP_TAG, (const void**)&fontface->cmap.data, - &fontface->cmap.size, &fontface->cmap.context, &exists); - if (FAILED(hr) || !exists) { - ERR("Font does not have a CMAP table\n"); + table->exists = FALSE; + hr = IDWriteFontFace2_TryGetFontTable(&fontface->IDWriteFontFace2_iface, tag, (const void**)&table->data, + &table->size, &table->context, &table->exists); + if (FAILED(hr) || !table->exists) { + WARN("Font does not have a %s table\n", debugstr_tag(tag)); return NULL; } - return fontface->cmap.data; + return table->data; +} + +static inline void* get_fontface_cmap(struct dwrite_fontface *fontface) +{ + return get_fontface_table(fontface, MS_CMAP_TAG, &fontface->cmap); +} + +static inline void* get_fontface_vdmx(struct dwrite_fontface *fontface) +{ + return get_fontface_table(fontface, MS_VDMX_TAG, &fontface->vdmx); } static void release_font_data(struct dwrite_font_data *data) @@ -281,6 +300,8 @@ static ULONG WINAPI dwritefontface_Release(IDWriteFontFace2 *iface) if (This->cmap.context) IDWriteFontFace2_ReleaseFontTable(iface, This->cmap.context); + if (This->vdmx.context) + IDWriteFontFace2_ReleaseFontTable(iface, This->vdmx.context); for (i = 0; i < This->file_count; i++) { if (This->streams[i]) IDWriteFontFileStream_Release(This->streams[i]); @@ -573,16 +594,8 @@ static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace static HRESULT WINAPI dwritefontface_GetGdiCompatibleMetrics(IDWriteFontFace2 *iface, FLOAT emSize, FLOAT pixels_per_dip, DWRITE_MATRIX const *transform, DWRITE_FONT_METRICS *metrics) { - struct dwrite_fontface *This = impl_from_IDWriteFontFace2(iface); DWRITE_FONT_METRICS1 metrics1; - HRESULT hr; - - TRACE("(%p)->(%.2f %.2f %p %p)\n", This, emSize, pixels_per_dip, transform, metrics); - - hr = IDWriteFontFace2_GetGdiCompatibleMetrics(iface, emSize, pixels_per_dip, transform, &metrics1); - if (FAILED(hr)) - return hr; - + HRESULT hr = IDWriteFontFace2_GetGdiCompatibleMetrics(iface, emSize, pixels_per_dip, transform, &metrics1); memcpy(metrics, &metrics1, sizeof(*metrics)); return hr; } @@ -604,12 +617,65 @@ static void WINAPI dwritefontface1_GetMetrics(IDWriteFontFace2 *iface, DWRITE_FO *metrics = This->metrics; } +static inline int round_metric(FLOAT metric) +{ + return (int)floor(metric + 0.5); +} + static HRESULT WINAPI dwritefontface1_GetGdiCompatibleMetrics(IDWriteFontFace2 *iface, FLOAT em_size, FLOAT pixels_per_dip, - const DWRITE_MATRIX *transform, DWRITE_FONT_METRICS1 *metrics) + const DWRITE_MATRIX *m, DWRITE_FONT_METRICS1 *metrics) { struct dwrite_fontface *This = impl_from_IDWriteFontFace2(iface); - FIXME("(%p)->(%f %f %p %p): stub\n", This, em_size, pixels_per_dip, transform, metrics); - return E_NOTIMPL; + 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 (pixels_per_dip <= 0.0) { + memset(metrics, 0, sizeof(*metrics)); + return E_INVALIDARG; + } + + em_size *= pixels_per_dip; + if (m && m->m22 != 0.0) + 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(IDWriteFontFace2 *iface, DWRITE_CARET_METRICS *metrics) @@ -2216,9 +2282,10 @@ HRESULT create_fontface(DWRITE_FONT_FACE_TYPE facetype, UINT32 files_number, IDW fontface->ref = 1; fontface->type = facetype; fontface->file_count = files_number; - fontface->cmap.data = NULL; - fontface->cmap.context = NULL; - fontface->cmap.size = 0; + memset(&fontface->cmap, 0, sizeof(fontface->cmap)); + memset(&fontface->vdmx, 0, sizeof(fontface->vdmx)); + fontface->cmap.exists = TRUE; + fontface->vdmx.exists = TRUE; fontface->index = index; fontface->simulations = simulations; memset(fontface->glyphs, 0, sizeof(fontface->glyphs)); diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 3d51e3118ff..63e1b748f02 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -238,6 +238,35 @@ typedef struct { TT_NameRecord nameRecord[1]; } TT_NAME_V0; +struct VDMX_Header +{ + WORD version; + WORD numRecs; + WORD numRatios; +}; + +struct VDMX_Ratio +{ + BYTE bCharSet; + BYTE xRatio; + BYTE yStartRatio; + BYTE yEndRatio; +}; + +struct VDMX_group +{ + WORD recs; + BYTE startsz; + BYTE endsz; +}; + +struct VDMX_vTable +{ + WORD yPelHeight; + SHORT yMax; + SHORT yMin; +}; + typedef struct { CHAR FeatureTag[4]; WORD Feature; @@ -1288,3 +1317,63 @@ HRESULT opentype_get_typographic_features(IDWriteFontFace *fontface, UINT32 scri return *count > max_tagcount ? E_NOT_SUFFICIENT_BUFFER : S_OK; } + +static const struct VDMX_group *find_vdmx_group(const struct VDMX_Header *hdr) +{ + WORD num_ratios, i, group_offset = 0; + struct VDMX_Ratio *ratios = (struct VDMX_Ratio*)(hdr + 1); + BYTE dev_x_ratio = 1, dev_y_ratio = 1; + + num_ratios = GET_BE_WORD(hdr->numRatios); + + for (i = 0; i < num_ratios; i++) { + + if (!ratios[i].bCharSet) continue; + + if ((ratios[i].xRatio == 0 && ratios[i].yStartRatio == 0 && + ratios[i].yEndRatio == 0) || + (ratios[i].xRatio == dev_x_ratio && ratios[i].yStartRatio <= dev_y_ratio && + ratios[i].yEndRatio >= dev_y_ratio)) + { + group_offset = GET_BE_WORD(*((WORD *)(ratios + num_ratios) + i)); + break; + } + } + if (group_offset) + return (const struct VDMX_group *)((BYTE *)hdr + group_offset); + return NULL; +} + +BOOL opentype_get_vdmx_size(const void *data, INT emsize, UINT16 *ascent, UINT16 *descent) +{ + const struct VDMX_Header *hdr = (const struct VDMX_Header*)data; + const struct VDMX_group *group = find_vdmx_group(hdr); + const struct VDMX_vTable *tables; + WORD recs, i; + + if (!data) + return FALSE; + + group = find_vdmx_group(hdr); + if (!group) + return FALSE; + + recs = GET_BE_WORD(group->recs); + if (emsize < group->startsz || emsize >= group->endsz) return FALSE; + + tables = (const struct VDMX_vTable *)(group + 1); + for (i = 0; i < recs; i++) { + WORD ppem = GET_BE_WORD(tables[i].yPelHeight); + if (ppem > emsize) { + FIXME("interpolate %d\n", emsize); + return FALSE; + } + + if (ppem == emsize) { + *ascent = (SHORT)GET_BE_WORD(tables[i].yMax); + *descent = -(SHORT)GET_BE_WORD(tables[i].yMin); + return TRUE; + } + } + return FALSE; +} diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index 7f8619ea371..9e33a1a1b20 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -3606,8 +3606,8 @@ static void test_GetGdiCompatibleMetrics_face(IDWriteFontFace *face) { IDWriteFontFace1 *fontface1 = NULL; HRESULT hr; - DWRITE_FONT_METRICS design_metrics; - DWRITE_FONT_METRICS1 design_metrics1; + DWRITE_FONT_METRICS design_metrics, comp_metrics; + DWRITE_FONT_METRICS1 design_metrics1, expected; FLOAT emsize, scale; int ascent, descent; const struct VDMX_Header *vdmx; @@ -3615,6 +3615,7 @@ static void test_GetGdiCompatibleMetrics_face(IDWriteFontFace *face) void *vdmx_ctx; BOOL exists; const struct VDMX_group *vdmx_group = NULL; + DWRITE_MATRIX m; hr = IDWriteFontFace_QueryInterface(face, &IID_IDWriteFontFace1, (void**)&fontface1); if (hr != S_OK) @@ -3634,16 +3635,59 @@ static void test_GetGdiCompatibleMetrics_face(IDWriteFontFace *face) else vdmx_group = find_vdmx_group(vdmx); + /* zero pixels per dip */ + memset(&comp_metrics, 0xcc, sizeof(comp_metrics)); + memset(&expected, 0, sizeof(expected)); + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 5.0, 0.0, NULL, &comp_metrics); + ok(hr == E_INVALIDARG, "got %08x\n", hr); + test_metrics_cmp(5.0, &comp_metrics, &expected); + + memset(&comp_metrics, 0xcc, sizeof(comp_metrics)); + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 5.0, -1.0, NULL, &comp_metrics); + ok(hr == E_INVALIDARG, "got %08x\n", hr); + test_metrics_cmp(5.0, &comp_metrics, &expected); + + memset(&m, 0, sizeof(m)); + /* zero matrix m22 */ + m.m22 = 1.0; + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 5.0, 1.0, NULL, (DWRITE_FONT_METRICS*)&expected); + ok(hr == S_OK, "got %08x\n", hr); + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 5.0, 1.0, &m, &comp_metrics); + ok(hr == S_OK, "got %08x\n", hr); + test_metrics_cmp(5.0, &comp_metrics, &expected); + + m.m22 = -1.0; + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 5.0, 1.0, &m, &comp_metrics); + ok(hr == S_OK, "got %08x\n", hr); + test_metrics_cmp(5.0, &comp_metrics, &expected); + + /* pixels per dip == 2 */ + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 10.0, 1.0, NULL, (DWRITE_FONT_METRICS*)&expected); + ok(hr == S_OK, "got %08x\n", hr); + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 5.0, 2.0, NULL, &comp_metrics); + ok(hr == S_OK, "got %08x\n", hr); + test_metrics_cmp(5.0, &comp_metrics, &expected); + + /* pixels per dip == 2, m22 == 3.0 */ + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 30.0, 1.0, NULL, (DWRITE_FONT_METRICS*)&expected); + ok(hr == S_OK, "got %08x\n", hr); + + m.m22 = 3.0; + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 5.0, 2.0, &m, &comp_metrics); + ok(hr == S_OK, "got %08x\n", hr); + test_metrics_cmp(5.0, &comp_metrics, &expected); + m.m22 = -3.0; + hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, 5.0, 2.0, &m, &comp_metrics); + ok(hr == S_OK, "got %08x\n", hr); + test_metrics_cmp(5.0, &comp_metrics, &expected); + for (emsize = 5; emsize <= design_metrics.designUnitsPerEm; emsize++) { DWRITE_FONT_METRICS1 comp_metrics1, expected; - DWRITE_FONT_METRICS comp_metrics; if (fontface1) { hr = IDWriteFontFace1_GetGdiCompatibleMetrics(fontface1, emsize, 1.0, NULL, &comp_metrics1); - todo_wine ok(hr == S_OK, "got %08x\n", hr); - if (hr != S_OK) return; } else { hr = IDWriteFontFace_GetGdiCompatibleMetrics(face, emsize, 1.0, NULL, &comp_metrics);