diff --git a/pkg/modules/chromium/chromium.go b/pkg/modules/chromium/chromium.go index f1a0cfae..485004e8 100644 --- a/pkg/modules/chromium/chromium.go +++ b/pkg/modules/chromium/chromium.go @@ -11,6 +11,7 @@ import ( "sync" "time" + "github.com/chromedp/cdproto/emulation" "github.com/chromedp/cdproto/fetch" "github.com/chromedp/cdproto/network" "github.com/chromedp/cdproto/page" @@ -30,6 +31,10 @@ var ( // allowed/denied lists. ErrURLNotAuthorized = errors.New("URL not authorized") + // ErrInvalidEmulatedMediaType happens if the emulated media type is not + // "screen" nor "print". Empty value are allowed though. + ErrInvalidEmulatedMediaType = errors.New("invalid emulated media type") + // ErrInvalidEvaluationExpression happens if an evaluation expression // returns an exception or undefined. ErrInvalidEvaluationExpression = errors.New("invalid evaluation expression") @@ -89,6 +94,11 @@ type Options struct { // Optional. ExtraHTTPHeaders map[string]string + // EmulatedMediaType is the media type to emulate, either "screen" or + // "print". + // Optional. + EmulatedMediaType string + // Landscape sets the paper orientation. // Optional. Landscape bool @@ -161,6 +171,7 @@ func DefaultOptions() Options { WaitForExpression: "", UserAgent: "", ExtraHTTPHeaders: nil, + EmulatedMediaType: "", Landscape: false, PrintBackground: false, Scale: 1.0, @@ -446,6 +457,28 @@ func (mod Chromium) PDF(ctx context.Context, logger *zap.Logger, URL, outputPath return fmt.Errorf("add CSS for exact colors: %w", err) }), + chromedp.ActionFunc(func(ctx context.Context) error { + if options.EmulatedMediaType == "" { + logger.Debug("no emulated media type") + + return nil + } + + if options.EmulatedMediaType != "screen" && options.EmulatedMediaType != "print" { + return fmt.Errorf("validate emulated media type '%s': %w", options.EmulatedMediaType, ErrInvalidEmulatedMediaType) + } + + logger.Debug(fmt.Sprintf("emulate media type '%s'", options.EmulatedMediaType)) + + emulatedMedia := emulation.SetEmulatedMedia() + + err := emulatedMedia.WithMedia(options.EmulatedMediaType).Do(ctx) + if err == nil { + return nil + } + + return fmt.Errorf("emulate media type '%s': %w", options.EmulatedMediaType, err) + }), chromedp.ActionFunc(func(ctx context.Context) error { if options.WaitDelay > 0 { // We wait for a given amount of time so that JavaScript diff --git a/pkg/modules/chromium/chromium_test.go b/pkg/modules/chromium/chromium_test.go index c8ee8507..c9adcb16 100644 --- a/pkg/modules/chromium/chromium_test.go +++ b/pkg/modules/chromium/chromium_test.go @@ -264,6 +264,25 @@ func TestChromium_PDF(t *testing.T) { }, }, }, + { + URL: "file:///tests/test/testdata/chromium/html/sample8/index.html", + options: Options{ + EmulatedMediaType: "foo", + }, + expectErr: true, + }, + { + URL: "file:///tests/test/testdata/chromium/html/sample8/index.html", + options: Options{ + EmulatedMediaType: "screen", + }, + }, + { + URL: "file:///tests/test/testdata/chromium/html/sample8/index.html", + options: Options{ + EmulatedMediaType: "print", + }, + }, { URL: "file:///tests/test/testdata/chromium/html/sample4/index.html", options: Options{ @@ -300,6 +319,13 @@ func TestChromium_PDF(t *testing.T) { WaitForExpression: "window.status === 'ready'", }, }, + { + URL: "file:///tests/test/testdata/chromium/html/sample4/index.html", + options: Options{ + WaitForExpression: "return undefined", + }, + expectErr: true, + }, { URL: "file:///tests/test/testdata/chromium/html/sample4/index.html", options: Options{ diff --git a/pkg/modules/chromium/routes.go b/pkg/modules/chromium/routes.go index eec620da..039d8a93 100644 --- a/pkg/modules/chromium/routes.go +++ b/pkg/modules/chromium/routes.go @@ -31,6 +31,7 @@ func FormDataChromiumPDFOptions(ctx *api.Context) (*api.FormData, Options) { waitForExpression string userAgent string extraHTTPHeaders map[string]string + emulatedMediaType string landscape, printBackground bool scale, paperWidth, paperHeight float64 marginTop, marginBottom, marginLeft, marginRight float64 @@ -58,6 +59,7 @@ func FormDataChromiumPDFOptions(ctx *api.Context) (*api.FormData, Options) { return nil }). + String("emulatedMediaType", &emulatedMediaType, defaultOptions.EmulatedMediaType). Bool("landscape", &landscape, defaultOptions.Landscape). Bool("printBackground", &printBackground, defaultOptions.PrintBackground). Float64("scale", &scale, defaultOptions.Scale). @@ -78,6 +80,7 @@ func FormDataChromiumPDFOptions(ctx *api.Context) (*api.FormData, Options) { WaitForExpression: waitForExpression, UserAgent: userAgent, ExtraHTTPHeaders: extraHTTPHeaders, + EmulatedMediaType: emulatedMediaType, Landscape: landscape, PrintBackground: printBackground, Scale: scale, @@ -294,6 +297,16 @@ func convertURL(ctx *api.Context, chromium API, engine gotenberg.PDFEngine, URL, ) } + if errors.Is(err, ErrInvalidEmulatedMediaType) { + return api.WrapError( + fmt.Errorf("convert to PDF: %w", err), + api.NewSentinelHTTPError( + http.StatusBadRequest, + fmt.Sprintf("The media type '%s' (emulatedMediaType) is invalid: allowed values are 'screen' or 'print'", options.EmulatedMediaType), + ), + ) + } + if errors.Is(err, ErrInvalidEvaluationExpression) { if options.WaitForExpression == "" { // We do not expect the 'waitWindowStatus' form field to return diff --git a/pkg/modules/chromium/routes_test.go b/pkg/modules/chromium/routes_test.go index b218bee5..0ee95bce 100644 --- a/pkg/modules/chromium/routes_test.go +++ b/pkg/modules/chromium/routes_test.go @@ -544,6 +544,21 @@ func TestConvertURL(t *testing.T) { expectHTTPErr: true, expectHTTPStatus: http.StatusForbidden, }, + { + ctx: &api.MockContext{Context: &api.Context{}}, + api: func() API { + chromiumAPI := struct{ ProtoAPI }{} + chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error { + return ErrInvalidEmulatedMediaType + } + + return chromiumAPI + }(), + options: DefaultOptions(), + expectErr: true, + expectHTTPErr: true, + expectHTTPStatus: http.StatusBadRequest, + }, { ctx: &api.MockContext{Context: &api.Context{}}, api: func() API { diff --git a/test/testdata/chromium/html/sample8/index.html b/test/testdata/chromium/html/sample8/index.html new file mode 100644 index 00000000..b0d42d76 --- /dev/null +++ b/test/testdata/chromium/html/sample8/index.html @@ -0,0 +1,19 @@ + + +
+ +Print media type
+Screen media type
+ + \ No newline at end of file