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

Htmlpdfapi Laravel Package

netgen/htmlpdfapi

Netgen HTMLPDFAPI is a PHP client for the HTMLPDFAPI service that converts HTML to PDF. Send raw HTML or a URL, configure your API credentials, and generate PDFs for invoices, reports, and other documents from your application.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a thin client around the HTMLPDFAPI.com REST service — it does not render PDFs itself, it serializes HTML to a remote endpoint and streams back a PDF binary. Because the repo is archived and the underlying SaaS has had availability issues, treat it as a legacy integration, not a greenfield choice.

Minimal steps:

  1. Install with a pinned version (composer require netgen/htmlpdfapi:"^1.0"); unpinned installs may fail because archived branches are not tagged against modern PHP. If you're on PHP 8.1+, expect to add symfony/polyfill or skip the bundle registration and instantiate the client class manually.
  2. Grab your API key from the HTMLPDFAPI.com dashboard — it is the single required config value. In a Symfony app this lives in html_pdf_api.api_key, fed from %env(HTMLPDF_API_KEY)%.
  3. Register the client as a service (the bundle does this for you in legacy Symfony; on Symfony 5+ you may need to declare it in services.yaml yourself).
  4. First use case: generate a single document — invoice, ticket, or report — where the HTML is under ~200 KB and CSS is simple. Send HTML, receive PDF bytes, write to storage/app/pdf/invoice-{$id}.pdf.

First place to look: the client class (HtmlPdfApiClient or similar) and its getPdf() method signature. Everything else in the package is glue — the actual behaviour is defined by the remote API's accepted parameters (html, css, js, filename, format, orientation).

Implementation Patterns

Wrap the client behind your own interface. Since the package is archived and the SaaS is fragile, never inject HtmlPdfApiClient directly into controllers or jobs. Define PdfGeneratorInterface::generate(string $html, array $options): string and make the package one implementation. That way, when you inevitably migrate to dompdf, weasyprint, or gotenberg, you change one class.

Build HTML with Twig, not string concatenation. Render your invoice/report template with Twig (or Blade) first, pass the rendered string to the API. This keeps layout logic in templates and lets you test rendering without touching the network. Pass base CSS inline or as a second API parameter — the service cannot fetch relative asset URLs.

Prefer the CSS/JS-inlined payload. If the API accepts separate css and js arrays, use them; if you inline everything yourself, minify first. The remote renderer has a hard payload ceiling (typically a few hundred KB) — base64-encoding logos is usually where teams blow past it.

Queue PDF generation for anything user-facing. Generating a 5–15 second PDF synchronously inside a web request is the fastest route to 504s. Dispatch a GeneratePdf job, store the resulting bytes in S3/local disk, and notify the user via email or a polling UI. Store only the path, never the bytes, in the primary database row.

Cache aggressively by content hash. PDFs are deterministic for identical HTML + options. Hash the input and store pdf/{sha256}.pdf; skip the API call entirely on repeat requests. This alone cuts costs by an order of magnitude on transactional documents (terms, receipts) that don't change.

Test with a fake client. Unit tests should never hit HTMLPDFAPI. Substitute a FakePdfGenerator that returns a fixture string. For the boundary, do one integration test behind a @group integration tag so CI only runs it when HTMLPDF_API_KEY is present.

Log the request payload on failure. The HTMLPDFAPI service rarely returns useful error bodies. When a call fails, log the HTML (scrubbed of PII) and options to a rotating channel so you can reproduce locally.

Gotchas and Tips

  • License is GPL-2.0. This is a real constraint for proprietary applications. A GPL PHP library pulled into a closed-source Laravel app is a compliance risk your legal team will flag. Check with counsel before shipping commercially; the client code is small enough to reimplement cleanly from the API docs.
  • Archived = no security patches. If a future PHP version breaks the Guzzle/HttpClient version it depends on, nobody will fix it. Vendor the two or three files you actually use, or reimplement — the surface area is tiny.
  • The remote service is the real dependency. HTMLPDFAPI.com has had downtime and unclear long-term support. Always wrap calls in retry-with-backoff (3 attempts, exponential) and a circuit breaker so a dead vendor doesn't hang your queue workers.
  • CSS support is weak. No flexbox, no grid, no modern CSS variables, limited @font-face (URLs must be absolute or fonts base64'd). Design PDFs with tables, floats, and explicit widths. Test every template against a real output — browser preview will lie to you.
  • Fonts must be embedded or reachable. Custom fonts via @font-face need absolute HTTPS URLs the service can fetch. Self-host in S3 or a CDN; Google Fonts URLs frequently time out.
  • Images: absolute URLs or base64 only. {{ asset() }} yields /images/logo.png — the remote service cannot resolve that. Use {% autoescape false %}{{ absolute_url(asset('images/logo.png')) }}{% endautoescape %} or base64-encode small logos.
  • Timezone and locale. PDFs are a record — freeze them. Render dates in the document's own timezone/locale, not config('app.timezone'), or you'll ship invoices with the wrong date to overseas customers.
  • Watch the response Content-Type. The client returns raw bytes; if you return response($pdf)->header('Content-Type', 'application/pdf') and get garbage, check that an exception handler isn't prepending HTML to the response, and that the client isn't decoding JSON error bodies as PDF bytes.
  • The API key is a secret. It must live in .env / a secrets manager, never in config files committed to Git, and never be sent from the browser. Rotate it if it ever appears in logs.
  • Debugging tip: drop the exact HTML into a local pdftoppm/weasyprint rendering to see what the service should produce. If local renderers also fail, the HTML is the problem; if only the service fails, it's a payload size, asset URL, or CSS-compat issue.
  • Migration escape hatch. Because the client is thin, swapping to Gotenberg (self-hosted, Docker, Chromium-backed) is usually a 1–2 day task once your interface and template layer are in place. If you're evaluating this package at all, budget for that migration rather than assuming it will last.
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.
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle