From bd4b5f6e7c9bd72c12b73cb541200d12f6927b73 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 01:49:17 -0300
Subject: [PATCH 01/31] docs: add tested user docs site and github pages deploy
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
.github/ISSUE_TEMPLATE/bug_report.yml | 13 ++
.github/ISSUE_TEMPLATE/commercial_support.yml | 36 ++++
.github/ISSUE_TEMPLATE/config.yml | 6 +
.github/ISSUE_TEMPLATE/documentation.yml | 23 +++
.github/ISSUE_TEMPLATE/feature_request.yml | 7 +
.github/ISSUE_TEMPLATE/rendering_bug.yml | 72 ++++++++
.github/PULL_REQUEST_TEMPLATE.md | 13 ++
.github/pull_request_template.md | 34 ++++
.github/workflows/docs.yml | 81 +++++++++
README.md | 155 ++++--------------
composer.json | 9 +
docs/community/contributing.md | 51 ++++++
docs/community/maintainer-notes.md | 27 +++
docs/community/roadmap.md | 17 ++
docs/community/sustainability.md | 32 ++++
docs/getting-started.md | 43 +++++
docs/guides/basic-template.md | 24 +++
docs/guides/images.md | 37 +++++
docs/guides/interpolation.md | 21 +++
docs/guides/placement.md | 23 +++
docs/guides/preview-pdf.md | 22 +++
docs/guides/signer-integration.md | 25 +++
docs/guides/svg.md | 22 +++
docs/index.md | 39 +++++
docs/installation.md | 31 ++++
docs/reference/errors.md | 38 +++++
docs/reference/limitations.md | 23 +++
docs/reference/output-contract.md | 33 ++++
docs/reference/public-api.md | 52 ++++++
docs/reference/supported-html-css.md | 43 +++++
docs/reference/supported-svg.md | 91 ++++++++++
docs/use-cases/libresign.md | 23 +++
examples/assets/sample.svg | 5 +
examples/assets/tiny-jpeg.base64 | 1 +
examples/assets/tiny-png.base64 | 1 +
examples/basic-template.php | 44 +++++
examples/images.php | 66 ++++++++
examples/interpolation.php | 45 +++++
examples/placement.php | 44 +++++
examples/preview-pdf.php | 38 +++++
examples/svg.php | 51 ++++++
mkdocs.yml | 44 +++++
phpunit.xml.dist | 3 +
requirements-docs.txt | 5 +
scripts/check-docs-drift.php | 113 +++++++++++++
tests/Documentation/ExamplesTest.php | 101 ++++++++++++
46 files changed, 1607 insertions(+), 120 deletions(-)
create mode 100644 .github/ISSUE_TEMPLATE/commercial_support.yml
create mode 100644 .github/ISSUE_TEMPLATE/documentation.yml
create mode 100644 .github/ISSUE_TEMPLATE/rendering_bug.yml
create mode 100644 .github/pull_request_template.md
create mode 100644 .github/workflows/docs.yml
create mode 100644 docs/community/contributing.md
create mode 100644 docs/community/maintainer-notes.md
create mode 100644 docs/community/roadmap.md
create mode 100644 docs/community/sustainability.md
create mode 100644 docs/getting-started.md
create mode 100644 docs/guides/basic-template.md
create mode 100644 docs/guides/images.md
create mode 100644 docs/guides/interpolation.md
create mode 100644 docs/guides/placement.md
create mode 100644 docs/guides/preview-pdf.md
create mode 100644 docs/guides/signer-integration.md
create mode 100644 docs/guides/svg.md
create mode 100644 docs/index.md
create mode 100644 docs/installation.md
create mode 100644 docs/reference/errors.md
create mode 100644 docs/reference/limitations.md
create mode 100644 docs/reference/output-contract.md
create mode 100644 docs/reference/public-api.md
create mode 100644 docs/reference/supported-html-css.md
create mode 100644 docs/reference/supported-svg.md
create mode 100644 docs/use-cases/libresign.md
create mode 100644 examples/assets/sample.svg
create mode 100644 examples/assets/tiny-jpeg.base64
create mode 100644 examples/assets/tiny-png.base64
create mode 100644 examples/basic-template.php
create mode 100644 examples/images.php
create mode 100644 examples/interpolation.php
create mode 100644 examples/placement.php
create mode 100644 examples/preview-pdf.php
create mode 100644 examples/svg.php
create mode 100644 mkdocs.yml
create mode 100644 requirements-docs.txt
create mode 100644 scripts/check-docs-drift.php
create mode 100644 tests/Documentation/ExamplesTest.php
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 33ec828..e5fdf6c 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -9,12 +9,14 @@ body:
id: summary
attributes:
label: Summary
+ description: Provide a short, objective problem statement.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
+ description: Include exact input and commands.
validations:
required: true
- type: textarea
@@ -23,3 +25,14 @@ body:
label: Expected behavior
validations:
required: true
+ - type: textarea
+ id: actual
+ attributes:
+ label: Actual behavior
+ validations:
+ required: true
+ - type: input
+ id: runtime
+ attributes:
+ label: Runtime details
+ placeholder: PHP version, package version/commit, OS
diff --git a/.github/ISSUE_TEMPLATE/commercial_support.yml b/.github/ISSUE_TEMPLATE/commercial_support.yml
new file mode 100644
index 0000000..32ea6b9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/commercial_support.yml
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2026 LibreSign
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+name: Commercial support
+description: Ask about paid support or sponsored prioritization
+labels: [support]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for your interest in commercial support.
+
+ This template does not guarantee free consulting. Sponsored or paid work can help prioritize compatibility, features, and long-term maintenance.
+
+ Current public funding channel: https://github.com/sponsors/LibreSign
+ - type: textarea
+ id: context
+ attributes:
+ label: Project context
+ description: Describe your use case, expected timeline, and technical scope.
+ validations:
+ required: true
+ - type: textarea
+ id: requested_scope
+ attributes:
+ label: Requested support scope
+ description: Include expected deliverables (for example SVG compatibility work, integration support, QA hardening).
+ validations:
+ required: true
+ - type: input
+ id: contact
+ attributes:
+ label: Preferred contact method
+ placeholder: email or issue-based follow-up
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 2e55af7..edce05d 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -6,3 +6,9 @@ contact_links:
- name: Security disclosure
url: https://github.com/LibreSign/xobject-template/security
about: Please report vulnerabilities privately.
+ - name: Documentation site
+ url: https://github.com/LibreSign/xobject-template/tree/main/docs
+ about: Browse the user documentation source.
+ - name: Sponsor maintenance
+ url: https://github.com/sponsors/LibreSign
+ about: Funding helps prioritize fixes and compatibility work.
diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml
new file mode 100644
index 0000000..656ae97
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation.yml
@@ -0,0 +1,23 @@
+# SPDX-FileCopyrightText: 2026 LibreSign
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+name: Documentation
+description: Report documentation errors or suggest improvements
+labels: [documentation]
+body:
+ - type: textarea
+ id: problem
+ attributes:
+ label: What is unclear or incorrect?
+ validations:
+ required: true
+ - type: input
+ id: page
+ attributes:
+ label: Affected page/path
+ placeholder: docs/reference/supported-svg.md
+ - type: textarea
+ id: suggestion
+ attributes:
+ label: Suggested improvement
+ description: Keep suggestions objective and user-focused.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 0350a47..d87bb0e 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -9,11 +9,18 @@ body:
id: problem
attributes:
label: Problem statement
+ description: Describe the user or integration problem first.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
+ description: Keep scope explicit and include expected API/behavior impact.
validations:
required: true
+ - type: textarea
+ id: non_goals
+ attributes:
+ label: Non-goals / out of scope
+ description: Clarify what should not be included.
diff --git a/.github/ISSUE_TEMPLATE/rendering_bug.yml b/.github/ISSUE_TEMPLATE/rendering_bug.yml
new file mode 100644
index 0000000..29a60a0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/rendering_bug.yml
@@ -0,0 +1,72 @@
+# SPDX-FileCopyrightText: 2026 LibreSign
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+name: Rendering bug
+description: Report a minimal reproducible rendering issue for HTML/CSS/SVG to XObject output
+labels: [bug, rendering]
+body:
+ - type: textarea
+ id: summary
+ attributes:
+ label: Summary
+ description: Describe the rendering issue in one or two sentences.
+ validations:
+ required: true
+ - type: textarea
+ id: minimal_input
+ attributes:
+ label: Minimal HTML/CSS/SVG input
+ description: Paste the smallest possible input that reproduces the issue.
+ render: markdown
+ validations:
+ required: true
+ - type: textarea
+ id: expected_output
+ attributes:
+ label: Expected output
+ validations:
+ required: true
+ - type: textarea
+ id: actual_output
+ attributes:
+ label: Actual output
+ validations:
+ required: true
+ - type: textarea
+ id: payload_details
+ attributes:
+ label: Generated content stream/resources (if possible)
+ description: Include compile result content stream/resources snippet when available.
+ - type: dropdown
+ id: affected_area
+ attributes:
+ label: Affected area
+ options:
+ - text
+ - PNG/JPEG
+ - SVG
+ - layout
+ - CSS
+ - interpolation
+ - placement
+ validations:
+ required: true
+ - type: input
+ id: php_version
+ attributes:
+ label: PHP version
+ placeholder: '8.2.x'
+ validations:
+ required: true
+ - type: input
+ id: package_version
+ attributes:
+ label: Package version or commit
+ placeholder: 'composer version, tag, or commit hash'
+ validations:
+ required: true
+ - type: textarea
+ id: fixture
+ attributes:
+ label: Minimal fixture attachment details
+ description: If possible, attach or describe a minimal fixture file set.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 0dfe019..b282c69 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -10,11 +10,24 @@
- [ ] Tests added/updated
- [ ] Relevant checks executed
+## Documentation
+
+- [ ] Documentation updated
+- [ ] Examples updated if public behavior changed
+- [ ] Supported HTML/CSS/SVG matrix updated if relevant
+- [ ] Documentation drift checks pass
+
## Performance
- [ ] Performance impact assessed
- [ ] Benchmark threshold still green
+## Output integrity
+
+- [ ] No unsupported feature documented as supported
+- [ ] No generated build artifacts committed to the source branch
+- [ ] LibreSign use case impact considered if relevant
+
## Compliance
- [ ] DCO sign-off in all commits
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..4445706
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,34 @@
+
+
+
+## Scope
+
+- [ ] Change scope and impact are clearly described
+
+## Evidence
+
+- [ ] Tests added/updated
+- [ ] Relevant checks executed
+
+## Documentation
+
+- [ ] Documentation updated
+- [ ] Examples updated if public behavior changed
+- [ ] Supported HTML/CSS/SVG matrix updated if relevant
+- [ ] Documentation drift checks pass
+
+## Performance
+
+- [ ] Performance impact considered
+
+## Output integrity
+
+- [ ] No unsupported feature documented as supported
+- [ ] No generated build artifacts committed to the source branch
+- [ ] LibreSign use case impact considered if relevant
+
+## Compliance
+
+- [ ] DCO sign-off in all commits
+- [ ] REUSE/SPDX metadata preserved
+- [ ] License compatibility reviewed
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..81ad86a
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,81 @@
+# SPDX-FileCopyrightText: 2026 LibreSign
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+name: docs
+
+on:
+ pull_request:
+ push:
+ branches: [main]
+
+permissions:
+ contents: read
+
+jobs:
+ validate-docs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Detect minimum PHP from composer.json
+ id: php_min
+ run: |
+ php_version=$(grep -Po '"php"\s*:\s*"\K[^"]+' composer.json | grep -Eo '[0-9]+\.[0-9]+' | head -n1)
+ [[ -n "$php_version" ]] || { echo "Could not determine minimum PHP version"; exit 1; }
+ echo "version=$php_version" >> "$GITHUB_OUTPUT"
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ steps.php_min.outputs.version }}
+
+ - name: Setup Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.12'
+
+ - name: Install Composer dependencies
+ run: composer install --no-interaction --prefer-dist
+
+ - name: Install PHPUnit bin dependencies
+ run: composer bin phpunit install --no-interaction --prefer-dist
+
+ - name: Install docs dependencies
+ run: python -m pip install --upgrade pip && pip install -r requirements-docs.txt
+
+ # Pull requests only validate docs. No deployment from PRs.
+ - name: Run docs tests
+ run: composer run docs:test
+
+ deploy-docs:
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ needs: validate-docs
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Setup Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.12'
+
+ - name: Install docs dependencies
+ run: python -m pip install --upgrade pip && pip install -r requirements-docs.txt
+
+ - name: Build docs site
+ run: python -m mkdocs build --strict
+
+ # Pushes to main publish docs to gh-pages branch.
+ # Maintainers should configure GitHub Pages source as:
+ # Deploy from branch -> gh-pages -> /root
+ - name: Deploy site to gh-pages branch
+ uses: peaceiris/actions-gh-pages@v4
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: ./site
+ publish_branch: gh-pages
+ force_orphan: true
diff --git a/README.md b/README.md
index 190be8a..d2ec7b9 100644
--- a/README.md
+++ b/README.md
@@ -3,150 +3,65 @@
# xobject-template
-> Compile a minimal HTML+CSS subset into reusable PDF Form XObject payloads.
+Minimal HTML+CSS to reusable PDF Form XObject compiler for visible signature appearances and document overlays.
-`xobject-template` is a focused rendering engine for projects that need **beautiful, vector-first, stable** reusable overlays inside PDF workflows.
+## What is xobject-template?
-## Why this package
+`xobject-template` is a focused PHP rendering engine that compiles a deterministic HTML/CSS subset into Form XObject-oriented output (`contentStream`, `resources`, `bbox`) for downstream 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
+## Why it exists
-## Quick integration
+Many systems need stable visual signature/stamp/label appearances, but do not want browser-grade rendering complexity inside PDF pipelines.
-Use the compiler to generate a reusable XObject result. Consumers that prefer arrays over DTOs can adapt the result into a generic payload shape.
+This package provides a scoped, integration-friendly rendering layer for that gap.
-```php
-use LibreSign\XObjectTemplate\Dto\CompileRequest;
-use LibreSign\XObjectTemplate\Integration\XObjectPayloadAdapter;
-use LibreSign\XObjectTemplate\Pdf\SinglePagePdfExporter;
-use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
+## Install
-$compiler = new XObjectTemplateCompiler();
-
-$result = $compiler->compile(new CompileRequest(
- html: '
Rendered for Alice
'
- . '',
- width: 240.0,
- height: 84.0,
-));
-
-$payload = (new XObjectPayloadAdapter())->toXObjectPayload($result);
-$pdf = (new SinglePagePdfExporter())->export($result);
-
-file_put_contents(__DIR__ . '/build/preview.pdf', $pdf);
+```bash
+composer require libresign/xobject-template
```
-### Standalone PDF export
-
-`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
-
-### Output contract
-
-- `$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 as `line_count`, `image_count`, `node_count`, and `render_ms`
-- `$payload`: transport-agnostic array with `stream`, `resources`, and `bbox`
-- `$pdf`: standalone PDF bytes ready to save, stream, or attach to preview workflows
-
-### Scaling a compiled XObject
-
-`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
+## Quick example
```php
-[$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.
+fromWidth($result, 175.0, 36.0, 72.0);
+$result = $compiler->compile(new CompileRequest(
+ html: '
Signed by {{ name }}
',
+ width: 240.0,
+ height: 84.0,
+ context: ['name' => 'Alice'],
+));
-$pdfCommand = $placement->toPdfCommand('Fm0');
-// q 0.729167 0 0 0.729167 36.000000 72.000000 cm /Fm0 Do Q
+// $result->contentStream
+// $result->resources
+// $result->bbox
```
-### Optional context interpolation
-
-If the caller passes `CompileRequest::context`, the compiler can interpolate simple `{{ key }}`
-placeholders before parsing the HTML subset.
+## Documentation
-- Values are HTML-escaped before insertion
-- Unknown placeholders are left untouched
-- Twig users can keep rendering HTML upstream and skip this feature entirely
+Full documentation site source is in `docs/` and published via GitHub Pages after merge:
-## Supported HTML/CSS subset
+- Installation, guides, examples, and support matrices: `docs/`
-### HTML
+## LibreSign use case
-- Supported elements: `
`, `
`, ``, ` `, ``
-- Text fragments are normalized into inline text nodes internally
-- Inline styles are read from the `style` attribute
-- Images use the `src` attribute as the source reference included in the resource dictionary
+- Docs page: `docs/use-cases/libresign.md`
+- LibreSign project:
-### CSS used by the renderer
+## Contributing
-- 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 `%`
-- `px` values are converted to PDF points using the package conversion rules
-- Unknown or incomplete CSS declarations are ignored instead of aborting the render
+- Main guide: `docs/community/contributing.md`
+- Repository policy: `CONTRIBUTING.md`
-### Rendering notes
+If this package helps your project generate reliable PDF signature appearances, please star the repository. It helps other developers discover the project and signals that this work is worth maintaining.
-- 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 (`Tw` word spacing)
-- Hyphenation supports a small deterministic subset: `hyphens:auto`, `hyphens:manual` with soft hyphens, and `hyphens:none`
-- Overflow clipping uses PDF clipping paths; `text-overflow:ellipsis` applies 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
-- `img` width/height fall back to `32x32` when 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
+## Support the project
-## Failure modes
+- Sponsor maintenance:
-- Unsupported HTML elements raise `UnsupportedSubsetException`
-- Malformed HTML fragments are normalized by `DOMDocument` before traversal
-- Empty text nodes and invalid inline-style fragments are ignored during parsing/rendering
diff --git a/composer.json b/composer.json
index e619327..df68c23 100644
--- a/composer.json
+++ b/composer.json
@@ -73,6 +73,15 @@
"sh:security": "shellcheck scripts/*.sh",
"test:unit": "vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --testsuite unit",
"test:integration": "vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --testsuite integration",
+ "docs:examples": "vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --testsuite documentation --filter ExamplesTest",
+ "docs:check-drift": "php scripts/check-docs-drift.php",
+ "docs:build": "python3 -m mkdocs build --strict",
+ "docs:serve": "python3 -m mkdocs serve",
+ "docs:test": [
+ "@docs:examples",
+ "@docs:check-drift",
+ "@docs:build"
+ ],
"test:coverage": "XDEBUG_MODE=coverage vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --coverage-text --coverage-clover=build/coverage/clover.xml",
"mutation:test": "vendor-bin/mutation/vendor/infection/infection/bin/infection --threads=max",
"benchmark:run": "vendor-bin/phpbench/vendor/phpbench/phpbench/bin/phpbench run --config=phpbench.json --report=aggregate",
diff --git a/docs/community/contributing.md b/docs/community/contributing.md
new file mode 100644
index 0000000..8177495
--- /dev/null
+++ b/docs/community/contributing.md
@@ -0,0 +1,51 @@
+
+
+
+# Contributing
+
+Contributions are welcome. Keep quality high and changes scoped.
+
+## Setup
+
+Install dependencies and run checks from repository root.
+
+## Core quality commands
+
+- `composer lint`
+- `composer test:unit`
+- `composer test:integration`
+- `composer docs:test`
+
+## Documentation and examples
+
+- Add/update runnable examples in `examples/`.
+- Keep examples small and deterministic.
+- Ensure docs snippets come from real examples.
+- Run docs drift checks before opening a PR.
+
+## Adding supported behavior
+
+When adding HTML/CSS/SVG support:
+
+1. Add/adjust implementation.
+2. Add or update fixture-based tests.
+3. Update support reference pages.
+4. Keep limitations explicit (do not overstate support).
+
+## Reporting rendering bugs
+
+A good minimal reproducible report includes:
+
+- minimal HTML/CSS/SVG input,
+- expected vs actual result,
+- generated output details when possible,
+- package version/commit and PHP version.
+
+## High-value contributions
+
+- SVG edge-case fixtures,
+- PDF compatibility fixtures,
+- layout bug fixes,
+- documentation improvements,
+- tests for real-world signature templates,
+- performance improvements backed by benchmark evidence.
diff --git a/docs/community/maintainer-notes.md b/docs/community/maintainer-notes.md
new file mode 100644
index 0000000..a03fb19
--- /dev/null
+++ b/docs/community/maintainer-notes.md
@@ -0,0 +1,27 @@
+
+
+
+# Maintainer notes
+
+## Documentation deployment
+
+- Pull requests validate docs only.
+- Pushes to `main` validate and publish the generated site to `gh-pages`.
+- GitHub Pages should be configured as: deploy from branch `gh-pages`, folder `/root`.
+
+## Validation flow
+
+- Execute `composer docs:test`.
+- Ensure examples in `examples/` remain runnable.
+- Keep support references aligned with parser/layout/SVG behavior and tests.
+
+## Scope control
+
+- Keep user docs focused on public contracts.
+- Avoid scope creep into browser-engine semantics or full PDF-signing behavior.
+- Keep LibreSign use case visible without making this package LibreSign-only.
+
+## Drift policy
+
+- Support matrix docs are checked by `scripts/check-docs-drift.php` against known code-level indicators.
+- TODO: add finer-grained automated extraction for CSS property lists if parser/layout structure changes substantially.
diff --git a/docs/community/roadmap.md b/docs/community/roadmap.md
new file mode 100644
index 0000000..9b2fbaf
--- /dev/null
+++ b/docs/community/roadmap.md
@@ -0,0 +1,17 @@
+
+
+
+# Roadmap
+
+> This page describes priorities, not implemented behavior.
+
+Possible priority areas:
+
+- SVG compatibility improvements.
+- Additional layout fixtures.
+- More PDF compatibility tests.
+- More documentation examples.
+- Performance benchmark expansions.
+- Additional downstream signer integration examples.
+
+Roadmap priorities may be influenced by real-world LibreSign needs, community contributions, and funded maintenance work.
diff --git a/docs/community/sustainability.md b/docs/community/sustainability.md
new file mode 100644
index 0000000..8ff697b
--- /dev/null
+++ b/docs/community/sustainability.md
@@ -0,0 +1,32 @@
+
+
+
+# Sustainability
+
+Reliable PDF appearance generation needs sustained maintenance.
+
+This package sits at the intersection of PDF constraints, deterministic layout, images, scoped SVG handling, and downstream signer integration. Edge cases accumulate quickly, and maintaining high-confidence fixtures and tests takes ongoing effort.
+
+## Why funding matters
+
+Sponsorship and paid support help sustain:
+
+- SVG support hardening,
+- documentation quality,
+- QA and drift checks,
+- benchmark and performance maintenance,
+- integration examples for downstream signer workflows.
+
+## Practical ways to support
+
+- Star the repository.
+- Sponsor the project.
+- Contribute fixtures/tests.
+- Report minimal reproducible issues.
+- Evaluate LibreSign if you need a full digital-signature workflow.
+
+Support channel:
+
+- GitHub Sponsors:
+
+If this package helps your project generate reliable PDF signature appearances, consider starring the repository, contributing a fixture, or sponsoring maintenance.
diff --git a/docs/getting-started.md b/docs/getting-started.md
new file mode 100644
index 0000000..217241c
--- /dev/null
+++ b/docs/getting-started.md
@@ -0,0 +1,43 @@
+
+
+
+# Getting started
+
+This page uses the real example file `examples/basic-template.php`.
+
+## Minimal compile flow
+
+The example creates a `CompileRequest`, calls `XObjectTemplateCompiler::compile()`, and writes a JSON output summary under `build/examples/`.
+
+```php
+compile(new CompileRequest(
+ html: '
Signed by {{ name }}
',
+ width: 240.0,
+ height: 84.0,
+ context: ['name' => 'Alice'],
+));
+```
+
+## Result at user level
+
+The compile result provides:
+
+- `contentStream`: PDF operators for a Form XObject stream.
+- `resources`: font/image resource dictionary data for downstream serialization.
+- `bbox`: `[x1, y1, x2, y2]` bounding box.
+- `metadata`: rendering diagnostics (`render_ms`, `line_count`, `image_count`, `node_count`).
+
+## Next steps
+
+- Basic usage details: [Guides / Basic template](guides/basic-template.md)
+- Placement/scaling in downstream PDFs: [Guides / Placement and scaling](guides/placement.md)
+- Full output contract: [Reference / Output contract](reference/output-contract.md)
+
+If this package helps your project generate reliable PDF signature appearances, consider starring the repository, contributing a fixture, or sponsoring maintenance.
diff --git a/docs/guides/basic-template.md b/docs/guides/basic-template.md
new file mode 100644
index 0000000..72edba6
--- /dev/null
+++ b/docs/guides/basic-template.md
@@ -0,0 +1,24 @@
+
+
+
+# Basic template
+
+Source example: `examples/basic-template.php`
+
+## What the example demonstrates
+
+- HTML input with inline styles.
+- `CompileRequest` with `width`, `height`, and optional `context`.
+- Compilation through `XObjectTemplateCompiler`.
+- Inspection of `contentStream`, `resources`, `bbox`, and `metadata`.
+
+## Expected output
+
+The example writes `build/examples/basic-template-result.json` and includes:
+
+- non-empty content stream,
+- font resources,
+- a 4-value bounding box,
+- render metadata.
+
+Use this as your baseline before moving to images, SVG, and placement guides.
diff --git a/docs/guides/images.md b/docs/guides/images.md
new file mode 100644
index 0000000..f9585a7
--- /dev/null
+++ b/docs/guides/images.md
@@ -0,0 +1,37 @@
+
+
+
+# Images
+
+Source example: `examples/images.php`
+
+## Supported raster formats
+
+Based on implementation and tests:
+
+- PNG: supported.
+- JPEG: supported.
+- Other raster formats (for example GIF): not supported.
+
+## Source resolution
+
+Image sources are file paths from ``.
+
+- During compile, the source path is carried into XObject resources.
+- During export/embedding, the file is read from disk.
+
+## Dimensions and resources
+
+- `` dimensions come from style width/height when provided.
+- Missing/invalid image dimensions default to `32x32` in layout.
+- Generated resources include XObject aliases (for example `Im0`, `Im1`) with `Source`, `Width`, and `Height` metadata.
+
+## Error behavior and limitations
+
+Typical failures include:
+
+- source file missing/unreadable,
+- unsupported mime type,
+- invalid PNG/JPEG payload.
+
+See [Errors and exceptions](../reference/errors.md) and [Supported SVG](../reference/supported-svg.md) for vector behavior.
diff --git a/docs/guides/interpolation.md b/docs/guides/interpolation.md
new file mode 100644
index 0000000..1d45f6e
--- /dev/null
+++ b/docs/guides/interpolation.md
@@ -0,0 +1,21 @@
+
+
+
+# Context interpolation
+
+Source example: `examples/interpolation.php`
+
+## Placeholder syntax
+
+The current interpolation feature supports placeholders like:
+
+- `{{ name }}`
+- `{{ role }}`
+
+## Behavior
+
+- Interpolation happens before HTML subset parsing.
+- Values are HTML-escaped before insertion.
+- Unknown placeholders remain unchanged.
+
+This is useful for signature labels and document workflow overlays where a template shape is fixed but content changes per signer/document.
diff --git a/docs/guides/placement.md b/docs/guides/placement.md
new file mode 100644
index 0000000..351bef4
--- /dev/null
+++ b/docs/guides/placement.md
@@ -0,0 +1,23 @@
+
+
+
+# Placement and scaling
+
+Source example: `examples/placement.php`
+
+## Downstream placement model
+
+Compile once at a design size, then place the generated Form XObject in downstream PDFs.
+
+In most workflows, proportional resizing should happen through placement scaling, not by recompiling HTML for each target size.
+
+## Public helper classes
+
+- `LibreSign\XObjectTemplate\Integration\XObjectPlacementCalculator`
+- `LibreSign\XObjectTemplate\Integration\XObjectPlacement`
+
+The example shows `fromWidth()`, `fromHeight()`, and `fromScale()` and emits a placement command string with `toPdfCommand()`.
+
+## Why this is recommended
+
+Uniform placement scaling keeps text, spacing, and images visually consistent and avoids introducing avoidable template variants.
diff --git a/docs/guides/preview-pdf.md b/docs/guides/preview-pdf.md
new file mode 100644
index 0000000..a7fec22
--- /dev/null
+++ b/docs/guides/preview-pdf.md
@@ -0,0 +1,22 @@
+
+
+
+# Preview PDF
+
+Source example: `examples/preview-pdf.php`
+
+## Why preview export is useful
+
+`xobject-template` compiles Form XObject payloads. For visual debugging, `SinglePagePdfExporter` can wrap a compile result into a one-page PDF using the result `bbox`.
+
+This makes template iteration easier because you can quickly inspect appearance output without wiring a full downstream signer flow.
+
+## What the example does
+
+- Compiles a simple template.
+- Exports a preview PDF.
+- Writes output to `build/examples/preview.pdf`.
+
+## Scope reminder
+
+Preview export is a helper for inspection and integration tests. Production signing/placement still happens in your downstream PDF workflow.
diff --git a/docs/guides/signer-integration.md b/docs/guides/signer-integration.md
new file mode 100644
index 0000000..171d1fe
--- /dev/null
+++ b/docs/guides/signer-integration.md
@@ -0,0 +1,25 @@
+
+
+
+# PDF signer integration
+
+`xobject-template` does **not** sign PDFs.
+
+It generates appearance payloads that downstream signers (or any PDF pipeline that can place Form XObjects) can consume.
+
+## What downstream consumers typically use
+
+- `contentStream`
+- `resources`
+- `bbox`
+- optional placement data via integration helpers
+
+## Typical flow
+
+1. Compile template to Form XObject payload.
+2. Place XObject in target page coordinates.
+3. Let downstream signer handle signature cryptography and document-level signing workflow.
+
+## LibreSign context
+
+LibreSign is a flagship use case where this package focuses on deterministic appearance generation while LibreSign handles broader digital-signature workflow responsibilities.
diff --git a/docs/guides/svg.md b/docs/guides/svg.md
new file mode 100644
index 0000000..a3dcdbd
--- /dev/null
+++ b/docs/guides/svg.md
@@ -0,0 +1,22 @@
+
+
+
+# SVG
+
+Source example: `examples/svg.php`
+
+## Current SVG usage path
+
+SVG is handled through image source input (``) and then converted to a PDF Form XObject by the current SVG implementation.
+
+## Typical use cases
+
+- deterministic logos,
+- deterministic icons,
+- deterministic vector shape overlays in signature appearances.
+
+## Important scope limits
+
+SVG support is intentionally scoped and deterministic. It is not a full browser-grade SVG engine.
+
+See [Reference / Supported SVG](../reference/supported-svg.md) for exact supported primitives, path commands, transforms, paint behavior, and known unsupported features.
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..1a90464
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,39 @@
+
+
+
+# xobject-template
+
+`xobject-template` compiles a focused HTML+CSS subset into reusable PDF Form XObject payloads.
+
+It is designed for deterministic visual appearance generation in digital-signature and document-workflow systems: signature appearances, stamps, labels, and overlays.
+
+It solves a practical integration problem: many PDF signers can place Form XObjects but do not want to implement HTML/CSS rendering.
+
+## What this package does
+
+- Renders text from a minimal HTML/CSS subset.
+- Supports context interpolation for template personalization.
+- Supports PNG/JPEG image embedding through file sources.
+- Supports scoped SVG rendering through current implementation support.
+- Produces reusable output contracts (`contentStream`, `resources`, `bbox`, metadata).
+
+## What this package is not
+
+- A full PDF signer.
+- A browser engine.
+- A generic PDF editor.
+- A complete HTML/CSS/SVG renderer.
+
+## Start quickly
+
+Go to [Getting started](getting-started.md) for a runnable example based on `examples/basic-template.php`.
+
+## Key references
+
+- [Supported HTML and CSS](reference/supported-html-css.md)
+- [Supported SVG](reference/supported-svg.md)
+- [LibreSign use case](use-cases/libresign.md)
+- [Contributing](community/contributing.md)
+- [Sustainability](community/sustainability.md)
+
+If this package helps your project generate reliable PDF signature appearances, consider starring the repository, contributing a fixture, or sponsoring maintenance.
diff --git a/docs/installation.md b/docs/installation.md
new file mode 100644
index 0000000..bb329b5
--- /dev/null
+++ b/docs/installation.md
@@ -0,0 +1,31 @@
+
+
+
+# Installation
+
+## Requirements
+
+From `composer.json`:
+
+- PHP: `^8.2`
+- Required PHP extensions: no explicit extra extensions declared by this package.
+
+Runtime notes:
+
+- The HTML parser uses `DOMDocument`.
+- Image metadata detection uses `getimagesizefromstring` for raster images.
+- The package reads local image files for image embedding/export workflows.
+
+## Install with Composer
+
+```bash
+composer require libresign/xobject-template
+```
+
+## Verify installation
+
+A quick verification is to run one example from this repository after dependencies are installed:
+
+- `examples/basic-template.php`
+
+See [Getting started](getting-started.md) for the exact flow and output expectations.
diff --git a/docs/reference/errors.md b/docs/reference/errors.md
new file mode 100644
index 0000000..b8e78fe
--- /dev/null
+++ b/docs/reference/errors.md
@@ -0,0 +1,38 @@
+
+
+
+# Errors and exceptions
+
+## Public exception to catch
+
+- `LibreSign\XObjectTemplate\Exception\UnsupportedSubsetException`
+ - Raised when HTML includes unsupported tags in the subset parser.
+
+## Common invalid argument failures
+
+`\InvalidArgumentException` can be raised by integration/export/image/SVG paths, for example:
+
+- invalid `bbox` for export/placement,
+- empty placement alias,
+- image source missing or unreadable,
+- unsupported image format,
+- invalid SVG/viewBox/path payload.
+
+## What users should do
+
+- Validate input templates against [Supported HTML and CSS](supported-html-css.md).
+- Validate SVG assets against [Supported SVG](supported-svg.md).
+- Keep reproducible template and asset fixtures for failing cases.
+
+## How to report a minimal reproducible issue
+
+Please include:
+
+- minimal HTML/CSS/SVG input,
+- compile request dimensions,
+- expected behavior,
+- actual behavior,
+- package version or commit hash,
+- PHP version and runtime context.
+
+See [Community / Contributing](../community/contributing.md) for reporting guidance.
diff --git a/docs/reference/limitations.md b/docs/reference/limitations.md
new file mode 100644
index 0000000..1d2ec6a
--- /dev/null
+++ b/docs/reference/limitations.md
@@ -0,0 +1,23 @@
+
+
+
+# Limitations
+
+`xobject-template` is intentionally scoped.
+
+## Explicit non-goals
+
+- Not a browser engine.
+- Not a full PDF signer.
+- Not a generic PDF editor.
+- Not a complete HTML/CSS/SVG renderer.
+
+## Rendering scope
+
+- HTML/CSS/SVG support is intentionally limited to deterministic overlay/stamp workflows.
+- Behavior is deterministic, but not browser-compatible.
+- Unsupported subset inputs should be treated as out of scope, not as promised behavior.
+
+## Integration expectation
+
+Consumers should validate templates that matter to their workflow and keep fixture-based regression tests for critical appearance requirements.
diff --git a/docs/reference/output-contract.md b/docs/reference/output-contract.md
new file mode 100644
index 0000000..67efc7f
--- /dev/null
+++ b/docs/reference/output-contract.md
@@ -0,0 +1,33 @@
+
+
+
+# Output contract
+
+`CompileResult` is the stable output boundary for integration.
+
+## Fields
+
+| Field | Type | Meaning |
+|---|---|---|
+| `contentStream` | `string` | PDF operators intended for a Form XObject stream body. |
+| `resources` | `array` | Resource dictionary data (fonts + referenced image XObjects). |
+| `bbox` | `[float,float,float,float]` | Bounding box `[x1,y1,x2,y2]` used for placement/export. |
+| `metadata` | `array` | Diagnostics such as `render_ms`, `line_count`, `image_count`, `node_count`. |
+
+## Resource shape used by helpers
+
+- Fonts are keyed aliases (for example `F1`..`F6`) with PDF font dictionaries.
+- Image resources are keyed aliases (for example `Im0`) with at least source/size metadata.
+
+## What downstream consumers should rely on
+
+- `contentStream` + `resources` + `bbox` as the core Form XObject payload contract.
+- `bbox` defines base geometry for placement scaling.
+
+## What is intentionally not guaranteed
+
+- Browser-equivalent rendering behavior.
+- Full HTML/CSS/SVG compatibility.
+- Stable internals of parser/layout implementation classes.
+
+For transport-neutral payload arrays, see `XObjectPayloadAdapter` in [Public API](public-api.md).
diff --git a/docs/reference/public-api.md b/docs/reference/public-api.md
new file mode 100644
index 0000000..3b9b5c8
--- /dev/null
+++ b/docs/reference/public-api.md
@@ -0,0 +1,52 @@
+
+
+
+# Public API
+
+This page documents user-facing API intended for integration.
+
+## Compiler entry point
+
+- `LibreSign\XObjectTemplate\XObjectTemplateCompiler`
+- `LibreSign\XObjectTemplate\Contract\XObjectTemplateCompilerInterface`
+
+Primary method:
+
+- `compile(CompileRequest $request): CompileResult`
+
+## Request DTO
+
+- `LibreSign\XObjectTemplate\Dto\CompileRequest`
+ - `html: string`
+ - `width: float = 240.0`
+ - `height: float = 84.0`
+ - `context: array = []`
+
+## Result DTO
+
+- `LibreSign\XObjectTemplate\Dto\CompileResult`
+ - `contentStream: string`
+ - `resources: array`
+ - `bbox: array{0: float, 1: float, 2: float, 3: float}`
+ - `metadata: array`
+
+## Integration helpers
+
+- `LibreSign\XObjectTemplate\Integration\XObjectPayloadAdapter`
+ - `toXObjectPayload(CompileResult $result): array{stream,resources,bbox}`
+- `LibreSign\XObjectTemplate\Integration\XObjectPlacementCalculator`
+ - `fromWidth(...)`, `fromHeight(...)`, `fromScale(...)`
+- `LibreSign\XObjectTemplate\Integration\XObjectPlacement`
+ - `toPdfCommand(string $alias): string`
+
+## Preview/export helper
+
+- `LibreSign\XObjectTemplate\Pdf\SinglePagePdfExporter`
+ - `export(CompileResult $result): string`
+
+## Exceptions users should handle
+
+- `LibreSign\XObjectTemplate\Exception\UnsupportedSubsetException`
+- `\InvalidArgumentException` from invalid placement/bounding box/image source/format scenarios.
+
+For exact output semantics, see [Output contract](output-contract.md).
diff --git a/docs/reference/supported-html-css.md b/docs/reference/supported-html-css.md
new file mode 100644
index 0000000..16db04f
--- /dev/null
+++ b/docs/reference/supported-html-css.md
@@ -0,0 +1,43 @@
+
+
+
+# Supported HTML and CSS
+
+This page reflects current implementation and tests.
+
+## HTML elements
+
+Supported:
+
+- `
`
+- `
`
+- ``
+- ` `
+- ``
+
+Not supported (examples): tables, lists, semantic formatting tags like ``.
+
+## CSS properties used by renderer
+
+Common supported properties include:
+
+- Typography: `font-size`, `font-family`, `font-weight`, `line-height`, `color`, `text-align`, `hyphens`, `white-space`
+- Layout/box: `margin`, `padding`, `width`, `height`, `overflow`, `text-overflow`
+- Decoration: `background-color`, `border-color`, `border-width`, `border-radius`
+- Flex subset: `display:flex`, `flex-direction`, `justify-content`, `align-items`, `gap`
+- Absolute positioning: `position:absolute`, `top`, `right`, `bottom`, `left`
+
+## Units and values
+
+- Unitless numeric values are accepted for many properties.
+- `px` is accepted and converted to PDF points.
+- `%` is supported for dimensions/offsets in current layout implementation.
+- Unknown or invalid declarations are ignored, not fatal.
+
+## Practical notes
+
+- Font alias mapping targets built-in Helvetica/Times/Courier variants.
+- Overflow clipping uses PDF clipping paths (`overflow:hidden`).
+- The behavior is deterministic and intentionally scoped for overlays/stamps, not browser parity.
+
+See [Limitations](limitations.md) before designing production templates.
diff --git a/docs/reference/supported-svg.md b/docs/reference/supported-svg.md
new file mode 100644
index 0000000..95e339c
--- /dev/null
+++ b/docs/reference/supported-svg.md
@@ -0,0 +1,91 @@
+
+
+
+# Supported SVG
+
+SVG support is intentionally scoped and deterministic.
+
+## Supported usage pattern
+
+- SVG is consumed through image source input (for example ``).
+- `.svg` and `.svgz` extension detection is supported.
+- Content-based SVG detection is also supported (`
';
+
+$result = (new XObjectTemplateCompiler())->compile(new CompileRequest(
+ html: $html,
+ width: 280.0,
+ height: 100.0,
+));
+
+$pdfFile = $outputDir . '/svg-preview.pdf';
+$pdfBytes = (new SinglePagePdfExporter())->export($result);
+file_put_contents($pdfFile, $pdfBytes);
+
+$jsonFile = $outputDir . '/svg-result.json';
+file_put_contents($jsonFile, json_encode([
+ 'bbox' => $result->bbox,
+ 'resources' => $result->resources,
+ 'metadata' => $result->metadata,
+], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+
+return [
+ 'example' => 'svg',
+ 'generated_files' => [$pdfFile, $jsonFile],
+ 'content_stream' => $result->contentStream,
+ 'resources' => $result->resources,
+ 'bbox' => $result->bbox,
+ 'pdf_file' => $pdfFile,
+];
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..8bb7b09
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,44 @@
+# SPDX-FileCopyrightText: 2026 LibreSign
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+site_name: xobject-template
+site_description: Minimal HTML+CSS to PDF Form XObject compiler for visible signature appearances
+repo_url: https://github.com/LibreSign/xobject-template
+repo_name: LibreSign/xobject-template
+strict: true
+theme:
+ name: material
+plugins:
+ - search
+markdown_extensions:
+ - admonition
+ - tables
+ - toc:
+ permalink: true
+nav:
+ - Home:
+ - Overview: index.md
+ - Installation: installation.md
+ - Getting started: getting-started.md
+ - Guides:
+ - Basic template: guides/basic-template.md
+ - Preview PDF: guides/preview-pdf.md
+ - Images: guides/images.md
+ - SVG: guides/svg.md
+ - Placement and scaling: guides/placement.md
+ - Context interpolation: guides/interpolation.md
+ - PDF signer integration: guides/signer-integration.md
+ - Reference:
+ - Public API: reference/public-api.md
+ - Output contract: reference/output-contract.md
+ - Supported HTML and CSS: reference/supported-html-css.md
+ - Supported SVG: reference/supported-svg.md
+ - Errors and exceptions: reference/errors.md
+ - Limitations: reference/limitations.md
+ - Use cases:
+ - LibreSign: use-cases/libresign.md
+ - Community:
+ - Contributing: community/contributing.md
+ - Sustainability: community/sustainability.md
+ - Roadmap: community/roadmap.md
+ - Maintainer notes: community/maintainer-notes.md
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 2927b38..63edd57 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -16,6 +16,9 @@
tests/Integration
+
+ tests/Documentation
+
diff --git a/requirements-docs.txt b/requirements-docs.txt
new file mode 100644
index 0000000..f4a88ee
--- /dev/null
+++ b/requirements-docs.txt
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2026 LibreSign
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+mkdocs>=1.6,<2
+mkdocs-material>=9.6,<10
diff --git a/scripts/check-docs-drift.php b/scripts/check-docs-drift.php
new file mode 100644
index 0000000..39d61eb
--- /dev/null
+++ b/scripts/check-docs-drift.php
@@ -0,0 +1,113 @@
+isFile()) {
+ continue;
+ }
+
+ if (strtolower($docEntry->getExtension()) !== 'md') {
+ continue;
+ }
+
+ $docsFiles[] = $docEntry->getPathname();
+}
+
+$allDocContent = '';
+foreach ($docsFiles as $docFile) {
+ $content = file_get_contents($docFile);
+ if (is_string($content)) {
+ $allDocContent .= "\n" . $content;
+ }
+}
+
+preg_match_all('/examples\/[a-z0-9\-]+\.php/', $allDocContent, $exampleMatches);
+$referencedExamples = array_values(array_unique($exampleMatches[0] ?? []));
+foreach ($referencedExamples as $exampleReference) {
+ $examplePath = $projectRoot . '/' . $exampleReference;
+ if (!is_file($examplePath)) {
+ $errors[] = sprintf('Documented example does not exist: %s', $exampleReference);
+ }
+}
+
+$examplesTestPath = $projectRoot . '/tests/Documentation/ExamplesTest.php';
+$examplesTest = file_get_contents($examplesTestPath);
+if (!is_string($examplesTest)) {
+ $errors[] = 'Could not read tests/Documentation/ExamplesTest.php';
+} else {
+ $allExamples = glob($projectRoot . '/examples/*.php');
+ if (!is_array($allExamples)) {
+ $allExamples = [];
+ }
+
+ foreach ($allExamples as $exampleFile) {
+ $base = basename($exampleFile);
+ if (!str_contains($examplesTest, "'" . $base . "'")) {
+ $errors[] = sprintf('Example not declared in ExamplesTest::documentedExamples(): %s', $base);
+ }
+ }
+}
+
+$subsetParserPath = $projectRoot . '/src/Html/SubsetHtmlParser.php';
+$subsetParser = file_get_contents($subsetParserPath);
+$supportedHtmlDoc = file_get_contents($projectRoot . '/docs/reference/supported-html-css.md');
+if (is_string($subsetParser) && is_string($supportedHtmlDoc)) {
+ if (preg_match('/private array \$allowedTags\s*=\s*\[(.*?)\];/s', $subsetParser, $allowedTagsMatch) === 1) {
+ preg_match_all("/'([a-z]+)'\\s*=>\\s*true/", $allowedTagsMatch[1], $tagMatches);
+ $tags = array_values(array_unique($tagMatches[1] ?? []));
+ foreach ($tags as $tag) {
+ if (!str_contains($supportedHtmlDoc, '<' . $tag . '>')) {
+ $errors[] = sprintf('Supported HTML tag missing from docs/reference/supported-html-css.md: <%s>', $tag);
+ }
+ }
+ }
+}
+
+$svgParserPath = $projectRoot . '/src/Pdf/Svg/SvgPathCommandParser.php';
+$svgParser = file_get_contents($svgParserPath);
+$supportedSvgDoc = file_get_contents($projectRoot . '/docs/reference/supported-svg.md');
+if (is_string($svgParser) && is_string($supportedSvgDoc)) {
+ preg_match_all("/'([A-Z])'\s*=>/", $svgParser, $cmdMatches);
+ $commands = array_values(array_unique($cmdMatches[1] ?? []));
+ foreach ($commands as $command) {
+ if (!str_contains($supportedSvgDoc, '`' . $command . '`,') && !str_contains($supportedSvgDoc, '`' . $command . '`')) {
+ $errors[] = sprintf('SVG path command missing from docs/reference/supported-svg.md: %s', $command);
+ }
+ }
+}
+
+if ($errors !== []) {
+ foreach ($errors as $error) {
+ fwrite(STDERR, '- ' . $error . PHP_EOL);
+ }
+
+ exit(1);
+}
+
+fwrite(STDOUT, "Documentation drift checks passed.\n");
diff --git a/tests/Documentation/ExamplesTest.php b/tests/Documentation/ExamplesTest.php
new file mode 100644
index 0000000..8a7ad7b
--- /dev/null
+++ b/tests/Documentation/ExamplesTest.php
@@ -0,0 +1,101 @@
+
+ */
+ public static function documentedExamples(): array
+ {
+ return [
+ 'basic-template.php',
+ 'preview-pdf.php',
+ 'images.php',
+ 'svg.php',
+ 'placement.php',
+ 'interpolation.php',
+ ];
+ }
+
+ /**
+ * @return iterable
+ */
+ public static function exampleProvider(): iterable
+ {
+ foreach (self::documentedExamples() as $exampleFile) {
+ yield $exampleFile => ['exampleFile' => $exampleFile];
+ }
+ }
+
+ #[DataProvider('exampleProvider')]
+ public function testExampleScriptsExecuteAndGenerateExpectedArtifacts(string $exampleFile): void
+ {
+ $projectRoot = dirname(__DIR__, 2);
+ $examplePath = $projectRoot . '/examples/' . $exampleFile;
+ self::assertFileExists($examplePath);
+
+ $result = require $examplePath;
+
+ self::assertIsArray($result);
+ self::assertArrayHasKey('generated_files', $result);
+ self::assertArrayHasKey('content_stream', $result);
+ self::assertArrayHasKey('resources', $result);
+ self::assertArrayHasKey('bbox', $result);
+
+ self::assertIsString($result['content_stream']);
+ self::assertNotSame('', trim((string) $result['content_stream']));
+
+ self::assertIsArray($result['resources']);
+ self::assertArrayHasKey('Font', $result['resources']);
+
+ self::assertIsArray($result['bbox']);
+ self::assertCount(4, $result['bbox']);
+
+ /** @var list $generatedFiles */
+ $generatedFiles = $result['generated_files'];
+ self::assertNotSame([], $generatedFiles);
+
+ foreach ($generatedFiles as $generatedFile) {
+ self::assertFileExists($generatedFile);
+ $contents = file_get_contents($generatedFile);
+ self::assertIsString($contents);
+ self::assertNotSame('', $contents);
+
+ if (str_ends_with($generatedFile, '.pdf')) {
+ self::assertStringStartsWith('%PDF-', $contents);
+ self::assertStringContainsString('%%EOF', $contents);
+ }
+ }
+
+ if (isset($result['pdf_file']) && is_string($result['pdf_file'])) {
+ $pdfContents = file_get_contents($result['pdf_file']);
+ self::assertIsString($pdfContents);
+ self::assertStringStartsWith('%PDF-', $pdfContents);
+ }
+ }
+
+ public function testEveryPhpExampleFileIsCoveredByTheProvider(): void
+ {
+ $projectRoot = dirname(__DIR__, 2);
+ $allPhpExamples = glob($projectRoot . '/examples/*.php');
+ self::assertIsArray($allPhpExamples);
+
+ $actual = array_map(static fn (string $path): string => basename($path), $allPhpExamples);
+ sort($actual);
+
+ $expected = self::documentedExamples();
+ sort($expected);
+
+ self::assertSame($expected, $actual);
+ }
+}
From 271fecd3f8845ecf7278d57d5beaa112ea492d1b Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 01:50:33 -0300
Subject: [PATCH 02/31] docs: remove duplicate PR template and reduce
contributing overlap
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
.github/pull_request_template.md | 34 --------------------------------
docs/community/contributing.md | 14 ++++++++-----
2 files changed, 9 insertions(+), 39 deletions(-)
delete mode 100644 .github/pull_request_template.md
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
deleted file mode 100644
index 4445706..0000000
--- a/.github/pull_request_template.md
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-## Scope
-
-- [ ] Change scope and impact are clearly described
-
-## Evidence
-
-- [ ] Tests added/updated
-- [ ] Relevant checks executed
-
-## Documentation
-
-- [ ] Documentation updated
-- [ ] Examples updated if public behavior changed
-- [ ] Supported HTML/CSS/SVG matrix updated if relevant
-- [ ] Documentation drift checks pass
-
-## Performance
-
-- [ ] Performance impact considered
-
-## Output integrity
-
-- [ ] No unsupported feature documented as supported
-- [ ] No generated build artifacts committed to the source branch
-- [ ] LibreSign use case impact considered if relevant
-
-## Compliance
-
-- [ ] DCO sign-off in all commits
-- [ ] REUSE/SPDX metadata preserved
-- [ ] License compatibility reviewed
diff --git a/docs/community/contributing.md b/docs/community/contributing.md
index 8177495..d7ad91c 100644
--- a/docs/community/contributing.md
+++ b/docs/community/contributing.md
@@ -5,25 +5,29 @@
Contributions are welcome. Keep quality high and changes scoped.
-## Setup
+The repository-level governance lives in the root file:
-Install dependencies and run checks from repository root.
+- `CONTRIBUTING.md`
-## Core quality commands
+Use this page as a **docs/examples complement**, not as a replacement for root contribution policy.
+
+## Docs and examples workflow
+
+From repository root, run:
- `composer lint`
- `composer test:unit`
- `composer test:integration`
- `composer docs:test`
-## Documentation and examples
+## Documentation and examples rules
- Add/update runnable examples in `examples/`.
- Keep examples small and deterministic.
- Ensure docs snippets come from real examples.
- Run docs drift checks before opening a PR.
-## Adding supported behavior
+## When adding supported behavior
When adding HTML/CSS/SVG support:
From 315c860449b00dfe57a213a99085db753d516af4 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 01:57:39 -0300
Subject: [PATCH 03/31] docs: switch to minimal maintenance mode
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
.github/ISSUE_TEMPLATE/config.yml | 3 -
.github/workflows/docs.yml | 81 -------------------
README.md | 10 +--
composer.json | 10 +--
docs/community/contributing.md | 55 -------------
docs/community/maintainer-notes.md | 27 -------
docs/community/roadmap.md | 17 ----
docs/community/sustainability.md | 32 --------
docs/getting-started.md | 43 ----------
docs/guides/basic-template.md | 24 ------
docs/guides/images.md | 37 ---------
docs/guides/interpolation.md | 21 -----
docs/guides/placement.md | 23 ------
docs/guides/preview-pdf.md | 22 ------
docs/guides/signer-integration.md | 25 ------
docs/guides/svg.md | 22 ------
docs/index.md | 39 ---------
docs/installation.md | 31 --------
docs/reference/errors.md | 38 ---------
docs/reference/limitations.md | 23 ------
docs/reference/output-contract.md | 33 --------
docs/reference/public-api.md | 52 ------------
docs/reference/supported-html-css.md | 43 ----------
docs/reference/supported-svg.md | 91 ---------------------
docs/use-cases/libresign.md | 23 ------
mkdocs.yml | 44 -----------
requirements-docs.txt | 5 --
scripts/check-docs-drift.php | 113 ---------------------------
28 files changed, 2 insertions(+), 985 deletions(-)
delete mode 100644 .github/workflows/docs.yml
delete mode 100644 docs/community/contributing.md
delete mode 100644 docs/community/maintainer-notes.md
delete mode 100644 docs/community/roadmap.md
delete mode 100644 docs/community/sustainability.md
delete mode 100644 docs/getting-started.md
delete mode 100644 docs/guides/basic-template.md
delete mode 100644 docs/guides/images.md
delete mode 100644 docs/guides/interpolation.md
delete mode 100644 docs/guides/placement.md
delete mode 100644 docs/guides/preview-pdf.md
delete mode 100644 docs/guides/signer-integration.md
delete mode 100644 docs/guides/svg.md
delete mode 100644 docs/index.md
delete mode 100644 docs/installation.md
delete mode 100644 docs/reference/errors.md
delete mode 100644 docs/reference/limitations.md
delete mode 100644 docs/reference/output-contract.md
delete mode 100644 docs/reference/public-api.md
delete mode 100644 docs/reference/supported-html-css.md
delete mode 100644 docs/reference/supported-svg.md
delete mode 100644 docs/use-cases/libresign.md
delete mode 100644 mkdocs.yml
delete mode 100644 requirements-docs.txt
delete mode 100644 scripts/check-docs-drift.php
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index edce05d..2be618c 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -6,9 +6,6 @@ contact_links:
- name: Security disclosure
url: https://github.com/LibreSign/xobject-template/security
about: Please report vulnerabilities privately.
- - name: Documentation site
- url: https://github.com/LibreSign/xobject-template/tree/main/docs
- about: Browse the user documentation source.
- name: Sponsor maintenance
url: https://github.com/sponsors/LibreSign
about: Funding helps prioritize fixes and compatibility work.
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
deleted file mode 100644
index 81ad86a..0000000
--- a/.github/workflows/docs.yml
+++ /dev/null
@@ -1,81 +0,0 @@
-# SPDX-FileCopyrightText: 2026 LibreSign
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-name: docs
-
-on:
- pull_request:
- push:
- branches: [main]
-
-permissions:
- contents: read
-
-jobs:
- validate-docs:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v6
-
- - name: Detect minimum PHP from composer.json
- id: php_min
- run: |
- php_version=$(grep -Po '"php"\s*:\s*"\K[^"]+' composer.json | grep -Eo '[0-9]+\.[0-9]+' | head -n1)
- [[ -n "$php_version" ]] || { echo "Could not determine minimum PHP version"; exit 1; }
- echo "version=$php_version" >> "$GITHUB_OUTPUT"
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: ${{ steps.php_min.outputs.version }}
-
- - name: Setup Python
- uses: actions/setup-python@v6
- with:
- python-version: '3.12'
-
- - name: Install Composer dependencies
- run: composer install --no-interaction --prefer-dist
-
- - name: Install PHPUnit bin dependencies
- run: composer bin phpunit install --no-interaction --prefer-dist
-
- - name: Install docs dependencies
- run: python -m pip install --upgrade pip && pip install -r requirements-docs.txt
-
- # Pull requests only validate docs. No deployment from PRs.
- - name: Run docs tests
- run: composer run docs:test
-
- deploy-docs:
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
- needs: validate-docs
- runs-on: ubuntu-latest
- permissions:
- contents: write
- steps:
- - name: Checkout
- uses: actions/checkout@v6
-
- - name: Setup Python
- uses: actions/setup-python@v6
- with:
- python-version: '3.12'
-
- - name: Install docs dependencies
- run: python -m pip install --upgrade pip && pip install -r requirements-docs.txt
-
- - name: Build docs site
- run: python -m mkdocs build --strict
-
- # Pushes to main publish docs to gh-pages branch.
- # Maintainers should configure GitHub Pages source as:
- # Deploy from branch -> gh-pages -> /root
- - name: Deploy site to gh-pages branch
- uses: peaceiris/actions-gh-pages@v4
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: ./site
- publish_branch: gh-pages
- force_orphan: true
diff --git a/README.md b/README.md
index d2ec7b9..30cd00a 100644
--- a/README.md
+++ b/README.md
@@ -43,21 +43,13 @@ $result = $compiler->compile(new CompileRequest(
// $result->bbox
```
-## Documentation
-
-Full documentation site source is in `docs/` and published via GitHub Pages after merge:
-
-- Installation, guides, examples, and support matrices: `docs/`
-
## LibreSign use case
-- Docs page: `docs/use-cases/libresign.md`
- LibreSign project:
## Contributing
-- Main guide: `docs/community/contributing.md`
-- Repository policy: `CONTRIBUTING.md`
+- Repository guide: `CONTRIBUTING.md`
If this package helps your project generate reliable PDF signature appearances, please star the repository. It helps other developers discover the project and signals that this work is worth maintaining.
diff --git a/composer.json b/composer.json
index df68c23..90ae953 100644
--- a/composer.json
+++ b/composer.json
@@ -73,15 +73,7 @@
"sh:security": "shellcheck scripts/*.sh",
"test:unit": "vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --testsuite unit",
"test:integration": "vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --testsuite integration",
- "docs:examples": "vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --testsuite documentation --filter ExamplesTest",
- "docs:check-drift": "php scripts/check-docs-drift.php",
- "docs:build": "python3 -m mkdocs build --strict",
- "docs:serve": "python3 -m mkdocs serve",
- "docs:test": [
- "@docs:examples",
- "@docs:check-drift",
- "@docs:build"
- ],
+ "examples:test": "vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --testsuite documentation --filter ExamplesTest",
"test:coverage": "XDEBUG_MODE=coverage vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --coverage-text --coverage-clover=build/coverage/clover.xml",
"mutation:test": "vendor-bin/mutation/vendor/infection/infection/bin/infection --threads=max",
"benchmark:run": "vendor-bin/phpbench/vendor/phpbench/phpbench/bin/phpbench run --config=phpbench.json --report=aggregate",
diff --git a/docs/community/contributing.md b/docs/community/contributing.md
deleted file mode 100644
index d7ad91c..0000000
--- a/docs/community/contributing.md
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-# Contributing
-
-Contributions are welcome. Keep quality high and changes scoped.
-
-The repository-level governance lives in the root file:
-
-- `CONTRIBUTING.md`
-
-Use this page as a **docs/examples complement**, not as a replacement for root contribution policy.
-
-## Docs and examples workflow
-
-From repository root, run:
-
-- `composer lint`
-- `composer test:unit`
-- `composer test:integration`
-- `composer docs:test`
-
-## Documentation and examples rules
-
-- Add/update runnable examples in `examples/`.
-- Keep examples small and deterministic.
-- Ensure docs snippets come from real examples.
-- Run docs drift checks before opening a PR.
-
-## When adding supported behavior
-
-When adding HTML/CSS/SVG support:
-
-1. Add/adjust implementation.
-2. Add or update fixture-based tests.
-3. Update support reference pages.
-4. Keep limitations explicit (do not overstate support).
-
-## Reporting rendering bugs
-
-A good minimal reproducible report includes:
-
-- minimal HTML/CSS/SVG input,
-- expected vs actual result,
-- generated output details when possible,
-- package version/commit and PHP version.
-
-## High-value contributions
-
-- SVG edge-case fixtures,
-- PDF compatibility fixtures,
-- layout bug fixes,
-- documentation improvements,
-- tests for real-world signature templates,
-- performance improvements backed by benchmark evidence.
diff --git a/docs/community/maintainer-notes.md b/docs/community/maintainer-notes.md
deleted file mode 100644
index a03fb19..0000000
--- a/docs/community/maintainer-notes.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-# Maintainer notes
-
-## Documentation deployment
-
-- Pull requests validate docs only.
-- Pushes to `main` validate and publish the generated site to `gh-pages`.
-- GitHub Pages should be configured as: deploy from branch `gh-pages`, folder `/root`.
-
-## Validation flow
-
-- Execute `composer docs:test`.
-- Ensure examples in `examples/` remain runnable.
-- Keep support references aligned with parser/layout/SVG behavior and tests.
-
-## Scope control
-
-- Keep user docs focused on public contracts.
-- Avoid scope creep into browser-engine semantics or full PDF-signing behavior.
-- Keep LibreSign use case visible without making this package LibreSign-only.
-
-## Drift policy
-
-- Support matrix docs are checked by `scripts/check-docs-drift.php` against known code-level indicators.
-- TODO: add finer-grained automated extraction for CSS property lists if parser/layout structure changes substantially.
diff --git a/docs/community/roadmap.md b/docs/community/roadmap.md
deleted file mode 100644
index 9b2fbaf..0000000
--- a/docs/community/roadmap.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-# Roadmap
-
-> This page describes priorities, not implemented behavior.
-
-Possible priority areas:
-
-- SVG compatibility improvements.
-- Additional layout fixtures.
-- More PDF compatibility tests.
-- More documentation examples.
-- Performance benchmark expansions.
-- Additional downstream signer integration examples.
-
-Roadmap priorities may be influenced by real-world LibreSign needs, community contributions, and funded maintenance work.
diff --git a/docs/community/sustainability.md b/docs/community/sustainability.md
deleted file mode 100644
index 8ff697b..0000000
--- a/docs/community/sustainability.md
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-# Sustainability
-
-Reliable PDF appearance generation needs sustained maintenance.
-
-This package sits at the intersection of PDF constraints, deterministic layout, images, scoped SVG handling, and downstream signer integration. Edge cases accumulate quickly, and maintaining high-confidence fixtures and tests takes ongoing effort.
-
-## Why funding matters
-
-Sponsorship and paid support help sustain:
-
-- SVG support hardening,
-- documentation quality,
-- QA and drift checks,
-- benchmark and performance maintenance,
-- integration examples for downstream signer workflows.
-
-## Practical ways to support
-
-- Star the repository.
-- Sponsor the project.
-- Contribute fixtures/tests.
-- Report minimal reproducible issues.
-- Evaluate LibreSign if you need a full digital-signature workflow.
-
-Support channel:
-
-- GitHub Sponsors:
-
-If this package helps your project generate reliable PDF signature appearances, consider starring the repository, contributing a fixture, or sponsoring maintenance.
diff --git a/docs/getting-started.md b/docs/getting-started.md
deleted file mode 100644
index 217241c..0000000
--- a/docs/getting-started.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-# Getting started
-
-This page uses the real example file `examples/basic-template.php`.
-
-## Minimal compile flow
-
-The example creates a `CompileRequest`, calls `XObjectTemplateCompiler::compile()`, and writes a JSON output summary under `build/examples/`.
-
-```php
-compile(new CompileRequest(
- html: '
Signed by {{ name }}
',
- width: 240.0,
- height: 84.0,
- context: ['name' => 'Alice'],
-));
-```
-
-## Result at user level
-
-The compile result provides:
-
-- `contentStream`: PDF operators for a Form XObject stream.
-- `resources`: font/image resource dictionary data for downstream serialization.
-- `bbox`: `[x1, y1, x2, y2]` bounding box.
-- `metadata`: rendering diagnostics (`render_ms`, `line_count`, `image_count`, `node_count`).
-
-## Next steps
-
-- Basic usage details: [Guides / Basic template](guides/basic-template.md)
-- Placement/scaling in downstream PDFs: [Guides / Placement and scaling](guides/placement.md)
-- Full output contract: [Reference / Output contract](reference/output-contract.md)
-
-If this package helps your project generate reliable PDF signature appearances, consider starring the repository, contributing a fixture, or sponsoring maintenance.
diff --git a/docs/guides/basic-template.md b/docs/guides/basic-template.md
deleted file mode 100644
index 72edba6..0000000
--- a/docs/guides/basic-template.md
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-# Basic template
-
-Source example: `examples/basic-template.php`
-
-## What the example demonstrates
-
-- HTML input with inline styles.
-- `CompileRequest` with `width`, `height`, and optional `context`.
-- Compilation through `XObjectTemplateCompiler`.
-- Inspection of `contentStream`, `resources`, `bbox`, and `metadata`.
-
-## Expected output
-
-The example writes `build/examples/basic-template-result.json` and includes:
-
-- non-empty content stream,
-- font resources,
-- a 4-value bounding box,
-- render metadata.
-
-Use this as your baseline before moving to images, SVG, and placement guides.
diff --git a/docs/guides/images.md b/docs/guides/images.md
deleted file mode 100644
index f9585a7..0000000
--- a/docs/guides/images.md
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-# Images
-
-Source example: `examples/images.php`
-
-## Supported raster formats
-
-Based on implementation and tests:
-
-- PNG: supported.
-- JPEG: supported.
-- Other raster formats (for example GIF): not supported.
-
-## Source resolution
-
-Image sources are file paths from ``.
-
-- During compile, the source path is carried into XObject resources.
-- During export/embedding, the file is read from disk.
-
-## Dimensions and resources
-
-- `` dimensions come from style width/height when provided.
-- Missing/invalid image dimensions default to `32x32` in layout.
-- Generated resources include XObject aliases (for example `Im0`, `Im1`) with `Source`, `Width`, and `Height` metadata.
-
-## Error behavior and limitations
-
-Typical failures include:
-
-- source file missing/unreadable,
-- unsupported mime type,
-- invalid PNG/JPEG payload.
-
-See [Errors and exceptions](../reference/errors.md) and [Supported SVG](../reference/supported-svg.md) for vector behavior.
diff --git a/docs/guides/interpolation.md b/docs/guides/interpolation.md
deleted file mode 100644
index 1d45f6e..0000000
--- a/docs/guides/interpolation.md
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-# Context interpolation
-
-Source example: `examples/interpolation.php`
-
-## Placeholder syntax
-
-The current interpolation feature supports placeholders like:
-
-- `{{ name }}`
-- `{{ role }}`
-
-## Behavior
-
-- Interpolation happens before HTML subset parsing.
-- Values are HTML-escaped before insertion.
-- Unknown placeholders remain unchanged.
-
-This is useful for signature labels and document workflow overlays where a template shape is fixed but content changes per signer/document.
diff --git a/docs/guides/placement.md b/docs/guides/placement.md
deleted file mode 100644
index 351bef4..0000000
--- a/docs/guides/placement.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-# Placement and scaling
-
-Source example: `examples/placement.php`
-
-## Downstream placement model
-
-Compile once at a design size, then place the generated Form XObject in downstream PDFs.
-
-In most workflows, proportional resizing should happen through placement scaling, not by recompiling HTML for each target size.
-
-## Public helper classes
-
-- `LibreSign\XObjectTemplate\Integration\XObjectPlacementCalculator`
-- `LibreSign\XObjectTemplate\Integration\XObjectPlacement`
-
-The example shows `fromWidth()`, `fromHeight()`, and `fromScale()` and emits a placement command string with `toPdfCommand()`.
-
-## Why this is recommended
-
-Uniform placement scaling keeps text, spacing, and images visually consistent and avoids introducing avoidable template variants.
diff --git a/docs/guides/preview-pdf.md b/docs/guides/preview-pdf.md
deleted file mode 100644
index a7fec22..0000000
--- a/docs/guides/preview-pdf.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-# Preview PDF
-
-Source example: `examples/preview-pdf.php`
-
-## Why preview export is useful
-
-`xobject-template` compiles Form XObject payloads. For visual debugging, `SinglePagePdfExporter` can wrap a compile result into a one-page PDF using the result `bbox`.
-
-This makes template iteration easier because you can quickly inspect appearance output without wiring a full downstream signer flow.
-
-## What the example does
-
-- Compiles a simple template.
-- Exports a preview PDF.
-- Writes output to `build/examples/preview.pdf`.
-
-## Scope reminder
-
-Preview export is a helper for inspection and integration tests. Production signing/placement still happens in your downstream PDF workflow.
diff --git a/docs/guides/signer-integration.md b/docs/guides/signer-integration.md
deleted file mode 100644
index 171d1fe..0000000
--- a/docs/guides/signer-integration.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-# PDF signer integration
-
-`xobject-template` does **not** sign PDFs.
-
-It generates appearance payloads that downstream signers (or any PDF pipeline that can place Form XObjects) can consume.
-
-## What downstream consumers typically use
-
-- `contentStream`
-- `resources`
-- `bbox`
-- optional placement data via integration helpers
-
-## Typical flow
-
-1. Compile template to Form XObject payload.
-2. Place XObject in target page coordinates.
-3. Let downstream signer handle signature cryptography and document-level signing workflow.
-
-## LibreSign context
-
-LibreSign is a flagship use case where this package focuses on deterministic appearance generation while LibreSign handles broader digital-signature workflow responsibilities.
diff --git a/docs/guides/svg.md b/docs/guides/svg.md
deleted file mode 100644
index a3dcdbd..0000000
--- a/docs/guides/svg.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-# SVG
-
-Source example: `examples/svg.php`
-
-## Current SVG usage path
-
-SVG is handled through image source input (``) and then converted to a PDF Form XObject by the current SVG implementation.
-
-## Typical use cases
-
-- deterministic logos,
-- deterministic icons,
-- deterministic vector shape overlays in signature appearances.
-
-## Important scope limits
-
-SVG support is intentionally scoped and deterministic. It is not a full browser-grade SVG engine.
-
-See [Reference / Supported SVG](../reference/supported-svg.md) for exact supported primitives, path commands, transforms, paint behavior, and known unsupported features.
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index 1a90464..0000000
--- a/docs/index.md
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-# xobject-template
-
-`xobject-template` compiles a focused HTML+CSS subset into reusable PDF Form XObject payloads.
-
-It is designed for deterministic visual appearance generation in digital-signature and document-workflow systems: signature appearances, stamps, labels, and overlays.
-
-It solves a practical integration problem: many PDF signers can place Form XObjects but do not want to implement HTML/CSS rendering.
-
-## What this package does
-
-- Renders text from a minimal HTML/CSS subset.
-- Supports context interpolation for template personalization.
-- Supports PNG/JPEG image embedding through file sources.
-- Supports scoped SVG rendering through current implementation support.
-- Produces reusable output contracts (`contentStream`, `resources`, `bbox`, metadata).
-
-## What this package is not
-
-- A full PDF signer.
-- A browser engine.
-- A generic PDF editor.
-- A complete HTML/CSS/SVG renderer.
-
-## Start quickly
-
-Go to [Getting started](getting-started.md) for a runnable example based on `examples/basic-template.php`.
-
-## Key references
-
-- [Supported HTML and CSS](reference/supported-html-css.md)
-- [Supported SVG](reference/supported-svg.md)
-- [LibreSign use case](use-cases/libresign.md)
-- [Contributing](community/contributing.md)
-- [Sustainability](community/sustainability.md)
-
-If this package helps your project generate reliable PDF signature appearances, consider starring the repository, contributing a fixture, or sponsoring maintenance.
diff --git a/docs/installation.md b/docs/installation.md
deleted file mode 100644
index bb329b5..0000000
--- a/docs/installation.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-# Installation
-
-## Requirements
-
-From `composer.json`:
-
-- PHP: `^8.2`
-- Required PHP extensions: no explicit extra extensions declared by this package.
-
-Runtime notes:
-
-- The HTML parser uses `DOMDocument`.
-- Image metadata detection uses `getimagesizefromstring` for raster images.
-- The package reads local image files for image embedding/export workflows.
-
-## Install with Composer
-
-```bash
-composer require libresign/xobject-template
-```
-
-## Verify installation
-
-A quick verification is to run one example from this repository after dependencies are installed:
-
-- `examples/basic-template.php`
-
-See [Getting started](getting-started.md) for the exact flow and output expectations.
diff --git a/docs/reference/errors.md b/docs/reference/errors.md
deleted file mode 100644
index b8e78fe..0000000
--- a/docs/reference/errors.md
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-# Errors and exceptions
-
-## Public exception to catch
-
-- `LibreSign\XObjectTemplate\Exception\UnsupportedSubsetException`
- - Raised when HTML includes unsupported tags in the subset parser.
-
-## Common invalid argument failures
-
-`\InvalidArgumentException` can be raised by integration/export/image/SVG paths, for example:
-
-- invalid `bbox` for export/placement,
-- empty placement alias,
-- image source missing or unreadable,
-- unsupported image format,
-- invalid SVG/viewBox/path payload.
-
-## What users should do
-
-- Validate input templates against [Supported HTML and CSS](supported-html-css.md).
-- Validate SVG assets against [Supported SVG](supported-svg.md).
-- Keep reproducible template and asset fixtures for failing cases.
-
-## How to report a minimal reproducible issue
-
-Please include:
-
-- minimal HTML/CSS/SVG input,
-- compile request dimensions,
-- expected behavior,
-- actual behavior,
-- package version or commit hash,
-- PHP version and runtime context.
-
-See [Community / Contributing](../community/contributing.md) for reporting guidance.
diff --git a/docs/reference/limitations.md b/docs/reference/limitations.md
deleted file mode 100644
index 1d2ec6a..0000000
--- a/docs/reference/limitations.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-# Limitations
-
-`xobject-template` is intentionally scoped.
-
-## Explicit non-goals
-
-- Not a browser engine.
-- Not a full PDF signer.
-- Not a generic PDF editor.
-- Not a complete HTML/CSS/SVG renderer.
-
-## Rendering scope
-
-- HTML/CSS/SVG support is intentionally limited to deterministic overlay/stamp workflows.
-- Behavior is deterministic, but not browser-compatible.
-- Unsupported subset inputs should be treated as out of scope, not as promised behavior.
-
-## Integration expectation
-
-Consumers should validate templates that matter to their workflow and keep fixture-based regression tests for critical appearance requirements.
diff --git a/docs/reference/output-contract.md b/docs/reference/output-contract.md
deleted file mode 100644
index 67efc7f..0000000
--- a/docs/reference/output-contract.md
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-# Output contract
-
-`CompileResult` is the stable output boundary for integration.
-
-## Fields
-
-| Field | Type | Meaning |
-|---|---|---|
-| `contentStream` | `string` | PDF operators intended for a Form XObject stream body. |
-| `resources` | `array` | Resource dictionary data (fonts + referenced image XObjects). |
-| `bbox` | `[float,float,float,float]` | Bounding box `[x1,y1,x2,y2]` used for placement/export. |
-| `metadata` | `array` | Diagnostics such as `render_ms`, `line_count`, `image_count`, `node_count`. |
-
-## Resource shape used by helpers
-
-- Fonts are keyed aliases (for example `F1`..`F6`) with PDF font dictionaries.
-- Image resources are keyed aliases (for example `Im0`) with at least source/size metadata.
-
-## What downstream consumers should rely on
-
-- `contentStream` + `resources` + `bbox` as the core Form XObject payload contract.
-- `bbox` defines base geometry for placement scaling.
-
-## What is intentionally not guaranteed
-
-- Browser-equivalent rendering behavior.
-- Full HTML/CSS/SVG compatibility.
-- Stable internals of parser/layout implementation classes.
-
-For transport-neutral payload arrays, see `XObjectPayloadAdapter` in [Public API](public-api.md).
diff --git a/docs/reference/public-api.md b/docs/reference/public-api.md
deleted file mode 100644
index 3b9b5c8..0000000
--- a/docs/reference/public-api.md
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-# Public API
-
-This page documents user-facing API intended for integration.
-
-## Compiler entry point
-
-- `LibreSign\XObjectTemplate\XObjectTemplateCompiler`
-- `LibreSign\XObjectTemplate\Contract\XObjectTemplateCompilerInterface`
-
-Primary method:
-
-- `compile(CompileRequest $request): CompileResult`
-
-## Request DTO
-
-- `LibreSign\XObjectTemplate\Dto\CompileRequest`
- - `html: string`
- - `width: float = 240.0`
- - `height: float = 84.0`
- - `context: array = []`
-
-## Result DTO
-
-- `LibreSign\XObjectTemplate\Dto\CompileResult`
- - `contentStream: string`
- - `resources: array`
- - `bbox: array{0: float, 1: float, 2: float, 3: float}`
- - `metadata: array`
-
-## Integration helpers
-
-- `LibreSign\XObjectTemplate\Integration\XObjectPayloadAdapter`
- - `toXObjectPayload(CompileResult $result): array{stream,resources,bbox}`
-- `LibreSign\XObjectTemplate\Integration\XObjectPlacementCalculator`
- - `fromWidth(...)`, `fromHeight(...)`, `fromScale(...)`
-- `LibreSign\XObjectTemplate\Integration\XObjectPlacement`
- - `toPdfCommand(string $alias): string`
-
-## Preview/export helper
-
-- `LibreSign\XObjectTemplate\Pdf\SinglePagePdfExporter`
- - `export(CompileResult $result): string`
-
-## Exceptions users should handle
-
-- `LibreSign\XObjectTemplate\Exception\UnsupportedSubsetException`
-- `\InvalidArgumentException` from invalid placement/bounding box/image source/format scenarios.
-
-For exact output semantics, see [Output contract](output-contract.md).
diff --git a/docs/reference/supported-html-css.md b/docs/reference/supported-html-css.md
deleted file mode 100644
index 16db04f..0000000
--- a/docs/reference/supported-html-css.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-# Supported HTML and CSS
-
-This page reflects current implementation and tests.
-
-## HTML elements
-
-Supported:
-
-- `
`
-- `
`
-- ``
-- ` `
-- ``
-
-Not supported (examples): tables, lists, semantic formatting tags like ``.
-
-## CSS properties used by renderer
-
-Common supported properties include:
-
-- Typography: `font-size`, `font-family`, `font-weight`, `line-height`, `color`, `text-align`, `hyphens`, `white-space`
-- Layout/box: `margin`, `padding`, `width`, `height`, `overflow`, `text-overflow`
-- Decoration: `background-color`, `border-color`, `border-width`, `border-radius`
-- Flex subset: `display:flex`, `flex-direction`, `justify-content`, `align-items`, `gap`
-- Absolute positioning: `position:absolute`, `top`, `right`, `bottom`, `left`
-
-## Units and values
-
-- Unitless numeric values are accepted for many properties.
-- `px` is accepted and converted to PDF points.
-- `%` is supported for dimensions/offsets in current layout implementation.
-- Unknown or invalid declarations are ignored, not fatal.
-
-## Practical notes
-
-- Font alias mapping targets built-in Helvetica/Times/Courier variants.
-- Overflow clipping uses PDF clipping paths (`overflow:hidden`).
-- The behavior is deterministic and intentionally scoped for overlays/stamps, not browser parity.
-
-See [Limitations](limitations.md) before designing production templates.
diff --git a/docs/reference/supported-svg.md b/docs/reference/supported-svg.md
deleted file mode 100644
index 95e339c..0000000
--- a/docs/reference/supported-svg.md
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-# Supported SVG
-
-SVG support is intentionally scoped and deterministic.
-
-## Supported usage pattern
-
-- SVG is consumed through image source input (for example ``).
-- `.svg` and `.svgz` extension detection is supported.
-- Content-based SVG detection is also supported (` `f`
-- stroke only -> `S`
-- fill + stroke -> `B`
-
-`stroke-width` is supported (style attribute takes precedence over presentation attribute).
-
-## ViewBox and dimensions
-
-- `viewBox` is supported and takes precedence over `width`/`height`.
-- If no valid `viewBox` is present, positive `width` and `height` are required.
-- Non-positive viewport dimensions are rejected.
-
-## Unsupported or intentionally out-of-scope
-
-- Full SVG text layout support.
-- Filters, masks, gradients, patterns, and full SVG compositing model.
-- Browser-equivalent SVG rendering semantics.
-
-## Error behavior
-
-Common error cases include:
-
-- invalid SVG root/empty payload,
-- malformed path data,
-- unsupported path command,
-- invalid or non-positive viewport definitions.
-
-If this package helps your project generate reliable PDF signature appearances, consider starring the repository, contributing a fixture, or sponsoring maintenance.
diff --git a/docs/use-cases/libresign.md b/docs/use-cases/libresign.md
deleted file mode 100644
index bba7d58..0000000
--- a/docs/use-cases/libresign.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-# LibreSign use case
-
-LibreSign is the flagship real-world use case for this package.
-
-`xobject-template` helps generate deterministic visual signature appearances, stamps, and overlays. LibreSign uses this kind of appearance generation inside a broader digital-signature workflow.
-
-## Scope split
-
-- `xobject-template`: appearance generation (Form XObject-oriented rendering).
-- LibreSign: end-to-end digital-signature workflow and platform integration.
-
-## Official links
-
-- LibreSign website:
-- LibreSign GitHub repository:
-- LibreSign documentation:
-
-If you need a complete digital-signature solution, evaluate LibreSign.
-
-If this package helps your project generate reliable PDF signature appearances, consider starring the repository, contributing a fixture, or sponsoring maintenance.
diff --git a/mkdocs.yml b/mkdocs.yml
deleted file mode 100644
index 8bb7b09..0000000
--- a/mkdocs.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-# SPDX-FileCopyrightText: 2026 LibreSign
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-site_name: xobject-template
-site_description: Minimal HTML+CSS to PDF Form XObject compiler for visible signature appearances
-repo_url: https://github.com/LibreSign/xobject-template
-repo_name: LibreSign/xobject-template
-strict: true
-theme:
- name: material
-plugins:
- - search
-markdown_extensions:
- - admonition
- - tables
- - toc:
- permalink: true
-nav:
- - Home:
- - Overview: index.md
- - Installation: installation.md
- - Getting started: getting-started.md
- - Guides:
- - Basic template: guides/basic-template.md
- - Preview PDF: guides/preview-pdf.md
- - Images: guides/images.md
- - SVG: guides/svg.md
- - Placement and scaling: guides/placement.md
- - Context interpolation: guides/interpolation.md
- - PDF signer integration: guides/signer-integration.md
- - Reference:
- - Public API: reference/public-api.md
- - Output contract: reference/output-contract.md
- - Supported HTML and CSS: reference/supported-html-css.md
- - Supported SVG: reference/supported-svg.md
- - Errors and exceptions: reference/errors.md
- - Limitations: reference/limitations.md
- - Use cases:
- - LibreSign: use-cases/libresign.md
- - Community:
- - Contributing: community/contributing.md
- - Sustainability: community/sustainability.md
- - Roadmap: community/roadmap.md
- - Maintainer notes: community/maintainer-notes.md
diff --git a/requirements-docs.txt b/requirements-docs.txt
deleted file mode 100644
index f4a88ee..0000000
--- a/requirements-docs.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-FileCopyrightText: 2026 LibreSign
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-mkdocs>=1.6,<2
-mkdocs-material>=9.6,<10
diff --git a/scripts/check-docs-drift.php b/scripts/check-docs-drift.php
deleted file mode 100644
index 39d61eb..0000000
--- a/scripts/check-docs-drift.php
+++ /dev/null
@@ -1,113 +0,0 @@
-isFile()) {
- continue;
- }
-
- if (strtolower($docEntry->getExtension()) !== 'md') {
- continue;
- }
-
- $docsFiles[] = $docEntry->getPathname();
-}
-
-$allDocContent = '';
-foreach ($docsFiles as $docFile) {
- $content = file_get_contents($docFile);
- if (is_string($content)) {
- $allDocContent .= "\n" . $content;
- }
-}
-
-preg_match_all('/examples\/[a-z0-9\-]+\.php/', $allDocContent, $exampleMatches);
-$referencedExamples = array_values(array_unique($exampleMatches[0] ?? []));
-foreach ($referencedExamples as $exampleReference) {
- $examplePath = $projectRoot . '/' . $exampleReference;
- if (!is_file($examplePath)) {
- $errors[] = sprintf('Documented example does not exist: %s', $exampleReference);
- }
-}
-
-$examplesTestPath = $projectRoot . '/tests/Documentation/ExamplesTest.php';
-$examplesTest = file_get_contents($examplesTestPath);
-if (!is_string($examplesTest)) {
- $errors[] = 'Could not read tests/Documentation/ExamplesTest.php';
-} else {
- $allExamples = glob($projectRoot . '/examples/*.php');
- if (!is_array($allExamples)) {
- $allExamples = [];
- }
-
- foreach ($allExamples as $exampleFile) {
- $base = basename($exampleFile);
- if (!str_contains($examplesTest, "'" . $base . "'")) {
- $errors[] = sprintf('Example not declared in ExamplesTest::documentedExamples(): %s', $base);
- }
- }
-}
-
-$subsetParserPath = $projectRoot . '/src/Html/SubsetHtmlParser.php';
-$subsetParser = file_get_contents($subsetParserPath);
-$supportedHtmlDoc = file_get_contents($projectRoot . '/docs/reference/supported-html-css.md');
-if (is_string($subsetParser) && is_string($supportedHtmlDoc)) {
- if (preg_match('/private array \$allowedTags\s*=\s*\[(.*?)\];/s', $subsetParser, $allowedTagsMatch) === 1) {
- preg_match_all("/'([a-z]+)'\\s*=>\\s*true/", $allowedTagsMatch[1], $tagMatches);
- $tags = array_values(array_unique($tagMatches[1] ?? []));
- foreach ($tags as $tag) {
- if (!str_contains($supportedHtmlDoc, '<' . $tag . '>')) {
- $errors[] = sprintf('Supported HTML tag missing from docs/reference/supported-html-css.md: <%s>', $tag);
- }
- }
- }
-}
-
-$svgParserPath = $projectRoot . '/src/Pdf/Svg/SvgPathCommandParser.php';
-$svgParser = file_get_contents($svgParserPath);
-$supportedSvgDoc = file_get_contents($projectRoot . '/docs/reference/supported-svg.md');
-if (is_string($svgParser) && is_string($supportedSvgDoc)) {
- preg_match_all("/'([A-Z])'\s*=>/", $svgParser, $cmdMatches);
- $commands = array_values(array_unique($cmdMatches[1] ?? []));
- foreach ($commands as $command) {
- if (!str_contains($supportedSvgDoc, '`' . $command . '`,') && !str_contains($supportedSvgDoc, '`' . $command . '`')) {
- $errors[] = sprintf('SVG path command missing from docs/reference/supported-svg.md: %s', $command);
- }
- }
-}
-
-if ($errors !== []) {
- foreach ($errors as $error) {
- fwrite(STDERR, '- ' . $error . PHP_EOL);
- }
-
- exit(1);
-}
-
-fwrite(STDOUT, "Documentation drift checks passed.\n");
From 1e061a929c2b8290a8d7df80b059df52a38c4f75 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 01:59:19 -0300
Subject: [PATCH 04/31] docs: isolate sources under docs/source and ignore
docs/site outputs
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
.gitignore | 2 ++
docs/README.md | 19 +++++++++++++++++++
docs/source/README.md | 12 ++++++++++++
3 files changed, 33 insertions(+)
create mode 100644 docs/README.md
create mode 100644 docs/source/README.md
diff --git a/.gitignore b/.gitignore
index 29c30da..0d25515 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,8 @@
/vendor-bin/**/composer.lock
/composer.lock
/build/
+/site/
+/docs/site/
/.phpunit.result.cache
/.infection.cache
/.idea/
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..91e4377
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,19 @@
+
+
+
+# Documentation folder layout
+
+This folder is intentionally split to isolate source content from generated output.
+
+## Structure
+
+- `docs/source/`: documentation source files maintained by contributors.
+- `docs/site/`: generated static site output (build artifact, not versioned).
+
+## Rules
+
+- Keep hand-written documentation in `docs/source/`.
+- Never commit generated files under `docs/site/`.
+- Build outputs in root `site/` are also ignored to reduce accidental commits.
+
+If a docs tool is reintroduced in the future, configure it to read from `docs/source/` and write to `docs/site/`.
diff --git a/docs/source/README.md b/docs/source/README.md
new file mode 100644
index 0000000..d014dfd
--- /dev/null
+++ b/docs/source/README.md
@@ -0,0 +1,12 @@
+
+
+
+# Documentation sources
+
+Place documentation source files in this directory.
+
+Suggested future convention:
+
+- one page per topic,
+- short user-focused sections,
+- examples that map to runnable files under `examples/`.
From 34bc1b096644e7773c4ee0b120c8412c37a50a51 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 02:03:36 -0300
Subject: [PATCH 05/31] docs: move examples to docs/source/examples with
updated path references
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
.github/.performance/baseline.json | 16 ++++++++++++++++
.../source/examples}/assets/sample.svg | 0
.../source/examples}/assets/tiny-jpeg.base64 | 0
.../source/examples}/assets/tiny-png.base64 | 0
.../source/examples}/basic-template.php | 4 ++--
{examples => docs/source/examples}/images.php | 6 +++---
.../source/examples}/interpolation.php | 4 ++--
{examples => docs/source/examples}/placement.php | 4 ++--
.../source/examples}/preview-pdf.php | 4 ++--
{examples => docs/source/examples}/svg.php | 6 +++---
tests/Documentation/ExamplesTest.php | 4 ++--
11 files changed, 32 insertions(+), 16 deletions(-)
create mode 100644 .github/.performance/baseline.json
rename {examples => docs/source/examples}/assets/sample.svg (100%)
rename {examples => docs/source/examples}/assets/tiny-jpeg.base64 (100%)
rename {examples => docs/source/examples}/assets/tiny-png.base64 (100%)
rename {examples => docs/source/examples}/basic-template.php (93%)
rename {examples => docs/source/examples}/images.php (93%)
rename {examples => docs/source/examples}/interpolation.php (93%)
rename {examples => docs/source/examples}/placement.php (93%)
rename {examples => docs/source/examples}/preview-pdf.php (92%)
rename {examples => docs/source/examples}/svg.php (90%)
diff --git a/.github/.performance/baseline.json b/.github/.performance/baseline.json
new file mode 100644
index 0000000..68d5a90
--- /dev/null
+++ b/.github/.performance/baseline.json
@@ -0,0 +1,16 @@
+{
+ "version": "1.0.0",
+ "created_at": "2026-06-01",
+ "allowed_regression_pct": 25.0,
+ "stale_threshold_pct": 35.0,
+ "benchmarks": {
+ "LibreSign\\XObjectTemplate\\Benchmarks\\CompilerBench::benchSimpleHtml": {
+ "mean": 0.356085,
+ "memory_real": 768
+ },
+ "LibreSign\\XObjectTemplate\\Benchmarks\\CompilerBench::benchComplexHtml": {
+ "mean": 1.366,
+ "memory_real": 1024
+ }
+ }
+}
diff --git a/examples/assets/sample.svg b/docs/source/examples/assets/sample.svg
similarity index 100%
rename from examples/assets/sample.svg
rename to docs/source/examples/assets/sample.svg
diff --git a/examples/assets/tiny-jpeg.base64 b/docs/source/examples/assets/tiny-jpeg.base64
similarity index 100%
rename from examples/assets/tiny-jpeg.base64
rename to docs/source/examples/assets/tiny-jpeg.base64
diff --git a/examples/assets/tiny-png.base64 b/docs/source/examples/assets/tiny-png.base64
similarity index 100%
rename from examples/assets/tiny-png.base64
rename to docs/source/examples/assets/tiny-png.base64
diff --git a/examples/basic-template.php b/docs/source/examples/basic-template.php
similarity index 93%
rename from examples/basic-template.php
rename to docs/source/examples/basic-template.php
index 057f581..6f19f74 100644
--- a/examples/basic-template.php
+++ b/docs/source/examples/basic-template.php
@@ -8,9 +8,9 @@
use LibreSign\XObjectTemplate\Dto\CompileRequest;
use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
-require dirname(__DIR__) . '/vendor/autoload.php';
+require dirname(__DIR__, 3) . '/vendor/autoload.php';
-$projectRoot = dirname(__DIR__);
+$projectRoot = dirname(__DIR__, 3);
$outputDir = $projectRoot . '/build/examples';
if (!is_dir($outputDir)) {
mkdir($outputDir, 0777, true);
diff --git a/examples/images.php b/docs/source/examples/images.php
similarity index 93%
rename from examples/images.php
rename to docs/source/examples/images.php
index 3933beb..dd7cf12 100644
--- a/examples/images.php
+++ b/docs/source/examples/images.php
@@ -9,10 +9,10 @@
use LibreSign\XObjectTemplate\Pdf\SinglePagePdfExporter;
use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
-require dirname(__DIR__) . '/vendor/autoload.php';
+require dirname(__DIR__, 3) . '/vendor/autoload.php';
-$projectRoot = dirname(__DIR__);
-$assetDir = $projectRoot . '/examples/assets';
+$projectRoot = dirname(__DIR__, 3);
+$assetDir = $projectRoot . '/docs/source/examples/assets';
$outputDir = $projectRoot . '/build/examples';
$outputAssetDir = $outputDir . '/assets';
diff --git a/examples/interpolation.php b/docs/source/examples/interpolation.php
similarity index 93%
rename from examples/interpolation.php
rename to docs/source/examples/interpolation.php
index 09380b7..eef92eb 100644
--- a/examples/interpolation.php
+++ b/docs/source/examples/interpolation.php
@@ -8,9 +8,9 @@
use LibreSign\XObjectTemplate\Dto\CompileRequest;
use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
-require dirname(__DIR__) . '/vendor/autoload.php';
+require dirname(__DIR__, 3) . '/vendor/autoload.php';
-$projectRoot = dirname(__DIR__);
+$projectRoot = dirname(__DIR__, 3);
$outputDir = $projectRoot . '/build/examples';
if (!is_dir($outputDir)) {
mkdir($outputDir, 0777, true);
diff --git a/examples/placement.php b/docs/source/examples/placement.php
similarity index 93%
rename from examples/placement.php
rename to docs/source/examples/placement.php
index 3c9578e..8613911 100644
--- a/examples/placement.php
+++ b/docs/source/examples/placement.php
@@ -9,9 +9,9 @@
use LibreSign\XObjectTemplate\Integration\XObjectPlacementCalculator;
use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
-require dirname(__DIR__) . '/vendor/autoload.php';
+require dirname(__DIR__, 3) . '/vendor/autoload.php';
-$projectRoot = dirname(__DIR__);
+$projectRoot = dirname(__DIR__, 3);
$outputDir = $projectRoot . '/build/examples';
if (!is_dir($outputDir)) {
mkdir($outputDir, 0777, true);
diff --git a/examples/preview-pdf.php b/docs/source/examples/preview-pdf.php
similarity index 92%
rename from examples/preview-pdf.php
rename to docs/source/examples/preview-pdf.php
index 95c3ab1..25f7f17 100644
--- a/examples/preview-pdf.php
+++ b/docs/source/examples/preview-pdf.php
@@ -9,9 +9,9 @@
use LibreSign\XObjectTemplate\Pdf\SinglePagePdfExporter;
use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
-require dirname(__DIR__) . '/vendor/autoload.php';
+require dirname(__DIR__, 3) . '/vendor/autoload.php';
-$projectRoot = dirname(__DIR__);
+$projectRoot = dirname(__DIR__, 3);
$outputDir = $projectRoot . '/build/examples';
if (!is_dir($outputDir)) {
mkdir($outputDir, 0777, true);
diff --git a/examples/svg.php b/docs/source/examples/svg.php
similarity index 90%
rename from examples/svg.php
rename to docs/source/examples/svg.php
index 233d4b5..1801994 100644
--- a/examples/svg.php
+++ b/docs/source/examples/svg.php
@@ -9,11 +9,11 @@
use LibreSign\XObjectTemplate\Pdf\SinglePagePdfExporter;
use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
-require dirname(__DIR__) . '/vendor/autoload.php';
+require dirname(__DIR__, 3) . '/vendor/autoload.php';
-$projectRoot = dirname(__DIR__);
+$projectRoot = dirname(__DIR__, 3);
$outputDir = $projectRoot . '/build/examples';
-$svgPath = $projectRoot . '/examples/assets/sample.svg';
+$svgPath = $projectRoot . '/docs/source/examples/assets/sample.svg';
if (!is_dir($outputDir)) {
mkdir($outputDir, 0777, true);
diff --git a/tests/Documentation/ExamplesTest.php b/tests/Documentation/ExamplesTest.php
index 8a7ad7b..d8b630d 100644
--- a/tests/Documentation/ExamplesTest.php
+++ b/tests/Documentation/ExamplesTest.php
@@ -41,7 +41,7 @@ public static function exampleProvider(): iterable
public function testExampleScriptsExecuteAndGenerateExpectedArtifacts(string $exampleFile): void
{
$projectRoot = dirname(__DIR__, 2);
- $examplePath = $projectRoot . '/examples/' . $exampleFile;
+ $examplePath = $projectRoot . '/docs/source/examples/' . $exampleFile;
self::assertFileExists($examplePath);
$result = require $examplePath;
@@ -87,7 +87,7 @@ public function testExampleScriptsExecuteAndGenerateExpectedArtifacts(string $ex
public function testEveryPhpExampleFileIsCoveredByTheProvider(): void
{
$projectRoot = dirname(__DIR__, 2);
- $allPhpExamples = glob($projectRoot . '/examples/*.php');
+ $allPhpExamples = glob($projectRoot . '/docs/source/examples/*.php');
self::assertIsArray($allPhpExamples);
$actual = array_map(static fn (string $path): string => basename($path), $allPhpExamples);
From 8bfdcbe7f7bed6e316b569d135e6cba577dbe359 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:32:37 -0300
Subject: [PATCH 06/31] chore: remove unecessary template
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
.github/ISSUE_TEMPLATE/commercial_support.yml | 36 -------------------
1 file changed, 36 deletions(-)
delete mode 100644 .github/ISSUE_TEMPLATE/commercial_support.yml
diff --git a/.github/ISSUE_TEMPLATE/commercial_support.yml b/.github/ISSUE_TEMPLATE/commercial_support.yml
deleted file mode 100644
index 32ea6b9..0000000
--- a/.github/ISSUE_TEMPLATE/commercial_support.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-# SPDX-FileCopyrightText: 2026 LibreSign
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-name: Commercial support
-description: Ask about paid support or sponsored prioritization
-labels: [support]
-body:
- - type: markdown
- attributes:
- value: |
- Thanks for your interest in commercial support.
-
- This template does not guarantee free consulting. Sponsored or paid work can help prioritize compatibility, features, and long-term maintenance.
-
- Current public funding channel: https://github.com/sponsors/LibreSign
- - type: textarea
- id: context
- attributes:
- label: Project context
- description: Describe your use case, expected timeline, and technical scope.
- validations:
- required: true
- - type: textarea
- id: requested_scope
- attributes:
- label: Requested support scope
- description: Include expected deliverables (for example SVG compatibility work, integration support, QA hardening).
- validations:
- required: true
- - type: input
- id: contact
- attributes:
- label: Preferred contact method
- placeholder: email or issue-based follow-up
- validations:
- required: true
From f06ecb02c3370f6635f132c9b91ff3e73b053ad1 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:40:45 -0300
Subject: [PATCH 07/31] docs: restore minimal public documentation pages
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/README.md | 4 +-
docs/index.md | 49 +++++++++++++++++++++
docs/source/README.md | 15 ++++++-
docs/source/guides/getting-started.md | 45 +++++++++++++++++++
docs/source/guides/index.md | 6 +++
docs/source/index.md | 43 ++++++++++++++++++
docs/source/reference/examples.md | 30 +++++++++++++
docs/source/reference/index.md | 6 +++
docs/source/use-cases/index.md | 6 +++
docs/source/use-cases/visible-signatures.md | 27 ++++++++++++
10 files changed, 228 insertions(+), 3 deletions(-)
create mode 100644 docs/index.md
create mode 100644 docs/source/guides/getting-started.md
create mode 100644 docs/source/guides/index.md
create mode 100644 docs/source/index.md
create mode 100644 docs/source/reference/examples.md
create mode 100644 docs/source/reference/index.md
create mode 100644 docs/source/use-cases/index.md
create mode 100644 docs/source/use-cases/visible-signatures.md
diff --git a/docs/README.md b/docs/README.md
index 91e4377..6083d98 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -7,9 +7,11 @@ This folder is intentionally split to isolate source content from generated outp
## Structure
-- `docs/source/`: documentation source files maintained by contributors.
+- `docs/source/`: documentation source files maintained by contributors and intended for publication.
- `docs/site/`: generated static site output (build artifact, not versioned).
+The public entry page is `docs/index.md`.
+
## Rules
- Keep hand-written documentation in `docs/source/`.
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..6021511
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,49 @@
+
+
+
+# xobject-template documentation
+
+This is the public documentation landing page for the repository.
+The full hand-written source lives under `docs/source/`.
+
+## What this library does
+
+`xobject-template` compiles a constrained HTML/CSS subset into reusable PDF Form XObject output.
+It is designed for predictable signature appearances, labels, stamps, overlays, and other compact PDF decorations.
+
+## Start here
+
+- [Getting started](source/guides/)
+- [Examples](source/reference/)
+- [Visible signature use case](source/use-cases/)
+
+## Highlights
+
+- deterministic rendering for PDF workflows,
+- a focused PHP API,
+- runnable examples covered by tests,
+- a small documentation surface that is easier to maintain.
+
+## Repository layout
+
+- `src/` — library code
+- `tests/` — unit, integration, and documentation tests
+- `docs/` — public documentation pages
+- `docs/source/` — source pages and example files
+
+## Quick example
+
+```php
+use LibreSign\XObjectTemplate\Dto\CompileRequest;
+use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
+
+$compiler = new XObjectTemplateCompiler();
+$result = $compiler->compile(new CompileRequest(
+ html: '
Signed by {{ name }}
',
+ width: 240.0,
+ height: 84.0,
+ context: ['name' => 'Alice'],
+));
+```
+
+For executable samples, see the [examples page](source/reference/).
diff --git a/docs/source/README.md b/docs/source/README.md
index d014dfd..d20190b 100644
--- a/docs/source/README.md
+++ b/docs/source/README.md
@@ -3,10 +3,21 @@
# Documentation sources
-Place documentation source files in this directory.
+This directory contains the hand-written documentation pages that are meant to be published.
-Suggested future convention:
+## Entry point
+
+- `index.md` — documentation home
+
+## Suggested structure
- one page per topic,
- short user-focused sections,
- examples that map to runnable files under `examples/`.
+
+## Published pages
+
+- `guides/getting-started.md`
+- `reference/examples.md`
+- `use-cases/visible-signatures.md`
+- `examples/` — runnable example sources and fixtures
diff --git a/docs/source/guides/getting-started.md b/docs/source/guides/getting-started.md
new file mode 100644
index 0000000..fe47d2f
--- /dev/null
+++ b/docs/source/guides/getting-started.md
@@ -0,0 +1,45 @@
+
+
+
+# Getting started
+
+## Install
+
+```bash
+composer require libresign/xobject-template
+```
+
+## Render a template
+
+```php
+compile(new CompileRequest(
+ html: '
Signed by {{ name }}
',
+ width: 240.0,
+ height: 84.0,
+ context: ['name' => 'Alice'],
+));
+
+// $result->contentStream
+// $result->resources
+// $result->bbox
+```
+
+## Validate the examples
+
+Run the documentation test suite to execute every example file and verify the generated artifacts:
+
+```bash
+composer run examples:test
+```
+
+## Where to look next
+
+- [Examples](../reference/examples.md)
+- [Visible signature use case](../use-cases/visible-signatures.md)
diff --git a/docs/source/guides/index.md b/docs/source/guides/index.md
new file mode 100644
index 0000000..06410a6
--- /dev/null
+++ b/docs/source/guides/index.md
@@ -0,0 +1,6 @@
+
+
+
+# Guides
+
+- [Getting started](getting-started.md)
diff --git a/docs/source/index.md b/docs/source/index.md
new file mode 100644
index 0000000..db167e5
--- /dev/null
+++ b/docs/source/index.md
@@ -0,0 +1,43 @@
+
+
+
+# xobject-template documentation
+
+`xobject-template` turns a constrained HTML/CSS subset into reusable PDF Form XObject output for signatures, stamps, labels, and overlays.
+
+## Start here
+
+- [Getting started](guides/)
+- [Examples](reference/)
+- [Visible signature use case](use-cases/)
+
+## What you get
+
+- deterministic rendering for PDF workflows,
+- a focused PHP API,
+- runnable examples that are covered by tests,
+- a small documentation footprint that is easier to maintain.
+
+## Repository layout
+
+- `src/` — library code
+- `tests/` — unit, integration, and documentation tests
+- `docs/source/` — documentation pages intended for publication
+- `docs/source/examples/` — executable examples with fixtures
+
+## Quick preview
+
+```php
+use LibreSign\XObjectTemplate\Dto\CompileRequest;
+use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
+
+$compiler = new XObjectTemplateCompiler();
+$result = $compiler->compile(new CompileRequest(
+ html: '
Signed by {{ name }}
',
+ width: 240.0,
+ height: 84.0,
+ context: ['name' => 'Alice'],
+));
+```
+
+For executable samples, see the [examples page](reference/).
diff --git a/docs/source/reference/examples.md b/docs/source/reference/examples.md
new file mode 100644
index 0000000..9bc4af1
--- /dev/null
+++ b/docs/source/reference/examples.md
@@ -0,0 +1,30 @@
+
+
+
+# Examples
+
+The examples are executable PHP files under `docs/source/examples/`.
+Each one is covered by `tests/Documentation/ExamplesTest.php`.
+
+## Included examples
+
+- `basic-template.php` — compile a simple template with a context variable
+- `preview-pdf.php` — export a single-page PDF preview
+- `images.php` — embed PNG and JPEG assets from base64 fixtures
+- `svg.php` — render SVG content as an image source
+- `placement.php` — demonstrate placement calculations
+- `interpolation.php` — interpolate context variables in text
+
+## How to run them
+
+```bash
+composer run examples:test
+```
+
+## Output files
+
+The examples write their generated artifacts to `build/examples/` so the repository stays clean and the outputs remain disposable.
+
+## Assets
+
+Supporting fixtures live in `docs/source/examples/assets/`.
diff --git a/docs/source/reference/index.md b/docs/source/reference/index.md
new file mode 100644
index 0000000..b5deb52
--- /dev/null
+++ b/docs/source/reference/index.md
@@ -0,0 +1,6 @@
+
+
+
+# Reference
+
+- [Examples](examples.md)
diff --git a/docs/source/use-cases/index.md b/docs/source/use-cases/index.md
new file mode 100644
index 0000000..7b52953
--- /dev/null
+++ b/docs/source/use-cases/index.md
@@ -0,0 +1,6 @@
+
+
+
+# Use cases
+
+- [Visible signature appearances](visible-signatures.md)
diff --git a/docs/source/use-cases/visible-signatures.md b/docs/source/use-cases/visible-signatures.md
new file mode 100644
index 0000000..d23b425
--- /dev/null
+++ b/docs/source/use-cases/visible-signatures.md
@@ -0,0 +1,27 @@
+
+
+
+# Visible signature appearances
+
+This library exists to support cases where a PDF needs a predictable visible appearance:
+
+- signature labels,
+- approval stamps,
+- overlay annotations,
+- status blocks,
+- other compact PDF decorations.
+
+## Why this matters
+
+PDF pipelines usually need stable layout more than browser-grade rendering. `xobject-template` keeps the surface area small so the output is easier to test and reason about.
+
+## Typical flow
+
+1. Build a `CompileRequest`.
+2. Pass HTML, dimensions, and context to `XObjectTemplateCompiler`.
+3. Consume the generated `contentStream`, `resources`, and `bbox` in downstream PDF tooling.
+
+## Related pages
+
+- [Getting started](../guides/getting-started.md)
+- [Examples](../reference/examples.md)
From 39f9b0807796eebf494705a7ccfbe1d60850f906 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:46:13 -0300
Subject: [PATCH 08/31] docs: simplify documentation landing pages
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/index.md | 17 ++++-------------
docs/source/index.md | 13 ++++---------
2 files changed, 8 insertions(+), 22 deletions(-)
diff --git a/docs/index.md b/docs/index.md
index 6021511..d094503 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -3,13 +3,11 @@
# xobject-template documentation
-This is the public documentation landing page for the repository.
-The full hand-written source lives under `docs/source/`.
+Public docs for `xobject-template`.
-## What this library does
+Source pages live in `docs/source/`.
-`xobject-template` compiles a constrained HTML/CSS subset into reusable PDF Form XObject output.
-It is designed for predictable signature appearances, labels, stamps, overlays, and other compact PDF decorations.
+`xobject-template` compiles a constrained HTML/CSS subset into PDF Form XObject output.
## Start here
@@ -17,19 +15,12 @@ It is designed for predictable signature appearances, labels, stamps, overlays,
- [Examples](source/reference/)
- [Visible signature use case](source/use-cases/)
-## Highlights
-
-- deterministic rendering for PDF workflows,
-- a focused PHP API,
-- runnable examples covered by tests,
-- a small documentation surface that is easier to maintain.
-
## Repository layout
- `src/` — library code
- `tests/` — unit, integration, and documentation tests
- `docs/` — public documentation pages
-- `docs/source/` — source pages and example files
+- `docs/source/` — editable source pages and examples
## Quick example
diff --git a/docs/source/index.md b/docs/source/index.md
index db167e5..4982af8 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -3,7 +3,9 @@
# xobject-template documentation
-`xobject-template` turns a constrained HTML/CSS subset into reusable PDF Form XObject output for signatures, stamps, labels, and overlays.
+Editable source for the public docs.
+
+`xobject-template` compiles a constrained HTML/CSS subset into PDF Form XObject output.
## Start here
@@ -11,13 +13,6 @@
- [Examples](reference/)
- [Visible signature use case](use-cases/)
-## What you get
-
-- deterministic rendering for PDF workflows,
-- a focused PHP API,
-- runnable examples that are covered by tests,
-- a small documentation footprint that is easier to maintain.
-
## Repository layout
- `src/` — library code
@@ -25,7 +20,7 @@
- `docs/source/` — documentation pages intended for publication
- `docs/source/examples/` — executable examples with fixtures
-## Quick preview
+## Quick example
```php
use LibreSign\XObjectTemplate\Dto\CompileRequest;
From c3f1666f273d5c579ea4e66094f488badd6fe73f Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:47:30 -0300
Subject: [PATCH 09/31] docs: clarify documentation landing pages
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/index.md | 4 ++--
docs/source/index.md | 4 +++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/docs/index.md b/docs/index.md
index d094503..88aa00e 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -3,9 +3,9 @@
# xobject-template documentation
-Public docs for `xobject-template`.
+This is the front door for the public docs site.
-Source pages live in `docs/source/`.
+If you're a developer trying to use `xobject-template`, start with the install guide, then open an example, then check a real use case.
`xobject-template` compiles a constrained HTML/CSS subset into PDF Form XObject output.
diff --git a/docs/source/index.md b/docs/source/index.md
index 4982af8..8d1344f 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -3,7 +3,9 @@
# xobject-template documentation
-Editable source for the public docs.
+Editable source for the public docs site.
+
+Use this folder when you want to update the pages that get published.
`xobject-template` compiles a constrained HTML/CSS subset into PDF Form XObject output.
From 14367f41cc8d9717cc98e6f2aa3fef44cdb177d8 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:49:09 -0300
Subject: [PATCH 10/31] docs: remove architecture-heavy landing page text
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/README.md | 15 ---------------
docs/index.md | 11 +----------
docs/source/index.md | 11 +----------
3 files changed, 2 insertions(+), 35 deletions(-)
diff --git a/docs/README.md b/docs/README.md
index 6083d98..9179016 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -4,18 +4,3 @@
# Documentation folder layout
This folder is intentionally split to isolate source content from generated output.
-
-## Structure
-
-- `docs/source/`: documentation source files maintained by contributors and intended for publication.
-- `docs/site/`: generated static site output (build artifact, not versioned).
-
-The public entry page is `docs/index.md`.
-
-## Rules
-
-- Keep hand-written documentation in `docs/source/`.
-- Never commit generated files under `docs/site/`.
-- Build outputs in root `site/` are also ignored to reduce accidental commits.
-
-If a docs tool is reintroduced in the future, configure it to read from `docs/source/` and write to `docs/site/`.
diff --git a/docs/index.md b/docs/index.md
index 88aa00e..7b73376 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -3,9 +3,7 @@
# xobject-template documentation
-This is the front door for the public docs site.
-
-If you're a developer trying to use `xobject-template`, start with the install guide, then open an example, then check a real use case.
+Read the docs.
`xobject-template` compiles a constrained HTML/CSS subset into PDF Form XObject output.
@@ -15,13 +13,6 @@ If you're a developer trying to use `xobject-template`, start with the install g
- [Examples](source/reference/)
- [Visible signature use case](source/use-cases/)
-## Repository layout
-
-- `src/` — library code
-- `tests/` — unit, integration, and documentation tests
-- `docs/` — public documentation pages
-- `docs/source/` — editable source pages and examples
-
## Quick example
```php
diff --git a/docs/source/index.md b/docs/source/index.md
index 8d1344f..61eebf8 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -3,9 +3,7 @@
# xobject-template documentation
-Editable source for the public docs site.
-
-Use this folder when you want to update the pages that get published.
+Docs content.
`xobject-template` compiles a constrained HTML/CSS subset into PDF Form XObject output.
@@ -15,13 +13,6 @@ Use this folder when you want to update the pages that get published.
- [Examples](reference/)
- [Visible signature use case](use-cases/)
-## Repository layout
-
-- `src/` — library code
-- `tests/` — unit, integration, and documentation tests
-- `docs/source/` — documentation pages intended for publication
-- `docs/source/examples/` — executable examples with fixtures
-
## Quick example
```php
From c09655fda5054372d8c3ae176b14a54c136e7122 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:49:47 -0300
Subject: [PATCH 11/31] docs: trim examples reference page
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/source/reference/examples.md | 4 ----
1 file changed, 4 deletions(-)
diff --git a/docs/source/reference/examples.md b/docs/source/reference/examples.md
index 9bc4af1..4740a7b 100644
--- a/docs/source/reference/examples.md
+++ b/docs/source/reference/examples.md
@@ -24,7 +24,3 @@ composer run examples:test
## Output files
The examples write their generated artifacts to `build/examples/` so the repository stays clean and the outputs remain disposable.
-
-## Assets
-
-Supporting fixtures live in `docs/source/examples/assets/`.
From 5e95e93089ac23eb73587ae037a5b7688a828f63 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:50:19 -0300
Subject: [PATCH 12/31] docs: simplify public docs summary
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/index.md b/docs/index.md
index 7b73376..50df52d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -5,7 +5,7 @@
Read the docs.
-`xobject-template` compiles a constrained HTML/CSS subset into PDF Form XObject output.
+`xobject-template` compiles a HTML/CSS template into PDF Form XObject output.
## Start here
From fbbedae9c88400167f4d00ba3e2c1f144f815949 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:52:39 -0300
Subject: [PATCH 13/31] docs: remove redundant reference index page
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/index.md | 6 ++----
docs/source/README.md | 15 +--------------
docs/source/index.md | 4 ++--
docs/source/reference/index.md | 6 ------
4 files changed, 5 insertions(+), 26 deletions(-)
delete mode 100644 docs/source/reference/index.md
diff --git a/docs/index.md b/docs/index.md
index 50df52d..9bd2354 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -3,14 +3,12 @@
# xobject-template documentation
-Read the docs.
-
`xobject-template` compiles a HTML/CSS template into PDF Form XObject output.
## Start here
- [Getting started](source/guides/)
-- [Examples](source/reference/)
+- [Examples](source/reference/examples.md)
- [Visible signature use case](source/use-cases/)
## Quick example
@@ -28,4 +26,4 @@ $result = $compiler->compile(new CompileRequest(
));
```
-For executable samples, see the [examples page](source/reference/).
+For executable samples, see the [examples page](source/reference/examples.md).
diff --git a/docs/source/README.md b/docs/source/README.md
index d20190b..ae3be7b 100644
--- a/docs/source/README.md
+++ b/docs/source/README.md
@@ -3,21 +3,8 @@
# Documentation sources
-This directory contains the hand-written documentation pages that are meant to be published.
+Hand-written docs live here.
## Entry point
- `index.md` — documentation home
-
-## Suggested structure
-
-- one page per topic,
-- short user-focused sections,
-- examples that map to runnable files under `examples/`.
-
-## Published pages
-
-- `guides/getting-started.md`
-- `reference/examples.md`
-- `use-cases/visible-signatures.md`
-- `examples/` — runnable example sources and fixtures
diff --git a/docs/source/index.md b/docs/source/index.md
index 61eebf8..c7da7b4 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -10,7 +10,7 @@ Docs content.
## Start here
- [Getting started](guides/)
-- [Examples](reference/)
+- [Examples](reference/examples.md)
- [Visible signature use case](use-cases/)
## Quick example
@@ -28,4 +28,4 @@ $result = $compiler->compile(new CompileRequest(
));
```
-For executable samples, see the [examples page](reference/).
+For executable samples, see the [examples page](reference/examples.md).
diff --git a/docs/source/reference/index.md b/docs/source/reference/index.md
deleted file mode 100644
index b5deb52..0000000
--- a/docs/source/reference/index.md
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-# Reference
-
-- [Examples](examples.md)
From 2649c765bb7f62acc63a1e1f9b88c51bc5543379 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:53:24 -0300
Subject: [PATCH 14/31] docs: remove redundant use-cases index page
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/index.md | 2 +-
docs/source/index.md | 2 +-
docs/source/use-cases/index.md | 6 ------
3 files changed, 2 insertions(+), 8 deletions(-)
delete mode 100644 docs/source/use-cases/index.md
diff --git a/docs/index.md b/docs/index.md
index 9bd2354..0a98899 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -9,7 +9,7 @@
- [Getting started](source/guides/)
- [Examples](source/reference/examples.md)
-- [Visible signature use case](source/use-cases/)
+- [Visible signature use case](source/use-cases/visible-signatures.md)
## Quick example
diff --git a/docs/source/index.md b/docs/source/index.md
index c7da7b4..508e456 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -11,7 +11,7 @@ Docs content.
- [Getting started](guides/)
- [Examples](reference/examples.md)
-- [Visible signature use case](use-cases/)
+- [Visible signature use case](use-cases/visible-signatures.md)
## Quick example
diff --git a/docs/source/use-cases/index.md b/docs/source/use-cases/index.md
deleted file mode 100644
index 7b52953..0000000
--- a/docs/source/use-cases/index.md
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-# Use cases
-
-- [Visible signature appearances](visible-signatures.md)
From 8650dae4d06155eaaf68ba898be3351df8f2f2f8 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 12:07:03 -0300
Subject: [PATCH 15/31] docs: remove redundant guides index page
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/index.md | 2 +-
docs/source/guides/index.md | 6 ------
docs/source/index.md | 2 +-
3 files changed, 2 insertions(+), 8 deletions(-)
delete mode 100644 docs/source/guides/index.md
diff --git a/docs/index.md b/docs/index.md
index 0a98899..1195ead 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -7,7 +7,7 @@
## Start here
-- [Getting started](source/guides/)
+- [Getting started](source/guides/getting-started.md)
- [Examples](source/reference/examples.md)
- [Visible signature use case](source/use-cases/visible-signatures.md)
diff --git a/docs/source/guides/index.md b/docs/source/guides/index.md
deleted file mode 100644
index 06410a6..0000000
--- a/docs/source/guides/index.md
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-# Guides
-
-- [Getting started](getting-started.md)
diff --git a/docs/source/index.md b/docs/source/index.md
index 508e456..e456b25 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -9,7 +9,7 @@ Docs content.
## Start here
-- [Getting started](guides/)
+- [Getting started](guides/getting-started.md)
- [Examples](reference/examples.md)
- [Visible signature use case](use-cases/visible-signatures.md)
From 4c0ae06188421b261412e63e8e12e9f57a9c53df Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Mon, 1 Jun 2026 12:08:49 -0300
Subject: [PATCH 16/31] docs: remove duplicate source/index.md
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
docs/source/index.md | 31 -------------------------------
1 file changed, 31 deletions(-)
delete mode 100644 docs/source/index.md
diff --git a/docs/source/index.md b/docs/source/index.md
deleted file mode 100644
index e456b25..0000000
--- a/docs/source/index.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-# xobject-template documentation
-
-Docs content.
-
-`xobject-template` compiles a constrained HTML/CSS subset into PDF Form XObject output.
-
-## Start here
-
-- [Getting started](guides/getting-started.md)
-- [Examples](reference/examples.md)
-- [Visible signature use case](use-cases/visible-signatures.md)
-
-## Quick example
-
-```php
-use LibreSign\XObjectTemplate\Dto\CompileRequest;
-use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
-
-$compiler = new XObjectTemplateCompiler();
-$result = $compiler->compile(new CompileRequest(
- html: '