├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json └── src ├── BaseConverter.php ├── ConverterInterface.php ├── Manager.php ├── TempFile.php ├── Template.php └── converters ├── Callback.php ├── Dompdf.php ├── Mpdf.php ├── Tcpdf.php └── Wkhtmltopdf.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Yii 2 HTML to PDF conversion extension Change Log 2 | ================================================= 3 | 4 | 1.0.7, October 17, 2019 5 | ----------------------- 6 | 7 | - Enh #21: Added support for Yii alias at `Wkhtmltopdf::$binPath` (berosoboy) 8 | 9 | 10 | 1.0.6, July 10, 2019 11 | -------------------- 12 | 13 | - Bug #17: Fixed inability to setup multiple 'fontDir' as array for `Mpdf` converter (klimov-paul) 14 | 15 | 16 | 1.0.5, January 24, 2019 17 | ----------------------- 18 | 19 | - Bug #16: Fixed temporary file removal on Windows (AlexRas007) 20 | 21 | 22 | 1.0.4, September 18, 2018 23 | ------------------------- 24 | 25 | - Bug #12: Fixed inability to setup 'tempDir' and 'fontDir' for `Mpdf` converter (klimov-paul) 26 | - Enh #15: Added method `TempFile::getContent()` (mludvik) 27 | 28 | 29 | 1.0.3, April 9, 2018 30 | -------------------- 31 | 32 | - Enh #6: Options 'coverContent', 'headerHtmlContent' and 'footerHtmlContent' added to `Wkhtmltopdf` (berosoboy, klimov-paul) 33 | - Enh #8: 'wkhtmltopdf' command composition improved ensuring options 'cover' and 'toc' do not utilize global ones (klimov-paul) 34 | 35 | 36 | 1.0.2, February 13, 2018 37 | ------------------------ 38 | 39 | - Enh #3: Added support for mPDF version >= 7.0 (klimov-paul) 40 | - Enh #5: 'wkhtmltopdf' command composition improved adding support for boolean and array options (berosoboy) 41 | 42 | 43 | 1.0.1, November 3, 2017 44 | ----------------------- 45 | 46 | - Bug: Usage of deprecated `yii\base\Object` changed to `yii\base\BaseObject` allowing compatibility with PHP 7.2 (klimov-paul) 47 | 48 | 49 | 1.0.0, May 19, 2016 50 | ------------------- 51 | 52 | - Initial release. 53 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The Yii framework is free software. It is released under the terms of 2 | the following BSD License. 3 | 4 | Copyright © 2015 by Yii2tech (https://github.com/yii2tech) 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | * Neither the name of Yii2tech nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

HTML to PDF conversion extension for Yii2

6 |
7 |

