mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-11-28 17:47:12 +08:00
@@ -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
|
||||||
@@ -43,12 +45,39 @@
|
|||||||
// Other subrs 0-2 implement flex
|
// Other subrs 0-2 implement flex
|
||||||
case FlexEnd:
|
case FlexEnd:
|
||||||
{
|
{
|
||||||
|
// https://github.com/apache/pdfbox/blob/2c23d8b4e3ad61852f0b6ee2b95b907eefba1fcf/fontbox/src/main/java/org/apache/fontbox/cff/Type1CharString.java#L339
|
||||||
context.IsFlexing = false;
|
context.IsFlexing = false;
|
||||||
// TODO: I don't really care about flexpoints, but we should probably handle them... one day.
|
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.");
|
}
|
||||||
//}
|
|
||||||
|
// 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();
|
context.ClearFlexPoints();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
if (context.IsFlexing)
|
if (context.IsFlexing)
|
||||||
{
|
{
|
||||||
|
context.AddFlexPoint(new PdfPoint(deltaX, deltaY));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user