Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement wkhtmltopdf backend #503

Draft
wants to merge 10 commits into
base: v2-unstable
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/Backend/Dompdf/DompdfAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function generateFromDOMDocument(DOMDocument $DOMDocument): StreamInterfa
public function generateFromHtmlFile(SplFileInfo $file): StreamInterface
{
$dompdf = $this->buildDompdf();
$dompdf->loadHtmlFile($file->getPath());
$dompdf->loadHtmlFile($file->getPathname());

return $this->createStream($dompdf);
}
Expand Down
150 changes: 150 additions & 0 deletions src/Backend/Dompdf/Tests/DompdfAdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\Dompdf\Tests;

use DOMDocument;
use KNPLabs\Snappy\Backend\Dompdf\DompdfAdapter;
use KNPLabs\Snappy\Backend\Dompdf\DompdfFactory;
use KNPLabs\Snappy\Core\Backend\Options;
use KNPLabs\Snappy\Core\Backend\Options\PageOrientation;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

final class DompdfAdapterTest extends TestCase
{
private DompdfAdapter $adapter;
private Options $options;
private StreamFactoryInterface $streamFactoryMock;
private string $tempDir;

protected function setUp(): void
{
$this->tempDir = sys_get_temp_dir();
$this->options = new Options(null, [
'output' => __DIR__,
'construct' => [
'chroot' => $this->tempDir,
]
]);

$this->streamFactoryMock = $this->createMock(StreamFactoryInterface::class);

$factory = new DompdfFactory($this->streamFactoryMock);
$this->adapter = $factory->create($this->options);
}

public function testGenerateFromDOMDocument(): void
{
$domDocument = new DOMDocument();
$domDocument->loadHTML('<html><body>Hello World</body></html>');

$expectedStreamMock = $this->createMock(StreamInterface::class);
$this->streamFactoryMock
->expects($this->once())
->method('createStream')
->with($this->isType('string'))
->willReturn($expectedStreamMock);

$output = $this->adapter->generateFromDOMDocument($domDocument);

$this->assertSame($expectedStreamMock, $output);
}

public function testGenerateFromHtmlFile(): void
{
$tempFilePath = $this->tempDir . '/test.html';
file_put_contents($tempFilePath, '<html><body>Temporary Test File</body></html>');
$fileMock = new \SplFileInfo($tempFilePath);

$expectedStreamMock = $this->createMock(StreamInterface::class);
$this->streamFactoryMock
->expects($this->once())
->method('createStream')
->with($this->isType('string'))
->willReturn($expectedStreamMock);

$output = $this->adapter->generateFromHtmlFile($fileMock);

$this->assertSame($expectedStreamMock, $output);

if (file_exists($tempFilePath)) {
unlink($tempFilePath);
}
}

public function testGenerateFromHtml(): void
{
$htmlContent = '<html><body>Test HTML content</body></html>';

$expectedStreamMock = $this->createMock(StreamInterface::class);
$this->streamFactoryMock
->expects($this->once())
->method('createStream')
->willReturn($expectedStreamMock);

$output = $this->adapter->generateFromHtml($htmlContent);

$this->assertSame($expectedStreamMock, $output);
}

public function testGenerateFromInvalidHtml(): void
{
$invalidHtmlContent = '<html><body><h1>Unclosed Header';

$expectedStreamMock = $this->createMock(StreamInterface::class);
$this->streamFactoryMock
->expects($this->once())
->method('createStream')
->willReturn($expectedStreamMock);

$output = $this->adapter->generateFromHtml($invalidHtmlContent);

$this->assertSame($expectedStreamMock, $output);
}

public function testGenerateFromEmptyHtml(): void
{
$htmlContent = '';

$expectedStreamMock = $this->createMock(StreamInterface::class);
$this->streamFactoryMock
->expects($this->once())
->method('createStream')
->willReturn($expectedStreamMock);

$output = $this->adapter->generateFromHtml($htmlContent);

$this->assertSame($expectedStreamMock, $output);
}

public function testStreamContentFromHtml(): void
{
$htmlContent = '<html><body>Test Content</body></html>';
$expectedOutput = 'PDF content for Test Content';

$this->streamFactoryMock
->method('createStream')
->willReturn($this->createStreamWithContent($expectedOutput));

$output = $this->adapter->generateFromHtml($htmlContent);
$this->assertSame($expectedOutput, $output->getContents());
}

private function createStreamWithContent(string $content): StreamInterface
{
$streamMock = $this->createMock(StreamInterface::class);
$streamMock->method('getContents')->willReturn($content);
return $streamMock;
}

public function testOptionsHandling(): void
{
$this->options = new Options(PageOrientation::LANDSCAPE, []);
$this->adapter = (new DompdfFactory($this->streamFactoryMock))->create($this->options);

$this->assertTrue(true);
}
}
12 changes: 12 additions & 0 deletions src/Backend/WkHtmlToPdf/ExtraOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf;

interface ExtraOption {
public function isRepeatable(): bool;

/** @return array<string|int|float> */
public function compile(): array;
}
24 changes: 24 additions & 0 deletions src/Backend/WkHtmlToPdf/ExtraOption/Allow.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

class Allow implements ExtraOption
{
public function __construct(private readonly string $path)
{
}

public function isRepeatable(): bool
{
return true;
}

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

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

class BypassProxyFor implements ExtraOption
{
public function __construct(private readonly string $value)
{
}

public function isRepeatable(): bool
{
return true;
}

public function compile(): array
{
return ['--bypass-proxy-for', $this->value];
}
}
24 changes: 24 additions & 0 deletions src/Backend/WkHtmlToPdf/ExtraOption/CacheDir.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

class CacheDir implements ExtraOption
{
public function __construct(private readonly string $path)
{
}

public function isRepeatable(): bool
{
return false;
}

public function compile(): array
{
return ['--cache-dir', $this->path];
}
}
24 changes: 24 additions & 0 deletions src/Backend/WkHtmlToPdf/ExtraOption/CheckBoxSvg.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

class CheckBoxSvg implements ExtraOption
{
public function __construct(private readonly string $path)
{
}

public function isRepeatable(): bool
{
return false;
}

public function compile(): array
{
return ['--checkbox-svg', $this->path];
}
}
24 changes: 24 additions & 0 deletions src/Backend/WkHtmlToPdf/ExtraOption/CheckboxCheckedSvg.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

class CheckboxCheckedSvg implements ExtraOption
{
public function __construct(private readonly string $path)
{
}

public function isRepeatable(): bool
{
return false;
}

public function compile(): array
{
return ['--checkbox-checked-svg', $this->path];
}
}
24 changes: 24 additions & 0 deletions src/Backend/WkHtmlToPdf/ExtraOption/Cookie.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

class Cookie implements ExtraOption
{
public function __construct(private readonly string $name, private readonly string $value)
{
}

public function isRepeatable(): bool
{
return true;
}

public function compile(): array
{
return ['--cookie', $this->name, $this->value];
}
}
22 changes: 22 additions & 0 deletions src/Backend/WkHtmlToPdf/ExtraOption/CookieJar.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\ExtraOption;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

final class CookieJar implements ExtraOption
{
public function __construct(public readonly string $path) {}

public function isRepeatable(): bool
{
return false;
}

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

declare(strict_types=1);

namespace KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

use KNPLabs\Snappy\Backend\WkHtmlToPdf\ExtraOption;

final class Copies implements ExtraOption
{
/**
* @param positive-int $number
*/
public function __construct(private readonly int $number) {}

public function isRepeatable(): bool
{
return false;
}

public function compile(): array
{
return ['--copies', $this->number];
}
}
Loading
Loading