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

Princexml Php Laravel Package

gridonic/princexml-php

A lightweight PHP wrapper for PrinceXML that simplifies converting HTML and CSS into high-quality PDFs. It offers a clean API for running Prince, handling input/output files and binary paths, making it easy to generate print-ready documents from PHP or Laravel applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the wrapper (it is still on Packagist but archived — expect it to just work as a thin shell shim):

composer require gridonic/princexml-php

The package is not a PDF engine. It shells out to the commercial prince binary. Install that first on every machine and CI container that will render PDFs:

prince --version        # must resolve on PATH, or pass an absolute path

Check the source before you write code — this is a PSR-0 conversion from 2017, so confirm the class name in src/ (the entry point is the Prince class; the autoload mapping is PSR-0, not PSR-4). Instantiate it with the binary path, then convert a rendered Blade view:

use PrinceXMLPhp\Prince; // confirm exact FQCN in src/ before adopting

$prince = new Prince(config('pdf.prince_bin')); // e.g. '/usr/bin/prince'
$prince->setHTML(true);
$prince->setLog(storage_path('logs/prince.log'));

$prince->convert_string_to_file(
    view('invoices.pdf', ['invoice' => $invoice])->render(),
    storage_path("app/invoices/{$invoice->id}.pdf"),
    config('app.url') // base URL for relative CSS/images
);

return response()->download(storage_path("app/invoices/{$invoice->id}.pdf"));

That Blade → HTML string → PDF file → download loop is the first use case and covers most of what you'll ever do with it.

Implementation Patterns

Bind it in a service provider, don't instantiate ad hoc. There is no Laravel package auto-discovery, facade, or config file here — you own that wiring:

// config/pdf.php
return [
    'prince_bin'  => env('PRINCE_BIN', '/usr/bin/prince'),
    'base_url'    => env('APP_URL'),
    'log'         => storage_path('logs/prince.log'),
];

// AppServiceProvider::register()
$this->app->bind(Prince::class, fn () => tap(new Prince(config('pdf.prince_bin')), function ($p) {
    $p->setHTML(true);
    $p->setLog(config('pdf.log'));
}));

Wrap it behind your own interface. Because the package is archived with zero dependents, isolate it so you can swap in symfony/process + the prince CLI, or a different engine, without touching controllers:

interface PdfRenderer { public function render(string $html, ?string $baseUrl = null): string; }

class PrinceRenderer implements PdfRenderer {
    public function __construct(private Prince $prince) {}
    public function render(string $html, ?string $baseUrl = null): string {
        return $this->prince->convert_string_to_string($html, $baseUrl ?? config('pdf.base_url'));
    }
}

Prefer *_to_string for HTTP responses. Return the bytes directly and skip temp-file cleanup:

$pdf = app(PdfRenderer::class)->render(view('reports.monthly', $data)->render());

return response($pdf, 200, [
    'Content-Type'        => 'application/pdf',
    'Content-Disposition' => 'inline; filename="report.pdf"',
]);

Use *_to_file when the PDF is large (avoids holding megabytes in memory) or when you store it for later.

Queue anything non-trivial. Conversion is synchronous and blocks the PHP worker for the full render. A 50-page report with images will hang a request. Dispatch a job, write the file to disk, then notify/download:

GenerateInvoicePdf::dispatch($invoice);

Asset and stylesheet resolution is the usual integration friction. Relative url() and <link href> resolve against the base URL passed to convert_*. Either pass a publicly reachable URL (config('pdf.base_url')) or stage assets on disk and pass a file:// base. If you use the wrapper's addStyleSheet() / .addScript(), they are passed to the CLI as separate arguments — handy for injecting a shared print.css on top of every document.

Design the HTML for PrinceXML, not the browser. Paged media CSS is where the engine earns its money: @page { size: A4; margin: 20mm }, @page :first, running headers via string-set, page-break-before, named pages, footnotes. Put that in a dedicated print stylesheet, not your app CSS.

Test with a skip when the binary is absent.

protected function setUp(): void {
    parent::setUp();
    if (!is_executable(config('pdf.prince_bin'))) {
        $this->markTestSkipped('prince binary not installed');
    }
}

Gotchas and Tips

  • Licensing is the real blocker. The wrapper is MIT, but PrinceXML the binary is commercial for production use (free for non-commercial/personal). Budget for a server license per host before you build on it. This is why the package has 8 stars and 0 dependents.
  • Archived in 2017, PHP 5 vintage. Composer 2 still loads PSR-0 via classmap, but PSR-0 is deprecated in favor of PSR-4. Run it on PHP 8 in dev early — expect possible deprecation notices or dynamic-property warnings. If the FQCN or autoload map clashes, fork it into app/Support/ as a classmap entry — it's ~a few hundred lines.
  • Binary path and shell. The wrapper invokes the CLI, so it inherits the PHP process's environment. Absolute paths are safest; proc_open/exec must not be in disable_functions, and the web user needs execute permission on the binary. On Windows, quoting differs — test there if you target it.
  • Base URL omission is the #1 "blank page" cause. Missing images, unapplied CSS, and empty output usually mean no base URL or unreachable assets. Start debugging with setLog(...) and read the Prince log — it lists every missing resource and CSS parse warning.
  • escapeshellarg protects you, but don't pass user-controlled paths. Never route a raw request parameter into an input/output filename. Generate paths yourself and keep the license file outside the public root.
  • Timeouts and memory. Synchronous conversion doesn't respect max_execution_time cleanly (it's an external process). Set an explicit job timeout, and cap document size in your app before calling out. Clean up temp files in a finally.
  • Binary version drift. PrinceXML CSS feature support changes across releases, and output can shift between versions. Pin the binary in your Docker image so PDFs stay reproducible across deploys.
  • Extension points are thin — mostly addStyleSheet/addScript, log path, and encrypt/key options. If you need streaming, per-page metadata, or job cancellation, you'll be editing the library. Fork and vendor it rather than fighting upstream.
  • Evaluate alternatives before committing. If you're starting fresh and don't specifically need Prince's paged-media typography, spatie/browsershot (headless Chrome) or dompdf are actively maintained, pure-PHP-friendly, and don't require a license. Use this package when Prince's print engine quality (footnotes, cross-references, PDF/X, tagged PDF) is the actual requirement.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager