Skip to content

Commit 0017f73

Browse files
committed
Correctly preserved the graphic state & some xml comments
1 parent 2fe3f2d commit 0017f73

19 files changed

+433
-968
lines changed

src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,30 @@
22
using System.Threading.Tasks;
33

44
namespace Synercoding.FileFormats.Pdf.Extensions;
5+
6+
/// <summary>
7+
/// Extension class for <see cref="IContentContext{TSelf}"/>
8+
/// </summary>
59
public static class IContentContextExtensions
610
{
11+
/// <summary>
12+
/// Wraps the operations in <paramref name="contentOperations"/> in save state and restore state operators.
13+
/// </summary>
14+
/// <typeparam name="TContext">The context type where this extension applies to.</typeparam>
15+
/// <param name="context">The content context where that will be wrapped.</param>
16+
/// <param name="contentOperations">The operations to be wrapped.</param>
17+
/// <returns>The same <typeparamref name="TContext"/> to enable method chaining.</returns>
718
public static TContext WrapInState<TContext>(this TContext context, Action<TContext> contentOperations)
819
where TContext : IContentContext<TContext>
920
=> context.WrapInState(contentOperations, static (operations, context) => operations(context));
1021

22+
/// <summary>
23+
/// Wraps the operations in <paramref name="contentOperations"/> in save state and restore state operators.
24+
/// </summary>
25+
/// <typeparam name="TContext">The context type where this extension applies to.</typeparam>
26+
/// <param name="context">The content context where that will be wrapped.</param>
27+
/// <param name="contentOperations">The async operations to be wrapped.</param>
28+
/// <returns>Task that resolves to the same <typeparamref name="TContext"/> to enable method chaining.</returns>
1129
public static Task<TContext> WrapInStateAsync<TContext>(this TContext context, Func<TContext, Task> contentOperations)
1230
where TContext : IContentContext<TContext>
1331
=> context.WrapInStateAsync(contentOperations, static (operations, context) => operations(context));

src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ public static IPageContentContext AddImage(this IPageContentContext context, Ima
7171
});
7272
}
7373

74-
public static IPageContentContext AddShapes(this IPageContentContext context, Action<IShapeContext> shapeOperations)
74+
public static IPageContentContext AddShapes(this IPageContentContext context, Action<IShapeContentContext> shapeOperations)
7575
=> context.AddShapes(shapeOperations, static (operations, context) => operations(context));
7676

77-
public static Task<IPageContentContext> AddShapesAsync(this IPageContentContext context, Func<IShapeContext, Task> shapeOperations)
77+
public static Task<IPageContentContext> AddShapesAsync(this IPageContentContext context, Func<IShapeContentContext, Task> shapeOperations)
7878
=> context.AddShapesAsync(shapeOperations, static (operations, context) => operations(context));
7979

