Signed-off-by: Shawn M. Chapla <schapla(a)codeweavers.com>
---
v3:
- Remove unnecessary else in draw_driver_string.
dlls/gdiplus/gdiplus_private.h | 3 +
dlls/gdiplus/graphics.c | 4 +
dlls/gdiplus/metafile.c | 182 +++++++++++++++++++++++++++++++--
dlls/gdiplus/tests/metafile.c | 14 +--
4 files changed, 185 insertions(+), 18 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..5194719fd6 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -7427,6 +7427,10 @@ static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text,
if (length == -1)
length = lstrlenW(text);
+ if (graphics->image && graphics->image->type == ImageTypeMetafile)
+ return METAFILE_DrawDriverString((GpMetafile*)graphics->image, text, length, font,
+ format, brush, positions, flags, matrix);
+
if (graphics->hdc && !graphics->alpha_hdc &&
brush->bt == BrushTypeSolidColor &&
(((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
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;
--
2.27.0