Skip to content

Instantly share code, notes, and snippets.

@spaceemotion
Created June 12, 2022 21:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spaceemotion/01a9c94519eaf57fecba4279a8d83cc2 to your computer and use it in GitHub Desktop.
Save spaceemotion/01a9c94519eaf57fecba4279a8d83cc2 to your computer and use it in GitHub Desktop.
Browsershot on Vapor
<?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();
}
}
<?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;
}
}
# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment