mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 19:54:52 +08:00
change type1 commands to be static and lazily evaluated and return the command sequences from the parser
This commit is contained in:
@@ -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.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// The arguments to pass to the other subroutine.
|
||||
/// </summary>
|
||||
public IReadOnlyList<int> Arguments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of arguments to pass to the other subroutine.
|
||||
/// </summary>
|
||||
public int ArgumentCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The index of the other subroutine to call in the font's Private dictionary.
|
||||
/// </summary>
|
||||
public int OtherSubroutineIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="CallOtherSubrCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="arguments">The arguments to pass to the other subroutine.</param>
|
||||
/// <param name="argumentCount">The number of arguments to pass to the other subroutine.</param>
|
||||
/// <param name="otherSubroutineIndex">The index of the other subroutine to call in the font's Private dictionary.</param>
|
||||
public CallOtherSubrCommand(IReadOnlyList<int> 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<decimal>(numberOfArguments);
|
||||
for (int j = 0; j < numberOfArguments; j++)
|
||||
{
|
||||
otherSubroutineArguments.Add(context.Stack.PopTop());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,33 +3,21 @@
|
||||
/// <summary>
|
||||
/// Calls a subroutine with index from the subroutines array in the Private dictionary.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the subroutine in the Private dictionary.
|
||||
/// </summary>
|
||||
public int SubroutineIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="CallSubrCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="subroutineIndex">The index of the subroutine in the Private dictionary.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,25 +3,26 @@
|
||||
/// <summary>
|
||||
/// This operator returns the result of dividing num1 by num2. The result is always a real.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,28 +1,25 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Arithmetic
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,25 +3,21 @@
|
||||
/// <summary>
|
||||
/// Returns from a charstring subroutine and continues execution in the calling charstring.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,40 +3,24 @@
|
||||
/// <summary>
|
||||
/// Sets the current point to (x, y) in absolute character space coordinates without performing a charstring moveto command.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The X coordinate in character space.
|
||||
/// </summary>
|
||||
public int X { get; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// The Y coordinate in character space.
|
||||
/// </summary>
|
||||
public int Y { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SetCurrentPointCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The X coordinate in character space.</param>
|
||||
/// <param name="y">The Y coordinate in character space.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,25 +3,21 @@
|
||||
/// <summary>
|
||||
/// Brackets an outline section for the dots in letters such as "i", "j" and "!".
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,41 +10,28 @@
|
||||
/// <remarks>
|
||||
/// Suited to letters with 3 horizontal stems like 'E'.
|
||||
/// </remarks>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The first Y coordinate for the stem zone, relative to the current left sidebearing Y point.
|
||||
/// </summary>
|
||||
public int Y { get; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move from Y vertically for the horizontal stem zone.
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="HStemCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="y">The lower Y coordinate of the stem zone.</param>
|
||||
/// <param name="deltaY">The distance to move from Y vertically for the stem zone.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// The first X coordinate for the stem zone, relative to the current left sidebearing X point.
|
||||
/// </summary>
|
||||
public int X { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move from X horizontally for the vertical stem zone.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the deferred execution of a Type 1 Build Char command.
|
||||
/// </summary>
|
||||
internal class LazyType1Command
|
||||
{
|
||||
private readonly Action<Type1BuildCharContext> runCommand;
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public LazyType1Command(string name, Action<Type1BuildCharContext> 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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,25 +3,21 @@
|
||||
/// <summary>
|
||||
/// Closes a sub-path. This command does not reposition the current point.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,33 +3,23 @@
|
||||
/// <summary>
|
||||
/// Horizontal line-to command.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The length of the horizontal line.
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="HLineToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaX">The length of the horizontal line.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,33 +3,23 @@
|
||||
/// <summary>
|
||||
/// Relative move to for horizontal dimension only.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move in horizontally.
|
||||
/// </summary>
|
||||
public int X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="HMoveToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The distance to move horizontally.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
/// </summary>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="HvCurveToCommand"/>.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,40 +3,24 @@
|
||||
/// <summary>
|
||||
/// Relative line-to command. Creates a line moving a distance relative to the current point.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move horizontally.
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move vertically.
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="RLineToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaX">The distance to move horizontally.</param>
|
||||
/// <param name="deltaY">The distance to move vertically.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
/// </remarks>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move horizontally.
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move vertically.
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="RMoveToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaX">The distance to move horizontally.</param>
|
||||
/// <param name="deltaY">The distance to move vertically.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,33 +3,23 @@
|
||||
/// <summary>
|
||||
/// Vertical-line to command.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The length of the vertical line.
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="VLineToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaY">The length of the vertical line.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,33 +3,23 @@
|
||||
/// <summary>
|
||||
/// Vertical move to. Moves relative to the current point.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move vertically.
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="VMoveToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaY">The distance to move vertically.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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).
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// The X value of the left sidebearing point.
|
||||
/// </summary>
|
||||
public int LeftSidebearingX { get; set; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// The Y value of the left sidebearing point.
|
||||
/// </summary>
|
||||
public int LeftSidebearingY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The X value of the character width vector.
|
||||
/// </summary>
|
||||
public int CharacterWidthX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Y value of the character width vector.
|
||||
/// </summary>
|
||||
public int CharacterWidthY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SbwCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="leftSidebearingX">The X value of the left sidebearing point.</param>
|
||||
/// <param name="leftSidebearingY">The Y value of the left sidebearing point.</param>
|
||||
/// <param name="characterWidthX">The X value of the character width vector.</param>
|
||||
/// <param name="characterWidthY">The Y value of the character width vector.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,61 +4,27 @@
|
||||
/// Standard encoding accented character.
|
||||
/// Makes an accented character from two other characters in the font program.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The x component of the left sidebearing of the accent.
|
||||
/// </summary>
|
||||
public int AccentLeftSidebearingX { get; set; }
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
/// <summary>
|
||||
/// The x position of the origin of the accent character relative to the base character.
|
||||
/// </summary>
|
||||
public int AccentOriginX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The y position of the origin of the accent character relative to the base character.
|
||||
/// </summary>
|
||||
public int AccentOriginY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The character code of the base character.
|
||||
/// </summary>
|
||||
public int BaseCharacterCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The character code of the accent character.
|
||||
/// </summary>
|
||||
public int AccentCharacterCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SeacCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="accentLeftSidebearingX">The x component of the left sidebearing of the accent.</param>
|
||||
/// <param name="accentOriginX">The x position of the origin of the accent character relative to the base character.</param>
|
||||
/// <param name="accentOriginY">The y position of the origin of the accent character relative to the base character.</param>
|
||||
/// <param name="baseCharacterCode">The character code of the base character.</param>
|
||||
/// <param name="accentCharacterCode">The character code of the accent character.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands
|
||||
{
|
||||
internal class Type1BuildCharContext
|
||||
{
|
||||
public Type1Stack Stack { get; }
|
||||
|
||||
public Type1Stack PostscriptStack { get; }
|
||||
}
|
||||
}
|
@@ -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<int> arguments, byte v, IReadOnlyList<byte> 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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
/// <summary>
|
||||
/// Decodes a set of CharStrings to their corresponding Type 1 BuildChar operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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).
|
||||
/// </remarks>
|
||||
internal static class Type1CharStringParser
|
||||
{
|
||||
public static void Parse(IReadOnlyList<Type1CharstringDecryptedBytes> charStrings, IReadOnlyList<Type1CharstringDecryptedBytes> subroutines)
|
||||
public static Type1CharStrings Parse(IReadOnlyList<Type1CharstringDecryptedBytes> charStrings, IReadOnlyList<Type1CharstringDecryptedBytes> subroutines)
|
||||
{
|
||||
if (charStrings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(charStrings));
|
||||
}
|
||||
|
||||
if (subroutines == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(subroutines));
|
||||
}
|
||||
|
||||
var charStringResults = new Dictionary<string, Type1CharStrings.CommandSequence>(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<int, Type1CharStrings.CommandSequence>(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<byte> charStringBytes)
|
||||
private static IReadOnlyList<DiscriminatedUnion<decimal, LazyType1Command>> ParseSingle(IReadOnlyList<byte> charStringBytes)
|
||||
{
|
||||
var numberStack = new List<int>();
|
||||
var interpreted = new List<DiscriminatedUnion<decimal, LazyType1Command>>();
|
||||
|
||||
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<decimal, LazyType1Command>.Case2(command));
|
||||
}
|
||||
else
|
||||
{
|
||||
var val = InterpretNumber(b, charStringBytes, ref i);
|
||||
|
||||
numberStack.Add(val);
|
||||
interpreted.Add(new DiscriminatedUnion<decimal, LazyType1Command>.Case1(val));
|
||||
}
|
||||
}
|
||||
|
||||
return interpreted;
|
||||
}
|
||||
|
||||
private static int InterpretNumber(byte b, IReadOnlyList<byte> 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<byte> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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<string, CommandSequence> CharStrings { get; }
|
||||
|
||||
public IReadOnlyDictionary<int, CommandSequence> Subroutines { get; }
|
||||
|
||||
public Type1CharStrings(IReadOnlyDictionary<string, CommandSequence> charStrings, IReadOnlyDictionary<int, CommandSequence> subroutines)
|
||||
{
|
||||
CharStrings = charStrings ?? throw new ArgumentNullException(nameof(charStrings));
|
||||
Subroutines = subroutines ?? throw new ArgumentNullException(nameof(subroutines));
|
||||
}
|
||||
|
||||
public class CommandSequence
|
||||
{
|
||||
public IReadOnlyList<DiscriminatedUnion<decimal, LazyType1Command>> Commands { get; }
|
||||
|
||||
public CommandSequence(IReadOnlyList<DiscriminatedUnion<decimal, LazyType1Command>> commands)
|
||||
{
|
||||
Commands = commands ?? throw new ArgumentNullException(nameof(commands));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join(", ", Commands.Select(x => x.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
52
src/UglyToad.PdfPig/Util/DiscriminatedUnion.cs
Normal file
52
src/UglyToad.PdfPig/Util/DiscriminatedUnion.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
|
||||
namespace UglyToad.PdfPig.Util
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
internal abstract class DiscriminatedUnion<A, B>
|
||||
{
|
||||
public abstract T Match<T>(Func<A, T> first, Func<B, T> second);
|
||||
|
||||
private DiscriminatedUnion() { }
|
||||
|
||||
public sealed class Case1 : DiscriminatedUnion<A, B>
|
||||
{
|
||||
public readonly A Item;
|
||||
|
||||
public Case1(A item)
|
||||
{
|
||||
Item = item;
|
||||
}
|
||||
|
||||
public override T Match<T>(Func<A, T> first, Func<B, T> second)
|
||||
{
|
||||
return first(Item);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Item?.ToString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Case2 : DiscriminatedUnion<A, B>
|
||||
{
|
||||
public readonly B Item;
|
||||
|
||||
public Case2(B item)
|
||||
{
|
||||
Item = item;
|
||||
}
|
||||
|
||||
public override T Match<T>(Func<A, T> first, Func<B, T> second)
|
||||
{
|
||||
return second(Item);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Item?.ToString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user