Skip to content

Commit

Permalink
Automatically add assets from twig
Browse files Browse the repository at this point in the history
Fixes #7
  • Loading branch information
StevenRenaux authored Mar 4, 2024
1 parent c448e19 commit 9777047
Show file tree
Hide file tree
Showing 19 changed files with 214 additions and 49 deletions.
18 changes: 17 additions & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

use Sensiolabs\GotenbergBundle\Client\GotenbergClient;
use Sensiolabs\GotenbergBundle\Client\GotenbergClientInterface;
use Sensiolabs\GotenbergBundle\Formatter\AssetBaseDirFormatter;
use Sensiolabs\GotenbergBundle\Pdf\Gotenberg;
use Sensiolabs\GotenbergBundle\Pdf\GotenbergInterface;
use Sensiolabs\GotenbergBundle\Twig\GotenbergAssetExtension;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use function Symfony\Component\DependencyInjection\Loader\Configurator\abstract_arg;
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
Expand All @@ -17,7 +20,7 @@
->args([
service('sensiolabs_gotenberg.client'),
abstract_arg('user configuration options'),
param('kernel.project_dir'),
service('sensiolabs_gotenberg.asset.base_dir_formatter'),
service('twig')->nullOnInvalid(),
])
->public()
Expand All @@ -30,4 +33,17 @@
])
->public()
->alias(GotenbergClientInterface::class, 'sensiolabs_gotenberg.client');

$services->set('sensiolabs_gotenberg.asset.base_dir_formatter', AssetBaseDirFormatter::class)
->args([
service(Filesystem::class),
param('kernel.project_dir'),
abstract_arg('base_directory to assets'),
])
->alias(AssetBaseDirFormatter::class, 'sensiolabs_gotenberg.asset.base_dir_formatter')
;

$services->set('sensiolabs_gotenberg.twig.asset_extension', GotenbergAssetExtension::class)
->tag('twig.extension')
;
};
1 change: 1 addition & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The default configuration for the bundle looks like :
sensiolabs_gotenberg:
base_uri: 'http://localhost:3000'
base_directory: '%kernel.project_dir%'
options:
paper_width: null # 8.5
paper_height: null # 11
Expand Down
22 changes: 9 additions & 13 deletions docs/customization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ already set in your ``sensiolabs_gotenberg.yml``.
Additional Assets
-----------------

You can reference any file (like images, fonts, stylesheets, and so on...)
from within a template.
If a template needs to link to a static asset (e.g. an image), this bundle provides an gotenberg_asset()
Twig function to help generate that path.

.. warning::
The only requirement is that their paths in the template file are on the root level.
This function work as `asset() Twig function`_ and fetch your assets in the public folder of your application
If your files are in another folder, you can override the default value of ``base_directory`` in your
configuration file ``config/sensiolabs_gotenberg.yml``.
The path provided can be relative as well as absolute.

.. code-block:: html

Expand All @@ -32,28 +34,21 @@ from within a template.
<title>PDF body</title>
</head>
<body>
<img src="ceo.jpeg" />
<img src="admin.jpeg" />
<img src="{{ gotenberg_asset('public/img/ceo.jpeg') }}" alt="CEO"/>
<img src="{{ gotenberg_asset('public/img/admin.jpeg') }}" alt="Admin"/>
<main>
<h1>Hello world!</h1>
</main>
</body>
</html>

When building the PDF, you just have to use the ``assets`` method to add any
asset referenced in the template to the request.

.. code-block:: php
use Sensiolabs\GotenbergBundle\Pdf\Gotenberg;
$twigPdfBuilder = $gotenberg->twig();
$twigPdfBuilder
->content('path/to/template.html.twig')
->assets(
'assets/images/profiles/ceo.jpeg',
'assets/images/profiles/admin.jpeg',
)
->generate()
.. tip::
Expand Down Expand Up @@ -339,6 +334,7 @@ Enable PDF for Universal Access for optimal accessibility.

For more information about `pdf formats`_.

.. _asset() Twig function: https://symfony.com/doc/current/templates.html#linking-to-css-javascript-and-image-assets
.. _assets: https://gotenberg.dev/docs/routes#html-file-into-pdf-route
.. _defaults properties: https://gotenberg.dev/docs/routes#page-properties-chromium
.. _Header and footer: https://gotenberg.dev/docs/routes#header--footer
Expand Down
17 changes: 10 additions & 7 deletions src/Builder/AbstractChromiumPdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Sensiolabs\GotenbergBundle\Enum\PdfPart;
use Sensiolabs\GotenbergBundle\Exception\ExtraHttpHeadersJsonEncodingException;
use Sensiolabs\GotenbergBundle\Exception\PdfPartRenderingException;
use Sensiolabs\GotenbergBundle\Formatter\AssetBaseDirFormatter;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\File as DataPartFile;
use Twig\Environment;
Expand All @@ -14,10 +15,10 @@ abstract class AbstractChromiumPdfBuilder extends AbstractPdfBuilder
{
public function __construct(
GotenbergClientInterface $gotenbergClient,
string $projectDir,
AssetBaseDirFormatter $asset,
private readonly ?Environment $twig = null,
) {
parent::__construct($gotenbergClient, $projectDir);
parent::__construct($gotenbergClient, $asset);
}

/**
Expand Down Expand Up @@ -232,9 +233,11 @@ public function assets(string ...$paths): static
*/
public function addAsset(string $path): static
{
$dataPart = new DataPart(new DataPartFile($this->resolveFilePath($path)));
$resolvedPath = $this->asset->resolve($path);

$this->formFields['assets'][$path] = $dataPart;
$dataPart = new DataPart(new DataPartFile($resolvedPath));

$this->formFields['assets'][$resolvedPath] = $dataPart;

return $this;
}
Expand Down Expand Up @@ -411,7 +414,7 @@ public function getMultipartFormData(): array
protected function withPdfPartFile(PdfPart $pdfPart, string $path): static
{
$dataPart = new DataPart(
new DataPartFile($this->resolveFilePath($path)),
new DataPartFile($this->asset->resolve($path)),
$pdfPart->value,
);

Expand All @@ -432,9 +435,9 @@ protected function withRenderedPart(PdfPart $pdfPart, string $template, array $c
}

try {
$html = $this->twig->render($template, $context);
$html = $this->twig->render($template, array_merge($context, ['_builder' => $this]));
} catch (\Throwable $error) {
throw new PdfPartRenderingException(sprintf('Could not render template "%s" into PDF part "%s".', $template, $pdfPart->value), previous: $error);
throw new PdfPartRenderingException(sprintf('Could not render template "%s" into PDF part "%s". %s', $template, $pdfPart->value, $error->getMessage()), previous: $error);
}

$this->formFields[$pdfPart->value] = new DataPart($html, $pdfPart->value, 'text/html');
Expand Down
10 changes: 3 additions & 7 deletions src/Builder/AbstractPdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Sensiolabs\GotenbergBundle\Client\GotenbergClientInterface;
use Sensiolabs\GotenbergBundle\Client\PdfResponse;
use Sensiolabs\GotenbergBundle\Exception\MissingRequiredFieldException;
use Sensiolabs\GotenbergBundle\Formatter\AssetBaseDirFormatter;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\String\UnicodeString;

Expand All @@ -17,7 +18,7 @@ abstract class AbstractPdfBuilder implements PdfBuilderInterface

public function __construct(
protected readonly GotenbergClientInterface $gotenbergClient,
protected readonly string $projectDir,
protected readonly AssetBaseDirFormatter $asset,
) {
}

Expand Down Expand Up @@ -64,16 +65,11 @@ public function setConfigurations(array $configurations): static
*/
protected function assertFileExtension(string $path, array $validExtensions): void
{
$file = new File($this->resolveFilePath($path));
$file = new File($this->asset->resolve($path));
$extension = $file->getExtension();

if (!\in_array($extension, $validExtensions, true)) {
throw new \InvalidArgumentException(sprintf('The file extension "%s" is not available in Gotenberg.', $extension));
}
}

protected function resolveFilePath(string $path): string
{
return str_starts_with($path, '/') ? $path : $this->projectDir.'/'.$path;
}
}
2 changes: 1 addition & 1 deletion src/Builder/LibreOfficePdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public function files(string ...$paths): self
foreach ($paths as $path) {
$this->assertFileExtension($path, self::AVAILABLE_EXTENSIONS);

$dataPart = new DataPart(new DataPartFile($this->resolveFilePath($path)));
$dataPart = new DataPart(new DataPartFile($this->asset->resolve($path)));

$this->formFields['files'][$path] = $dataPart;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Builder/MarkdownPdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function files(string ...$paths): self
foreach ($paths as $path) {
$this->assertFileExtension($path, ['md']);

$dataPart = new DataPart(new DataPartFile($this->resolveFilePath($path)));
$dataPart = new DataPart(new DataPartFile($this->asset->resolve($path)));

$this->formFields['files'][$path] = $dataPart;
}
Expand Down
5 changes: 5 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public function getConfigTreeBuilder(): TreeBuilder
->thenInvalid('Invalid API Gotenberg host.')
->end()
->end()
->scalarNode('base_directory')
->info('Base directory will be used for assets, files, markdown')
->defaultValue('%kernel.project_dir%')
->cannotBeEmpty()
->end()
->arrayNode('default_options')
->addDefaultsIfNotSet()
->children()
Expand Down
5 changes: 4 additions & 1 deletion src/DependencyInjection/SensiolabsGotenbergExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ public function load(array $configs, ContainerBuilder $container): void

$configuration = new Configuration();

/** @var array{base_uri: string, default_options: array<string, mixed>} $config */
/** @var array{base_uri: string, base_directory: string, default_options: array<string, mixed>} $config */
$config = $this->processConfiguration($configuration, $configs);

$definition = $container->getDefinition('sensiolabs_gotenberg.client');
$definition->replaceArgument(0, $config['base_uri']);

$definition = $container->getDefinition('sensiolabs_gotenberg');
$definition->replaceArgument(1, $this->cleanDefaultOptions($config['default_options']));

$definition = $container->getDefinition('sensiolabs_gotenberg.asset.base_dir_formatter');
$definition->replaceArgument(0, $config['base_directory']);
}

/**
Expand Down
28 changes: 28 additions & 0 deletions src/Formatter/AssetBaseDirFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Sensiolabs\GotenbergBundle\Formatter;

use Symfony\Component\Filesystem\Filesystem;

final readonly class AssetBaseDirFormatter
{
private string $baseDir;

public function __construct(private Filesystem $filesystem, private string $projectDir, string $baseDir)
{
$this->baseDir = rtrim($baseDir, '/');
}

public function resolve(string $path): string
{
if ($this->filesystem->isAbsolutePath($path)) {
return $path;
}

if ($this->filesystem->isAbsolutePath($this->baseDir)) {
return "{$this->baseDir}/{$path}";
}

return "{$this->projectDir}/{$this->baseDir}/{$path}";
}
}
11 changes: 6 additions & 5 deletions src/Pdf/Gotenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Sensiolabs\GotenbergBundle\Builder\MarkdownPdfBuilder;
use Sensiolabs\GotenbergBundle\Builder\UrlPdfBuilder;
use Sensiolabs\GotenbergBundle\Client\GotenbergClientInterface;
use Sensiolabs\GotenbergBundle\Formatter\AssetBaseDirFormatter;
use Twig\Environment;

final readonly class Gotenberg implements GotenbergInterface
Expand All @@ -17,35 +18,35 @@
public function __construct(
private GotenbergClientInterface $gotenbergClient,
private array $userConfigurations,
private string $projectDir,
private AssetBaseDirFormatter $asset,
private ?Environment $twig = null,
) {
}

public function html(): HtmlPdfBuilder
{
return (new HtmlPdfBuilder($this->gotenbergClient, $this->projectDir, $this->twig))
return (new HtmlPdfBuilder($this->gotenbergClient, $this->asset, $this->twig))
->setConfigurations($this->userConfigurations)
;
}

public function url(): UrlPdfBuilder
{
return (new UrlPdfBuilder($this->gotenbergClient, $this->projectDir, $this->twig))
return (new UrlPdfBuilder($this->gotenbergClient, $this->asset, $this->twig))
->setConfigurations($this->userConfigurations)
;
}

public function markdown(): MarkdownPdfBuilder
{
return (new MarkdownPdfBuilder($this->gotenbergClient, $this->projectDir, $this->twig))
return (new MarkdownPdfBuilder($this->gotenbergClient, $this->asset, $this->twig))
->setConfigurations($this->userConfigurations)
;
}

public function office(): LibreOfficePdfBuilder
{
return (new LibreOfficePdfBuilder($this->gotenbergClient, $this->projectDir))
return (new LibreOfficePdfBuilder($this->gotenbergClient, $this->asset))
->setConfigurations($this->userConfigurations)
;
}
Expand Down
34 changes: 34 additions & 0 deletions src/Twig/GotenbergAssetExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Sensiolabs\GotenbergBundle\Twig;

use Sensiolabs\GotenbergBundle\Builder\AbstractChromiumPdfBuilder;
use Symfony\Component\Mime\Part\File;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

final class GotenbergAssetExtension extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction('gotenberg_asset', $this->getAssetUrl(...), ['needs_context' => true]),
];
}

/**
* @param array<string, mixed> $context
*/
public function getAssetUrl(array $context, string $path): string
{
$builder = $context['_builder'];

if (!$builder instanceof AbstractChromiumPdfBuilder) {
throw new \LogicException('You need to extend from AbstractChromiumPdfBuilder to use gotenberg_asset function.');
}

$builder->addAsset($path);

return (new File($path))->getFilename();
}
}
Loading

0 comments on commit 9777047

Please sign in to comment.