Skip to content

Commit c2a2c06

Browse files
authored
Merge pull request #37 from synercoder/features/shape-support
Features/shape support fixes #2
2 parents 9ace674 + a487886 commit c2a2c06

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+3145
-277
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020 Gerard Gunnewijk
3+
Copyright (c) 2021 Gerard Gunnewijk
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This project is licensed under MIT license.
1515
## Specifications used
1616
This library was created using the specifications lay out in ["PDF 32000-1:2008, Document management – Portable document format – Part 1: PDF 1.7"](https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf).
1717

18-
The full specifications are not implemented. This library currently only supports placements of images and setting the different boxes.
18+
The full specifications are not implemented. This library currently only supports placements of images, drawing of vector shapes (CMYK, RGB & gray scale), and setting the different boxes.
1919

2020
## Remarks
2121
Unlike most PDF libraries this library does not create the entire PDF model in memory before writing the PDF to a (file)stream. Most libaries support editing capabilities, because this libary only supports creating files, it was not necessary to keep the PDF model in memory. This results in less memory usage.

samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using Synercoding.FileFormats.Pdf.Extensions;
2+
using Synercoding.FileFormats.Pdf.LowLevel.Graphics;
3+
using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors;
24
using Synercoding.Primitives;
35
using Synercoding.Primitives.Extensions;
6+
using System;
47
using System.IO;
58

