From 0f0a6ce042e58a36a80ce9168238367434c6c052 Mon Sep 17 00:00:00 2001 From: Julien Neuhart Date: Thu, 25 Apr 2024 17:24:23 +0200 Subject: [PATCH] feat(chromium): add clip options to screenshot routes --- pkg/modules/chromium/browser.go | 1 + pkg/modules/chromium/browser_test.go | 49 ++++++++++++++++++++++++++++ pkg/modules/chromium/chromium.go | 16 +++++++++ pkg/modules/chromium/routes.go | 8 +++++ pkg/modules/chromium/routes_test.go | 12 +++++++ pkg/modules/chromium/tasks.go | 21 ++++++++++++ 6 files changed, 107 insertions(+) diff --git a/pkg/modules/chromium/browser.go b/pkg/modules/chromium/browser.go index 060f2acb..d7a30326 100644 --- a/pkg/modules/chromium/browser.go +++ b/pkg/modules/chromium/browser.go @@ -258,6 +258,7 @@ func (b *chromiumBrowser) screenshot(ctx context.Context, logger *zap.Logger, ur waitDelayBeforePrintActionFunc(logger, b.arguments.disableJavaScript, options.WaitDelay), waitForExpressionBeforePrintActionFunc(logger, b.arguments.disableJavaScript, options.WaitForExpression), // Screenshot specific. + setDeviceMetricsOverride(logger, options.Width, options.Height), captureScreenshotActionFunc(logger, outputPath, options), }) } diff --git a/pkg/modules/chromium/browser_test.go b/pkg/modules/chromium/browser_test.go index 89d8a937..4591f7b3 100644 --- a/pkg/modules/chromium/browser_test.go +++ b/pkg/modules/chromium/browser_test.go @@ -1996,6 +1996,54 @@ func TestChromiumBrowser_screenshot(t *testing.T) { "no emulated media type", "no wait delay", "no wait expression", + "set device metrics override", + }, + }, + { + scenario: "success (clip)", + browser: newChromiumBrowser( + browserArguments{ + binPath: os.Getenv("CHROMIUM_BIN_PATH"), + wsUrlReadTimeout: 5 * time.Second, + allowList: regexp2.MustCompile("", 0), + denyList: regexp2.MustCompile("", 0), + }, + ), + fs: func() *gotenberg.FileSystem { + fs := gotenberg.NewFileSystem() + + err := os.MkdirAll(fs.WorkingDirPath(), 0o755) + if err != nil { + t.Fatalf(fmt.Sprintf("expected no error but got: %v", err)) + } + + err = os.WriteFile(fmt.Sprintf("%s/index.html", fs.WorkingDirPath()), []byte("

Default options

"), 0o755) + if err != nil { + t.Fatalf("expected no error but got: %v", err) + } + + return fs + }(), + options: func() ScreenshotOptions { + options := DefaultScreenshotOptions() + options.Clip = true + return options + }(), + noDeadline: false, + start: true, + expectError: false, + expectedLogEntries: []string{ + "cache not cleared", + "cookies not cleared", + "JavaScript not disabled", + "no cookies to set", + "no extra HTTP headers", + "navigate to", + "default white background not hidden", + "no emulated media type", + "no wait delay", + "no wait expression", + "set device metrics override", }, }, { @@ -2042,6 +2090,7 @@ func TestChromiumBrowser_screenshot(t *testing.T) { "no emulated media type", "no wait delay", "no wait expression", + "set device metrics override", }, }, } { diff --git a/pkg/modules/chromium/chromium.go b/pkg/modules/chromium/chromium.go index 1b6fd79d..cbf96181 100644 --- a/pkg/modules/chromium/chromium.go +++ b/pkg/modules/chromium/chromium.go @@ -239,6 +239,19 @@ func DefaultPdfOptions() PdfOptions { type ScreenshotOptions struct { Options + // Width is the device screen width in pixels. + // Optional. + Width int + + // Height is the device screen height in pixels. + // Optional. + Height int + + // Clip defines whether to clip the screenshot according to the device + // dimensions. + // Optional. + Clip bool + // Format is the image compression format, either "png" or "jpeg" or // "webp". // Optional. @@ -258,6 +271,9 @@ type ScreenshotOptions struct { func DefaultScreenshotOptions() ScreenshotOptions { return ScreenshotOptions{ Options: DefaultOptions(), + Width: 800, + Height: 600, + Clip: false, Format: "png", Quality: 100, OptimizeForSpeed: false, diff --git a/pkg/modules/chromium/routes.go b/pkg/modules/chromium/routes.go index d45203e5..a0436540 100644 --- a/pkg/modules/chromium/routes.go +++ b/pkg/modules/chromium/routes.go @@ -182,12 +182,17 @@ func FormDataChromiumScreenshotOptions(ctx *api.Context) (*api.FormData, Screens defaultScreenshotOptions := DefaultScreenshotOptions() var ( + width, height int + clip bool format string quality int optimizeForSpeed bool ) form. + Int("width", &width, defaultScreenshotOptions.Width). + Int("height", &height, defaultScreenshotOptions.Height). + Bool("clip", &clip, defaultScreenshotOptions.Clip). Custom("format", func(value string) error { if value == "" { format = defaultScreenshotOptions.Format @@ -228,6 +233,9 @@ func FormDataChromiumScreenshotOptions(ctx *api.Context) (*api.FormData, Screens screenshotOptions := ScreenshotOptions{ Options: options, + Width: width, + Height: height, + Clip: clip, Format: format, Quality: quality, OptimizeForSpeed: optimizeForSpeed, diff --git a/pkg/modules/chromium/routes_test.go b/pkg/modules/chromium/routes_test.go index af0d4885..050fa769 100644 --- a/pkg/modules/chromium/routes_test.go +++ b/pkg/modules/chromium/routes_test.go @@ -385,6 +385,15 @@ func TestFormDataChromiumScreenshotOptions(t *testing.T) { ctx: func() *api.ContextMock { ctx := &api.ContextMock{Context: new(api.Context)} ctx.SetValues(map[string][]string{ + "width": { + "1280", + }, + "height": { + "800", + }, + "clip": { + "true", + }, "optimizeForSpeed": { "true", }, @@ -396,6 +405,9 @@ func TestFormDataChromiumScreenshotOptions(t *testing.T) { }(), expectedOptions: func() ScreenshotOptions { options := DefaultScreenshotOptions() + options.Width = 1280 + options.Height = 800 + options.Clip = true options.OptimizeForSpeed = true options.EmulatedMediaType = "screen" return options diff --git a/pkg/modules/chromium/tasks.go b/pkg/modules/chromium/tasks.go index 4047dde7..2c593da6 100644 --- a/pkg/modules/chromium/tasks.go +++ b/pkg/modules/chromium/tasks.go @@ -121,6 +121,14 @@ func captureScreenshotActionFunc(logger *zap.Logger, outputPath string, options WithOptimizeForSpeed(options.OptimizeForSpeed). WithFormat(page.CaptureScreenshotFormat(options.Format)) + if options.Clip { + captureScreenshot = captureScreenshot.WithClip(&page.Viewport{ + Width: float64(options.Width), + Height: float64(options.Height), + Scale: 1, + }) + } + if options.Format == "jpeg" { captureScreenshot = captureScreenshot. WithQuality(int64(options.Quality)) @@ -154,6 +162,19 @@ func captureScreenshotActionFunc(logger *zap.Logger, outputPath string, options } } +func setDeviceMetricsOverride(logger *zap.Logger, width, height int) chromedp.ActionFunc { + return func(ctx context.Context) error { + logger.Debug("set device metrics override") + + err := emulation.SetDeviceMetricsOverride(int64(width), int64(height), 1.0, false).Do(ctx) + if err == nil { + return nil + } + + return fmt.Errorf("set device metrics override: %w", err) + } +} + func clearCacheActionFunc(logger *zap.Logger, clear bool) chromedp.ActionFunc { return func(ctx context.Context) error { // See https://github.com/gotenberg/gotenberg/issues/753.