mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-19 10:47:56 +08:00
add operation processing for text state operations, support for matrix multiplication with tests and td operation support
This commit is contained in:
90
src/UglyToad.Pdf.Tests/Core/TransformationMatrixTests.cs
Normal file
90
src/UglyToad.Pdf.Tests/Core/TransformationMatrixTests.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
namespace UglyToad.Pdf.Tests.Core
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Pdf.Core;
|
||||
using Xunit;
|
||||
|
||||
public class TransformationMatrixTests
|
||||
{
|
||||
public static IEnumerable<object[]> MultiplicationData => new[]
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new decimal[]
|
||||
{
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
},
|
||||
new decimal[]
|
||||
{
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
},
|
||||
new decimal[]
|
||||
{
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
}
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new decimal[]
|
||||
{
|
||||
65, 9, 3,
|
||||
5, 2, 7,
|
||||
11, 1, 6
|
||||
},
|
||||
new decimal[]
|
||||
{
|
||||
1, 2, 3,
|
||||
4, 5, 6,
|
||||
7, 8, 9
|
||||
},
|
||||
new decimal[]
|
||||
{
|
||||
122, 199, 276,
|
||||
62, 76, 90,
|
||||
57, 75, 93
|
||||
}
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new decimal[]
|
||||
{
|
||||
3, 5, 7,
|
||||
11, 13, -3,
|
||||
17, -6, -9
|
||||
},
|
||||
new decimal[]
|
||||
{
|
||||
5, 4, 3,
|
||||
3, 7, 12,
|
||||
1, 0, 6
|
||||
},
|
||||
new decimal[]
|
||||
{
|
||||
37, 47, 111,
|
||||
91, 135, 171,
|
||||
58, 26, -75
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(MultiplicationData))]
|
||||
public void MultipliesCorrectly(decimal[] a, decimal[] b, decimal[] expected)
|
||||
{
|
||||
var matrixA = TransformationMatrix.FromArray(a);
|
||||
var matrixB = TransformationMatrix.FromArray(b);
|
||||
|
||||
var expectedMatrix = TransformationMatrix.FromArray(expected);
|
||||
|
||||
var result = matrixA.Multiply(matrixB);
|
||||
|
||||
Assert.Equal(expectedMatrix, result);
|
||||
}
|
||||
}
|
||||
}
|
@@ -41,7 +41,7 @@ ET";
|
||||
|
||||
Assert.Equal(BeginText.Value, result.GraphicsStateOperations[0]);
|
||||
|
||||
var font = Assert.IsType<SetFontSize>(result.GraphicsStateOperations[1]);
|
||||
var font = Assert.IsType<SetFontAndSize>(result.GraphicsStateOperations[1]);
|
||||
Assert.Equal(CosName.Create("F13"), font.Font);
|
||||
Assert.Equal(48, font.Size);
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
/// </summary>
|
||||
internal struct TransformationMatrix
|
||||
{
|
||||
public static TransformationMatrix Default = new TransformationMatrix(new decimal[]
|
||||
public static TransformationMatrix Identity = new TransformationMatrix(new decimal[]
|
||||
{
|
||||
1,0,0,
|
||||
0,1,0,
|
||||
@@ -23,6 +23,44 @@
|
||||
public decimal D => value[4];
|
||||
public decimal E => value[6];
|
||||
public decimal F => value[7];
|
||||
|
||||
public decimal this[int row, int col]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (row >= Rows)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"The transformation matrix only contains {Rows} rows and is zero indexed, you tried to access row {row}.");
|
||||
}
|
||||
|
||||
if (row < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Cannot access negative rows in a matrix.");
|
||||
}
|
||||
|
||||
if (col >= Columns)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"The transformation matrix only contains {Columns} columns and is zero indexed, you tried to access column {col}.");
|
||||
}
|
||||
|
||||
if (col < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Cannot access negative columns in a matrix.");
|
||||
}
|
||||
|
||||
var resultIndex = row * Rows + col;
|
||||
|
||||
if (resultIndex > value.Length - 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} mapped to the index {resultIndex} which was not in the value array.");
|
||||
}
|
||||
|
||||
return value[resultIndex];
|
||||
}
|
||||
}
|
||||
|
||||
public const int Rows = 3;
|
||||
public const int Columns = 3;
|
||||
|
||||
public TransformationMatrix(decimal[] value)
|
||||
{
|
||||
@@ -46,8 +84,8 @@
|
||||
|
||||
return new PdfPoint(x, y);
|
||||
}
|
||||
|
||||
public static TransformationMatrix FromArray(decimal[] values)
|
||||
|
||||
public static TransformationMatrix FromArray(params decimal[] values)
|
||||
{
|
||||
if (values.Length == 9)
|
||||
{
|
||||
@@ -67,6 +105,52 @@
|
||||
throw new ArgumentException("The array must either define all 9 elements of the matrix or all 6 key elements. Instead array was: " + values);
|
||||
}
|
||||
|
||||
public TransformationMatrix Multiply(TransformationMatrix matrix)
|
||||
{
|
||||
var result = new decimal[9];
|
||||
|
||||
for (int i = 0; i < Rows; i++)
|
||||
{
|
||||
for (int j = 0; j < Columns; j++)
|
||||
{
|
||||
var index = (i * Rows) + j;
|
||||
|
||||
for (int x = 0; x < Rows; x++)
|
||||
{
|
||||
result[index] += this[i, x] * matrix[x, j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new TransformationMatrix(result);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is TransformationMatrix m))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals(this, m);
|
||||
}
|
||||
|
||||
public static bool Equals(TransformationMatrix a, TransformationMatrix b)
|
||||
{
|
||||
for (int i = 0; i < Rows; i++)
|
||||
{
|
||||
for (int j = 0; j < Columns; j++)
|
||||
{
|
||||
if (a[i, j] != b[i, j])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{A}, {B}, 0\r\n{C}, {D}, 0\r\n{E}, {F}, 1";
|
||||
|
@@ -61,7 +61,7 @@
|
||||
/// <summary>
|
||||
/// Maps positions from user coordinates to device coordinates.
|
||||
/// </summary>
|
||||
public TransformationMatrix CurrentTransformationMatrix { get; set; } = TransformationMatrix.Default;
|
||||
public TransformationMatrix CurrentTransformationMatrix { get; set; } = TransformationMatrix.Identity;
|
||||
|
||||
#region Device Dependent
|
||||
|
||||
|
@@ -16,8 +16,8 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.TextMatrices.TextMatrix = TransformationMatrix.Default;
|
||||
operationContext.TextMatrices.TextLineMatrix = TransformationMatrix.Default;
|
||||
operationContext.TextMatrices.TextMatrix = TransformationMatrix.Identity;
|
||||
operationContext.TextMatrices.TextLineMatrix = TransformationMatrix.Identity;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -16,8 +16,8 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.TextMatrices.TextMatrix = TransformationMatrix.Default;
|
||||
operationContext.TextMatrices.TextLineMatrix = TransformationMatrix.Default;
|
||||
operationContext.TextMatrices.TextMatrix = TransformationMatrix.Identity;
|
||||
operationContext.TextMatrices.TextLineMatrix = TransformationMatrix.Identity;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -1,5 +1,8 @@
|
||||
namespace UglyToad.Pdf.Graphics.Operations.TextPositioning
|
||||
{
|
||||
using Content;
|
||||
using Core;
|
||||
|
||||
internal class MoveToNextLineWithOffset : IGraphicsStateOperation
|
||||
{
|
||||
public const string Symbol = "Td";
|
||||
@@ -16,6 +19,18 @@
|
||||
Ty = ty;
|
||||
}
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
var currentTextLineMatrix = operationContext.TextMatrices.TextLineMatrix;
|
||||
|
||||
var matrix = TransformationMatrix.FromArray(1, 0, 0, 1, Tx, Ty);
|
||||
|
||||
var transformed = matrix.Multiply(currentTextLineMatrix);
|
||||
|
||||
operationContext.TextMatrices.TextLineMatrix = transformed;
|
||||
operationContext.TextMatrices.TextMatrix = transformed;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Tx} {Ty} {Symbol}";
|
||||
|
@@ -1,5 +1,7 @@
|
||||
namespace UglyToad.Pdf.Graphics.Operations.TextState
|
||||
{
|
||||
using Content;
|
||||
|
||||
internal class SetCharacterSpacing : IGraphicsStateOperation
|
||||
{
|
||||
public const string Symbol = "Tc";
|
||||
@@ -13,6 +15,13 @@
|
||||
Spacing = spacing;
|
||||
}
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
var currentState = operationContext.GetCurrentState();
|
||||
|
||||
currentState.FontState.CharacterSpacing = Spacing;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Spacing} {Symbol}";
|
||||
|
@@ -1,8 +1,9 @@
|
||||
namespace UglyToad.Pdf.Graphics.Operations.TextState
|
||||
{
|
||||
using Content;
|
||||
using Cos;
|
||||
|
||||
internal class SetFontSize : IGraphicsStateOperation
|
||||
internal class SetFontAndSize : IGraphicsStateOperation
|
||||
{
|
||||
public const string Symbol = "Tf";
|
||||
|
||||
@@ -12,11 +13,19 @@
|
||||
|
||||
public decimal Size { get; }
|
||||
|
||||
public SetFontSize(CosName font, decimal size)
|
||||
public SetFontAndSize(CosName font, decimal size)
|
||||
{
|
||||
Font = font;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
var currentState = operationContext.GetCurrentState();
|
||||
|
||||
currentState.FontState.FontSize = Size;
|
||||
currentState.FontState.FontName = Font;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
@@ -1,5 +1,7 @@
|
||||
namespace UglyToad.Pdf.Graphics.Operations.TextState
|
||||
{
|
||||
using Content;
|
||||
|
||||
internal class SetHorizontalScaling : IGraphicsStateOperation
|
||||
{
|
||||
public const string Symbol = "Tz";
|
||||
@@ -12,6 +14,13 @@
|
||||
{
|
||||
Scale = scale;
|
||||
}
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
var currentState = operationContext.GetCurrentState();
|
||||
|
||||
currentState.FontState.HorizontalScaling = Scale;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
@@ -1,5 +1,7 @@
|
||||
namespace UglyToad.Pdf.Graphics.Operations.TextState
|
||||
{
|
||||
using Content;
|
||||
|
||||
internal class SetTextLeading : IGraphicsStateOperation
|
||||
{
|
||||
public const string Symbol = "TL";
|
||||
@@ -13,6 +15,13 @@
|
||||
Leading = leading;
|
||||
}
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
var currentState = operationContext.GetCurrentState();
|
||||
|
||||
currentState.FontState.Leading = Leading;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Leading} {Symbol}";
|
||||
|
@@ -1,5 +1,7 @@
|
||||
namespace UglyToad.Pdf.Graphics.Operations.TextState
|
||||
{
|
||||
using Content;
|
||||
|
||||
internal class SetTextRenderingMode : IGraphicsStateOperation
|
||||
{
|
||||
public const string Symbol = "Tr";
|
||||
@@ -13,6 +15,13 @@
|
||||
Mode = (RenderingMode)mode;
|
||||
}
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
var currentState = operationContext.GetCurrentState();
|
||||
|
||||
currentState.FontState.RenderingMode = Mode;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Mode} {Symbol}";
|
||||
|
@@ -1,5 +1,7 @@
|
||||
namespace UglyToad.Pdf.Graphics.Operations.TextState
|
||||
{
|
||||
using Content;
|
||||
|
||||
internal class SetTextRise : IGraphicsStateOperation
|
||||
{
|
||||
public const string Symbol = "Ts";
|
||||
@@ -12,6 +14,13 @@
|
||||
{
|
||||
Rise = rise;
|
||||
}
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
var currentState = operationContext.GetCurrentState();
|
||||
|
||||
currentState.FontState.Rise = Rise;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
@@ -1,5 +1,7 @@
|
||||
namespace UglyToad.Pdf.Graphics.Operations.TextState
|
||||
{
|
||||
using Content;
|
||||
|
||||
internal class SetWordSpacing : IGraphicsStateOperation
|
||||
{
|
||||
public const string Symbol = "Tw";
|
||||
@@ -13,6 +15,13 @@
|
||||
Spacing = spacing;
|
||||
}
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
var currentState = operationContext.GetCurrentState();
|
||||
|
||||
currentState.FontState.WordSpacing = Spacing;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Spacing} {Symbol}";
|
||||
|
Reference in New Issue
Block a user