Added "Paths" collection to Page object.

Added matrix transformation to path operators.
This commit is contained in:
vadimy
2019-07-16 00:35:29 -04:00
parent 453faf50af
commit b9d0cca2a6
14 changed files with 73 additions and 34 deletions

View File

@@ -9,6 +9,7 @@
using Util; using Util;
using Util.JetBrains.Annotations; using Util.JetBrains.Annotations;
using XObjects; using XObjects;
using UglyToad.PdfPig.Geometry;
/// <summary> /// <summary>
/// Contains the content and provides access to methods of a single page in the <see cref="PdfDocument"/>. /// Contains the content and provides access to methods of a single page in the <see cref="PdfDocument"/>.
@@ -41,6 +42,11 @@
/// </summary> /// </summary>
public IReadOnlyList<Letter> Letters => Content?.Letters ?? new Letter[0]; public IReadOnlyList<Letter> Letters => Content?.Letters ?? new Letter[0];
/// <summary>
/// The set of <see cref="PdfPath"/>s drawn by the PDF content.
/// </summary>
public List<PdfPath> Paths => Content?.Paths ?? new List<PdfPath>();
/// <summary> /// <summary>
/// The full text of all characters on the page in the order they are presented in the PDF content. /// The full text of all characters on the page in the order they are presented in the PDF content.
/// </summary> /// </summary>

View File

@@ -5,6 +5,7 @@
using Graphics.Operations; using Graphics.Operations;
using Tokenization.Scanner; using Tokenization.Scanner;
using XObjects; using XObjects;
using UglyToad.PdfPig.Geometry;
/// <summary> /// <summary>
/// ///
@@ -23,8 +24,9 @@
internal IReadOnlyList<IGraphicsStateOperation> GraphicsStateOperations { get; } internal IReadOnlyList<IGraphicsStateOperation> GraphicsStateOperations { get; }
public IReadOnlyList<Letter> Letters { get; } public IReadOnlyList<Letter> Letters { get; }
public List<PdfPath> Paths { get; }
internal PageContent(IReadOnlyList<IGraphicsStateOperation> graphicsStateOperations, IReadOnlyList<Letter> letters, internal PageContent(IReadOnlyList<IGraphicsStateOperation> graphicsStateOperations, IReadOnlyList<Letter> letters, List<PdfPath> paths,
IReadOnlyDictionary<XObjectType, List<XObjectContentRecord>> xObjects, IReadOnlyDictionary<XObjectType, List<XObjectContentRecord>> xObjects,
IPdfTokenScanner pdfScanner, IPdfTokenScanner pdfScanner,
XObjectFactory xObjectFactory, XObjectFactory xObjectFactory,
@@ -32,6 +34,7 @@
{ {
GraphicsStateOperations = graphicsStateOperations; GraphicsStateOperations = graphicsStateOperations;
Letters = letters; Letters = letters;
Paths = paths;
this.xObjects = xObjects; this.xObjects = xObjects;
this.pdfScanner = pdfScanner; this.pdfScanner = pdfScanner;
this.xObjectFactory = xObjectFactory; this.xObjectFactory = xObjectFactory;

View File

@@ -5,27 +5,29 @@ namespace UglyToad.PdfPig.Geometry
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UglyToad.PdfPig.Core;
/// <summary> /// <summary>
/// A path in a PDF document, used by glyphs and page content. /// A path in a PDF document, used by glyphs and page content.
/// </summary> /// </summary>
public class PdfPath public class PdfPath
{ {
private readonly List<IPathCommand> commands = new List<IPathCommand>(); public readonly List<IPathCommand> Commands = new List<IPathCommand>();
private PdfPoint? currentPosition; private PdfPoint? currentPosition;
internal TransformationMatrix CurrentTransformationMatrix;
internal void MoveTo(decimal x, decimal y) internal void MoveTo(decimal x, decimal y)
{ {
currentPosition = new PdfPoint(x, y); currentPosition = CurrentTransformationMatrix.Transform(new PdfPoint(x, y));
commands.Add(new Move(currentPosition.Value)); Commands.Add(new Move(currentPosition.Value));
} }
internal void LineTo(decimal x, decimal y) internal void LineTo(decimal x, decimal y)
{ {
if (currentPosition.HasValue) if (currentPosition.HasValue)
{ {
var to = new PdfPoint(x, y); var to = CurrentTransformationMatrix.Transform(new PdfPoint(x, y));
commands.Add(new Line(currentPosition.Value, to)); Commands.Add(new Line(currentPosition.Value, to));
currentPosition = to; currentPosition = to;
} }
else else
@@ -40,9 +42,9 @@ namespace UglyToad.PdfPig.Geometry
{ {
if (currentPosition.HasValue) if (currentPosition.HasValue)
{ {
var to = new PdfPoint(x3, y3); var to = CurrentTransformationMatrix.Transform(new PdfPoint(x3, y3));
commands.Add(new BezierCurve(currentPosition.Value, Commands.Add(new BezierCurve(currentPosition.Value,
new PdfPoint(x1, y1), new PdfPoint(x2, y2), to)); CurrentTransformationMatrix.Transform(new PdfPoint(x1, y1)), CurrentTransformationMatrix.Transform(new PdfPoint(x2, y2)), to));
currentPosition = to; currentPosition = to;
} }
else else
@@ -55,12 +57,12 @@ namespace UglyToad.PdfPig.Geometry
internal void ClosePath() internal void ClosePath()
{ {
commands.Add(new Close()); Commands.Add(new Close());
} }
internal PdfRectangle? GetBoundingRectangle() internal PdfRectangle? GetBoundingRectangle()
{ {
if (commands.Count == 0) if (Commands.Count == 0)
{ {
return null; return null;
} }
@@ -71,7 +73,7 @@ namespace UglyToad.PdfPig.Geometry
var minY = decimal.MaxValue; var minY = decimal.MaxValue;
var maxY = decimal.MinValue; var maxY = decimal.MinValue;
foreach (var command in commands) foreach (var command in Commands)
{ {
var rect = command.GetBoundingRectangle(); var rect = command.GetBoundingRectangle();
if (rect == null) if (rect == null)
@@ -106,7 +108,7 @@ namespace UglyToad.PdfPig.Geometry
internal string ToSvg() internal string ToSvg()
{ {
var builder = new StringBuilder(); var builder = new StringBuilder();
foreach (var pathCommand in commands) foreach (var pathCommand in Commands)
{ {
pathCommand.WriteSvg(builder); pathCommand.WriteSvg(builder);
} }
@@ -136,7 +138,7 @@ namespace UglyToad.PdfPig.Geometry
var bbox = GetBoundingRectangle(); var bbox = GetBoundingRectangle();
var bboxes = new List<PdfRectangle>(); var bboxes = new List<PdfRectangle>();
foreach (var command in commands) foreach (var command in Commands)
{ {
var segBbox = command.GetBoundingRectangle(); var segBbox = command.GetBoundingRectangle();
if (segBbox.HasValue) if (segBbox.HasValue)
@@ -153,7 +155,7 @@ namespace UglyToad.PdfPig.Geometry
return result; return result;
} }
internal interface IPathCommand public interface IPathCommand
{ {
PdfRectangle? GetBoundingRectangle(); PdfRectangle? GetBoundingRectangle();
@@ -173,7 +175,7 @@ namespace UglyToad.PdfPig.Geometry
} }
} }
private class Move : IPathCommand public class Move : IPathCommand
{ {
public PdfPoint Location { get; } public PdfPoint Location { get; }
@@ -193,7 +195,7 @@ namespace UglyToad.PdfPig.Geometry
} }
} }
private class Line : IPathCommand public class Line : IPathCommand
{ {
public PdfPoint From { get; } public PdfPoint From { get; }
@@ -216,7 +218,7 @@ namespace UglyToad.PdfPig.Geometry
} }
} }
internal class BezierCurve : IPathCommand public class BezierCurve : IPathCommand
{ {
public PdfPoint StartPoint { get; } public PdfPoint StartPoint { get; }
@@ -379,6 +381,11 @@ namespace UglyToad.PdfPig.Geometry
internal void Rectangle(decimal x, decimal y, decimal width, decimal height) internal void Rectangle(decimal x, decimal y, decimal width, decimal height)
{ {
currentPosition = CurrentTransformationMatrix.Transform(new PdfPoint(x, y));
LineTo(x + width, y);
LineTo(x + width, y + height);
LineTo(x, y + height);
LineTo(x, y);
} }
} }
} }

View File

@@ -48,7 +48,6 @@
}; };
public List<Letter> Letters = new List<Letter>(); public List<Letter> Letters = new List<Letter>();
public ContentStreamProcessor(PdfRectangle cropBox, IResourceStore resourceStore, UserSpaceUnit userSpaceUnit, PageRotationDegrees rotation, bool isLenientParsing, public ContentStreamProcessor(PdfRectangle cropBox, IResourceStore resourceStore, UserSpaceUnit userSpaceUnit, PageRotationDegrees rotation, bool isLenientParsing,
IPdfTokenScanner pdfScanner, IPdfTokenScanner pdfScanner,
XObjectFactory xObjectFactory, XObjectFactory xObjectFactory,
@@ -70,7 +69,7 @@
ProcessOperations(operations); ProcessOperations(operations);
return new PageContent(operations, Letters, xObjects, pdfScanner, xObjectFactory, isLenientParsing); return new PageContent(operations, Letters, paths, xObjects, pdfScanner, xObjectFactory, isLenientParsing);
} }
private void ProcessOperations(IReadOnlyList<IGraphicsStateOperation> operations) private void ProcessOperations(IReadOnlyList<IGraphicsStateOperation> operations)
@@ -192,7 +191,7 @@
var textState = currentState.FontState; var textState = currentState.FontState;
var fontSize = textState.FontSize; var fontSize = textState.FontSize;
var horizontalScaling = textState.HorizontalScaling/100m; var horizontalScaling = textState.HorizontalScaling / 100m;
var font = resourceStore.GetFont(textState.FontName); var font = resourceStore.GetFont(textState.FontName);
var isVertical = font.IsVertical; var isVertical = font.IsVertical;
@@ -226,7 +225,7 @@
} }
else else
{ {
bytes = OtherEncodings.StringAsLatin1Bytes(((StringToken) token).Data); bytes = OtherEncodings.StringAsLatin1Bytes(((StringToken)token).Data);
} }
ShowText(new ByteArrayInputBytes(bytes)); ShowText(new ByteArrayInputBytes(bytes));
@@ -267,6 +266,7 @@
public void BeginSubpath() public void BeginSubpath()
{ {
CurrentPath = new PdfPath(); CurrentPath = new PdfPath();
CurrentPath.CurrentTransformationMatrix = GetCurrentState().CurrentTransformationMatrix;
} }
public void StrokePath(bool close) public void StrokePath(bool close)
@@ -275,12 +275,21 @@
{ {
ClosePath(); ClosePath();
} }
paths.Add(CurrentPath);
}
public void FillPath(bool close)
{
if (close)
{
ClosePath();
}
paths.Add(CurrentPath);
} }
public void ClosePath() public void ClosePath()
{ {
CurrentPath.ClosePath(); CurrentPath.ClosePath();
paths.Add(CurrentPath);
CurrentPath = null; CurrentPath = null;
} }
@@ -297,12 +306,12 @@
if (state.TryGet(NameToken.Lc, pdfScanner, out NumericToken lcToken)) if (state.TryGet(NameToken.Lc, pdfScanner, out NumericToken lcToken))
{ {
currentGraphicsState.CapStyle = (LineCapStyle) lcToken.Int; currentGraphicsState.CapStyle = (LineCapStyle)lcToken.Int;
} }
if (state.TryGet(NameToken.Lj, pdfScanner, out NumericToken ljToken)) if (state.TryGet(NameToken.Lj, pdfScanner, out NumericToken ljToken))
{ {
currentGraphicsState.JoinStyle = (LineJoinStyle) ljToken.Int; currentGraphicsState.JoinStyle = (LineJoinStyle)ljToken.Int;
} }
if (state.TryGet(NameToken.Font, pdfScanner, out ArrayToken fontArray) && fontArray.Length == 2 if (state.TryGet(NameToken.Font, pdfScanner, out ArrayToken fontArray) && fontArray.Length == 2

View File

@@ -82,6 +82,12 @@
/// <param name="close">Whether to also close the path.</param> /// <param name="close">Whether to also close the path.</param>
void StrokePath(bool close); void StrokePath(bool close);
/// <summary>
/// Fill the current path.
/// </summary>
/// <param name="close">Whether to also close the path.</param>
void FillPath(bool close);
/// <summary> /// <summary>
/// Close the current path. /// Close the current path.
/// </summary> /// </summary>

View File

@@ -28,6 +28,7 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
operationContext.FillPath(true);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -28,6 +28,7 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
operationContext.FillPath(true);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -28,6 +28,7 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
operationContext.FillPath(false);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -28,6 +28,7 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
operationContext.FillPath(false);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -29,6 +29,7 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
operationContext.FillPath(false);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -28,6 +28,7 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
operationContext.FillPath(false);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -29,6 +29,7 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
operationContext.FillPath(false);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -52,7 +52,7 @@
{ {
operationContext.BeginSubpath(); operationContext.BeginSubpath();
operationContext.CurrentPath.Rectangle(LowerLeft.X, LowerLeft.Y, Width, Height); operationContext.CurrentPath.Rectangle(LowerLeft.X, LowerLeft.Y, Width, Height);
operationContext.CurrentPath.ClosePath(); //operationContext.CurrentPath.ClosePath();
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -37,6 +37,7 @@
{ {
operationContext.BeginSubpath(); operationContext.BeginSubpath();
operationContext.CurrentPosition = Point; operationContext.CurrentPosition = Point;
operationContext.CurrentPath.LineTo(Point.X, Point.Y);
} }
/// <inheritdoc /> /// <inheritdoc />