Skip to content

Commit b616fc7

Browse files
committed
refactor: generalize XObject API and simplify CI
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent 8ef921e commit b616fc7

17 files changed

Lines changed: 143 additions & 102 deletions

.github/workflows/coverage.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ name: coverage
66
on:
77
pull_request:
88

9+
env:
10+
COVERAGE_THRESHOLDS: '95 95'
11+
912
jobs:
1013
coverage:
1114
runs-on: ubuntu-latest
@@ -23,4 +26,27 @@ jobs:
2326
coverage: xdebug
2427
- run: composer install --no-interaction --prefer-dist
2528
- run: composer bin phpunit install --no-interaction --prefer-dist
26-
- run: composer run test:coverage
29+
- name: Generate coverage reports
30+
run: XDEBUG_MODE=coverage vendor-bin/phpunit/vendor/phpunit/phpunit/phpunit --coverage-text --coverage-clover=build/coverage/clover.xml --coverage-cobertura=build/coverage/cobertura.xml --coverage-html=build/coverage/html
31+
- name: Store temporary coverage cache
32+
uses: actions/cache/save@v4
33+
with:
34+
path: build/coverage
35+
key: coverage-${{ github.event.pull_request.number }}-${{ github.sha }}
36+
- name: Upload temporary coverage artifact
37+
uses: actions/upload-artifact@v4
38+
with:
39+
name: coverage-report-${{ github.event.pull_request.number }}-${{ github.sha }}
40+
path: build/coverage
41+
retention-days: 7
42+
- name: Validate minimum line coverage
43+
uses: irongut/CodeCoverageSummary@v1.3.0
44+
with:
45+
filename: build/coverage/cobertura.xml
46+
fail_below_min: true
47+
format: text
48+
hide_branch_rate: false
49+
hide_complexity: true
50+
indicators: true
51+
output: console
52+
thresholds: ${{ env.COVERAGE_THRESHOLDS }}

.github/workflows/licenses-compliance.yml

Lines changed: 0 additions & 25 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ Run key checks before opening/updating PR:
2222
- `composer run test:unit`
2323
- `composer run test:coverage`
2424
- `composer run deps:audit`
25+
26+
## Coverage policy
27+
28+
- CI generates Clover XML and HTML reports under `build/coverage/`
29+
- Pull requests must keep **minimum line coverage at 95%**
30+
- Coverage reports are stored temporarily in GitHub Actions ephemeral storage for post-PR analysis

README.md

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,71 @@
33

44
# xobject-template
55

6-
> Compile a minimal HTML+CSS subset into reusable PDF Form XObject templates for visible signatures.
6+
> Compile a minimal HTML+CSS subset into reusable PDF Form XObject payloads.
77
8-
`xobject-template` is a focused rendering engine for digital-signature ecosystems that need **beautiful, vector-first, stable** appearance templates with predictable performance.
8+
`xobject-template` is a focused rendering engine for projects that need **beautiful, vector-first, stable** reusable overlays inside PDF workflows.
99

1010
## Why this package
1111

12-
- Product-ready visible signature templates (text + image) as real XObject payloads
13-
- Clean integration path for `pdf-signer-php` and LibreSign-like platforms
12+
- Reusable XObject payloads for labels, stamps, approvals, and other document overlays
13+
- Clean integration path for any PDF pipeline that can embed Form XObjects
1414
- Lean API designed for long-term compatibility and monetizable maintainability
1515

1616
## Quick integration
1717

18-
Use the compiler to generate `content stream`, `resources`, and `bbox`, then map the output to your signature appearance DTO adapter.
18+
Use the compiler to generate a reusable XObject result. Consumers that prefer arrays over DTOs can adapt the result into a generic payload shape.
19+
20+
```php
21+
use LibreSign\XObjectTemplate\Dto\CompileRequest;
22+
use LibreSign\XObjectTemplate\Integration\XObjectPayloadAdapter;
23+
use LibreSign\XObjectTemplate\XObjectTemplateCompiler;
24+
25+
$compiler = new XObjectTemplateCompiler();
26+
27+
$result = $compiler->compile(new CompileRequest(
28+
html: '<div style="font-size:10px;color:#111111">Rendered for Alice</div>'
29+
. '<img src="/tmp/example-image.png" style="width:24px;height:24px" />',
30+
width: 240.0,
31+
height: 84.0,
32+
));
33+
34+
$payload = (new XObjectPayloadAdapter())->toXObjectPayload($result);
35+
```
36+
37+
### Output contract
38+
39+
- `$result->contentStream`: PDF operators ready for a Form XObject stream
40+
- `$result->resources`: font/image resource dictionary keyed for PDF serialization
41+
- `$result->bbox`: bounding box as `[x1, y1, x2, y2]`
42+
- `$result->metadata`: render diagnostics such as `line_count`, `image_count`, `node_count`, and `render_ms`
43+
- `$payload`: transport-agnostic array with `stream`, `resources`, and `bbox`
44+
45+
## Supported HTML/CSS subset
46+
47+
### HTML
48+
49+
- Supported elements: `<div>`, `<p>`, `<span>`, `<br>`, `<img>`
50+
- Text fragments are normalized into inline text nodes internally
51+
- Inline styles are read from the `style` attribute
52+
- Images use the `src` attribute as the source reference included in the resource dictionary
53+
54+
### CSS used by the renderer
55+
56+
- Typography: `font-size`, `font-family`, `font-weight`, `line-height`, `color`
57+
- Layout: `margin`, `padding`, `text-align`, `width`, `height`
58+
- Numeric values can be provided as unitless numbers or `px`
59+
- `px` values are converted to PDF points using the package conversion rules
60+
- Unknown or incomplete CSS declarations are ignored instead of aborting the render
61+
62+
### Rendering notes
63+
64+
- Font family mapping currently targets the built-in Helvetica, Times, and Courier aliases used by the generated PDF resources
65+
- `img` width/height fall back to `32x32` when omitted or invalid
66+
- Image and text placement are clamped to the requested output box
67+
- The compiler output is not tied to any single downstream package; any consumer that understands Form XObject stream/resources/bbox data can use it
68+
69+
## Failure modes
70+
71+
- Unsupported HTML elements raise `UnsupportedSubsetException`
72+
- Malformed HTML fragments are normalized by `DOMDocument` before traversal
73+
- Empty text nodes and invalid inline-style fragments are ignored during parsing/rendering

