PdfGenerator
in package
Generic PDF generator from a DB template ({@see PdfTemplateVariant}).
Renders the Latte content of a variant (css plus per-language html/header/footer) through mPDF
into a file and returns its absolute path. A stateless System service shared across modules
(Invoicer via InvoicePdfGenerator), so it knows
no domain context: the caller supplies the complete $args (the template key contract) and
the target folder.
Rendering uses a standalone Latte engine like SendEmailTrait,
with the price/priceFormat/printDate filters registered manually (the DI LatteFactory is
file-based, so extension filters do not reach a standalone engine).
IMPORTANT: templates are admin-edited Latte code rendered without a sandbox, so everyone who can edit a template must be trusted.
Exceptions are not swallowed into the UI: they are logged via Debugger::log() and the
method returns null, so the best-effort post-commit caller does not fail the invoice.
Layouts: the look (css plus header/html/footer) can be chosen independently of the variant
through the optional $layoutId. Content is looked up in PdfTemplateVariantLayoutText
(resolveContent()) with a fallback to the default layout. variant_texts
(resolveText()) carries only name (meta_title); content always lives on the layout.
Table of Contents
Constants
- MPDF_TAG_UNESCAPE : array<string|int, mixed> = ['#.' => '{', '.#' => '}']
- In header/footer templates mPDF `{PAGENO}`/`{nbpg}` are written as `#.PAGENO.#` so Latte does not take them for tags; after rendering `#.`/`.#` is converted back to `{`/`}`.
Properties
- $config : Config
- $pdfTemplateLayoutManager : PdfTemplateLayoutManager
- $pdfTemplateManager : PdfTemplateManager
- $priceLatteExtension : PriceLatteExtension
Methods
- __construct() : mixed
- generate() : string|null
- Legacy-compatible entry: finds the variant by id (across all PdfTemplates) and renders it.
- generateFromVariant() : string|null
- Core of the render from an already loaded variant (no DB lookup); use it directly when the caller holds the variant.
- applyHeaderFooter() : void
- configPath() : string
- Absolute path from `params.{module}.{key}.abs` (see `config/Shared/parameters.neon`).
- createEngine() : Engine
- defaultLayoutId() : int|null
- emailTemplatePath() : string
- findVariant() : PdfTemplateVariant|null
- isHtml() : bool
- Heuristic: is the value an HTML fragment (`SetHTMLHeader()` vs plain `SetHeader()`).
- latteTempDir() : string
- mpdfTempDir() : string
- renderFragment() : string
- Renders a Latte fragment (header/footer) and converts `#.`/`.#` back to `{`/`}` for mPDF tags.
- resolveContent() : PdfTemplateVariantLayoutText, 1: string}|null
- Resolves the content (header/html/footer) and the final css for rendering: the layout (`$layoutId` or the default), falling back to the default layout when the chosen one has no content for this variant. The css is `layout.css . "\n" . variant.css`; `variant.css` is usually empty and remains an optional per-variant addition on top of the layout css.
- resolveLayoutText() : PdfTemplateVariantLayoutText|null
- Returns the `layout_texts` row (variant, layout, language) with a language fallback (mirrors `resolveText()`).
- resolveText() : PdfTemplateVariantText|null
Constants
MPDF_TAG_UNESCAPE
In header/footer templates mPDF `{PAGENO}`/`{nbpg}` are written as `#.PAGENO.#` so Latte does not take them for tags; after rendering `#.`/`.#` is converted back to `{`/`}`.
private
array<string|int, mixed>
MPDF_TAG_UNESCAPE
= ['#.' => '{', '.#' => '}']
Properties
$config read-only
private
Config
$config
$pdfTemplateLayoutManager read-only
private
PdfTemplateLayoutManager
$pdfTemplateLayoutManager
$pdfTemplateManager read-only
private
PdfTemplateManager
$pdfTemplateManager
$priceLatteExtension read-only
private
PriceLatteExtension
$priceLatteExtension
Methods
__construct()
public
__construct(PdfTemplateManager $pdfTemplateManager, PdfTemplateLayoutManager $pdfTemplateLayoutManager, Config $config, PriceLatteExtension $priceLatteExtension) : mixed
Parameters
- $pdfTemplateManager : PdfTemplateManager
- $pdfTemplateLayoutManager : PdfTemplateLayoutManager
- $config : Config
- $priceLatteExtension : PriceLatteExtension
generate()
Legacy-compatible entry: finds the variant by id (across all PdfTemplates) and renders it.
public
generate(int $pdfTemplateVariantId, array<string, mixed> $args, string $fileName, string $folderAbs[, int|null $languageId = null ][, int|null $layoutId = null ]) : string|null
Returns the absolute file path, or null on failure.
Parameters
- $pdfTemplateVariantId : int
- $args : array<string, mixed>
- $fileName : string
- $folderAbs : string
- $languageId : int|null = null
- $layoutId : int|null = null
Return values
string|nullgenerateFromVariant()
Core of the render from an already loaded variant (no DB lookup); use it directly when the caller holds the variant.
public
generateFromVariant(PdfTemplateVariant $variant, array<string, mixed> $args, string $fileName, string $folderAbs[, int|null $languageId = null ][, int|null $layoutId = null ]) : string|null
Parameters
- $variant : PdfTemplateVariant
- $args : array<string, mixed>
- $fileName : string
- $folderAbs : string
- $languageId : int|null = null
- $layoutId : int|null = null
Return values
string|nullapplyHeaderFooter()
private
applyHeaderFooter(Mpdf $mpdf, Engine $latte, PdfTemplateVariantLayoutText $text, array<string, mixed> $args) : void
Parameters
- $mpdf : Mpdf
- $latte : Engine
- $text : PdfTemplateVariantLayoutText
- $args : array<string, mixed>
configPath()
Absolute path from `params.{module}.{key}.abs` (see `config/Shared/parameters.neon`).
private
configPath(int $module, string $key) : string
Parameters
- $module : int
- $key : string
Return values
stringcreateEngine()
private
createEngine() : Engine
Return values
EnginedefaultLayoutId()
private
defaultLayoutId() : int|null
Return values
int|nullemailTemplatePath()
private
emailTemplatePath() : string
Return values
stringfindVariant()
private
findVariant(int $variantId) : PdfTemplateVariant|null
Parameters
- $variantId : int
Return values
PdfTemplateVariant|nullisHtml()
Heuristic: is the value an HTML fragment (`SetHTMLHeader()` vs plain `SetHeader()`).
private
isHtml(string $value) : bool
Parameters
- $value : string
Return values
boollatteTempDir()
private
latteTempDir() : string
Return values
stringmpdfTempDir()
private
mpdfTempDir() : string
Return values
stringrenderFragment()
Renders a Latte fragment (header/footer) and converts `#.`/`.#` back to `{`/`}` for mPDF tags.
private
renderFragment(Engine $latte, string $html, array<string, mixed> $args) : string
Parameters
- $latte : Engine
- $html : string
- $args : array<string, mixed>
Return values
stringresolveContent()
Resolves the content (header/html/footer) and the final css for rendering: the layout (`$layoutId` or the default), falling back to the default layout when the chosen one has no content for this variant. The css is `layout.css . "\n" . variant.css`; `variant.css` is usually empty and remains an optional per-variant addition on top of the layout css.
private
resolveContent(PdfTemplateVariant $variant, int $languageId, int|null $layoutId) : PdfTemplateVariantLayoutText, 1: string}|null
Parameters
- $variant : PdfTemplateVariant
- $languageId : int
- $layoutId : int|null
Return values
PdfTemplateVariantLayoutText, 1: string}|null —null when no layout (not even
the default) has content for this variant; the caller logs it and returns null.
resolveLayoutText()
Returns the `layout_texts` row (variant, layout, language) with a language fallback (mirrors `resolveText()`).
private
resolveLayoutText(PdfTemplateVariant $variant, int $layoutId, int $languageId) : PdfTemplateVariantLayoutText|null
Parameters
- $variant : PdfTemplateVariant
- $layoutId : int
- $languageId : int
Return values
PdfTemplateVariantLayoutText|nullresolveText()
private
resolveText(PdfTemplateVariant $variant, int $languageId) : PdfTemplateVariantText|null
Parameters
- $variant : PdfTemplateVariant
- $languageId : int