fix(pdfengines): single file for watermark/stamp, and add the feature to the downloadFrom one

This commit is contained in:
Julien Neuhart
2026-03-18 23:22:31 +01:00
parent b98378311f
commit 410c1dfd7f
11 changed files with 173 additions and 77 deletions

View File

@@ -13,6 +13,7 @@ post {
body:multipart-form { body:multipart-form {
files: @file(../../test/integration/testdata/page_1.pdf) files: @file(../../test/integration/testdata/page_1.pdf)
embeds: @file(../../test/integration/testdata/page_1.pdf) embeds: @file(../../test/integration/testdata/page_1.pdf)
~downloadFrom: [{"url":"https://example.com/attachment.xml","embedded":true}]
} }
headers { headers {

View File

@@ -16,6 +16,8 @@ body:multipart-form {
stampExpression: APPROVED stampExpression: APPROVED
~stampPages: 1-2 ~stampPages: 1-2
~stampOptions: {"font":"Helvetica","fontSize":"48","color":"#00FF00","opacity":"0.5","rotation":"0"} ~stampOptions: {"font":"Helvetica","fontSize":"48","color":"#00FF00","opacity":"0.5","rotation":"0"}
~stamp: @file(../../test/integration/testdata/watermark.png)
~downloadFrom: [{"url":"https://example.com/stamp.png","field":"stamp"}]
} }
headers { headers {

View File

@@ -16,6 +16,8 @@ body:multipart-form {
watermarkExpression: CONFIDENTIAL watermarkExpression: CONFIDENTIAL
~watermarkPages: 1-2 ~watermarkPages: 1-2
~watermarkOptions: {"font":"Helvetica","fontSize":"48","color":"#FF0000","opacity":"0.3","rotation":"45"} ~watermarkOptions: {"font":"Helvetica","fontSize":"48","color":"#FF0000","opacity":"0.3","rotation":"45"}
~watermark: @file(../../test/integration/testdata/watermark.png)
~downloadFrom: [{"url":"https://example.com/watermark.png","field":"watermark"}]
} }
headers { headers {

View File

@@ -81,8 +81,14 @@ type downloadFrom struct {
// ExtraHttpHeaders are the HTTP headers to send alongside. // ExtraHttpHeaders are the HTTP headers to send alongside.
ExtraHttpHeaders map[string]string `json:"extraHttpHeaders"` ExtraHttpHeaders map[string]string `json:"extraHttpHeaders"`
// Download as embed file // Embedded routes the downloaded file as an embed. Deprecated: use
// Field instead. Kept for backward compatibility.
Embedded bool `json:"embedded"` Embedded bool `json:"embedded"`
// Field routes the downloaded file to a specific form field bucket.
// Supported values: "watermark", "stamp". For embeds, prefer the
// Embedded flag or set Field to "embedded".
Field string `json:"field"`
} }
// newContext returns a [Context] by parsing a "multipart/form-data" request. // newContext returns a [Context] by parsing a "multipart/form-data" request.
@@ -323,8 +329,15 @@ func newContext(echoCtx echo.Context, logger *zap.Logger, fs *gotenberg.FileSyst
} }
ctx.files[filename] = path ctx.files[filename] = path
if dl.Embedded {
// Route the downloaded file to the appropriate field bucket.
switch {
case dl.Field == "embedded" || dl.Embedded:
ctx.filesByField[EmbedsFormField] = append(ctx.filesByField[EmbedsFormField], path) ctx.filesByField[EmbedsFormField] = append(ctx.filesByField[EmbedsFormField], path)
case dl.Field == "watermark":
ctx.filesByField[WatermarkFormField] = append(ctx.filesByField[WatermarkFormField], path)
case dl.Field == "stamp":
ctx.filesByField[StampFormField] = append(ctx.filesByField[StampFormField], path)
} }
return nil return nil

View File

@@ -21,11 +21,11 @@ const (
// EmbedsFormField represents the form field name for embedding files. // EmbedsFormField represents the form field name for embedding files.
EmbedsFormField string = "embeds" EmbedsFormField string = "embeds"
// WatermarksFormField represents the form field name for watermark files. // WatermarkFormField represents the form field name for the watermark file.
WatermarksFormField string = "watermarks" WatermarkFormField string = "watermark"
// StampsFormField represents the form field name for stamp files. // StampFormField represents the form field name for the stamp file.
StampsFormField string = "stamps" StampFormField string = "stamp"
) )
// FormData is a helper for validating and hydrating values from a // FormData is a helper for validating and hydrating values from a
@@ -412,31 +412,31 @@ func (form *FormData) MandatoryPaths(extensions []string, target *[]string) *For
return form return form
} }
// Watermarks binds the absolute paths of form data files that should be // Watermark binds the absolute path of the form data file that should be
// used as watermark sources. Only files uploaded with the "watermarks" // used as a watermark source. Only a file uploaded with the "watermark"
// field name will be included. // field name will be included.
func (form *FormData) Watermarks(target *[]string) *FormData { func (form *FormData) Watermark(target *string) *FormData {
if form.errors != nil { if form.errors != nil {
return form return form
} }
if paths, ok := form.filesByField[WatermarksFormField]; ok { if paths, ok := form.filesByField[WatermarkFormField]; ok && len(paths) > 0 {
*target = append(*target, paths...) *target = paths[0]
} }
return form return form
} }
// Stamps binds the absolute paths of form data files that should be // Stamp binds the absolute path of the form data file that should be
// used as stamp sources. Only files uploaded with the "stamps" // used as a stamp source. Only a file uploaded with the "stamp"
// field name will be included. // field name will be included.
func (form *FormData) Stamps(target *[]string) *FormData { func (form *FormData) Stamp(target *string) *FormData {
if form.errors != nil { if form.errors != nil {
return form return form
} }
if paths, ok := form.filesByField[StampsFormField]; ok { if paths, ok := form.filesByField[StampFormField]; ok && len(paths) > 0 {
*target = append(*target, paths...) *target = paths[0]
} }
return form return form
@@ -444,11 +444,11 @@ func (form *FormData) Stamps(target *[]string) *FormData {
// paths bind the absolute paths of form data files, according to a list of // paths bind the absolute paths of form data files, according to a list of
// file extensions, to a string slice variable. // file extensions, to a string slice variable.
// embeds, watermarks, and stamps are excluded. // embeds, watermark, and stamp files are excluded.
func (form *FormData) paths(extensions []string, target *[]string) *FormData { func (form *FormData) paths(extensions []string, target *[]string) *FormData {
embeds, ok := form.filesByField[EmbedsFormField] embeds, ok := form.filesByField[EmbedsFormField]
watermarks, wmOk := form.filesByField[WatermarksFormField] watermarks, wmOk := form.filesByField[WatermarkFormField]
stamps, stOk := form.filesByField[StampsFormField] stamps, stOk := form.filesByField[StampFormField]
for filename, path := range form.files { for filename, path := range form.files {
if ok && slices.Contains(embeds, path) { if ok && slices.Contains(embeds, path) {

View File

@@ -416,9 +416,9 @@ func convertUrlRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form) userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form)
embedPaths := pdfengines.FormDataPdfEmbeds(form) embedPaths := pdfengines.FormDataPdfEmbeds(form)
watermark := pdfengines.FormDataPdfWatermark(form, false) watermark := pdfengines.FormDataPdfWatermark(form, false)
watermarkFiles := pdfengines.FormDataPdfWatermarkFiles(form) watermarkFile := pdfengines.FormDataPdfWatermarkFile(form)
stamp := pdfengines.FormDataPdfStamp(form, false) stamp := pdfengines.FormDataPdfStamp(form, false)
stampFiles := pdfengines.FormDataPdfStampFiles(form) stampFile := pdfengines.FormDataPdfStampFile(form)
rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false) rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false)
var url string var url string
@@ -429,11 +429,11 @@ func convertUrlRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err) return fmt.Errorf("validate form data: %w", err)
} }
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 { if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFiles[0] watermark.Expression = watermarkFile
} }
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 { if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFiles[0] stamp.Expression = stampFile
} }
err = convertUrl(ctx, chromium, engine, url, options, mode, pdfFormats, metadata, userPassword, ownerPassword, embedPaths, watermark, stamp, rotateAngle, rotatePages) err = convertUrl(ctx, chromium, engine, url, options, mode, pdfFormats, metadata, userPassword, ownerPassword, embedPaths, watermark, stamp, rotateAngle, rotatePages)
@@ -491,9 +491,9 @@ func convertHtmlRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form) userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form)
embedPaths := pdfengines.FormDataPdfEmbeds(form) embedPaths := pdfengines.FormDataPdfEmbeds(form)
watermark := pdfengines.FormDataPdfWatermark(form, false) watermark := pdfengines.FormDataPdfWatermark(form, false)
watermarkFiles := pdfengines.FormDataPdfWatermarkFiles(form) watermarkFile := pdfengines.FormDataPdfWatermarkFile(form)
stamp := pdfengines.FormDataPdfStamp(form, false) stamp := pdfengines.FormDataPdfStamp(form, false)
stampFiles := pdfengines.FormDataPdfStampFiles(form) stampFile := pdfengines.FormDataPdfStampFile(form)
rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false) rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false)
var inputPath string var inputPath string
@@ -504,11 +504,11 @@ func convertHtmlRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err) return fmt.Errorf("validate form data: %w", err)
} }
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 { if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFiles[0] watermark.Expression = watermarkFile
} }
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 { if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFiles[0] stamp.Expression = stampFile
} }
url := fmt.Sprintf("file://%s", inputPath) url := fmt.Sprintf("file://%s", inputPath)
@@ -568,9 +568,9 @@ func convertMarkdownRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form) userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form)
embedPaths := pdfengines.FormDataPdfEmbeds(form) embedPaths := pdfengines.FormDataPdfEmbeds(form)
watermark := pdfengines.FormDataPdfWatermark(form, false) watermark := pdfengines.FormDataPdfWatermark(form, false)
watermarkFiles := pdfengines.FormDataPdfWatermarkFiles(form) watermarkFile := pdfengines.FormDataPdfWatermarkFile(form)
stamp := pdfengines.FormDataPdfStamp(form, false) stamp := pdfengines.FormDataPdfStamp(form, false)
stampFiles := pdfengines.FormDataPdfStampFiles(form) stampFile := pdfengines.FormDataPdfStampFile(form)
rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false) rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false)
var ( var (
@@ -586,11 +586,11 @@ func convertMarkdownRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err) return fmt.Errorf("validate form data: %w", err)
} }
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 { if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFiles[0] watermark.Expression = watermarkFile
} }
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 { if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFiles[0] stamp.Expression = stampFile
} }
url, err := markdownToHtml(ctx, inputPath, markdownPaths) url, err := markdownToHtml(ctx, inputPath, markdownPaths)

