├── LICENSE.md ├── README.md ├── composer.json ├── config └── pdfable.php ├── resources └── views │ └── base.blade.php ├── src ├── Commands │ └── MakeCommand.php ├── Concerns │ ├── CanAccessPropertiesAndMethods.php │ ├── CanBeAttached.php │ ├── CanBeQueued.php │ ├── CanBeRendered.php │ └── CanBeStored.php ├── Drivers │ ├── BrowsershotDriver.php │ ├── Driver.php │ └── WkhtmltopdfDriver.php ├── Layout │ ├── Page.php │ ├── PageOrientation.php │ └── PageSize.php ├── Pdfable.php └── PdfableServiceProvider.php └── stubs ├── Pdfable.stub └── view.stub /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) pxlrbt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Pdfable 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/pxlrbt/laravel-pdfable.svg?style=flat-square)](https://packagist.org/packages/pxlrbt/laravel-pdfable) 4 | [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/pxlrbt/laravel-pdfable/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/pxlrbt/laravel-pdfable/actions?query=workflow%3Arun-tests+branch%3Amain) 5 | [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/pxlrbt/laravel-pdfable/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/pxlrbt/laravel-pdfable/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/pxlrbt/laravel-pdfable.svg?style=flat-square)](https://packagist.org/packages/pxlrbt/laravel-pdfable) 7 | 8 | Keep the logic for your PDFs in one place like you do with Laravel's Mailables. 9 | 10 | ## Installation 11 | 12 | You can install the package via composer: 13 | 14 | ```bash 15 | composer require pxlrbt/laravel-pdfable 16 | ``` 17 | 18 | You can publish the config file with: 19 | 20 | ```bash 21 | php artisan vendor:publish --tag="pdfable-config" 22 | ``` 23 | 24 | Optionally, you can publish the views using 25 | 26 | ```bash 27 | php artisan vendor:publish --tag="pdfable-views" 28 | ``` 29 | 30 | ## Configuration 31 | 32 | Currently two drivers are supported: 33 | 34 | - Browsershot (default) 35 | - Wkhtmltopdf (legacy, wkhtmltopdf is deprecated) 36 | 37 | ### Browsershot Driver 38 | 39 | This is the default driver and requires [spatie/browsershot](https://github.com/spatie/browsershot). Please follow the installation instructions for that package. 40 | 41 | You can configure the Browsershot driver via `BrowsershotDriver::configureUsing()` in your `AppServiceProvider`: 42 | 43 | ```php 44 | BrowsershotDriver::configureUsing( 45 | fn (Browsershot $browser) => $browser->setCustomTempPath(storage_path('tmp')) 46 | ); 47 | ``` 48 | 49 | ### Wkhtmltopdf Driver 50 | 51 | To use the wkhtmlpdf Driver, make sure `wkhtmltopdf` is installed on your system and globally available. 52 | 53 | Then, set the `PDFABLE_DRIVER` option in your `.env` file to `wkhtmltopdf`. 54 | 55 | 56 | ## Generating Pdfables 57 | 58 | You can use the make command to generate a Pdfable class and view. 59 | 60 | ```shell 61 | 62 | php artisan make:pdf Invoice 63 | 64 | ``` 65 | 66 | ## Usage 67 | 68 | You can directly use, pass or return Pdfables in many places in your app. 69 | 70 | ### As Files 71 | 72 | You can store Pdfables via `->store()` method. This will use `outputFile()` method on the class to determine the class name. Optionally, you can pass a custom filename. 73 | 74 | ```php 75 | (new Invoice($order)->store())); 76 | ``` 77 | 78 | ### As Responses 79 | 80 | You can either stream, download or return your Pdfables HTML for debugging. 81 | 82 | #### HTML 83 | 84 | To return HTML in a debugging view, just return the Pdfable. 85 | 86 | ```php 87 | Route::get('/invoice/{order}', fn (Order $order) => new Invoice($order)); 88 | ``` 89 | 90 | #### Stream 91 | 92 | To stream your Pdfable, add the `->stream()` method. 93 | 94 | ```php 95 | Route::get('/invoice/{order}', fn (Order $order) => (new Invoice($order)->stream())); 96 | ``` 97 | 98 | #### Download 99 | 100 | To download your Pdfable, add the `->download()` method. Optionally, you can also override the filename from here. 101 | 102 | ```php 103 | Route::get('/invoice/{order}', fn (Order $order) => (new Invoice($order)->download('custom-filename.pdf'))); 104 | ``` 105 | 106 | ### As Mailable Attachment 107 | 108 | To use a Pdfable as a mail attachment, just pass it via `->attach()`. Make sure your mailables/notifications are queued for faster processing. 109 | 110 | ```php 111 | return (new MailMessage) 112 | ->subject("Your Invoice") 113 | ->attach(new Invoice($order)); 114 | ``` 115 | 116 | ### As Jobs 117 | 118 | Pdfs can take some time to create, so you can queue your Pdfables and create them in the background with the known Laravel methods. 119 | 120 | ```php 121 | dispatch(new Invoice($order)); 122 | // or 123 | Invoice::dispatch($order); 124 | // ... 125 | ``` 126 | 127 | ## Writing Pdfables 128 | 129 | Once you have generated a pdfable class, open it up so we can explore its contents. Pdfable class configuration is done in several methods. 130 | 131 | ### Configuring The View 132 | 133 | The view is configured via static `$view` property. 134 | 135 | ```php 136 | class Invoice extends Pdfable 137 | { 138 | public string $view = 'pdf.task'; 139 | } 140 | ``` 141 | 142 | ### Configuring The Page/Layout 143 | 144 | You can return a `Page` object to configure the PDF page size, orientation and margins. 145 | 146 | ```php 147 | public function page(): Page 148 | { 149 | return Page::make()->size(PageSize::A4)->margins('narrow'); 150 | } 151 | ``` 152 | 153 | ### Passing Additional Data 154 | 155 | Pass additional data via the constructor of your Pdfable for later use. 156 | 157 | ```php 158 | public function __construct( 159 | public Order $order, 160 | public ?Customer $customer = null, 161 | ) 162 | {} 163 | ``` 164 | 165 | ### Accessing Data From View 166 | 167 | Similar to Laravel's Blade Components you can access properties and public methods directly from your view file. 168 | 169 | ```html 170 |

Invoice for Order {{ $order->id }}

171 | 172 |
Total: {{ $getTotal() }}
173 | ``` 174 | 175 | ### Configuring The Output File 176 | 177 | When saving a Pdfable to the disk, you can provide a default path via `filename()` and override the default disk via `$disk` property. 178 | 179 | ```php 180 | public function filename(): string 181 | { 182 | return "customers/{$this->customer->id}/{$this->order->id}.pdf"; 183 | } 184 | ``` 185 | 186 | ### Queuing A Pdfable 187 | 188 | Pdfables implement `ShouldQueue` and therefore can be pushed to a queue via `Invoice::dispatch()`. You can also use other queue configuration methods directly on your Pdfable like `backoff()`, `retryUntil()`, `uniqueId()`, ... 189 | 190 | ## Credits 191 | 192 | - [Dennis Koch](https://github.com/pxlrbt) 193 | - [All Contributors](../../contributors) 194 | 195 | ## License 196 | 197 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 198 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pxlrbt/laravel-pdfable", 3 | "description": "This is my package laravel-pdfable", 4 | "keywords": [ 5 | "pxlrbt", 6 | "laravel", 7 | "laravel-pdfable" 8 | ], 9 | "homepage": "https://github.com/pxlrbt/laravel-pdfable", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Dennis Koch", 14 | "email": "info@pixelarbeit.de", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.1", 20 | "spatie/laravel-package-tools": "^1.13.0", 21 | "illuminate/contracts": "^9.0|^10.0|^11.0" 22 | }, 23 | "require-dev": { 24 | "laravel/pint": "^1.0", 25 | "nunomaduro/collision": "^6.0", 26 | "nunomaduro/larastan": "^2.0.1", 27 | "orchestra/testbench": "^7.0", 28 | "pestphp/pest": "^1.21", 29 | "pestphp/pest-plugin-laravel": "^1.1", 30 | "phpstan/extension-installer": "^1.1", 31 | "phpstan/phpstan-deprecation-rules": "^1.0", 32 | "phpstan/phpstan-phpunit": "^1.0", 33 | "phpunit/phpunit": "^9.5" 34 | }, 35 | "suggest": { 36 | "spatie/browsershot": "^3.57.5" 37 | }, 38 | "autoload": { 39 | "psr-4": { 40 | "pxlrbt\\LaravelPdfable\\": "src", 41 | "pxlrbt\\LaravelPdfable\\Database\\Factories\\": "database/factories" 42 | } 43 | }, 44 | "autoload-dev": { 45 | "psr-4": { 46 | "pxlrbt\\LaravelPdfable\\Tests\\": "tests" 47 | } 48 | }, 49 | "scripts": { 50 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 51 | "analyse": "vendor/bin/phpstan analyse", 52 | "test": "vendor/bin/pest", 53 | "test-coverage": "vendor/bin/pest --coverage", 54 | "format": "vendor/bin/pint" 55 | }, 56 | "config": { 57 | "sort-packages": true, 58 | "allow-plugins": { 59 | "pestphp/pest-plugin": true, 60 | "phpstan/extension-installer": true 61 | } 62 | }, 63 | "extra": { 64 | "laravel": { 65 | "providers": [ 66 | "pxlrbt\\LaravelPdfable\\PdfableServiceProvider" 67 | ] 68 | } 69 | }, 70 | "minimum-stability": "dev", 71 | "prefer-stable": true 72 | } 73 | -------------------------------------------------------------------------------- /config/pdfable.php: -------------------------------------------------------------------------------- 1 | env('PDFABLE_DRIVER', 'browsershot'), 10 | 11 | 'drivers' => [ 12 | 'browsershot' => BrowsershotDriver::class, 13 | 'wkhtmltopdf' => WkhtmltopdfAdapter::class, 14 | ], 15 | 16 | 'layout' => [ 17 | 'defaults' => [ 18 | 'page-size' => PageSize::A4, 19 | 'orientation' => PageOrientation::Portrait, 20 | 'margins' => 'moderate', 21 | ], 22 | 23 | 'margins' => [ 24 | 'narrow' => [12.7, 12.7, 12.7, 12.7], 25 | 'moderate' => [25.4, 19.05, 25.4, 19.05], 26 | 'wide' => [25.4, 50.8, 25.4, 50.8], 27 | 'none' => [0, 0, 0, 0], 28 | ], 29 | ], 30 | ]; 31 | -------------------------------------------------------------------------------- /resources/views/base.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @yield('title') 6 | 7 | 8 | @yield('content') 9 | 10 | @if ($preview) 11 | @php 12 | $page = $page(); 13 | $dpi = 1 / 25.4 * 96; 14 | 15 | $width = ($page->getWidth() * $dpi) . 'px'; 16 | $height = ($page->getHeight() * $dpi) . 'px'; 17 | $margins = array_map(fn ($value) => $value * $dpi . 'px', $page->getMargins()); 18 | @endphp 19 | 20 | 42 | @endif 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/Commands/MakeCommand.php: -------------------------------------------------------------------------------- 1 | argument('name'); 20 | $class = Str::studly($name); 21 | $view = Str::lower($name); 22 | 23 | if (! $this->isClassNameValid($class)) { 24 | $this->line(" WHOOPS! 😳 \n"); 25 | $this->line("Class is invalid: {$class}"); 26 | 27 | return; 28 | } 29 | 30 | if ($this->isReservedClassName($class)) { 31 | $this->line(" WHOOPS! 😳 \n"); 32 | $this->line("Class is reserved: {$class}"); 33 | 34 | return; 35 | } 36 | 37 | $force = $this->option('force'); 38 | 39 | $data = [ 40 | 'namespace' => $namespace, 41 | 'class' => $class, 42 | 'view' => $view, 43 | ]; 44 | 45 | $class = $this->createClass($class, $data, $force); 46 | $view = $this->createView($view, $force); 47 | 48 | if ($class || $view) { 49 | $this->line(" COMPONENT CREATED 🤙\n"); 50 | $class && $this->line("CLASS: $class"); 51 | $view && $this->line("VIEW: $view"); 52 | } 53 | } 54 | 55 | protected function createClass($name, $data, $force = false) 56 | { 57 | $path = app_path("Pdfs/{$name}.php"); 58 | 59 | if (File::exists($path) && ! $force) { 60 | $this->line("Class already exists: {$path}"); 61 | 62 | return false; 63 | } 64 | 65 | $this->copyStubToApp('Pdfable', $path, $data); 66 | 67 | return $path; 68 | } 69 | 70 | protected function createView($name, $force = false) 71 | { 72 | $path = resource_path("views/pdfs/{$name}.blade.php"); 73 | 74 | if (File::exists($path) && ! $force) { 75 | $this->line("View already exists: {$path}"); 76 | 77 | return false; 78 | } 79 | 80 | $this->copyStubToApp('view', $path); 81 | 82 | return $path; 83 | } 84 | 85 | public function isClassNameValid($name) 86 | { 87 | return preg_match("/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/", $name); 88 | } 89 | 90 | public function isReservedClassName($name) 91 | { 92 | return array_search(strtolower($name), $this->getReservedName()) !== false; 93 | } 94 | 95 | protected function copyStubToApp(string $stub, string $targetPath, array $replacements = []): void 96 | { 97 | $filesystem = app(Filesystem::class); 98 | 99 | if (! File::exists($stubPath = base_path("stubs/laravel-pdfable/{$stub}.stub"))) { 100 | $stubPath = realpath(__DIR__."/../../stubs/{$stub}.stub"); 101 | } 102 | 103 | $stub = Str::of($filesystem->get($stubPath)); 104 | 105 | foreach ($replacements as $key => $replacement) { 106 | $stub = $stub->replace("{{ {$key} }}", $replacement); 107 | } 108 | 109 | $stub = (string) $stub; 110 | 111 | File::makeDirectory(dirname($targetPath), force: true); 112 | 113 | File::put($targetPath, $stub); 114 | } 115 | 116 | private function getReservedName() 117 | { 118 | return [ 119 | 'parent', 120 | 'component', 121 | 'interface', 122 | '__halt_compiler', 123 | 'abstract', 124 | 'and', 125 | 'array', 126 | 'as', 127 | 'break', 128 | 'callable', 129 | 'case', 130 | 'catch', 131 | 'class', 132 | 'clone', 133 | 'const', 134 | 'continue', 135 | 'declare', 136 | 'default', 137 | 'die', 138 | 'do', 139 | 'echo', 140 | 'else', 141 | 'elseif', 142 | 'empty', 143 | 'enddeclare', 144 | 'endfor', 145 | 'endforeach', 146 | 'endif', 147 | 'endswitch', 148 | 'endwhile', 149 | 'eval', 150 | 'exit', 151 | 'extends', 152 | 'final', 153 | 'finally', 154 | 'fn', 155 | 'for', 156 | 'foreach', 157 | 'function', 158 | 'global', 159 | 'goto', 160 | 'if', 161 | 'implements', 162 | 'include', 163 | 'include_once', 164 | 'instanceof', 165 | 'insteadof', 166 | 'interface', 167 | 'isset', 168 | 'list', 169 | 'namespace', 170 | 'new', 171 | 'or', 172 | 'print', 173 | 'private', 174 | 'protected', 175 | 'public', 176 | 'require', 177 | 'require_once', 178 | 'return', 179 | 'static', 180 | 'switch', 181 | 'throw', 182 | 'trait', 183 | 'try', 184 | 'unset', 185 | 'use', 186 | 'var', 187 | 'while', 188 | 'xor', 189 | 'yield', 190 | ]; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/Concerns/CanAccessPropertiesAndMethods.php: -------------------------------------------------------------------------------- 1 | attributes = $this->attributes ?: $this->newAttributeBag(); 48 | 49 | return array_merge($this->extractPublicProperties(), $this->extractPublicMethods()); 50 | } 51 | 52 | protected function extractPublicProperties() 53 | { 54 | $class = get_class($this); 55 | 56 | if (! isset(static::$propertyCache[$class])) { 57 | $reflection = new ReflectionClass($this); 58 | 59 | static::$propertyCache[$class] = collect($reflection->getProperties(ReflectionProperty::IS_PUBLIC)) 60 | ->reject(function (ReflectionProperty $property) { 61 | return $property->isStatic(); 62 | }) 63 | ->reject(function (ReflectionProperty $property) { 64 | return $this->shouldIgnore($property->getName()); 65 | }) 66 | ->map(function (ReflectionProperty $property) { 67 | return $property->getName(); 68 | })->all(); 69 | } 70 | 71 | $values = []; 72 | 73 | foreach (static::$propertyCache[$class] as $property) { 74 | $values[$property] = $this->{$property}; 75 | } 76 | 77 | return $values; 78 | } 79 | 80 | /** 81 | * Extract the public methods for the component. 82 | * 83 | * @return array 84 | */ 85 | protected function extractPublicMethods() 86 | { 87 | $class = get_class($this); 88 | 89 | if (! isset(static::$methodCache[$class])) { 90 | $reflection = new ReflectionClass($this); 91 | 92 | static::$methodCache[$class] = collect($reflection->getMethods(ReflectionMethod::IS_PUBLIC)) 93 | ->reject(function (ReflectionMethod $method) { 94 | return $this->shouldIgnore($method->getName()); 95 | }) 96 | ->map(function (ReflectionMethod $method) { 97 | return $method->getName(); 98 | }); 99 | } 100 | 101 | $values = []; 102 | 103 | foreach (static::$methodCache[$class] as $method) { 104 | $values[$method] = $this->createVariableFromMethod(new ReflectionMethod($this, $method)); 105 | } 106 | 107 | return $values; 108 | } 109 | 110 | /** 111 | * Create a callable variable from the given method. 112 | * 113 | * @return mixed 114 | */ 115 | protected function createVariableFromMethod(ReflectionMethod $method) 116 | { 117 | return $method->getNumberOfParameters() === 0 118 | ? $this->createInvokableVariable($method->getName()) 119 | : Closure::fromCallable([$this, $method->getName()]); 120 | } 121 | 122 | /** 123 | * Create an invokable, toStringable variable for the given component method. 124 | * 125 | * @return \Illuminate\View\InvokableComponentVariable 126 | */ 127 | protected function createInvokableVariable(string $method) 128 | { 129 | return new InvokableComponentVariable(function () use ($method) { 130 | return $this->{$method}(); 131 | }); 132 | } 133 | 134 | /** 135 | * Determine if the given property / method should be ignored. 136 | * 137 | * @param string $name 138 | * @return bool 139 | */ 140 | protected function shouldIgnore($name) 141 | { 142 | return str_starts_with($name, '__') || 143 | in_array($name, $this->ignoredMethods()); 144 | } 145 | 146 | /** 147 | * Get the methods that should be ignored. 148 | * 149 | * @return array 150 | */ 151 | protected function ignoredMethods() 152 | { 153 | return array_merge([ 154 | 'data', 155 | 'render', 156 | 'resolveView', 157 | 'shouldRender', 158 | 'view', 159 | 'withName', 160 | 'withAttributes', 161 | ], $this->except); 162 | } 163 | 164 | /** 165 | * Get a new attribute bag instance. 166 | * 167 | * @return \Illuminate\View\ComponentAttributeBag 168 | */ 169 | protected function newAttributeBag(array $attributes = []) 170 | { 171 | return new ComponentAttributeBag($attributes); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/Concerns/CanBeAttached.php: -------------------------------------------------------------------------------- 1 | $this->driver()->getData($this), 16 | $this->displayFilename() ?? 'attachment.pdf' 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Concerns/CanBeQueued.php: -------------------------------------------------------------------------------- 1 | save(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Concerns/CanBeRendered.php: -------------------------------------------------------------------------------- 1 | filename()); 10 | } 11 | 12 | public function stream() 13 | { 14 | return response()->stream(function () { 15 | echo $this->driver()->getData($this); 16 | }, 17 | 200, 18 | ['Content-Type' => 'application/pdf'] 19 | ); 20 | } 21 | 22 | public function download(?string $filename = null) 23 | { 24 | $filename = $filename ?? $this->displayFilename(); 25 | 26 | // Remove all characters that are not the separator, letters, numbers, or whitespace 27 | $sanitizedFilename = preg_replace('![^'.preg_quote('-').'\pL\pN\s]+!u', '', $filename); 28 | 29 | return response()->streamDownload(function () { 30 | echo $this->driver()->getData($this); 31 | }, 32 | $sanitizedFilename, 33 | ['Content-Type' => 'application/pdf'] 34 | ); 35 | } 36 | 37 | /** 38 | * Entrypoint for responses 39 | */ 40 | public function render() 41 | { 42 | $this->preview = true; 43 | 44 | return $this->getView(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Concerns/CanBeStored.php: -------------------------------------------------------------------------------- 1 | disk = $disk; 19 | 20 | return $this; 21 | } 22 | 23 | protected function getDisk(): string 24 | { 25 | return $this->disk ?? config('filesystems.default'); 26 | } 27 | 28 | public function save(?string $filename = null): static 29 | { 30 | Storage::disk($this->getDisk())->put( 31 | $filename ?? $this->filename(), 32 | $this->driver()->getData($this), 33 | ); 34 | 35 | return $this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Drivers/BrowsershotDriver.php: -------------------------------------------------------------------------------- 1 | getView()->render(); 21 | $page = $pdf->page(); 22 | 23 | $browser = Browsershot::html($html) 24 | ->paperSize($page->getWidth(), $page->getHeight()) 25 | ->margins(...$page->getMargins()); 26 | 27 | if (self::$configureUsing !== null) { 28 | $browser = call_user_func(static::$configureUsing, $browser); 29 | } 30 | 31 | return base64_decode( 32 | $browser->base64pdf() 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Drivers/Driver.php: -------------------------------------------------------------------------------- 1 | getBinaryPath(), 25 | '--disable-smart-shrinking', 26 | '--page-height', $this->page->getHeight(), 27 | '--page-width', $this->page->getWidth(), 28 | '-B', $this->page->getMarginBottom(), 29 | '-T', $this->page->getMarginTop(), 30 | '-L', $this->page->getMarginLeft(), 31 | '-R', $this->page->getMarginRight(), 32 | '-', 33 | '-', 34 | ]; 35 | } 36 | 37 | public function getData(Pdfable $pdf): ?string 38 | { 39 | $this->pdf = $pdf; 40 | $this->page = $pdf->page(); 41 | 42 | $process = new Process($this->getCommandOptions()); 43 | 44 | $process->setInput($pdf->getView()->render()); 45 | 46 | $process->run(); 47 | 48 | if ($process->isSuccessful()) { 49 | return $process->getOutput(); 50 | } 51 | 52 | $process->clearOutput(); 53 | 54 | throw new ProcessFailedException($process); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Layout/Page.php: -------------------------------------------------------------------------------- 1 | size(config('pdfable.layout.defaults.page-size')) 17 | ->orientation(config('pdfable.layout.defaults.orientation')) 18 | ->margins(config('pdfable.layout.defaults.margins')); 19 | } 20 | 21 | public function margins(array|string|null $margins = null) 22 | { 23 | if (is_array($margins)) { 24 | $this->margins = $margins; 25 | 26 | return $this; 27 | } 28 | 29 | $this->margins = config('pdfable.layout.margins')[$margins]; 30 | 31 | return $this; 32 | } 33 | 34 | public function size(PageSize|array $size): static 35 | { 36 | $this->size = is_array($size) ? $size : $size->size(); 37 | 38 | return $this; 39 | } 40 | 41 | public function orientation(PageOrientation $orientation): static 42 | { 43 | $this->orientation = $orientation; 44 | 45 | return $this; 46 | } 47 | 48 | public function portrait(): static 49 | { 50 | $this->orientation = PageOrientation::Portrait; 51 | 52 | return $this; 53 | } 54 | 55 | public function landscape(): static 56 | { 57 | $this->orientation = PageOrientation::Landscape; 58 | 59 | return $this; 60 | } 61 | 62 | public function getHeight() 63 | { 64 | return $this->orientation === PageOrientation::Portrait 65 | ? $this->size[1] 66 | : $this->size[0]; 67 | } 68 | 69 | public function getWidth() 70 | { 71 | return $this->orientation === PageOrientation::Portrait 72 | ? $this->size[0] 73 | : $this->size[1]; 74 | } 75 | 76 | public function getMargins(): array 77 | { 78 | return $this->margins; 79 | } 80 | 81 | public function getMarginTop() 82 | { 83 | return $this->margins[0]; 84 | } 85 | 86 | public function getMarginRight() 87 | { 88 | return $this->margins[1]; 89 | } 90 | 91 | public function getMarginBottom() 92 | { 93 | return $this->margins[2]; 94 | } 95 | 96 | public function getMarginLeft() 97 | { 98 | return $this->margins[3]; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Layout/PageOrientation.php: -------------------------------------------------------------------------------- 1 | [216, 279], 19 | self::Legal => [216, 356], 20 | self::Tabloid => [279, 432], 21 | 22 | self::A3 => [297, 420], 23 | self::A4 => [210, 297], 24 | self::A5 => [148, 210], 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Pdfable.php: -------------------------------------------------------------------------------- 1 | view, $this->data()); 34 | } 35 | 36 | public function page(): Page 37 | { 38 | return Page::make(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/PdfableServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('laravel-pdfable') 20 | ->hasConfigFile() 21 | ->hasCommand(MakeCommand::class) 22 | ->hasViewComponent('base', 'base') 23 | ->hasViews('laravel-pdfable'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /stubs/Pdfable.stub: -------------------------------------------------------------------------------- 1 |