diff --git a/pkg/modules/exiftool/exiftool_test.go b/pkg/modules/exiftool/exiftool_test.go index 52c8f31d..f3e4a6fd 100644 --- a/pkg/modules/exiftool/exiftool_test.go +++ b/pkg/modules/exiftool/exiftool_test.go @@ -91,6 +91,15 @@ func TestExiftool_Split(t *testing.T) { } } +func TestExiftool_Flatten(t *testing.T) { + engine := new(ExifTool) + err := engine.Flatten(context.Background(), zap.NewNop(), "", "") + + if !errors.Is(err, gotenberg.ErrPdfEngineMethodNotSupported) { + t.Errorf("expected error %v, but got: %v", gotenberg.ErrPdfEngineMethodNotSupported, err) + } +} + func TestExiftool_Convert(t *testing.T) { engine := new(ExifTool) err := engine.Convert(context.Background(), zap.NewNop(), gotenberg.PdfFormats{}, "", "") diff --git a/pkg/modules/libreoffice/pdfengine/pdfengine_test.go b/pkg/modules/libreoffice/pdfengine/pdfengine_test.go index 1dc4ed73..c76a2b5a 100644 --- a/pkg/modules/libreoffice/pdfengine/pdfengine_test.go +++ b/pkg/modules/libreoffice/pdfengine/pdfengine_test.go @@ -127,6 +127,15 @@ func TestLibreOfficePdfEngine_Split(t *testing.T) { } } +func TestLibreOfficePdfEngine_Flatten(t *testing.T) { + engine := new(LibreOfficePdfEngine) + err := engine.Flatten(context.Background(), zap.NewNop(), "", "") + + if !errors.Is(err, gotenberg.ErrPdfEngineMethodNotSupported) { + t.Errorf("expected error %v, but got: %v", gotenberg.ErrPdfEngineMethodNotSupported, err) + } +} + func TestLibreOfficePdfEngine_Convert(t *testing.T) { for _, tc := range []struct { scenario string diff --git a/pkg/modules/pdfcpu/pdfcpu_test.go b/pkg/modules/pdfcpu/pdfcpu_test.go index e996aed8..50eff3ff 100644 --- a/pkg/modules/pdfcpu/pdfcpu_test.go +++ b/pkg/modules/pdfcpu/pdfcpu_test.go @@ -239,6 +239,15 @@ func TestPdfCpu_Split(t *testing.T) { } } +func TestPdfCpu_Flatten(t *testing.T) { + mod := new(PdfCpu) + err := mod.Flatten(context.TODO(), zap.NewNop(), "", "") + + if !errors.Is(err, gotenberg.ErrPdfEngineMethodNotSupported) { + t.Errorf("expected error %v, but got: %v", gotenberg.ErrPdfEngineMethodNotSupported, err) + } +} + func TestPdfCpu_Convert(t *testing.T) { mod := new(PdfCpu) err := mod.Convert(context.TODO(), zap.NewNop(), gotenberg.PdfFormats{}, "", "") diff --git a/pkg/modules/pdftk/pdftk_test.go b/pkg/modules/pdftk/pdftk_test.go index 73311f72..857bca23 100644 --- a/pkg/modules/pdftk/pdftk_test.go +++ b/pkg/modules/pdftk/pdftk_test.go @@ -232,6 +232,15 @@ func TestPdfCpu_Split(t *testing.T) { } } +func TestPdfTk_Flatten(t *testing.T) { + engine := new(PdfTk) + err := engine.Flatten(context.TODO(), zap.NewNop(), "", "") + + if !errors.Is(err, gotenberg.ErrPdfEngineMethodNotSupported) { + t.Errorf("expected error %v, but got: %v", gotenberg.ErrPdfEngineMethodNotSupported, err) + } +} + func TestPdfTk_Convert(t *testing.T) { engine := new(PdfTk) err := engine.Convert(context.TODO(), zap.NewNop(), gotenberg.PdfFormats{}, "", "") diff --git a/pkg/modules/qpdf/qpdf.go b/pkg/modules/qpdf/qpdf.go index 898a919b..917c142f 100644 --- a/pkg/modules/qpdf/qpdf.go +++ b/pkg/modules/qpdf/qpdf.go @@ -103,7 +103,22 @@ func (engine *QPdf) Merge(ctx context.Context, logger *zap.Logger, inputPaths [] // Flatten is not available in this implementation. func (engine *QPdf) Flatten(ctx context.Context, logger *zap.Logger, inputPath, outputPath string) error { - return fmt.Errorf("flatten PDF with QPDF: %w", gotenberg.ErrPdfEngineMethodNotSupported) + var args []string + args = append(args, "--flatten-annotations=all") + args = append(args, inputPath) + args = append(args, outputPath) + + cmd, err := gotenberg.CommandContext(ctx, logger, engine.binPath, args...) + if err != nil { + return fmt.Errorf("create command: %w", err) + } + + _, err = cmd.Exec() + if err == nil { + return nil + } + + return fmt.Errorf("flatten PDFs with QPDF: %w", err) } // Convert is not available in this implementation. diff --git a/pkg/modules/qpdf/qpdf_test.go b/pkg/modules/qpdf/qpdf_test.go index 9c79721b..b2ec541f 100644 --- a/pkg/modules/qpdf/qpdf_test.go +++ b/pkg/modules/qpdf/qpdf_test.go @@ -232,6 +232,64 @@ func TestQPdf_Split(t *testing.T) { } } +func TestQPdf_Flatten(t *testing.T) { + for _, tc := range []struct { + scenario string + ctx context.Context + inputPath string + expectError bool + }{ + { + scenario: "invalid context", + ctx: nil, + expectError: true, + }, + { + scenario: "invalid input path", + ctx: context.TODO(), + inputPath: "foo.pdf", + expectError: true, + }, + { + scenario: "success", + ctx: context.TODO(), + inputPath: "/tests/test/testdata/pdfengines/sample3.pdf", + expectError: false, + }, + } { + t.Run(tc.scenario, func(t *testing.T) { + engine := new(QPdf) + err := engine.Provision(nil) + if err != nil { + t.Fatalf("expected error but got: %v", err) + } + + fs := gotenberg.NewFileSystem(new(gotenberg.OsMkdirAll)) + outputDir, err := fs.MkdirAll() + if err != nil { + t.Fatalf("expected error but got: %v", err) + } + + defer func() { + err = os.RemoveAll(fs.WorkingDirPath()) + if err != nil { + t.Fatalf("expected no error while cleaning up but got: %v", err) + } + }() + + err = engine.Flatten(tc.ctx, zap.NewNop(), tc.inputPath, outputDir+"/foo.pdf") + + if !tc.expectError && err != nil { + t.Fatalf("expected no error but got: %v", err) + } + + if tc.expectError && err == nil { + t.Fatal("expected error but got none") + } + }) + } +} + func TestQPdf_Convert(t *testing.T) { engine := new(QPdf) err := engine.Convert(context.TODO(), zap.NewNop(), gotenberg.PdfFormats{}, "", "") diff --git a/test/testdata/pdfengines/sample3.pdf b/test/testdata/pdfengines/sample3.pdf new file mode 100644 index 00000000..d079b892 Binary files /dev/null and b/test/testdata/pdfengines/sample3.pdf differ