View File

@@ -33,9 +33,9 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form) userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form)
embedPaths := pdfengines.FormDataPdfEmbeds(form) embedPaths := pdfengines.FormDataPdfEmbeds(form)
watermark := pdfengines.FormDataPdfWatermark(form, false) watermark := pdfengines.FormDataPdfWatermark(form, false)
watermarkFiles := pdfengines.FormDataPdfWatermarkFiles(form) watermarkFile := pdfengines.FormDataPdfWatermarkFile(form)
stamp := pdfengines.FormDataPdfStamp(form, false) stamp := pdfengines.FormDataPdfStamp(form, false)
stampFiles := pdfengines.FormDataPdfStampFiles(form) stampFile := pdfengines.FormDataPdfStampFile(form)
angle, rotatePages := pdfengines.FormDataPdfRotate(form, false) angle, rotatePages := pdfengines.FormDataPdfRotate(form, false)
zeroValuedSplitMode := gotenberg.SplitMode{} zeroValuedSplitMode := gotenberg.SplitMode{}
@@ -189,11 +189,11 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
return fmt.Errorf("validate form data: %w", err) return fmt.Errorf("validate form data: %w", err)
} }
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 { if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFiles[0] watermark.Expression = watermarkFile
} }
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 { if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFiles[0] stamp.Expression = stampFile
} }
err = pdfengines.ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths) err = pdfengines.ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)

