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 d99ec410..e73ac8e6 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/DotSectionCommand.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/DotSectionCommand.cs
@@ -1,6 +1,27 @@
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
{
+ ///
+ /// Brackets an outline section for the dots in letters such as "i", "j" and "!".
+ ///
internal 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 DotSectionCommand Instance { get; } = new DotSectionCommand();
+
+ private DotSectionCommand()
+ {
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
}
}
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 c1d989aa..49151f8d 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStem3Command.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStem3Command.cs
@@ -1,6 +1,50 @@
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
{
+ ///
+ /// Declares the vertical ranges of three horizontal stem zones:
+ /// 1st: between y0 and y0 + dy0
+ /// 2nd: between y1 and y1 + dy1
+ /// 3rd: between y2 and y2 + dy2
+ /// Where y0, y1 and y2 are all relative to the y coordinate of the left sidebearing point.
+ ///
+ ///
+ /// Suited to letters with 3 horizontal stems like 'E'.
+ ///
internal 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 int Y0 { get; }
+
+ 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)
+ {
+ Y0 = y0;
+ DeltaY0 = deltaY0;
+ Y1 = y1;
+ DeltaY1 = deltaY1;
+ Y2 = y2;
+ DeltaY2 = deltaY2;
+ }
+
+ public override string ToString()
+ {
+ return $"{Y0} {DeltaY0} {Y1} {DeltaY1} {Y2} {DeltaY2} {Name}";
+ }
}
}
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 b067f838..8d2534e2 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStemCommand.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/HStemCommand.cs
@@ -1,6 +1,43 @@
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
{
+ ///
+ /// 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
{
+ 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;
+
+ ///
+ /// The first Y coordinate for the stem zone, relative to the current left sidebearing Y point.
+ ///
+ public int Y { get; }
+
+ ///
+ /// 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)
+ {
+ Y = y;
+ DeltaY = deltaY;
+ }
+
+ public override string ToString()
+ {
+ return $"{Y} {DeltaY} {Name}";
+ }
}
}
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 5f7314c7..6f72200f 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStem3Command.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStem3Command.cs
@@ -1,6 +1,50 @@
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
{
+ ///
+ /// Declares the horizontal ranges of three vertical stem zones.
+ /// 1st: between x0 and x0 + dx0
+ /// 2nd: between x1 and x1 + dx1
+ /// 3rd: between x2 and x2 + dx2
+ /// Where x0, x1 and x2 are all relative to the x coordinate of the left sidebearing point.
+ ///
+ ///
+ /// Suited to letters with 3 vertical stems, for instance 'm'.
+ ///
internal class VStem3Command
{
+ public const string Name = "vstem3";
+
+ public static readonly byte First = 12;
+ public static readonly byte? Second = 1;
+
+ public bool TakeFromStackBottom { get; } = true;
+ public bool ClearsOperandStack { get; } = true;
+
+ public int X0 { get; }
+
+ 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)
+ {
+ X0 = x0;
+ DeltaX0 = deltaX0;
+ X1 = x1;
+ DeltaX1 = deltaX1;
+ X2 = x2;
+ DeltaX2 = deltaX2;
+ }
+
+ public override string ToString()
+ {
+ return $"{X0} {DeltaX0} {X1} {DeltaX1} {X2} {DeltaX2} {Name}";
+ }
}
}
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 ce119b5e..18503d27 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStemCommand.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Commands/Hint/VStemCommand.cs
@@ -1,6 +1,38 @@
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.Hint
{
+ ///
+ /// 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
{
+ 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;
+
+ ///
+ /// 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)
+ {
+ X = x;
+ DeltaX = deltaX;
+ }
+
+ public override string ToString()
+ {
+ return $"{X} {DeltaX} {Name}";
+ }
}
}
diff --git a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringCommandFactory.cs b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringCommandFactory.cs
index 9b24f873..5bf6d40f 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringCommandFactory.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringCommandFactory.cs
@@ -1,10 +1,30 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace UglyToad.PdfPig.Fonts.Type1.CharStrings
+namespace UglyToad.PdfPig.Fonts.Type1.CharStrings
{
- class Type1CharStringCommandFactory
+ 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 d59e791f..5582c09f 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringParser.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/CharStrings/Type1CharStringParser.cs
@@ -1,10 +1,60 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace UglyToad.PdfPig.Fonts.Type1.CharStrings
+namespace UglyToad.PdfPig.Fonts.Type1.CharStrings
{
- class Type1CharStringParser
+ using System.Collections.Generic;
+ using Parser;
+
+ internal class Type1CharStringParser
{
+ public static void Parse(IReadOnlyList charStrings, IReadOnlyList subroutines)
+ {
+ foreach (var charString in charStrings)
+ {
+ ParseSingle(charString.Bytes);
+ }
+ }
+
+ private static void ParseSingle(IReadOnlyList charStringBytes)
+ {
+ var numberStack = 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);
+ }
+ else
+ {
+ var val = InterpretNumber(b, charStringBytes, ref i);
+
+ numberStack.Add(val);
+ }
+ }
+ }
+
+ private static int InterpretNumber(byte b, IReadOnlyList bytes, ref int i)
+ {
+ if (b >= 32 && b <= 246)
+ {
+ return b - 139;
+ }
+
+ if (b >= 247 && b <= 250)
+ {
+ var w = bytes[++i];
+ return ((b - 247) * 256) + w + 108;
+ }
+
+ if (b >= 251 && b <= 254)
+ {
+ var w = bytes[++i];
+ return -((b - 251) * 256) - w - 108;
+ }
+
+ var result = bytes[++i] << 24 + bytes[++i] << 16 + bytes[++i] << 8 + bytes[++i];
+
+ return result;
+ }
}
}
diff --git a/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1EncryptedPortionParser.cs b/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1EncryptedPortionParser.cs
index 1edab9eb..73f2e87e 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1EncryptedPortionParser.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1EncryptedPortionParser.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+ using CharStrings;
using IO;
using PdfPig.Parser.Parts;
using Tokenization.Tokens;
@@ -69,8 +70,9 @@
for (var i = 0; i < length; i++)
{
var token = tokenizer.GetNext();
+
// premature end
- if (token.Type != Type1Token.TokenType.Literal)
+ if (token == null || token.Type != Type1Token.TokenType.Literal)
{
break;
}
@@ -80,18 +82,21 @@
switch (key)
{
case Type1Symbols.RdProcedure:
+ case Type1Symbols.RdProcedureAlt:
{
var procedureTokens = ReadProcedure(tokenizer);
ReadTillDef(tokenizer);
break;
}
case Type1Symbols.NoAccessDef:
+ case Type1Symbols.NoAccessDefAlt:
{
var procedureTokens = ReadProcedure(tokenizer);
ReadTillDef(tokenizer);
break;
}
case Type1Symbols.NoAccessPut:
+ case Type1Symbols.NoAccessPutAlt:
{
var procedureTokens = ReadProcedure(tokenizer);
ReadTillDef(tokenizer);
@@ -193,6 +198,7 @@
case Type1Symbols.Len4:
{
lenIv = (int)ReadNumeric(tokenizer);
+ ReadTillDef(tokenizer);
break;
}
case Type1Symbols.BlueShift:
@@ -240,17 +246,51 @@
ReadTillDef(tokenizer);
break;
}
+ case Type1Symbols.ExpansionFactor:
+ {
+ builder.ExpansionFactor = ReadNumeric(tokenizer);
+ ReadTillDef(tokenizer);
+ break;
+ }
+ case Type1Symbols.Erode:
+ {
+ ReadTillDef(tokenizer, true);
+ break;
+ }
+ default:
+ {
+ ReadTillDef(tokenizer, true);
+ break;
+ }
}
}
var currentToken = tokenizer.CurrentToken;
- while (currentToken.Type != Type1Token.TokenType.Literal
- && !string.Equals(currentToken.Text, "CharStrings", StringComparison.OrdinalIgnoreCase))
+
+ IReadOnlyList charStrings;
+ if (currentToken != null)
{
- currentToken = tokenizer.GetNext();
+ while (currentToken != null && currentToken.Type != Type1Token.TokenType.Literal
+ && !string.Equals(currentToken.Text, "CharStrings", StringComparison.OrdinalIgnoreCase))
+ {
+ currentToken = tokenizer.GetNext();
+ }
+
+ if (currentToken != null)
+ {
+ charStrings = ReadCharStrings(tokenizer, lenIv, isLenientParsing);
+ }
+ else
+ {
+ charStrings = new Type1CharstringDecryptedBytes[0];
+ }
+ }
+ else
+ {
+ charStrings = new Type1CharstringDecryptedBytes[0];
}
- var charStrings = ReadCharStrings(tokenizer, lenIv, isLenientParsing);
+ Type1CharStringParser.Parse(charStrings, builder.Subroutines ?? new Type1CharstringDecryptedBytes[0]);
return decrypted;
}
@@ -438,7 +478,7 @@
}
}
- private static void ReadTillDef(Type1Tokenizer tokenizer)
+ private static void ReadTillDef(Type1Tokenizer tokenizer, bool skip = false)
{
Type1Token token;
while ((token = tokenizer.GetNext()) != null)
@@ -455,7 +495,7 @@
break;
}
}
- else
+ else if (!skip)
{
throw new InvalidOperationException($"Encountered unexpected non-name token while reading till 'def' token: {token}");
}
@@ -614,7 +654,7 @@
private static IReadOnlyList ReadCharStrings(Type1Tokenizer tokenizer, int lenIv, bool isLenientParsing)
{
- var length = (int) ReadNumeric(tokenizer);
+ var length = (int)ReadNumeric(tokenizer);
ReadExpected(tokenizer, Type1Token.TokenType.Name, "dict");
diff --git a/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1Symbols.cs b/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1Symbols.cs
index e24b8f30..69b7e1df 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1Symbols.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1Symbols.cs
@@ -14,7 +14,9 @@
public const string Len4 = "lenIV";
public const string MinFeature = "MinFeature";
public const string NoAccessDef = "ND";
+ public const string NoAccessDefAlt = "|-";
public const string NoAccessPut = "NP";
+ public const string NoAccessPutAlt = "|";
public const string OtherBlues = "OtherBlues";
public const string OtherSubroutines = "OtherSubrs";
public const string Password = "password";
@@ -27,5 +29,9 @@
public const string StemSnapVerticalWidths = "StemSnapV";
public const string Subroutines = "Subrs";
public const string UniqueId = "UniqueID";
+ ///
+ /// This is shown briefly in the Type 1 spec but nowhere is it specified what it means or does.
+ ///
+ public const string Erode = "Erode";
}
}
diff --git a/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1Tokenizer.cs b/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1Tokenizer.cs
index 540a74e1..9c6db52b 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1Tokenizer.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/Parser/Type1Tokenizer.cs
@@ -115,7 +115,7 @@
throw new InvalidOperationException($"The binary portion of the type 1 font was invalid at position {bytes.CurrentOffset}.");
}
- if (name.Equals("RD") || name.Equals("-|"))
+ if (name.Equals(Type1Symbols.RdProcedure, StringComparison.OrdinalIgnoreCase) || name.Equals(Type1Symbols.RdProcedureAlt))
{
if (previousToken.Type == Type1Token.TokenType.Integer)
{
@@ -377,7 +377,7 @@
// Skip preceding space.
bytes.MoveNext();
// TODO: may be wrong
- bytes.MoveNext();
+ // bytes.MoveNext();
byte[] data = new byte[length];
for (int i = 0; i < length; i++)
diff --git a/src/UglyToad.PdfPig/Fonts/Type1/Type1PrivateDictionary.cs b/src/UglyToad.PdfPig/Fonts/Type1/Type1PrivateDictionary.cs
index 0dc0d3d1..459afbe0 100644
--- a/src/UglyToad.PdfPig/Fonts/Type1/Type1PrivateDictionary.cs
+++ b/src/UglyToad.PdfPig/Fonts/Type1/Type1PrivateDictionary.cs
@@ -52,6 +52,8 @@
public MinFeature MinFeature { get; set; }
public bool RoundStemUp { get; set; }
+
+ public decimal? ExpansionFactor { get; set; }
}
public class MinFeature