Skip to content

Commit d19f39f

Browse files
authored
Merge pull request #63 from synercoder/features/support-overprinting
Features/support overprinting
2 parents ad15301 + d09933f commit d19f39f

File tree

18 files changed

+166
-19
lines changed

18 files changed

+166
-19
lines changed

.github/workflows/dotnet-core.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
run: dotnet pack -v normal -c Release --no-restore --include-symbols --include-source -p:SymbolPackageFormat=snupkg -p:PackageVersion=1.0.0-pre+$GITHUB_RUN_ID src/$PROJECT_NAME/$PROJECT_NAME.*proj
4646
- name: Upload Artifact
4747
if: matrix.os == 'ubuntu-latest'
48-
uses: actions/upload-artifact@v2
48+
uses: actions/upload-artifact@v4
4949
with:
5050
name: nupkg
5151
path: ./artifacts/pkg/Release/${{ env.PROJECT_NAME }}.*.nupkg
@@ -55,7 +55,7 @@ jobs:
5555
runs-on: ubuntu-latest
5656
steps:
5757
- name: Download Artifact
58-
uses: actions/download-artifact@v1
58+
uses: actions/download-artifact@v4
5959
with:
6060
name: nupkg
6161
- name: Push to GitHub Feed
@@ -90,4 +90,4 @@ jobs:
9090
done
9191
- name: Push to NuGet Feed
9292
if: ${{ env.NUGET_FEED }} != ''
93-
run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --skip-duplicate --api-key $NUGET_KEY
93+
run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --skip-duplicate --api-key $NUGET_KEY

Directory.Build.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Update="SixLabors.ImageSharp" Version="3.0.*" />
10+
<PackageReference Update="SixLabors.ImageSharp" Version="3.1.*" />
1111
</ItemGroup>
1212

1313
<!-- Disable auto imports/usings from ImageSharp -->

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ public static void Main(string[] args)
216216

217217
page.Content.AddShapes(trimBox, static (trim, context) =>
218218
{
219+
context.SetExtendedGraphicsState(new ExtendedGraphicsState()
220+
{
221+
Overprint = true
222+
});
219223
context.SetStroke(new SpotColor(new Separation(LowLevel.PdfName.Get("CutContour"), PredefinedColors.Magenta), 1));
220224
context.Rectangle(trim);
221225
context.Stroke();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace Synercoding.FileFormats.Pdf;
2+
3+
/// <summary>
4+
/// Class representing an ExtGState dictionary.
5+
/// </summary>
6+
public sealed record class ExtendedGraphicsState
7+
{
8+
/// <summary>
9+
/// A flag specifying whether to apply overprint.
10+
/// There are two separate overprint parameters: one for stroking and one for all other painting operations.
11+
/// Specifying an <see cref="Overprint"/> entry sets both parameters
12+
/// unless there is also an <see cref="OverprintNonStroking"/> entry in the same graphics state parameter dictionary,
13+
/// in which case the <see cref="Overprint"/> entry sets only the overprint parameter for stroking.
14+
/// </summary>
15+
public bool? Overprint { get; set; }
16+
17+
/// <summary>
18+
/// A flag specifying whether to apply overprint for painting operations other than stroking.
19+
/// If this entry is absent, the <see cref="Overprint"/> entry, if any, sets this parameter.
20+
/// </summary>
21+
public bool? OverprintNonStroking { get; set; }
22+
}
23+

src/Synercoding.FileFormats.Pdf/GraphicState.cs renamed to src/Synercoding.FileFormats.Pdf/GraphicsState.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ namespace Synercoding.FileFormats.Pdf;
77
/// <summary>
88
/// Class representing the grahpic state of a PDF at a certain moment in time.
99
/// </summary>
10-
public sealed class GraphicState
10+
public sealed class GraphicsState
1111
{
12-
internal GraphicState()
12+
internal GraphicsState()
1313
{
1414
CTM = Matrix.Identity;
1515
Fill = PredefinedColors.Black;
@@ -111,9 +111,9 @@ internal GraphicState()
111111
/// </summary>
112112
public double TextRise { get; internal set; }
113113

114-
internal GraphicState Clone()
114+
internal GraphicsState Clone()
115115
{
116-
return new GraphicState()
116+
return new GraphicsState()
117117
{
118118
CTM = CTM,
119119
Fill = Fill,

src/Synercoding.FileFormats.Pdf/IContentContext.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public interface IContentContext<TSelf>
1919
/// <summary>
2020
/// Represents the current graphic state
2121
/// </summary>
22-
GraphicState GraphicState { get; }
22+
GraphicsState GraphicState { get; }
2323

2424
/// <summary>
2525
/// Wrap the <paramref name="contentOperations"/> in save and restore state operators
@@ -40,7 +40,7 @@ public interface IContentContext<TSelf>
4040
Task<TSelf> WrapInStateAsync<T>(T data, Func<T, TSelf, Task> contentOperations);
4141

4242
/// <summary>
43-
/// Concatenate a matrix to <see cref="GraphicState.CTM"/>
43+
/// Concatenate a matrix to <see cref="GraphicsState.CTM"/>
4444
/// </summary>
4545
/// <param name="matrix">The matrix to concat</param>
4646
/// <returns>This <see cref="IContentContext{TSelf}"/> to enable chaining operations</returns>
@@ -94,5 +94,12 @@ public interface IContentContext<TSelf>
9494
/// <param name="dashPattern">The dash pattern to set</param>
9595
/// <returns>This <see cref="IContentContext{TSelf}"/> to enable chaining operations</returns>
9696
TSelf SetDashPattern(Dash dashPattern);
97+
98+
/// <summary>
99+
/// Set an extended graphics state (ExtGState) dictionary.
100+
/// </summary>
101+
/// <param name="extendedGraphicsState">The state to apply.</param>
102+
/// <returns>This <see cref="IContentContext{TSelf}"/> to enable chaining operations</returns>
103+
TSelf SetExtendedGraphicsState(ExtendedGraphicsState extendedGraphicsState);
97104
}
98105

src/Synercoding.FileFormats.Pdf/ITextContentContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public interface ITextContentContext : IContentContext<ITextContentContext>
101101
ITextContentContext ShowTextOnNextLine(string text);
102102

103103
/// <summary>
104-
/// Operation to show text on the next line and setting the <see cref="GraphicState.WordSpacing"/> and <see cref="GraphicState.CharacterSpacing"/>
104+
/// Operation to show text on the next line and setting the <see cref="GraphicsState.WordSpacing"/> and <see cref="GraphicsState.CharacterSpacing"/>
105105
/// </summary>
106106
/// <param name="text">The text to show</param>
107107
/// <param name="wordSpacing">The word spacing to set</param>

src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ namespace Synercoding.FileFormats.Pdf.Internals;
66

77
internal class PageContentContext : IPageContentContext
88
{
9-
public PageContentContext(ContentStream contentStream, GraphicState graphicState)
9+
public PageContentContext(ContentStream contentStream, GraphicsState graphicState)
1010
{
1111
RawContentStream = contentStream;
1212
GraphicState = graphicState;
1313
}
1414

1515
public ContentStream RawContentStream { get; }
1616

17-
public GraphicState GraphicState { get; }
17+
public GraphicsState GraphicState { get; }
1818

1919
public IPageContentContext AddImage(Image image)
2020
{
@@ -155,4 +155,11 @@ public async Task<IPageContentContext> AddShapesAsync<T>(T data, Func<T, IShapeC
155155

156156
return this;
157157
}
158+
159+
public IPageContentContext SetExtendedGraphicsState(ExtendedGraphicsState extendedGraphicsState)
160+
{
161+
RawContentStream.SetExtendedGraphicsState(extendedGraphicsState);
162+
163+
return this;
164+
}
158165
}

src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ namespace Synercoding.FileFormats.Pdf.Internals;
66

77
internal class ShapesContentContext : IShapeContentContext
88
{
9-
public ShapesContentContext(ContentStream contentStream, GraphicState graphicState)
9+
public ShapesContentContext(ContentStream contentStream, GraphicsState graphicState)
1010
{
1111
RawContentStream = contentStream;
1212
GraphicState = graphicState;
1313
}
1414

1515
public ContentStream RawContentStream { get; }
1616

17-
public GraphicState GraphicState { get; }
17+
public GraphicsState GraphicState { get; }
1818

1919
public IShapeContentContext ConcatenateMatrix(Matrix matrix)
2020
{
@@ -204,4 +204,11 @@ public IShapeContentContext EndPathNoStrokeNoFill()
204204

205205
return this;
206206
}
207+
208+
public IShapeContentContext SetExtendedGraphicsState(ExtendedGraphicsState extendedGraphicsState)
209+
{
210+
RawContentStream.SetExtendedGraphicsState(extendedGraphicsState);
211+
212+
return this;
213+
}
207214
}

src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ namespace Synercoding.FileFormats.Pdf.Internals;
77

88
internal class TextContentContext : ITextContentContext
99
{
10-
public TextContentContext(ContentStream contentStream, GraphicState graphicState)
10+
public TextContentContext(ContentStream contentStream, GraphicsState graphicState)
1111
{
1212
RawContentStream = contentStream;
1313
GraphicState = graphicState;
1414
}
1515

1616
public ContentStream RawContentStream { get; }
1717

18-
public GraphicState GraphicState { get; }
18+
public GraphicsState GraphicState { get; }
1919

2020
public ITextContentContext ConcatenateMatrix(Matrix matrix)
2121
{
@@ -225,4 +225,11 @@ public ITextContentContext ShowTextOnNextLine(string text, double wordSpacing, d
225225

226226
return this;
227227
}
228+
229+
public ITextContentContext SetExtendedGraphicsState(ExtendedGraphicsState extendedGraphicsState)
230+
{
231+
RawContentStream.SetExtendedGraphicsState(extendedGraphicsState);
232+
233+
return this;
234+
}
228235
}

src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,20 @@ public ContentStream Paint(PdfName resource)
387387
return this;
388388
}
389389

390+
/// <summary>
391+
/// Set an extended graphics state (ExtGState) dictionary using a gs operator..
392+
/// </summary>
393+
/// <param name="state">The state to apply.</param>
394+
/// <returns>The <see cref="ContentStream"/> to support chaining operations.</returns>
395+
public ContentStream SetExtendedGraphicsState(ExtendedGraphicsState state)
396+
{
397+
var name = Resources.AddExtendedGraphicsState(state);
398+
399+
InnerStream.Write(name).Space().Write("gs").NewLine();
400+
401+
return this;
402+
}
403+
390404
/// <summary>
391405
/// Write the operator (m) to the stream
392406
/// </summary>

src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public enum LineJoinStyle
99
/// The outer edges of the strokes for the two segments shall be extended until they meet at an angle.
1010
/// </summary>
1111
/// <remarks>
12-
/// If the segments meet at too sharp an angle (see <see cref="GraphicState.MiterLimit"/>), a bevel join shall be used instead.
12+
/// If the segments meet at too sharp an angle (see <see cref="GraphicsState.MiterLimit"/>), a bevel join shall be used instead.
1313
/// </remarks>
1414
MiterJoin = 0,
1515
/// <summary>

src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ internal sealed class PageResources : IDisposable
99
{
1010
private const string PREFIX_IMAGE = "Im";
1111
private const string PREFIX_SEPARATION = "Sep";
12+
private const string PREFIX_EXTGSTATE = "ExGs";
1213

1314
private readonly TableBuilder _tableBuilder;
1415
private readonly Map<PdfName, Image> _images;
1516
private readonly Dictionary<Separation, (PdfName Name, PdfReference Id)> _separations;
1617
private readonly Dictionary<Type1StandardFont, PdfReference> _standardFonts;
18+
private readonly Dictionary<ExtendedGraphicsState, (PdfName Name, PdfReference Id)> _extendedGraphicsStates;
1719

20+
private int _stateCounter = 0;
1821
private int _separationCounter = 0;
1922
private int _imageCounter = 0;
2023

@@ -24,11 +27,15 @@ internal PageResources(TableBuilder tableBuilder)
2427
_images = new Map<PdfName, Image>();
2528
_separations = new Dictionary<Separation, (PdfName Name, PdfReference Id)>();
2629
_standardFonts = new Dictionary<Type1StandardFont, PdfReference>();
30+
_extendedGraphicsStates = new Dictionary<ExtendedGraphicsState, (PdfName Name, PdfReference Id)>();
2731
}
2832

2933
public IReadOnlyDictionary<PdfName, Image> Images
3034
=> _images.Forward;
3135

36+
public IReadOnlyDictionary<ExtendedGraphicsState, (PdfName Name, PdfReference Id)> ExtendedGraphicsStates
37+
=> _extendedGraphicsStates;
38+
3239
internal IReadOnlyDictionary<Separation, (PdfName Name, PdfReference Id)> SeparationReferences
3340
=> _separations;
3441

@@ -105,4 +112,16 @@ internal PdfName AddSeparation(Separation separation)
105112

106113
return name;
107114
}
115+
116+
internal PdfName AddExtendedGraphicsState(ExtendedGraphicsState extendedGraphicsState)
117+
{
118+
if (_extendedGraphicsStates.TryGetValue(extendedGraphicsState, out var tuple))
119+
return tuple.Name;
120+
121+
var key = PREFIX_EXTGSTATE + Interlocked.Increment(ref _stateCounter).ToString().PadLeft(6, '0');
122+
var name = PdfName.Get(key);
123+
_extendedGraphicsStates[extendedGraphicsState] = (name, _tableBuilder.ReserveId());
124+
125+
return name;
126+
}
108127
}

src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,18 @@ public ObjectStream Write(PdfPage page)
159159
}
160160
}));
161161
}
162+
163+
if (resources.ExtendedGraphicsStates.Count != 0)
164+
{
165+
stream.Write(PdfName.Get("ExtGState"), resources.ExtendedGraphicsStates.Values, static (extGStates, stream) => stream.Dictionary(extGStates, static (extendedGStates, dict) =>
166+
{
167+
foreach (var (name, reference) in extendedGStates)
168+
{
169+
dict.Write(name, reference);
170+
}
171+
}));
172+
}
173+
162174
}));
163175

