From 1d4dc7767d2869f47cfa9c9860cf832968518c46 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Thu, 1 Nov 2018 19:34:22 +0000 Subject: [PATCH] change type1 commands to be static and lazily evaluated and return the command sequences from the parser --- .../Arithmetic/CallOtherSubrCommand.cs | 47 ++---- .../Commands/Arithmetic/CallSubrCommand.cs | 28 +--- .../Commands/Arithmetic/DivCommand.cs | 23 +-- .../Commands/Arithmetic/PopCommand.cs | 21 ++- .../Commands/Arithmetic/ReturnCommand.cs | 16 +- .../Arithmetic/SetCurrentPointCommand.cs | 32 +--- .../Commands/Hint/DotSectionCommand.cs | 18 +-- .../Commands/Hint/HStem3Command.cs | 37 ++--- .../CharStrings/Commands/Hint/HStemCommand.cs | 32 +--- .../Commands/Hint/VStem3Command.cs | 35 ++--- .../CharStrings/Commands/Hint/VStemCommand.cs | 29 ++-- .../CharStrings/Commands/LazyType1Command.cs | 53 +++++++ .../PathConstruction/ClosePathCommand.cs | 18 +-- .../PathConstruction/HLineToCommand.cs | 24 +-- .../PathConstruction/HMoveToCommand.cs | 26 +-- .../PathConstruction/HvCurveToCommand.cs | 32 ++-- .../PathConstruction/RLineToCommand.cs | 32 +--- .../PathConstruction/RMoveToCommand.cs | 32 +--- .../RelativeRCurveToCommand.cs | 34 ++-- .../PathConstruction/VLineToCommand.cs | 24 +-- .../PathConstruction/VMoveToCommand.cs | 24 +-- .../PathConstruction/VhCurveToCommand.cs | 26 ++- .../StartFinishOutline/EndCharCommand.cs | 16 +- .../StartFinishOutline/HsbwCommand.cs | 18 +-- .../Commands/StartFinishOutline/SbwCommand.cs | 46 ++---- .../StartFinishOutline/SeacCommand.cs | 56 ++----- .../Commands/Type1BuildCharContext.cs | 9 ++ .../Type1CharStringCommandFactory.cs | 30 ---- .../CharStrings/Type1CharStringParser.cs | 148 +++++++++++++++++- .../Type1/CharStrings/Type1CharStrings.cs | 36 +++++ .../Parser/Type1EncryptedPortionParser.cs | 2 +- .../Util/DiscriminatedUnion.cs | 52 ++++++ 32 files changed, 515 insertions(+), 541 deletions(-) create mode 100644 src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/LazyType1Command.cs create mode 100644 src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Type1BuildCharContext.cs delete mode 100644 src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringCommandFactory.cs create mode 100644 src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStrings.cs create mode 100644 src/UglyToad.PdfPig/Util/DiscriminatedUnion.cs diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/CallOtherSubrCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/CallOtherSubrCommand.cs index 8ddf1e23..89654d8d 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/CallOtherSubrCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/CallOtherSubrCommand.cs @@ -7,48 +7,27 @@ /// the PostScript language procedure at the other subroutine index in the OtherSubrs array in the Private dictionary /// (or a built-in function equivalent to this procedure) is executed. /// - internal class CallOtherSubrCommand + internal static class CallOtherSubrCommand { public const string Name = "callothersubr"; public static readonly byte First = 12; public static readonly byte? Second = 16; - public bool TakeFromStackBottom { get; } = false; - public bool ClearsOperandStack { get; } = false; + public static bool TakeFromStackBottom { get; } = false; + public static bool ClearsOperandStack { get; } = false; + + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// The arguments to pass to the other subroutine. - /// - public IReadOnlyList Arguments { get; } - - /// - /// The number of arguments to pass to the other subroutine. - /// - public int ArgumentCount { get; } - - /// - /// The index of the other subroutine to call in the font's Private dictionary. - /// - public int OtherSubroutineIndex { get; } - - /// - /// Create a new . - /// - /// The arguments to pass to the other subroutine. - /// The number of arguments to pass to the other subroutine. - /// The index of the other subroutine to call in the font's Private dictionary. - public CallOtherSubrCommand(IReadOnlyList arguments, int argumentCount, int otherSubroutineIndex) + public static void Run(Type1BuildCharContext context) { - Arguments = arguments; - ArgumentCount = argumentCount; - OtherSubroutineIndex = otherSubroutineIndex; - } - - public override string ToString() - { - var joined = string.Join(" ", Arguments); - return $"{joined} {ArgumentCount} {OtherSubroutineIndex} {Name}"; + var index = (int)context.Stack.PopTop(); + var numberOfArguments = (int)context.Stack.PopTop(); + var otherSubroutineArguments = new List(numberOfArguments); + for (int j = 0; j < numberOfArguments; j++) + { + otherSubroutineArguments.Add(context.Stack.PopTop()); + } } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/CallSubrCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/CallSubrCommand.cs index a3481fe2..3ac956b9 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/CallSubrCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/CallSubrCommand.cs @@ -3,33 +3,21 @@ /// /// Calls a subroutine with index from the subroutines array in the Private dictionary. /// - internal class CallSubrCommand + internal static class CallSubrCommand { public const string Name = "callsubr"; public static readonly byte First = 10; public static readonly byte? Second = null; + + public static bool TakeFromStackBottom { get; } = false; + public static bool ClearsOperandStack { get; } = false; + + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public bool TakeFromStackBottom { get; } = false; - public bool ClearsOperandStack { get; } = false; - - /// - /// The index of the subroutine in the Private dictionary. - /// - public int SubroutineIndex { get; } - - /// - /// Creates a new . - /// - /// The index of the subroutine in the Private dictionary. - public CallSubrCommand(int subroutineIndex) + public static void Run(Type1BuildCharContext context) { - SubroutineIndex = subroutineIndex; - } - - public override string ToString() - { - return $"{SubroutineIndex} {Name}"; + var index = (int)context.Stack.PopTop(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/DivCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/DivCommand.cs index 4c28710a..aaa50a7a 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/DivCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/DivCommand.cs @@ -3,25 +3,26 @@ /// /// This operator returns the result of dividing num1 by num2. The result is always a real. /// - internal class DivCommand + internal static class DivCommand { public const string Name = "div"; public static readonly byte First = 12; public static readonly byte? Second = 12; - public bool TakeFromStackBottom { get; } = false; - public bool ClearsOperandStack { get; } = false; - - public static DivCommand Instance { get; } = new DivCommand(); - - private DivCommand() + public static bool TakeFromStackBottom { get; } = false; + public static bool ClearsOperandStack { get; } = false; + + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); + + public static void Run(Type1BuildCharContext context) { - } + var first = context.Stack.PopTop(); + var second = context.Stack.PopTop(); - public override string ToString() - { - return Name; + var result = first / second; + + context.Stack.Push(result); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/PopCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/PopCommand.cs index 4ce36f45..5266fd27 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/PopCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/PopCommand.cs @@ -1,28 +1,25 @@ namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Arithmetic { /// - /// Pops a number from the top of the interpreter operand stack and pushes that number onto the operand stack. + /// Pops a number from the top of the PostScript interpreter operand stack and pushes that number onto the operand stack. /// This command is used only to retrieve a result from an OtherSubrs procedure. /// - internal class PopCommand + internal static class PopCommand { public const string Name = "pop"; public static readonly byte First = 12; public static readonly byte? Second = 17; + + public static bool TakeFromStackBottom { get; } = false; + public static bool ClearsOperandStack { get; } = false; - public bool TakeFromStackBottom { get; } = false; - public bool ClearsOperandStack { get; } = false; + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public static PopCommand Instance { get; } = new PopCommand(); - - private PopCommand() + public static void Run(Type1BuildCharContext context) { - } - - public override string ToString() - { - return Name; + var num = context.PostscriptStack.PopTop(); + context.Stack.Push(num); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/ReturnCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/ReturnCommand.cs index e5614a35..c1b0f94c 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/ReturnCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/ReturnCommand.cs @@ -3,25 +3,21 @@ /// /// Returns from a charstring subroutine and continues execution in the calling charstring. /// - internal class ReturnCommand + internal static class ReturnCommand { public const string Name = "return"; public static readonly byte First = 11; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = false; - public bool ClearsOperandStack { get; } = false; + public static bool TakeFromStackBottom { get; } = false; + public static bool ClearsOperandStack { get; } = false; + + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public static ReturnCommand Instance { get; } = new ReturnCommand(); - - private ReturnCommand() + public static void Run(Type1BuildCharContext context) { - } - public override string ToString() - { - return Name; } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/SetCurrentPointCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/SetCurrentPointCommand.cs index 1aac3e2f..9f25dac7 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/SetCurrentPointCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Arithmetic/SetCurrentPointCommand.cs @@ -3,40 +3,24 @@ /// /// Sets the current point to (x, y) in absolute character space coordinates without performing a charstring moveto command. /// - internal class SetCurrentPointCommand + internal static class SetCurrentPointCommand { public const string Name = "setcurrentpoint"; public static readonly byte First = 12; public static readonly byte? Second = 33; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The X coordinate in character space. - /// - public int X { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// The Y coordinate in character space. - /// - public int Y { get; } - - /// - /// Creates a new . - /// - /// The X coordinate in character space. - /// The Y coordinate in character space. - public SetCurrentPointCommand(int x, int y) + public static void Run(Type1BuildCharContext context) { - X = x; - Y = y; - } + var x = context.Stack.PopBottom(); + var y = context.Stack.PopBottom(); - public override string ToString() - { - return $"{X} {Y} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/DotSectionCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/DotSectionCommand.cs index e73ac8e6..67fc47ad 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/DotSectionCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/DotSectionCommand.cs @@ -3,25 +3,21 @@ /// /// Brackets an outline section for the dots in letters such as "i", "j" and "!". /// - internal class DotSectionCommand + internal static class DotSectionCommand { public const string Name = "dotsection"; public static readonly byte First = 12; public static readonly byte? Second = 0; - public bool TakeFromStackBottom { get; } = false; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = false; + public static bool ClearsOperandStack { get; } = true; + + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public static DotSectionCommand Instance { get; } = new DotSectionCommand(); - - private DotSectionCommand() + public static void Run(Type1BuildCharContext context) { - } - - public override string ToString() - { - return Name; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStem3Command.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStem3Command.cs index 49151f8d..8cef1dab 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStem3Command.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStem3Command.cs @@ -10,41 +10,28 @@ /// /// Suited to letters with 3 horizontal stems like 'E'. /// - internal class HStem3Command + internal static class HStem3Command { public const string Name = "hstem3"; public static readonly byte First = 12; public static readonly byte? Second = 2; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - public int Y0 { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public int DeltaY0 { get; } - - public int Y1 { get; } - - public int DeltaY1 { get; } - - public int Y2 { get; } - - public int DeltaY2 { get; } - - public HStem3Command(int y0, int deltaY0, int y1, int deltaY1, int y2, int deltaY2) + public static void Run(Type1BuildCharContext context) { - Y0 = y0; - DeltaY0 = deltaY0; - Y1 = y1; - DeltaY1 = deltaY1; - Y2 = y2; - DeltaY2 = deltaY2; - } + var y0 = context.Stack.PopBottom(); + var dy0 = context.Stack.PopBottom(); + var y1 = context.Stack.PopBottom(); + var dy1 = context.Stack.PopBottom(); + var y2 = context.Stack.PopBottom(); + var dy2 = context.Stack.PopBottom(); - public override string ToString() - { - return $"{Y0} {DeltaY0} {Y1} {DeltaY1} {Y2} {DeltaY2} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStemCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStemCommand.cs index 8d2534e2..f2898c93 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStemCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStemCommand.cs @@ -4,40 +4,24 @@ /// Declares the vertical range of a horizontal stem zone between the y coordinates y and y+dy, /// where y is relative to the y coordinate of the left sidebearing point. /// - internal class HStemCommand + internal static class HStemCommand { public const string Name = "hstem"; public static readonly byte First = 1; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The first Y coordinate for the stem zone, relative to the current left sidebearing Y point. - /// - public int Y { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// The distance to move from Y vertically for the horizontal stem zone. - /// - public int DeltaY { get; } - - /// - /// Create a new . - /// - /// The lower Y coordinate of the stem zone. - /// The distance to move from Y vertically for the stem zone. - public HStemCommand(int y, int deltaY) + public static void Run(Type1BuildCharContext context) { - Y = y; - DeltaY = deltaY; - } + var y = context.Stack.PopBottom(); + var dy = context.Stack.PopBottom(); - public override string ToString() - { - return $"{Y} {DeltaY} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStem3Command.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStem3Command.cs index 6f72200f..b2bcb1cf 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStem3Command.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStem3Command.cs @@ -17,34 +17,21 @@ public static readonly byte First = 12; public static readonly byte? Second = 1; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - public int X0 { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public int DeltaX0 { get; } - - public int X1 { get; } - - public int DeltaX1 { get; } - - public int X2 { get; } - - public int DeltaX2 { get; } - - public VStem3Command(int x0, int deltaX0, int x1, int deltaX1, int x2, int deltaX2) + public static void Run(Type1BuildCharContext context) { - X0 = x0; - DeltaX0 = deltaX0; - X1 = x1; - DeltaX1 = deltaX1; - X2 = x2; - DeltaX2 = deltaX2; - } + var x0 = context.Stack.PopBottom(); + var dx0 = context.Stack.PopBottom(); + var x1 = context.Stack.PopBottom(); + var dx1 = context.Stack.PopBottom(); + var x2 = context.Stack.PopBottom(); + var dx2 = context.Stack.PopBottom(); - public override string ToString() - { - return $"{X0} {DeltaX0} {X1} {DeltaX1} {X2} {DeltaX2} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStemCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStemCommand.cs index 18503d27..237a7e2e 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStemCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStemCommand.cs @@ -4,35 +4,24 @@ /// Declares the horizontal range of a vertical stem zone between the x coordinates x and x+dx, /// where x is relative to the x coordinate of the left sidebearing point. /// - internal class VStemCommand + internal static class VStemCommand { public const string Name = "vstem"; public static readonly byte First = 3; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; + + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// The first X coordinate for the stem zone, relative to the current left sidebearing X point. - /// - public int X { get; } - - /// - /// The distance to move from X horizontally for the vertical stem zone. - /// - public int DeltaX { get; } - - public VStemCommand(int x, int deltaX) + public static void Run(Type1BuildCharContext context) { - X = x; - DeltaX = deltaX; - } + var x = context.Stack.PopBottom(); + var dx = context.Stack.PopBottom(); - public override string ToString() - { - return $"{X} {DeltaX} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/LazyType1Command.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/LazyType1Command.cs new file mode 100644 index 00000000..88542a16 --- /dev/null +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/LazyType1Command.cs @@ -0,0 +1,53 @@ +using System; + +namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands +{ + /// + /// Represents the deferred execution of a Type 1 Build Char command. + /// + internal class LazyType1Command + { + private readonly Action runCommand; + + public string Name { get; } + + public LazyType1Command(string name, Action runCommand) + { + Name = name; + this.runCommand = runCommand ?? throw new ArgumentNullException(nameof(runCommand)); + } + + public void Run(Type1BuildCharContext context) + { + runCommand(context); + } + + public override string ToString() + { + return Name; + } + } + + internal class Type1Stack + { + public decimal PopTop() + { + throw new NotImplementedException(); + } + + public decimal PopBottom() + { + throw new NotImplementedException(); + } + + public void Push(decimal value) + { + + } + + public void Clear() + { + + } + } +} diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/ClosePathCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/ClosePathCommand.cs index 29087499..1786429e 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/ClosePathCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/ClosePathCommand.cs @@ -3,25 +3,21 @@ /// /// Closes a sub-path. This command does not reposition the current point. /// - internal class ClosePathCommand + internal static class ClosePathCommand { public const string Name = "closepath"; public static readonly byte First = 9; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = false; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = false; + public static bool ClearsOperandStack { get; } = true; - public static ClosePathCommand Instance { get; } = new ClosePathCommand(); - - private ClosePathCommand() + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); + + public static void Run(Type1BuildCharContext context) { - } - - public override string ToString() - { - return Name; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HLineToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HLineToCommand.cs index 23cf4afe..518d7340 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HLineToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HLineToCommand.cs @@ -3,33 +3,23 @@ /// /// Horizontal line-to command. /// - internal class HLineToCommand + internal static class HLineToCommand { public const string Name = "hlineto"; public static readonly byte First = 6; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The length of the horizontal line. - /// - public int DeltaX { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// Create a new . - /// - /// The length of the horizontal line. - public HLineToCommand(int deltaX) + public static void Run(Type1BuildCharContext context) { - DeltaX = deltaX; - } + var deltaX = context.Stack.PopBottom(); - public override string ToString() - { - return $"{DeltaX} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HMoveToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HMoveToCommand.cs index c1e935e3..ebb5f8d4 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HMoveToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HMoveToCommand.cs @@ -3,33 +3,23 @@ /// /// Relative move to for horizontal dimension only. /// - internal class HMoveToCommand + internal static class HMoveToCommand { public const string Name = "hmoveto"; public static readonly byte First = 22; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The distance to move in horizontally. - /// - public int X { get; set; } - - /// - /// Create a new . - /// - /// The distance to move horizontally. - public HMoveToCommand(int x) - { - X = x; - } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public override string ToString() + public static void Run(Type1BuildCharContext context) { - return $"{X} {Name}"; + var x = context.Stack.PopBottom(); + + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HvCurveToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HvCurveToCommand.cs index 03f4d112..8367e377 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HvCurveToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/HvCurveToCommand.cs @@ -4,38 +4,26 @@ /// Horizontal vertical curve to command. Draws a Bézier curve when the first Bézier tangent is horizontal and the second Bézier tangent is vertical. /// Equivalent to dx1 0 dx2 dy2 0 dy3 rrcurveto. /// - internal class HvCurveToCommand + internal static class HvCurveToCommand { public const string Name = "hvcurveto"; public static readonly byte First = 31; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - public int PostControlPointDeltaX { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public int PreControlPointDeltaX { get; } - - public int PreControlPointDeltaY { get; } - - public int EndPointDeltaY { get; } - - /// - /// Create a new . - /// - public HvCurveToCommand(int postControlPointDeltaX, int preControlPointDeltaX, int preControlPointDeltaY, int endPointDeltaY) + public static void Run(Type1BuildCharContext context) { - PostControlPointDeltaX = postControlPointDeltaX; - PreControlPointDeltaX = preControlPointDeltaX; - PreControlPointDeltaY = preControlPointDeltaY; - EndPointDeltaY = endPointDeltaY; - } + var postControlPointDeltaX = context.Stack.PopBottom(); + var preControlPointDeltaX = context.Stack.PopBottom(); + var preControlPointDeltaY = context.Stack.PopBottom(); + var endPointDeltaY = context.Stack.PopBottom(); - public override string ToString() - { - return $"{PostControlPointDeltaX} {PreControlPointDeltaX} {PreControlPointDeltaY} {EndPointDeltaY} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RLineToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RLineToCommand.cs index c038d248..b8615bdf 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RLineToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RLineToCommand.cs @@ -3,40 +3,24 @@ /// /// Relative line-to command. Creates a line moving a distance relative to the current point. /// - internal class RLineToCommand + internal static class RLineToCommand { public const string Name = "rlineto"; public static readonly byte First = 5; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The distance to move horizontally. - /// - public int DeltaX { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// The distance to move vertically. - /// - public int DeltaY { get; } - - /// - /// Create a new . - /// - /// The distance to move horizontally. - /// The distance to move vertically. - public RLineToCommand(int deltaX, int deltaY) + public static void Run(Type1BuildCharContext context) { - DeltaX = deltaX; - DeltaY = deltaY; - } + var deltaX = context.Stack.PopBottom(); + var deltaY = context.Stack.PopBottom(); - public override string ToString() - { - return $"{DeltaX} {DeltaY} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RMoveToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RMoveToCommand.cs index 78b8be07..c279737c 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RMoveToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RMoveToCommand.cs @@ -9,40 +9,24 @@ /// If the previous path operation in the current path was also a moveto or rmoveto, /// that point is deleted from the current path and the new moveto point replaces it. /// - internal class RMoveToCommand + internal static class RMoveToCommand { public const string Name = "rmoveto"; public static readonly byte First = 21; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The distance to move horizontally. - /// - public int DeltaX { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// The distance to move vertically. - /// - public int DeltaY { get; } - - /// - /// Create a new . - /// - /// The distance to move horizontally. - /// The distance to move vertically. - public RMoveToCommand(int deltaX, int deltaY) + public static void Run(Type1BuildCharContext context) { - DeltaX = deltaX; - DeltaY = deltaY; - } + var deltaX = context.Stack.PopBottom(); + var deltaY = context.Stack.PopBottom(); - public override string ToString() - { - return $"{DeltaX} {DeltaY} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RelativeRCurveToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RelativeRCurveToCommand.cs index 92b8dae3..0cc00f2d 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RelativeRCurveToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/RelativeRCurveToCommand.cs @@ -5,36 +5,28 @@ /// point, the arguments to rrcurveto are relative to each other. /// Equivalent to: dx1 dy1 (dx1+dx2) (dy1+dy2) (dx1+dx2+dx3) (dy1+dy2+dy3) rcurveto. /// - internal class RelativeRCurveToCommand + internal static class RelativeRCurveToCommand { public const string Name = "rrcurveto"; public static readonly byte First = 8; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - public int DeltaX1 { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public int DeltaY1 { get; } - - public int DeltaX2 { get; } - - public int DeltaY2 { get; } - - public int DeltaX3 { get; } - - public int DeltaY3 { get; } - - public RelativeRCurveToCommand(int deltaX1, int deltaY1, int deltaX2, int deltaY2, int deltaX3, int deltaY3) + public static void Run(Type1BuildCharContext context) { - DeltaX1 = deltaX1; - DeltaY1 = deltaY1; - DeltaX2 = deltaX2; - DeltaY2 = deltaY2; - DeltaX3 = deltaX3; - DeltaY3 = deltaY3; + var dx1 = context.Stack.PopBottom(); + var dy1 = context.Stack.PopBottom(); + var dx2 = context.Stack.PopBottom(); + var dy2 = context.Stack.PopBottom(); + var dx3 = context.Stack.PopBottom(); + var dy3 = context.Stack.PopBottom(); + + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VLineToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VLineToCommand.cs index e08ae45a..0c962ede 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VLineToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VLineToCommand.cs @@ -3,33 +3,23 @@ /// /// Vertical-line to command. /// - internal class VLineToCommand + internal static class VLineToCommand { public const string Name = "vlineto"; public static readonly byte First = 7; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The length of the vertical line. - /// - public int DeltaY { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// Create a new . - /// - /// The length of the vertical line. - public VLineToCommand(int deltaY) + public static void Run(Type1BuildCharContext context) { - DeltaY = deltaY; - } + var deltaY = context.Stack.PopBottom(); - public override string ToString() - { - return $"{DeltaY} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VMoveToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VMoveToCommand.cs index a5c46232..64344f4b 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VMoveToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VMoveToCommand.cs @@ -3,33 +3,23 @@ /// /// Vertical move to. Moves relative to the current point. /// - internal class VMoveToCommand + internal static class VMoveToCommand { public const string Name = "vmoveto"; public static readonly byte First = 4; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The distance to move vertically. - /// - public int DeltaY { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// Create a new . - /// - /// The distance to move vertically. - public VMoveToCommand(int deltaY) + public static void Run(Type1BuildCharContext context) { - DeltaY = deltaY; - } + var deltaY = context.Stack.PopBottom(); - public override string ToString() - { - return $"{DeltaY} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VhCurveToCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VhCurveToCommand.cs index 04cd2521..7a712506 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VhCurveToCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/PathConstruction/VhCurveToCommand.cs @@ -5,30 +5,26 @@ /// Equivalent to 0 dy1 dx2 dy2 dx3 0 rrcurveto. /// This command eliminates two arguments from an rrcurveto call when the first Bézier tangent is vertical and the second Bézier tangent is horizontal. /// - internal class VhCurveToCommand + internal static class VhCurveToCommand { public const string Name = "vhcurveto"; public static readonly byte First = 30; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - public int PostControlPointDeltaY { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public int PreControlPointDeltaX { get; } - - public int PreControlPointDeltaY { get; } - - public int EndPointDeltaX { get; } - - public VhCurveToCommand(int postControlPointDeltaY, int preControlPointDeltaX, int preControlPointDeltaY, int endPointDeltaX) + public static void Run(Type1BuildCharContext context) { - PostControlPointDeltaY = postControlPointDeltaY; - PreControlPointDeltaX = preControlPointDeltaX; - PreControlPointDeltaY = preControlPointDeltaY; - EndPointDeltaX = endPointDeltaX; + var postControlPointDeltaY = context.Stack.PopBottom(); + var preControlPointDeltaX = context.Stack.PopBottom(); + var preControlPointDeltaY = context.Stack.PopBottom(); + var endPointDeltaX = context.Stack.PopBottom(); + + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/EndCharCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/EndCharCommand.cs index 74c76031..d32ffbb7 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/EndCharCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/EndCharCommand.cs @@ -4,25 +4,21 @@ /// Finishes a charstring outline definition and must be the last command in a character's outline /// (except for accented characters defined using seac). /// - internal class EndCharCommand + internal static class EndCharCommand { public const string Name = "endchar"; public static readonly byte First = 14; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = false; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = false; + public static bool ClearsOperandStack { get; } = true; - public static EndCharCommand Instance { get; } = new EndCharCommand(); + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - private EndCharCommand() + public static void Run(Type1BuildCharContext context) { - } - - public override string ToString() - { - return Name; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/HsbwCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/HsbwCommand.cs index 9141c061..f44b7234 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/HsbwCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/HsbwCommand.cs @@ -6,24 +6,24 @@ /// This command sets the left sidebearing point at (sbx, 0) and sets the character width vector to(wx, 0) in character space. /// This command also sets the current point to (sbx, 0), but does not place the point in the character path. /// - internal class HsbwCommand + internal static class HsbwCommand { public const string Name = "hsbw"; public static readonly byte First = 13; public static readonly byte? Second = null; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - public int LeftSidebearingPointX { get; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - public int CharacterWidthVectorX { get; } - - public HsbwCommand(int leftSidebearingPointX, int characterWidthVectorX) + public static void Run(Type1BuildCharContext context) { - LeftSidebearingPointX = leftSidebearingPointX; - CharacterWidthVectorX = characterWidthVectorX; + var leftSidebearingPointX = context.Stack.PopBottom(); + var characterWidthVectorX = context.Stack.PopBottom(); + + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/SbwCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/SbwCommand.cs index 0031d880..0a869681 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/SbwCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/SbwCommand.cs @@ -11,47 +11,19 @@ public static readonly byte First = 12; public static readonly byte? Second = 7; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The X value of the left sidebearing point. - /// - public int LeftSidebearingX { get; set; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// The Y value of the left sidebearing point. - /// - public int LeftSidebearingY { get; set; } - - /// - /// The X value of the character width vector. - /// - public int CharacterWidthX { get; } - - /// - /// The Y value of the character width vector. - /// - public int CharacterWidthY { get; } - - /// - /// Create a new . - /// - /// The X value of the left sidebearing point. - /// The Y value of the left sidebearing point. - /// The X value of the character width vector. - /// The Y value of the character width vector. - public SbwCommand(int leftSidebearingX, int leftSidebearingY, int characterWidthX, int characterWidthY) + public static void Run(Type1BuildCharContext context) { - LeftSidebearingX = leftSidebearingX; - LeftSidebearingY = leftSidebearingY; - CharacterWidthX = characterWidthX; - CharacterWidthY = characterWidthY; - } + var leftSidebearingX = context.Stack.PopBottom(); + var leftSidebearingY = context.Stack.PopBottom(); + var characterWidthX = context.Stack.PopBottom(); + var characterWidthY = context.Stack.PopBottom(); - public override string ToString() - { - return $"{LeftSidebearingX} {LeftSidebearingY} {CharacterWidthX} {CharacterWidthY} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/SeacCommand.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/SeacCommand.cs index a951879c..18df53e8 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/SeacCommand.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/StartFinishOutline/SeacCommand.cs @@ -4,61 +4,27 @@ /// Standard encoding accented character. /// Makes an accented character from two other characters in the font program. /// - internal class SeacCommand + internal static class SeacCommand { public const string Name = "seac"; public static readonly byte First = 12; public static readonly byte? Second = 6; - public bool TakeFromStackBottom { get; } = true; - public bool ClearsOperandStack { get; } = true; + public static bool TakeFromStackBottom { get; } = true; + public static bool ClearsOperandStack { get; } = true; - /// - /// The x component of the left sidebearing of the accent. - /// - public int AccentLeftSidebearingX { get; set; } + public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); - /// - /// The x position of the origin of the accent character relative to the base character. - /// - public int AccentOriginX { get; set; } - - /// - /// The y position of the origin of the accent character relative to the base character. - /// - public int AccentOriginY { get; set; } - - /// - /// The character code of the base character. - /// - public int BaseCharacterCode { get; set; } - - /// - /// The character code of the accent character. - /// - public int AccentCharacterCode { get; set; } - - /// - /// Create a new . - /// - /// The x component of the left sidebearing of the accent. - /// The x position of the origin of the accent character relative to the base character. - /// The y position of the origin of the accent character relative to the base character. - /// The character code of the base character. - /// The character code of the accent character. - public SeacCommand(int accentLeftSidebearingX, int accentOriginX, int accentOriginY, int baseCharacterCode, int accentCharacterCode) + public static void Run(Type1BuildCharContext context) { - AccentLeftSidebearingX = accentLeftSidebearingX; - AccentOriginX = accentOriginX; - AccentOriginY = accentOriginY; - BaseCharacterCode = baseCharacterCode; - AccentCharacterCode = accentCharacterCode; - } + var accentLeftSidebearingX = context.Stack.PopBottom(); + var accentOriginX = context.Stack.PopBottom(); + var accentOriginY = context.Stack.PopBottom(); + var baseCharacterCode = context.Stack.PopBottom(); + var accentCharacterCode = context.Stack.PopBottom(); - public override string ToString() - { - return $"{AccentLeftSidebearingX} {AccentOriginX} {AccentOriginY} {BaseCharacterCode} {AccentCharacterCode} {Name}"; + context.Stack.Clear(); } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Type1BuildCharContext.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Type1BuildCharContext.cs new file mode 100644 index 00000000..f0cabcb2 --- /dev/null +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Type1BuildCharContext.cs @@ -0,0 +1,9 @@ +namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands +{ + internal class Type1BuildCharContext + { + public Type1Stack Stack { get; } + + public Type1Stack PostscriptStack { get; } + } +} diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringCommandFactory.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringCommandFactory.cs deleted file mode 100644 index 5bf6d40f..00000000 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringCommandFactory.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace UglyToad.PdfPig.Fonts.Type1.CharStrings -{ - using System.Collections.Generic; - using Commands.Hint; - - internal static class Type1CharStringCommandFactory - { - public static object GetCommand(List arguments, byte v, IReadOnlyList bytes, ref int i) - { - switch (v) - { - case 1: - { - return new HStemCommand(arguments[0], arguments[1]); - } - case 12: - { - var next = bytes[++i]; - switch (next) - { - - } - - break; - } - } - return null; - } - } -} diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringParser.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringParser.cs index 5582c09f..f3028037 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringParser.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringParser.cs @@ -1,36 +1,99 @@ namespace UglyToad.PdfPig.Fonts.Type1.CharStrings { + using System; using System.Collections.Generic; + using Commands; + using Commands.Arithmetic; + using Commands.Hint; + using Commands.PathConstruction; + using Commands.StartFinishOutline; using Parser; + using Util; - internal class Type1CharStringParser + /// + /// Decodes a set of CharStrings to their corresponding Type 1 BuildChar operations. + /// + /// + /// A charstring is an encrypted sequence of unsigned 8-bit bytes that encode integers and commands. + /// Type 1 BuildChar, when interpreting a charstring, will first decrypt it and then will decode + /// its bytes one at a time in sequence. + /// + /// The value in a byte indicates a command, a number, or subsequent bytes that are to be interpreted + /// in a special way. + /// + /// Once the bytes are decoded into numbers and commands, the execution of these numbers and commands proceeds in a + /// manner similar to the operation of the PostScript language. Type 1 BuildChar uses its own operand stack, + /// called the Type 1 BuildChar operand stack, that is distinct from the PostScript interpreter operand stack. + /// + /// This stack holds up to 24 numeric entries. A number, decoded from a charstring, is pushed onto the Type 1 + /// BuildChar operand stack. A command expects its arguments in order on this operand stack with all arguments generally taken + /// from the bottom of the stack (first argument bottom-most); + /// however, some commands, particularly the subroutine commands, normally work from the top of the stack. If a command returns + /// results, they are pushed onto the Type 1 BuildChar operand stack (last result topmost). + /// + internal static class Type1CharStringParser { - public static void Parse(IReadOnlyList charStrings, IReadOnlyList subroutines) + public static Type1CharStrings Parse(IReadOnlyList charStrings, IReadOnlyList subroutines) { + if (charStrings == null) + { + throw new ArgumentNullException(nameof(charStrings)); + } + + if (subroutines == null) + { + throw new ArgumentNullException(nameof(subroutines)); + } + + var charStringResults = new Dictionary(charStrings.Count); + foreach (var charString in charStrings) { - ParseSingle(charString.Bytes); + var commandSequence = ParseSingle(charString.Bytes); + + charStringResults[charString.Name] = new Type1CharStrings.CommandSequence(commandSequence); } + + var subroutineResults = new Dictionary(subroutines.Count); + + foreach (var subroutine in subroutines) + { + var commandSequence = ParseSingle(subroutine.Bytes); + + subroutineResults[subroutine.Index] = new Type1CharStrings.CommandSequence(commandSequence); + } + + return new Type1CharStrings(charStringResults, subroutineResults); } - private static void ParseSingle(IReadOnlyList charStringBytes) + private static IReadOnlyList> ParseSingle(IReadOnlyList charStringBytes) { - var numberStack = new List(); + var interpreted = new List>(); + for (var i = 0; i < charStringBytes.Count; i++) { var b = charStringBytes[i]; if (b <= 31) { - var command = Type1CharStringCommandFactory.GetCommand(numberStack, b, charStringBytes, ref i); + var command = GetCommand(b, charStringBytes, ref i); + + if (command == null) + { + throw new InvalidOperationException($"Could not find command with code {b}."); + } + + interpreted.Add(new DiscriminatedUnion.Case2(command)); } else { var val = InterpretNumber(b, charStringBytes, ref i); - numberStack.Add(val); + interpreted.Add(new DiscriminatedUnion.Case1(val)); } } + + return interpreted; } private static int InterpretNumber(byte b, IReadOnlyList bytes, ref int i) @@ -52,9 +115,78 @@ return -((b - 251) * 256) - w - 108; } - var result = bytes[++i] << 24 + bytes[++i] << 16 + bytes[++i] << 8 + bytes[++i]; + var result = bytes[++i] << 24 | bytes[++i] << 16 | bytes[++i] << 8 | bytes[++i]; return result; } + + public static LazyType1Command GetCommand(byte v, IReadOnlyList bytes, ref int i) + { + switch (v) + { + case 1: + return HStemCommand.Lazy; + case 3: + return VStemCommand.Lazy; + case 4: + return VMoveToCommand.Lazy; + case 5: + return RLineToCommand.Lazy; + case 6: + return HLineToCommand.Lazy; + case 7: + return VLineToCommand.Lazy; + case 8: + return RelativeRCurveToCommand.Lazy; + case 9: + return ClosePathCommand.Lazy; + case 10: + return CallSubrCommand.Lazy; + case 11: + return ReturnCommand.Lazy; + case 13: + return HsbwCommand.Lazy; + case 14: + return EndCharCommand.Lazy; + case 21: + return RMoveToCommand.Lazy; + case 22: + return HMoveToCommand.Lazy; + case 30: + return VhCurveToCommand.Lazy; + case 31: + return HvCurveToCommand.Lazy; + case 12: + { + var next = bytes[++i]; + + switch (next) + { + case 0: + return DotSectionCommand.Lazy; + case 1: + return VStem3Command.Lazy; + case 2: + return HStem3Command.Lazy; + case 6: + return SeacCommand.Lazy; + case 7: + return SbwCommand.Lazy; + case 12: + return DivCommand.Lazy; + case 16: + return CallOtherSubrCommand.Lazy; + case 17: + return PopCommand.Lazy; + case 33: + return SetCurrentPointCommand.Lazy; + } + + break; + } + } + + return null; + } } } diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStrings.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStrings.cs new file mode 100644 index 00000000..f27ff52d --- /dev/null +++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStrings.cs @@ -0,0 +1,36 @@ +namespace UglyToad.PdfPig.Fonts.Type1.CharStrings +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Commands; + using Util; + + internal class Type1CharStrings + { + public IReadOnlyDictionary CharStrings { get; } + + public IReadOnlyDictionary Subroutines { get; } + + public Type1CharStrings(IReadOnlyDictionary charStrings, IReadOnlyDictionary subroutines) + { + CharStrings = charStrings ?? throw new ArgumentNullException(nameof(charStrings)); + Subroutines = subroutines ?? throw new ArgumentNullException(nameof(subroutines)); + } + + public class CommandSequence + { + public IReadOnlyList> Commands { get; } + + public CommandSequence(IReadOnlyList> commands) + { + Commands = commands ?? throw new ArgumentNullException(nameof(commands)); + } + + public override string ToString() + { + return string.Join(", ", Commands.Select(x => x.ToString())); + } + } + } +} diff --git a/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1EncryptedPortionParser.cs b/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1EncryptedPortionParser.cs index 73f2e87e..44636b78 100644 --- a/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1EncryptedPortionParser.cs +++ b/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1EncryptedPortionParser.cs @@ -290,7 +290,7 @@ charStrings = new Type1CharstringDecryptedBytes[0]; } - Type1CharStringParser.Parse(charStrings, builder.Subroutines ?? new Type1CharstringDecryptedBytes[0]); + var instructions = Type1CharStringParser.Parse(charStrings, builder.Subroutines ?? new Type1CharstringDecryptedBytes[0]); return decrypted; } diff --git a/src/UglyToad.PdfPig/Util/DiscriminatedUnion.cs b/src/UglyToad.PdfPig/Util/DiscriminatedUnion.cs new file mode 100644 index 00000000..88837bd1 --- /dev/null +++ b/src/UglyToad.PdfPig/Util/DiscriminatedUnion.cs @@ -0,0 +1,52 @@ +using System; + +namespace UglyToad.PdfPig.Util +{ + // ReSharper disable once InconsistentNaming + internal abstract class DiscriminatedUnion + { + public abstract T Match(Func first, Func second); + + private DiscriminatedUnion() { } + + public sealed class Case1 : DiscriminatedUnion + { + public readonly A Item; + + public Case1(A item) + { + Item = item; + } + + public override T Match(Func first, Func second) + { + return first(Item); + } + + public override string ToString() + { + return Item?.ToString() ?? string.Empty; + } + } + + public sealed class Case2 : DiscriminatedUnion + { + public readonly B Item; + + public Case2(B item) + { + Item = item; + } + + public override T Match(Func first, Func second) + { + return second(Item); + } + + public override string ToString() + { + return Item?.ToString() ?? string.Empty; + } + } + } +}