composer.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
{
22
"name": "libresign/xobject-template",
3-
"description": "Minimal HTML+CSS to PDF Form XObject compiler for visible signature appearance.",
3+
"description": "Minimal HTML+CSS to reusable PDF Form XObject compiler.",
44
"type": "library",
55
"license": "AGPL-3.0-or-later",
66
"keywords": [
77
"pdf",
88
"xobject",
9-
"signature",
9+
"template",
10+
"rendering",
11+
"layout",
1012
"html",
1113
"css"
1214
],
@@ -59,7 +61,6 @@
5961
"composer:validate": "composer validate --strict",
6062
"composer:normalize:check": "vendor-bin/qa/vendor/ergebnis/composer-normalize/bin/composer-normalize --dry-run",
6163
"bc:check": "vendor-bin/qa/vendor/roave/backward-compatibility-check/bin/roave-backward-compatibility-check --from=origin/main --to=HEAD",
62-
"licenses:check": "php scripts/licenses-check.php",
6364
"changelog:check": "echo 'Handled by GitHub Action: .github/workflows/changelog.yml'",
6465
"release:draft": "echo 'Handled by GitHub Action: .github/workflows/release-draft.yml'",
6566
"deps:audit": "composer audit",

scripts/licenses-check.php

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/Contract/XObjectTemplateCompilerInterface.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@
99

1010
use LibreSign\XObjectTemplate\Dto\CompileRequest;
1111
use LibreSign\XObjectTemplate\Dto\CompileResult;
12+
use LibreSign\XObjectTemplate\Exception\UnsupportedSubsetException;
1213

1314
interface XObjectTemplateCompilerInterface
1415
{
16+
/**
17+
* Compile the supported HTML+CSS subset into a reusable PDF Form XObject payload.
18+
*
19+
* @throws UnsupportedSubsetException If the HTML fragment contains an unsupported element.
20+
*/
1521
public function compile(CompileRequest $request): CompileResult;
1622
}

src/Html/SubsetHtmlParser.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ final class SubsetHtmlParser
2828

2929
/**
3030
* @return list<Node>
31+
*
32+
* @throws UnsupportedSubsetException If the HTML fragment contains an element outside the supported subset.
3133
*/
3234
public function parse(string $html): array
3335
{

src/Integration/SignatureAppearanceXObjectAdapter.php renamed to src/Integration/XObjectPayloadAdapter.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99

1010
use LibreSign\XObjectTemplate\Dto\CompileResult;
1111

12-
final class SignatureAppearanceXObjectAdapter
12+
final class XObjectPayloadAdapter
1313
{
1414
/**
15-
* Output compatible with consumers expecting stream/resources pair.
15+
* Output compatible with consumers expecting a generic Form XObject payload.
1616
*
1717
* @return array{
1818
* stream: string,
1919
* resources: array<string, mixed>,
2020
* bbox: array{0: float, 1: float, 2: float, 3: float}
2121
* }
2222
*/
23-
public function toPdfSignerPayload(CompileResult $result): array
23+
public function toXObjectPayload(CompileResult $result): array
2424
{
2525
return [
2626
'stream' => $result->contentStream,

src/XObjectTemplateCompiler.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use LibreSign\XObjectTemplate\Contract\XObjectTemplateCompilerInterface;
1111
use LibreSign\XObjectTemplate\Dto\CompileRequest;
1212
use LibreSign\XObjectTemplate\Dto\CompileResult;
13+
use LibreSign\XObjectTemplate\Exception\UnsupportedSubsetException;
1314
use LibreSign\XObjectTemplate\Html\SubsetHtmlParser;
1415
use LibreSign\XObjectTemplate\Layout\LinearLayoutEngine;
1516
use LibreSign\XObjectTemplate\Pdf\ColorParser;
@@ -37,6 +38,11 @@ public function __construct(
3738
);
3839
}
3940

41+
/**
42+
* Compile the supported HTML+CSS subset into a reusable PDF Form XObject payload.
43+
*
44+
* @throws UnsupportedSubsetException If the HTML fragment contains an unsupported element.
45+
*/
4046
public function compile(CompileRequest $request): CompileResult
4147
{
4248
$start = hrtime(true);

0 commit comments

Comments
 (0)