Laravel
papercut/laravel lets you write documents as Blade views. Each <x-doc::*> tag builds a core component, and the whole view becomes one document. Loops, conditionals, slots and your own components all work just like they do in any other Blade view.
See Installation to add the package. Laravel discovers the service provider and the Papercut facade automatically.
A view and a route
{{-- resources/views/pdf/hello.blade.php --}}
<x-doc::document title="Hello">
<x-doc::page>
<x-doc::h1>Hello, {{ $name }}</x-doc::h1>
<x-doc::p>Rendered by Papercut.</x-doc::p>
</x-doc::page>
</x-doc::document>use Papercut\Laravel\Facades\Papercut;
Route::get('/hello', fn () => Papercut::view('pdf.hello', ['name' => 'Ada'])->pdf()->download('hello.pdf'));| Call | What you get back |
|---|---|
Papercut::view($view, $data) | a PendingDocument |
->pdf() | a PendingOutput: download(), inline(), save(), store(), toString(), output() |
->html() | a PendingHtml; see HTML export |
->encrypt($password) | a copy whose PDF needs a password; see Passwords |
->document() | the core Document the view built |
Papercut::make($document) | a PendingDocument for a document built in PHP |
Your own components
Your own Blade components can wrap <x-doc::*> tags. They're a good place to keep pieces you use in lots of documents, like a branded header or a stat card.
{{-- resources/views/components/stat-card.blade.php --}}
@props(['label', 'value', 'color' => 'slate-900'])
<x-doc::column gap="3pt" padding="14pt" background="slate-50" radius="8pt" grow="1">
<x-doc::text size="8pt" bold color="slate-500">{{ $label }}</x-doc::text>
<x-doc::text size="22pt" bold :color="$color">{{ $value }}</x-doc::text>
</x-doc::column><x-doc::row gap="8pt">
<x-stat-card label="SECTIONS" value="4" color="indigo-600" />
<x-stat-card label="TABLE ROWS" :value="count($rows)" color="emerald-600" />
</x-doc::row>Build on the decorators where you can. A question component made from section, field and choices picks up the theme automatically, so it matches the rest of your documents.
Passing values
Attributes accept plain strings (gap="8pt") or PHP values with the usual colon syntax (:gap="$gap"). Boolean attributes can be written on their own (bold) or bound to a value (:bold="$isTotal").
Embedding PHP-built nodes
<x-doc::node :node="$node" /> places a component built in PHP inside a Blade view. It's useful for a chart, a table put together in a service class, or anything else that's easier to build in code.
Inside a text tag, a Text node becomes part of the line. One limitation: an inline tag like <b> can't wrap around an embedded node, so close it on either side instead:
<x-doc::text><b>x </b><x-doc::node :node="$t" /><b> y</b></x-doc::text>The tag prefix
Tags use the doc prefix by default. If that clashes with something in your app, change prefix in the configuration.
When something goes wrong
If a tag has an attribute it doesn't recognise, the render stops with an UnknownAttribute error that names the tag and suggests what you probably meant:
Unknown attribute "colour" on <x-doc::text>. Did you mean "color"?Tags in the wrong place, such as an <x-doc::tr> outside a table or a view without an <x-doc::document> root, throw InvalidStructure.
Laravel wraps any exception thrown while rendering a view in an Illuminate\View\ViewException. Call getPrevious() on it to get Papercut's original exception.