mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-11-28 09:28:25 +08:00
@@ -3,6 +3,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using UglyToad.PdfPig.Core;
|
||||
using UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction;
|
||||
|
||||
/// <summary>
|
||||
/// 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 ClearsOperandStack { get; } = false;
|
||||
|
||||
|
||||
public static LazyType1Command Lazy { get; } = new LazyType1Command(Name, Run);
|
||||
|
||||
public static void Run(Type1BuildCharContext context)
|
||||
{
|
||||
var index = (int) context.Stack.PopTop();
|
||||
|
||||
var index = (int)context.Stack.PopTop();
|
||||
|
||||
// What it should do
|
||||
var numberOfArguments = (int)context.Stack.PopTop();
|
||||
var otherSubroutineArguments = new List<double>(numberOfArguments);
|
||||
@@ -42,17 +44,44 @@
|
||||
{
|
||||
// Other subrs 0-2 implement flex
|
||||
case FlexEnd:
|
||||
{
|
||||
context.IsFlexing = false;
|
||||
// TODO: I don't really care about flexpoints, but we should probably handle them... one day.
|
||||
//if (context.FlexPoints.Count < 7)
|
||||
//{
|
||||
// throw new NotSupportedException("There must be at least 7 flex points defined by an other subroutine.");
|
||||
//}
|
||||
{
|
||||
// https://github.com/apache/pdfbox/blob/2c23d8b4e3ad61852f0b6ee2b95b907eefba1fcf/fontbox/src/main/java/org/apache/fontbox/cff/Type1CharString.java#L339
|
||||
context.IsFlexing = false;
|
||||
if (context.FlexPoints.Count < 7)
|
||||
{
|
||||
throw new NotSupportedException("There must be at least 7 flex points defined by an other subroutine.");
|
||||
}
|
||||
|
||||
context.ClearFlexPoints();
|
||||
break;
|
||||
}
|
||||
// reference point is relative to start point
|
||||
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:
|
||||
Debug.Assert(otherSubroutineArguments.Count == 0, "Flex begin should have no arguments.");
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
internal static class SetCurrentPointCommand
|
||||
{
|
||||
@@ -22,8 +24,8 @@
|
||||
var x = 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,19 +19,19 @@
|
||||
|
||||
public static void Run(Type1BuildCharContext context)
|
||||
{
|
||||
var x = context.Stack.PopBottom();
|
||||
|
||||
var actualX = context.CurrentPosition.X + x;
|
||||
var y = context.CurrentPosition.Y;
|
||||
var deltaX = context.Stack.PopBottom();
|
||||
|
||||
if (context.IsFlexing)
|
||||
{
|
||||
// TODO: flex support
|
||||
// not in the Type 1 spec, but exists in some fonts
|
||||
context.AddFlexPoint(new PdfPoint(deltaX, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentPosition = new PdfPoint(actualX, y);
|
||||
context.Path.MoveTo(actualX, y);
|
||||
var x = context.CurrentPosition.X + deltaX;
|
||||
var y = context.CurrentPosition.Y;
|
||||
context.CurrentPosition = new PdfPoint(x, y);
|
||||
context.Path.MoveTo(x, y);
|
||||
}
|
||||
|
||||
context.Stack.Clear();
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
if (context.IsFlexing)
|
||||
{
|
||||
|
||||
context.AddFlexPoint(new PdfPoint(deltaX, deltaY));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace UglyToad.PdfPig.Fonts.Type1.CharStrings.Commands.PathConstruction
|
||||
{
|
||||
using Core;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical move to. Moves relative to the current point.
|
||||
@@ -23,7 +24,8 @@
|
||||
|
||||
if (context.IsFlexing)
|
||||
{
|
||||
// TODO: flex commands
|
||||
// not in the Type 1 spec, but exists in some fonts
|
||||
context.AddFlexPoint(new PdfPoint(0, deltaY));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/// 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
|
||||
internal static class SbwCommand
|
||||
{
|
||||
public const string Name = "sbw";
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
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,
|
||||
Func<int, PdfSubpath> characterByIndexFactory,
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
public void AddFlexPoint(PdfPoint point)
|
||||
{
|
||||
|
||||
FlexPoints.Add(point);
|
||||
}
|
||||
|
||||
public PdfSubpath GetCharacter(int characterCode)
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
public void ClearFlexPoints()
|
||||
{
|
||||
|
||||
FlexPoints.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/data.pdf
Normal file
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/data.pdf
Normal file
Binary file not shown.
@@ -8,7 +8,7 @@
|
||||
|
||||
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 Nine = 57;
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
if ((currentByte >= Zero && currentByte <= Nine) || currentByte == '-' || currentByte == '+' || currentByte == '.')
|
||||
{
|
||||
characters = StringBuilderPool.Borrow();
|
||||
characters = stringBuilder;
|
||||
characters.Append((char)currentByte);
|
||||
}
|
||||
else
|
||||
@@ -53,7 +53,7 @@
|
||||
try
|
||||
{
|
||||
var str = characters.ToString();
|
||||
StringBuilderPool.Return(characters);
|
||||
characters.Clear();
|
||||
|
||||
switch (str)
|
||||
{
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
namespace UglyToad.PdfPig.Tokenization
|
||||
{
|
||||
using Core;
|
||||
using System.Text;
|
||||
using Tokens;
|
||||
|
||||
internal class PlainTokenizer : ITokenizer
|
||||
{
|
||||
private static readonly StringBuilderPool StringBuilderPool = new StringBuilderPool(10);
|
||||
private readonly StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
public bool ReadsNextByte { get; } = true;
|
||||
|
||||
@@ -18,7 +19,7 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
var builder = StringBuilderPool.Borrow();
|
||||
var builder = stringBuilder;
|
||||
builder.Append((char)currentByte);
|
||||
while (inputBytes.MoveNext())
|
||||
{
|
||||
@@ -39,7 +40,7 @@
|
||||
}
|
||||
|
||||
var text = builder.ToString();
|
||||
StringBuilderPool.Return(builder);
|
||||
builder.Clear();
|
||||
|
||||
switch (text)
|
||||
{
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
private static readonly DictionaryTokenizer DictionaryTokenizer = new DictionaryTokenizer();
|
||||
private static readonly HexTokenizer HexTokenizer = new HexTokenizer();
|
||||
private static readonly NameTokenizer NameTokenizer = new NameTokenizer();
|
||||
private static readonly NumericTokenizer NumericTokenizer = new NumericTokenizer();
|
||||
private static readonly PlainTokenizer PlainTokenizer = new PlainTokenizer();
|
||||
private static readonly StringTokenizer StringTokenizer = new StringTokenizer();
|
||||
|
||||
// NOTE: these are not thread safe so should not be static. Each instance includes a
|
||||
// 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 IInputBytes inputBytes;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
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 TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken token)
|
||||
@@ -23,7 +23,7 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
var builder = StringBuilderPool.Borrow();
|
||||
var builder = stringBuilder;
|
||||
var numberOfBrackets = 1;
|
||||
var isEscapeActive = false;
|
||||
var isLineBreaking = false;
|
||||
@@ -178,7 +178,7 @@
|
||||
encodedWith = StringToken.Encoding.Iso88591;
|
||||
}
|
||||
|
||||
StringBuilderPool.Return(builder);
|
||||
builder.Clear();
|
||||
|
||||
token = new StringToken(tokenStr, encodedWith);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
Reference in New Issue
Block a user