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

@@ -81,8 +81,14 @@ type downloadFrom struct {
// ExtraHttpHeaders are the HTTP headers to send alongside.
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"`
// 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.
@@ -323,8 +329,15 @@ func newContext(echoCtx echo.Context, logger *zap.Logger, fs *gotenberg.FileSyst
}
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)
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

View File

@@ -21,11 +21,11 @@ const (
// EmbedsFormField represents the form field name for embedding files.
EmbedsFormField string = "embeds"
// WatermarksFormField represents the form field name for watermark files.
WatermarksFormField string = "watermarks"
// WatermarkFormField represents the form field name for the watermark file.
WatermarkFormField string = "watermark"
// StampsFormField represents the form field name for stamp files.
StampsFormField string = "stamps"
// StampFormField represents the form field name for the stamp file.
StampFormField string = "stamp"
)
// 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
}
// Watermarks binds the absolute paths of form data files that should be
// used as watermark sources. Only files uploaded with the "watermarks"
// Watermark binds the absolute path of the form data file that should be
// used as a watermark source. Only a file uploaded with the "watermark"
// field name will be included.
func (form *FormData) Watermarks(target *[]string) *FormData {
func (form *FormData) Watermark(target *string) *FormData {
if form.errors != nil {
return form
}
if paths, ok := form.filesByField[WatermarksFormField]; ok {
*target = append(*target, paths...)
if paths, ok := form.filesByField[WatermarkFormField]; ok && len(paths) > 0 {
*target = paths[0]
}
return form
}
// Stamps binds the absolute paths of form data files that should be
// used as stamp sources. Only files uploaded with the "stamps"
// Stamp binds the absolute path of the form data file that should be
// used as a stamp source. Only a file uploaded with the "stamp"
// field name will be included.
func (form *FormData) Stamps(target *[]string) *FormData {
func (form *FormData) Stamp(target *string) *FormData {
if form.errors != nil {
return form
}
if paths, ok := form.filesByField[StampsFormField]; ok {
*target = append(*target, paths...)
if paths, ok := form.filesByField[StampFormField]; ok && len(paths) > 0 {
*target = paths[0]
}
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
// 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 {
embeds, ok := form.filesByField[EmbedsFormField]
watermarks, wmOk := form.filesByField[WatermarksFormField]
stamps, stOk := form.filesByField[StampsFormField]
watermarks, wmOk := form.filesByField[WatermarkFormField]
stamps, stOk := form.filesByField[StampFormField]
for filename, path := range form.files {
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)
embedPaths := pdfengines.FormDataPdfEmbeds(form)
watermark := pdfengines.FormDataPdfWatermark(form, false)
watermarkFiles := pdfengines.FormDataPdfWatermarkFiles(form)
watermarkFile := pdfengines.FormDataPdfWatermarkFile(form)
stamp := pdfengines.FormDataPdfStamp(form, false)
stampFiles := pdfengines.FormDataPdfStampFiles(form)
stampFile := pdfengines.FormDataPdfStampFile(form)
rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false)
var url string
@@ -429,11 +429,11 @@ func convertUrlRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err)
}
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 {
watermark.Expression = watermarkFiles[0]
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFile
}
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 {
stamp.Expression = stampFiles[0]
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFile
}
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)
embedPaths := pdfengines.FormDataPdfEmbeds(form)
watermark := pdfengines.FormDataPdfWatermark(form, false)
watermarkFiles := pdfengines.FormDataPdfWatermarkFiles(form)
watermarkFile := pdfengines.FormDataPdfWatermarkFile(form)
stamp := pdfengines.FormDataPdfStamp(form, false)
stampFiles := pdfengines.FormDataPdfStampFiles(form)
stampFile := pdfengines.FormDataPdfStampFile(form)
rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false)
var inputPath string
@@ -504,11 +504,11 @@ func convertHtmlRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err)
}
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 {
watermark.Expression = watermarkFiles[0]
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFile
}
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 {
stamp.Expression = stampFiles[0]
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFile
}
url := fmt.Sprintf("file://%s", inputPath)
@@ -568,9 +568,9 @@ func convertMarkdownRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
userPassword, ownerPassword := pdfengines.FormDataPdfEncrypt(form)
embedPaths := pdfengines.FormDataPdfEmbeds(form)
watermark := pdfengines.FormDataPdfWatermark(form, false)
watermarkFiles := pdfengines.FormDataPdfWatermarkFiles(form)
watermarkFile := pdfengines.FormDataPdfWatermarkFile(form)
stamp := pdfengines.FormDataPdfStamp(form, false)
stampFiles := pdfengines.FormDataPdfStampFiles(form)
stampFile := pdfengines.FormDataPdfStampFile(form)
rotateAngle, rotatePages := pdfengines.FormDataPdfRotate(form, false)
var (
@@ -586,11 +586,11 @@ func convertMarkdownRoute(chromium Api, engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err)
}
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 {
watermark.Expression = watermarkFiles[0]
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFile
}
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 {
stamp.Expression = stampFiles[0]
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFile
}
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)
embedPaths := pdfengines.FormDataPdfEmbeds(form)
watermark := pdfengines.FormDataPdfWatermark(form, false)
watermarkFiles := pdfengines.FormDataPdfWatermarkFiles(form)
watermarkFile := pdfengines.FormDataPdfWatermarkFile(form)
stamp := pdfengines.FormDataPdfStamp(form, false)
stampFiles := pdfengines.FormDataPdfStampFiles(form)
stampFile := pdfengines.FormDataPdfStampFile(form)
angle, rotatePages := pdfengines.FormDataPdfRotate(form, false)
zeroValuedSplitMode := gotenberg.SplitMode{}
@@ -189,11 +189,11 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
return fmt.Errorf("validate form data: %w", err)
}
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 {
watermark.Expression = watermarkFiles[0]
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFile
}
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 {
stamp.Expression = stampFiles[0]
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFile
}
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.
func FormDataPdfWatermarkFiles(form *api.FormData) []string {
var paths []string
form.Watermarks(&paths)
return paths
// FormDataPdfWatermarkFile extracts the watermark file path from form data.
func FormDataPdfWatermarkFile(form *api.FormData) string {
var path string
form.Watermark(&path)
return path
}
// FormDataPdfStampFiles extracts stamp file paths from form data.
func FormDataPdfStampFiles(form *api.FormData) []string {
var paths []string
form.Stamps(&paths)
return paths
// FormDataPdfStampFile extracts the stamp file path from form data.
func FormDataPdfStampFile(form *api.FormData) string {
var path string
form.Stamp(&path)
return path
}
// 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)
embedPaths := FormDataPdfEmbeds(form)
watermark := FormDataPdfWatermark(form, false)
watermarkFiles := FormDataPdfWatermarkFiles(form)
watermarkFile := FormDataPdfWatermarkFile(form)
stamp := FormDataPdfStamp(form, false)
stampFiles := FormDataPdfStampFiles(form)
stampFile := FormDataPdfStampFile(form)
angle, rotatePages := FormDataPdfRotate(form, false)
var inputPaths []string
@@ -625,11 +625,11 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err)
}
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 {
watermark.Expression = watermarkFiles[0]
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFile
}
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 {
stamp.Expression = stampFiles[0]
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFile
}
err = ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)
@@ -759,9 +759,9 @@ func splitRoute(engine gotenberg.PdfEngine) api.Route {
userPassword, ownerPassword := FormDataPdfEncrypt(form)
embedPaths := FormDataPdfEmbeds(form)
watermark := FormDataPdfWatermark(form, false)
watermarkFiles := FormDataPdfWatermarkFiles(form)
watermarkFile := FormDataPdfWatermarkFile(form)
stamp := FormDataPdfStamp(form, false)
stampFiles := FormDataPdfStampFiles(form)
stampFile := FormDataPdfStampFile(form)
angle, rotatePages := FormDataPdfRotate(form, false)
var inputPaths []string
@@ -774,11 +774,11 @@ func splitRoute(engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("validate form data: %w", err)
}
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && len(watermarkFiles) > 0 {
watermark.Expression = watermarkFiles[0]
if (watermark.Source == gotenberg.StampSourceImage || watermark.Source == gotenberg.StampSourcePDF) && watermarkFile != "" {
watermark.Expression = watermarkFile
}
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && len(stampFiles) > 0 {
stamp.Expression = stampFiles[0]
if (stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF) && stampFile != "" {
stamp.Expression = stampFile
}
err = ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)
@@ -1190,7 +1190,7 @@ func watermarkRoute(engine gotenberg.PdfEngine) api.Route {
form := ctx.FormData()
stamp := FormDataPdfWatermark(form, true)
watermarkFiles := FormDataPdfWatermarkFiles(form)
watermarkFile := FormDataPdfWatermarkFile(form)
var inputPaths []string
err := form.
@@ -1201,7 +1201,7 @@ func watermarkRoute(engine gotenberg.PdfEngine) api.Route {
}
if stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF {
if len(watermarkFiles) == 0 {
if watermarkFile == "" {
return api.WrapError(
errors.New("no watermark file provided"),
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)
@@ -1241,7 +1241,7 @@ func stampRoute(engine gotenberg.PdfEngine) api.Route {
form := ctx.FormData()
stamp := FormDataPdfStamp(form, true)
stampFiles := FormDataPdfStampFiles(form)
stampFile := FormDataPdfStampFile(form)
var inputPaths []string
err := form.
@@ -1252,7 +1252,7 @@ func stampRoute(engine gotenberg.PdfEngine) api.Route {
}
if stamp.Source == gotenberg.StampSourceImage || stamp.Source == gotenberg.StampSourcePDF {
if len(stampFiles) == 0 {
if stampFile == "" {
return api.WrapError(
errors.New("no stamp file provided"),
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)