Merge pull request #2 from UglyToad/master

Updating
This commit is contained in:
Michael Plaisted
2021-01-20 09:15:27 -06:00
committed by GitHub
14 changed files with 117 additions and 41 deletions

View File

@@ -3,6 +3,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using UglyToad.PdfPig.Core;
using UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction;
/// <summary> /// <summary>
/// Call other subroutine command. Arguments are pushed onto the PostScript interpreter operand stack then /// Call other subroutine command. Arguments are pushed onto the PostScript interpreter operand stack then
@@ -23,13 +25,13 @@
public static bool TakeFromStackBottom { get; } = false; public static bool TakeFromStackBottom { get; } = false;
public static bool ClearsOperandStack { get; } = false; public static bool ClearsOperandStack { get; } = false;
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run); public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
public static void Run(Type1BuildCharContext context) public static void Run(Type1BuildCharContext context)
{ {
var index = (int) context.Stack.PopTop(); var index = (int)context.Stack.PopTop();
// What it should do // What it should do
var numberOfArguments = (int)context.Stack.PopTop(); var numberOfArguments = (int)context.Stack.PopTop();
var otherSubroutineArguments = new List<double>(numberOfArguments); var otherSubroutineArguments = new List<double>(numberOfArguments);
@@ -42,17 +44,44 @@
{ {
// Other subrs 0-2 implement flex // Other subrs 0-2 implement flex
case FlexEnd: case FlexEnd:
{ {
context.IsFlexing = false; // https://github.com/apache/pdfbox/blob/2c23d8b4e3ad61852f0b6ee2b95b907eefba1fcf/fontbox/src/main/java/org/apache/fontbox/cff/Type1CharString.java#L339
// TODO: I don't really care about flexpoints, but we should probably handle them... one day. context.IsFlexing = false;
//if (context.FlexPoints.Count < 7) if (context.FlexPoints.Count < 7)
//{ {
// throw new NotSupportedException("There must be at least 7 flex points defined by an other subroutine."); throw new NotSupportedException("There must be at least 7 flex points defined by an other subroutine.");
//} }
context.ClearFlexPoints(); // reference point is relative to start point
break; PdfPoint reference = context.FlexPoints[0];
} reference = reference.Translate(context.CurrentPosition.X, context.CurrentPosition.Y);
// first point is relative to reference point
PdfPoint first = context.FlexPoints[1];
first = first.Translate(reference.X, reference.Y);
// make the first point relative to the start point
first = first.Translate(-context.CurrentPosition.X, -context.CurrentPosition.Y);
context.Stack.Push(first.X);
context.Stack.Push(first.Y);
context.Stack.Push(context.FlexPoints[2].X);
context.Stack.Push(context.FlexPoints[2].Y);
context.Stack.Push(context.FlexPoints[3].X);
context.Stack.Push(context.FlexPoints[3].Y);
RelativeRCurveToCommand.Run(context);
context.Stack.Push(context.FlexPoints[4].X);
context.Stack.Push(context.FlexPoints[4].Y);
context.Stack.Push(context.FlexPoints[5].X);
context.Stack.Push(context.FlexPoints[5].Y);
context.Stack.Push(context.FlexPoints[6].X);
context.Stack.Push(context.FlexPoints[6].Y);
RelativeRCurveToCommand.Run(context);
context.ClearFlexPoints();
break;
}
case FlexBegin: case FlexBegin:
Debug.Assert(otherSubroutineArguments.Count == 0, "Flex begin should have no arguments."); Debug.Assert(otherSubroutineArguments.Count == 0, "Flex begin should have no arguments.");

View File

@@ -4,6 +4,8 @@
/// <summary> /// <summary>
/// Sets the current point to (x, y) in absolute character space coordinates without performing a charstring moveto command. /// Sets the current point to (x, y) in absolute character space coordinates without performing a charstring moveto command.
/// <para>This establishes the current point for a subsequent relative path building command.
/// The 'setcurrentpoint' command is used only in conjunction with results from 'OtherSubrs' procedures.</para>
/// </summary> /// </summary>
internal static class SetCurrentPointCommand internal static class SetCurrentPointCommand
{ {
@@ -22,8 +24,8 @@
var x = context.Stack.PopBottom(); var x = context.Stack.PopBottom();
var y = context.Stack.PopBottom(); var y = context.Stack.PopBottom();
context.CurrentPosition = new PdfPoint(x, y); //context.CurrentPosition = new PdfPoint(x, y);
// TODO: need to investigate why odd behavior when the current point is actualy set.
context.Stack.Clear(); context.Stack.Clear();
} }
} }

View File

@@ -19,19 +19,19 @@
public static void Run(Type1BuildCharContext context) public static void Run(Type1BuildCharContext context)
{ {
var x = context.Stack.PopBottom(); var deltaX = context.Stack.PopBottom();
var actualX = context.CurrentPosition.X + x;
var y = context.CurrentPosition.Y;
if (context.IsFlexing) if (context.IsFlexing)
{ {
// TODO: flex support // not in the Type 1 spec, but exists in some fonts
context.AddFlexPoint(new PdfPoint(deltaX, 0));
} }
else else
{ {
context.CurrentPosition = new PdfPoint(actualX, y); var x = context.CurrentPosition.X + deltaX;
context.Path.MoveTo(actualX, y); var y = context.CurrentPosition.Y;
context.CurrentPosition = new PdfPoint(x, y);
context.Path.MoveTo(x, y);
} }
context.Stack.Clear(); context.Stack.Clear();

View File

@@ -30,7 +30,7 @@
if (context.IsFlexing) if (context.IsFlexing)
{ {
context.AddFlexPoint(new PdfPoint(deltaX, deltaY));
} }
else else
{ {

View File

@@ -1,6 +1,7 @@
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
{ {
using Core; using Core;
using System;
/// <summary> /// <summary>
/// Vertical move to. Moves relative to the current point. /// Vertical move to. Moves relative to the current point.
@@ -23,7 +24,8 @@
if (context.IsFlexing) if (context.IsFlexing)
{ {
// TODO: flex commands // not in the Type 1 spec, but exists in some fonts
context.AddFlexPoint(new PdfPoint(0, deltaY));
} }
else else
{ {

View File

@@ -6,7 +6,7 @@
/// Sets left sidebearing and the character width vector. /// 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. /// This command also sets the current point to(sbx, sby), but does not place the point in the character path.
/// </summary> /// </summary>
internal class SbwCommand internal static class SbwCommand
{ {
public const string Name = "sbw"; public const string Name = "sbw";

View File

@@ -28,7 +28,7 @@
public CharStringStack PostscriptStack { get; } = new CharStringStack(); public CharStringStack PostscriptStack { get; } = new CharStringStack();
public IReadOnlyList<PdfPoint> FlexPoints { get; } public List<PdfPoint> FlexPoints { get; } = new List<PdfPoint>();
public Type1BuildCharContext(IReadOnlyDictionary<int, Type1CharStrings.CommandSequence> subroutines, public Type1BuildCharContext(IReadOnlyDictionary<int, Type1CharStrings.CommandSequence> subroutines,
Func<int, PdfSubpath> characterByIndexFactory, Func<int, PdfSubpath> characterByIndexFactory,
@@ -41,7 +41,7 @@
public void AddFlexPoint(PdfPoint point) public void AddFlexPoint(PdfPoint point)
{ {
FlexPoints.Add(point);
} }
public PdfSubpath GetCharacter(int characterCode) public PdfSubpath GetCharacter(int characterCode)
@@ -61,7 +61,7 @@
public void ClearFlexPoints() public void ClearFlexPoints()
{ {
FlexPoints.Clear();
} }
} }
} }

View File

@@ -0,0 +1,39 @@
using System;
using System.IO;
using UglyToad.PdfPig.Core;
using Xunit;
namespace UglyToad.PdfPig.Tests.Fonts.Type1
{
public class Type1CharStringParserTests
{
[Fact]
public void CorrectBoundingBoxesFlexPoints()
{
PointComparer pointComparer = new PointComparer(new DoubleComparer(3));
var documentFolder = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "Integration", "Documents"));
var filePath = Path.Combine(documentFolder, "data.pdf");
using (var doc = PdfDocument.Open(filePath))
{
var page = doc.GetPage(1);
var letters = page.Letters;
// check 'm'
var m = letters[0];
Assert.Equal("m", m.Value);
Assert.Equal(new PdfPoint(253.4458, 658.431), m.GlyphRectangle.BottomLeft, pointComparer);
Assert.Equal(new PdfPoint(261.22659, 662.83446), m.GlyphRectangle.TopRight, pointComparer);
// check 'p'
var p = letters[1];
Assert.Equal("p", p.Value);
Assert.Equal(new PdfPoint(261.70778, 656.49825), p.GlyphRectangle.BottomLeft, pointComparer);
Assert.Equal(new PdfPoint(266.6193, 662.83446), p.GlyphRectangle.TopRight, pointComparer);
}
}
}
}

View File

@@ -8,7 +8,7 @@
internal class NumericTokenizer : ITokenizer internal class NumericTokenizer : ITokenizer
{ {
private static readonly StringBuilderPool StringBuilderPool = new StringBuilderPool(10); private readonly StringBuilder stringBuilder = new StringBuilder();
private const byte Zero = 48; private const byte Zero = 48;
private const byte Nine = 57; private const byte Nine = 57;
@@ -23,7 +23,7 @@
if ((currentByte >= Zero && currentByte <= Nine) || currentByte == '-' || currentByte == '+' || currentByte == '.') if ((currentByte >= Zero && currentByte <= Nine) || currentByte == '-' || currentByte == '+' || currentByte == '.')
{ {
characters = StringBuilderPool.Borrow(); characters = stringBuilder;
characters.Append((char)currentByte); characters.Append((char)currentByte);
} }
else else
@@ -53,7 +53,7 @@
try try
{ {
var str = characters.ToString(); var str = characters.ToString();
StringBuilderPool.Return(characters); characters.Clear();
switch (str) switch (str)
{ {

View File

@@ -1,11 +1,12 @@
namespace UglyToad.PdfPig.Tokenization namespace UglyToad.PdfPig.Tokenization
{ {
using Core; using Core;
using System.Text;
using Tokens; using Tokens;
internal class PlainTokenizer : ITokenizer internal class PlainTokenizer : ITokenizer
{ {
private static readonly StringBuilderPool StringBuilderPool = new StringBuilderPool(10); private readonly StringBuilder stringBuilder = new StringBuilder();
public bool ReadsNextByte { get; } = true; public bool ReadsNextByte { get; } = true;
@@ -18,7 +19,7 @@
return false; return false;
} }
var builder = StringBuilderPool.Borrow(); var builder = stringBuilder;
builder.Append((char)currentByte); builder.Append((char)currentByte);
while (inputBytes.MoveNext()) while (inputBytes.MoveNext())
{ {
@@ -39,7 +40,7 @@
} }
var text = builder.ToString(); var text = builder.ToString();
StringBuilderPool.Return(builder); builder.Clear();
switch (text) switch (text)
{ {

View File

@@ -15,9 +15,12 @@
private static readonly DictionaryTokenizer DictionaryTokenizer = new DictionaryTokenizer(); private static readonly DictionaryTokenizer DictionaryTokenizer = new DictionaryTokenizer();
private static readonly HexTokenizer HexTokenizer = new HexTokenizer(); private static readonly HexTokenizer HexTokenizer = new HexTokenizer();
private static readonly NameTokenizer NameTokenizer = new NameTokenizer(); private static readonly NameTokenizer NameTokenizer = new NameTokenizer();
private static readonly NumericTokenizer NumericTokenizer = new NumericTokenizer();
private static readonly PlainTokenizer PlainTokenizer = new PlainTokenizer(); // NOTE: these are not thread safe so should not be static. Each instance includes a
private static readonly StringTokenizer StringTokenizer = new StringTokenizer(); // StringBuilder it re-uses.
private readonly PlainTokenizer PlainTokenizer = new PlainTokenizer();
private readonly NumericTokenizer NumericTokenizer = new NumericTokenizer();
private readonly StringTokenizer StringTokenizer = new StringTokenizer();
private readonly ScannerScope scope; private readonly ScannerScope scope;
private readonly IInputBytes inputBytes; private readonly IInputBytes inputBytes;

View File

@@ -6,7 +6,7 @@
internal class StringTokenizer : ITokenizer internal class StringTokenizer : ITokenizer
{ {
private static readonly StringBuilderPool StringBuilderPool = new StringBuilderPool(16); private readonly StringBuilder stringBuilder = new StringBuilder();
public bool ReadsNextByte { get; } = false; public bool ReadsNextByte { get; } = false;
public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken token) public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken token)
@@ -23,7 +23,7 @@
return false; return false;
} }
var builder = StringBuilderPool.Borrow(); var builder = stringBuilder;
var numberOfBrackets = 1; var numberOfBrackets = 1;
var isEscapeActive = false; var isEscapeActive = false;
var isLineBreaking = false; var isLineBreaking = false;
@@ -178,7 +178,7 @@
encodedWith = StringToken.Encoding.Iso88591; encodedWith = StringToken.Encoding.Iso88591;
} }
StringBuilderPool.Return(builder); builder.Clear();
token = new StringToken(tokenStr, encodedWith); token = new StringToken(tokenStr, encodedWith);

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks> <TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>