diff --git a/SharpEmf.sln.DotSettings b/SharpEmf.sln.DotSettings
index 8dd08cb..9b7b738 100644
--- a/SharpEmf.sln.DotSettings
+++ b/SharpEmf.sln.DotSettings
@@ -11,6 +11,8 @@
True
True
True
+ True
+ True
True
True
True
@@ -24,13 +26,16 @@
True
True
True
+ True
True
True
True
True
True
+ True
True
True
+ True
True
True
True
@@ -45,6 +50,7 @@
True
True
True
+ True
True
True
True
@@ -60,11 +66,18 @@
True
True
True
+ True
+ True
True
+ True
True
True
+ True
True
+ True
+ True
True
+ True
True
True
True
@@ -91,21 +104,31 @@
True
True
True
+ True
+ True
True
True
+ True
True
True
True
+ True
True
True
True
True
True
+ True
True
True
True
True
True
+ True
+ True
+ True
+ True
+ True
True
True
True
@@ -116,5 +139,8 @@
True
True
True
+ True
+ True
+ True
True
True
\ No newline at end of file
diff --git a/src/SharpEmf/Enums/BackgroundMode.cs b/src/SharpEmf/Enums/BackgroundMode.cs
new file mode 100644
index 0000000..20a3a97
--- /dev/null
+++ b/src/SharpEmf/Enums/BackgroundMode.cs
@@ -0,0 +1,21 @@
+using JetBrains.Annotations;
+
+namespace SharpEmf.Enums;
+
+///
+/// Used to specify the background mode to be used with text, hatched brushes, and pen styles that are not solid.
+/// The background mode determines how to combine the background with foreground text, hatched brushes, and pen styles that are not solid lines
+///
+[PublicAPI]
+public enum BackgroundMode : uint
+{
+ ///
+ /// Background remains untouched
+ ///
+ TRANSPARENT = 0x0001,
+
+ ///
+ /// Background is filled with the current background color before the text, hatched brush, or pen is drawn
+ ///
+ OPAQUE = 0x0002
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Enums/EmfRecordType.cs b/src/SharpEmf/Enums/EmfRecordType.cs
index 1944c40..c6ea5ff 100644
--- a/src/SharpEmf/Enums/EmfRecordType.cs
+++ b/src/SharpEmf/Enums/EmfRecordType.cs
@@ -75,6 +75,37 @@ public enum EmfRecordType : uint
///
EMR_POLYPOLYGON = 0x00000008,
+ ///
+ /// Defines the window extent
+ ///
+ EMR_SETWINDOWEXTEX = 0x00000009,
+
+ ///
+ /// Defines the window origin
+ ///
+ EMR_SETWINDOWORGEX = 0x0000000A,
+
+ ///
+ /// Defines the viewport extent
+ ///
+ EMR_SETVIEWPORTEXTEX = 0x0000000B,
+
+ ///
+ /// Defines the viewport origin
+ ///
+ EMR_SETVIEWPORTORGEX = 0x0000000C,
+
+ ///
+ /// Defines the mapping mode, which defines the unit of measure used to transform page space units into device space units,
+ /// and defines the orientation of the device's X and Y axes
+ ///
+ EMR_SETMAPMODE = 0x00000011,
+
+ ///
+ /// Defines the background mix mode, which is used with text, hatched brushes, and pen styles that are not solid lines
+ ///
+ EMR_SETBKMODE = 0x00000012,
+
///
/// Defines polygon fill mode
///
@@ -110,6 +141,26 @@ public enum EmfRecordType : uint
///
EMR_INTERSECTCLIPRECT = 0x0000001E,
+ ///
+ /// Saves the current state of the playback device context in an array of states
+ ///
+ EMR_SAVEDC = 0x00000021,
+
+ ///
+ /// Restores the playback device context to the specified state, which was saved by a preceding record
+ ///
+ EMR_RESTOREDC = 0x00000022,
+
+ ///
+ /// Defines a two-dimensional linear transform between world space and page space
+ ///
+ EMR_SETWORLDTRANSFORM = 0x00000023,
+
+ ///
+ /// Redefines the world transform by using the specified mode
+ ///
+ EMR_MODIFYWORLDTRANSFORM = 0x00000024,
+
///
/// Selects an object in the playback device context, which is identified by its index in the EMF object table
///
@@ -369,6 +420,11 @@ public enum EmfRecordType : uint
///
EMR_POLYDRAW16 = 0x0000005C,
+ ///
+ /// Defines an extended logical pen that has the specified style, width, color, and brush attributes
+ ///
+ EMR_EXTCREATEPEN = 0x0000005F,
+
///
/// Draws one or more ASCII text strings using the current font and text colors
///
diff --git a/src/SharpEmf/Enums/MapMode.cs b/src/SharpEmf/Enums/MapMode.cs
new file mode 100644
index 0000000..06e3af8
--- /dev/null
+++ b/src/SharpEmf/Enums/MapMode.cs
@@ -0,0 +1,44 @@
+using JetBrains.Annotations;
+
+namespace SharpEmf.Enums;
+
+///
+/// Used to define the unit of measure for transforming page space units into device space units and for defining the orientation of the drawing axes
+///
+[PublicAPI]
+public enum MapMode : uint
+{
+ ///
+ /// Each logical unit is mapped to one device pixel. Positive x is to the right; positive y is down
+ ///
+ MM_TEXT = 0x01,
+ ///
+ /// Each logical unit is mapped to 0.1 millimeter. Positive x is to the right; positive y is up
+ ///
+ MM_LOMETRIC = 0x02,
+ ///
+ /// Each logical unit is mapped to 0.01 millimeter. Positive x is to the right; positive y is up
+ ///
+ MM_HIMETRIC = 0x03,
+ ///
+ /// Each logical unit is mapped to 0.01 inch. Positive x is to the right; positive y is up
+ ///
+ MM_LOENGLISH = 0x04,
+ ///
+ ///Each logical unit is mapped to 0.001 inch. Positive x is to the right; positive y is up
+ ///
+ MM_HIENGLISH = 0x05,
+ ///
+ /// Each logical unit is mapped to one-twentieth of a printer's point (1/1440 inch, also called a "twip"). Positive x is to the right; positive y is up
+ ///
+ MM_TWIPS = 0x06,
+ ///
+ /// Logical units are isotropic; that is, they are mapped to arbitrary units with equally scaled axes.
+ /// Thus, one unit along the x-axis is equal to one unit along the y-axis
+ ///
+ MM_ISOTROPIC = 0x07,
+ ///
+ /// Logical units are anisotropic; that is, they are mapped to arbitrary units with arbitrarily scaled axes
+ ///
+ MM_ANISOTROPIC = 0x08
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Enums/ModifyWorldTransformMode.cs b/src/SharpEmf/Enums/ModifyWorldTransformMode.cs
new file mode 100644
index 0000000..34bfef3
--- /dev/null
+++ b/src/SharpEmf/Enums/ModifyWorldTransformMode.cs
@@ -0,0 +1,30 @@
+using JetBrains.Annotations;
+
+namespace SharpEmf.Enums;
+
+///
+/// Defines modes for changing the world-space to page-space transform that is currently defined in the playback device context
+///
+[PublicAPI]
+public enum ModifyWorldTransformMode : uint
+{
+ ///
+ /// Reset the current transform using the identity matrix. In this mode, the specified transform data is ignored
+ ///
+ MWT_IDENTITY = 0x01,
+
+ ///
+ /// Multiply the current transform. In this mode, the specified transform data is the left multiplicand, and the current transform is the right multiplicand
+ ///
+ MWT_LEFTMULTIPLY = 0x02,
+
+ ///
+ /// Multiply the current transform. In this mode, the specified transform data is the right multiplicand, and the current transform is the left multiplicand
+ ///
+ MWT_RIGHTMULTIPLY = 0x03,
+
+ ///
+ /// Set the current transform to the specified transform data
+ ///
+ MWT_SET = 0x04
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Enums/PenStyle.cs b/src/SharpEmf/Enums/PenStyle.cs
new file mode 100644
index 0000000..efc98eb
--- /dev/null
+++ b/src/SharpEmf/Enums/PenStyle.cs
@@ -0,0 +1,105 @@
+using JetBrains.Annotations;
+
+namespace SharpEmf.Enums;
+
+///
+/// Defines the attributes of pens that can be used in graphics operations.
+/// A pen style is a combination of pen type, line style, line cap, and line join
+///
+[PublicAPI]
+public enum PenStyle : uint
+{
+ ///
+ /// A pen type that specifies a line with a width of one logical unit and a style that is a solid color
+ ///
+ PS_COSMETIC = 0x00000000,
+
+ ///
+ /// A line cap that specifies round ends
+ ///
+ PS_ENDCAP_ROUND = 0x00000000,
+
+ ///
+ /// A line join that specifies round joins
+ ///
+ PS_JOIN_ROUND = 0x00000000,
+
+ ///
+ /// A line style that is a solid color
+ ///
+ PS_SOLID = 0x00000000,
+
+ ///
+ /// A line style that is dashed
+ ///
+ PS_DASH = 0x00000001,
+
+ ///
+ /// A line style that is dotted
+ ///
+ PS_DOT = 0x00000002,
+
+ ///
+ /// A line style that consists of alternating dashes and dots
+ ///
+ PS_DASHDOT = 0x00000003,
+
+ ///
+ /// A line style that consists of dashes and double dots
+ ///
+ PS_DASHDOTDOT = 0x00000004,
+
+ ///
+ /// A line style that is invisible
+ ///
+ PS_NULL = 0x00000005,
+
+ ///
+ /// A line style that is a solid color
+ ///
+ ///
+ /// When this style is specified in a drawing record that takes a bounding rectangle,
+ /// the dimensions of the figure are shrunk so that it fits entirely in the bounding rectangle, considering the width of the pen
+ ///
+ PS_INSIDEFRAME = 0x00000006,
+
+ ///
+ /// A line style that is defined by a styling array, which specifies the lengths of dashes and gaps in the line
+ ///
+ PS_USERSTYLE = 0x00000007,
+
+ ///
+ /// A line style in which every other pixel is set. This style is applicable only to a pen type of
+ ///
+ PS_ALTERNATE = 0x00000008,
+
+ ///
+ /// A line cap that specifies square ends
+ ///
+ PS_ENDCAP_SQUARE = 0x00000100,
+
+ ///
+ /// A line cap that specifies flat ends
+ ///
+ PS_ENDCAP_FLAT = 0x00000200,
+
+ ///
+ /// A line join that specifies beveled joins
+ ///
+ PS_JOIN_BEVEL = 0x00001000,
+
+ // TODO: add cref
+ ///
+ /// A line join that specifies mitered joins when the lengths of the joins are within the current miter length limit.
+ /// If the lengths of the joins exceed the miter limit, beveled joins are specified
+ ///
+ ///
+ /// The miter length limit is a metafile state property that is set by the EMR_SETMITERLIMIT record
+ ///
+ PS_JOIN_MITER = 0x00002000,
+
+ ///
+ /// A pen type that specifies a line with a width that is measured in logical units and a style that can contain any of the attributes of a brush
+ ///
+ PS_GEOMETRIC = 0x00010000
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Extensions/StreamExtensions.cs b/src/SharpEmf/Extensions/StreamExtensions.cs
index a6ff15a..7aca345 100644
--- a/src/SharpEmf/Extensions/StreamExtensions.cs
+++ b/src/SharpEmf/Extensions/StreamExtensions.cs
@@ -68,6 +68,11 @@ internal static T ReadEnum(this Stream stream) where T : struct, Enum
internal static byte[] ReadByteArray(this Stream stream, int length)
{
+ if (length == 0)
+ {
+ return Array.Empty();
+ }
+
var buffer = new byte[length];
stream.ReadExactly(buffer);
return buffer;
@@ -75,6 +80,11 @@ internal static byte[] ReadByteArray(this Stream stream, int length)
internal static uint[] ReadUInt32Array(this Stream stream, int length)
{
+ if (length == 0)
+ {
+ return Array.Empty();
+ }
+
Span buffer = length <= 1024 ? stackalloc byte[length * 4] : new byte[length * 4];
stream.ReadExactly(buffer);
diff --git a/src/SharpEmf/Objects/ColorRef.cs b/src/SharpEmf/Objects/ColorRef.cs
index aaef974..e226f97 100644
--- a/src/SharpEmf/Objects/ColorRef.cs
+++ b/src/SharpEmf/Objects/ColorRef.cs
@@ -1,6 +1,4 @@
-using System.Diagnostics;
-using JetBrains.Annotations;
-using SharpEmf.Exceptions;
+using JetBrains.Annotations;
namespace SharpEmf.Objects;
diff --git a/src/SharpEmf/Objects/LogPenEx.cs b/src/SharpEmf/Objects/LogPenEx.cs
new file mode 100644
index 0000000..5047ecf
--- /dev/null
+++ b/src/SharpEmf/Objects/LogPenEx.cs
@@ -0,0 +1,120 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Extensions;
+
+namespace SharpEmf.Objects;
+
+///
+/// Specifies the style, width, and color of an extended logical pen
+///
+///
+/// The following table shows the relationship between the , , and fields in this object:
+///
+/// | BrushStyle | ColorRef | BrushHatch |
+/// |-----------------|---------------------------------------|--------------------------------------------------------------|
+/// | BS_SOLID | Specifies the color of lines drawn by | Not used and is ignored |
+/// | | the pen | |
+/// |-----------------|---------------------------------------|--------------------------------------------------------------|
+/// | BS_NULL | Not used and is ignored | Not used and is ignored |
+/// |-----------------|---------------------------------------|--------------------------------------------------------------|
+/// | BS_HATCHED | Specifies the foreground color of | Specifies the orientation of lines used to create the hatch |
+/// | | the hatch pattern | If PS_GEOMETRIC is not set in the PenStyle field, this field |
+/// | | | MUST be either HS_SOLIDTEXTCLR or HS_SOLIDBKCLR |
+/// |-----------------|---------------------------------------|--------------------------------------------------------------|
+/// | BS_PATTERN | The low-order 16-bits is a value from | Not used and is ignored |
+/// | | the ColorUsage enum | |
+/// |-----------------|---------------------------------------|--------------------------------------------------------------|
+/// | BS_DIBPATTERN | The low-order 16 bits is a value from | Not used and is ignored |
+/// | | the ColorUsage enum | |
+/// |-----------------|---------------------------------------|--------------------------------------------------------------|
+/// | BS_DIBPATTERNPT | The low-order 16 bits is a value from | Not used and is ignored |
+/// | | the ColorUsage enum | |
+/// |-----------------|---------------------------------------|--------------------------------------------------------------|
+///
+///
+[PublicAPI]
+public class LogPenEx
+{
+ ///
+ /// Specifies the pen style
+ ///
+ public PenStyle PenStyle { get; }
+
+ ///
+ /// Specifies the width of the line drawn by the pen
+ ///
+ ///
+ /// If the pen type in the field is , this value is the width in logical units;
+ /// otherwise, the width is specified in device units. If the pen type in the field is
+ /// , this value MUST be 0x00000001
+ ///
+ public uint Width { get; }
+
+ ///
+ /// Specifies a brush style for the pen
+ ///
+ ///
+ /// If the pen type in the field is , this value is either or
+ /// . The value of this field can be , but only if the line style specified in
+ /// is . The style SHOULD be used to specify a brush that has no effect
+ ///
+ public BrushStyle BrushStyle { get; }
+
+ ///
+ /// Specifies the color of the pen
+ ///
+ public ColorRef ColorRef { get; }
+
+ ///
+ /// The brush hatch pattern
+ ///
+ public HatchStyle BrushHatch { get; }
+
+ ///
+ /// The number of elements in the array specified in the StyleEntry field.
+ ///
+ ///
+ /// This value SHOULD be zero if does not specify
+ ///
+ public uint NumStyleEntries { get; }
+
+ ///
+ /// Defines the lengths of dashes and gaps in the line drawn by this pen when the value of is .
+ /// The array contains the number of entries specified by , but it is used as if it repeated indefinitely
+ ///
+ ///
+ /// The first entry in the array specifies the length of the first dash. The second entry specifies the length of the first gap.
+ /// Thereafter, lengths of dashes and gaps alternate. If the pen type in the field is ,
+ /// lengths are specified in logical units; otherwise, they are specified in device units.
+ ///
+ public IReadOnlyList StyleEntry { get; }
+
+ private LogPenEx(PenStyle penStyle, uint width, BrushStyle brushStyle, ColorRef colorRef, HatchStyle brushHatch, uint numStyleEntries, IReadOnlyList styleEntry)
+ {
+ PenStyle = penStyle;
+ Width = width;
+ BrushStyle = brushStyle;
+ ColorRef = colorRef;
+ BrushHatch = brushHatch;
+ NumStyleEntries = numStyleEntries;
+ StyleEntry = styleEntry;
+ }
+
+ public static LogPenEx Parse(Stream stream)
+ {
+ var penStyle = stream.ReadEnum();
+ var width = stream.ReadUInt32();
+ var brushStyle = stream.ReadEnum();
+ var colorRef = ColorRef.Parse(stream);
+ var brushHatch = stream.ReadEnum();
+ var numStyleEntries = stream.ReadUInt32();
+
+ var styleEntry = numStyleEntries == 0 ? Array.Empty() : new PenStyle[numStyleEntries];
+ for (int i = 0; i < numStyleEntries; i++)
+ {
+ styleEntry[i] = stream.ReadEnum();
+ }
+
+ return new LogPenEx(penStyle, width, brushStyle, colorRef, brushHatch, numStyleEntries, styleEntry);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Objects/XForm.cs b/src/SharpEmf/Objects/XForm.cs
index 4b76360..782a280 100644
--- a/src/SharpEmf/Objects/XForm.cs
+++ b/src/SharpEmf/Objects/XForm.cs
@@ -16,9 +16,13 @@ namespace SharpEmf.Objects;
/// | Operation | M11 | M12 | M21 | M22 |
/// |------------|---------------------------------|-------------------------------------|-----------------------------------|-------------------------------|
/// | Rotation | Cosine | Sine | Negative sine | Cosine |
+/// |------------|---------------------------------|-------------------------------------|-----------------------------------|-------------------------------|
/// | Scaling | Horizontal scaling component | Not used | Not used | Vertical Scaling Component |
+/// |------------|---------------------------------|-------------------------------------|-----------------------------------|-------------------------------|
/// | Shear | Not used | Horizontal Proportionality Constant | Vertical Proportionality Constant | Not used |
+/// |------------|---------------------------------|-------------------------------------|-----------------------------------|-------------------------------|
/// | Reflection | Horizontal Reflection Component | Not used | Not used | Vertical Reflection Component |
+/// |------------|---------------------------------|-------------------------------------|-----------------------------------|-------------------------------|
///
///
[PublicAPI]
diff --git a/src/SharpEmf/Records/Bitmap/EmrBitBlt.cs b/src/SharpEmf/Records/Bitmap/EmrBitBlt.cs
index 6ad4102..32388f5 100644
--- a/src/SharpEmf/Records/Bitmap/EmrBitBlt.cs
+++ b/src/SharpEmf/Records/Bitmap/EmrBitBlt.cs
@@ -147,6 +147,11 @@ private EmrBitBlt(
public static EmrBitBlt Parse(Stream stream, EmfRecordType recordType, uint size)
{
+ var positionBeforeParsing =
+ stream.Position -
+ // Base record fields
+ (Unsafe.SizeOf() + Unsafe.SizeOf());
+
var bounds = RectL.Parse(stream);
var xDest = stream.ReadInt32();
var yDest = stream.ReadInt32();
@@ -163,26 +168,8 @@ public static EmrBitBlt Parse(Stream stream, EmfRecordType recordType, uint size
var offBitsSrc = stream.ReadUInt32();
var cbBitsSrc = stream.ReadUInt32();
- var selfSizeWithoutBuffers =
- // Base record fields
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- // Self fields
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf() +
- Unsafe.SizeOf();
+ var positionAfterParsing = stream.Position;
+ var selfSizeWithoutBuffers = positionAfterParsing - positionBeforeParsing;
long seekOffset = 0;
if (offBmiSrc != 0)
diff --git a/src/SharpEmf/Records/Control/Header/EmfMetafileHeader.cs b/src/SharpEmf/Records/Control/Header/EmfMetafileHeader.cs
index 6837842..cb418a1 100644
--- a/src/SharpEmf/Records/Control/Header/EmfMetafileHeader.cs
+++ b/src/SharpEmf/Records/Control/Header/EmfMetafileHeader.cs
@@ -143,13 +143,8 @@ public static EmfMetafileHeader Parse(Stream stream, EmfRecordType recordType, u
var palEntries = stream.ReadUInt32();
- var device = new SizeL(
- cx: stream.ReadUInt32(),
- cy: stream.ReadUInt32());
-
- var millimeters = new SizeL(
- cx: stream.ReadUInt32(),
- cy: stream.ReadUInt32());
+ var device = SizeL.Parse(stream);
+ var millimeters = SizeL.Parse(stream);
var baseHeader = new EmfMetafileHeader(
recordType: recordType,
diff --git a/src/SharpEmf/Records/EnhancedMetafileRecord.cs b/src/SharpEmf/Records/EnhancedMetafileRecord.cs
index 48c3974..80c7019 100644
--- a/src/SharpEmf/Records/EnhancedMetafileRecord.cs
+++ b/src/SharpEmf/Records/EnhancedMetafileRecord.cs
@@ -12,6 +12,7 @@
using SharpEmf.Records.ObjectManipulation;
using SharpEmf.Records.PathBracket;
using SharpEmf.Records.State;
+using SharpEmf.Records.Transform;
namespace SharpEmf.Records;
@@ -50,12 +51,22 @@ public static EnhancedMetafileRecord Parse(Stream stream)
EmfRecordType.EMR_POLYLINETO => EmrPolyLineTo.Parse,
EmfRecordType.EMR_POLYPOLYLINE => EmrPolyPolyline.Parse,
EmfRecordType.EMR_POLYPOLYGON => EmrPolyPolygon.Parse,
+ EmfRecordType.EMR_SETWINDOWEXTEX => EmrSetWindowExtEx.Parse,
+ EmfRecordType.EMR_SETWINDOWORGEX => EmrSetWindowOrgEx.Parse,
+ EmfRecordType.EMR_SETVIEWPORTEXTEX => EmrSetViewportExtEx.Parse,
+ EmfRecordType.EMR_SETVIEWPORTORGEX => EmrSetViewportOrgEx.Parse,
+ EmfRecordType.EMR_SETMAPMODE => EmrSetMapMode.Parse,
+ EmfRecordType.EMR_SETBKMODE => EmrSetBkMode.Parse,
EmfRecordType.EMR_SETPOLYFILLMODE => EmrSetPolyfillMode.Parse,
EmfRecordType.EMR_SETPIXELV => EmrSetPixelV.Parse,
EmfRecordType.EMR_OFFSETCLIPRGN => EmrOffsetClipRgn.Parse,
EmfRecordType.EMR_MOVETOEX => EmrMoveToEx.Parse,
EmfRecordType.EMR_EXCLUDECLIPRECT => EmrExcludeClipRect.Parse,
EmfRecordType.EMR_INTERSECTCLIPRECT => EmrIntersectClipRect.Parse,
+ EmfRecordType.EMR_SAVEDC => EmrSaveDC.Parse,
+ EmfRecordType.EMR_RESTOREDC => EmrRestoreDC.Parse,
+ EmfRecordType.EMR_SETWORLDTRANSFORM => EmrSetWorldTransform.Parse,
+ EmfRecordType.EMR_MODIFYWORLDTRANSFORM => EmrModifyWorldTransform.Parse,
EmfRecordType.EMR_SELECTOBJECT => EmrSelectObject.Parse,
EmfRecordType.EMR_CREATEBRUSHINDIRECT => EmrCreateBrushIndirect.Parse,
EmfRecordType.EMR_DELETEOBJECT => EmrDeleteObject.Parse,
@@ -96,6 +107,7 @@ public static EnhancedMetafileRecord Parse(Stream stream)
EmfRecordType.EMR_POLYPOLYLINE16 => EmrPolyPolyline16.Parse,
EmfRecordType.EMR_POLYPOLYGON16 => EmrPolyPolygon16.Parse,
EmfRecordType.EMR_POLYDRAW16 => EmrPolyDraw16.Parse,
+ EmfRecordType.EMR_EXTCREATEPEN => EmrExtCreatePen.Parse,
EmfRecordType.EMR_POLYTEXTOUTA => EmrPolyTextOutA.Parse,
EmfRecordType.EMR_POLYTEXTOUTW => EmrPolyTextOutW.Parse,
EmfRecordType.EMR_DRAWESCAPE => EmrDrawEscape.Parse,
diff --git a/src/SharpEmf/Records/ObjectCreation/EmrExtCreatePen.cs b/src/SharpEmf/Records/ObjectCreation/EmrExtCreatePen.cs
new file mode 100644
index 0000000..b1b1b1d
--- /dev/null
+++ b/src/SharpEmf/Records/ObjectCreation/EmrExtCreatePen.cs
@@ -0,0 +1,116 @@
+using System.Runtime.CompilerServices;
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Extensions;
+using SharpEmf.Interfaces;
+using SharpEmf.Objects;
+
+namespace SharpEmf.Records.ObjectCreation;
+
+///
+[PublicAPI]
+public record EmrExtCreatePen : EnhancedMetafileRecord, IEmfParsable
+{
+ ///
+ /// Specifies the index of the extended logical pen object in the EMF object table
+ ///
+ ///
+ /// This index MUST be saved so that this object can be reused or modified
+ ///
+ public uint IhPen { get; }
+
+ ///
+ /// Specifies the offset from the start of this record to the DIB header if the record contains a DIB
+ ///
+ public uint OffBmi { get; }
+
+ ///
+ /// Specifies the size of the DIB header if the record contains a DIB
+ ///
+ public uint CbBmi { get; }
+
+ ///
+ /// Specifies the offset from the start of this record to the DIB bits if the record contains a DIB
+ ///
+ public uint OffBits { get; }
+
+ ///
+ /// Specifies the size of the DIB bits if the record contains a DIB
+ ///
+ public uint CbBits { get; }
+
+ ///
+ /// Specifies an extended logical pen with attributes including an optional line style array
+ ///
+ public LogPenEx Elp { get; }
+
+ ///
+ /// The Device Independent Bitmap header
+ ///
+ public IReadOnlyList BmiSrc { get; }
+
+ ///
+ /// The Device Independent Bitmap bits
+ ///
+ public IReadOnlyList BitsSrc { get; }
+
+ private EmrExtCreatePen(
+ EmfRecordType recordType,
+ uint size,
+ uint ihPen,
+ uint offBmi,
+ uint cbBmi,
+ uint offBits,
+ uint cbBits,
+ LogPenEx elp,
+ IReadOnlyList bmiSrc,
+ IReadOnlyList bitsSrc) : base(recordType, size)
+ {
+ IhPen = ihPen;
+ OffBmi = offBmi;
+ CbBmi = cbBmi;
+ OffBits = offBits;
+ CbBits = cbBits;
+ Elp = elp;
+ BmiSrc = bmiSrc;
+ BitsSrc = bitsSrc;
+ }
+
+ public static EmrExtCreatePen Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var positionBeforeParsing =
+ stream.Position -
+ // Base record fields
+ (Unsafe.SizeOf() + Unsafe.SizeOf());
+
+ var ihPen = stream.ReadUInt32();
+ var offBmi = stream.ReadUInt32();
+ var cbBmi = stream.ReadUInt32();
+ var offBits = stream.ReadUInt32();
+ var cbBits = stream.ReadUInt32();
+ var elp = LogPenEx.Parse(stream);
+
+ var positionAfterParsing = stream.Position;
+ var selfSizeWithoutBuffers = positionAfterParsing - positionBeforeParsing;
+
+ long seekOffset = 0;
+ // Is the second condition correct?
+ if (offBmi != 0 && offBmi > selfSizeWithoutBuffers)
+ {
+ seekOffset = offBmi - selfSizeWithoutBuffers;
+ stream.Seek(seekOffset, SeekOrigin.Current);
+ }
+
+ var bmiSrc = stream.ReadByteArray((int)cbBmi);
+ // Same as above
+ if (offBits != 0 && offBits > seekOffset + bmiSrc.Length + selfSizeWithoutBuffers)
+ {
+ seekOffset = offBits - (seekOffset + bmiSrc.Length + selfSizeWithoutBuffers);
+ stream.Seek(seekOffset, SeekOrigin.Current);
+ }
+
+ var bitsSrc = stream.ReadByteArray((int)cbBits);
+
+ return new EmrExtCreatePen(recordType, size, ihPen, offBmi, cbBmi, offBits, cbBits, elp, bmiSrc, bitsSrc);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/State/EmrRestoreDC.cs b/src/SharpEmf/Records/State/EmrRestoreDC.cs
new file mode 100644
index 0000000..719a69e
--- /dev/null
+++ b/src/SharpEmf/Records/State/EmrRestoreDC.cs
@@ -0,0 +1,33 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Extensions;
+using SharpEmf.Interfaces;
+
+namespace SharpEmf.Records.State;
+
+///
+[PublicAPI]
+public record EmrRestoreDC : EnhancedMetafileRecord, IEmfParsable
+{
+ ///
+ /// Specifies the saved state to restore relative to the current state
+ ///
+ ///
+ /// This value MUST be negative; –1 represents the state that was most recently saved on the stack, –2 the one before that, etc.
+ ///
+ /// The stack can contain state information for multiple instances of the playback device context.
+ /// When a state is restored, all state instances that were saved more recently MUST be discarded
+ ///
+ public int SavedDC { get; }
+
+ private EmrRestoreDC(EmfRecordType recordType, uint size, int savedDC) : base(recordType, size)
+ {
+ SavedDC = savedDC;
+ }
+
+ public static EmrRestoreDC Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var savedDC = stream.ReadInt32();
+ return new EmrRestoreDC(recordType, size, savedDC);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/State/EmrSaveDC.cs b/src/SharpEmf/Records/State/EmrSaveDC.cs
new file mode 100644
index 0000000..f7a9f9a
--- /dev/null
+++ b/src/SharpEmf/Records/State/EmrSaveDC.cs
@@ -0,0 +1,19 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Interfaces;
+
+namespace SharpEmf.Records.State;
+
+///
+[PublicAPI]
+public record EmrSaveDC : EnhancedMetafileRecord, IEmfParsable
+{
+ private EmrSaveDC(EmfRecordType recordType, uint size) : base(recordType, size)
+ {
+ }
+
+ public static EmrSaveDC Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ return new EmrSaveDC(recordType, size);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/State/EmrSetBkMode.cs b/src/SharpEmf/Records/State/EmrSetBkMode.cs
new file mode 100644
index 0000000..3ff7635
--- /dev/null
+++ b/src/SharpEmf/Records/State/EmrSetBkMode.cs
@@ -0,0 +1,24 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Extensions;
+using SharpEmf.Interfaces;
+
+namespace SharpEmf.Records.State;
+
+///
+[PublicAPI]
+public record EmrSetBkMode : EnhancedMetafileRecord, IEmfParsable
+{
+ public BackgroundMode BackgroundMode { get; }
+
+ private EmrSetBkMode(EmfRecordType Type, uint Size, BackgroundMode backgroundMode) : base(Type, Size)
+ {
+ BackgroundMode = backgroundMode;
+ }
+
+ public static EmrSetBkMode Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var backgroundMode = stream.ReadEnum();
+ return new EmrSetBkMode(recordType, size, backgroundMode);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/State/EmrSetMapMode.cs b/src/SharpEmf/Records/State/EmrSetMapMode.cs
new file mode 100644
index 0000000..62adc36
--- /dev/null
+++ b/src/SharpEmf/Records/State/EmrSetMapMode.cs
@@ -0,0 +1,24 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Extensions;
+using SharpEmf.Interfaces;
+
+namespace SharpEmf.Records.State;
+
+///
+[PublicAPI]
+public record EmrSetMapMode : EnhancedMetafileRecord, IEmfParsable
+{
+ public MapMode MapMode { get; }
+
+ private EmrSetMapMode(EmfRecordType Type, uint Size, MapMode mapMode) : base(Type, Size)
+ {
+ MapMode = mapMode;
+ }
+
+ public static EmrSetMapMode Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var mapMode = stream.ReadEnum();
+ return new EmrSetMapMode(recordType, size, mapMode);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/State/EmrSetViewportExtEx.cs b/src/SharpEmf/Records/State/EmrSetViewportExtEx.cs
new file mode 100644
index 0000000..462b39c
--- /dev/null
+++ b/src/SharpEmf/Records/State/EmrSetViewportExtEx.cs
@@ -0,0 +1,24 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Interfaces;
+using SharpEmf.WmfTypes;
+
+namespace SharpEmf.Records.State;
+
+///
+[PublicAPI]
+public record EmrSetViewportExtEx : EnhancedMetafileRecord, IEmfParsable
+{
+ public SizeL Extent { get; }
+
+ private EmrSetViewportExtEx(EmfRecordType recordType, uint size, SizeL extent) : base(recordType, size)
+ {
+ Extent = extent;
+ }
+
+ public static EmrSetViewportExtEx Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var extent = SizeL.Parse(stream);
+ return new EmrSetViewportExtEx(recordType, size, extent);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/State/EmrSetViewportOrgEx.cs b/src/SharpEmf/Records/State/EmrSetViewportOrgEx.cs
new file mode 100644
index 0000000..9be2c44
--- /dev/null
+++ b/src/SharpEmf/Records/State/EmrSetViewportOrgEx.cs
@@ -0,0 +1,24 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Interfaces;
+using SharpEmf.WmfTypes;
+
+namespace SharpEmf.Records.State;
+
+///
+[PublicAPI]
+public record EmrSetViewportOrgEx : EnhancedMetafileRecord, IEmfParsable
+{
+ public PointL Origin { get; }
+
+ private EmrSetViewportOrgEx(EmfRecordType recordType, uint size, PointL origin) : base(recordType, size)
+ {
+ Origin = origin;
+ }
+
+ public static EmrSetViewportOrgEx Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var origin = PointL.Parse(stream);
+ return new EmrSetViewportOrgEx(recordType, size, origin);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/State/EmrSetWindowExtEx.cs b/src/SharpEmf/Records/State/EmrSetWindowExtEx.cs
new file mode 100644
index 0000000..49f8d15
--- /dev/null
+++ b/src/SharpEmf/Records/State/EmrSetWindowExtEx.cs
@@ -0,0 +1,24 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Interfaces;
+using SharpEmf.WmfTypes;
+
+namespace SharpEmf.Records.State;
+
+///
+[PublicAPI]
+public record EmrSetWindowExtEx : EnhancedMetafileRecord, IEmfParsable
+{
+ public SizeL Extent { get; }
+
+ private EmrSetWindowExtEx(EmfRecordType recordType, uint size, SizeL extent) : base(recordType, size)
+ {
+ Extent = extent;
+ }
+
+ public static EmrSetWindowExtEx Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var extent = SizeL.Parse(stream);
+ return new EmrSetWindowExtEx(recordType, size, extent);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/State/EmrSetWindowOrgEx.cs b/src/SharpEmf/Records/State/EmrSetWindowOrgEx.cs
new file mode 100644
index 0000000..9e59845
--- /dev/null
+++ b/src/SharpEmf/Records/State/EmrSetWindowOrgEx.cs
@@ -0,0 +1,24 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Interfaces;
+using SharpEmf.WmfTypes;
+
+namespace SharpEmf.Records.State;
+
+///
+[PublicAPI]
+public record EmrSetWindowOrgEx : EnhancedMetafileRecord, IEmfParsable
+{
+ public PointL Origin { get; }
+
+ private EmrSetWindowOrgEx(EmfRecordType recordType, uint size, PointL origin) : base(recordType, size)
+ {
+ Origin = origin;
+ }
+
+ public static EmrSetWindowOrgEx Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var origin = PointL.Parse(stream);
+ return new EmrSetWindowOrgEx(recordType, size, origin);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/Transform/EmrModifyWorldTransform.cs b/src/SharpEmf/Records/Transform/EmrModifyWorldTransform.cs
new file mode 100644
index 0000000..d648775
--- /dev/null
+++ b/src/SharpEmf/Records/Transform/EmrModifyWorldTransform.cs
@@ -0,0 +1,36 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Extensions;
+using SharpEmf.Interfaces;
+using SharpEmf.Objects;
+
+namespace SharpEmf.Records.Transform;
+
+///
+[PublicAPI]
+public record EmrModifyWorldTransform : EnhancedMetafileRecord, IEmfParsable
+{
+ ///
+ /// Used according to the to define a new value for the world-space to page-space transform in the playback device context
+ ///
+ public XForm XForm { get; }
+
+ ///
+ /// Specifies how the transform specified in is used
+ ///
+ public ModifyWorldTransformMode ModifyWorldTransformMode { get; }
+
+ private EmrModifyWorldTransform(EmfRecordType Type, uint Size, XForm xForm, ModifyWorldTransformMode modifyWorldTransformMode) : base(Type, Size)
+ {
+ XForm = xForm;
+ ModifyWorldTransformMode = modifyWorldTransformMode;
+ }
+
+ public static EmrModifyWorldTransform Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var xForm = XForm.Parse(stream);
+ var modifyWorldTransformMode = stream.ReadEnum();
+
+ return new EmrModifyWorldTransform(recordType, size, xForm, modifyWorldTransformMode);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/Records/Transform/EmrSetWorldTransform.cs b/src/SharpEmf/Records/Transform/EmrSetWorldTransform.cs
new file mode 100644
index 0000000..365afc8
--- /dev/null
+++ b/src/SharpEmf/Records/Transform/EmrSetWorldTransform.cs
@@ -0,0 +1,24 @@
+using JetBrains.Annotations;
+using SharpEmf.Enums;
+using SharpEmf.Interfaces;
+using SharpEmf.Objects;
+
+namespace SharpEmf.Records.Transform;
+
+///
+[PublicAPI]
+public record EmrSetWorldTransform : EnhancedMetafileRecord, IEmfParsable
+{
+ public XForm XForm { get; }
+
+ private EmrSetWorldTransform(EmfRecordType Type, uint Size, XForm xForm) : base(Type, Size)
+ {
+ XForm = xForm;
+ }
+
+ public static EmrSetWorldTransform Parse(Stream stream, EmfRecordType recordType, uint size)
+ {
+ var xForm = XForm.Parse(stream);
+ return new EmrSetWorldTransform(recordType, size, xForm);
+ }
+}
\ No newline at end of file
diff --git a/src/SharpEmf/WmfTypes/SizeL.cs b/src/SharpEmf/WmfTypes/SizeL.cs
index 8e14f79..5265923 100644
--- a/src/SharpEmf/WmfTypes/SizeL.cs
+++ b/src/SharpEmf/WmfTypes/SizeL.cs
@@ -1,4 +1,5 @@
using JetBrains.Annotations;
+using SharpEmf.Extensions;
namespace SharpEmf.WmfTypes;
@@ -18,9 +19,13 @@ public readonly struct SizeL
///
public uint Cy { get; }
- public SizeL(uint cx, uint cy)
+ private SizeL(uint cx, uint cy)
{
Cx = cx;
Cy = cy;
}
+
+ public static SizeL Parse(Stream stream) => new(
+ cx: stream.ReadUInt32(),
+ cy: stream.ReadUInt32());
}
\ No newline at end of file