#24 add the missing operations for the graphics state

This commit is contained in:
Eliot Jones
2019-01-06 15:46:28 +00:00
parent 6f0293b2a5
commit cdf5546a1b
22 changed files with 1288 additions and 76 deletions

View File

@@ -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<string>();
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}.");
}
}
}
}

View File

@@ -0,0 +1,46 @@
namespace UglyToad.PdfPig.Graphics.Operations.Compatibility
{
using System.IO;
/// <inheritdoc />
/// <summary>
/// Begin a compatibility section. Unrecognized operators (along with their operands) are ignored without error.
/// </summary>
public class BeginCompatibilitySection : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "BX";
/// <summary>
/// The instance of the <see cref="BeginCompatibilitySection"/> operation.
/// </summary>
public static readonly BeginCompatibilitySection Value = new BeginCompatibilitySection();
/// <inheritdoc />
public string Operator => Symbol;
private BeginCompatibilitySection()
{
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return Symbol;
}
}
}

View File

@@ -0,0 +1,46 @@
namespace UglyToad.PdfPig.Graphics.Operations.Compatibility
{
using System.IO;
/// <inheritdoc />
/// <summary>
/// End a compatibility section.
/// </summary>
public class EndCompatibilitySection : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "EX";
/// <summary>
/// The instance of the <see cref="EndCompatibilitySection"/> operation.
/// </summary>
public static readonly EndCompatibilitySection Value = new EndCompatibilitySection();
/// <inheritdoc />
public string Operator => Symbol;
private EndCompatibilitySection()
{
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return Symbol;
}
}
}

View File

@@ -0,0 +1,46 @@
namespace UglyToad.PdfPig.Graphics.Operations.InlineImages
{
using System.IO;
/// <inheritdoc />
/// <summary>
/// Begin an inline image object.
/// </summary>
public class BeginInlineImage : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "BI";
/// <summary>
/// The instance of the <see cref="BeginInlineImage"/> operation.
/// </summary>
public static readonly BeginInlineImage Value = new BeginInlineImage();
/// <inheritdoc />
public string Operator => Symbol;
private BeginInlineImage()
{
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return Symbol;
}
}
}

View File

@@ -0,0 +1,46 @@
namespace UglyToad.PdfPig.Graphics.Operations.InlineImages
{
using System.IO;
/// <inheritdoc />
/// <summary>
/// Begin the image data for an inline image object.
/// </summary>
public class BeginInlineImageData : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "ID";
/// <summary>
/// The instance of the <see cref="BeginInlineImageData"/> operation.
/// </summary>
public static readonly BeginInlineImageData Value = new BeginInlineImageData();
/// <inheritdoc />
public string Operator => Symbol;
private BeginInlineImageData()
{
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return Symbol;
}
}
}

View File

@@ -0,0 +1,46 @@
namespace UglyToad.PdfPig.Graphics.Operations.InlineImages
{
using System.IO;
/// <inheritdoc />
/// <summary>
/// End an inline image object.
/// </summary>
public class EndInlineImage : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "EI";
/// <summary>
/// The instance of the <see cref="EndInlineImage"/> operation.
/// </summary>
public static readonly EndInlineImage Value = new EndInlineImage();
/// <inheritdoc />
public string Operator => Symbol;
private EndInlineImage()
{
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return Symbol;
}
}
}

View File

@@ -0,0 +1,55 @@
namespace UglyToad.PdfPig.Graphics.Operations.MarkedContent
{
using System.IO;
using Tokens;
using Writer;
/// <inheritdoc />
/// <summary>
/// Begin a marked-content sequence terminated by a balancing <see cref="EndMarkedContent"/> operator.
/// </summary>
public class BeginMarkedContent : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "BMC";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// A name indicating the role or significance of the sequence.
/// </summary>
public NameToken Name { get; }
/// <summary>
/// Create a new <see cref="BeginMarkedContent"/>.
/// </summary>
/// <param name="name">The name of the marked-content sequence.</param>
public BeginMarkedContent(NameToken name)
{
Name = name;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
TokenWriter.WriteToken(Name, stream);
stream.WriteWhiteSpace();
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return $"{Name} {Symbol}";
}
}
}

View File

@@ -0,0 +1,88 @@
namespace UglyToad.PdfPig.Graphics.Operations.MarkedContent
{
using System.IO;
using Tokens;
using Util.JetBrains.Annotations;
using Writer;
/// <inheritdoc />
/// <summary>
/// Begin a marked-content sequence with an associated property list terminated by a balancing <see cref="EndMarkedContent"/> operator.
/// </summary>
public class BeginMarkedContentWithProperties : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "BDC";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// A name indicating the role or significance of the point.
/// </summary>
public NameToken Name { get; }
/// <summary>
/// The name of the property dictionary in the Properties subdictionary of the current resource dictionary.
/// Can be <see langword="null"/> if the property dictionary is provided inline.
/// </summary>
[CanBeNull]
public NameToken PropertyDictionaryName { get; }
/// <summary>
/// The marked-content sequence properties.
/// Can be <see langword="null"/> if a name of the property dictionary is provided instead.
/// </summary>
[CanBeNull]
public DictionaryToken Properties { get; }
/// <summary>
/// Create a new <see cref="BeginMarkedContentWithProperties"/>.
/// </summary>
/// <param name="name">The name of the marked-content point.</param>
/// <param name="propertyDictionaryName">The name of the property dictionary.</param>
public BeginMarkedContentWithProperties(NameToken name, NameToken propertyDictionaryName)
{
Name = name;
PropertyDictionaryName = propertyDictionaryName;
}
/// <summary>
/// Create a new <see cref="BeginMarkedContentWithProperties"/>.
/// </summary>
/// <param name="name">The name of the marked-content point.</param>
/// <param name="properties">The properties of the marked-content point.</param>
public BeginMarkedContentWithProperties(NameToken name, DictionaryToken properties)
{
Name = name;
Properties = properties;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
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();
}
}
}

View File

@@ -45,84 +45,11 @@
stream.WriteText(Symbol);
stream.WriteNewLine();
}
}
/// <inheritdoc />
/// <summary>
/// Begin a marked-content sequence terminated by a balancing EMC operator.
/// </summary>
public class BeginMarkedContent : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "BMC";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// A name indicating the role or significance of the sequence.
/// </summary>
public NameToken Name { get; }
/// <summary>
/// Create a new <see cref="BeginMarkedContent"/>.
/// </summary>
/// <param name="name">The name of the marked-content sequence.</param>
public BeginMarkedContent(NameToken name)
public override string ToString()
{
Name = name;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
TokenWriter.WriteToken(Name, stream);
stream.WriteWhiteSpace();
stream.WriteText(Symbol);
stream.WriteNewLine();
}
}
/// <inheritdoc />
/// <summary>
/// End a marked-content sequence.
/// </summary>
public class EndMarkedContent : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "EMC";
/// <summary>
/// The instance of the <see cref="EndMarkedContent"/> operation.
/// </summary>
public static readonly EndMarkedContent Value = new EndMarkedContent();
/// <inheritdoc />
public string Operator => Symbol;
private EndMarkedContent()
{
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteText(Symbol);
stream.WriteNewLine();
return $"{Name} {Symbol}";
}
}
}

View File

@@ -0,0 +1,88 @@
namespace UglyToad.PdfPig.Graphics.Operations.MarkedContent
{
using System.IO;
using Tokens;
using Util.JetBrains.Annotations;
using Writer;
/// <inheritdoc />
/// <summary>
/// Designate a single marked-content point in the content stream with an associated property list.
/// </summary>
public class DesignateMarkedContentPointWithProperties : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "DP";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// A name indicating the role or significance of the point.
/// </summary>
public NameToken Name { get; }
/// <summary>
/// The name of the property dictionary in the Properties subdictionary of the current resource dictionary.
/// Can be <see langword="null"/> if the property dictionary is provided inline.
/// </summary>
[CanBeNull]
public NameToken PropertyDictionaryName { get; }
/// <summary>
/// The marked-content point properties.
/// Can be <see langword="null"/> if a name of the property dictionary is provided instead.
/// </summary>
[CanBeNull]
public DictionaryToken Properties { get; }
/// <summary>
/// Create a new <see cref="DesignateMarkedContentPointWithProperties"/>.
/// </summary>
/// <param name="name">The name of the marked-content point.</param>
/// <param name="propertyDictionaryName">The name of the property dictionary.</param>
public DesignateMarkedContentPointWithProperties(NameToken name, NameToken propertyDictionaryName)
{
Name = name;
PropertyDictionaryName = propertyDictionaryName;
}
/// <summary>
/// Create a new <see cref="DesignateMarkedContentPointWithProperties"/>.
/// </summary>
/// <param name="name">The name of the marked-content point.</param>
/// <param name="properties">The properties of the marked-content point.</param>
public DesignateMarkedContentPointWithProperties(NameToken name, DictionaryToken properties)
{
Name = name;
Properties = properties;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
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();
}
}
}

View File

@@ -0,0 +1,46 @@
namespace UglyToad.PdfPig.Graphics.Operations.MarkedContent
{
using System.IO;
/// <inheritdoc />
/// <summary>
/// End a marked-content sequence.
/// </summary>
public class EndMarkedContent : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "EMC";
/// <summary>
/// The instance of the <see cref="EndMarkedContent"/> operation.
/// </summary>
public static readonly EndMarkedContent Value = new EndMarkedContent();
/// <inheritdoc />
public string Operator => Symbol;
private EndMarkedContent()
{
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return Symbol;
}
}
}

View File

@@ -0,0 +1,57 @@
namespace UglyToad.PdfPig.Graphics.Operations
{
using System;
using System.IO;
using Tokens;
/// <inheritdoc />
/// <summary>
/// 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.
/// </summary>
public class PaintShading : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "sh";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The name of a shading dictionary resource in the Shading subdictionary of the current resource dictionary.
/// </summary>
public NameToken Name { get; }
/// <summary>
/// Create a new <see cref="PaintShading"/>.
/// </summary>
/// <param name="name">The name of the shading dictionary.</param>
public PaintShading(NameToken name)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteText($"/{Name}");
stream.WriteWhiteSpace();
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return $"{Name} {Symbol}";
}
}
}

View File

@@ -0,0 +1,60 @@
namespace UglyToad.PdfPig.Graphics.Operations
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
/// <inheritdoc />
/// <summary>
/// Set the nonstroking color based on the current color space.
/// </summary>
public class SetNonStrokeColor : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "sc";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The values for the color, 1 for grayscale, 3 for RGB, 4 for CMYK.
/// </summary>
public IReadOnlyList<decimal> Operands { get; }
/// <summary>
/// Create a new <see cref="SetNonStrokeColor"/>.
/// </summary>
/// <param name="operands">The color operands.</param>
public SetNonStrokeColor(decimal[] operands)
{
Operands = operands;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
foreach (var operand in Operands)
{
stream.WriteDecimal(operand);
stream.WriteWhiteSpace();
}
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
var arguments = string.Join(" ", Operands.Select(x => x.ToString("N")));
return $"{arguments} {Symbol}";
}
}
}

View File

@@ -0,0 +1,89 @@
namespace UglyToad.PdfPig.Graphics.Operations
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Tokens;
using Writer;
/// <inheritdoc />
/// <summary>
/// Set the stroking color based on the current color space with support for Pattern, Separation, DeviceN, and ICCBased color spaces.
/// </summary>
public class SetNonStrokeColorAdvanced : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "scn";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The values for the color.
/// </summary>
public IReadOnlyList<decimal> Operands { get; }
/// <summary>
/// The name of an entry in the Pattern subdictionary of the current resource dictionary.
/// </summary>
public NameToken PatternName { get; }
/// <summary>
/// Create a new <see cref="SetNonStrokeColorAdvanced"/>.
/// </summary>
/// <param name="operands">The color operands.</param>
public SetNonStrokeColorAdvanced(IReadOnlyList<decimal> operands)
{
Operands = operands;
}
/// <summary>
/// Create a new <see cref="SetNonStrokeColorAdvanced"/>.
/// </summary>
/// <param name="operands">The color operands.</param>
/// <param name="patternName">The pattern name.</param>
public SetNonStrokeColorAdvanced(IReadOnlyList<decimal> operands, NameToken patternName)
{
Operands = operands;
PatternName = patternName;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
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();
}
/// <inheritdoc />
public override string ToString()
{
var arguments = string.Join(" ", Operands.Select(x => x.ToString("N")));
if (PatternName != null)
{
arguments += $" {PatternName}";
}
return $"{arguments} {Symbol}";
}
}
}

View File

@@ -11,7 +11,7 @@
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "K";
public const string Symbol = "k";
/// <inheritdoc />
public string Operator => Symbol;

View File

@@ -0,0 +1,55 @@
namespace UglyToad.PdfPig.Graphics.Operations
{
using System;
using System.IO;
using Tokens;
using Writer;
/// <inheritdoc />
/// <summary>
/// Set the current color space to use for nonstroking operations.
/// </summary>
public class SetNonStrokeColorSpace : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "cs";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The name of the color space.
/// </summary>
public NameToken Name { get; }
/// <summary>
/// Create a new <see cref="SetNonStrokeColorSpace"/>.
/// </summary>
/// <param name="name">The name of the color space.</param>
public SetNonStrokeColorSpace(NameToken name)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
TokenWriter.WriteToken(Name, stream);
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return $"{Name} {Symbol}";
}
}
}

View File

@@ -0,0 +1,60 @@
namespace UglyToad.PdfPig.Graphics.Operations
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
/// <inheritdoc />
/// <summary>
/// Set the stroking color based on the current color space.
/// </summary>
public class SetStrokeColor : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "SC";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The values for the color, 1 for grayscale, 3 for RGB, 4 for CMYK.
/// </summary>
public IReadOnlyList<decimal> Operands { get; }
/// <summary>
/// Create a new <see cref="SetStrokeColor"/>.
/// </summary>
/// <param name="operands">The color operands.</param>
public SetStrokeColor(decimal[] operands)
{
Operands = operands;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
foreach (var operand in Operands)
{
stream.WriteDecimal(operand);
stream.WriteWhiteSpace();
}
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
var arguments = string.Join(" ", Operands.Select(x => x.ToString("N")));
return $"{arguments} {Symbol}";
}
}
}

View File

@@ -0,0 +1,89 @@
namespace UglyToad.PdfPig.Graphics.Operations
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Tokens;
using Writer;
/// <inheritdoc />
/// <summary>
/// Set the stroking color based on the current color space with support for Pattern, Separation, DeviceN, and ICCBased color spaces.
/// </summary>
public class SetStrokeColorAdvanced : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "SCN";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The values for the color.
/// </summary>
public IReadOnlyList<decimal> Operands { get; }
/// <summary>
/// The name of an entry in the Pattern subdictionary of the current resource dictionary.
/// </summary>
public NameToken PatternName { get; }
/// <summary>
/// Create a new <see cref="SetStrokeColor"/>.
/// </summary>
/// <param name="operands">The color operands.</param>
public SetStrokeColorAdvanced(IReadOnlyList<decimal> operands)
{
Operands = operands;
}
/// <summary>
/// Create a new <see cref="SetStrokeColor"/>.
/// </summary>
/// <param name="operands">The color operands.</param>
/// <param name="patternName">The pattern name.</param>
public SetStrokeColorAdvanced(IReadOnlyList<decimal> operands, NameToken patternName)
{
Operands = operands;
PatternName = patternName;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
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();
}
/// <inheritdoc />
public override string ToString()
{
var arguments = string.Join(" ", Operands.Select(x => x.ToString("N")));
if (PatternName != null)
{
arguments += $" {PatternName}";
}
return $"{arguments} {Symbol}";
}
}
}

View File

@@ -0,0 +1,55 @@
namespace UglyToad.PdfPig.Graphics.Operations
{
using System;
using System.IO;
using Tokens;
using Writer;
/// <inheritdoc />
/// <summary>
/// Set the current color space to use for stroking operations.
/// </summary>
public class SetStrokeColorSpace : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "CS";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The name of the color space.
/// </summary>
public NameToken Name { get; }
/// <summary>
/// Create a new <see cref="SetStrokeColorSpace"/>.
/// </summary>
/// <param name="name">The name of the color space.</param>
public SetStrokeColorSpace(NameToken name)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
TokenWriter.WriteToken(Name, stream);
stream.WriteText(Symbol);
stream.WriteNewLine();
}
/// <inheritdoc />
public override string ToString()
{
return $"{Name} {Symbol}";
}
}
}

View File

@@ -0,0 +1,62 @@
namespace UglyToad.PdfPig.Graphics.Operations.TextState
{
using System.IO;
/// <inheritdoc />
/// <summary>
/// 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.
/// </summary>
public class Type3SetGlyphWidth : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "d0";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The horizontal displacement in the glyph coordinate system.
/// </summary>
public decimal HorizontalDisplacement { get; }
/// <summary>
/// The vertical displacement in the glyph coordinate system. Must be 0.
/// </summary>
public decimal VerticalDisplacement { get; }
/// <summary>
/// Create a new <see cref="Type3SetGlyphWidth"/>.
/// </summary>
/// <param name="horizontalDisplacement">The horizontal displacement in the glyph coordinate system.</param>
/// <param name="verticalDisplacement">The vertical displacement in the glyph coordinate system.</param>
public Type3SetGlyphWidth(decimal horizontalDisplacement, decimal verticalDisplacement)
{
HorizontalDisplacement = horizontalDisplacement;
VerticalDisplacement = verticalDisplacement;
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
public void Write(Stream stream)
{
stream.WriteDecimal(HorizontalDisplacement);
stream.WriteWhiteSpace();
stream.WriteNumberText(VerticalDisplacement, Symbol);
}
/// <inheritdoc />
public override string ToString()
{
return $"{HorizontalDisplacement} {VerticalDisplacement} {Symbol}";
}
}
}

View File

@@ -0,0 +1,83 @@
namespace UglyToad.PdfPig.Graphics.Operations.TextState
{
using System.IO;
using Geometry;
/// <inheritdoc />
/// <summary>
/// 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.
/// </summary>
public class Type3SetGlyphWidthAndBoundingBox : IGraphicsStateOperation
{
/// <summary>
/// The symbol for this operation in a stream.
/// </summary>
public const string Symbol = "d1";
/// <inheritdoc />
public string Operator => Symbol;
/// <summary>
/// The horizontal displacement in the glyph coordinate system.
/// </summary>
public decimal HorizontalDisplacement { get; }
/// <summary>
/// The vertical displacement in the glyph coordinate system. Must be 0.
/// </summary>
public decimal VerticalDisplacement { get; }
/// <summary>
/// The glyph bounding box.
/// </summary>
public PdfRectangle BoundingBox { get; }
/// <summary>
/// Create a new <see cref="Type3SetGlyphWidthAndBoundingBox"/>.
/// </summary>
/// <param name="horizontalDisplacement">The horizontal displacement in the glyph coordinate system.</param>
/// <param name="verticalDisplacement">The vertical displacement in the glyph coordinate system.</param>
/// <param name="lowerLeftX">The lower left x coordinate of the glyph bounding box.</param>
/// <param name="lowerLeftY">The lower left y coordinate of the glyph bounding box.</param>
/// <param name="upperRightX">The upper right x coordinate of the glyph bounding box.</param>
/// <param name="upperRightY">The upper right y coordinate of the glyph bounding box.</param>
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));
}
/// <inheritdoc />
public void Run(IOperationContext operationContext)
{
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
public override string ToString()
{
return $"{HorizontalDisplacement} {VerticalDisplacement} {BoundingBox.Left} {BoundingBox.Bottom} {BoundingBox.Right} {BoundingBox.Top} {Symbol}";
}
}
}

View File

@@ -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))