mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 19:54:52 +08:00
create command classes for each of the commands in type 1 charstrings
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
|
@@ -0,0 +1,54 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Arithmetic
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Call other subroutine command. Arguments are pushed onto the PostScript interpreter operand stack then
|
||||
/// 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
|
||||
{
|
||||
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;
|
||||
|
||||
/// <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)
|
||||
{
|
||||
Arguments = arguments;
|
||||
ArgumentCount = argumentCount;
|
||||
OtherSubroutineIndex = otherSubroutineIndex;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var joined = string.Join(" ", Arguments);
|
||||
return $"{joined} {ArgumentCount} {OtherSubroutineIndex} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Arithmetic
|
||||
{
|
||||
/// <summary>
|
||||
/// Calls a subroutine with index from the subroutines array in the Private dictionary.
|
||||
/// </summary>
|
||||
internal class CallSubrCommand
|
||||
{
|
||||
public const string Name = "callsubr";
|
||||
|
||||
public static readonly byte First = 10;
|
||||
public static readonly byte? Second = null;
|
||||
|
||||
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)
|
||||
{
|
||||
SubroutineIndex = subroutineIndex;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{SubroutineIndex} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Arithmetic
|
||||
{
|
||||
/// <summary>
|
||||
/// This operator returns the result of dividing num1 by num2. The result is always a real.
|
||||
/// </summary>
|
||||
internal 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 override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
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.
|
||||
/// This command is used only to retrieve a result from an OtherSubrs procedure.
|
||||
/// </summary>
|
||||
internal class PopCommand
|
||||
{
|
||||
public const string Name = "pop";
|
||||
|
||||
public static readonly byte First = 12;
|
||||
public static readonly byte? Second = 17;
|
||||
|
||||
public bool TakeFromStackBottom { get; } = false;
|
||||
public bool ClearsOperandStack { get; } = false;
|
||||
|
||||
public static PopCommand Instance { get; } = new PopCommand();
|
||||
|
||||
private PopCommand()
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Arithmetic
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns from a charstring subroutine and continues execution in the calling charstring.
|
||||
/// </summary>
|
||||
internal 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 ReturnCommand Instance { get; } = new ReturnCommand();
|
||||
|
||||
private ReturnCommand()
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Arithmetic
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the current point to (x, y) in absolute character space coordinates without performing a charstring moveto command.
|
||||
/// </summary>
|
||||
internal 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;
|
||||
|
||||
/// <summary>
|
||||
/// The X coordinate in character space.
|
||||
/// </summary>
|
||||
public int X { get; }
|
||||
|
||||
/// <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)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{X} {Y} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
|
||||
{
|
||||
internal class DotSectionCommand
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
|
||||
{
|
||||
internal class HStem3Command
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
|
||||
{
|
||||
internal class HStemCommand
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
|
||||
{
|
||||
internal class VStem3Command
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
|
||||
{
|
||||
internal class VStemCommand
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Closes a sub-path. This command does not reposition the current point.
|
||||
/// </summary>
|
||||
internal 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 ClosePathCommand Instance { get; } = new ClosePathCommand();
|
||||
|
||||
private ClosePathCommand()
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Horizontal line-to command.
|
||||
/// </summary>
|
||||
internal 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;
|
||||
|
||||
/// <summary>
|
||||
/// The length of the horizontal line.
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="HLineToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaX">The length of the horizontal line.</param>
|
||||
public HLineToCommand(int deltaX)
|
||||
{
|
||||
DeltaX = deltaX;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{DeltaX} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Relative move to for horizontal dimension only.
|
||||
/// </summary>
|
||||
internal 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;
|
||||
|
||||
/// <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 override string ToString()
|
||||
{
|
||||
return $"{X} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
{
|
||||
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 int PostControlPointDeltaX { get; }
|
||||
|
||||
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)
|
||||
{
|
||||
PostControlPointDeltaX = postControlPointDeltaX;
|
||||
PreControlPointDeltaX = preControlPointDeltaX;
|
||||
PreControlPointDeltaY = preControlPointDeltaY;
|
||||
EndPointDeltaY = endPointDeltaY;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{PostControlPointDeltaX} {PreControlPointDeltaX} {PreControlPointDeltaY} {EndPointDeltaY} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Relative line-to command. Creates a line moving a distance relative to the current point.
|
||||
/// </summary>
|
||||
internal 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;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move horizontally.
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
|
||||
/// <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)
|
||||
{
|
||||
DeltaX = deltaX;
|
||||
DeltaY = deltaY;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{DeltaX} {DeltaY} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Relative move to command. starts a new subpath of the current path in the same manner as moveto.
|
||||
/// However, the number pair is interpreted as a displacement relative to the current point (x, y) rather than as an absolute coordinate.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// moveto: moveto sets the current point in the graphics state to the user space coordinate (x, y) without adding any line segments to the current path.
|
||||
/// 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
|
||||
{
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move horizontally.
|
||||
/// </summary>
|
||||
public int DeltaX { get; }
|
||||
|
||||
/// <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)
|
||||
{
|
||||
DeltaX = deltaX;
|
||||
DeltaY = deltaY;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{DeltaX} {DeltaY} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Relative rcurveto. Whereas the arguments to the rcurveto operator in the PostScript language are all relative to the current
|
||||
/// 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
|
||||
{
|
||||
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 int DeltaX1 { get; }
|
||||
|
||||
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)
|
||||
{
|
||||
DeltaX1 = deltaX1;
|
||||
DeltaY1 = deltaY1;
|
||||
DeltaX2 = deltaX2;
|
||||
DeltaY2 = deltaY2;
|
||||
DeltaX3 = deltaX3;
|
||||
DeltaY3 = deltaY3;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Vertical-line to command.
|
||||
/// </summary>
|
||||
internal 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;
|
||||
|
||||
/// <summary>
|
||||
/// The length of the vertical line.
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="VLineToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaY">The length of the vertical line.</param>
|
||||
public VLineToCommand(int deltaY)
|
||||
{
|
||||
DeltaY = deltaY;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{DeltaY} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Vertical move to. Moves relative to the current point.
|
||||
/// </summary>
|
||||
internal 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;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to move vertically.
|
||||
/// </summary>
|
||||
public int DeltaY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="VMoveToCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaY">The distance to move vertically.</param>
|
||||
public VMoveToCommand(int deltaY)
|
||||
{
|
||||
DeltaY = deltaY;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{DeltaY} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Vertical-horizontal curveto.
|
||||
/// 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
|
||||
{
|
||||
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 int PostControlPointDeltaY { get; }
|
||||
|
||||
public int PreControlPointDeltaX { get; }
|
||||
|
||||
public int PreControlPointDeltaY { get; }
|
||||
|
||||
public int EndPointDeltaX { get; }
|
||||
|
||||
public VhCurveToCommand(int postControlPointDeltaY, int preControlPointDeltaX, int preControlPointDeltaY, int endPointDeltaX)
|
||||
{
|
||||
PostControlPointDeltaY = postControlPointDeltaY;
|
||||
PreControlPointDeltaX = preControlPointDeltaX;
|
||||
PreControlPointDeltaY = preControlPointDeltaY;
|
||||
EndPointDeltaX = endPointDeltaX;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.StartFinishOutline
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
{
|
||||
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 EndCharCommand Instance { get; } = new EndCharCommand();
|
||||
|
||||
private EndCharCommand()
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.StartFinishOutline
|
||||
{
|
||||
/// <summary>
|
||||
/// The name hsbw stands for horizontal sidebearing and width;
|
||||
/// horizontal indicates that the y component of both the sidebearing and width is 0.
|
||||
/// 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
|
||||
{
|
||||
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 int LeftSidebearingPointX { get; }
|
||||
|
||||
public int CharacterWidthVectorX { get; }
|
||||
|
||||
public HsbwCommand(int leftSidebearingPointX, int characterWidthVectorX)
|
||||
{
|
||||
LeftSidebearingPointX = leftSidebearingPointX;
|
||||
CharacterWidthVectorX = characterWidthVectorX;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.StartFinishOutline
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets left sidebearing and the character width vector.
|
||||
/// This command also sets the current point to(sbx, sby), but does not place the point in the character path.
|
||||
/// </summary>
|
||||
internal class SbwCommand
|
||||
{
|
||||
public const string Name = "sbw";
|
||||
|
||||
public static readonly byte First = 12;
|
||||
public static readonly byte? Second = 7;
|
||||
|
||||
public bool TakeFromStackBottom { get; } = true;
|
||||
public bool ClearsOperandStack { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The X value of the left sidebearing point.
|
||||
/// </summary>
|
||||
public int LeftSidebearingX { get; set; }
|
||||
|
||||
/// <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)
|
||||
{
|
||||
LeftSidebearingX = leftSidebearingX;
|
||||
LeftSidebearingY = leftSidebearingY;
|
||||
CharacterWidthX = characterWidthX;
|
||||
CharacterWidthY = characterWidthY;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{LeftSidebearingX} {LeftSidebearingY} {CharacterWidthX} {CharacterWidthY} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.StartFinishOutline
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard encoding accented character.
|
||||
/// Makes an accented character from two other characters in the font program.
|
||||
/// </summary>
|
||||
internal 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;
|
||||
|
||||
/// <summary>
|
||||
/// The x component of the left sidebearing of the accent.
|
||||
/// </summary>
|
||||
public int AccentLeftSidebearingX { get; set; }
|
||||
|
||||
/// <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)
|
||||
{
|
||||
AccentLeftSidebearingX = accentLeftSidebearingX;
|
||||
AccentOriginX = accentOriginX;
|
||||
AccentOriginY = accentOriginY;
|
||||
BaseCharacterCode = baseCharacterCode;
|
||||
AccentCharacterCode = accentCharacterCode;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{AccentLeftSidebearingX} {AccentOriginX} {AccentOriginY} {BaseCharacterCode} {AccentCharacterCode} {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings
|
||||
{
|
||||
class Type1CharStringCommandFactory
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings
|
||||
{
|
||||
class Type1CharStringParser
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.Parser
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
internal class Type1CharstringDecryptedBytes
|
||||
{
|
||||
public IReadOnlyList<byte> Bytes { get; }
|
||||
|
||||
public int Index { get; }
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public SourceType Source { get; }
|
||||
|
||||
public Type1CharstringDecryptedBytes(IReadOnlyList<byte> bytes, int index)
|
||||
{
|
||||
Bytes = bytes ?? throw new ArgumentNullException(nameof(bytes));
|
||||
Index = index;
|
||||
Name = ".notdef";
|
||||
Source = SourceType.Subroutine;
|
||||
}
|
||||
|
||||
public Type1CharstringDecryptedBytes(string name, IReadOnlyList<byte> bytes, int index)
|
||||
{
|
||||
Bytes = bytes ?? throw new ArgumentNullException(nameof(bytes));
|
||||
Index = index;
|
||||
Name = name;
|
||||
Source = SourceType.Charstring;
|
||||
}
|
||||
|
||||
public enum SourceType
|
||||
{
|
||||
Subroutine,
|
||||
Charstring
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,6 +14,7 @@
|
||||
private const int EexecRandomBytes = 4;
|
||||
private const int Len4Bytes = 4;
|
||||
private const int Password = 5839;
|
||||
private const int CharstringEncryptionKey = 4330;
|
||||
|
||||
public IReadOnlyList<byte> Parse(IReadOnlyList<byte> bytes, bool isLenientParsing)
|
||||
{
|
||||
@@ -137,11 +138,13 @@
|
||||
case Type1Symbols.BlueScale:
|
||||
{
|
||||
builder.BlueScale = ReadNumeric(tokenizer);
|
||||
ReadTillDef(tokenizer);
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.ForceBold:
|
||||
{
|
||||
builder.ForceBold = ReadBoolean(tokenizer);
|
||||
ReadTillDef(tokenizer);
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.MinFeature:
|
||||
@@ -160,6 +163,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
builder.MinFeature = new Type1PrivateDictionary.MinFeature(procedureTokens[0].AsInt(), procedureTokens[1].AsInt());
|
||||
|
||||
ReadTillDef(tokenizer);
|
||||
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.Password:
|
||||
@@ -171,12 +178,16 @@
|
||||
}
|
||||
|
||||
builder.Password = password;
|
||||
|
||||
ReadTillDef(tokenizer);
|
||||
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.UniqueId:
|
||||
{
|
||||
var id = (int)ReadNumeric(tokenizer);
|
||||
builder.UniqueId = id;
|
||||
ReadTillDef(tokenizer);
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.Len4:
|
||||
@@ -187,11 +198,13 @@
|
||||
case Type1Symbols.BlueShift:
|
||||
{
|
||||
builder.BlueShift = (int)ReadNumeric(tokenizer);
|
||||
ReadTillDef(tokenizer);
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.BlueFuzz:
|
||||
{
|
||||
builder.BlueFuzz = (int)ReadNumeric(tokenizer);
|
||||
ReadTillDef(tokenizer);
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.FamilyBlues:
|
||||
@@ -207,32 +220,38 @@
|
||||
case Type1Symbols.LanguageGroup:
|
||||
{
|
||||
builder.LanguageGroup = (int)ReadNumeric(tokenizer);
|
||||
ReadTillDef(tokenizer);
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.RndStemUp:
|
||||
{
|
||||
builder.RoundStemUp = ReadBoolean(tokenizer);
|
||||
ReadTillDef(tokenizer);
|
||||
break;
|
||||
}
|
||||
case Type1Symbols.Subroutines:
|
||||
{
|
||||
//readSubrs(lenIV);
|
||||
builder.Subroutines = ReadSubroutines(tokenizer, lenIv, isLenientParsing);
|
||||
break;
|
||||
|
||||
}
|
||||
case Type1Symbols.OtherSubroutines:
|
||||
{
|
||||
ReadOtherSubroutines(tokenizer, isLenientParsing);
|
||||
ReadTillDef(tokenizer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (tokenizer.CurrentToken != null)
|
||||
var currentToken = tokenizer.CurrentToken;
|
||||
while (currentToken.Type != Type1Token.TokenType.Literal
|
||||
&& !string.Equals(currentToken.Text, "CharStrings", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
tokenizer.GetNext();
|
||||
currentToken = tokenizer.GetNext();
|
||||
}
|
||||
|
||||
var charStrings = ReadCharStrings(tokenizer, lenIv, isLenientParsing);
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
@@ -242,8 +261,6 @@
|
||||
/// The first byte must not be whitespace.
|
||||
/// One of the first four ciphertext bytes must not be an ASCII hex character.
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsBinary(IReadOnlyList<byte> bytes)
|
||||
{
|
||||
if (bytes.Count < 4)
|
||||
@@ -428,6 +445,11 @@
|
||||
{
|
||||
if (token.Type == Type1Token.TokenType.Name)
|
||||
{
|
||||
if (string.Equals(token.Text, "ND", StringComparison.OrdinalIgnoreCase) || string.Equals(token.Text, "|-", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(token.Text, "def", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
break;
|
||||
@@ -459,7 +481,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<T> ReadArrayValues<T>(Type1Tokenizer tokenizer, Func<Type1Token, T> converter, bool hasReadStart = false)
|
||||
private static IReadOnlyList<T> ReadArrayValues<T>(Type1Tokenizer tokenizer, Func<Type1Token, T> converter, bool hasReadStart = false,
|
||||
bool includeDef = true)
|
||||
{
|
||||
if (!hasReadStart)
|
||||
{
|
||||
@@ -488,6 +511,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (includeDef)
|
||||
{
|
||||
ReadTillDef(tokenizer);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -522,7 +550,7 @@
|
||||
|
||||
if (start.Type == Type1Token.TokenType.StartArray)
|
||||
{
|
||||
ReadArrayValues(tokenizer, x => x, true);
|
||||
ReadArrayValues(tokenizer, x => x, true, false);
|
||||
}
|
||||
else if (start.Type == Type1Token.TokenType.Integer || start.Type == Type1Token.TokenType.Real)
|
||||
{
|
||||
@@ -542,5 +570,99 @@
|
||||
throw new InvalidOperationException($"Failed to read start of /OtherSubrs array. Got start token: {start}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<Type1CharstringDecryptedBytes> ReadSubroutines(Type1Tokenizer tokenizer, int lenIv, bool isLenientParsing)
|
||||
{
|
||||
var length = (int)ReadNumeric(tokenizer);
|
||||
var subroutines = new List<Type1CharstringDecryptedBytes>(length);
|
||||
|
||||
ReadExpected(tokenizer, Type1Token.TokenType.Name, "array");
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var current = tokenizer.GetNext();
|
||||
if (current.Type != Type1Token.TokenType.Name || !string.Equals(current.Text, "dup"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var index = (int)ReadNumeric(tokenizer);
|
||||
|
||||
var byteLength = (int)ReadNumeric(tokenizer);
|
||||
|
||||
var charstring = tokenizer.GetNext();
|
||||
|
||||
if (!(charstring is Type1DataToken charstringToken))
|
||||
{
|
||||
throw new InvalidOperationException($"Found an unexpected token instead of subroutine charstring: {charstring}.");
|
||||
}
|
||||
|
||||
if (!isLenientParsing && charstringToken.Data.Count != byteLength)
|
||||
{
|
||||
throw new InvalidOperationException($"The subroutine charstring {charstringToken} did not have the expected length of {byteLength}.");
|
||||
}
|
||||
|
||||
var subroutine = Decrypt(charstringToken.Data, CharstringEncryptionKey, lenIv);
|
||||
subroutines.Add(new Type1CharstringDecryptedBytes(subroutine, index));
|
||||
ReadTillPut(tokenizer);
|
||||
}
|
||||
|
||||
ReadTillDef(tokenizer);
|
||||
|
||||
return subroutines;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<Type1CharstringDecryptedBytes> ReadCharStrings(Type1Tokenizer tokenizer, int lenIv, bool isLenientParsing)
|
||||
{
|
||||
var length = (int) ReadNumeric(tokenizer);
|
||||
|
||||
ReadExpected(tokenizer, Type1Token.TokenType.Name, "dict");
|
||||
|
||||
// dup begin or CharStrings begin.
|
||||
ReadExpected(tokenizer, Type1Token.TokenType.Name);
|
||||
ReadExpected(tokenizer, Type1Token.TokenType.Name, "begin");
|
||||
|
||||
var results = new List<Type1CharstringDecryptedBytes>();
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var token = tokenizer.GetNext();
|
||||
|
||||
if (token.Type == Type1Token.TokenType.Name &&
|
||||
string.Equals(token.Text, "end", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (token.Type != Type1Token.TokenType.Literal)
|
||||
{
|
||||
throw new InvalidOperationException($"Type 1 font error. Expected literal for charstring name, instead got: {token}.");
|
||||
}
|
||||
|
||||
var name = token.Text;
|
||||
|
||||
var charstringLength = ReadNumeric(tokenizer);
|
||||
var charstring = tokenizer.GetNext();
|
||||
if (!(charstring is Type1DataToken charstringToken))
|
||||
{
|
||||
throw new InvalidOperationException($"Got wrong type of token, expected charstring, instead got: {charstring}.");
|
||||
}
|
||||
|
||||
if (!isLenientParsing && charstringToken.Data.Count != charstringLength)
|
||||
{
|
||||
throw new InvalidOperationException($"The charstring {charstringToken} did not have the expected length of {charstringLength}.");
|
||||
}
|
||||
|
||||
var data = Decrypt(charstringToken.Data, CharstringEncryptionKey, lenIv);
|
||||
|
||||
results.Add(new Type1CharstringDecryptedBytes(name, data, i));
|
||||
|
||||
ReadTillDef(tokenizer);
|
||||
}
|
||||
|
||||
ReadExpected(tokenizer, Type1Token.TokenType.Name, "end");
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -376,6 +376,8 @@
|
||||
{
|
||||
// Skip preceding space.
|
||||
bytes.MoveNext();
|
||||
// TODO: may be wrong
|
||||
bytes.MoveNext();
|
||||
|
||||
byte[] data = new byte[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Parser;
|
||||
|
||||
internal class Type1PrivateDictionary
|
||||
{
|
||||
@@ -12,7 +13,7 @@
|
||||
|
||||
public object NoAccessDef { get; set; }
|
||||
|
||||
public object[] Subroutines { get; set; }
|
||||
public IReadOnlyList<Type1CharstringDecryptedBytes> Subroutines { get; set; }
|
||||
|
||||
public object[] OtherSubroutines { get; set; }
|
||||
|
||||
@@ -48,9 +49,22 @@
|
||||
|
||||
public int LenIv { get; set; }
|
||||
|
||||
public object[] MinFeature { get; set; }
|
||||
public MinFeature MinFeature { get; set; }
|
||||
|
||||
public bool RoundStemUp { get; set; }
|
||||
}
|
||||
|
||||
public class MinFeature
|
||||
{
|
||||
public int First { get; }
|
||||
|
||||
public int Second { get; }
|
||||
|
||||
public MinFeature(int first, int second)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,5 +36,9 @@
|
||||
<EmbeddedResource Include="Resources\CMap\*" />
|
||||
<EmbeddedResource Include="Resources\GlyphList\*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Fonts\Type1\CharStrings\Commands\Subroutine\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Reference in New Issue
Block a user