NAME
PDF::Make::Markup::Profile - the rules a template runs under
SYNOPSIS
my $markup = PDF::Make::Markup::Profile->render($template, $data);
DESCRIPTION
Templates arrive from whoever holds the account and run here, over data that frequently came from someone else again. This module is the boundary, and it is deliberately small enough to read in one sitting.
Escaping is not optional
Every value a template interpolates is escaped. A customer called Smith & Sons <Ltd> appears as those characters rather than as broken markup, and a hostile value cannot close an element and open its own. auto_escape cannot be passed to "engine": attempting it is an error rather than something that quietly wins.
Stencil escapes to &, <, >, " and ', all of which PDF::Make::Markup::Parse decodes, so the round trip is exact.
{% raw %} is refused
Stencil's {% raw value %} prints a value with no escaping. There is no way to disable it in the engine, so a template that uses it is rejected before it compiles, with the line number and the reason. Markup comes from the template; values come from the data. A template that needs to vary its structure should do it with {% if %} and {% for %}, which is the same expressiveness without handing structure to the payload.
Filters are a fixed list
Stencil takes filters as Perl coderefs, which is a fine design for an application and an unacceptable one for a multi-tenant service, so the map is fixed here and cannot be extended by a caller. filters cannot be passed to "engine".
On top of Stencil's own upper, lower, trim, html, uri and default, this profile adds money and number. Both are pure functions of their input. Adding one is a code change and a review, which is the intended cost.
Strictness
strict is on: a field the data does not have is an error rather than an empty space. A silently blank invoice number is worse than a render that refuses, because only one of them is noticed.
What this module does not do
It does not bound CPU time. A template that loops for a very long time is still running inside a C render loop that a Perl alarm will not interrupt, so the wall-clock limit belongs in the worker that calls this - a process it can kill - and not here. Saying so plainly is better than a timeout that looks like protection and is not. The output size cap is enforced, since that is checkable after the fact.