diff --git a/cmd/gotenberg.go b/cmd/gotenberg.go index 7d46f24d..318e36ef 100644 --- a/cmd/gotenberg.go +++ b/cmd/gotenberg.go @@ -75,7 +75,6 @@ func Run() { go func(app gotenberg.App) { id := app.(gotenberg.Module).Descriptor().ID err = app.Start() - if err != nil { fmt.Printf("[FATAL] starting %s: %s\n", id, err) os.Exit(1) diff --git a/pkg/modules/api/context.go b/pkg/modules/api/context.go index 5efac5be..b5243e8e 100644 --- a/pkg/modules/api/context.go +++ b/pkg/modules/api/context.go @@ -164,7 +164,6 @@ func newContext(echoCtx echo.Context, logger *zap.Logger, fs *gotenberg.FileSyst for _, files := range form.File { for _, fh := range files { err = copyToDisk(fh) - if err != nil { return ctx, cancel, fmt.Errorf("copy to disk: %w", err) } @@ -191,14 +190,11 @@ func (ctx *Context) FormData() *FormData { } } -// GeneratePath generates a path within the context's working directory. It does not create a file. +// GeneratePath generates a path within the context's working directory. // It either generates a new UUID-based filename or uses the provided filename. -func (ctx *Context) GeneratePath(extension string, optionalFilename ...string) string { - var filename string - if len(optionalFilename) > 0 { - // Use the provided filename - filename = optionalFilename[0] - } else { +// It does not create a file. +func (ctx *Context) GeneratePath(filename, extension string) string { + if filename == "" { // Generate a new UUID-based filename filename = uuid.New().String() } @@ -254,7 +250,7 @@ func (ctx *Context) BuildOutputFile() (string, error) { ImplicitTopLevelFolder: false, } - archivePath := ctx.GeneratePath(".zip") + archivePath := ctx.GeneratePath(".zip", "") err := z.Archive(ctx.outputPaths, archivePath) if err != nil { diff --git a/pkg/modules/api/context_test.go b/pkg/modules/api/context_test.go index da9bbd92..0b59c5a0 100644 --- a/pkg/modules/api/context_test.go +++ b/pkg/modules/api/context_test.go @@ -190,11 +190,15 @@ func TestContext_GeneratePath(t *testing.T) { dirPath: "/foo", } - path := ctx.GeneratePath(".pdf") - + path := ctx.GeneratePath("", ".pdf") if !strings.HasPrefix(path, ctx.dirPath) { t.Errorf("expected '%s' to start with '%s'", path, ctx.dirPath) } + + path = ctx.GeneratePath("foo.txt", ".pdf") + if !strings.Contains(path, "foo.txt.pdf") { + t.Errorf("expected '%s' to start with '%s'", path, ctx.dirPath) + } } func TestContext_AddOutputPaths(t *testing.T) { diff --git a/pkg/modules/chromium/routes.go b/pkg/modules/chromium/routes.go index ac4d5d23..97b01691 100644 --- a/pkg/modules/chromium/routes.go +++ b/pkg/modules/chromium/routes.go @@ -494,7 +494,7 @@ func markdownToHtml(ctx *api.Context, inputPath string, markdownPaths []string) ) } - inputPath = ctx.GeneratePath(".html") + inputPath = ctx.GeneratePath("", ".html") err = os.WriteFile(inputPath, buffer.Bytes(), 0o600) if err != nil { @@ -505,7 +505,7 @@ func markdownToHtml(ctx *api.Context, inputPath string, markdownPaths []string) } func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url string, pdfFormats gotenberg.PdfFormats, options PdfOptions) error { - outputPath := ctx.GeneratePath(".pdf") + outputPath := ctx.GeneratePath("", ".pdf") err := chromium.Pdf(ctx, ctx.Log(), url, outputPath, options) err = handleChromiumError(err, url, options.Options) @@ -549,10 +549,9 @@ func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url zeroValued := gotenberg.PdfFormats{} if pdfFormats != zeroValued { convertInputPath := outputPath - convertOutputPath := ctx.GeneratePath(".pdf") + convertOutputPath := ctx.GeneratePath("", ".pdf") err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath) - if err != nil { if errors.Is(err, gotenberg.ErrPdfFormatNotSupported) { return api.WrapError( @@ -581,7 +580,7 @@ func convertUrl(ctx *api.Context, chromium Api, engine gotenberg.PdfEngine, url func screenshotUrl(ctx *api.Context, chromium Api, url string, options ScreenshotOptions) error { ext := fmt.Sprintf(".%s", options.Format) - outputPath := ctx.GeneratePath(ext) + outputPath := ctx.GeneratePath("", ext) err := chromium.Screenshot(ctx, ctx.Log(), url, outputPath, options) err = handleChromiumError(err, url, options.Options) diff --git a/pkg/modules/libreoffice/routes.go b/pkg/modules/libreoffice/routes.go index d05b9080..a6bee965 100644 --- a/pkg/modules/libreoffice/routes.go +++ b/pkg/modules/libreoffice/routes.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" "path/filepath" - "strings" "github.com/labstack/echo/v4" @@ -53,25 +52,11 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap PdfUa: pdfua, } - // We need to check and see if there are any duplicate filenames in inputPaths. - filenameCounts := make(map[string]int) - for _, path := range inputPaths { - filename := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) - filenameCounts[filename]++ - } - // Alright, let's convert each document to PDF. outputPaths := make([]string, len(inputPaths)) for i, inputPath := range inputPaths { - filename := strings.TrimSuffix(filepath.Base(inputPath), filepath.Ext(inputPath)) - extension := filepath.Ext(inputPath) - // Ex: `document.docx`, `document.doc` -> `document.docx.pdf`, `document.doc.pdf` - if filenameCounts[filename] > 1 { - outputPaths[i] = ctx.GeneratePath(".pdf", filename+extension) - } else { - outputPaths[i] = ctx.GeneratePath(".pdf", filename) - } - + // document.docx -> document.docx.pdf. + outputPaths[i] = ctx.GeneratePath(filepath.Base(inputPath), ".pdf") options := libreofficeapi.Options{ Landscape: landscape, PageRanges: nativePageRanges, @@ -108,7 +93,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap // win: if there is only one PDF, skip this step. if len(outputPaths) > 1 && merge { - outputPath := ctx.GeneratePath(".pdf") + outputPath := ctx.GeneratePath("", ".pdf") err = engine.Merge(ctx, ctx.Log(), outputPaths, outputPath) if err != nil { @@ -120,7 +105,7 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap zeroValued := gotenberg.PdfFormats{} if !nativePdfFormats && pdfFormats != zeroValued { convertInputPath := outputPath - convertOutputPath := ctx.GeneratePath(".pdf") + convertOutputPath := ctx.GeneratePath("", ".pdf") err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath) if err != nil { @@ -160,7 +145,8 @@ func convertRoute(libreOffice libreofficeapi.Uno, engine gotenberg.PdfEngine) ap for i, outputPath := range outputPaths { convertInputPath := outputPath - convertOutputPaths[i] = ctx.GeneratePath(".pdf") + // document.docx -> document.docx.pdf. + convertOutputPaths[i] = ctx.GeneratePath(filepath.Base(inputPaths[i]), ".pdf") err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPaths[i]) if err != nil { diff --git a/pkg/modules/libreoffice/routes_test.go b/pkg/modules/libreoffice/routes_test.go index 6dd2971e..4fd75ded 100644 --- a/pkg/modules/libreoffice/routes_test.go +++ b/pkg/modules/libreoffice/routes_test.go @@ -220,6 +220,7 @@ func TestConvertRoute(t *testing.T) { expectError: false, expectHttpError: false, expectOutputPathsCount: 1, + expectFileNames: []string{"/document.docx.pdf"}, }, { scenario: "success (many files)", @@ -228,6 +229,7 @@ func TestConvertRoute(t *testing.T) { ctx.SetFiles(map[string]string{ "document.docx": "/document.docx", "document2.docx": "/document2.docx", + "document2.doc": "/document2.doc", }) return ctx }(), @@ -236,19 +238,21 @@ func TestConvertRoute(t *testing.T) { return nil }, ExtensionsMock: func() []string { - return []string{".docx"} + return []string{".docx", ".doc"} }, }, expectError: false, expectHttpError: false, - expectOutputPathsCount: 2, + expectOutputPathsCount: 3, + expectFileNames: []string{"/document.docx.pdf", "/document2.docx.pdf", "/document2.doc.pdf"}, }, { - scenario: "success with non-native PDF/A & PDF/UA (single file)", + scenario: "success with non-native PDF/A & PDF/UA (many files)", ctx: func() *api.ContextMock { ctx := &api.ContextMock{Context: new(api.Context)} ctx.SetFiles(map[string]string{ - "document.docx": "/document.docx", + "document.docx": "/document.docx", + "document2.docx": "/document2.docx", }) ctx.SetValues(map[string][]string{ "pdfa": { @@ -278,14 +282,16 @@ func TestConvertRoute(t *testing.T) { }, expectError: false, expectHttpError: false, - expectOutputPathsCount: 1, + expectOutputPathsCount: 2, + expectFileNames: []string{"/document.docx.pdf", "/document2.docx.pdf"}, }, { - scenario: "success with native PDF/A & PDF/UA (single file)", + scenario: "success with native PDF/A & PDF/UA (many files)", ctx: func() *api.ContextMock { ctx := &api.ContextMock{Context: new(api.Context)} ctx.SetFiles(map[string]string{ - "document.docx": "/document.docx", + "document.docx": "/document.docx", + "document2.docx": "/document2.docx", }) ctx.SetValues(map[string][]string{ "pdfa": { @@ -312,7 +318,8 @@ func TestConvertRoute(t *testing.T) { }, expectError: false, expectHttpError: false, - expectOutputPathsCount: 1, + expectOutputPathsCount: 2, + expectFileNames: []string{"/document.docx.pdf", "/document2.docx.pdf"}, }, { scenario: "merge error", @@ -494,72 +501,6 @@ func TestConvertRoute(t *testing.T) { expectHttpError: false, expectOutputPathsCount: 1, }, - { - scenario: "success (not merged, unique filenames with different extensions)", - ctx: func() *api.ContextMock { - ctx := &api.ContextMock{Context: new(api.Context)} - ctx.SetFiles(map[string]string{ - "document.docx": "/document.docx", - "document2.docx": "/document2.docx", - }) - ctx.SetValues(map[string][]string{ - "merge": { - "false", - }, - }) - return ctx - }(), - libreOffice: &libreofficeapi.ApiMock{ - PdfMock: func(ctx context.Context, logger *zap.Logger, inputPath, outputPath string, options libreofficeapi.Options) error { - return nil - }, - ExtensionsMock: func() []string { - return []string{".docx", ".doc"} - }, - }, - engine: &gotenberg.PdfEngineMock{ - MergeMock: func(ctx context.Context, logger *zap.Logger, inputPaths []string, outputPath string) error { - return nil - }, - }, - expectError: false, - expectHttpError: false, - expectOutputPathsCount: 2, - }, - { - scenario: "success (not merged, duplicate filenames with different extensions)", - ctx: func() *api.ContextMock { - ctx := &api.ContextMock{Context: new(api.Context)} - ctx.SetFiles(map[string]string{ - "document.docx": "/document.docx", - "document2.docx": "/document2.docx", - "document2.doc": "/document2.doc", - }) - ctx.SetValues(map[string][]string{ - "merge": { - "false", - }, - }) - return ctx - }(), - libreOffice: &libreofficeapi.ApiMock{ - PdfMock: func(ctx context.Context, logger *zap.Logger, inputPath, outputPath string, options libreofficeapi.Options) error { - return nil - }, - ExtensionsMock: func() []string { - return []string{".docx", ".doc"} - }, - }, - engine: &gotenberg.PdfEngineMock{ - MergeMock: func(ctx context.Context, logger *zap.Logger, inputPaths []string, outputPath string) error { - return nil - }, - }, - expectError: false, - expectHttpError: false, - expectOutputPathsCount: 3, - expectFileNames: []string{"document.pdf", "document2.docx.pdf", "document2.doc.pdf"}, - }, { scenario: "success with non-native PDF/A & PDF/UA (merge)", ctx: func() *api.ContextMock { @@ -686,8 +627,10 @@ func TestConvertRoute(t *testing.T) { t.Errorf("expected %d output paths but got %d", tc.expectOutputPathsCount, len(tc.ctx.OutputPaths())) } - if !reflect.DeepEqual(tc.ctx.OutputPaths(), tc.expectFileNames) { - t.Errorf("expected output paths %v, got %v", tc.expectFileNames, tc.ctx.OutputPaths()) + if len(tc.expectFileNames) != 0 { + if !reflect.DeepEqual(tc.ctx.OutputPaths(), tc.expectFileNames) { + t.Errorf("expected output paths %v, got %v", tc.expectFileNames, tc.ctx.OutputPaths()) + } } }) } diff --git a/pkg/modules/pdfengines/routes.go b/pkg/modules/pdfengines/routes.go index 6728e0b7..df41116f 100644 --- a/pkg/modules/pdfengines/routes.go +++ b/pkg/modules/pdfengines/routes.go @@ -43,7 +43,7 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route { // Alright, let's merge the PDFs. - outputPath := ctx.GeneratePath(".pdf") + outputPath := ctx.GeneratePath("", ".pdf") err = engine.Merge(ctx, ctx.Log(), inputPaths, outputPath) if err != nil { @@ -56,10 +56,9 @@ func mergeRoute(engine gotenberg.PdfEngine) api.Route { zeroValued := gotenberg.PdfFormats{} if pdfFormats != zeroValued { convertInputPath := outputPath - convertOutputPath := ctx.GeneratePath(".pdf") + convertOutputPath := ctx.GeneratePath("", ".pdf") err = engine.Convert(ctx, ctx.Log(), pdfFormats, convertInputPath, convertOutputPath) - if err != nil { if errors.Is(err, gotenberg.ErrPdfFormatNotSupported) { return api.WrapError( @@ -137,10 +136,9 @@ func convertRoute(engine gotenberg.PdfEngine) api.Route { outputPaths := make([]string, len(inputPaths)) for i, inputPath := range inputPaths { - outputPaths[i] = ctx.GeneratePath(".pdf") + outputPaths[i] = ctx.GeneratePath("", ".pdf") err = engine.Convert(ctx, ctx.Log(), pdfFormats, inputPath, outputPaths[i]) - if err != nil { if errors.Is(err, gotenberg.ErrPdfFormatNotSupported) { return api.WrapError(