8080
public static IPageContentContext AddText(this IPageContentContext context, Action<ITextContentContext> textOperations)
@@ -104,7 +104,7 @@ public static IPageContentContext AddText<T>(this IPageContentContext context, s
104104
var lines = StringHelper.SplitOnNewLines(text).ToArray();
105105

106106
// if no leading parameter is set, and the text spans multiple lines, set textleading to the font size.
107-
if (lines.Length > 1 && context.TextLeading == default)
107+
if (lines.Length > 1 && context.GraphicState.TextLeading == default)
108108
context.SetTextLeading(size);
109109

110110
for (int i = 0; i < lines.Length; i++)

src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
namespace Synercoding.FileFormats.Pdf.Extensions;
55
public static class IShapeContextExtensions
66
{
7-
public static IShapeContext Rectangle(this IShapeContext context, Rectangle rectangle)
7+
public static IShapeContentContext Rectangle(this IShapeContentContext context, Rectangle rectangle)
88
=> context.Rectangle(rectangle.LLX.AsRaw(Unit.Points), rectangle.LLY.AsRaw(Unit.Points), rectangle.Width.AsRaw(Unit.Points), rectangle.Height.AsRaw(Unit.Points));
99
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using Synercoding.FileFormats.Pdf.LowLevel.Colors;
2+
using Synercoding.FileFormats.Pdf.LowLevel.Graphics;
3+
using Synercoding.FileFormats.Pdf.LowLevel.Text;
4+
using System.Linq;
5+
6+
namespace Synercoding.FileFormats.Pdf;
7+
8+
public sealed class GraphicState
9+
{
10+
internal GraphicState()
11+
{
12+
CTM = Matrix.Identity;
13+
FillColor = PredefinedColors.Black;
14+
StrokeColor = PredefinedColors.Black;
15+
LineWidth = 1.0;
16+
LineCap = LineCapStyle.ButtCap;
17+
LineJoin = LineJoinStyle.MiterJoin;
18+
MiterLimit = 10.0;
19+
DashPattern = new Dash();
20+
CharacterSpacing = 0.0;
21+
WordSpacing = 0.0;
22+
HorizontalScaling = 100.0;
23+
TextLeading = 0.0;
24+
Font = null;
25+
FontSize = null;
26+
TextRenderingMode = TextRenderingMode.Fill;
27+
TextRise = 0.0;
28+
}
29+
30+
public Matrix CTM { get; internal set; }
31+
public Color FillColor { get; internal set; }
32+
public Color StrokeColor { get; internal set; }
33+
public double LineWidth { get; internal set; }
34+
public LineCapStyle LineCap { get; internal set; }
35+
public LineJoinStyle LineJoin { get; internal set; }
36+
public double MiterLimit { get; internal set; }
37+
public Dash DashPattern { get; internal set; }
38+
public double CharacterSpacing { get; internal set; }
39+
public double WordSpacing { get; internal set; }
40+
public double HorizontalScaling { get; internal set; }
41+
public double TextLeading { get; internal set; }
42+
public Font? Font { get; internal set; }
43+
public double? FontSize { get; internal set; }
44+
public TextRenderingMode TextRenderingMode { get; internal set; }
45+
public double TextRise { get; internal set; }
46+
47+
internal GraphicState Clone()
48+
{
49+
return new GraphicState()
50+
{
51+
CTM = CTM,
52+
FillColor = FillColor,
53+
StrokeColor = StrokeColor,
54+
LineWidth = LineWidth,
55+
LineCap = LineCap,
56+
LineJoin = LineJoin,
57+
MiterLimit = MiterLimit,
58+
DashPattern = new Dash(DashPattern.Array.ToArray(), DashPattern.Phase),
59+
CharacterSpacing = CharacterSpacing,
60+
WordSpacing = WordSpacing,
61+
HorizontalScaling = HorizontalScaling,
62+
TextLeading = TextLeading,
63+
Font = Font,
64+
FontSize = FontSize,
65+
TextRenderingMode = TextRenderingMode,
66+
TextRise = TextRise
67+
};
68+
}
69+
}
70+

src/Synercoding.FileFormats.Pdf/IContentContext.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,12 @@ public interface IContentContext<TSelf>
1111
{
1212
ContentStream RawContentStream { get; }
1313

14+
GraphicState GraphicState { get; }
15+
1416
TSelf WrapInState<T>(T data, Action<T, TSelf> contentOperations);
1517

1618
Task<TSelf> WrapInStateAsync<T>(T data, Func<T, TSelf, Task> contentOperations);
1719

18-
Matrix CTM { get; }
19-
Color FillColor { get; }
20-
Color StrokeColor { get; }
21-
double LineWidth { get; }
22-
LineCapStyle LineCap { get; }
23-
LineJoinStyle LineJoin { get; }
24-
double MiterLimit { get; }
25-
Dash DashPattern { get; }
26-
2720
TSelf ConcatenateMatrix(Matrix matrix);
2821

2922
TSelf SetStroke(Color stroke);

src/Synercoding.FileFormats.Pdf/IPageContentContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ public interface IPageContentContext : IContentContext<IPageContentContext>
1010
IPageContentContext AddText<T>(T data, Action<T, ITextContentContext> textOperations);
1111
Task<IPageContentContext> AddTextAsync<T>(T data, Func<T, ITextContentContext, Task> textOperations);
1212

13-
IPageContentContext AddShapes<T>(T data, Action<T, IShapeContext> shapeOperations);
14-
Task<IPageContentContext> AddShapesAsync<T>(T data, Func<T, IShapeContext, Task> shapeOperations);
13+
IPageContentContext AddShapes<T>(T data, Action<T, IShapeContentContext> shapeOperations);
14+
Task<IPageContentContext> AddShapesAsync<T>(T data, Func<T, IShapeContentContext, Task> shapeOperations);
1515
}

src/Synercoding.FileFormats.Pdf/IShapeContext.cs renamed to src/Synercoding.FileFormats.Pdf/IShapeContentContext.cs

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,28 @@
22

33
namespace Synercoding.FileFormats.Pdf;
44

5-
public interface IShapeContext : IContentContext<IShapeContext>
5+
/// <summary>
6+
/// Context to enable shape operations on the content
7+
/// </summary>
8+
public interface IShapeContentContext : IContentContext<IShapeContentContext>
69
{
710
/// <summary>
811
/// Begin a new subpath by moving the current point to the coordinates (<paramref name="x"/>, <paramref name="y"/>),
912
/// omitting any connecting line segment. Appends an (m) operator to the content stream
1013
/// </summary>
1114
/// <param name="x">The X coordinate of the move</param>
1215
/// <param name="y">The Y coordinate of the move</param>
13-
/// <returns>The calling <see cref="IShapeContext"/> to support chaining operations.</returns>
14-
IShapeContext Move(double x, double y);
16+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
17+
IShapeContentContext Move(double x, double y);
1518

1619

1720
/// <summary>
1821
/// Add a line (l) operator to the content stream
1922
/// </summary>
2023
/// <param name="x">The X coordinate of the line end point</param>
2124
/// <param name="y">The Y coordinate of the line end point</param>
22-
/// <returns>The calling <see cref="IShapeContext"/> to support chaining operations.</returns>
23-
IShapeContext LineTo(double x, double y);
25+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
26+
IShapeContentContext LineTo(double x, double y);
2427

2528
/// <summary>
2629
/// Add a rectangle (re) operator to the content stream
@@ -29,8 +32,8 @@ public interface IShapeContext : IContentContext<IShapeContext>
2932
/// <param name="y">The Y coordinate of the rectangle</param>
3033
/// <param name="width">The width of the rectangle</param>
3134
/// <param name="height">The height of the rectangle</param>
32-
/// <returns>The calling <see cref="IShapeContext"/> to support chaining operations.</returns>
33-
IShapeContext Rectangle(double x, double y, double width, double height);
35+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
36+
IShapeContentContext Rectangle(double x, double y, double width, double height);
3437

3538
/// <summary>
3639
/// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (<paramref name="finalX"/>, <paramref name="finalY"/>),
@@ -43,8 +46,8 @@ public interface IShapeContext : IContentContext<IShapeContext>
4346
/// <param name="cpY2">The Y coordinate of the second control point</param>
4447
/// <param name="finalX">The X coordinate of the endpoint of the curve</param>
4548
/// <param name="finalY">The Y coordinate of the endpoint of the curve</param>
46-
/// <returns>The calling <see cref="IShapeContext"/> to support chaining operations.</returns>
47-
IShapeContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY);
49+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
50+
IShapeContentContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY);
4851

4952
/// <summary>
5053
/// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (<paramref name="finalX"/>, <paramref name="finalY"/>),
@@ -55,8 +58,8 @@ public interface IShapeContext : IContentContext<IShapeContext>
5558
/// <param name="cpY2">The Y coordinate of the second control point</param>
5659
/// <param name="finalX">The X coordinate of the endpoint of the curve</param>
5760
/// <param name="finalY">The Y coordinate of the endpoint of the curve</param>
58-
/// <returns>The calling <see cref="IShapeContext"/> to support chaining operations.</returns>
59-
IShapeContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY);
61+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
62+
IShapeContentContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY);
6063