69
namespace Synercoding.FileFormats.Pdf.ConsoleTester
@@ -53,6 +56,45 @@ public static void Main(string[] args)
5356
page.AddImage(eyeStream, new Rectangle(offSet, offSet, width + offSet, height + offSet, Unit.Millimeters));
5457
}
5558
})
59+
// Test shape graphics
60+
.AddPage(page =>
61+
{
62+
page.AddShapes(ctx =>
63+
{
64+
ctx.DefaultState(g =>
65+
{
66+
g.LineWidth = 1;
67+
g.Fill = null;
68+
g.Stroke = null;
69+
g.Dash = new Dash()
70+
{
71+
Array = Array.Empty<double>(),
72+
Phase = 0
73+
};
74+
g.MiterLimit = 10;
75+
g.LineCap = LineCapStyle.ButtCap;
76+
g.LineJoin = LineJoinStyle.MiterJoin;
77+
});
78+
79+
ctx.NewPath(g => { g.Fill = PredefinedColors.Red; g.Stroke = PredefinedColors.Black; g.LineWidth = 5; })
80+
.Move(100, 100)
81+
.LineTo(200, 100)
82+
.LineTo(200, 200)
83+
.LineTo(100, 200);
84+
ctx.NewPath(g => { g.Fill = PredefinedColors.Blue; g.Stroke = null; })
85+
.Move(50, 50)
86+
.LineTo(150, 50)
87+
.LineTo(150, 150)
88+
.LineTo(50, 150)
89+
.Close();
90+
ctx.NewPath(g => { g.Fill = null; g.Stroke = PredefinedColors.Yellow; g.LineWidth = 3; g.Dash = new Dash() { Array = new[] { 5d } }; })
91+
.Move(150, 150)
92+
.LineTo(250, 150)
93+
.LineTo(250, 250)
94+
.LineTo(150, 250)
95+
.Close();
96+
});
97+
})
5698
// Test placement using matrix
5799
.AddPage(page =>
58100
{

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
using Synercoding.FileFormats.Pdf.Internals;
2+
using Synercoding.FileFormats.Pdf.LowLevel;
13
using Synercoding.Primitives;
4+
using System;
25
using System.IO;
36

47
namespace Synercoding.FileFormats.Pdf.Extensions
@@ -123,5 +126,30 @@ public static PdfPage AddImage(this PdfPage page, Stream jpgStream, int original
123126
/// <returns>The same <see cref="PdfPage"/> to chain other calls.</returns>
124127
public static PdfPage AddImage(this PdfPage page, Stream jpgStream, int originalWidth, int originalHeight, Rectangle rectangle)
125128
=> page.AddImage(jpgStream, originalWidth, originalHeight, rectangle.AsPlacementMatrix());
129+
130+
/// <summary>
131+
/// Add shapes to the pdf page
132+
/// </summary>
133+
/// <param name="page">The page to add the shapes to</param>
134+
/// <param name="paintAction">The action painting the shapes</param>
135+
/// <returns>The same <see cref="PdfPage"/> to chain other calls.</returns>
136+
public static PdfPage AddShapes(this PdfPage page, Action<IShapeContext> paintAction)
137+
=> page.AddShapes(paintAction, static (action, context) => action(context));
138+
139+
/// <summary>
140+
/// Add shapes to the pdf page
141+
/// </summary>
142+
/// <typeparam name="T">Type of <paramref name="data"/></typeparam>
143+
/// <param name="page">The page to add the shapes to</param>
144+
/// <param name="data">Data that can be passed to the <paramref name="paintAction"/></param>
145+
/// <param name="paintAction">The action painting the shapes</param>
146+
/// <returns>The same <see cref="PdfPage"/> to chain other calls.</returns>
147+
public static PdfPage AddShapes<T>(this PdfPage page, T data, Action<T, IShapeContext> paintAction)
148+
{
149+
using (var context = new ShapeContext(page.ContentStream, page.Resources))
150+
paintAction(data, context);
151+
152+
return page;
153+
}
126154
}
127155
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
using Synercoding.Primitives;
1+
using Synercoding.Primitives;
22

33
namespace Synercoding.FileFormats.Pdf.Extensions
44
{
5+
/// <summary>
6+
/// Extension methods for primitives
7+
/// </summary>
58
public static class PrimitiveExtensions
69
{
10+
/// <summary>
11+
/// Convert a <see cref="Rectangle"/> to a transformation matrix
12+
/// </summary>
13+
/// <param name="rectangle">The <see cref="Rectangle"/> to use</param>
14+
/// <returns>Returns a <see cref="Matrix"/> representing the provided <see cref="Rectangle"/>.</returns>
715
public static Matrix AsPlacementMatrix(this Rectangle rectangle)
816
{
917
rectangle = rectangle.ConvertTo(Unit.Points);
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using Synercoding.Primitives;
2+
3+
namespace Synercoding.FileFormats.Pdf
4+
{
5+
/// <summary>
6+
/// Interface representing a path in the content stream of a page
7+
/// </summary>
8+
public interface IPath
9+
{
10+
/// <summary>
11+
/// Begin a new subpath by moving the current point to the coordinates (<paramref name="x"/>, <paramref name="y"/>),
12+
/// ommitting any connecting line segment. Appends an (m) operator to the content stream
13+
/// </summary>
14+
/// <param name="x">The X coordinate of the move</param>
15+
/// <param name="y">The Y coordinate of the move</param>
16+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
17+
IPath Move(double x, double y);
18+
19+
/// <summary>
20+
/// Begin a new subpath by moving the current point to the coordinates (<paramref name="point"/>),
21+
/// ommitting any connecting line segment. Appends an (m) operator to the content stream
22+
/// </summary>
23+
/// <param name="point">The point to where to move</param>
24+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
25+
IPath Move(Point point);
26+
27+
/// <summary>
28+
/// Add a line (l) operator to the content stream
29+
/// </summary>
30+
/// <param name="x">The X coordinate of the line end point</param>
31+
/// <param name="y">The Y coordinate of the line end point</param>
32+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
33+
IPath LineTo(double x, double y);
34+
35+
/// <summary>
36+
/// Add a line (l) operator to the content stream
37+
/// </summary>
38+
/// <param name="point">The point to where to line to</param>
39+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
40+
IPath LineTo(Point point);
41+
42+
/// <summary>
43+
/// Add a rectangle (re) operator to the content stream
44+
/// </summary>
45+
/// <param name="x">The X coordinate of the rectangle</param>
46+
/// <param name="y">The Y coordinate of the rectangle</param>
47+
/// <param name="width">The width of the rectangle</param>
48+
/// <param name="height">The height of the rectangle</param>
49+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
50+
IPath Rectangle(double x, double y, double width, double height);
51+
52+
/// <summary>
53+
/// Add a rectangle (re) operator to the content stream
54+
/// </summary>
55+
/// <param name="rectangle">The rectangle to add to the content stream</param>
56+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
57+
IPath Rectangle(Rectangle rectangle);
58+
59+
/// <summary>
60+
/// 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"/>),
61+
/// using (<paramref name="cpX1"/>, <paramref name="cpY1"/>) and (<paramref name="cpX2"/>, <paramref name="cpY2"/>) as the Bézier control points.
62+
/// Adds a Cubic Bézier Curve (c) operator to the content stream.
63+
/// </summary>
64+
/// <param name="cpX1">The X coordinate of the first control point</param>
65+
/// <param name="cpY1">The Y coordinate of the first control point</param>
66+
/// <param name="cpX2">The X coordinate of the second control point</param>
67+
/// <param name="cpY2">The Y coordinate of the second control point</param>
68+
/// <param name="finalX">The X coordinate of the endpoint of the curve</param>
69+
/// <param name="finalY">The Y coordinate of the endpoint of the curve</param>
70+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
71+
IPath CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY);
72+
73+
/// <summary>
74+
/// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (<paramref name="final"/>),
75+
/// using (<paramref name="cp1"/>) and (<paramref name="cp2"/>) as the Bézier control points.
76+
/// Adds a Cubic Bézier Curve (c) operator to the content stream.
77+
/// </summary>
78+
/// <param name="cp1">The first control point</param>
79+
/// <param name="cp2">The second control point</param>
80+
/// <param name="final">The endpoint of the curve</param>
81+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
82+
IPath CurveTo(Point cp1, Point cp2, Point final);
83+
84+
/// <summary>
85+
/// 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"/>),
86+
/// using the current point and (<paramref name="cpX2"/>, <paramref name="cpY2"/>) as the Bézier control points.
87+
/// Adds a Cubic Bézier Curve (v) operator to the content stream.
88+
/// </summary>
89+
/// <param name="cpX2">The X coordinate of the second control point</param>
90+
/// <param name="cpY2">The Y coordinate of the second control point</param>
91+
/// <param name="finalX">The X coordinate of the endpoint of the curve</param>
92+
/// <param name="finalY">The Y coordinate of the endpoint of the curve</param>
93+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
94+
IPath CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY);
95+
96+
/// <summary>
97+
/// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (<paramref name="final"/>),
98+
/// using the current point and (<paramref name="cp2"/>) as the Bézier control points.
99+
/// Adds a Cubic Bézier Curve (v) operator to the content stream.
100+
/// </summary>
101+
/// <param name="cp2">The second control point</param>
102+
/// <param name="final">The endpoint of the curve</param>
103+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
104+
IPath CurveToWithStartAnker(Point cp2, Point final);
105+
106+
/// <summary>
107+
/// 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"/>),
108+
/// using (<paramref name="cpX1"/>, <paramref name="cpY1"/>) and (<paramref name="finalX"/>, <paramref name="finalY"/>) as the Bézier control points.
109+
/// Adds a Cubic Bézier Curve (y) operator to the content stream.
110+
/// </summary>
111+
/// <param name="cpX1">The X coordinate of the first control point</param>
112+
/// <param name="cpY1">The Y coordinate of the first control point</param>
113+
/// <param name="finalX">The X coordinate of the endpoint of the curve</param>
114+
/// <param name="finalY">The Y coordinate of the endpoint of the curve</param>
115+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
116+
IPath CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY);
117+
118+
/// <summary>
119+
/// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (<paramref name="final"/>),
120+
/// using (<paramref name="cp1"/>) and (<paramref name="final"/>) as the Bézier control points.
121+
/// Adds a Cubic Bézier Curve (y) operator to the content stream.
122+
/// </summary>
123+
/// <param name="cp1">The first control point</param>
124+
/// <param name="final">The endpoint of the curve</param>
125+
/// <returns>The calling <see cref="IPath"/> to support chaining operations.</returns>
126+
IPath CurveToWithEndAnker(Point cp1, Point final);
127+
128+
/// <summary>
129+
/// Close the <see cref="IPath"/>
130+
/// </summary>
131+
void Close();
132+
}
133+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Synercoding.FileFormats.Pdf.LowLevel.Graphics;
2+
using System;
3+
4+
namespace Synercoding.FileFormats.Pdf
5+
{
6+
/// <summary>
7+
/// Interface representing a context which can be used to draw shapes on a page
8+
/// </summary>
9+
public interface IShapeContext
10+
{
11+
/// <summary>
12+
/// Change the default graphics state
13+
/// </summary>
14+
/// <param name="configureState">Action used to configure the <see cref="GraphicsState"/></param>
15+
/// <returns>The calling <see cref="IShapeContext"/> to support chaining</returns>
16+
IShapeContext DefaultState(Action<GraphicsState> configureState);
17+
18+
/// <summary>
19+
/// Start a new <see cref="IPath"/>
20+
/// </summary>
21+
/// <returns>The new <see cref="IPath"/> object to chain pathing operators</returns>
22+
IPath NewPath();
23+
24+
/// <summary>
25+
/// Start a new <see cref="IPath"/> with a different graphics state
26+
/// </summary>
27+
/// <param name="configureState">The action used to change the <see cref="GraphicsState"/></param>
28+
/// <returns>The new <see cref="IPath"/> object to chain pathing operators</returns>
29+
IPath NewPath(Action<GraphicsState> configureState);
30+
}
31+
}

src/Synercoding.FileFormats.Pdf/Image.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
using SixLabors.ImageSharp;
1+
using SixLabors.ImageSharp;
22
using Synercoding.FileFormats.Pdf.LowLevel;
33
using Synercoding.FileFormats.Pdf.LowLevel.Extensions;
44
using System;
55
using System.IO;
66

77
namespace Synercoding.FileFormats.Pdf
88
{
9-
public class Image : IPdfObject, IDisposable
9+
/// <summary>
10+
/// Class representing an image inside a pdf
11+
/// </summary>
12+
public sealed class Image : IPdfObject, IDisposable
1013
{
1114
private readonly Stream _imageStream;
1215
private readonly int _width;

src/Synercoding.FileFormats.Pdf/LowLevel/ByteSizes.cs renamed to src/Synercoding.FileFormats.Pdf/Internals/ByteSizes.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
using System;
1+
using System;
22
using System.Globalization;
33

4-
namespace Synercoding.FileFormats.Pdf.LowLevel
4+
namespace Synercoding.FileFormats.Pdf.Internals
55
{
66
internal static class ByteSizes
77
{

0 commit comments

Comments
 (0)