addPage('/home/joe/page.html'); * $pdf->addPage('....'); * $pdf->addPage('http://google.com'); * * // Add a cover (same sources as above are possible) * $pdf->addCover('mycover.html'); * * // Add a Table of contents * $pdf->addToc(); * * // Save the PDF * $pdf->saveAs('/tmp/new.pdf'); * * // ... or send to client for inline display * $pdf->send(); * * // ... or send to client as file download * $pdf->send('test.pdf'); * * * Setting options * --------------- * * The wkhtmltopdf binary knows different types of options (please see wkhtmltopdf -H): * * * global options (e.g. to set the document's DPI) * * page options (e.g. to supply a custom CSS file for a page) * * toc options (e.g. to set a TOC header) * * In addition this class also supports global page options: You can set default page options * that will be applied to every page you add. You can also override these defaults per page: * * $pdf = new WkHtmlToPdf($options); // Set global PDF options * $pdf->setOptions($options); // Set global PDF options (alternative) * $pdf->setPageOptions($options); // Set default page options * $pdf->addPage($page, $options); // Add page with options (overrides default page options) * $pdf->addCover($page, $options); // Add cover with options (overrides default page options) * $pdf->addToc($options); // Add TOC with options * * * Special global options * ---------------------- * * * bin: Path to the wkhtmltopdf binary. Defaults to /usr/bin/wkhtmltopdf. * * tmp: Path to tmp directory. Defaults to PHP temp dir. * * enableEscaping: Whether arguments to wkhtmltopdf should be escaped. Default is true. * * version9: Whether to use command line syntax for wkhtmltopdf < 0.10 * * * Error handling * -------------- * * saveAs() and send() will return false on error. In this case the detailed error message * from wkhtmltopdf can be obtained through getError(): * * if(!$pdf->send()) * throw new Exception('Could not create PDF: '.$pdf->getError()); * * * Note for Windows users * ---------------------- * * If you use double quotes (") or percent signs (%) as option values, they may get * converted to spaces. You can set `enableEscaping` to `false` in this case. But * then you have to take care of proper escaping yourself. In some cases it may be * neccessary to surround your argument values with extra double quotes. * * * @author Michael Härtl (sponsored by PeoplePerHour.com) * @version 1.1.6 * @license http://www.opensource.org/licenses/MIT */ class WkHtmlToPdf { // protected $bin = '/usr/bin/wkhtmltopdf'; // MC (10.08.2021) :: Activated after installation of package "wkhtmltopdf" // protected $bin = '../include/wkhtmltopdf'; // MC (earlier) :: Binary in votian include path directly, this solutions works protected $bin = 'wkhtmltopdf'; // MC (26.09.2025) :: Binary system call protected $enableEscaping = true; protected $version9 = false; protected $options = array(); protected $pageOptions = array(); protected $objects = array(); protected $tmp; protected $tmpFile; protected $tmpFiles = array(); protected $error; // Regular expression to detect HTML strings const REGEX_HTML = '/setOptions($options); } /** * Remove temporary PDF file and pages when script completes */ public function __destruct() { if($this->tmpFile!==null) unlink($this->tmpFile); foreach($this->tmpFiles as $tmp) unlink($tmp); } /** * Add a page object to the output * * @param string $input either a URL, a HTML string or a PDF/HTML filename * @param array $options optional options for this page */ public function addPage($input,$options=array()) { $options['input'] = preg_match(self::REGEX_HTML, $input) ? $this->createTmpFile($input) : $input; $this->objects[] = array_merge($this->pageOptions,$options); } /** * Add a cover page object to the output * * @param string $input either a URL or a PDF filename * @param array $options optional options for this page */ public function addCover($input,$options=array()) { $options['input'] = ($this->version9 ? '--' : '')."cover $input"; $this->objects[] = array_merge($this->pageOptions,$options); } /** * Add a TOC object to the output * * @param array $options optional options for the table of contents */ public function addToc($options=array()) { $options['input'] = ($this->version9 ? '--' : '')."toc"; $this->objects[] = $options; } /** * Save the PDF to given filename (triggers PDF creation) * * @param string $filename to save PDF as * @return bool whether PDF was created successfully */ public function saveAs($filename) { if(($pdfFile = $this->getPdfFilename())===false) return false; copy($pdfFile,$filename); return true; } /** * Send PDF to client, either inline or as download (triggers PDF creation) * * @param mixed $filename the filename to send. If empty, the PDF is streamed. * @return bool whether PDF was created successfully */ public function send($filename=null) { if(($pdfFile = $this->getPdfFilename())===false) return false; if (ob_get_level()) { ob_end_clean(); } header('Content-Type: application/pdf'); header('Content-Disposition: inline; filename="' . basename($filename) . '"'); header('Content-Length: ' . filesize($pdfFile)); header('Cache-Control: private, max-age=0, must-revalidate'); header('Pragma: public'); if ($filename !== null) header("Content-Disposition: attachment; filename=\"$filename\""); readfile($filename); exit; return true; } /** * Set global option(s) * * @param array $options list of global options to set as name/value pairs */ public function setOptions($options) { foreach($options as $key=>$val) if($key==='bin') $this->bin = $val; elseif($key==='tmp') $this->tmp = $val; elseif($key==='enableEscaping') $this->enableEscaping = (bool)$val; elseif($key==='version9') $this->version9 = (bool)$val; elseif(is_int($key)) $this->options[] = $val; else $this->options[$key] = $val; } /** * @param array $options that should be applied to all pages as name/value pairs */ public function setPageOptions($options=array()) { $this->pageOptions = $options; } /** * @return mixed the detailled error message including the wkhtmltopdf command or null if none */ public function getError() { return $this->error; } /** * @return string path to temp directory */ public function getTmpDir() { if($this->tmp===null) $this->tmp = sys_get_temp_dir(); return $this->tmp; } /** * @param string $filename the filename of the output file * @return string the wkhtmltopdf command string */ public function getCommand($filename) { $command = $this->enableEscaping ? escapeshellarg($this->bin) : $this->bin; $command .= $this->renderOptions($this->options); foreach($this->objects as $object) { $command .= ' '.$object['input']; unset($object['input']); $command .= $this->renderOptions($object); } return $command.' '.$filename; } /** * @return mixed the temporary PDF filename or false on error (triggers PDf creation) */ protected function getPdfFilename() { if($this->tmpFile===null) { $tmpFile = tempnam($this->getTmpDir(),'tmp_WkHtmlToPdf_'); if($this->createPdf($tmpFile)===true) $this->tmpFile = $tmpFile; else return false; } return $this->tmpFile; } /** * Create the temporary PDF file */ protected function createPdf($fileName) { $command = $this->getCommand($fileName); // we use proc_open with pipes to fetch error output $descriptors = array( 2 => array('pipe','w'), ); $process = proc_open($command, $descriptors, $pipes, null, null, array('bypass_shell'=>true)); if(is_resource($process)) { $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]); $result = proc_close($process); if($result!==0) { if (!file_exists($fileName) || filesize($fileName)===0) $this->error = "Could not run command $command:\n$stderr"; else $this->error = "Warning: an error occured while creating the PDF.\n$stderr"; } } else $this->error = "Could not run command $command"; return $this->error===null; } /** * Create a tmp file with given content * * @param string $content the file content * @return string the path to the created file */ protected function createTmpFile($content) { $tmpFile = tempnam($this->getTmpDir(),'tmp_WkHtmlToPdf_'); rename($tmpFile, ($tmpFile.='.html')); file_put_contents($tmpFile, $content); $this->tmpFiles[] = $tmpFile; return $tmpFile; } /** * @param array $options for a wkhtml, either global or for an object * @return string the string with options */ protected function renderOptions($options) { $out = ''; foreach($options as $key=>$val) if(is_numeric($key)) $out .= " --$val"; else $out .= " --$key ".($this->enableEscaping ? escapeshellarg($val) : $val); return $out; } }