Signed-off-by: Shawn M. Chapla schapla@codeweavers.com --- v2: - Remove updates to pixel testing (completely removed in patch 1/4).
dlls/gdiplus/gdiplus_private.h | 3 + dlls/gdiplus/graphics.c | 5 +- dlls/gdiplus/metafile.c | 182 +++++++++++++++++++++++++++++++-- dlls/gdiplus/tests/metafile.c | 14 +-- 4 files changed, 185 insertions(+), 19 deletions(-)
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 2ceaeade01..1b2de4e8f4 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -104,6 +104,9 @@ extern GpStatus METAFILE_DrawImagePointsRect(GpMetafile* metafile, GpImage *imag extern GpStatus METAFILE_AddSimpleProperty(GpMetafile *metafile, SHORT prop, SHORT val) DECLSPEC_HIDDEN; extern GpStatus METAFILE_DrawPath(GpMetafile *metafile, GpPen *pen, GpPath *path) DECLSPEC_HIDDEN; extern GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path) DECLSPEC_HIDDEN; +extern GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length, + GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush, + GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix) DECLSPEC_HIDDEN; extern void METAFILE_Free(GpMetafile *metafile) DECLSPEC_HIDDEN;
extern void calc_curve_bezier(const GpPointF *pts, REAL tension, REAL *x1, diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 6c50d2d56c..383a6286ac 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -7427,7 +7427,10 @@ static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, if (length == -1) length = lstrlenW(text);
- if (graphics->hdc && !graphics->alpha_hdc && + if (graphics->image && graphics->image->type == ImageTypeMetafile) + return METAFILE_DrawDriverString((GpMetafile*)graphics->image, text, length, font, + format, brush, positions, flags, matrix); + else if (graphics->hdc && !graphics->alpha_hdc && brush->bt == BrushTypeSolidColor && (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000) stat = GDI32_GdipDrawDriverString(graphics, text, length, font, format, diff --git a/dlls/gdiplus/metafile.c b/dlls/gdiplus/metafile.c index d25278d115..cedffb23ca 100644 --- a/dlls/gdiplus/metafile.c +++ b/dlls/gdiplus/metafile.c @@ -378,6 +378,17 @@ typedef struct EmfPlusImageAttributes DWORD Reserved2; } EmfPlusImageAttributes;
+typedef struct EmfPlusFont +{ + DWORD Version; + float EmSize; + DWORD SizeUnit; + DWORD FontStyleFlags; + DWORD Reserved; + DWORD Length; + WCHAR FamilyName[1]; +} EmfPlusFont; + typedef struct EmfPlusObject { EmfPlusRecordHeader Header; @@ -389,6 +400,7 @@ typedef struct EmfPlusObject EmfPlusRegion region; EmfPlusImage image; EmfPlusImageAttributes image_attributes; + EmfPlusFont font; } ObjectData; } EmfPlusObject;
@@ -537,17 +549,6 @@ typedef struct EmfPlusFillPie } RectData; } EmfPlusFillPie;
-typedef struct EmfPlusFont -{ - DWORD Version; - float EmSize; - DWORD SizeUnit; - DWORD FontStyleFlags; - DWORD Reserved; - DWORD Length; - WCHAR FamilyName[1]; -} EmfPlusFont; - typedef struct EmfPlusDrawDriverString { EmfPlusRecordHeader Header; @@ -4643,3 +4644,162 @@ GpStatus METAFILE_FillPath(GpMetafile *metafile, GpBrush *brush, GpPath *path) METAFILE_WriteRecords(metafile); return Ok; } + +static GpStatus METAFILE_AddFontObject(GpMetafile *metafile, GDIPCONST GpFont *font, DWORD *id) +{ + EmfPlusObject *object_record; + EmfPlusFont *font_record; + GpStatus stat; + INT fn_len; + INT style; + + *id = -1; + + if (metafile->metafile_type != MetafileTypeEmfPlusOnly && + metafile->metafile_type != MetafileTypeEmfPlusDual) + return Ok; + + /* The following cast is ugly, but GdipGetFontStyle does treat + its first parameter as const. */ + stat = GdipGetFontStyle((GpFont*)font, &style); + if (stat != Ok) + return stat; + + fn_len = lstrlenW(font->family->FamilyName); + stat = METAFILE_AllocateRecord(metafile, + FIELD_OFFSET(EmfPlusObject, ObjectData.font.FamilyName[(fn_len + 1) & ~1]), + (void**)&object_record); + if (stat != Ok) + return stat; + + *id = METAFILE_AddObjectId(metafile); + + object_record->Header.Type = EmfPlusRecordTypeObject; + object_record->Header.Flags = *id | ObjectTypeFont << 8; + + font_record = &object_record->ObjectData.font; + font_record->Version = VERSION_MAGIC2; + font_record->EmSize = font->emSize; + font_record->SizeUnit = font->unit; + font_record->FontStyleFlags = style; + font_record->Reserved = 0; + font_record->Length = fn_len; + + memcpy(font_record->FamilyName, font->family->FamilyName, + fn_len * sizeof(*font->family->FamilyName)); + + return Ok; +} + +GpStatus METAFILE_DrawDriverString(GpMetafile *metafile, GDIPCONST UINT16 *text, INT length, + GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush, + GDIPCONST PointF *positions, INT flags, GDIPCONST GpMatrix *matrix) +{ + DWORD brush_id; + DWORD font_id; + DWORD alloc_size; + GpStatus stat; + EmfPlusDrawDriverString *draw_string_record; + BYTE *cursor; + BOOL inline_color; + BOOL include_matrix = FALSE; + + if (length <= 0) + return InvalidParameter; + + if (metafile->metafile_type != MetafileTypeEmfPlusOnly && + metafile->metafile_type != MetafileTypeEmfPlusDual) + { + FIXME("metafile type not supported: %i\n", metafile->metafile_type); + return NotImplemented; + } + + stat = METAFILE_AddFontObject(metafile, font, &font_id); + if (stat != Ok) + return stat; + + inline_color = (brush->bt == BrushTypeSolidColor); + if (!inline_color) + { + stat = METAFILE_AddBrushObject(metafile, brush, &brush_id); + if (stat != Ok) + return stat; + } + + if (matrix) + { + BOOL identity; + + stat = GdipIsMatrixIdentity(matrix, &identity); + if (stat != Ok) + return stat; + + include_matrix = !identity; + } + + alloc_size = FIELD_OFFSET(EmfPlusDrawDriverString, VariableData) + + length * (sizeof(*text) + sizeof(*positions)); + + if (include_matrix) + alloc_size += sizeof(*matrix); + + /* Pad record to DWORD alignment. */ + alloc_size = (alloc_size + 3) & ~3; + + stat = METAFILE_AllocateRecord(metafile, alloc_size, (void**)&draw_string_record); + if (stat != Ok) + return stat; + + draw_string_record->Header.Type = EmfPlusRecordTypeDrawDriverString; + draw_string_record->Header.Flags = font_id; + draw_string_record->DriverStringOptionsFlags = flags; + draw_string_record->MatrixPresent = include_matrix; + draw_string_record->GlyphCount = length; + + if (inline_color) + { + draw_string_record->Header.Flags |= 0x8000; + draw_string_record->brush.Color = ((GpSolidFill*)brush)->color; + } + else + draw_string_record->brush.BrushId = brush_id; + + cursor = &draw_string_record->VariableData[0]; + + memcpy(cursor, text, length * sizeof(*text)); + cursor += length * sizeof(*text); + + if (flags & DriverStringOptionsRealizedAdvance) + { + static BOOL fixme_written = FALSE; + + /* Native never writes DriverStringOptionsRealizedAdvance. Instead, + in the case of RealizedAdvance, each glyph position is computed + and serialized. + + While native GDI+ is capable of playing back metafiles with this + flag set, it is possible that some application might rely on + metafiles produced from GDI+ not setting this flag. Ideally we + would also compute the position of each glyph here, serialize those + values, and not set DriverStringOptionsRealizedAdvance. */ + if (!fixme_written) + { + fixme_written = TRUE; + FIXME("serializing RealizedAdvance flag and single GlyphPos with padding\n"); + } + + *((PointF*)cursor) = *positions; + } + else + memcpy(cursor, positions, length * sizeof(*positions)); + + if (include_matrix) + { + cursor += length * sizeof(*positions); + memcpy(cursor, matrix, sizeof(*matrix)); + } + + METAFILE_WriteRecords(metafile); + + return Ok; +} diff --git a/dlls/gdiplus/tests/metafile.c b/dlls/gdiplus/tests/metafile.c index 986989bb1c..371a3c21b4 100644 --- a/dlls/gdiplus/tests/metafile.c +++ b/dlls/gdiplus/tests/metafile.c @@ -2956,11 +2956,11 @@ static void test_restoredc(void) static const emfplus_record drawdriverstring_records[] = { { EMR_HEADER }, { EmfPlusRecordTypeHeader }, - { EmfPlusRecordTypeObject, ObjectTypeFont << 8, 1, 0 }, - { EmfPlusRecordTypeDrawDriverString, 0x8000, 1, 0 }, - { EmfPlusRecordTypeObject, (ObjectTypeFont << 8) | 1, 1 }, - { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 2, 1, 1 }, - { EmfPlusRecordTypeDrawDriverString, 0x1, 1, 1 }, + { EmfPlusRecordTypeObject, ObjectTypeFont << 8 }, + { EmfPlusRecordTypeDrawDriverString, 0x8000 }, + { EmfPlusRecordTypeObject, (ObjectTypeFont << 8) | 1 }, + { EmfPlusRecordTypeObject, (ObjectTypeBrush << 8) | 2 }, + { EmfPlusRecordTypeDrawDriverString, 0x1 }, { EmfPlusRecordTypeEndOfFile }, { EMR_EOF }, { 0 } @@ -3022,14 +3022,14 @@ static void test_drawdriverstring(void)
stat = GdipDrawDriverString(graphics, L"Test", 4, solidfont, solidbrush, solidpos, DriverStringOptionsCmapLookup, matrix); - todo_wine expect(Ok, stat); + expect(Ok, stat);
stat = GdipSetMatrixElements(matrix, 1.5, 0.0, 0.0, 1.5, 0.0, 0.0); expect(Ok, stat);
stat = GdipDrawDriverString(graphics, L"Test ", 5, hatchfont, hatchbrush, &hatchpos, DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, matrix); - todo_wine expect(Ok, stat); + expect(Ok, stat);
stat = GdipDeleteGraphics(graphics); graphics = NULL;