164176
// Content stream
@@ -217,6 +229,22 @@ public ObjectStream Write(PdfReference reference, Separation separation)
217229
return this;
218230
}
219231

232+
public ObjectStream Write(PdfReference reference, ExtendedGraphicsState state)
233+
{
234+
if (!_tableBuilder.TrySetPosition(reference, InnerStream.Position))
235+
return this;
236+
237+
_indirectDictionary(reference, state, static (state, dict) =>
238+
{
239+
if (state.Overprint.HasValue)
240+
dict.Write(PdfName.Get("OP"), state.Overprint.Value);
241+
if (state.OverprintNonStroking.HasValue)
242+
dict.Write(PdfName.Get("op"), state.OverprintNonStroking.Value);
243+
});
244+
245+
return this;
246+
}
247+
220248
private void _indirectDictionary<T>(PdfReference reference, T data, Action<T, PdfDictionary> dictionaryAction)
221249
{
222250
InnerStream

src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,22 @@ public PdfDictionary Write(PdfName key, int value)
146146
return this;
147147
}
148148

149+
/// <summary>
150+
/// Write a boolean to the dictionary
151+
/// </summary>
152+
/// <param name="key">The key of the item in the dictionary</param>
153+
/// <param name="value">The boolean to write</param>
154+
/// <returns>The <see cref="PdfDictionary"/> to support chaining operations.</returns>
155+
public PdfDictionary Write(PdfName key, bool value)
156+
{
157+
_stream
158+
.Write(key)
159+
.Space()
160+
.Write(value);
161+
162+
return this;
163+
}
164+
149165
/// <summary>
150166
/// Write a text to the dictionary
151167
/// </summary>

src/Synercoding.FileFormats.Pdf/LowLevel/PdfStream.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ public PdfStream Write(char c)
5656
return WriteByte((byte)( c & 0xFF ));
5757
}
5858

59+
/// <summary>
60+
/// Write a <see cref="bool"/> to the stream
61+
/// </summary>
62+
/// <param name="b">The boolean to write.</param>
63+
/// <returns>The calling <see cref="PdfStream"/> to support chaining operations.</returns>
64+
public PdfStream Write(bool b)
65+
{
66+
return b
67+
? Write("true")
68+
: Write("false");
69+
}
70+
5971
/// <summary>
6072
/// Write a <see cref="int"/> to the stream
6173
/// </summary>

src/Synercoding.FileFormats.Pdf/PdfPage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ internal PdfPage(TableBuilder tableBuilder, PageTree parent)
2626
Resources = new PageResources(_tableBuilder);
2727
var contentStream = new ContentStream(tableBuilder.ReserveId(), Resources);
2828

29-
Content = new PageContentContext(contentStream, new GraphicState());
29+
Content = new PageContentContext(contentStream, new GraphicsState());
3030
}
3131

3232
internal PdfReference Parent

0 commit comments

Comments
 (0)