Skip to content

Instantly share code, notes, and snippets.

@spaceemotion
Created June 12, 2022 21:37
Show Gist options
  • Save spaceemotion/01a9c94519eaf57fecba4279a8d83cc2 to your computer and use it in GitHub Desktop.
Save spaceemotion/01a9c94519eaf57fecba4279a8d83cc2 to your computer and use it in GitHub Desktop.

Revisions

  1. spaceemotion created this gist Jun 12, 2022.
    61 changes: 61 additions & 0 deletions BrowsershotExtended.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,61 @@
    <?php

    declare(strict_types=1);

    namespace App\Services\Browsershot;

    use RuntimeException;
    use Spatie\Browsershot\Browsershot as SpatieBrowsershot;
    use Spatie\TemporaryDirectory\TemporaryDirectory;

    use function file_put_contents;
    use function is_dir;
    use function mkdir;
    use function sprintf;

    /**
    * Custom overwrites for the base package:
    * - Put all temporary files under a sub-folder inside /tmp
    * - Configure user-data dir
    *
    * @internal
    */
    class BrowsershotExtended extends SpatieBrowsershot
    {
    public function __construct(string $url = '', bool $deviceEmulate = false)
    {
    parent::__construct($url, $deviceEmulate);

    // Keep assets in cache between requests
    if (!is_dir('/tmp/browsershot-user-data') && !mkdir('/tmp/browsershot-user-data')) {
    throw new RuntimeException(sprintf('Directory "%s" was not created', '/tmp/browsershot-user-data'));
    }

    $this->setUserDataDir('/tmp/browsershot-user-data');
    }

    protected function createTemporaryHtmlFile(): string
    {
    $this->temporaryHtmlDirectory = $this->createTemporaryDirectory();

    file_put_contents($temporaryHtmlFile = $this->temporaryHtmlDirectory->path('index.html'), $this->html);

    return "file://{$temporaryHtmlFile}";
    }

    protected function createTemporaryOptionsFile(string $command): string
    {
    $this->temporaryOptionsDirectory = $this->createTemporaryDirectory();

    file_put_contents($temporaryOptionsFile = $this->temporaryOptionsDirectory->path('command.js'), $command);

    return "file://{$temporaryOptionsFile}";
    }

    protected function createTemporaryDirectory(): TemporaryDirectory
    {
    return (new TemporaryDirectory())
    ->location('/tmp/browsershot')
    ->create();
    }
    }
    82 changes: 82 additions & 0 deletions BrowsershotService.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,82 @@
    <?php

    declare(strict_types=1);

    namespace App\Services\Browsershot;

    use App\Services\Pdf\Pdf;
    use Spatie\Browsershot\Browsershot as SpatieBrowsershot;

    class BrowsershotService
    {
    public function forPdf(Pdf $pdf): SpatieBrowsershot
    {
    $instance = new BrowsershotExtended();
    $instance->disableJavascript();
    $instance->dismissDialogs();
    $instance->ignoreHttpsErrors();

    $instance->setHtml($pdf->getContents()->render());
    $instance->landscape($pdf->landscape);
    $instance->scale(1);

    // Allow background colors
    $instance->showBackground();
    $instance->hideBrowserHeaderAndFooter();

    // Set default page sizes
    $instance->format('A4');
    $instance->setOption('preferCssPageSize', true);
    $instance->margins(
    $pdf->marginTop(),
    $pdf->marginRight(),
    $pdf->marginBottom(),
    $pdf->marginLeft(),
    );

    // See https://github.com/alixaxel/chrome-aws-lambda/blob/master/source/index.ts#L94
    $instance->addChromiumArguments([
    'allow-running-insecure-content', // https://source.chromium.org/search?q=lang:cpp+symbol:kAllowRunningInsecureContent&ss=chromium
    'autoplay-policy' => 'user-gesture-required', // https://source.chromium.org/search?q=lang:cpp+symbol:kAutoplayPolicy&ss=chromium
    'disable-component-update', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableComponentUpdate&ss=chromium
    'disable-domain-reliability', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableDomainReliability&ss=chromium
    'disable-features' => 'AudioServiceOutOfProcess,IsolateOrigins,site-per-process', // https://source.chromium.org/search?q=file:content_features.cc&ss=chromium
    'disable-print-preview', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisablePrintPreview&ss=chromium
    'disable-setuid-sandbox', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSetuidSandbox&ss=chromium
    'disable-site-isolation-trials', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSiteIsolation&ss=chromium
    'disable-speech-api', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableSpeechAPI&ss=chromium
    'disable-web-security', // https://source.chromium.org/search?q=lang:cpp+symbol:kDisableWebSecurity&ss=chromium
    'disk-cache-size' => 33554432, // https://source.chromium.org/search?q=lang:cpp+symbol:kDiskCacheSize&ss=chromium
    'enable-features' => 'SharedArrayBuffer', // https://source.chromium.org/search?q=file:content_features.cc&ss=chromium
    'hide-scrollbars', // https://source.chromium.org/search?q=lang:cpp+symbol:kHideScrollbars&ss=chromium
    'ignore-gpu-blocklist', // https://source.chromium.org/search?q=lang:cpp+symbol:kIgnoreGpuBlocklist&ss=chromium
    'in-process-gpu', // https://source.chromium.org/search?q=lang:cpp+symbol:kInProcessGPU&ss=chromium
    'mute-audio', // https://source.chromium.org/search?q=lang:cpp+symbol:kMuteAudio&ss=chromium
    'no-default-browser-check', // https://source.chromium.org/search?q=lang:cpp+symbol:kNoDefaultBrowserCheck&ss=chromium
    'no-pings', // https://source.chromium.org/search?q=lang:cpp+symbol:kNoPings&ss=chromium
    'no-sandbox', // https://source.chromium.org/search?q=lang:cpp+symbol:kNoSandbox&ss=chromium
    'no-zygote', // https://source.chromium.org/search?q=lang:cpp+symbol:kNoZygote&ss=chromium
    'use-gl' => 'swiftshader', // https://source.chromium.org/search?q=lang:cpp+symbol:kUseGl&ss=chromium
    'window-size' => '1920,1080', // https://source.chromium.org/search?q=lang:cpp+symbol:kWindowSize&ss=chromium
    'single-process', // https://source.chromium.org/search?q=lang:cpp+symbol:kSingleProcess&ss=chromium

    // https://github.com/hiroppy/dynamic-rendering-sample/blob/98c40e730bc75ac076080ccd30f6f118414d48d5/rendertron/Dockerfile#L12
    'disable-2d-canvas-clip-aa',
    'disable-accelerated-2d-canvas',
    'disable-breakpad',
    'disable-canvas-aa',
    'disable-desktop-notifications',
    'disable-extensions',
    'disable-gl-drawing-for-tests',
    'disable-permissions-api',
    'disable-sync',
    'no-first-run',
    ]);

    // Hardcode module path to fix npm 8.6 $HOME path problem
    // Coincidentally, this speeds up the warmup time by about 400ms
    $instance->setNodeModulePath('/usr/local/lib/node_modules');

    return $instance;
    }
    }
    50 changes: 50 additions & 0 deletions Dockerfile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,50 @@
    # According to the docs, we should use the vapor-provided packages here,
    # but they seem to be a bit out of date (we do want to have bugfixes asap)

    # See: https://github.com/laravel/vapor-dockerfiles/blob/master/php81.Dockerfile
    FROM php:8.1-fpm-alpine

    # Add php extensions
    ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
    RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions \
    mcrypt \
    redis \
    pdo_mysql \
    pcntl \
    bcmath \
    zip \
    opcache \
    intl

    # Install dependencies
    RUN apk add --no-cache nodejs-current npm

    # Install chrome/chromium + Additional fonts
    RUN apk add --no-cache chromium msttcorefonts-installer fontconfig && \
    update-ms-fonts && \
    fc-cache -f

    # Install puppeteer
    ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
    PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser

    RUN npm install -g puppeteer@^14

    # Prepare Vapor environment
    RUN cp "/etc/ssl/cert.pem" /opt/cert.pem

    COPY runtime/bootstrap /opt/bootstrap
    COPY runtime/bootstrap.php /opt/bootstrap.php
    COPY runtime/php.ini /usr/local/etc/php/php.ini

    RUN chmod 755 /opt/bootstrap
    RUN chmod 755 /opt/bootstrap.php

    COPY . /var/task

    RUN mkdir /.npm && chmod 777 /.npm

    ENTRYPOINT []

    CMD /opt/bootstrap