From d46fe55c2d7f794ef81e1ed9d96fc2497c03ca86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kulh=C3=A1nek?= Date: Fri, 12 Feb 2021 00:22:43 +0100 Subject: [PATCH] Default classes --- src/Driver/AbstractPDF.php | 414 +++++++++++++++++++++++++++ src/Driver/Browserless.php | 250 ++++++++++++++++ src/Driver/SelfHostedBrowserless.php | 15 + src/Exception/ApiException.php | 7 + 4 files changed, 686 insertions(+) create mode 100644 src/Driver/AbstractPDF.php create mode 100644 src/Driver/Browserless.php create mode 100644 src/Driver/SelfHostedBrowserless.php create mode 100644 src/Exception/ApiException.php diff --git a/src/Driver/AbstractPDF.php b/src/Driver/AbstractPDF.php new file mode 100644 index 0000000..1265f38 --- /dev/null +++ b/src/Driver/AbstractPDF.php @@ -0,0 +1,414 @@ +format = $format; + return $this; + } + + public function setMargin( + ?string $top, + ?string $right = NULL, + ?string $bottom = NULL, + ?string $left = NULL + ): self + { + if (func_num_args() === 1) { + $this->marginTop = $top; + $this->marginRight = $top; + $this->marginBottom = $top; + $this->marginLeft = $top; + } elseif (func_num_args() === 2) { + $this->marginTop = $top; + $this->marginRight = $right; + $this->marginBottom = $top; + $this->marginLeft = $right; + } elseif (func_num_args() === 3) { + $this->marginTop = $top; + $this->marginRight = $right; + $this->marginBottom = $bottom; + $this->marginLeft = $right; + } else { + $this->marginTop = $top; + $this->marginRight = $right; + $this->marginBottom = $bottom; + $this->marginLeft = $left; + } + return $this; + } + + /** + * Sets the `waitUntil` option in the Chrome `goto` call. + * This option defines at what point in the document lifecycle should + * the PDF engine begin rendering. + * + * @param string|null $until + * @return self + */ + public function setWaitUntil(?string $until): self + { + $this->waitUntil = $until; + return $this; + } + + /** + * Sets the page ranges to render. + * Any page not specified is not contained in the final PDF + * + * @param string $ranges e.g., 1,2,5-7 + * @return self + */ + public function setPageRanges(?string $ranges): self + { + $this->pageRanges = $ranges; + return $this; + } + + /** + * Sets whether or not background graphics should be rendered + * + * @param bool $shouldPrintBackground + * @return self + */ + public function setPrintBackground(bool $shouldPrintBackground): self + { + $this->printBackground = $shouldPrintBackground; + return $this; + } + + /** + * Sets a media type to emulate + * + * @param string|null $emulateMedia E.g., print or screen + * @return self + */ + public function setMediaEmulation(?string $emulateMedia): self + { + $this->emulateMedia = $emulateMedia; + return $this; + } + + /** + * Sets the rendering scale + * + * @param float|null $scale + * @return self + */ + public function setScale(?float $scale): self + { + $this->scale = $scale; + return $this; + } + + /** + * Sets whether to display the header and footer + * + * @param boolean $displayHeaderFooter + * @return self + */ + public function setDisplayHeaderFooter(?bool $displayHeaderFooter = TRUE): self + { + $this->displayHeaderFooter = $displayHeaderFooter; + return $this; + } + + /** + * Sets the header content, automatically enabling `setDisplayHeaderFooter` if necessary + * + * @param ?string $header + * @return self + */ + public function setHeader(?string $header): self + { + $this->header = $header; + $this->setDisplayHeaderFooter($this->getHeader() !== NULL || $this->getFooter() !== NULL); + return $this; + } + + /** + * Sets the footer content, automatically enabling `setDisplayHeaderFooter` if necessary + * + * @param ?string $footer + * @return self + */ + public function setFooter(?string $footer): self + { + $this->footer = $footer; + $this->setDisplayHeaderFooter($this->getHeader() !== NULL || $this->getFooter() !== NULL); + return $this; + } + + /** + * Sets the paper width, overridden by `setFormat` + * + * @param mixed $width + * @return self + */ + public function setWidth($width): self + { + $this->width = $width; + return $this; + } + + /** + * Sets the paper height, overridden by `setFormat` + * + * @param mixed $height + * @return self + */ + public function setHeight($height): self + { + $this->height = $height; + return $this; + } + + /** + * Sets whether `@page` CSS declarations should take priority + * over `width`, `height` or `format` + * + * @param null|bool $prefer + * @return self + */ + public function setPreferCSSPageSize(?bool $prefer): self + { + $this->preferCSSPageSize = $prefer; + return $this; + } + + /** + * Sets whether the paper orentiation should be landscape + * + * @param bool $landscape + * @return self + */ + public function setLandscape(bool $landscape): self + { + $this->landscape = $landscape; + return $this; + } + + /** + * Gets the paper format + * + * @return string + */ + public function getFormat(): string + { + return $this->format; + } + + /** + * @return string|null + */ + public function getMarginTop(): ?string + { + return $this->marginTop; + } + + /** + * @return string|null + */ + public function getMarginRight(): ?string + { + return $this->marginRight; + } + + /** + * @return string|null + */ + public function getMarginBottom(): ?string + { + return $this->marginBottom; + } + + /** + * @return string|null + */ + public function getMarginLeft(): ?string + { + return $this->marginLeft; + } + + /** + * Get the event which triggers the PDF engine to begin rendering + * + * @return string|null + */ + public function getWaitUntil(): ?string + { + return $this->waitUntil; + } + + /** + * Gets the page ranges to render + * + * @return string|null + */ + public function getPageRanges(): ?string + { + return $this->pageRanges; + } + + /** + * Gets whether background graphics will be rendered + * + * @return bool + */ + public function getPrintBackground(): bool + { + return $this->printBackground; + } + + /** + * Get the emulated media type + * + * @return string|null + */ + public function getMediaEmulation(): ?string + { + return $this->emulateMedia; + } + + /** + * Gets the document scale + * + * @return float|null + */ + public function getScale(): ?float + { + return $this->scale; + } + + /** + * Gets whether or not the header and footer will be displayed + * + * @return bool|null + */ + public function getDisplayHeaderFooter(): ?bool + { + return $this->displayHeaderFooter; + } + + /** + * Get the header contents + * + * @return string|null + */ + public function getHeader(): ?string + { + return $this->header; + } + + /** + * Get the footer contents + * + * @return string|null + */ + public function getFooter(): ?string + { + return $this->footer; + } + + /** + * Get the page width + * + * @return mixed + */ + public function getWidth() + { + return $this->width; + } + + /** + * Get the page height + * + * @return mixed + */ + public function getHeight() + { + return $this->height; + } + + /** + * Gets whether `@page` CSS declarations should take priority + * over `width`, `height` or `format` + * + * @return bool|null + */ + public function getPreferCSSPageSize(): ?bool + { + return $this->preferCSSPageSize; + } + + /** + * Gets whether the page orientation should be landscape + * + * @return bool|null + */ + public function getLandscape(): ?bool + { + return $this->landscape; + } + + /** + * Renders a string of HTML to a PDF + * + * @param string $content Content to render + * @return string + */ + abstract public function renderContent(string $content); + + /** + * Renders a URL to a PDF + * + * @param string $content URL to render + * @return resource + */ + abstract public function renderURL(string $url); + + /** + * Renders a local file to a PDF + * + * @param string $content Local file to render + * @return resource + */ + abstract public function renderFile(string $path); +} \ No newline at end of file diff --git a/src/Driver/Browserless.php b/src/Driver/Browserless.php new file mode 100644 index 0000000..b2a87a1 --- /dev/null +++ b/src/Driver/Browserless.php @@ -0,0 +1,250 @@ +client = $client; + if ($apiKey !== NULL) { + $this->setApiKey($apiKey); + } + } + + /** + * Sets the PDF documents rotation + * + * @param int $rotation The number of degrees to rotate the document by + * @return self + */ + public function setRotation(int $rotation = NULL): self + { + $this->rotate = $rotation; + return $this; + } + + /** + * Sets the browserless API key + * + * @param string $apiKey + * @return self + */ + public function setApiKey(string $apiKey): self + { + $this->apiKey = $apiKey; + return $this; + } + + /** + * Sets whether or not to ask Browserless to attempt to render the document in safe mode + * + * @link https://docs.browserless.io/docs/pdf.html#safemode + * @param bool $safeMode + * @return self + */ + public function setSafeMode(bool $safeMode): self + { + $this->safeMode = $safeMode; + return $this; + } + + /** + * Sets the maximum time the PDF renderer should be prepared to spend rendering + * + * @param int $milliseconds + * @return self + */ + public function setTimeout(int $milliseconds = NULL): self + { + $this->timeout = $milliseconds; + return $this; + } + + /** + * Retrieves the rendering timeout + * + * @return int|null + */ + public function getTimeout(): ?int + { + return $this->timeout; + } + + /** + * Retrieves the browserless.io API key + * + * @return string|null + */ + public function getApiKey(): ?string + { + return $this->apiKey; + } + + /** + * Whether the document will be rendered in safe mode or not + * + * @return bool + */ + public function getSafeMode(): bool + { + return $this->safeMode; + } + + /** + * Gets the documents rotation angle in degrees + * + * @return int|null + */ + public function getRotation(): ?int + { + return $this->rotate; + } + + /** + * Gets the payload of JSON options to be sent to browserless, minus the `url` or `html` property + * + * @return array + */ + public function getFormattedOptions(): array + { + $pdfOptions = []; + if ($this->getDisplayHeaderFooter() !== NULL) { + $pdfOptions['displayHeaderFooter'] = $this->getDisplayHeaderFooter(); + } + if ($this->getFooter() !== NULL) { + $pdfOptions['footerTemplate'] = $this->getFooter(); + } + if ($this->getFormat() !== NULL) { + $pdfOptions['format'] = $this->getFormat(); + } + if ($this->getHeader() !== NULL) { + $pdfOptions['headerTemplate'] = $this->getHeader(); + } + if ($this->getLandscape() !== NULL) { + $pdfOptions['landscape'] = $this->getLandscape(); + } + $margin = [ + 'top' => $this->getMarginTop(), + 'right' => $this->getMarginRight(), + 'bottom' => $this->getMarginBottom(), + 'left' => $this->getMarginLeft(), + ]; + $margin = array_filter($margin); + if (!empty($margin)) { + $pdfOptions['margin'] = $margin; + } + + if ($this->getPageRanges() !== NULL) { + $pdfOptions['pageRanges'] = $this->getPageRanges(); + } + if ($this->getPreferCSSPageSize() !== NULL) { + $pdfOptions['preferCSSPageSize'] = $this->getPreferCSSPageSize(); + } + if ($this->getPrintBackground() !== NULL) { + $pdfOptions['printBackground'] = $this->getPrintBackground(); + } + if ($this->getScale() !== NULL) { + $pdfOptions['scale'] = $this->getScale(); + } + if ($this->getWidth() !== NULL) { + $pdfOptions['width'] = $this->getWidth(); + } + if ($this->getHeight() !== NULL) { + $pdfOptions['height'] = $this->getHeight(); + } + + $options = [ + 'options' => $pdfOptions, + 'safeMode' => $this->getSafeMode(), + ]; + + $goto = []; + if ($this->getWaitUntil() !== NULL) { + $goto['waitUntil'] = $this->getWaitUntil(); + } + if ($this->getTimeout() !== NULL) { + $goto['timeout'] = $this->getTimeout(); + } + if (!empty($goto)) { + $options['gotoOptions'] = $goto; + } + + if ($this->getRotation() !== NULL) { + $options['rotate'] = $this->getRotation(); + } + + if ($this->getMediaEmulation() !== NULL) { + $options['emulateMedia'] = $this->getMediaEmulation(); + } + + return $options; + } + + /** + * @param array $options + * @return string + */ + private function render(array $options): string + { + try { + + $requestOptions = [ + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'body' => json_encode($options), + ]; + if ($this->getApiKey()) { + $requestOptions['query']['token'] = $this->getApiKey(); + } + /** @var HttpClient\Response\TraceableResponse $response */ + $response = $this->client->request('POST', $this->apiUrl . $this->pdfEndpoint, $requestOptions); + return $response->getContent(); + } catch (ClientException $e) { + throw new APIException("Failed to render PDF: {$e->getMessage()}", $e->getCode(), $e); + } + } + + /** + * @inheritdoc + */ + public function renderContent(string $content) + { + $options = $this->getFormattedOptions(); + $options['html'] = $content; + return $this->render($options); + } + + /** + * @inheritdoc + */ + public function renderURL(string $url) + { + $options = $this->getFormattedOptions(); + $options['url'] = $url; + return $this->render($options); + } + + /** + * @inheritdoc + */ + public function renderFile(string $path) + { + $options = $this->getFormattedOptions(); + $options['html'] = file_get_contents($path); + return $this->render($options); + } +} \ No newline at end of file diff --git a/src/Driver/SelfHostedBrowserless.php b/src/Driver/SelfHostedBrowserless.php new file mode 100644 index 0000000..0c6e02d --- /dev/null +++ b/src/Driver/SelfHostedBrowserless.php @@ -0,0 +1,15 @@ +apiUrl = $apiUrl; + parent::__construct($client, $apiKey); + } +} \ No newline at end of file diff --git a/src/Exception/ApiException.php b/src/Exception/ApiException.php new file mode 100644 index 0000000..7df5157 --- /dev/null +++ b/src/Exception/ApiException.php @@ -0,0 +1,7 @@ +