View File

@@ -544,18 +544,18 @@ func formDataPdfStampOrWatermark(form *api.FormData, prefix string, mandatory bo
} }
} }
// FormDataPdfWatermarkFiles extracts watermark file paths from form data. // FormDataPdfWatermarkFile extracts the watermark file path from form data.
func FormDataPdfWatermarkFiles(form *api.FormData) []string { func FormDataPdfWatermarkFile(form *api.FormData) string {
var paths []string var path string
form.Watermarks(&paths) form.Watermark(&path)
return paths return path
} }
// FormDataPdfStampFiles extracts stamp file paths from form data. // FormDataPdfStampFile extracts the stamp file path from form data.
func FormDataPdfStampFiles(form *api.FormData) []string { func FormDataPdfStampFile(form *api.FormData) string {
var paths []string var path string
form.Stamps(&paths) form.Stamp(&path)
return paths return path
} }
// WatermarkStub applies a watermark to a list of PDF files. If the stamp has // WatermarkStub applies a watermark to a list of PDF files. If the stamp has
@@ -608,9 +608,9 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
userPassword, ownerPassword := FormDataPdfEncrypt(form) userPassword, ownerPassword := FormDataPdfEncrypt(form)
embedPaths := FormDataPdfEmbeds(form) embedPaths := FormDataPdfEmbeds(form)
watermark := FormDataPdfWatermark(form, false) watermark := FormDataPdfWatermark(form, false)
watermarkFiles := FormDataPdfWatermarkFiles(form) watermarkFile := FormDataPdfWatermarkFile(form)
stamp := FormDataPdfStamp(form, false) stamp := FormDataPdfStamp(form, false)
stampFiles := FormDataPdfStampFiles(form) stampFile := FormDataPdfStampFile(form)
angle, rotatePages := FormDataPdfRotate(form, false) angle, rotatePages := FormDataPdfRotate(form, false)
var inputPaths []string var inputPaths []string
@@ -625,11 +625,11 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err) return fmt.Errorf("validate form data: %w", err)
} }
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 { if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFiles[0] watermark.Expression = watermarkFile
} }
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 { if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFiles[0] stamp.Expression = stampFile
} }
err = ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths) err = ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)
@@ -759,9 +759,9 @@ func splitRoute(engine gotenberg.PdfEngine) api.Route {
userPassword, ownerPassword := FormDataPdfEncrypt(form) userPassword, ownerPassword := FormDataPdfEncrypt(form)
embedPaths := FormDataPdfEmbeds(form) embedPaths := FormDataPdfEmbeds(form)
watermark := FormDataPdfWatermark(form, false) watermark := FormDataPdfWatermark(form, false)
watermarkFiles := FormDataPdfWatermarkFiles(form) watermarkFile := FormDataPdfWatermarkFile(form)
stamp := FormDataPdfStamp(form, false) stamp := FormDataPdfStamp(form, false)
stampFiles := FormDataPdfStampFiles(form) stampFile := FormDataPdfStampFile(form)
angle, rotatePages := FormDataPdfRotate(form, false) angle, rotatePages := FormDataPdfRotate(form, false)
var inputPaths []string var inputPaths []string
@@ -774,11 +774,11 @@ func splitRoute(engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err) return fmt.Errorf("validate form data: %w", err)
} }
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 { if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFiles[0] watermark.Expression = watermarkFile
} }
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 { if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFiles[0] stamp.Expression = stampFile
} }
err = ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths) err = ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)
@@ -1190,7 +1190,7 @@ func watermarkRoute(engine gotenberg.PdfEngine) api.Route {
form := ctx.FormData() form := ctx.FormData()
stamp := FormDataPdfWatermark(form, true) stamp := FormDataPdfWatermark(form, true)
watermarkFiles := FormDataPdfWatermarkFiles(form) watermarkFile := FormDataPdfWatermarkFile(form)
var inputPaths []string var inputPaths []string
err := form. err := form.
@@ -1201,7 +1201,7 @@ func watermarkRoute(engine gotenberg.PdfEngine) api.Route {
} }
if stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF { if stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF {
if len(watermarkFiles) == 0 { if watermarkFile == "" {
return api.WrapError( return api.WrapError(
errors.New("no watermark file provided"), errors.New("no watermark file provided"),
api.NewSentinelHttpError( api.NewSentinelHttpError(
@@ -1210,7 +1210,7 @@ func watermarkRoute(engine gotenberg.PdfEngine) api.Route {
), ),
) )
} }
stamp.Expression = watermarkFiles[0] stamp.Expression = watermarkFile
} }
err = WatermarkStub(ctx, engine, stamp, inputPaths) err = WatermarkStub(ctx, engine, stamp, inputPaths)
@@ -1241,7 +1241,7 @@ func stampRoute(engine gotenberg.PdfEngine) api.Route {
form := ctx.FormData() form := ctx.FormData()
stamp := FormDataPdfStamp(form, true) stamp := FormDataPdfStamp(form, true)
stampFiles := FormDataPdfStampFiles(form) stampFile := FormDataPdfStampFile(form)
var inputPaths []string var inputPaths []string
err := form. err := form.
@@ -1252,7 +1252,7 @@ func stampRoute(engine gotenberg.PdfEngine) api.Route {
} }
if stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF { if stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF {
if len(stampFiles) == 0 { if stampFile == "" {
return api.WrapError( return api.WrapError(
errors.New("no stamp file provided"), errors.New("no stamp file provided"),
api.NewSentinelHttpError( api.NewSentinelHttpError(
@@ -1261,7 +1261,7 @@ func stampRoute(engine gotenberg.PdfEngine) api.Route {
), ),
) )
} }
stamp.Expression = stampFiles[0] stamp.Expression = stampFile
} }
err = StampStub(ctx, engine, stamp, inputPaths) err = StampStub(ctx, engine, stamp, inputPaths)

View File

@@ -560,6 +560,32 @@ Feature: /forms/pdfengines/merge
Then the response status code should be 200 Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf" Then the response header "Content-Type" should be "application/pdf"
@download-from
Scenario: POST /forms/pdfengines/merge (Watermark via Download From)
Given I have a Gotenberg container with the following environment variable(s):
| PDFENGINES_WATERMARK_ENGINES | pdfcpu |
Given I have a static server
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/merge" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file |
| files | testdata/page_2.pdf | file |
| downloadFrom | [{"url":"http://host.docker.internal:%d/static/testdata/watermark.png","field":"watermark"}] | field |
| watermarkSource | image | field |
Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf"
@download-from
Scenario: POST /forms/pdfengines/merge (Stamp via Download From)
Given I have a Gotenberg container with the following environment variable(s):
| PDFENGINES_STAMP_ENGINES | pdfcpu |
Given I have a static server
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/merge" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file |
| files | testdata/page_2.pdf | file |
| downloadFrom | [{"url":"http://host.docker.internal:%d/static/testdata/page_2.pdf","field":"stamp"}] | field |
| stampSource | pdf | field |
Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf"
@webhook @webhook
Scenario: POST /forms/pdfengines/merge (Webhook) Scenario: POST /forms/pdfengines/merge (Webhook)
Given I have a default Gotenberg container Given I have a default Gotenberg container

View File

@@ -45,7 +45,7 @@ Feature: /forms/pdfengines/stamp
| PDFENGINES_STAMP_ENGINES | pdfcpu | | PDFENGINES_STAMP_ENGINES | pdfcpu |
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s): When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file | | files | testdata/page_1.pdf | file |
| stamps | testdata/watermark.png | file | | stamp | testdata/watermark.png | file |
| stampSource | image | field | | stampSource | image | field |
Then the response status code should be 200 Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf" Then the response header "Content-Type" should be "application/pdf"
@@ -56,7 +56,7 @@ Feature: /forms/pdfengines/stamp
| PDFENGINES_STAMP_ENGINES | pdfcpu | | PDFENGINES_STAMP_ENGINES | pdfcpu |
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s): When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file | | files | testdata/page_1.pdf | file |
| stamps | testdata/page_2.pdf | file | | stamp | testdata/page_2.pdf | file |
| stampSource | pdf | field | | stampSource | pdf | field |
Then the response status code should be 200 Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf" Then the response header "Content-Type" should be "application/pdf"
@@ -67,7 +67,7 @@ Feature: /forms/pdfengines/stamp
| PDFENGINES_STAMP_ENGINES | pdftk | | PDFENGINES_STAMP_ENGINES | pdftk |
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s): When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file | | files | testdata/page_1.pdf | file |
| stamps | testdata/page_2.pdf | file | | stamp | testdata/page_2.pdf | file |
| stampSource | pdf | field | | stampSource | pdf | field |
Then the response status code should be 200 Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf" Then the response header "Content-Type" should be "application/pdf"
@@ -86,6 +86,32 @@ Feature: /forms/pdfengines/stamp
At least one PDF engine cannot process the requested stamp source type, while others may have failed due to different issues At least one PDF engine cannot process the requested stamp source type, while others may have failed due to different issues
""" """
@download-from
Scenario: POST /forms/pdfengines/stamp (Image via Download From)
Given I have a Gotenberg container with the following environment variable(s):
| PDFENGINES_STAMP_ENGINES | pdfcpu |
Given I have a static server
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file |
| downloadFrom | [{"url":"http://host.docker.internal:%d/static/testdata/watermark.png","field":"stamp"}] | field |
| stampSource | image | field |
Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf"
Then there should be 1 PDF(s) in the response
@download-from
Scenario: POST /forms/pdfengines/stamp (PDF via Download From)
Given I have a Gotenberg container with the following environment variable(s):
| PDFENGINES_STAMP_ENGINES | pdfcpu |
Given I have a static server
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file |
| downloadFrom | [{"url":"http://host.docker.internal:%d/static/testdata/page_2.pdf","field":"stamp"}] | field |
| stampSource | pdf | field |
Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf"
Then there should be 1 PDF(s) in the response
Scenario: POST /forms/pdfengines/stamp (Many PDFs) Scenario: POST /forms/pdfengines/stamp (Many PDFs)
Given I have a default Gotenberg container Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s): When I make a "POST" request to Gotenberg at the "/forms/pdfengines/stamp" endpoint with the following form data and header(s):

