rich-id/terms-module-snappy-bundle
A Laravel module from rich-id that bundles terms and conditions handling with Snappy PDF support, so you can manage, render, and serve policy documents from a single package. Note: minimal adoption and metadata; review the source before use. (219 characters)
This is a first-party-style bundle (0 stars, 0 dependents, single tagged release v1.0.0 — "Initial version of Snappy Generator") that plugs the Snappy PDF engine — the PHP wrapper around the wkhtmltopdf binary — into the rich-id/terms-module renderer pipeline. The release note tells you two things and nothing more: the package owns a generator abstraction (expect a class named something like SnappyGenerator rather than SnappyDriver), and v1.0.0 is the first cut, not a stabilisation. Semver 1.0.0 on an initial version is a packaging decision, not a maturity signal — read src/ before writing code.
Minimum path:
wkhtmltopdf; there is no pure-PHP fallback. On Debian/Ubuntu: apt-get install wkhtmltopdf; on macOS: brew install --cask wkhtmltopdf. Confirm with wkhtmltopdf --version and record the exact version in your Dockerfile — pagination changes between 0.12.5 and 0.12.6.composer require rich-id/terms-module-snappy-bundle and check composer.json constraints against your PHP/Laravel version manually (composer why-not rich-id/terms-module-snappy-bundle). An initial 1.0.0 will target the Laravel version current at the time of writing, not yours — verify before assuming it boots on 11/12.php artisan vendor:publish --provider="RichId\TermsModuleSnappy\...ServiceProvider" (or --tag=terms-snappy). You are looking for binary_path, an options array, and a key that names/selects the generator. If vendor:publish lists nothing, the package may ship config inline-only — read the provider's register() to find the merge key.config/app.php (Laravel ≤10) or bootstrap/providers.php (Laravel 11+), and check whether config/terms.php in the core module needs 'renderer' => 'snappy' or a Manager::extend('snappy', ...) call. The word "Generator" in the release note suggests it implements a generator contract rather than a renderer one — confirm which interface you actually have to satisfy before wiring anything.response()->streamDownload(fn () => $pdf, 'terms.pdf', ['Content-Type' => 'application/pdf']), with inline instead of attachment for an in-browser preview. This is the smallest end-to-end slice that exercises binary path, generator resolution, template rendering, and HTTP response at once.Where to look first: the provider's register()/boot() (what it binds and under what key), the generator class (what options it forwards to Snappy, and whether options are per-call or per-constructor), and the core terms-module generator/renderer interface (what you actually have to satisfy). Because there is exactly one release note line and no changelog history, src/ is the only documentation that exists.
Keep Snappy as transport, not as a template engine. Let the terms module own documents, revisions, and Blade/HTML templates; let this bundle own only "HTML string in, PDF bytes out." That boundary is what makes it replaceable later — and with a 1.0.0 initial release, replaceability is the main thing you are buying.
Profile-based options. Snappy's option array is where all the real behaviour lives (--page-size, margins, --header-html, --footer-html, --enable-local-file-access, --dpi, --disable-smart-shrinking). Define named profiles in config (terms, letter, receipt) and select one per document type rather than mutating a global array at call time. On a first release, expect the config to expose a single flat options array; wrapping it in profiles yourself is cheap insurance against upstream changing that shape.
Render Blade to a string, then hand it over. view('terms.pdf.document', $data)->render() produces the HTML; the generator converts it. This keeps CSS and assets inside your app, avoids giving Snappy filesystem paths, and makes the HTML unit-testable without invoking a binary.
Stream vs. persist. Two distinct patterns:
document_id . updated_at . hash(options), serve from storage, and bust the cache on document save. This is what you want for anything user-facing at scale — and it also insulates you from the generator's API changing under a patch release.Queue it when it's big. Snappy is synchronous and blocking. Multi-page documents with remote assets belong in a job; return a "generating" state and notify or email the artifact. Never call it inside a request that also does DB writes you care about — a hung render will hold the transaction.
Test against the contract, not the bytes. In CI, bind a fake generator at the terms-module interface and assert on the HTML and options passed in. Snapshot-testing PDF bytes is brittle across binary versions; if you must run the real binary, pin it in the test image and assert page count via pdfinfo, not file hash.
Integration tip: if your app already has barryvdh/laravel-snappy, check for a duplicate Knp\Snappy\Pdf binding before installing — two packages registering the same service is a classic silent-override bug here, and with a single-release dependency you have no upstream issue tracker to confirm which one wins.
--enable-local-file-access is mandatory on newer builds for local CSS and images. Without it, assets fail silently into blank space — no exception, no log line. Prefer absolute URLs from asset() or Storage::url() over relative paths.fontconfig and a font set will substitute glyphs and break every layout measurement. Install your fonts in the image explicitly.QT_QPA_PLATFORM=offscreen or xvfb-run; use a distro-patched wkhtmltopdf package rather than a raw static download where you can.<link>/@import in terms templates.Manager::extend('snappy', ...) must run in a provider that boots before the first resolution. Symptom: "driver/generator not supported" intermittently, depending on which request path triggers it first. First releases commonly get this boot ordering wrong — if the bundled provider is deferred, force it non-deferred.storage/app/debug/{id}.html, open it in Chrome, and print with --print-to-pdf. That isolates "my HTML is wrong" from "Snappy can't render my HTML" in seconds. With no upstream docs, this is also the fastest way to reverse-engineer what options the generator actually forwards.<img src> are an SSRF vector through the generator's network access, and stray <script> behaves inconsistently between QtWebKit versions."1.0.0", not ^1.0), fork it into your VCS if you need a fix, and don't build a roadmap on upstream responsiveness. If a v1.0.1 appears, read the diff before updating — an initial-version package has no compatibility promises to lean on.spatie/browsershot for Chrome-quality output, or dompdf for trivial documents — and keep this bundle only for legacy templates. Design your call sites so swapping the generator is a config change, not a refactor.How can I help you explore Laravel packages today?