Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pdf Signable Bundle Laravel Package

nowo-tech/pdf-signable-bundle

Symfony bundle for adding digital signature support to PDF documents, enabling signing, verifying, and managing signed PDFs within your application. Simplifies PDF signing workflows and integrates with existing Symfony services.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require nowo-tech/pdf-signable-bundle

Flex may register it automatically; otherwise add it to config/bundles.php. There is no documented config file contract in the package metadata, so before writing any YAML, open src/DependencyInjection/Configuration.php and the README — the config tree is the authoritative list of options, not blog posts.

Publish the assets first — this is step zero

The bundle's value is a PDF.js viewer plus drag-and-drop boxes, which means JavaScript and a worker file must be reachable from the browser. Do this before you touch a form, otherwise you will spend an hour debugging "nothing renders":

php bin/console assets:install --symlink

If you use AssetMapper or Webpack Encore, confirm how the bundle expects its entrypoint to be loaded (check src/Resources/assets for the source and src/Resources/public/build for what actually ships). Treat public/build as opaque output — never patch it.

Where to look first, in order

  1. README.md — the only place with a working end-to-end example.
  2. src/Form/ — the form type you embed. Its configureOptions tells you what the field needs (PDF source, viewer options, whether the AcroForm editor path is enabled).
  3. src/Resources/views/ — the Twig template and the data attributes that boot the JS.
  4. src/Resources/assets/ — the TypeScript that wires PDF.js to the box overlay; this is where coordinate conversion lives.
  5. composer.json — PHP/Symfony constraints. The bundle targets PHP >= 8.2 and Symfony 7/8; do not try it on a Symfony 5.4 app.

First use case to build

A back-office form: upload a PDF, render it in the viewer, drag one signature box onto page 1, submit, and persist the box as JSON on an entity. Nothing more. Do not start with multi-signer workflows or AcroForm editing — you need to prove the asset pipeline, the form round-trip, and the coordinate round-trip separately, and they fail in different ways.

Implementation Patterns

Embed the field in your own form, don't build a page around it

The bundle gives you a form type, not a controller or a full UI. The idiomatic integration is to add the field to an existing Symfony form and let your own controller handle persistence:

$builder->add('signatureLayout', PdfSignableType::class, [
    'pdf' => $document->getPdfPath(),
    // viewer/editor options — read Configuration.php for exact keys
]);

Keeping the field embedded means you inherit your app's CSRF, validation, and security rather than fighting a bundle-provided controller.

Persist coordinates normalized, render absolute

Store each box as {page, x, y, w, h} in normalized 0–1 space relative to the page's visible box, plus the page rotation. Absolute PDF points look convenient until you render a thumbnail at a different scale or a print preview opens. Normalizing once at capture time makes every downstream consumer (thumbnail, signing library, audit view) a pure multiply.

Two-phase workflow: capture, then consume

The bundle solves coordinate capture, not cryptographic signing. The natural workflow is:

  1. Upload PDF → store the original file immutably.
  2. User drags boxes → submit → validate and persist the coordinate JSON.
  3. A separate service reads the coordinates and hands them to your signing stack (an external e-sign API, a PDF library, or a queue job).

Keep phases 2 and 3 in separate services. The moment you let the coordinate capture code also stamp signatures, you lose the ability to re-open a document and adjust placement without re-signing.

Treat the AcroForm editor as opt-in per document

The AcroForm editor is described as optional and it mutates the PDF structure. Gate it behind a config flag and a per-document decision — flat/scanned PDFs with no form fields get only the drag-and-drop path. Always write AcroForm output to a new path, never over the uploaded original.

Use a data transformer for the JSON payload

The field submits a JSON string, not a typed object. A DataTransformer that maps JSON ↔ a SignatureLayout value object keeps your entity clean and lets you attach constraints (NotBlank, custom "boxes inside page bounds", "no overlapping boxes"). Without a transformer you end up with json_decode scattered across controllers.

Asset pipeline integration

Whatever frontend stack you use, integrate at the bundle's existing init point rather than writing a parallel bootstrap. If you run Turbo/htmx, re-initialize the viewer on turbo:load — a DOMContentLoaded-only init works on first paint and silently dies on every subsequent navigation, which reads as a flaky bug rather than a lifecycle bug.

Testing pattern

The high-value test is headless: submit the form with a coordinates payload, assert the entity persisted the right structure and that invalid boxes are rejected. Dragging is not worth an end-to-end browser test; the coordinate math is, so unit-test the normal/absolute conversion functions directly.

Gotchas and Tips

Vendor risk is the headline. Zero stars, zero dependents, a single maintainer, and a suspicious release date (2026-10-26). Pin an exact version in composer.json, read the source before depending on it in production, and be prepared to vendor the form type into your app if the project goes quiet. Budget for that fork; do not assume you'll get upstream fixes.

PDF.js worker 404 is the #1 failure. Symptoms: blank canvas, Setting up fake worker warning. Causes: assets not installed, wrong public path, or a CSP that blocks the worker. If you run a strict Content-Security-Policy, you likely need worker-src 'self' blob: and possibly blob: under script-src.

Viewer/worker version skew. If you load PDF.js from a CDN elsewhere in your app, the version must match the worker the bundle ships. Two PDF.js copies on one page produces confusing, non-obvious breakage.

Coordinate origin flip. PDF space has its origin at bottom-left; the DOM has it at top-left. Every conversion must flip y, and must account for the page's CropBox offset versus MediaBox — a cropped PDF will place every box offset by the crop margin if you assume MediaBox. Rotation (/Rotate 90/180/270) compounds this: store the rotation alongside the coordinates and apply it at render, don't bake it into stored values.

Units. PDF units are points (72/inch); the DOM is CSS pixels scaled by the viewer zoom. Never persist pixel values captured at whatever zoom the user happened to be at.

Form field not mapped. If the field holds only client-side state, mark it 'mapped' => false and read it from $form->get(...)->getData() in the controller. Otherwise Symfony throws a transformation failure on an unmapped property.

Large PDFs. The viewer lazy-loads pages but the uploaded file still crosses PHP's upload_max_filesize / post_max_size and any memory limit you set for the request. Cap page count in a validator with a clear error message rather than letting it OOM.

Extension points, in preference order: decorate services via services.yaml → override the Twig template through your app's bundle-override path → contribute an upstream patch. Do not edit files under vendor/, and do not edit src/Resources/public/build even in a fork — rebuild from src/Resources/assets.

Debugging order when nothing works: (1) browser console for PDF.js errors, (2) Network tab for 404s on the worker/viewer bundle, (3) view-source the rendered field to confirm the data attributes the JS expects are present, (4) Symfony profiler for form transformation errors. In that order — it's almost always (1) or (2).

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin