From 257439b8a3dee5ad59f625f41f4800499e47c036 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sat, 2 Dec 2017 14:57:44 +0000 Subject: [PATCH] make classes internal and scaffold reading of glyphs --- .../Parser/PageContentParserTests.cs | 26 +++---- src/UglyToad.Pdf/Content/Page.cs | 4 +- src/UglyToad.Pdf/Content/ResourceContainer.cs | 4 +- src/UglyToad.Pdf/Core/TransformationMatrix.cs | 10 +++ src/UglyToad.Pdf/Fonts/IFont.cs | 34 ++++++++- src/UglyToad.Pdf/Geometry/PdfVector.cs | 25 +++++++ .../Graphics/ContentStreamProcessor.cs | 73 ++++++++++++++++--- src/UglyToad.Pdf/Parser/IPageContentParser.cs | 5 +- src/UglyToad.Pdf/Parser/PageContentParser.cs | 8 +- 9 files changed, 153 insertions(+), 36 deletions(-) create mode 100644 src/UglyToad.Pdf/Geometry/PdfVector.cs diff --git a/src/UglyToad.Pdf.Tests/Parser/PageContentParserTests.cs b/src/UglyToad.Pdf.Tests/Parser/PageContentParserTests.cs index 55cdadc8..89726869 100644 --- a/src/UglyToad.Pdf.Tests/Parser/PageContentParserTests.cs +++ b/src/UglyToad.Pdf.Tests/Parser/PageContentParserTests.cs @@ -38,28 +38,28 @@ ET"; var result = parser.Parse(operationFactory, input.Bytes); - Assert.Equal(7, result.GraphicsStateOperations.Count); + Assert.Equal(7, result.Count); - Assert.Equal(BeginText.Value, result.GraphicsStateOperations[0]); + Assert.Equal(BeginText.Value, result[0]); - var font = Assert.IsType(result.GraphicsStateOperations[1]); + var font = Assert.IsType(result[1]); Assert.Equal(CosName.Create("F13"), font.Font); Assert.Equal(48, font.Size); - var nextLine = Assert.IsType(result.GraphicsStateOperations[2]); + var nextLine = Assert.IsType(result[2]); Assert.Equal(20, nextLine.Tx); Assert.Equal(38, nextLine.Ty); - var renderingMode = Assert.IsType(result.GraphicsStateOperations[3]); + var renderingMode = Assert.IsType(result[3]); Assert.Equal(RenderingMode.Stroke, renderingMode.Mode); - var lineWidth = Assert.IsType(result.GraphicsStateOperations[4]); + var lineWidth = Assert.IsType(result[4]); Assert.Equal(2, lineWidth.Width); - var text = Assert.IsType(result.GraphicsStateOperations[5]); + var text = Assert.IsType(result[5]); Assert.Equal("ABC", text.Text); - Assert.Equal(EndText.Value, result.GraphicsStateOperations[6]); + Assert.Equal(EndText.Value, result[6]); } [Fact] @@ -74,18 +74,18 @@ ET"; var result = parser.Parse(operationFactory, input.Bytes); - Assert.Equal(4, result.GraphicsStateOperations.Count); + Assert.Equal(4, result.Count); - Assert.Equal(BeginText.Value, result.GraphicsStateOperations[0]); + Assert.Equal(BeginText.Value, result[0]); - var moveLine = Assert.IsType(result.GraphicsStateOperations[1]); + var moveLine = Assert.IsType(result[1]); Assert.Equal(21, moveLine.Tx); Assert.Equal(32, moveLine.Ty); - var renderingMode = Assert.IsType(result.GraphicsStateOperations[2]); + var renderingMode = Assert.IsType(result[2]); Assert.Equal(RenderingMode.Fill, renderingMode.Mode); - Assert.Equal(EndText.Value, result.GraphicsStateOperations[3]); + Assert.Equal(EndText.Value, result[3]); } private const string SimpleGoogleDocPageContent = @" diff --git a/src/UglyToad.Pdf/Content/Page.cs b/src/UglyToad.Pdf/Content/Page.cs index d587ea28..8da72d2f 100644 --- a/src/UglyToad.Pdf/Content/Page.cs +++ b/src/UglyToad.Pdf/Content/Page.cs @@ -93,8 +93,10 @@ var textContents = OtherEncodings.BytesAsLatin1String(contents); } - Content = parsingArguments.Container.Get() + var operations = parsingArguments.Container.Get() .Parse(parsingArguments.Container.Get(), new ByteArrayInputBytes(contents)); + + } } } diff --git a/src/UglyToad.Pdf/Content/ResourceContainer.cs b/src/UglyToad.Pdf/Content/ResourceContainer.cs index ef106f50..88ecee28 100644 --- a/src/UglyToad.Pdf/Content/ResourceContainer.cs +++ b/src/UglyToad.Pdf/Content/ResourceContainer.cs @@ -7,12 +7,12 @@ using Fonts; using Parser; - public interface IResourceStore + internal interface IResourceStore { IFont GetFont(CosName name); } - public class ResourceContainer : IResourceStore + internal class ResourceContainer : IResourceStore { private readonly Dictionary loadedFonts = new Dictionary(); diff --git a/src/UglyToad.Pdf/Core/TransformationMatrix.cs b/src/UglyToad.Pdf/Core/TransformationMatrix.cs index 371154fd..3fedd02d 100644 --- a/src/UglyToad.Pdf/Core/TransformationMatrix.cs +++ b/src/UglyToad.Pdf/Core/TransformationMatrix.cs @@ -166,5 +166,15 @@ { return $"{A}, {B}, 0\r\n{C}, {D}, 0\r\n{E}, {F}, 1"; } + + public static TransformationMatrix GetTranslationMatrix(decimal x, decimal y) + { + return new TransformationMatrix(new [] + { + 1, 0, 0, + 0, 1, 0, + x, y, 1 + }); + } } } diff --git a/src/UglyToad.Pdf/Fonts/IFont.cs b/src/UglyToad.Pdf/Fonts/IFont.cs index 18bd0433..960f8e1f 100644 --- a/src/UglyToad.Pdf/Fonts/IFont.cs +++ b/src/UglyToad.Pdf/Fonts/IFont.cs @@ -2,22 +2,50 @@ { using Cmap; using Cos; + using Geometry; + using IO; - public interface IFont + internal interface IFont { + CosName Name { get; } + CosName SubType { get; } string BaseFontType { get; } - CMap ToUnicode { get; } + bool IsVertical { get; } + + int ReadCharacterCode(IInputBytes bytes, out int codeLength); + + string GetUnicode(int characterCode); + + PdfVector GetDisplacement(int characterCode); } - public class CompositeFont : IFont + internal class CompositeFont : IFont { + public CosName Name { get; } + public CosName SubType { get; } public string BaseFontType { get; } + public bool IsVertical { get; } public CMap ToUnicode { get; } + + public int ReadCharacterCode(IInputBytes bytes, out int codeLength) + { + throw new System.NotImplementedException(); + } + + public string GetUnicode(int characterCode) + { + throw new System.NotImplementedException(); + } + + public PdfVector GetDisplacement(int characterCode) + { + throw new System.NotImplementedException(); + } } } diff --git a/src/UglyToad.Pdf/Geometry/PdfVector.cs b/src/UglyToad.Pdf/Geometry/PdfVector.cs new file mode 100644 index 00000000..2f52ef6a --- /dev/null +++ b/src/UglyToad.Pdf/Geometry/PdfVector.cs @@ -0,0 +1,25 @@ +namespace UglyToad.Pdf.Geometry +{ + internal struct PdfVector + { + public decimal X { get; } + + public decimal Y { get; } + + public PdfVector(decimal x, decimal y) + { + X = x; + Y = y; + } + + public PdfVector Scale(decimal scale) + { + return new PdfVector(X * scale, Y * scale); + } + + public override string ToString() + { + return $"({X}, {Y})"; + } + } +} diff --git a/src/UglyToad.Pdf/Graphics/ContentStreamProcessor.cs b/src/UglyToad.Pdf/Graphics/ContentStreamProcessor.cs index c2d2b79d..5cd9fbfb 100644 --- a/src/UglyToad.Pdf/Graphics/ContentStreamProcessor.cs +++ b/src/UglyToad.Pdf/Graphics/ContentStreamProcessor.cs @@ -3,43 +3,50 @@ using System; using System.Collections.Generic; using Content; + using Fonts; using Geometry; using IO; using Operations; + using Pdf.Core; internal class ContentStreamProcessor : IOperationContext { private readonly IResourceStore resourceStore; - private readonly Stack graphicsStack = new Stack(); + + private Stack graphicsStack = new Stack(); public TextMatrices TextMatrices { get; } = new TextMatrices(); public int StackSize => graphicsStack.Count; - - + public ContentStreamProcessor(PdfRectangle cropBox, IResourceStore resourceStore) { this.resourceStore = resourceStore; } - public void Process(IReadOnlyList operations) + public PageContent Process(IReadOnlyList operations) { var currentState = CloneAllStates(); - + ProcessOperations(operations); + + return new PageContent(); } private void ProcessOperations(IReadOnlyList operations) { foreach (var stateOperation in operations) { - // stateOperation.Run(); + stateOperation.Run(this, resourceStore); } } private Stack CloneAllStates() { - throw new NotImplementedException(); + var saved = graphicsStack; + graphicsStack = new Stack(); + graphicsStack.Push(saved.Peek().DeepClone()); + return saved; } public CurrentGraphicsState GetCurrentState() @@ -59,9 +66,57 @@ public void ShowText(IInputBytes bytes) { - var renderingMatrix = TextMatrices.GetRenderingMatrix(GetCurrentState()); - var font = resourceStore.GetFont(GetCurrentState().FontState.FontName); + + var fontSize = GetCurrentState().FontState.FontSize; + var horizontalScaling = GetCurrentState().FontState.HorizontalScaling; + var characterSpacing = GetCurrentState().FontState.CharacterSpacing; + + while (bytes.MoveNext()) + { + var code = font.ReadCharacterCode(bytes, out int codeLength); + + var unicode = font.GetUnicode(code); + + var wordSpacing = 0m; + if (code == ' ' && codeLength == 1) + { + wordSpacing += GetCurrentState().FontState.WordSpacing; + } + + var renderingMatrix = TextMatrices.GetRenderingMatrix(GetCurrentState()); + + if (font.IsVertical) + { + throw new NotImplementedException("Vertical fonts are currently unsupported, please submit a pull request."); + } + + var displacement = font.GetDisplacement(code); + + ShowGlyph(renderingMatrix, font, code, unicode, displacement); + + decimal tx, ty; + if (font.IsVertical) + { + tx = 0; + ty = displacement.Y * fontSize + characterSpacing + wordSpacing; + } + else + { + tx = (displacement.X * fontSize + characterSpacing + wordSpacing) * horizontalScaling; + ty = 0; + } + + var translate = TransformationMatrix.GetTranslationMatrix(tx, ty); + + TextMatrices.TextMatrix = translate.Multiply(TextMatrices.TextMatrix); + } + } + + private void ShowGlyph(TransformationMatrix renderingMatrix, IFont font, + int characterCode, string unicode, PdfVector displacement) + { + throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/src/UglyToad.Pdf/Parser/IPageContentParser.cs b/src/UglyToad.Pdf/Parser/IPageContentParser.cs index 8cbc38b8..ceafb76b 100644 --- a/src/UglyToad.Pdf/Parser/IPageContentParser.cs +++ b/src/UglyToad.Pdf/Parser/IPageContentParser.cs @@ -1,11 +1,12 @@ namespace UglyToad.Pdf.Parser { - using Content; + using System.Collections.Generic; using Graphics; + using Graphics.Operations; using IO; internal interface IPageContentParser { - PageContent Parse(IGraphicsStateOperationFactory operationFactory, IInputBytes inputBytes); + IReadOnlyList Parse(IGraphicsStateOperationFactory operationFactory, IInputBytes inputBytes); } } \ No newline at end of file diff --git a/src/UglyToad.Pdf/Parser/PageContentParser.cs b/src/UglyToad.Pdf/Parser/PageContentParser.cs index d5350efe..dc7a4316 100644 --- a/src/UglyToad.Pdf/Parser/PageContentParser.cs +++ b/src/UglyToad.Pdf/Parser/PageContentParser.cs @@ -1,7 +1,6 @@ namespace UglyToad.Pdf.Parser { using System.Collections.Generic; - using Content; using Graphics; using Graphics.Operations; using IO; @@ -10,7 +9,7 @@ internal class PageContentParser : IPageContentParser { - public PageContent Parse(IGraphicsStateOperationFactory operationFactory, IInputBytes inputBytes) + public IReadOnlyList Parse(IGraphicsStateOperationFactory operationFactory, IInputBytes inputBytes) { var scanner = new CoreTokenScanner(inputBytes); @@ -41,10 +40,7 @@ } } - return new PageContent - { - GraphicsStateOperations = graphicsStateOperations - }; + return graphicsStateOperations; } } }