fix(pdfengines): better workflow when applying PDF/A or PDF/UA compliance

This commit is contained in:
Julien Neuhart
2026-03-18 15:24:31 +01:00
parent 7fb4c89832
commit 21e300fcec
11 changed files with 252 additions and 112 deletions

View File

@@ -39,6 +39,21 @@ Commits must follow the [Conventional Commits](https://www.conventionalcommits.o
Common types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `ci`, `build`. The scope should match the module or area of the change (e.g., `chromium`, `pdfengines`, `api`).
## Adding PDF Engine Features
When adding a new PDF engine capability (e.g., bookmarks, watermark, stamp, embed), you must update the Makefile to include the corresponding engine list variable and flag. Every `--pdfengines-*-engines` flag registered in `pkg/modules/pdfengines/pdfengines.go` must have a matching entry in the Makefile:
1. **Add a variable** in the Makefile's variable block (around line 60-70):
```makefile
PDFENGINES_<FEATURE>_ENGINES=<default engines>
```
2. **Add the flag** in the Makefile's command args block (around line 140-155):
```makefile
--pdfengines-<feature>-engines=$(PDFENGINES_<FEATURE>_ENGINES) \
```
The default value should match what is defined in `pdfengines.go`'s `fs.StringSlice(...)` call for that flag.
## Coding Patterns
- **Error handling:** Always wrap errors with context using `fmt.Errorf("description: %w", err)`. Never swallow errors silently.

View File

@@ -66,6 +66,8 @@ PDFENGINES_READ_METADATA_ENGINES=exiftool
PDFENGINES_WRITE_METADATA_ENGINES=exiftool
PDFENGINES_READ_BOOKMARKS_ENGINES=pdfcpu
PDFENGINES_WRITE_BOOKMARKS_ENGINES=pdfcpu
PDFENGINES_WATERMARK_ENGINES=pdfcpu,pdftk
PDFENGINES_STAMP_ENGINES=pdfcpu,pdftk
PDFENGINES_ENCRYPT_ENGINES=qpdf,pdfcpu,pdftk
PDFENGINES_EMBED_ENGINES=pdfcpu
PROMETHEUS_NAMESPACE=gotenberg
@@ -146,6 +148,8 @@ run: ## Start a Gotenberg container
--pdfengines-write-metadata-engines=$(PDFENGINES_WRITE_METADATA_ENGINES) \
--pdfengines-read-bookmarks-engines=$(PDFENGINES_READ_BOOKMARKS_ENGINES) \
--pdfengines-write-bookmarks-engines=$(PDFENGINES_WRITE_BOOKMARKS_ENGINES) \
--pdfengines-watermark-engines=$(PDFENGINES_WATERMARK_ENGINES) \
--pdfengines-stamp-engines=$(PDFENGINES_STAMP_ENGINES) \
--pdfengines-encrypt-engines=$(PDFENGINES_ENCRYPT_ENGINES) \
--pdfengines-embed-engines=$(PDFENGINES_EMBED_ENGINES) \
--prometheus-namespace=$(PROMETHEUS_NAMESPACE) \

View File

@@ -781,24 +781,36 @@ func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url
return fmt.Errorf("convert to PDF: %w", err)
}
err = pdfengines.ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)
if err != nil {
return err
}
outputPaths, err := pdfengines.SplitPdfStub(ctx, engine, mode, []string{outputPath})
if err != nil {
return fmt.Errorf("split PDF: %w", err)
}
err = pdfengines.WatermarkStub(ctx, engine, watermark, outputPaths)
if err != nil {
return fmt.Errorf("watermark PDFs: %w", err)
}
err = pdfengines.StampStub(ctx, engine, stamp, outputPaths)
if err != nil {
return fmt.Errorf("stamp PDFs: %w", err)
}
convertOutputPaths, err := pdfengines.ConvertStub(ctx, engine, pdfFormats, outputPaths)
if err != nil {
return fmt.Errorf("convert PDF(s): %w", err)
}
err = pdfengines.WatermarkStub(ctx, engine, watermark, convertOutputPaths)
// Metadata, embeds are written after Convert, as LibreOffice
// strips them during PDF/A conversion.
err = pdfengines.WriteMetadataStub(ctx, engine, metadata, convertOutputPaths)
if err != nil {
return fmt.Errorf("watermark PDFs: %w", err)
}
err = pdfengines.StampStub(ctx, engine, stamp, convertOutputPaths)
if err != nil {
return fmt.Errorf("stamp PDFs: %w", err)
return fmt.Errorf("write metadata: %w", err)
}
err = pdfengines.EmbedFilesStub(ctx, engine, embedPaths, convertOutputPaths)
@@ -806,11 +818,6 @@ func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url
return fmt.Errorf("embed files into PDFs: %w", err)
}
err = pdfengines.WriteMetadataStub(ctx, engine, metadata, convertOutputPaths)
if err != nil {
return fmt.Errorf("write metadata: %w", err)
}
err = pdfengines.EncryptPdfStub(ctx, engine, userPassword, ownerPassword, convertOutputPaths)
if err != nil {
return fmt.Errorf("encrypt PDFs: %w", err)

View File

@@ -195,6 +195,14 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
stamp.Expression = stampFiles[0]
}
err = pdfengines.ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)
if err != nil {
return err
}
hasPostProcessing := watermark.Source != "" || stamp.Source != "" ||
len(embedPaths) > 0 || len(metadata) > 0 || flatten
outputPaths := make([]string, len(inputPaths))
for i, inputPath := range inputPaths {
outputPaths[i] = ctx.GeneratePath(".pdf")
@@ -230,9 +238,10 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
NativeTiledWatermarkText: nativeTiledWatermarkText,
}
if nativePdfFormats && splitMode == zeroValuedSplitMode {
if nativePdfFormats && splitMode == zeroValuedSplitMode && !hasPostProcessing {
// Only natively apply given PDF formats if we're not
// splitting the PDF later.
// splitting the PDF later and no post-processing features
// are enabled (as they would degrade compliance).
options.PdfFormats = pdfFormats
}
@@ -298,7 +307,27 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
}
}
if !nativePdfFormats || (nativePdfFormats && splitMode != zeroValuedSplitMode) {
err = pdfengines.WatermarkStub(ctx, engine, watermark, outputPaths)
if err != nil {
return fmt.Errorf("watermark PDFs: %w", err)
}
err = pdfengines.StampStub(ctx, engine, stamp, outputPaths)
if err != nil {
return fmt.Errorf("stamp PDFs: %w", err)
}
if flatten {
err = pdfengines.FlattenStub(ctx, engine, outputPaths)
if err != nil {
return fmt.Errorf("flatten PDFs: %w", err)
}
}
needsConvertStub := !nativePdfFormats ||
(nativePdfFormats && splitMode != zeroValuedSplitMode) ||
(nativePdfFormats && hasPostProcessing)
if needsConvertStub {
convertOutputPaths, err := pdfengines.ConvertStub(ctx, engine, pdfFormats, outputPaths)
if err != nil {
return fmt.Errorf("convert PDFs: %w", err)
@@ -318,31 +347,16 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap
}
}
err = pdfengines.WatermarkStub(ctx, engine, watermark, outputPaths)
if err != nil {
return fmt.Errorf("watermark PDFs: %w", err)
}
err = pdfengines.StampStub(ctx, engine, stamp, outputPaths)
if err != nil {
return fmt.Errorf("stamp PDFs: %w", err)
}
err = pdfengines.EmbedFilesStub(ctx, engine, embedPaths, outputPaths)
if err != nil {
return fmt.Errorf("embed files into PDFs: %w", err)
}
// Metadata, embeds are written after Convert, as LibreOffice
// strips them during PDF/A conversion.
err = pdfengines.WriteMetadataStub(ctx, engine, metadata, outputPaths)
if err != nil {
return fmt.Errorf("write metadata: %w", err)
}
if flatten {
err = pdfengines.FlattenStub(ctx, engine, outputPaths)
if err != nil {
return fmt.Errorf("flatten PDFs: %w", err)
}
err = pdfengines.EmbedFilesStub(ctx, engine, embedPaths, outputPaths)
if err != nil {
return fmt.Errorf("embed files into PDFs: %w", err)
}
err = pdfengines.EncryptPdfStub(ctx, engine, userPassword, ownerPassword, outputPaths)

View File

@@ -166,6 +166,41 @@ func FormDataPdfBookmarks(form *api.FormData, mandatory bool) any {
return bookmarks
}
// ValidatePdfFormatsCompat checks for incompatible combinations of PDF formats
// with other features and returns an appropriate error if found.
func ValidatePdfFormatsCompat(pdfFormats gotenberg.PdfFormats, userPassword string, embedPaths []string) error {
zeroValued := gotenberg.PdfFormats{}
if pdfFormats == zeroValued {
return nil
}
// PDF/A forbids encryption per the standard.
if pdfFormats.PdfA != "" && userPassword != "" {
return api.WrapError(
errors.New("PDF/A format is incompatible with encryption"),
api.NewSentinelHttpError(
http.StatusBadRequest,
"Invalid form data: PDF/A format is incompatible with encryption",
),
)
}
// Only PDF/A-3 variants allow embedded file attachments.
if pdfFormats.PdfA != "" && len(embedPaths) > 0 {
if pdfFormats.PdfA != gotenberg.PdfA3a && pdfFormats.PdfA != gotenberg.PdfA3b && pdfFormats.PdfA != gotenberg.PdfA3u {
return api.WrapError(
fmt.Errorf("PDF format '%s' does not support embedded files", pdfFormats.PdfA),
api.NewSentinelHttpError(
http.StatusBadRequest,
fmt.Sprintf("Invalid form data: PDF format '%s' does not support embedded files; only PDF/A-3 variants allow attachments", pdfFormats.PdfA),
),
)
}
}
return nil
}
// MergeStub merges given PDFs. If only one input PDF, it does nothing and
// returns the corresponding input path.
func MergeStub(ctx *api.Context, engine gotenberg.PdfEngine, inputPaths []string) (string, error) {
@@ -546,16 +581,18 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
stamp.Expression = stampFiles[0]
}
err = ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)
if err != nil {
return err
}
outputPath := ctx.GeneratePath(".pdf")
err = engine.Merge(ctx, ctx.Log(), inputPaths, outputPath)
if err != nil {
return fmt.Errorf("merge PDFs: %w", err)
}
outputPaths, err := ConvertStub(ctx, engine, pdfFormats, []string{outputPath})
if err != nil {
return fmt.Errorf("convert PDF: %w", err)
}
outputPaths := []string{outputPath}
err = WatermarkStub(ctx, engine, watermark, outputPaths)
if err != nil {
@@ -567,11 +604,21 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("stamp PDFs: %w", err)
}
err = EmbedFilesStub(ctx, engine, embedPaths, outputPaths)
if err != nil {
return fmt.Errorf("embed files into PDFs: %w", err)
if flatten {
err = FlattenStub(ctx, engine, outputPaths)
if err != nil {
return fmt.Errorf("flatten PDFs: %w", err)
}
}
outputPaths, err = ConvertStub(ctx, engine, pdfFormats, outputPaths)
if err != nil {
return fmt.Errorf("convert PDF: %w", err)
}
// Bookmarks, metadata, and embeds are written after Convert,
// as LibreOffice strips them during PDF/A conversion.
var finalBookmarks []gotenberg.Bookmark
if b, ok := bookmarks.([]gotenberg.Bookmark); ok {
finalBookmarks = b
@@ -620,11 +667,9 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route {
return fmt.Errorf("write metadata: %w", err)
}
if flatten {
err = FlattenStub(ctx, engine, outputPaths)
if err != nil {
return fmt.Errorf("flatten PDFs: %w", err)
}
err = EmbedFilesStub(ctx, engine, embedPaths, outputPaths)
if err != nil {
return fmt.Errorf("embed files into PDFs: %w", err)
}
err = EncryptPdfStub(ctx, engine, userPassword, ownerPassword, outputPaths)
@@ -679,41 +724,48 @@ func splitRoute(engine gotenberg.PdfEngine) api.Route {
stamp.Expression = stampFiles[0]
}
err = ValidatePdfFormatsCompat(pdfFormats, userPassword, embedPaths)
if err != nil {
return err
}
outputPaths, err := SplitPdfStub(ctx, engine, mode, inputPaths)
if err != nil {
return fmt.Errorf("split PDFs: %w", err)
}
err = WatermarkStub(ctx, engine, watermark, outputPaths)
if err != nil {
return fmt.Errorf("watermark PDFs: %w", err)
}
err = StampStub(ctx, engine, stamp, outputPaths)
if err != nil {
return fmt.Errorf("stamp PDFs: %w", err)
}
if flatten {
err = FlattenStub(ctx, engine, outputPaths)
if err != nil {
return fmt.Errorf("flatten PDFs: %w", err)
}
}
convertOutputPaths, err := ConvertStub(ctx, engine, pdfFormats, outputPaths)
if err != nil {
return fmt.Errorf("convert PDFs: %w", err)
}
err = WatermarkStub(ctx, engine, watermark, convertOutputPaths)
if err != nil {
return fmt.Errorf("watermark PDFs: %w", err)
}
err = StampStub(ctx, engine, stamp, convertOutputPaths)
if err != nil {
return fmt.Errorf("stamp PDFs: %w", err)
}
err = EmbedFilesStub(ctx, engine, embedPaths, convertOutputPaths)
if err != nil {
return fmt.Errorf("embed files into PDFs: %w", err)
}
// Metadata, embeds are written after Convert, as LibreOffice
// strips them during PDF/A conversion.
err = WriteMetadataStub(ctx, engine, metadata, convertOutputPaths)
if err != nil {
return fmt.Errorf("write metadata: %w", err)
}
if flatten {
err = FlattenStub(ctx, engine, convertOutputPaths)
if err != nil {
return fmt.Errorf("flatten PDFs: %w", err)
}
err = EmbedFilesStub(ctx, engine, embedPaths, convertOutputPaths)
if err != nil {
return fmt.Errorf("embed files into PDFs: %w", err)
}
err = EncryptPdfStub(ctx, engine, userPassword, ownerPassword, convertOutputPaths)

View File

@@ -1033,11 +1033,11 @@ Feature: /forms/chromium/convert/html
@stamp
@flatten
@embed
Scenario: POST /forms/chromium/convert/html (PDF/A-1b & PDF/UA-1 & Metadata & Watermark & Stamp & Flatten & Embeds)
Scenario: POST /forms/chromium/convert/html (PDF/A-3b & PDF/UA-1 & Metadata & Watermark & Stamp & Flatten & Embeds)
Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/chromium/convert/html" endpoint with the following form data and header(s):
| files | testdata/page-1-html/index.html | file |
| pdfa | PDF/A-1b | field |
| pdfa | PDF/A-3b | field |
| pdfua | true | field |
| metadata | {"Author":"Julien Neuhart","Copyright":"Julien Neuhart","CreateDate":"2006-09-18T16:27:50-04:00","Creator":"Gotenberg","Keywords":["first","second"],"Marked":true,"ModDate":"2006-09-18T16:27:50-04:00","PDFVersion":1.7,"Producer":"Gotenberg","Subject":"Sample","Title":"Sample","Trapped":"Unknown"} | field |
| flatten | true | field |
@@ -1049,8 +1049,8 @@ Feature: /forms/chromium/convert/html
Then there should be 1 PDF(s) in the response
Then there should be the following file(s) in the response:
| foo.pdf |
Then the response PDF(s) should be valid "PDF/A-1b" with a tolerance of 11 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 2 failed rule(s)
Then the response PDF(s) should be valid "PDF/A-3b" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 3 failed rule(s)
Then the response PDF(s) should be flatten
Then the response PDF(s) should have the "embed_1.xml" file embedded
Then the response PDF(s) should have the "embed_2.xml" file embedded
@@ -1064,11 +1064,8 @@ Feature: /forms/chromium/convert/html
"foo.pdf": {
"Author": "Julien Neuhart",
"Copyright": "Julien Neuhart",
"CreateDate": "2006:09:18 16:27:50-04:00",
"Creator": "Gotenberg",
"Keywords": ["first", "second"],
"Marked": true,
"ModDate": "2006:09:18 16:27:50-04:00",
"PDFVersion": 1.7,
"Producer": "Gotenberg",
"Subject": "Sample",

View File

@@ -1012,12 +1012,12 @@ Feature: /forms/chromium/convert/markdown
@metadata
@flatten
@embed
Scenario: POST /forms/chromium/convert/markdown (PDF/A-1b & PDF/UA-1 & Metadata & Flatten & Embeds)
Scenario: POST /forms/chromium/convert/markdown (PDF/A-3b & PDF/UA-1 & Metadata & Flatten & Embeds)
Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/chromium/convert/markdown" endpoint with the following form data and header(s):
| files | testdata/page-1-markdown/index.html | file |
| files | testdata/page-1-markdown/page_1.md | file |
| pdfa | PDF/A-1b | field |
| pdfa | PDF/A-3b | field |
| pdfua | true | field |
| metadata | {"Author":"Julien Neuhart","Copyright":"Julien Neuhart","CreateDate":"2006-09-18T16:27:50-04:00","Creator":"Gotenberg","Keywords":["first","second"],"Marked":true,"ModDate":"2006-09-18T16:27:50-04:00","PDFVersion":1.7,"Producer":"Gotenberg","Subject":"Sample","Title":"Sample","Trapped":"Unknown"} | field |
| flatten | true | field |
@@ -1029,8 +1029,8 @@ Feature: /forms/chromium/convert/markdown
Then there should be 1 PDF(s) in the response
Then there should be the following file(s) in the response:
| foo.pdf |
Then the response PDF(s) should be valid "PDF/A-1b" with a tolerance of 9 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 2 failed rule(s)
Then the response PDF(s) should be valid "PDF/A-3b" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 3 failed rule(s)
Then the response PDF(s) should be flatten
Then the response PDF(s) should have the "embed_1.xml" file embedded
Then the response PDF(s) should have the "embed_2.xml" file embedded
@@ -1044,11 +1044,8 @@ Feature: /forms/chromium/convert/markdown
"foo.pdf": {
"Author": "Julien Neuhart",
"Copyright": "Julien Neuhart",
"CreateDate": "2006:09:18 16:27:50-04:00",
"Creator": "Gotenberg",
"Keywords": ["first", "second"],
"Marked": true,
"ModDate": "2006:09:18 16:27:50-04:00",
"PDFVersion": 1.7,
"Producer": "Gotenberg",
"Subject": "Sample",

View File

@@ -1107,12 +1107,12 @@ Feature: /forms/chromium/convert/url
@metadata
@flatten
@embed
Scenario: POST /forms/chromium/convert/url (PDF/A-1b & PDF/UA-1 & Metadata & Flatten)
Scenario: POST /forms/chromium/convert/url (PDF/A-3b & PDF/UA-1 & Metadata & Flatten & Embeds)
Given I have a default Gotenberg container
Given I have a static server
When I make a "POST" request to Gotenberg at the "/forms/chromium/convert/url" endpoint with the following form data and header(s):
| url | http://host.docker.internal:%d/html/testdata/page-1-html/index.html | field |
| pdfa | PDF/A-1b | field |
| pdfa | PDF/A-3b | field |
| pdfua | true | field |
| metadata | {"Author":"Julien Neuhart","Copyright":"Julien Neuhart","CreateDate":"2006-09-18T16:27:50-04:00","Creator":"Gotenberg","Keywords":["first","second"],"Marked":true,"ModDate":"2006-09-18T16:27:50-04:00","PDFVersion":1.7,"Producer":"Gotenberg","Subject":"Sample","Title":"Sample","Trapped":"Unknown"} | field |
| flatten | true | field |
@@ -1124,8 +1124,8 @@ Feature: /forms/chromium/convert/url
Then there should be 1 PDF(s) in the response
Then there should be the following file(s) in the response:
| foo.pdf |
Then the response PDF(s) should be valid "PDF/A-1b" with a tolerance of 9 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 2 failed rule(s)
Then the response PDF(s) should be valid "PDF/A-3b" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 3 failed rule(s)
Then the response PDF(s) should be flatten
Then the response PDF(s) should have the "embed_1.xml" file embedded
Then the response PDF(s) should have the "embed_2.xml" file embedded
@@ -1139,11 +1139,8 @@ Feature: /forms/chromium/convert/url
"foo.pdf": {
"Author": "Julien Neuhart",
"Copyright": "Julien Neuhart",
"CreateDate": "2006:09:18 16:27:50-04:00",
"Creator": "Gotenberg",
"Keywords": ["first", "second"],
"Marked": true,
"ModDate": "2006:09:18 16:27:50-04:00",
"PDFVersion": 1.7,
"Producer": "Gotenberg",
"Subject": "Sample",

View File

@@ -642,11 +642,11 @@ Feature: /forms/libreoffice/convert
@stamp
@flatten
@embed
Scenario: POST /forms/libreoffice/convert (PDF/A-1b & PDF/UA-1 & Metadata & Watermark & Stamp & Flatten & Embeds)
Scenario: POST /forms/libreoffice/convert (PDF/A-3b & PDF/UA-1 & Metadata & Watermark & Stamp & Flatten & Embeds)
Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/libreoffice/convert" endpoint with the following form data and header(s):
| files | testdata/page_1.docx | file |
| pdfa | PDF/A-1b | field |
| pdfa | PDF/A-3b | field |
| pdfua | true | field |
| metadata | {"Author":"Julien Neuhart","Copyright":"Julien Neuhart","CreateDate":"2006-09-18T16:27:50-04:00","Creator":"Gotenberg","Keywords":["first","second"],"Marked":true,"ModDate":"2006-09-18T16:27:50-04:00","PDFVersion":1.7,"Producer":"Gotenberg","Subject":"Sample","Title":"Sample","Trapped":"Unknown"} | field |
| watermarkSource | text | field |
@@ -662,8 +662,8 @@ Feature: /forms/libreoffice/convert
Then there should be 1 PDF(s) in the response
Then there should be the following file(s) in the response:
| foo.pdf |
Then the response PDF(s) should be valid "PDF/A-1b" with a tolerance of 12 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/A-3b" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 3 failed rule(s)
Then the response PDF(s) should be flatten
Then the response PDF(s) should have the "embed_1.xml" file embedded
Then the response PDF(s) should have the "embed_2.xml" file embedded
@@ -677,11 +677,8 @@ Feature: /forms/libreoffice/convert
"foo.pdf": {
"Author": "Julien Neuhart",
"Copyright": "Julien Neuhart",
"CreateDate": "2006:09:18 16:27:50-04:00",
"Creator": "Gotenberg",
"Keywords": ["first", "second"],
"Marked": true,
"ModDate": "2006:09:18 16:27:50-04:00",
"PDFVersion": 1.7,
"Producer": "Gotenberg",
"Subject": "Sample",

View File

@@ -446,12 +446,12 @@ Feature: /forms/pdfengines/merge
@flatten
@embed
@bookmarks
Scenario: POST /forms/pdfengines/merge (PDF/A-1b & PDF/UA-1 & Metadata & Watermark & Stamp & Flatten & Embeds & Bookmarks)
Scenario: POST /forms/pdfengines/merge (PDF/A-3b & PDF/UA-1 & Metadata & Watermark & Stamp & Flatten & Embeds & Bookmarks)
Given I have a default Gotenberg container
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 |
| pdfa | PDF/A-1b | field |
| pdfa | PDF/A-3b | field |
| pdfua | true | field |
| metadata | {"Author":"Julien Neuhart","Copyright":"Julien Neuhart","CreateDate":"2006-09-18T16:27:50-04:00","Creator":"Gotenberg","Keywords":["first","second"],"Marked":true,"ModDate":"2006-09-18T16:27:50-04:00","PDFVersion":1.7,"Producer":"Gotenberg","Subject":"Sample","Title":"Sample","Trapped":"Unknown"} | field |
| watermarkSource | text | field |
@@ -477,8 +477,8 @@ Feature: /forms/pdfengines/merge
"""
Page 2
"""
Then the response PDF(s) should be valid "PDF/A-1b" with a tolerance of 12 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/A-3b" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 3 failed rule(s)
Then the response PDF(s) should be flatten
Then the response PDF(s) should have the "embed_1.xml" file embedded
Then the response PDF(s) should have the "embed_2.xml" file embedded
@@ -507,11 +507,8 @@ Feature: /forms/pdfengines/merge
"foo.pdf": {
"Author": "Julien Neuhart",
"Copyright": "Julien Neuhart",
"CreateDate": "2006:09:18 16:27:50-04:00",
"Creator": "Gotenberg",
"Keywords": ["first", "second"],
"Marked": true,
"ModDate": "2006:09:18 16:27:50-04:00",
"PDFVersion": 1.7,
"Producer": "Gotenberg",
"Subject": "Sample",
@@ -595,3 +592,36 @@ Feature: /forms/pdfengines/merge
| files | testdata/page_2.pdf | file |
Then the response status code should be 200
Then the response header "Content-Type" should be "application/pdf"
@convert
@encrypt
Scenario: POST /forms/pdfengines/merge (PDF/A + Encrypt => 400)
Given I have a default Gotenberg container
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 |
| pdfa | PDF/A-1b | field |
| userPassword | secret | field |
Then the response status code should be 400
@convert
@embed
Scenario: POST /forms/pdfengines/merge (PDF/A-1b + Embeds => 400)
Given I have a default Gotenberg container
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 |
| pdfa | PDF/A-1b | field |
| embeds | testdata/embed_1.xml | file |
Then the response status code should be 400
@convert
@embed
Scenario: POST /forms/pdfengines/merge (PDF/A-3b + Embeds => 200)
Given I have a default Gotenberg container
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 |
| pdfa | PDF/A-3b | field |
| embeds | testdata/embed_1.xml | file |
Then the response status code should be 200

View File

@@ -524,13 +524,13 @@ Feature: /forms/pdfengines/split
@stamp
@flatten
@embed
Scenario: POST /forms/pdfengines/split (PDF/A-1b & PDF/UA-1 & Metadata & Watermark & Stamp & Flatten & Embeds)
Scenario: POST /forms/pdfengines/split (PDF/A-3b & PDF/UA-1 & Metadata & Watermark & Stamp & Flatten & Embeds)
Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/split" endpoint with the following form data and header(s):
| files | testdata/pages_3.pdf | file |
| splitMode | intervals | field |
| splitSpan | 2 | field |
| pdfa | PDF/A-1b | field |
| pdfa | PDF/A-3b | field |
| pdfua | true | field |
| metadata | {"Author":"Julien Neuhart","Copyright":"Julien Neuhart","CreateDate":"2006-09-18T16:27:50-04:00","Creator":"Gotenberg","Keywords":["first","second"],"Marked":true,"ModDate":"2006-09-18T16:27:50-04:00","PDFVersion":1.7,"Producer":"Gotenberg","Subject":"Sample","Title":"Sample","Trapped":"Unknown"} | field |
| watermarkSource | text | field |
@@ -560,8 +560,8 @@ Feature: /forms/pdfengines/split
"""
Page 3
"""
Then the response PDF(s) should be valid "PDF/A-1b" with a tolerance of 12 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/A-3b" with a tolerance of 5 failed rule(s)
Then the response PDF(s) should be valid "PDF/UA-1" with a tolerance of 3 failed rule(s)
Then the response PDF(s) should be flatten
Then the response PDF(s) should have the "embed_1.xml" file embedded
Then the response PDF(s) should have the "embed_2.xml" file embedded
@@ -576,11 +576,8 @@ Feature: /forms/pdfengines/split
"pages_3_0.pdf": {
"Author": "Julien Neuhart",
"Copyright": "Julien Neuhart",
"CreateDate": "2006:09:18 16:27:50-04:00",
"Creator": "Gotenberg",
"Keywords": ["first", "second"],
"Marked": true,
"ModDate": "2006:09:18 16:27:50-04:00",
"PDFVersion": 1.7,
"Producer": "Gotenberg",
"Subject": "Sample",
@@ -590,11 +587,8 @@ Feature: /forms/pdfengines/split
"pages_3_1.pdf": {
"Author": "Julien Neuhart",
"Copyright": "Julien Neuhart",
"CreateDate": "2006:09:18 16:27:50-04:00",
"Creator": "Gotenberg",
"Keywords": ["first", "second"],
"Marked": true,
"ModDate": "2006:09:18 16:27:50-04:00",
"PDFVersion": 1.7,
"Producer": "Gotenberg",
"Subject": "Sample",
@@ -720,3 +714,39 @@ Feature: /forms/pdfengines/split
| splitSpan | 2 | field |
Then the response status code should be 200
Then the response header "Content-Type" should be "application/zip"
@convert
@encrypt
Scenario: POST /forms/pdfengines/split (PDF/A + Encrypt => 400)
Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/split" endpoint with the following form data and header(s):
| files | testdata/pages_3.pdf | file |
| splitMode | intervals | field |
| splitSpan | 2 | field |
| pdfa | PDF/A-1b | field |
| userPassword | secret | field |
Then the response status code should be 400
@convert
@embed
Scenario: POST /forms/pdfengines/split (PDF/A-1b + Embeds => 400)
Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/split" endpoint with the following form data and header(s):
| files | testdata/pages_3.pdf | file |
| splitMode | intervals | field |
| splitSpan | 2 | field |
| pdfa | PDF/A-1b | field |
| embeds | testdata/embed_1.xml | file |
Then the response status code should be 400
@convert
@embed
Scenario: POST /forms/pdfengines/split (PDF/A-3b + Embeds => 200)
Given I have a default Gotenberg container
When I make a "POST" request to Gotenberg at the "/forms/pdfengines/split" endpoint with the following form data and header(s):
| files | testdata/pages_3.pdf | file |
| splitMode | intervals | field |
| splitSpan | 2 | field |
| pdfa | PDF/A-3b | field |
| embeds | testdata/embed_1.xml | file |
Then the response status code should be 200