mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-15 11:52:14 +01:00
refactor(pdfengines): embeds => attachments
This commit is contained in:
@@ -1,27 +1,28 @@
|
||||
//nolint:dupl
|
||||
package pdfengines
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
|
||||
)
|
||||
|
||||
type multiPdfEngines struct {
|
||||
mergeEngines []gotenberg.PdfEngine
|
||||
splitEngines []gotenberg.PdfEngine
|
||||
flattenEngines []gotenberg.PdfEngine
|
||||
convertEngines []gotenberg.PdfEngine
|
||||
readMetadataEngines []gotenberg.PdfEngine
|
||||
writeMetadataEngines []gotenberg.PdfEngine
|
||||
passwordEngines []gotenberg.PdfEngine
|
||||
embedEngines []gotenberg.PdfEngine
|
||||
readBookmarksEngines []gotenberg.PdfEngine
|
||||
writeBookmarksEngines []gotenberg.PdfEngine
|
||||
mergeEngines []gotenberg.PdfEngine
|
||||
splitEngines []gotenberg.PdfEngine
|
||||
flattenEngines []gotenberg.PdfEngine
|
||||
convertEngines []gotenberg.PdfEngine
|
||||
readMetadataEngines []gotenberg.PdfEngine
|
||||
writeMetadataEngines []gotenberg.PdfEngine
|
||||
passwordEngines []gotenberg.PdfEngine
|
||||
attachmentsEngines []gotenberg.PdfEngine
|
||||
}
|
||||
|
||||
func newMultiPdfEngines(
|
||||
@@ -32,27 +33,26 @@ func newMultiPdfEngines(
|
||||
readMetadataEngines,
|
||||
writeMetadataEngines,
|
||||
passwordEngines,
|
||||
embedEngines,
|
||||
readBookmarksEngines,
|
||||
writeBookmarksEngines []gotenberg.PdfEngine,
|
||||
attachmentsEngines []gotenberg.PdfEngine,
|
||||
) *multiPdfEngines {
|
||||
return &multiPdfEngines{
|
||||
mergeEngines: mergeEngines,
|
||||
splitEngines: splitEngines,
|
||||
flattenEngines: flattenEngines,
|
||||
convertEngines: convertEngines,
|
||||
readMetadataEngines: readMetadataEngines,
|
||||
writeMetadataEngines: writeMetadataEngines,
|
||||
passwordEngines: passwordEngines,
|
||||
embedEngines: embedEngines,
|
||||
readBookmarksEngines: readBookmarksEngines,
|
||||
writeBookmarksEngines: writeBookmarksEngines,
|
||||
mergeEngines: mergeEngines,
|
||||
splitEngines: splitEngines,
|
||||
flattenEngines: flattenEngines,
|
||||
convertEngines: convertEngines,
|
||||
readMetadataEngines: readMetadataEngines,
|
||||
writeMetadataEngines: writeMetadataEngines,
|
||||
passwordEngines: passwordEngines,
|
||||
attachmentsEngines: attachmentsEngines,
|
||||
}
|
||||
}
|
||||
|
||||
// Merge combines multiple PDF files into a single document using the first
|
||||
// available engine that supports PDF merging.
|
||||
func (multi *multiPdfEngines) Merge(ctx context.Context, logger *zap.Logger, inputPaths []string, outputPath string) error {
|
||||
func (multi *multiPdfEngines) Merge(ctx context.Context, logger *slog.Logger, inputPaths []string, outputPath string) error {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "multiPdfEngines.Merge", trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
@@ -72,7 +72,10 @@ func (multi *multiPdfEngines) Merge(ctx context.Context, logger *zap.Logger, inp
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("merge PDFs with multi PDF engines: %w", err)
|
||||
err = fmt.Errorf("merge PDFs with multi PDF engines: %w", err)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
type splitResult struct {
|
||||
@@ -82,7 +85,10 @@ type splitResult struct {
|
||||
|
||||
// Split divides the PDF into separate pages using the first available engine
|
||||
// that supports PDF splitting.
|
||||
func (multi *multiPdfEngines) Split(ctx context.Context, logger *zap.Logger, mode gotenberg.SplitMode, inputPath, outputDirPath string) ([]string, error) {
|
||||
func (multi *multiPdfEngines) Split(ctx context.Context, logger *slog.Logger, mode gotenberg.SplitMode, inputPath, outputDirPath string) ([]string, error) {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "multiPdfEngines.Split", trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
var mu sync.Mutex // to safely append errors.
|
||||
|
||||
@@ -108,12 +114,18 @@ func (multi *multiPdfEngines) Split(ctx context.Context, logger *zap.Logger, mod
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("split PDF with multi PDF engines: %w", err)
|
||||
err = fmt.Errorf("split PDF with multi PDF engines: %w", err)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Flatten merges existing annotation appearances with page content using the
|
||||
// first available engine that supports flattening.
|
||||
func (multi *multiPdfEngines) Flatten(ctx context.Context, logger *zap.Logger, inputPath string) error {
|
||||
func (multi *multiPdfEngines) Flatten(ctx context.Context, logger *slog.Logger, inputPath string) error {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "multiPdfEngines.Flatten", trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
@@ -133,12 +145,18 @@ func (multi *multiPdfEngines) Flatten(ctx context.Context, logger *zap.Logger, i
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("flatten PDF with multi PDF engines: %w", err)
|
||||
err = fmt.Errorf("flatten PDF with multi PDF engines: %w", err)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// Convert transforms the given PDF to a specific PDF format using the first
|
||||
// available engine that supports PDF conversion.
|
||||
func (multi *multiPdfEngines) Convert(ctx context.Context, logger *zap.Logger, formats gotenberg.PdfFormats, inputPath, outputPath string) error {
|
||||
func (multi *multiPdfEngines) Convert(ctx context.Context, logger *slog.Logger, formats gotenberg.PdfFormats, inputPath, outputPath string) error {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "multiPdfEngines.Convert", trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
@@ -158,7 +176,10 @@ func (multi *multiPdfEngines) Convert(ctx context.Context, logger *zap.Logger, f
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("convert PDF to '%+v' with multi PDF engines: %w", formats, err)
|
||||
err = fmt.Errorf("convert PDF to '%+v' with multi PDF engines: %w", formats, err)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
type readMetadataResult struct {
|
||||
@@ -168,7 +189,10 @@ type readMetadataResult struct {
|
||||
|
||||
// ReadMetadata extracts metadata from a PDF file using the first available
|
||||
// engine that supports metadata reading.
|
||||
func (multi *multiPdfEngines) ReadMetadata(ctx context.Context, logger *zap.Logger, inputPath string) (map[string]any, error) {
|
||||
func (multi *multiPdfEngines) ReadMetadata(ctx context.Context, logger *slog.Logger, inputPath string) (map[string]any, error) {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "multiPdfEngines.ReadMetadata", trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
var mu sync.Mutex // to safely append errors.
|
||||
|
||||
@@ -194,12 +218,18 @@ func (multi *multiPdfEngines) ReadMetadata(ctx context.Context, logger *zap.Logg
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("read PDF metadata with multi PDF engines: %w", err)
|
||||
err = fmt.Errorf("read PDF metadata with multi PDF engines: %w", err)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// WriteMetadata embeds metadata into a PDF file using the first available
|
||||
// WriteMetadata attachments metadata into a PDF file using the first available
|
||||
// engine that supports metadata writing.
|
||||
func (multi *multiPdfEngines) WriteMetadata(ctx context.Context, logger *zap.Logger, metadata map[string]any, inputPath string) error {
|
||||
func (multi *multiPdfEngines) WriteMetadata(ctx context.Context, logger *slog.Logger, metadata map[string]any, inputPath string) error {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "multiPdfEngines.WriteMetadata", trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
@@ -219,73 +249,18 @@ func (multi *multiPdfEngines) WriteMetadata(ctx context.Context, logger *zap.Log
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("write PDF metadata with multi PDF engines: %w", err)
|
||||
}
|
||||
|
||||
type readBookmarksResult struct {
|
||||
bookmarks []gotenberg.Bookmark
|
||||
err error
|
||||
}
|
||||
|
||||
// ReadBookmarks reads bookmarks from a PDF file using the first available
|
||||
// engine that supports bookmarks reading.
|
||||
func (multi *multiPdfEngines) ReadBookmarks(ctx context.Context, logger *zap.Logger, inputPath string) ([]gotenberg.Bookmark, error) {
|
||||
var err error
|
||||
var mu sync.Mutex // to safely append errors.
|
||||
|
||||
for _, engine := range multi.readBookmarksEngines {
|
||||
resultChan := make(chan readBookmarksResult, 1)
|
||||
|
||||
go func(engine gotenberg.PdfEngine) {
|
||||
bookmarks, err := engine.ReadBookmarks(ctx, logger, inputPath)
|
||||
resultChan <- readBookmarksResult{bookmarks: bookmarks, err: err}
|
||||
}(engine)
|
||||
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
if result.err != nil {
|
||||
mu.Lock()
|
||||
err = multierr.Append(err, result.err)
|
||||
mu.Unlock()
|
||||
} else {
|
||||
return result.bookmarks, nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("read PDF bookmarks with multi PDF engines: %w", err)
|
||||
}
|
||||
|
||||
// WriteBookmarks adds a document outline (bookmarks) to a PDF file using the
|
||||
// first available engine that supports bookmarks writing.
|
||||
func (multi *multiPdfEngines) WriteBookmarks(ctx context.Context, logger *zap.Logger, inputPath string, bookmarks []gotenberg.Bookmark) error {
|
||||
var err error
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
for _, engine := range multi.writeBookmarksEngines {
|
||||
go func(engine gotenberg.PdfEngine) {
|
||||
errChan <- engine.WriteBookmarks(ctx, logger, inputPath, bookmarks)
|
||||
}(engine)
|
||||
|
||||
select {
|
||||
case writeBookmarksErr := <-errChan:
|
||||
errored := multierr.AppendInto(&err, writeBookmarksErr)
|
||||
if !errored {
|
||||
return nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("write PDF bookmarks with multi PDF engines: %w", err)
|
||||
err = fmt.Errorf("write PDF metadata with multi PDF engines: %w", err)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// Encrypt adds password protection to a PDF file using the first available
|
||||
// engine that supports password protection.
|
||||
func (multi *multiPdfEngines) Encrypt(ctx context.Context, logger *zap.Logger, inputPath, userPassword, ownerPassword string) error {
|
||||
func (multi *multiPdfEngines) Encrypt(ctx context.Context, logger *slog.Logger, inputPath, userPassword, ownerPassword string) error {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "multiPdfEngines.Encrypt", trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
@@ -305,23 +280,29 @@ func (multi *multiPdfEngines) Encrypt(ctx context.Context, logger *zap.Logger, i
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("encrypt PDF using multi PDF engines: %w", err)
|
||||
err = fmt.Errorf("encrypt PDF using multi PDF engines: %w", err)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// EmbedFiles embeds files into a PDF using the first available
|
||||
// engine that supports file embedding.
|
||||
func (multi *multiPdfEngines) EmbedFiles(ctx context.Context, logger *zap.Logger, filePaths []string, inputPath string) error {
|
||||
// AddAttachments adds attachments into a PDF using the first available
|
||||
// engine that supports file attaching.
|
||||
func (multi *multiPdfEngines) AddAttachments(ctx context.Context, logger *slog.Logger, filePaths []string, inputPath string) error {
|
||||
ctx, span := gotenberg.Tracer().Start(ctx, "multiPdfEngines.AddAttachments", trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
|
||||
var err error
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
for _, engine := range multi.embedEngines {
|
||||
for _, engine := range multi.attachmentsEngines {
|
||||
go func(engine gotenberg.PdfEngine) {
|
||||
errChan <- engine.EmbedFiles(ctx, logger, filePaths, inputPath)
|
||||
errChan <- engine.AddAttachments(ctx, logger, filePaths, inputPath)
|
||||
}(engine)
|
||||
|
||||
select {
|
||||
case embedErr := <-errChan:
|
||||
errored := multierr.AppendInto(&err, embedErr)
|
||||
case attachmentsErr := <-errChan:
|
||||
errored := multierr.AppendInto(&err, attachmentsErr)
|
||||
if !errored {
|
||||
return nil
|
||||
}
|
||||
@@ -330,7 +311,10 @@ func (multi *multiPdfEngines) EmbedFiles(ctx context.Context, logger *zap.Logger
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("embed files into PDF using multi PDF engines: %w", err)
|
||||
err = fmt.Errorf("add attachments into PDF using multi PDF engines: %w", err)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// Interface guards.
|
||||
|
||||
Reference in New Issue
Block a user