6164
/// <summary>
6265
/// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (<paramref name="finalX"/>, <paramref name="finalY"/>),
@@ -67,39 +70,59 @@ public interface IShapeContext : IContentContext<IShapeContext>
6770
/// <param name="cpY1">The Y coordinate of the first control point</param>
6871
/// <param name="finalX">The X coordinate of the endpoint of the curve</param>
6972
/// <param name="finalY">The Y coordinate of the endpoint of the curve</param>
70-
/// <returns>The calling <see cref="IShapeContext"/> to support chaining operations.</returns>
71-
IShapeContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY);
73+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
74+
IShapeContentContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY);
7275

7376
/// <summary>
74-
/// Close the active subpath in this <see cref="IShapeContext"/>
77+
/// Close the active subpath in this <see cref="IShapeContentContext"/>
7578
/// </summary>
76-
IShapeContext CloseSubPath();
79+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
80+
IShapeContentContext CloseSubPath();
7781

7882
/// <summary>
79-
///
83+
/// Mark the current path to be used as a clipping mask.
8084
/// </summary>
81-
/// <param name="fillRule"></param>
82-
/// <returns></returns>
83-
IShapeContext MarkPathForClipping(FillRule fillRule);
85+
/// <param name="fillRule">The <see cref="FillRule"/> to use</param>
86+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
87+
IShapeContentContext MarkPathForClipping(FillRule fillRule);
8488

