Compile a minimal HTML+CSS subset into reusable PDF Form XObject payloads.
xobject-template is a focused rendering engine for projects that need beautiful, vector-first, stable reusable overlays inside PDF workflows.
- Reusable XObject payloads for labels, stamps, approvals, and other document overlays
- Clean integration path for any PDF pipeline that can embed Form XObjects
- Lean API designed for long-term compatibility and monetizable maintainability
Use the compiler to generate a reusable XObject result. Consumers that prefer arrays over DTOs can adapt the result into a generic payload shape.
use LibreSign\XObjectTemplate\Dto\CompileRequest;
use LibreSign\XObjectTemplate\Integration\XObjectPayloadAdapter;
use LibreSign\XObjectTemplate\Pdf\SinglePagePdfExporter;
use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
$compiler = new XObjectTemplateCompiler();
$result = $compiler->compile(new CompileRequest(
html: '<div style="font-size:10px;color:#111111">Rendered for Alice</div>'
. '<img src="/tmp/example-image.png" style="width:24px;height:24px" />',
width: 240.0,
height: 84.0,
));
$payload = (new XObjectPayloadAdapter())->toXObjectPayload($result);
$pdf = (new SinglePagePdfExporter())->export($result);
file_put_contents(__DIR__ . '/build/preview.pdf', $pdf);SinglePagePdfExporter wraps a compiled XObject result into a one-page PDF whose MediaBox matches the compiled bbox size exactly.
- The page size is derived from
$result->bbox - Non-zero bounding boxes are translated back to the page origin automatically
- Local PNG and JPEG image sources are embedded into the standalone PDF during export
$result->contentStream: PDF operators ready for a Form XObject stream$result->resources: font/image resource dictionary keyed for PDF serialization$result->bbox: bounding box as[x1, y1, x2, y2]$result->metadata: render diagnostics such asline_count,image_count,node_count, andrender_ms$payload: transport-agnostic array withstream,resources, andbbox$pdf: standalone PDF bytes ready to save, stream, or attach to preview workflows
CompileRequest::width and CompileRequest::height define the base design size of the template.
If a downstream consumer needs to place the compiled stamp at a different size while preserving the
original aspect ratio, it should keep the compiled XObject unchanged and apply a uniform scale during
PDF placement instead of recompiling the HTML with new dimensions.
- Read the base size from
$result->bbox - Compute a single scale factor from the target width or target height
- Apply the same scale to both axes in the placement matrix
[$minX, $minY, $maxX, $maxY] = $result->bbox;
$baseWidth = $maxX - $minX;
$baseHeight = $maxY - $minY;
$targetWidth = 175.0;
$scale = $targetWidth / $baseWidth;
$targetHeight = $baseHeight * $scale;
// Consumer-side PDF placement concept:
$placement = sprintf(
'q %F 0 0 %F %F %F cm /Fm0 Do Q',
$scale,
$scale,
$x,
$y,
);Using a uniform placement scale keeps text, images, spacing, and line breaks visually aligned. Recompiling only to emulate a proportional resize is usually the wrong integration point for this package.
For consumers that want a small helper instead of recalculating the matrix manually, the package also
ships LibreSign\XObjectTemplate\Integration\XObjectPlacementCalculator and
LibreSign\XObjectTemplate\Integration\XObjectPlacement.
use LibreSign\XObjectTemplate\Integration\XObjectPlacementCalculator;
$placement = (new XObjectPlacementCalculator())->fromWidth($result, 175.0, 36.0, 72.0);
$pdfCommand = $placement->toPdfCommand('Fm0');
// q 0.729167 0 0 0.729167 36.000000 72.000000 cm /Fm0 Do QIf the caller passes CompileRequest::context, the compiler can interpolate simple {{ key }}
placeholders before parsing the HTML subset.
- Values are HTML-escaped before insertion
- Unknown placeholders are left untouched
- Twig users can keep rendering HTML upstream and skip this feature entirely
- Supported elements:
<div>,<p>,<span>,<br>,<img> - Text fragments are normalized into inline text nodes internally
- Inline styles are read from the
styleattribute - Images use the
srcattribute as the source reference included in the resource dictionary
- Typography:
font-size,font-family,font-weight,line-height,color,text-align,hyphens,white-space - Layout:
margin,padding,width,height,overflow,text-overflow - Vector box styling:
background-color,border-color,border-width,border-radius - Structured layout:
display:flex,flex-direction,justify-content,align-items,gap - Absolute placement:
position:absolute,top,right,bottom,left - Numeric values can be provided as unitless numbers or
px;width,height, and positional offsets also accept% pxvalues are converted to PDF points using the package conversion rules- Unknown or incomplete CSS declarations are ignored instead of aborting the render
- Font family mapping currently targets the built-in Helvetica, Times, and Courier aliases used by the generated PDF resources
- Text alignment uses measured widths for left, center, right, and basic justified output (
Twword spacing) - Hyphenation supports a small deterministic subset:
hyphens:auto,hyphens:manualwith soft hyphens, andhyphens:none - Overflow clipping uses PDF clipping paths;
text-overflow:ellipsisapplies when hidden overflow truncates visible text - Backgrounds and borders are emitted as vector rectangles, including rounded corners
- Percentage-based sizing and offsets resolve relative to the current layout container
- Flex layouts are intentionally small-scope and predictable: the engine supports deterministic row/column compositions for stamps, labels, and approval blocks rather than full browser-grade CSS
imgwidth/height fall back to32x32when omitted or invalid- Image and text placement are clamped to the requested output box
- The compiler output is not tied to any single downstream package; any consumer that understands Form XObject stream/resources/bbox data can use it
- Unsupported HTML elements raise
UnsupportedSubsetException - Malformed HTML fragments are normalized by
DOMDocumentbefore traversal - Empty text nodes and invalid inline-style fragments are ignored during parsing/rendering