8 | 9 | This extension provides basic support for HTML to PDF and PHP to PDF conversion. 10 | 11 | For license information check the [LICENSE](LICENSE.md)-file. 12 | 13 | [![Latest Stable Version](https://img.shields.io/packagist/v/yii2tech/html2pdf.svg)](https://packagist.org/packages/yii2tech/html2pdf) 14 | [![Total Downloads](https://img.shields.io/packagist/dt/yii2tech/html2pdf.svg)](https://packagist.org/packages/yii2tech/html2pdf) 15 | [![Build Status](https://travis-ci.org/yii2tech/html2pdf.svg?branch=master)](https://travis-ci.org/yii2tech/html2pdf) 16 | 17 | 18 | Installation 19 | ------------ 20 | 21 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 22 | 23 | Either run 24 | 25 | ``` 26 | php composer.phar require --prefer-dist yii2tech/html2pdf 27 | ``` 28 | 29 | or add 30 | 31 | ```json 32 | "yii2tech/html2pdf": "*" 33 | ``` 34 | 35 | to the require section of your composer.json. 36 | 37 | > Note: you'll have to install software for the actual HTML to PDF conversion separately, depending on the 38 | particular converter, you would like to use. 39 | 40 | 41 | Usage 42 | ----- 43 | 44 | This extension provides support for HTML to PDF and PHP to PDF conversion. It allows composition of the PDF files 45 | from HTML and via rendering PHP templates. 46 | 47 | Extension functionality is aggregated into `\yii2tech\html2pdf\Manager` application component. 48 | Application configuration example: 49 | 50 | ```php 51 | [ 55 | 'html2pdf' => [ 56 | 'class' => 'yii2tech\html2pdf\Manager', 57 | 'viewPath' => '@app/views/pdf', 58 | 'converter' => 'wkhtmltopdf', 59 | ], 60 | ], 61 | ... 62 | ]; 63 | ``` 64 | 65 | For the simple conversion you can use `\yii2tech\html2pdf\Manager::convert()` and `\yii2tech\html2pdf\Manager::convertFile()` methods: 66 | 67 | ```php 68 | 72 | 73 | 74 | 75 | 76 |

Simple Content

77 | 78 | 79 | HTML; 80 | 81 | // create PDF file from HTML content : 82 | Yii::$app->html2pdf 83 | ->convert($html) 84 | ->saveAs('/path/to/output.pdf'); 85 | 86 | // convert HTML file to PDF file : 87 | Yii::$app->html2pdf 88 | ->convertFile('/path/to/source.html') 89 | ->saveAs('/path/to/output.pdf'); 90 | ``` 91 | 92 | The actual conversion result determined by particular converter used. 93 | You may use `\yii2tech\html2pdf\Manager::$converter` property for the converter setup. 94 | 95 | Several built-in converters are provided: 96 | 97 | - [yii2tech\html2pdf\converters\Wkhtmltopdf](src/converters/Wkhtmltopdf.php) - uses [wkhtmltopdf](http://wkhtmltopdf.org/) utility for the conversion. 98 | - [yii2tech\html2pdf\converters\Dompdf](src/converters/Dompdf.php) - uses [dompdf](https://github.com/dompdf/dompdf) library for the conversion. 99 | - [yii2tech\html2pdf\converters\Mpdf](src/converters/Mpdf.php) - uses [mpdf](https://github.com/mpdf/mpdf) library for the conversion. 100 | - [yii2tech\html2pdf\converters\Tcpdf](src/converters/Tcpdf.php) - uses [TCPDF](http://www.tcpdf.org) library for the conversion. 101 | - [yii2tech\html2pdf\converters\Callback](src/converters/Callback.php) - uses a custom PHP callback for the conversion. 102 | 103 | **Heads up!** Most of the provided converters require additional software been installed, which is not provided by 104 | his extension by default. You'll have to install it manually, once you decide, which converter you will use. 105 | Please refer to the particular converter class for more details. 106 | 107 | You may specify conversion options via second argument of the `convert()` or `convertFile()` method: 108 | 109 | ```php 110 | html2pdf 113 | ->convertFile('/path/to/source.html', ['pageSize' => 'A4']) 114 | ->saveAs('/path/to/output.pdf'); 115 | ``` 116 | 117 | You may setup default conversion options at the `\yii2tech\html2pdf\Manager` level: 118 | 119 | ```php 120 | [ 124 | 'html2pdf' => [ 125 | 'class' => 'yii2tech\html2pdf\Manager', 126 | 'viewPath' => '@app/pdf', 127 | 'converter' => [ 128 | 'class' => 'yii2tech\html2pdf\converters\Wkhtmltopdf', 129 | 'defaultOptions' => [ 130 | 'pageSize' => 'A4' 131 | ], 132 | ] 133 | ], 134 | ], 135 | ... 136 | ]; 137 | ``` 138 | 139 | > Note: the actual list of available conversion options depends on the particular converter to be used. 140 | 141 | 142 | ## Template usage 143 | 144 | You may create PDF files rendering PHP templates (view files), which composes HTML output. 145 | Such files are processed as regular view files, allowing passing params and layout wrapping. 146 | Method `\yii2tech\html2pdf\Manager::render()` used for this: 147 | 148 | ```php 149 | html2pdf 152 | ->render('invoice', ['user' => Yii::$app->user->identity]) 153 | ->saveAs('/path/to/output.pdf'); 154 | ``` 155 | 156 | You may use a shared layout for the templates, which can be setup via `\yii2tech\html2pdf\Manager::$layout`. 157 | 158 | During each rendering view is working in context of `\yii2tech\html2pdf\Template` object, which can be used to adjust 159 | layout or PDF conversion options inside view file: 160 | 161 | ```php 162 | context; 167 | 168 | $context->layout = 'layouts/payment'; // use specific layout for this template 169 | 170 | // specify particular PDF conversion for this template: 171 | $context->pdfOptions = [ 172 | 'pageSize' => 'A4', 173 | // ... 174 | ]; 175 | ?> 176 |

Invoice

177 |

For: name ?>

178 | ... 179 | ``` 180 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yii2tech/html2pdf", 3 | "description": "Provides support for HTML to PDF and PHP to PDF conversion", 4 | "keywords": ["yii2", "html2pdf", "php2pdf", "pdf", "convert", "conversion", "template"], 5 | "type": "yii2-extension", 6 | "license": "BSD-3-Clause", 7 | "support": { 8 | "issues": "https://github.com/yii2tech/html2pdf/issues", 9 | "forum": "http://www.yiiframework.com/forum/", 10 | "wiki": "https://github.com/yii2tech/html2pdf/wiki", 11 | "source": "https://github.com/yii2tech/html2pdf" 12 | }, 13 | "authors": [ 14 | { 15 | "name": "Paul Klimov", 16 | "email": "klimov.paul@gmail.com" 17 | } 18 | ], 19 | "require": { 20 | "yiisoft/yii2": "~2.0.14" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "4.8.27|^5.0|^6.0" 24 | }, 25 | "repositories": [ 26 | { 27 | "type": "composer", 28 | "url": "https://asset-packagist.org" 29 | } 30 | ], 31 | "autoload": { 32 | "psr-4": { "yii2tech\\html2pdf\\": "src" } 33 | }, 34 | "extra": { 35 | "branch-alias": { 36 | "dev-master": "1.0.x-dev" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/BaseConverter.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 1.0 17 | */ 18 | abstract class BaseConverter extends Component implements ConverterInterface 19 | { 20 | /** 21 | * @var array list of the default conversion options. 22 | * These options will be merged with the ones specified for particular conversion. 23 | */ 24 | public $defaultOptions = []; 25 | 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function convert($html, $outputFileName, $options = []) 31 | { 32 | $options = array_merge($this->defaultOptions, $options); 33 | $this->convertInternal($html, $outputFileName, $options); 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | public function convertFile($sourceFileName, $outputFileName, $options = []) 40 | { 41 | $this->convert(file_get_contents($sourceFileName), $outputFileName, $options); 42 | } 43 | 44 | /** 45 | * Converts given HTML content into PDF file. 46 | * @param string $html source HTML content. 47 | * @param string $outputFileName output PDF file name. 48 | * @param array $options conversion options. 49 | */ 50 | abstract protected function convertInternal($html, $outputFileName, $options); 51 | } -------------------------------------------------------------------------------- /src/ConverterInterface.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 1.0 15 | */ 16 | interface ConverterInterface 17 | { 18 | /** 19 | * Creates a PDF file from given HTML content. 20 | * @param string $html source HTML content. 21 | * @param string $outputFileName output PDF file name. 22 | * @param array $options conversion options. 23 | */ 24 | public function convert($html, $outputFileName, $options = []); 25 | 26 | /** 27 | * Converts given HTML file into PDF file. 28 | * @param string $sourceFileName source HTML file name. 29 | * @param string $outputFileName output PDF file name. 30 | * @param array $options conversion options. 31 | */ 32 | public function convertFile($sourceFileName, $outputFileName, $options = []); 33 | } -------------------------------------------------------------------------------- /src/Manager.php: -------------------------------------------------------------------------------- 1 | [ 32 | * 'html2pdf' => [ 33 | * 'class' => 'yii2tech\html2pdf\Manager', 34 | * 'viewPath' => '@app/pdf', 35 | * 'converter' => 'wkhtmltopdf', 36 | * ], 37 | * ], 38 | * // ... 39 | * ]; 40 | * ``` 41 | * 42 | * @see ConverterInterface 43 | * @see Template 44 | * 45 | * @property string $viewPath path ro the directory containing view files. 46 | * @property ConverterInterface|array|string $converter converter instance or its configuration. 47 | * @property View $view View instance. Note that the type of this property differs in getter and setter. See 48 | * {@see getView()} and {@see setView()} for details. 49 | * 50 | * @author Paul Klimov 51 | * @since 1.0 52 | */ 53 | class Manager extends Component 54 | { 55 | /** 56 | * @var string|bool layout view name. This is the layout used to render HTML source. 57 | * The property can take the following values: 58 | * 59 | * - a relative view name: a view file relative to {@see viewPath}, e.g., 'layouts/main'. 60 | * - a path alias: an absolute view file path specified as a path alias, e.g., '@app/pdf/layout'. 61 | * - a bool false: the layout is disabled. 62 | */ 63 | public $layout = 'layouts/main'; 64 | 65 | /** 66 | * @var string path to the directory containing view files. 67 | */ 68 | private $_viewPath; 69 | /** 70 | * @var \yii\base\View|array view instance or its array configuration. 71 | */ 72 | private $_view = []; 73 | /** 74 | * @var ConverterInterface|array|string converter instance or its configuration. 75 | */ 76 | private $_converter = 'wkhtmltopdf'; 77 | 78 | 79 | /** 80 | * Returns the directory containing view files. 81 | * @return string the view path that may be prefixed to a relative view name. 82 | */ 83 | public function getViewPath() 84 | { 85 | if ($this->_viewPath === null) { 86 | $this->setViewPath('@app/pdf'); 87 | } 88 | return $this->_viewPath; 89 | } 90 | 91 | /** 92 | * Sets the directory that contains the view files. 93 | * @param string $path the root directory of view files. 94 | */ 95 | public function setViewPath($path) 96 | { 97 | $this->_viewPath = Yii::getAlias($path); 98 | } 99 | 100 | /** 101 | * @param array|View $view view instance or its array configuration that will be used to 102 | * render message bodies. 103 | * @throws InvalidConfigException on invalid argument. 104 | */ 105 | public function setView($view) 106 | { 107 | if (!is_array($view) && !is_object($view)) { 108 | throw new InvalidConfigException('"' . get_class($this) . '::$view" should be either object or configuration array, "' . gettype($view) . '" given.'); 109 | } 110 | $this->_view = $view; 111 | } 112 | 113 | /** 114 | * @return View view instance. 115 | */ 116 | public function getView() 117 | { 118 | if (!is_object($this->_view)) { 119 | $this->_view = $this->createView($this->_view); 120 | } 121 | return $this->_view; 122 | } 123 | 124 | /** 125 | * @return ConverterInterface converter instance 126 | */ 127 | public function getConverter() 128 | { 129 | if (!is_object($this->_converter)) { 130 | $this->_converter = $this->createConverter($this->_converter); 131 | } 132 | return $this->_converter; 133 | } 134 | 135 | /** 136 | * @param ConverterInterface|array|string $converter 137 | */ 138 | public function setConverter($converter) 139 | { 140 | $this->_converter = $converter; 141 | } 142 | 143 | /** 144 | * Creates view instance from given configuration. 145 | * @param array $config view configuration. 146 | * @return View view instance. 147 | */ 148 | protected function createView(array $config) 149 | { 150 | if (!array_key_exists('class', $config)) { 151 | $config['class'] = View::className(); 152 | } 153 | return Yii::createObject($config); 154 | } 155 | 156 | /** 157 | * Creates converter instance from given configuration. 158 | * @param mixed $config converter configuration. 159 | * @return View view instance. 160 | */ 161 | protected function createConverter($config) 162 | { 163 | if (is_string($config)) { 164 | switch (strtolower($config)) { 165 | case 'wkhtmltopdf': 166 | $config = [ 167 | 'class' => Wkhtmltopdf::className() 168 | ]; 169 | break; 170 | case 'mpdf': 171 | $config = [ 172 | 'class' => Mpdf::className() 173 | ]; 174 | break; 175 | case 'tcpdf': 176 | $config = [ 177 | 'class' => Tcpdf::className() 178 | ]; 179 | break; 180 | case 'dompdf': 181 | $config = [ 182 | 'class' => Dompdf::className() 183 | ]; 184 | break; 185 | } 186 | } elseif (is_array($config)) { 187 | if (!array_key_exists('class', $config)) { 188 | $config['class'] = Callback::className(); 189 | } 190 | } 191 | 192 | return Instance::ensure($config, 'yii2tech\html2pdf\ConverterInterface'); 193 | } 194 | 195 | /** 196 | * Renders the specified view with optional parameters and layout and converts rendering result into the PDF file. 197 | * @param string $view the view name or the path alias of the view file. 198 | * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file. 199 | * @return TempFile converted PDF file representation. 200 | */ 201 | public function render($view, $params = []) 202 | { 203 | $template = new Template([ 204 | 'view' => $this->getView(), 205 | 'viewPath' => $this->getViewPath(), 206 | 'viewName' => $view, 207 | 'layout' => $this->layout, 208 | ]); 209 | $htmlContent = $template->render($params); 210 | 211 | return $this->convert($htmlContent, $template->pdfOptions); 212 | } 213 | 214 | /** 215 | * Converts HTML content into PDF file. 216 | * @param string $html source HTML content. 217 | * @param array $options conversion options. 218 | * @return TempFile converted PDF file representation. 219 | * @throws Exception on failure. 220 | */ 221 | public function convert($html, $options = []) 222 | { 223 | $outputFileName = $this->generateTempFileName('pdf'); 224 | $this->getConverter()->convert($html, $outputFileName, $options); 225 | if (!file_exists($outputFileName)) { 226 | throw new Exception('HTML to PDF conversion failed: no output file created.'); 227 | } 228 | return new TempFile(['name' => $outputFileName]); 229 | } 230 | 231 | /** 232 | * Converts HTML file into PDF file. 233 | * @param string $fileName source file name. 234 | * @param array $options conversion options. 235 | * @return TempFile converted PDF file representation. 236 | * @throws Exception on failure. 237 | */ 238 | public function convertFile($fileName, $options = []) 239 | { 240 | $outputFileName = $this->generateTempFileName('pdf'); 241 | $this->getConverter()->convertFile($fileName, $outputFileName, $options); 242 | if (!file_exists($outputFileName)) { 243 | throw new Exception('HTML to PDF conversion failed: no output file created.'); 244 | } 245 | return new TempFile(['name' => $outputFileName]); 246 | } 247 | 248 | /** 249 | * Generates temporary file name. 250 | * @param string $extension file extension. 251 | * @return string full path to temp file. 252 | */ 253 | protected function generateTempFileName($extension = 'tmp') 254 | { 255 | $tempPath = Yii::getAlias('@runtime/html2pdf'); 256 | FileHelper::createDirectory($tempPath); 257 | 258 | do { 259 | $fileName = $tempPath . DIRECTORY_SEPARATOR . uniqid('html2pdf', true) . '.' . $extension; 260 | } while (file_exists($fileName)); 261 | 262 | return $fileName; 263 | } 264 | } -------------------------------------------------------------------------------- /src/TempFile.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class TempFile extends BaseObject 22 | { 23 | /** 24 | * @var string the path of the file. 25 | * Note, this is a temporary file which will be automatically deleted on object destruction. 26 | */ 27 | public $name; 28 | 29 | 30 | /** 31 | * Destructor. 32 | * Removes associated temporary file if it exists. 33 | */ 34 | public function __destruct() 35 | { 36 | $this->delete(); 37 | } 38 | 39 | /** 40 | * Copies this file into another location. 41 | * @param string $destinationFileName destination file name (may content path alias). 42 | * @return bool whether operation was successful. 43 | */ 44 | public function copy($destinationFileName) 45 | { 46 | $destinationFileName = $this->prepareDestinationFileName($destinationFileName); 47 | return copy($this->name, $destinationFileName); 48 | } 49 | 50 | /** 51 | * Moves this file into another location. 52 | * @param string $destinationFileName destination file name (may content path alias). 53 | * @return bool whether operation was successful. 54 | */ 55 | public function move($destinationFileName) 56 | { 57 | $destinationFileName = $this->prepareDestinationFileName($destinationFileName); 58 | $result = rename($this->name, $destinationFileName); 59 | $this->name = null; 60 | return $result; 61 | } 62 | 63 | /** 64 | * Saves this file. 65 | * @param string $file destination file name (may content path alias). 66 | * @param bool $deleteTempFile whether to delete associated temp file or not. 67 | * @return bool whether operation was successful. 68 | */ 69 | public function saveAs($file, $deleteTempFile = true) 70 | { 71 | if ($deleteTempFile) { 72 | return $this->move($file); 73 | } 74 | return $this->copy($file); 75 | } 76 | 77 | /** 78 | * Prepares raw destination file name for the file copy/move operation: 79 | * resolves path alias and creates missing directories. 80 | * @param string $destinationFileName destination file name 81 | * @return string real destination file name 82 | */ 83 | protected function prepareDestinationFileName($destinationFileName) 84 | { 85 | $destinationFileName = Yii::getAlias($destinationFileName); 86 | $destinationPath = dirname($destinationFileName); 87 | FileHelper::createDirectory($destinationPath); 88 | return $destinationFileName; 89 | } 90 | 91 | /** 92 | * Deletes associated temporary file. 93 | * @return bool whether file has been deleted. 94 | */ 95 | public function delete() 96 | { 97 | if (!empty($this->name) && file_exists($this->name)) { 98 | $result = FileHelper::unlink($this->name); 99 | $this->name = null; 100 | return $result; 101 | } 102 | return false; 103 | } 104 | 105 | /** 106 | * Gets content of this file. 107 | * @return string file content. 108 | * @since 1.0.4 109 | */ 110 | public function getContent() 111 | { 112 | return file_get_contents($this->name); 113 | } 114 | 115 | /** 116 | * Prepares response for sending a file to the browser. 117 | * Note: this method works only while running web application. 118 | * @param string $name the file name shown to the user. If null, it will be determined from {@see name}. 119 | * @param array $options additional options for sending the file. See {@see \yii\web\Response::sendFile()} for more details. 120 | * @return \yii\web\Response application response instance. 121 | */ 122 | public function send($name = null, $options = []) 123 | { 124 | return Yii::$app->getResponse()->sendFile($this->name, $name, $options); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Template.php: -------------------------------------------------------------------------------- 1 | 20 | * @since 1.0 21 | */ 22 | class Template extends BaseObject implements ViewContextInterface 23 | { 24 | /** 25 | * @var string path ro the directory containing view files. 26 | */ 27 | public $viewPath; 28 | /** 29 | * @var string the view name for his template. 30 | */ 31 | public $viewName; 32 | /** 33 | * @var string|bool layout view name. This is the layout used to render HTML source. 34 | * The property can take the following values: 35 | * 36 | * - a relative view name: a view file relative to {@see viewPath}, e.g., 'layouts/main'. 37 | * - a path alias: an absolute view file path specified as a path alias, e.g., '@app/pdf/layout'. 38 | * - a bool false: the layout is disabled. 39 | */ 40 | public $layout; 41 | /** 42 | * @var \yii\base\View view instance, which should be used for rendering. 43 | */ 44 | public $view; 45 | /** 46 | * @var array list of HTML to PDF conversion options, which should be applied while converting 47 | * template rendering result into PDF. 48 | */ 49 | public $pdfOptions = []; 50 | 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function getViewPath() 56 | { 57 | return $this->viewPath; 58 | } 59 | 60 | /** 61 | * Renders this template. 62 | * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file. 63 | * @return string the rendering result. 64 | */ 65 | public function render($params = []) 66 | { 67 | $output = $this->view->render($this->viewName, $params, $this); 68 | if ($this->layout !== false) { 69 | return $this->view->render($this->layout, ['content' => $output], $this); 70 | } else { 71 | return $output; 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/converters/Callback.php: -------------------------------------------------------------------------------- 1 | 19 | * @since 1.0 20 | */ 21 | class Callback extends BaseConverter 22 | { 23 | /** 24 | * @var callable PHP callback, which should be called in order to perform conversion of HTML content into a PDF file. 25 | * Callback should have following signature: 26 | * 27 | * ```php 28 | * function (string $htmlContent, string $outputFileName, array $options) {...} 29 | * ``` 30 | * 31 | * This field can be omitted in case {@see fileCallback} is set. 32 | */ 33 | public $callback; 34 | /** 35 | * @var callable PHP callback, which should be called in order to perform conversion of HTML file into a PDF file. 36 | * Callback should have following signature: 37 | * 38 | * ```php 39 | * function (string $sourceFileName, string $outputFileName, array $options) {...} 40 | * ``` 41 | * 42 | * This field can be omitted in case {@see callback} is set. 43 | */ 44 | public $fileCallback; 45 | 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public function convertFile($sourceFileName, $outputFileName, $options = []) 51 | { 52 | if ($this->fileCallback === null) { 53 | parent::convertFile($sourceFileName, $outputFileName, $options); 54 | } else { 55 | $options = array_merge($this->defaultOptions, $options); 56 | call_user_func($this->fileCallback, $sourceFileName, $outputFileName, $options); 57 | } 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | protected function convertInternal($html, $outputFileName, $options) 64 | { 65 | if ($this->callback === null) { 66 | if ($this->fileCallback === null) { 67 | throw new InvalidConfigException("Either 'callback' or 'fileCallback' must be set."); 68 | } 69 | 70 | $tempPath = Yii::getAlias('@runtime/html2pdf'); 71 | FileHelper::createDirectory($tempPath); 72 | 73 | $sourceFileName = tempnam($tempPath, 'wkhtmltopdf'); 74 | file_put_contents($sourceFileName, $html); 75 | 76 | try { 77 | $this->convertFile($sourceFileName, $outputFileName, $options); 78 | } catch (\Exception $e) { 79 | unlink($sourceFileName); 80 | throw $e; 81 | } 82 | 83 | unlink($sourceFileName); 84 | } else { 85 | call_user_func($this->callback, $html, $outputFileName, $options); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/converters/Dompdf.php: -------------------------------------------------------------------------------- 1 | 25 | * @since 1.0 26 | */ 27 | class Dompdf extends BaseConverter 28 | { 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | protected function convertInternal($html, $outputFileName, $options) 34 | { 35 | $pageSize = ArrayHelper::remove($options, 'pageSize', 'A4'); 36 | $orientation = ArrayHelper::remove($options, 'orientation', 'portrait'); 37 | 38 | if (empty($options)) { 39 | $dompdfOptions = null; 40 | } else { 41 | $dompdfOptions = new \Dompdf\Options(); 42 | foreach ($options as $name => $value) { 43 | $dompdfOptions->set($name, $value); 44 | } 45 | } 46 | 47 | $dompdf = new \Dompdf\Dompdf($dompdfOptions); 48 | $dompdf->setPaper($pageSize, $orientation); 49 | 50 | $dompdf->loadHtml($html); 51 | $dompdf->render(); 52 | 53 | file_put_contents($outputFileName, $dompdf->output()); 54 | } 55 | } -------------------------------------------------------------------------------- /src/converters/Mpdf.php: -------------------------------------------------------------------------------- 1 | 27 | * @since 1.0 28 | */ 29 | class Mpdf extends BaseConverter 30 | { 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | protected function convertInternal($html, $outputFileName, $options) 35 | { 36 | $charset = ArrayHelper::remove($options, 'charset', Yii::$app->charset); 37 | $pageSize = ArrayHelper::remove($options, 'pageSize', 'A4'); 38 | 39 | if (class_exists('Mpdf\Mpdf')) { 40 | $config = [ 41 | 'mode' => $charset, 42 | 'format' => $pageSize, 43 | 'tempDir' => Yii::getAlias(ArrayHelper::remove($options, 'tempDir', '@runtime')), 44 | ]; 45 | 46 | if (isset($options['fontDir'])) { 47 | if (is_array($options['fontDir'])) { 48 | $config['fontDir'] = array_map(['Yii', 'getAlias'], $config['fontDir']); 49 | unset($options['fontDir']); 50 | } else { 51 | $options['fontDir'] = Yii::getAlias($options['fontDir']); 52 | } 53 | } 54 | 55 | $pdf = new \Mpdf\Mpdf($config); 56 | 57 | if (isset($options['fontDir'])) { 58 | $pdf->AddFontDirectory($options['fontDir']); 59 | unset($options['fontDir']); 60 | } 61 | } else { 62 | $pdf = new \mPDF($charset, $pageSize); 63 | } 64 | 65 | foreach ($options as $name => $value) { 66 | $setter = 'Set' . $name; 67 | if (method_exists($pdf, $setter)) { 68 | $pdf->$setter($value); 69 | } else { 70 | $pdf->$name = $value; 71 | } 72 | } 73 | 74 | $pdf->WriteHTML($html); 75 | $pdf->Output($outputFileName, 'F'); 76 | } 77 | } -------------------------------------------------------------------------------- /src/converters/Tcpdf.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 1.0 25 | */ 26 | class Tcpdf extends BaseConverter 27 | { 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | protected function convertInternal($html, $outputFileName, $options) 32 | { 33 | $charset = ArrayHelper::remove($options, 'charset', Yii::$app->charset); 34 | $pageSize = ArrayHelper::remove($options, 'pageSize', 'A4'); 35 | $orientation = ucfirst(ArrayHelper::remove($options, 'orientation', 'P')); 36 | $unit = ArrayHelper::remove($options, 'unit', 'mm'); 37 | 38 | $pdf = new \TCPDF($orientation, $unit, $pageSize, true, $charset, false); 39 | 40 | // set auto page breaks 41 | $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); 42 | 43 | foreach ($options as $name => $value) { 44 | $setter = 'Set' . $name; 45 | if (method_exists($pdf, $setter)) { 46 | $pdf->$setter($value); 47 | } else { 48 | $pdf->$name = $value; 49 | } 50 | } 51 | 52 | // add a page 53 | $pdf->AddPage(); 54 | 55 | $pdf->WriteHTML($html); 56 | $pdf->Output($outputFileName, 'F'); 57 | } 58 | } -------------------------------------------------------------------------------- /src/converters/Wkhtmltopdf.php: -------------------------------------------------------------------------------- 1 | 39 | * @since 1.0 40 | */ 41 | class Wkhtmltopdf extends BaseConverter 42 | { 43 | /** 44 | * @var string path to the 'wkhtmltopdf' command, for example: '/usr/local/bin/wkhtmltopdf'. 45 | * Since 1.0.7 Yii alias can be used here, for example: '@app/bin/wkhtmltopdf'. 46 | * Default is 'wkhtmltopdf' assuming 'wkhtmltopdf' command is available in OS shell. 47 | */ 48 | public $binPath = 'wkhtmltopdf'; 49 | 50 | /** 51 | * @var string[] list of created temporary file names. 52 | * @since 1.0.3 53 | */ 54 | private $tmpFiles = []; 55 | 56 | 57 | /** 58 | * Destructor. 59 | * Ensures temporary files are deleted. 60 | * @since 1.0.3 61 | */ 62 | public function __destruct() 63 | { 64 | $this->clearTmpFiles(); 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function convertFile($sourceFileName, $outputFileName, $options = []) 71 | { 72 | $options = array_merge($this->defaultOptions, $options); 73 | $this->convertFileInternal($sourceFileName, $outputFileName, $options); 74 | } 75 | 76 | /** 77 | * Converts given HTML file into PDF file. 78 | * @param string $sourceFileName source HTML file. 79 | * @param string $outputFileName output PDF file name. 80 | * @param array $options conversion options. 81 | * @throws Exception on failure. 82 | */ 83 | protected function convertFileInternal($sourceFileName, $outputFileName, $options) 84 | { 85 | try { 86 | $command = Yii::getAlias($this->binPath); 87 | foreach ($this->normalizeOptions($options) as $name => $value) { 88 | $command .= $this->buildCommandOption($name, $value); 89 | } 90 | $command .= ' ' . escapeshellarg($sourceFileName) . ' ' . escapeshellarg($outputFileName); 91 | $command .= ' 2>&1'; 92 | 93 | $outputLines = []; 94 | exec($command, $outputLines, $exitCode); 95 | 96 | if ($exitCode !== 0) { 97 | throw new Exception("Unable to convert file '{$sourceFileName}': " . implode("\n", $outputLines)); 98 | } 99 | } catch (\Exception $e) { 100 | $this->clearTmpFiles(); 101 | throw $e; 102 | } catch (\Throwable $e) { 103 | $this->clearTmpFiles(); 104 | throw $e; 105 | } 106 | 107 | $this->clearTmpFiles(); 108 | } 109 | 110 | /** 111 | * {@inheritdoc} 112 | */ 113 | protected function convertInternal($html, $outputFileName, $options) 114 | { 115 | $sourceFileName = $this->createTmpFile($html, 'html'); // enforce '.html' extension to avoid 'Failed loading page' error 116 | $this->convertFileInternal($sourceFileName, $outputFileName, $options); 117 | } 118 | 119 | /** 120 | * Normalizes raw conversion options for the shell command composition. 121 | * @param array $options raw conversion options 122 | * @return array normalized options. 123 | */ 124 | protected function normalizeOptions($options) 125 | { 126 | $result = []; 127 | 128 | foreach ($options as $name => $value) { 129 | if (is_null($value) || $value === false) { 130 | continue; 131 | } 132 | $normalizedName = Inflector::camel2id($name); 133 | $result[$normalizedName] = $value; 134 | } 135 | 136 | $fileOptions = [ 137 | 'header-html', 138 | 'footer-html', 139 | 'cover', 140 | ]; 141 | foreach ($fileOptions as $fileOption) { 142 | $contentOption = $fileOption . '-content'; 143 | if (isset($result[$contentOption])) { 144 | // enforce '.html' extension to avoid 'Failed loading page' error 145 | $result[$fileOption] = $this->createTmpFile($result[$contentOption], 'html'); 146 | unset($result[$contentOption]); 147 | } 148 | } 149 | 150 | // make sure 'toc' and 'cover' options to be last, so global options will not mix with them 151 | uksort($result, function ($a, $b) { 152 | if ($a === 'toc') { 153 | return 1; 154 | } 155 | if ($b === 'toc') { 156 | return -1; 157 | } 158 | if ($a === 'cover') { 159 | return 1; 160 | } 161 | if ($b === 'cover') { 162 | return -1; 163 | } 164 | 165 | return 0; 166 | }); 167 | 168 | return $result; 169 | } 170 | 171 | /** 172 | * Builds option for the shell command composition. 173 | * @param string $name option name. 174 | * @param mixed $value option value. 175 | * @return string option shell representation. 176 | * @since 1.0.2 177 | */ 178 | protected function buildCommandOption($name, $value) 179 | { 180 | $prefix = '--'; 181 | if (in_array($name, ['toc', 'cover'])) { // Don't add '--' in these options 182 | $prefix = ''; 183 | } 184 | 185 | $option = ' ' . $prefix . $name; 186 | 187 | if ($value === true) { 188 | return $option; 189 | } 190 | 191 | if (is_array($value)) { // Support repeatable options 192 | $repeatableOptions = []; 193 | foreach ($value as $k => $v) { 194 | $repeatableOptions[] = $option . (is_string($k) ? ' ' . escapeshellarg($k) : '') . ' ' .escapeshellarg($v); 195 | } 196 | return implode(' ', $repeatableOptions); 197 | } 198 | 199 | return $option . ' ' . escapeshellarg($value); 200 | } 201 | 202 | /** 203 | * Returns path to directory for the temporary files storage. 204 | * Directory will be created if it does not yet exist. 205 | * @return string file path. 206 | * @throws \yii\base\Exception if the directory could not be created. 207 | * @since 1.0.3 208 | */ 209 | protected function getTmpFilePath() 210 | { 211 | $tempPath = Yii::getAlias('@runtime/html2pdf'); 212 | FileHelper::createDirectory($tempPath); 213 | return $tempPath; 214 | } 215 | 216 | /** 217 | * @param string $content file content. 218 | * @param null $extension file extension to be enforced. 219 | * @return string generated file name. 220 | * @throws Exception on failure. 221 | * @since 1.0.3 222 | */ 223 | protected function createTmpFile($content, $extension = null) 224 | { 225 | $tempFileName = tempnam($this->getTmpFilePath(), 'wkhtmltopdf'); 226 | if ($tempFileName === false) { 227 | throw new Exception('Unable to create temporary file.'); 228 | } 229 | 230 | if ($extension !== null) { 231 | // sometimes enforcing of file extension, like '.html', is needed since 'wkhtmltopdf' is sensitive to it. 232 | $tempFileNameWithExtension = $tempFileName . '.' . $extension; 233 | rename($tempFileName, $tempFileNameWithExtension); 234 | $tempFileName = $tempFileNameWithExtension; 235 | } 236 | 237 | file_put_contents($tempFileName, $content); 238 | 239 | $this->tmpFiles[] = $tempFileName; 240 | return $tempFileName; 241 | } 242 | 243 | /** 244 | * Removes temporary files. 245 | * @since 1.0.3 246 | */ 247 | protected function clearTmpFiles() 248 | { 249 | foreach ($this->tmpFiles as $tmpFile) { 250 | if (file_exists($tmpFile)) { 251 | unlink($tmpFile); 252 | } 253 | } 254 | $this->tmpFiles = []; 255 | } 256 | } 257 | --------------------------------------------------------------------------------