From cdf5546a1bdec29c555790c23af8dd9a45541bb6 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sun, 6 Jan 2019 15:46:28 +0000 Subject: [PATCH] #24 add the missing operations for the graphics state --- .../PublicApiScannerTests.cs | 117 ++++++++++++++++++ .../BeginCompatibilitySection.cs | 46 +++++++ .../Compatibility/EndCompatibilitySection.cs | 46 +++++++ .../InlineImages/BeginInlineImage.cs | 46 +++++++ .../InlineImages/BeginInlineImageData.cs | 46 +++++++ .../Operations/InlineImages/EndInlineImage.cs | 46 +++++++ .../MarkedContent/BeginMarkedContent.cs | 55 ++++++++ .../BeginMarkedContentWithProperties.cs | 88 +++++++++++++ .../DesignateMarkedContentPoint.cs | 77 +----------- ...signateMarkedContentPointWithProperties.cs | 88 +++++++++++++ .../MarkedContent/EndMarkedContent.cs | 46 +++++++ .../Graphics/Operations/PaintShading.cs | 57 +++++++++ .../Graphics/Operations/SetNonStrokeColor.cs | 60 +++++++++ .../Operations/SetNonStrokeColorAdvanced.cs | 89 +++++++++++++ .../Operations/SetNonStrokeColorDeviceCmyk.cs | 2 +- .../Operations/SetNonStrokeColorSpace.cs | 55 ++++++++ .../Graphics/Operations/SetStrokeColor.cs | 60 +++++++++ .../Operations/SetStrokeColorAdvanced.cs | 89 +++++++++++++ .../Operations/SetStrokeColorSpace.cs | 55 ++++++++ .../TextState/Type3SetGlyphWidth.cs | 62 ++++++++++ .../Type3SetGlyphWidthAndBoundingBox.cs | 83 +++++++++++++ ...ReflectionGraphicsStateOperationFactory.cs | 51 ++++++++ 22 files changed, 1288 insertions(+), 76 deletions(-) create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/Compatibility/BeginCompatibilitySection.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/Compatibility/EndCompatibilitySection.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/InlineImages/BeginInlineImage.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/InlineImages/BeginInlineImageData.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/InlineImages/EndInlineImage.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContent.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContentWithProperties.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPointWithProperties.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/EndMarkedContent.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/PaintShading.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColor.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorAdvanced.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorSpace.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColor.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorAdvanced.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorSpace.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/TextState/Type3SetGlyphWidth.cs create mode 100644 src/UglyToad.PdfPig/Graphics/Operations/TextState/Type3SetGlyphWidthAndBoundingBox.cs diff --git a/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs b/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs index e72e5f84..2499eeab 100644 --- a/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs +++ b/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs @@ -1,6 +1,9 @@ namespace UglyToad.PdfPig.Tests { using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using PdfPig.Graphics.Operations; using Xunit; public class PublicApiScannerTests @@ -66,6 +69,8 @@ "UglyToad.PdfPig.Graphics.IOperationContext", "UglyToad.PdfPig.Graphics.Operations.ClippingPaths.ModifyClippingByEvenOddIntersect", "UglyToad.PdfPig.Graphics.Operations.ClippingPaths.ModifyClippingByNonZeroWindingIntersect", + "UglyToad.PdfPig.Graphics.Operations.Compatibility.BeginCompatibilitySection", + "UglyToad.PdfPig.Graphics.Operations.Compatibility.EndCompatibilitySection", "UglyToad.PdfPig.Graphics.Operations.CloseAndStrokePath", "UglyToad.PdfPig.Graphics.Operations.CloseFillPathEvenOddRuleAndStroke", "UglyToad.PdfPig.Graphics.Operations.CloseFillPathNonZeroWindingAndStroke", @@ -83,10 +88,16 @@ "UglyToad.PdfPig.Graphics.Operations.General.SetLineWidth", "UglyToad.PdfPig.Graphics.Operations.General.SetMiterLimit", "UglyToad.PdfPig.Graphics.Operations.IGraphicsStateOperation", + "UglyToad.PdfPig.Graphics.Operations.InlineImages.BeginInlineImage", + "UglyToad.PdfPig.Graphics.Operations.InlineImages.BeginInlineImageData", + "UglyToad.PdfPig.Graphics.Operations.InlineImages.EndInlineImage", "UglyToad.PdfPig.Graphics.Operations.InvokeNamedXObject", "UglyToad.PdfPig.Graphics.Operations.MarkedContent.BeginMarkedContent", + "UglyToad.PdfPig.Graphics.Operations.MarkedContent.BeginMarkedContentWithProperties", "UglyToad.PdfPig.Graphics.Operations.MarkedContent.DesignateMarkedContentPoint", + "UglyToad.PdfPig.Graphics.Operations.MarkedContent.DesignateMarkedContentPointWithProperties", "UglyToad.PdfPig.Graphics.Operations.MarkedContent.EndMarkedContent", + "UglyToad.PdfPig.Graphics.Operations.PaintShading", "UglyToad.PdfPig.Graphics.Operations.PathConstruction.AppendDualControlPointBezierCurve", "UglyToad.PdfPig.Graphics.Operations.PathConstruction.AppendEndControlPointBezierCurve", "UglyToad.PdfPig.Graphics.Operations.PathConstruction.AppendRectangle", @@ -94,12 +105,18 @@ "UglyToad.PdfPig.Graphics.Operations.PathConstruction.AppendStraightLineSegment", "UglyToad.PdfPig.Graphics.Operations.PathConstruction.BeginNewSubpath", "UglyToad.PdfPig.Graphics.Operations.PathConstruction.CloseSubpath", + "UglyToad.PdfPig.Graphics.Operations.SetNonStrokeColor", + "UglyToad.PdfPig.Graphics.Operations.SetNonStrokeColorAdvanced", "UglyToad.PdfPig.Graphics.Operations.SetNonStrokeColorDeviceCmyk", "UglyToad.PdfPig.Graphics.Operations.SetNonStrokeColorDeviceGray", "UglyToad.PdfPig.Graphics.Operations.SetNonStrokeColorDeviceRgb", + "UglyToad.PdfPig.Graphics.Operations.SetNonStrokeColorSpace", "UglyToad.PdfPig.Graphics.Operations.SetStrokeColorDeviceCmyk", + "UglyToad.PdfPig.Graphics.Operations.SetStrokeColor", + "UglyToad.PdfPig.Graphics.Operations.SetStrokeColorAdvanced", "UglyToad.PdfPig.Graphics.Operations.SetStrokeColorDeviceGray", "UglyToad.PdfPig.Graphics.Operations.SetStrokeColorDeviceRgb", + "UglyToad.PdfPig.Graphics.Operations.SetStrokeColorSpace", "UglyToad.PdfPig.Graphics.Operations.SpecialGraphicsState.ModifyCurrentTransformationMatrix", "UglyToad.PdfPig.Graphics.Operations.SpecialGraphicsState.Pop", "UglyToad.PdfPig.Graphics.Operations.SpecialGraphicsState.Push", @@ -122,6 +139,8 @@ "UglyToad.PdfPig.Graphics.Operations.TextState.SetTextRenderingMode", "UglyToad.PdfPig.Graphics.Operations.TextState.SetTextRise", "UglyToad.PdfPig.Graphics.Operations.TextState.SetWordSpacing", + "UglyToad.PdfPig.Graphics.Operations.TextState.Type3SetGlyphWidth", + "UglyToad.PdfPig.Graphics.Operations.TextState.Type3SetGlyphWidthAndBoundingBox", "UglyToad.PdfPig.Graphics.TextMatrices", "UglyToad.PdfPig.IndirectReference", "UglyToad.PdfPig.IO.IInputBytes", @@ -160,5 +179,103 @@ Assert.True(publicTypeNames.Contains(expectedPublicType), $"Type should be public: {expectedPublicType}."); } } + + [Fact] + public void AllSpecificationOperatorsArePresent() + { + var assembly = typeof(PdfDocument).Assembly; + + var types = assembly.GetTypes().Where(x => typeof(IGraphicsStateOperation).IsAssignableFrom(x) && !x.IsInterface); + + var expectedSymbols = new[] + { + "b", + "B", + "b*", + "B*", + "BDC", + "BI", + "BMC", + "BT", + "BX", + "c", + "cm", + "CS", + "cs", + "d", + "d0", + "d1", + "Do", + "DP", + "EI", + "EMC", + "ET", + "EX", + "f", + "F", + "f*", + "G", + "g", + "gs", + "h", + "i", + "ID", + "j", + "J", + "K", + "k", + "l", + "m", + "M", + "MP", + "n", + "q", + "Q", + "re", + "RG", + "rg", + "ri", + "s", + "S", + "SC", + "sc", + "SCN", + "scn", + "sh", + "T*", + "Tc", + "Td", + "TD", + "Tf", + "Tj", + "TJ", + "TL", + "Tm", + "Tr", + "Ts", + "Tw", + "Tz", + "v", + "w", + "W", + "W*", + "y", + "'", + "\"" + }; + + var symbols = new List(); + + foreach (var type in types) + { + var symbol = type.GetField("Symbol", BindingFlags.Public | BindingFlags.Static); + symbols.Add(symbol.GetValue(null).ToString()); + } + + foreach (var expectedSymbol in expectedSymbols) + { + Assert.True(symbols.Contains(expectedSymbol), $"There is no operation defined with the symbol: {expectedSymbol}."); + } + } } } diff --git a/src/UglyToad.PdfPig/Graphics/Operations/Compatibility/BeginCompatibilitySection.cs b/src/UglyToad.PdfPig/Graphics/Operations/Compatibility/BeginCompatibilitySection.cs new file mode 100644 index 00000000..f64e9246 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/Compatibility/BeginCompatibilitySection.cs @@ -0,0 +1,46 @@ +namespace UglyToad.PdfPig.Graphics.Operations.Compatibility +{ + using System.IO; + + /// + /// + /// Begin a compatibility section. Unrecognized operators (along with their operands) are ignored without error. + /// + public class BeginCompatibilitySection : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "BX"; + + /// + /// The instance of the operation. + /// + public static readonly BeginCompatibilitySection Value = new BeginCompatibilitySection(); + + /// + public string Operator => Symbol; + + private BeginCompatibilitySection() + { + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return Symbol; + } + } +} diff --git a/src/UglyToad.PdfPig/Graphics/Operations/Compatibility/EndCompatibilitySection.cs b/src/UglyToad.PdfPig/Graphics/Operations/Compatibility/EndCompatibilitySection.cs new file mode 100644 index 00000000..7b57ce96 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/Compatibility/EndCompatibilitySection.cs @@ -0,0 +1,46 @@ +namespace UglyToad.PdfPig.Graphics.Operations.Compatibility +{ + using System.IO; + + /// + /// + /// End a compatibility section. + /// + public class EndCompatibilitySection : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "EX"; + + /// + /// The instance of the operation. + /// + public static readonly EndCompatibilitySection Value = new EndCompatibilitySection(); + + /// + public string Operator => Symbol; + + private EndCompatibilitySection() + { + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return Symbol; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/BeginInlineImage.cs b/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/BeginInlineImage.cs new file mode 100644 index 00000000..d46f917c --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/BeginInlineImage.cs @@ -0,0 +1,46 @@ +namespace UglyToad.PdfPig.Graphics.Operations.InlineImages +{ + using System.IO; + + /// + /// + /// Begin an inline image object. + /// + public class BeginInlineImage : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "BI"; + + /// + /// The instance of the operation. + /// + public static readonly BeginInlineImage Value = new BeginInlineImage(); + + /// + public string Operator => Symbol; + + private BeginInlineImage() + { + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return Symbol; + } + } +} diff --git a/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/BeginInlineImageData.cs b/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/BeginInlineImageData.cs new file mode 100644 index 00000000..e21e1031 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/BeginInlineImageData.cs @@ -0,0 +1,46 @@ +namespace UglyToad.PdfPig.Graphics.Operations.InlineImages +{ + using System.IO; + + /// + /// + /// Begin the image data for an inline image object. + /// + public class BeginInlineImageData : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "ID"; + + /// + /// The instance of the operation. + /// + public static readonly BeginInlineImageData Value = new BeginInlineImageData(); + + /// + public string Operator => Symbol; + + private BeginInlineImageData() + { + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return Symbol; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/EndInlineImage.cs b/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/EndInlineImage.cs new file mode 100644 index 00000000..87eacfec --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/InlineImages/EndInlineImage.cs @@ -0,0 +1,46 @@ +namespace UglyToad.PdfPig.Graphics.Operations.InlineImages +{ + using System.IO; + + /// + /// + /// End an inline image object. + /// + public class EndInlineImage : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "EI"; + + /// + /// The instance of the operation. + /// + public static readonly EndInlineImage Value = new EndInlineImage(); + + /// + public string Operator => Symbol; + + private EndInlineImage() + { + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return Symbol; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContent.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContent.cs new file mode 100644 index 00000000..da2da861 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContent.cs @@ -0,0 +1,55 @@ +namespace UglyToad.PdfPig.Graphics.Operations.MarkedContent +{ + using System.IO; + using Tokens; + using Writer; + + /// + /// + /// Begin a marked-content sequence terminated by a balancing operator. + /// + public class BeginMarkedContent : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "BMC"; + + /// + public string Operator => Symbol; + + /// + /// A name indicating the role or significance of the sequence. + /// + public NameToken Name { get; } + + /// + /// Create a new . + /// + /// The name of the marked-content sequence. + public BeginMarkedContent(NameToken name) + { + Name = name; + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + TokenWriter.WriteToken(Name, stream); + stream.WriteWhiteSpace(); + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return $"{Name} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContentWithProperties.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContentWithProperties.cs new file mode 100644 index 00000000..8c45a508 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContentWithProperties.cs @@ -0,0 +1,88 @@ +namespace UglyToad.PdfPig.Graphics.Operations.MarkedContent +{ + using System.IO; + using Tokens; + using Util.JetBrains.Annotations; + using Writer; + + /// + /// + /// Begin a marked-content sequence with an associated property list terminated by a balancing operator. + /// + public class BeginMarkedContentWithProperties : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "BDC"; + + /// + public string Operator => Symbol; + + /// + /// A name indicating the role or significance of the point. + /// + public NameToken Name { get; } + + /// + /// The name of the property dictionary in the Properties subdictionary of the current resource dictionary. + /// Can be if the property dictionary is provided inline. + /// + [CanBeNull] + public NameToken PropertyDictionaryName { get; } + + /// + /// The marked-content sequence properties. + /// Can be if a name of the property dictionary is provided instead. + /// + [CanBeNull] + public DictionaryToken Properties { get; } + + /// + /// Create a new . + /// + /// The name of the marked-content point. + /// The name of the property dictionary. + public BeginMarkedContentWithProperties(NameToken name, NameToken propertyDictionaryName) + { + Name = name; + PropertyDictionaryName = propertyDictionaryName; + } + + /// + /// Create a new . + /// + /// The name of the marked-content point. + /// The properties of the marked-content point. + public BeginMarkedContentWithProperties(NameToken name, DictionaryToken properties) + { + Name = name; + Properties = properties; + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + TokenWriter.WriteToken(Name, stream); + stream.WriteWhiteSpace(); + + if (PropertyDictionaryName != null) + { + TokenWriter.WriteToken(PropertyDictionaryName, stream); + } + else + { + TokenWriter.WriteToken(Properties, stream); + } + + stream.WriteWhiteSpace(); + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPoint.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPoint.cs index e9c9a12d..a78d2b91 100644 --- a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPoint.cs +++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPoint.cs @@ -45,84 +45,11 @@ stream.WriteText(Symbol); stream.WriteNewLine(); } - } - - /// - /// - /// Begin a marked-content sequence terminated by a balancing EMC operator. - /// - public class BeginMarkedContent : IGraphicsStateOperation - { - /// - /// The symbol for this operation in a stream. - /// - public const string Symbol = "BMC"; /// - public string Operator => Symbol; - - /// - /// A name indicating the role or significance of the sequence. - /// - public NameToken Name { get; } - - /// - /// Create a new . - /// - /// The name of the marked-content sequence. - public BeginMarkedContent(NameToken name) + public override string ToString() { - Name = name; - } - - /// - public void Run(IOperationContext operationContext) - { - } - - /// - public void Write(Stream stream) - { - TokenWriter.WriteToken(Name, stream); - stream.WriteWhiteSpace(); - stream.WriteText(Symbol); - stream.WriteNewLine(); - } - } - - /// - /// - /// End a marked-content sequence. - /// - public class EndMarkedContent : IGraphicsStateOperation - { - /// - /// The symbol for this operation in a stream. - /// - public const string Symbol = "EMC"; - - /// - /// The instance of the operation. - /// - public static readonly EndMarkedContent Value = new EndMarkedContent(); - - /// - public string Operator => Symbol; - - private EndMarkedContent() - { - } - - /// - public void Run(IOperationContext operationContext) - { - } - - /// - public void Write(Stream stream) - { - stream.WriteText(Symbol); - stream.WriteNewLine(); + return $"{Name} {Symbol}"; } } } diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPointWithProperties.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPointWithProperties.cs new file mode 100644 index 00000000..c2411814 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPointWithProperties.cs @@ -0,0 +1,88 @@ +namespace UglyToad.PdfPig.Graphics.Operations.MarkedContent +{ + using System.IO; + using Tokens; + using Util.JetBrains.Annotations; + using Writer; + + /// + /// + /// Designate a single marked-content point in the content stream with an associated property list. + /// + public class DesignateMarkedContentPointWithProperties : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "DP"; + + /// + public string Operator => Symbol; + + /// + /// A name indicating the role or significance of the point. + /// + public NameToken Name { get; } + + /// + /// The name of the property dictionary in the Properties subdictionary of the current resource dictionary. + /// Can be if the property dictionary is provided inline. + /// + [CanBeNull] + public NameToken PropertyDictionaryName { get; } + + /// + /// The marked-content point properties. + /// Can be if a name of the property dictionary is provided instead. + /// + [CanBeNull] + public DictionaryToken Properties { get; } + + /// + /// Create a new . + /// + /// The name of the marked-content point. + /// The name of the property dictionary. + public DesignateMarkedContentPointWithProperties(NameToken name, NameToken propertyDictionaryName) + { + Name = name; + PropertyDictionaryName = propertyDictionaryName; + } + + /// + /// Create a new . + /// + /// The name of the marked-content point. + /// The properties of the marked-content point. + public DesignateMarkedContentPointWithProperties(NameToken name, DictionaryToken properties) + { + Name = name; + Properties = properties; + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + TokenWriter.WriteToken(Name, stream); + stream.WriteWhiteSpace(); + + if (PropertyDictionaryName != null) + { + TokenWriter.WriteToken(PropertyDictionaryName, stream); + } + else + { + TokenWriter.WriteToken(Properties, stream); + } + + stream.WriteWhiteSpace(); + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/EndMarkedContent.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/EndMarkedContent.cs new file mode 100644 index 00000000..326f27ac --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/EndMarkedContent.cs @@ -0,0 +1,46 @@ +namespace UglyToad.PdfPig.Graphics.Operations.MarkedContent +{ + using System.IO; + + /// + /// + /// End a marked-content sequence. + /// + public class EndMarkedContent : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "EMC"; + + /// + /// The instance of the operation. + /// + public static readonly EndMarkedContent Value = new EndMarkedContent(); + + /// + public string Operator => Symbol; + + private EndMarkedContent() + { + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return Symbol; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/PaintShading.cs b/src/UglyToad.PdfPig/Graphics/Operations/PaintShading.cs new file mode 100644 index 00000000..fecb893f --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/PaintShading.cs @@ -0,0 +1,57 @@ +namespace UglyToad.PdfPig.Graphics.Operations +{ + using System; + using System.IO; + using Tokens; + + /// + /// + /// Paint the shape and color shading described by a shading dictionary, subject to the current clipping path. + /// The current color in the graphics state is neither used nor altered. + /// The effect is different from that of painting a path using a shading pattern as the current color. + /// + public class PaintShading : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "sh"; + + /// + public string Operator => Symbol; + + /// + /// The name of a shading dictionary resource in the Shading subdictionary of the current resource dictionary. + /// + public NameToken Name { get; } + + /// + /// Create a new . + /// + /// The name of the shading dictionary. + public PaintShading(NameToken name) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteText($"/{Name}"); + stream.WriteWhiteSpace(); + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return $"{Name} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColor.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColor.cs new file mode 100644 index 00000000..09789070 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColor.cs @@ -0,0 +1,60 @@ +namespace UglyToad.PdfPig.Graphics.Operations +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + + /// + /// + /// Set the nonstroking color based on the current color space. + /// + public class SetNonStrokeColor : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "sc"; + + /// + public string Operator => Symbol; + + /// + /// The values for the color, 1 for grayscale, 3 for RGB, 4 for CMYK. + /// + public IReadOnlyList Operands { get; } + + /// + /// Create a new . + /// + /// The color operands. + public SetNonStrokeColor(decimal[] operands) + { + Operands = operands; + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + foreach (var operand in Operands) + { + stream.WriteDecimal(operand); + stream.WriteWhiteSpace(); + } + + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + var arguments = string.Join(" ", Operands.Select(x => x.ToString("N"))); + return $"{arguments} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorAdvanced.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorAdvanced.cs new file mode 100644 index 00000000..ccc43157 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorAdvanced.cs @@ -0,0 +1,89 @@ +namespace UglyToad.PdfPig.Graphics.Operations +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Tokens; + using Writer; + + /// + /// + /// Set the stroking color based on the current color space with support for Pattern, Separation, DeviceN, and ICCBased color spaces. + /// + public class SetNonStrokeColorAdvanced : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "scn"; + + /// + public string Operator => Symbol; + + /// + /// The values for the color. + /// + public IReadOnlyList Operands { get; } + + /// + /// The name of an entry in the Pattern subdictionary of the current resource dictionary. + /// + public NameToken PatternName { get; } + + /// + /// Create a new . + /// + /// The color operands. + public SetNonStrokeColorAdvanced(IReadOnlyList operands) + { + Operands = operands; + } + + /// + /// Create a new . + /// + /// The color operands. + /// The pattern name. + public SetNonStrokeColorAdvanced(IReadOnlyList operands, NameToken patternName) + { + Operands = operands; + PatternName = patternName; + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + foreach (var operand in Operands) + { + stream.WriteDecimal(operand); + stream.WriteWhiteSpace(); + } + + if (PatternName != null) + { + TokenWriter.WriteToken(PatternName, stream); + } + + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + var arguments = string.Join(" ", Operands.Select(x => x.ToString("N"))); + + if (PatternName != null) + { + arguments += $" {PatternName}"; + } + + return $"{arguments} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorDeviceCmyk.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorDeviceCmyk.cs index 1e86cf69..dd39f974 100644 --- a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorDeviceCmyk.cs +++ b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorDeviceCmyk.cs @@ -11,7 +11,7 @@ /// /// The symbol for this operation in a stream. /// - public const string Symbol = "K"; + public const string Symbol = "k"; /// public string Operator => Symbol; diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorSpace.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorSpace.cs new file mode 100644 index 00000000..c0eca9d9 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorSpace.cs @@ -0,0 +1,55 @@ +namespace UglyToad.PdfPig.Graphics.Operations +{ + using System; + using System.IO; + using Tokens; + using Writer; + + /// + /// + /// Set the current color space to use for nonstroking operations. + /// + public class SetNonStrokeColorSpace : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "cs"; + + /// + public string Operator => Symbol; + + /// + /// The name of the color space. + /// + public NameToken Name { get; } + + /// + /// Create a new . + /// + /// The name of the color space. + public SetNonStrokeColorSpace(NameToken name) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + TokenWriter.WriteToken(Name, stream); + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return $"{Name} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColor.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColor.cs new file mode 100644 index 00000000..00fcb029 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColor.cs @@ -0,0 +1,60 @@ +namespace UglyToad.PdfPig.Graphics.Operations +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + + /// + /// + /// Set the stroking color based on the current color space. + /// + public class SetStrokeColor : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "SC"; + + /// + public string Operator => Symbol; + + /// + /// The values for the color, 1 for grayscale, 3 for RGB, 4 for CMYK. + /// + public IReadOnlyList Operands { get; } + + /// + /// Create a new . + /// + /// The color operands. + public SetStrokeColor(decimal[] operands) + { + Operands = operands; + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + foreach (var operand in Operands) + { + stream.WriteDecimal(operand); + stream.WriteWhiteSpace(); + } + + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + var arguments = string.Join(" ", Operands.Select(x => x.ToString("N"))); + return $"{arguments} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorAdvanced.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorAdvanced.cs new file mode 100644 index 00000000..60a90ab3 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorAdvanced.cs @@ -0,0 +1,89 @@ +namespace UglyToad.PdfPig.Graphics.Operations +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Tokens; + using Writer; + + /// + /// + /// Set the stroking color based on the current color space with support for Pattern, Separation, DeviceN, and ICCBased color spaces. + /// + public class SetStrokeColorAdvanced : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "SCN"; + + /// + public string Operator => Symbol; + + /// + /// The values for the color. + /// + public IReadOnlyList Operands { get; } + + /// + /// The name of an entry in the Pattern subdictionary of the current resource dictionary. + /// + public NameToken PatternName { get; } + + /// + /// Create a new . + /// + /// The color operands. + public SetStrokeColorAdvanced(IReadOnlyList operands) + { + Operands = operands; + } + + /// + /// Create a new . + /// + /// The color operands. + /// The pattern name. + public SetStrokeColorAdvanced(IReadOnlyList operands, NameToken patternName) + { + Operands = operands; + PatternName = patternName; + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + foreach (var operand in Operands) + { + stream.WriteDecimal(operand); + stream.WriteWhiteSpace(); + } + + if (PatternName != null) + { + TokenWriter.WriteToken(PatternName, stream); + } + + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + var arguments = string.Join(" ", Operands.Select(x => x.ToString("N"))); + + if (PatternName != null) + { + arguments += $" {PatternName}"; + } + + return $"{arguments} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorSpace.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorSpace.cs new file mode 100644 index 00000000..9ffaa7a5 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorSpace.cs @@ -0,0 +1,55 @@ +namespace UglyToad.PdfPig.Graphics.Operations +{ + using System; + using System.IO; + using Tokens; + using Writer; + + /// + /// + /// Set the current color space to use for stroking operations. + /// + public class SetStrokeColorSpace : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "CS"; + + /// + public string Operator => Symbol; + + /// + /// The name of the color space. + /// + public NameToken Name { get; } + + /// + /// Create a new . + /// + /// The name of the color space. + public SetStrokeColorSpace(NameToken name) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + TokenWriter.WriteToken(Name, stream); + stream.WriteText(Symbol); + stream.WriteNewLine(); + } + + /// + public override string ToString() + { + return $"{Name} {Symbol}"; + } + } +} diff --git a/src/UglyToad.PdfPig/Graphics/Operations/TextState/Type3SetGlyphWidth.cs b/src/UglyToad.PdfPig/Graphics/Operations/TextState/Type3SetGlyphWidth.cs new file mode 100644 index 00000000..116b95b0 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/TextState/Type3SetGlyphWidth.cs @@ -0,0 +1,62 @@ +namespace UglyToad.PdfPig.Graphics.Operations.TextState +{ + using System.IO; + + /// + /// + /// Set width information for the glyph and declare that the glyph description specifies both its shape and its color for a Type 3 font. + /// wx specifies the horizontal displacement in the glyph coordinate system; + /// it must be consistent with the corresponding width in the font's Widths array. + /// wy must be 0. + /// + public class Type3SetGlyphWidth : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "d0"; + + /// + public string Operator => Symbol; + + /// + /// The horizontal displacement in the glyph coordinate system. + /// + public decimal HorizontalDisplacement { get; } + + /// + /// The vertical displacement in the glyph coordinate system. Must be 0. + /// + public decimal VerticalDisplacement { get; } + + /// + /// Create a new . + /// + /// The horizontal displacement in the glyph coordinate system. + /// The vertical displacement in the glyph coordinate system. + public Type3SetGlyphWidth(decimal horizontalDisplacement, decimal verticalDisplacement) + { + HorizontalDisplacement = horizontalDisplacement; + VerticalDisplacement = verticalDisplacement; + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteDecimal(HorizontalDisplacement); + stream.WriteWhiteSpace(); + stream.WriteNumberText(VerticalDisplacement, Symbol); + } + + /// + public override string ToString() + { + return $"{HorizontalDisplacement} {VerticalDisplacement} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/Operations/TextState/Type3SetGlyphWidthAndBoundingBox.cs b/src/UglyToad.PdfPig/Graphics/Operations/TextState/Type3SetGlyphWidthAndBoundingBox.cs new file mode 100644 index 00000000..5d430a76 --- /dev/null +++ b/src/UglyToad.PdfPig/Graphics/Operations/TextState/Type3SetGlyphWidthAndBoundingBox.cs @@ -0,0 +1,83 @@ +namespace UglyToad.PdfPig.Graphics.Operations.TextState +{ + using System.IO; + using Geometry; + + /// + /// + /// Set width information for the glyph and declare that the glyph description specifies both its shape and its color for a Type 3 font. + /// Also sets the glyph bounding box. + /// + public class Type3SetGlyphWidthAndBoundingBox : IGraphicsStateOperation + { + /// + /// The symbol for this operation in a stream. + /// + public const string Symbol = "d1"; + + /// + public string Operator => Symbol; + + /// + /// The horizontal displacement in the glyph coordinate system. + /// + public decimal HorizontalDisplacement { get; } + + /// + /// The vertical displacement in the glyph coordinate system. Must be 0. + /// + public decimal VerticalDisplacement { get; } + + /// + /// The glyph bounding box. + /// + public PdfRectangle BoundingBox { get; } + + /// + /// Create a new . + /// + /// The horizontal displacement in the glyph coordinate system. + /// The vertical displacement in the glyph coordinate system. + /// The lower left x coordinate of the glyph bounding box. + /// The lower left y coordinate of the glyph bounding box. + /// The upper right x coordinate of the glyph bounding box. + /// The upper right y coordinate of the glyph bounding box. + public Type3SetGlyphWidthAndBoundingBox(decimal horizontalDisplacement, decimal verticalDisplacement, + decimal lowerLeftX, + decimal lowerLeftY, + decimal upperRightX, + decimal upperRightY) + { + HorizontalDisplacement = horizontalDisplacement; + VerticalDisplacement = verticalDisplacement; + BoundingBox = new PdfRectangle(new PdfPoint(lowerLeftX, lowerLeftY), new PdfPoint(upperRightX, upperRightY)); + } + + /// + public void Run(IOperationContext operationContext) + { + } + + /// + public void Write(Stream stream) + { + stream.WriteDecimal(HorizontalDisplacement); + stream.WriteWhiteSpace(); + stream.WriteDecimal(VerticalDisplacement); + stream.WriteWhiteSpace(); + stream.WriteDecimal(BoundingBox.Left); + stream.WriteWhiteSpace(); + stream.WriteDecimal(BoundingBox.Bottom); + stream.WriteWhiteSpace(); + stream.WriteDecimal(BoundingBox.Right); + stream.WriteWhiteSpace(); + stream.WriteNumberText(BoundingBox.Top, Symbol); + } + + /// + public override string ToString() + { + return $"{HorizontalDisplacement} {VerticalDisplacement} {BoundingBox.Left} {BoundingBox.Bottom} {BoundingBox.Right} {BoundingBox.Top} {Symbol}"; + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Graphics/ReflectionGraphicsStateOperationFactory.cs b/src/UglyToad.PdfPig/Graphics/ReflectionGraphicsStateOperationFactory.cs index 9b418794..854f7e16 100644 --- a/src/UglyToad.PdfPig/Graphics/ReflectionGraphicsStateOperationFactory.cs +++ b/src/UglyToad.PdfPig/Graphics/ReflectionGraphicsStateOperationFactory.cs @@ -6,6 +6,7 @@ namespace UglyToad.PdfPig.Graphics using System.Reflection; using Exceptions; using Operations; + using Operations.MarkedContent; using Operations.TextShowing; using Operations.TextState; using Tokens; @@ -84,6 +85,56 @@ namespace UglyToad.PdfPig.Graphics var information = string.Join(", ", operands.Select(x => x.ToString())); throw new PdfDocumentFormatException($"Attempted to set font with wrong number of parameters: [{information}]"); + case DesignateMarkedContentPointWithProperties.Symbol: + var dpName = (NameToken)operands[0]; + if (operands[1] is DictionaryToken contentPointDictionary) + { + return new DesignateMarkedContentPointWithProperties(dpName, contentPointDictionary); + } + else if (operands[1] is NameToken contentPointName) + { + return new DesignateMarkedContentPointWithProperties(dpName, contentPointName); + } + + var errorMessageDp = string.Join(", ", operands.Select(x => x.ToString())); + throw new PdfDocumentFormatException($"Attempted to set a marked-content point with invalid parameters: [{errorMessageDp}]"); + case BeginMarkedContentWithProperties.Symbol: + var bdcName = (NameToken) operands[0]; + if (operands[1] is DictionaryToken contentSequenceDictionary) + { + return new BeginMarkedContentWithProperties(bdcName, contentSequenceDictionary); + } + else if (operands[1] is NameToken contentSequenceName) + { + return new BeginMarkedContentWithProperties(bdcName, contentSequenceName); + } + + var errorMessageBdc = string.Join(", ", operands.Select(x => x.ToString())); + throw new PdfDocumentFormatException($"Attempted to set a marked-content sequence with invalid parameters: [{errorMessageBdc}]"); + case SetStrokeColorAdvanced.Symbol: + if (operands[operands.Count - 1] is NameToken scnPatternName) + { + return new SetStrokeColorAdvanced(operands.Take(operands.Count - 1).Select(x => ((NumericToken)x).Data).ToList(), scnPatternName); + } + else if(operands.All(x => x is NumericToken)) + { + return new SetStrokeColorAdvanced(operands.Select(x => ((NumericToken)x).Data).ToList()); + } + + var errorMessageScn = string.Join(", ", operands.Select(x => x.ToString())); + throw new PdfDocumentFormatException($"Attempted to set a stroke color space (SCN) with invalid arguments: [{errorMessageScn}]"); + case SetNonStrokeColorAdvanced.Symbol: + if (operands[operands.Count - 1] is NameToken scnLowerPatternName) + { + return new SetStrokeColorAdvanced(operands.Take(operands.Count - 1).Select(x => ((NumericToken)x).Data).ToList(), scnLowerPatternName); + } + else if (operands.All(x => x is NumericToken)) + { + return new SetStrokeColorAdvanced(operands.Select(x => ((NumericToken)x).Data).ToList()); + } + + var errorMessageScnLower = string.Join(", ", operands.Select(x => x.ToString())); + throw new PdfDocumentFormatException($"Attempted to set a non-stroke color space (scn) with invalid arguments: [{errorMessageScnLower}]"); } if (!operations.TryGetValue(op.Data, out Type operationType))