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