View File

@@ -45,7 +45,7 @@ Feature: /forms/pdfengines/watermark
| PDFENGINES_WATERMARK_ENGINES | pdfcpu | | PDFENGINES_WATERMARK_ENGINES | pdfcpu |
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s): When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file | | files | testdata/page_1.pdf | file |
| watermarks | testdata/watermark.png | file | | watermark | testdata/watermark.png | file |
| watermarkSource | image | field | | watermarkSource | image | field |
Then the response status code should be 200 Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf" Then the response header "Content-Type" should be "application/pdf"
@@ -56,7 +56,7 @@ Feature: /forms/pdfengines/watermark
| PDFENGINES_WATERMARK_ENGINES | pdfcpu | | PDFENGINES_WATERMARK_ENGINES | pdfcpu |
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s): When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file | | files | testdata/page_1.pdf | file |
| watermarks | testdata/page_2.pdf | file | | watermark | testdata/page_2.pdf | file |
| watermarkSource | pdf | field | | watermarkSource | pdf | field |
Then the response status code should be 200 Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf" Then the response header "Content-Type" should be "application/pdf"
@@ -67,7 +67,7 @@ Feature: /forms/pdfengines/watermark
| PDFENGINES_WATERMARK_ENGINES | pdftk | | PDFENGINES_WATERMARK_ENGINES | pdftk |
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s): When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file | | files | testdata/page_1.pdf | file |
| watermarks | testdata/page_2.pdf | file | | watermark | testdata/page_2.pdf | file |
| watermarkSource | pdf | field | | watermarkSource | pdf | field |
Then the response status code should be 200 Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf" Then the response header "Content-Type" should be "application/pdf"
@@ -86,6 +86,32 @@ Feature: /forms/pdfengines/watermark
At least one PDF engine cannot process the requested stamp source type, while others may have failed due to different issues At least one PDF engine cannot process the requested stamp source type, while others may have failed due to different issues
""" """
@download-from
Scenario: POST /forms/pdfengines/watermark (Image via Download From)
Given I have a Gotenberg container with the following environment variable(s):
| PDFENGINES_WATERMARK_ENGINES | pdfcpu |
Given I have a static server
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file |
| downloadFrom | [{"url":"http://host.docker.internal:%d/static/testdata/watermark.png","field":"watermark"}] | field |
| watermarkSource | image | field |
Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf"
Then there should be 1 PDF(s) in the response
@download-from
Scenario: POST /forms/pdfengines/watermark (PDF via Download From)
Given I have a Gotenberg container with the following environment variable(s):
| PDFENGINES_WATERMARK_ENGINES | pdfcpu |
Given I have a static server
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s):
| files | testdata/page_1.pdf | file |
| downloadFrom | [{"url":"http://host.docker.internal:%d/static/testdata/page_2.pdf","field":"watermark"}] | field |
| watermarkSource | pdf | field |
Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf"
Then there should be 1 PDF(s) in the response
Scenario: POST /forms/pdfengines/watermark (Many PDFs) Scenario: POST /forms/pdfengines/watermark (Many PDFs)
Given I have a default Gotenberg container Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s): When I make a "POST" request to Gotenberg at the "/forms/pdfengines/watermark" endpoint with the following form data and header(s):