85-
IShapeContext Stroke();
89+
/// <summary>
90+
/// Stroke the current path
91+
/// </summary>
92+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
93+
IShapeContentContext Stroke();
8694

8795
/// <summary>
8896
/// Close and stroke the path.
8997
/// </summary>
9098
/// <remarks>This operator has the same effect as the sequence <see cref="CloseSubPath"/>() and then <see cref="Stroke"/>().</remarks>
91-
/// <returns></returns>
92-
IShapeContext CloseSubPathAndStroke();
99+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
100+
IShapeContentContext CloseSubPathAndStroke();
93101

94-
IShapeContext Fill(FillRule fillRule);
102+
/// <summary>
103+
/// Fill the current path using <paramref name="fillRule"/> as the fill rule.
104+
/// </summary>
105+
/// <param name="fillRule">The <see cref="FillRule"/> to use</param>
106+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
107+
IShapeContentContext Fill(FillRule fillRule);
95108

96-
IShapeContext FillThenStroke(FillRule fillRule);
109+
/// <summary>
110+
/// Fill the current path using <paramref name="fillRule"/> as the fill rule, and then stroke the path.
111+
/// </summary>
112+
/// <param name="fillRule">The <see cref="FillRule"/> to use</param>
113+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
114+
IShapeContentContext FillThenStroke(FillRule fillRule);
97115

98-
IShapeContext CloseSubPathFillStroke(FillRule fillRule);
116+
/// <summary>
117+
/// Close the current path, then fill the current path using <paramref name="fillRule"/> as the fill rule, and then stroke the path.
118+
/// </summary>
119+
/// <param name="fillRule">The <see cref="FillRule"/> to use</param>
120+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
121+
IShapeContentContext CloseSubPathFillStroke(FillRule fillRule);
99122

100123
/// <summary>
101124
/// End the path object without filling or stroking it. This operator is a path-painting no-op, used primarily for the side effect of changing the current clipping path.
102125
/// </summary>
103-
/// <returns>The calling <see cref="IShapeContext"/> to support chaining operations.</returns>
104-
IShapeContext EndPathNoStrokeNoFill();
126+
/// <returns>The calling <see cref="IShapeContentContext"/> to support chaining operations.</returns>
127+
IShapeContentContext EndPathNoStrokeNoFill();
105128
}

src/Synercoding.FileFormats.Pdf/ITextContentContext.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
using Synercoding.FileFormats.Pdf.LowLevel.Text;
1+
using Synercoding.FileFormats.Pdf.LowLevel.Text;
22

33
namespace Synercoding.FileFormats.Pdf;
44

55
public interface ITextContentContext : IContentContext<ITextContentContext>
66
{
7-
double CharacterSpacing { get; }
8-
double WordSpacing { get; }
9-
double HorizontalScaling { get; }
10-
double TextLeading { get; }
11-
Font? Font { get; }
12-
double? FontSize { get; }
13-
TextRenderingMode TextRenderingMode { get; }
14-
double TextRise { get; }
15-
167
ITextContentContext SetCharacterSpacing(double spacing);
178
ITextContentContext SetWordSpacing(double spacing);
189
ITextContentContext SetHorizontalScaling(double scaling);

src/Synercoding.FileFormats.Pdf/Image.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,14 @@ internal Image(PdfReference id, Stream jpgStream, int width, int height, PdfName
7878
/// </summary>
7979
public int Height { get; }
8080

81+
/// <summary>
82+
/// The name of the colorspace used in this <see cref="Image"/>
83+
/// </summary>
8184
public PdfName ColorSpace { get; }
8285

86+
/// <summary>
87+
/// The decode array used in this <see cref="Image"/>
88+
/// </summary>
8389
public double[] DecodeArray { get; }
8490

8591
/// <inheritdoc />

0 commit comments

Comments
 (0)