Skip to content

Commit 26d55e0

Browse files
committed
Add cross-cutting Watermark, Stamp, Rotation, and Split options
Introduce four new cross-cutting facets on BuildRequestBase, available on all request types: RotationOptions, SplitOptions, WatermarkOptions, and StampOptions. Add DDD value objects: RotationAngle (90/180/270), PageRanges (validated format like "1-3,5"), OverlaySource enum (text/image/pdf), and SplitMode enum (intervals/pages). All enforce domain constraints at creation time. Wire into BaseBuilder with SetRotationOptions, SetSplitOptions, SetWatermarkOptions, and SetStampOptions fluent builder methods.
1 parent 09b6c02 commit 26d55e0

17 files changed

Lines changed: 1019 additions & 1 deletion

File tree

src/Gotenberg.Sharp.Api.Client/Domain/Builders/BaseBuilder.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,63 @@ public TBuilder SetPdfOutputOptions(PdfOutputOptions options)
8888
return (TBuilder)this;
8989
}
9090

91+
/// <summary>
92+
/// Configures rotation options for the resulting PDF.
93+
/// </summary>
94+
public TBuilder SetRotationOptions(Action<RotationOptionsBuilder> action)
95+
{
96+
if (action == null) throw new ArgumentNullException(nameof(action));
97+
98+
this.Request.RotationOptions ??= new RotationOptions();
99+
100+
action(new RotationOptionsBuilder(this.Request.RotationOptions));
101+
102+
return (TBuilder)this;
103+
}
104+
105+
/// <summary>
106+
/// Configures split options for the resulting PDF.
107+
/// When splitting returns multiple files, Gotenberg returns a ZIP.
108+
/// </summary>
109+
public TBuilder SetSplitOptions(Action<SplitOptionsBuilder> action)
110+
{
111+
if (action == null) throw new ArgumentNullException(nameof(action));
112+
113+
this.Request.SplitOptions ??= new SplitOptions();
114+
115+
action(new SplitOptionsBuilder(this.Request.SplitOptions));
116+
117+
return (TBuilder)this;
118+
}
119+
120+
/// <summary>
121+
/// Configures watermark options (background overlay) for the resulting PDF.
122+
/// </summary>
123+
public TBuilder SetWatermarkOptions(Action<WatermarkOptionsBuilder> action)
124+
{
125+
if (action == null) throw new ArgumentNullException(nameof(action));
126+
127+
this.Request.WatermarkOptions ??= new WatermarkOptions();
128+
129+
action(new WatermarkOptionsBuilder(this.Request.WatermarkOptions));
130+
131+
return (TBuilder)this;
132+
}
133+
134+
/// <summary>
135+
/// Configures stamp options (foreground overlay) for the resulting PDF.
136+
/// </summary>
137+
public TBuilder SetStampOptions(Action<StampOptionsBuilder> action)
138+
{
139+
if (action == null) throw new ArgumentNullException(nameof(action));
140+
141+
this.Request.StampOptions ??= new StampOptions();
142+
143+
action(new StampOptionsBuilder(this.Request.StampOptions));
144+
145+
return (TBuilder)this;
146+
}
147+
91148
/// <summary>
92149
/// Builds the request synchronously. Use when all content is already in memory (no async operations).
93150
/// </summary>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2019-2026 Chris Mohan, Jaben Cargman
2+
// and GotenbergSharpApiClient Contributors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
using Gotenberg.Sharp.API.Client.Domain.ValueObjects;
17+
18+
namespace Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
19+
20+
public sealed class RotationOptionsBuilder
21+
{
22+
private readonly RotationOptions _options;
23+
24+
internal RotationOptionsBuilder(RotationOptions options)
25+
{
26+
_options = options;
27+
}
28+
29+
public RotationOptionsBuilder SetAngle(RotationAngle angle)
30+
{
31+
_options.RotateAngle = angle ?? throw new ArgumentNullException(nameof(angle));
32+
return this;
33+
}
34+
35+
public RotationOptionsBuilder SetAngle(int degrees)
36+
{
37+
return SetAngle(RotationAngle.Create(degrees));
38+
}
39+
40+
public RotationOptionsBuilder SetPages(PageRanges pages)
41+
{
42+
_options.RotatePages = pages ?? throw new ArgumentNullException(nameof(pages));
43+
return this;
44+
}
45+
46+
public RotationOptionsBuilder SetPages(string pages)
47+
{
48+
return SetPages(PageRanges.Create(pages));
49+
}
50+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2019-2026 Chris Mohan, Jaben Cargman
2+
// and GotenbergSharpApiClient Contributors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
using Gotenberg.Sharp.API.Client.Domain.ValueObjects;
17+
18+
namespace Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
19+
20+
public sealed class SplitOptionsBuilder
21+
{
22+
private readonly SplitOptions _options;
23+
24+
internal SplitOptionsBuilder(SplitOptions options)
25+
{
26+
_options = options;
27+
}
28+
29+
public SplitOptionsBuilder SetMode(SplitMode mode)
30+
{
31+
_options.Mode = mode;
32+
return this;
33+
}
34+
35+
public SplitOptionsBuilder SetSpan(string span)
36+
{
37+
if (string.IsNullOrWhiteSpace(span))
38+
throw new ArgumentException("Split span must not be null or empty.", nameof(span));
39+
40+
_options.Span = span;
41+
return this;
42+
}
43+
44+
public SplitOptionsBuilder SetUnify(bool unify = true)
45+
{
46+
_options.Unify = unify;
47+
return this;
48+
}
49+
50+
/// <summary>
51+
/// Configures interval-based splitting (e.g., split every N pages).
52+
/// </summary>
53+
public SplitOptionsBuilder SplitByIntervals(string span)
54+
{
55+
return SetMode(SplitMode.Intervals).SetSpan(span);
56+
}
57+
58+
/// <summary>
59+
/// Configures page-based splitting (e.g., extract specific page ranges).
60+
/// </summary>
61+
public SplitOptionsBuilder SplitByPages(string span, bool unify = false)
62+
{
63+
return SetMode(SplitMode.Pages).SetSpan(span).SetUnify(unify);
64+
}
65+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2019-2026 Chris Mohan, Jaben Cargman
2+
// and GotenbergSharpApiClient Contributors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
using Gotenberg.Sharp.API.Client.Domain.ValueObjects;
17+
18+
using Newtonsoft.Json.Linq;
19+
20+
namespace Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
21+
22+
public sealed class StampOptionsBuilder
23+
{
24+
private readonly StampOptions _options;
25+
26+
internal StampOptionsBuilder(StampOptions options)
27+
{
28+
_options = options;
29+
}
30+
31+
public StampOptionsBuilder SetSource(OverlaySource source)
32+
{
33+
_options.Source = source;
34+
return this;
35+
}
36+
37+
public StampOptionsBuilder SetExpression(string expression)
38+
{
39+
if (string.IsNullOrWhiteSpace(expression))
40+
throw new ArgumentException("Expression must not be null or empty.", nameof(expression));
41+
42+
_options.Expression = expression;
43+
return this;
44+
}
45+
46+
public StampOptionsBuilder SetPages(PageRanges pages)
47+
{
48+
_options.Pages = pages ?? throw new ArgumentNullException(nameof(pages));
49+
return this;
50+
}
51+
52+
public StampOptionsBuilder SetPages(string pages)
53+
{
54+
return SetPages(PageRanges.Create(pages));
55+
}
56+
57+
public StampOptionsBuilder SetOptions(JObject options)
58+
{
59+
_options.Options = options ?? throw new ArgumentNullException(nameof(options));
60+
return this;
61+
}
62+
63+
/// <summary>
64+
/// Convenience method for a text stamp.
65+
/// </summary>
66+
public StampOptionsBuilder SetTextStamp(string text, string? pages = null)
67+
{
68+
SetSource(OverlaySource.Text);
69+
SetExpression(text);
70+
71+
if (pages != null)
72+
SetPages(pages);
73+
74+
return this;
75+
}
76+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2019-2026 Chris Mohan, Jaben Cargman
2+
// and GotenbergSharpApiClient Contributors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
using Gotenberg.Sharp.API.Client.Domain.ValueObjects;
17+
18+
using Newtonsoft.Json.Linq;
19+
20+
namespace Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
21+
22+
public sealed class WatermarkOptionsBuilder
23+
{
24+
private readonly WatermarkOptions _options;
25+
26+
internal WatermarkOptionsBuilder(WatermarkOptions options)
27+
{
28+
_options = options;
29+
}
30+
31+
public WatermarkOptionsBuilder SetSource(OverlaySource source)
32+
{
33+
_options.Source = source;
34+
return this;
35+
}
36+
37+
public WatermarkOptionsBuilder SetExpression(string expression)
38+
{
39+
if (string.IsNullOrWhiteSpace(expression))
40+
throw new ArgumentException("Expression must not be null or empty.", nameof(expression));
41+
42+
_options.Expression = expression;
43+
return this;
44+
}
45+
46+
public WatermarkOptionsBuilder SetPages(PageRanges pages)
47+
{
48+
_options.Pages = pages ?? throw new ArgumentNullException(nameof(pages));
49+
return this;
50+
}
51+
52+
public WatermarkOptionsBuilder SetPages(string pages)
53+
{
54+
return SetPages(PageRanges.Create(pages));
55+
}
56+
57+
public WatermarkOptionsBuilder SetOptions(JObject options)
58+
{
59+
_options.Options = options ?? throw new ArgumentNullException(nameof(options));
60+
return this;
61+
}
62+
63+
/// <summary>
64+
/// Convenience method for a text watermark.
65+
/// </summary>
66+
public WatermarkOptionsBuilder SetTextWatermark(string text, string? pages = null)
67+
{
68+
SetSource(OverlaySource.Text);
69+
SetExpression(text);
70+
71+
if (pages != null)
72+
SetPages(pages);
73+
74+
return this;
75+
}
76+
}

src/Gotenberg.Sharp.Api.Client/Domain/Requests/BuildRequestBase.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,26 @@ public abstract class BuildRequestBase
2828
/// </summary>
2929
public PdfOutputOptions? PdfOutputOptions { get; set; }
3030

31+
/// <summary>
32+
/// Cross-cutting rotation options (angle and page ranges).
33+
/// </summary>
34+
public RotationOptions? RotationOptions { get; set; }
35+
36+
/// <summary>
37+
/// Cross-cutting split options (mode, span, and unify).
38+
/// </summary>
39+
public SplitOptions? SplitOptions { get; set; }
40+
41+
/// <summary>
42+
/// Cross-cutting watermark options (background overlay).
43+
/// </summary>
44+
public WatermarkOptions? WatermarkOptions { get; set; }
45+
46+
/// <summary>
47+
/// Cross-cutting stamp options (foreground overlay).
48+
/// </summary>
49+
public StampOptions? StampOptions { get; set; }
50+
3151
protected abstract string ApiPath { get; }
3252

3353
private const string _dispositionType = Constants.HttpContent.Disposition.Types.FormData;
@@ -43,7 +63,11 @@ internal static StringContent CreateFormDataItem<T>(T value, string fieldName)
4363

4464
protected virtual IEnumerable<HttpContent> ToHttpContent()
4565
{
46-
return this.PdfOutputOptions.IfNullEmptyContent();
66+
return this.PdfOutputOptions.IfNullEmptyContent()
67+
.Concat(this.RotationOptions.IfNullEmptyContent())
68+
.Concat(this.SplitOptions.IfNullEmptyContent())
69+
.Concat(this.WatermarkOptions.IfNullEmptyContent())
70+
.Concat(this.StampOptions.IfNullEmptyContent());
4771
}
4872

4973
protected virtual void Validate()

src/Gotenberg.Sharp.Api.Client/Domain/Requests/Facets/FacetBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
using System.Globalization;
1717

18+
using Gotenberg.Sharp.API.Client.Domain.ValueObjects;
19+
1820
namespace Gotenberg.Sharp.API.Client.Domain.Requests.Facets;
1921

2022
public abstract class FacetBase : IConvertToHttpContent
@@ -78,6 +80,8 @@ public virtual IEnumerable<HttpContent> ToHttpContent()
7880
LibrePdfFormats format => format.ToFormDataValue(),
7981
ConversionPdfFormats format => format.ToFormDataValue(),
8082
List<Cookie> cookies => JsonConvert.SerializeObject(cookies),
83+
OverlaySource overlaySource => overlaySource.ToFormValue(),
84+
SplitMode splitMode => splitMode.ToFormValue(),
8185
float f => f.ToString(cultureInfo),
8286
double d => d.ToString(cultureInfo),
8387
decimal c => c.ToString(cultureInfo),

0 commit comments

Comments
 (0)