Skip to content

Commit

Permalink
feat: implement wkhtmltopdf backend
Browse files Browse the repository at this point in the history
  • Loading branch information
alexpozzi committed Oct 16, 2024
1 parent 3ed9af8 commit 527d5b3
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 4 deletions.
Empty file.
21 changes: 21 additions & 0 deletions src/Backend/WkHtmlToPdf/OptionGroup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf;

abstract class OptionGroup {
/** @return array<string|int|float> */
public function compile(): array
{
$options = [];

foreach ($this as $property) {
if ($property instanceof Option) {
$options = array_merge($options, $property->compile());
}
}

return $options;
}
}
22 changes: 22 additions & 0 deletions src/Backend/WkHtmlToPdf/Options/GlobalOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\Options;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\OptionGroup;
use KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions\CollateOption;
use KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions\CookieJarOption;
use KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions\NoCollateOption;
use KNPLabs\Snappy\Core\Backend\Options\PageOrientation;

final class GlobalOptions extends OptionGroup
{
public function __construct(
public readonly ?CollateOption $collate,
public readonly ?NoCollateOption $noCollate,
public readonly ?CookieJarOption $cookieJar,
public readonly ?PageOrientation $pageOrientation,
) {
}
}
17 changes: 17 additions & 0 deletions src/Backend/WkHtmlToPdf/Options/GlobalOptions/CollateOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\Option;

final class CollateOption implements Option
{
public function __construct() {}

public function compile(): array
{
return ['--collate'];
}
}
15 changes: 15 additions & 0 deletions src/Backend/WkHtmlToPdf/Options/GlobalOptions/CookieJarOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions;

final class CookieJarOption
{
public function __construct(public readonly string $path) {}

public function compile(): array
{
return ['--no-collate'];
}
}
15 changes: 15 additions & 0 deletions src/Backend/WkHtmlToPdf/Options/GlobalOptions/CopiesOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions;

final class CopiesOption
{
public function __construct(private readonly int $number) {}

public function compile(): array
{
return ['--copies', $this->number];
}
}
15 changes: 15 additions & 0 deletions src/Backend/WkHtmlToPdf/Options/GlobalOptions/DpiOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions;

final class DpiOptions
{
public function __construct(private readonly int $dpi) {}

public function compile(): array
{
return ['--dpi', $this->dpi];
}
}
15 changes: 15 additions & 0 deletions src/Backend/WkHtmlToPdf/Options/GlobalOptions/GrayscaleOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions;

final class GrayscaleOption
{
public function __construct() {}

public function compile(): array
{
return ['--grayscale'];
}
}
10 changes: 10 additions & 0 deletions src/Backend/WkHtmlToPdf/Options/GlobalOptions/ImageDpiOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions;

final class ImageDpiOption
{
public function __construct(public readonly int $dpi) {}
}
10 changes: 10 additions & 0 deletions src/Backend/WkHtmlToPdf/Options/GlobalOptions/NoCollateOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\Options\GlobalOptions;

final class NoCollateOption
{
public function __construct() {}
}
122 changes: 118 additions & 4 deletions src/Backend/WkHtmlToPdf/WkHtmlToPdfAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf;

use KNPLabs\Snappy\Core\Backend\Adapter;
use KNPLabs\Snappy\Core\Backend\Adapter\HtmlFileToPdf;
use KNPLabs\Snappy\Core\Backend\Adapter\Reconfigurable;
use KNPLabs\Snappy\Core\Backend\Adapter\UriToPdf;
use KNPLabs\Snappy\Core\Backend\Options;
use KNPLabs\Snappy\Core\Backend\Options\PageOrientation;
use KNPLabs\Snappy\Core\Stream\FileStream;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use Symfony\Component\Process\Process;
use SplFileInfo;

final class WkHtmlToPdfAdapter implements HtmlFileToPdf
final class WkHtmlToPdfAdapter implements HtmlFileToPdf, UriToPdf
{
/**
* @use Reconfigurable<self>
Expand All @@ -26,14 +32,122 @@ public function __construct(
private string $binary,
private int $timeout,
WkHtmlToPdfFactory $factory,
Options $options
Options $options,
private readonly StreamFactoryInterface $streamFactory,
private readonly UriFactoryInterface $uriFactory,
) {
$this->factory = $factory;
$this->options = $options;
}

public function generateFromHtmlFile(SplFileInfo $file): StreamInterface
{
throw new \Exception("Not implemented for {$this->binary} with timeout {$this->timeout}.");
$filepath = $file->getRealPath();

if ($filepath === false) {
throw new \RuntimeException("File not found: {$file->getPathname()}.");
}

return $this->generateFromUri(
$this->uriFactory->createUri($filepath)->withScheme('file')
);
}

public function generateFromUri(UriInterface $uri): StreamInterface
{
$outputStream = FileStream::createTmpFile($this->streamFactory);

$process = new Process(
command: [
$this->binary,
...$this->compileOptions(),
$uri->toString(),
$outputStream->file->getPathname(),
],
timeout: $this->timeout,
);

return $outputStream;
}

/**
* @return array<string|int|float>
*/
private function compileOptions(): array
{
return array_merge(
$this->compileGlobalOptions(),
$this->compileOutlineOptions(),
$this->compilePageOptions(),
$this->compileHeadersAndFootersOptions(),
$this->compileTocOptions(),
);
}

/**
* @return array<string, mixed>
*/
private function compileGlobalOptions(): array
{
if (isset($this->options->extraOptions['global']) && \is_array($this->options->extraOptions['global'])) {
return $this->options->extraOptions['global'];
}

if ($this->options->pageOrientation !== null) {
$options['orientation'] = match ($this->options->pageOrientation) {
PageOrientation::PORTRAIT => 'Portrait',
PageOrientation::LANDSCAPE => 'Landscape',
};
}

return [];
}

/**
* @return array<string, mixed>
*/
private function compileOutlineOptions(): array
{
if (isset($this->options->extraOptions['outline']) && \is_array($this->options->extraOptions['outline'])) {
return $this->options->extraOptions['outline'];
}

return [];
}

/**
* @return array<string, mixed>
*/
private function compilePageOptions(): array
{
if (isset($this->options->extraOptions['page']) && \is_array($this->options->extraOptions['page'])) {
return $this->options->extraOptions['page'];
}

return [];
}

/**
* @return array<string, mixed>
*/
private function compileHeadersAndFootersOptions(): array
{
if (isset($this->options->extraOptions['headersAndFooters']) && \is_array($this->options->extraOptions['headersAndFooters'])) {
return $this->options->extraOptions['headersAndFooters'];
}

return [];
}

/**
* @return array<string, mixed>
*/
private function compileTocOptions(): array
{
if (isset($this->options->extraOptions['toc']) && \is_array($this->options->extraOptions['toc'])) {
return $this->options->extraOptions['toc'];
}

return [];
}
}

0 comments on commit 527d5b3

Please sign in to comment.