/WkHtmlToPdf.php

http://github.com/mikehaertl/phpwkhtmltopdf · PHP · 287 lines · 134 code · 34 blank · 119 comment · 12 complexity · c2fe42f74616beca00ed7fca6d2d9d0a MD5 · raw file

  1. <?php
  2. /**
  3. * WkHtmlToPdf
  4. *
  5. * This class is a slim wrapper around wkhtmltopdf.
  6. *
  7. * It provides a simple and clean interface to ease PDF creation with wkhtmltopdf.
  8. * The wkhtmltopdf binary must be installed and working on your system. The static
  9. * binary is preferred but this class should also work with the non static version,
  10. * even though a lot of features will be missing.
  11. *
  12. * Basic use:
  13. *
  14. * $pdf = new WkHtmlToPdf;
  15. * $pdf->addPage('http://google.com');
  16. * $pdf->addPage('/home/joe/my.pdf');
  17. * $pdf->addCover('mycover.pdf');
  18. * $pdf->addToc();
  19. *
  20. * // Save the PDF
  21. * $pdf->saveAs('/tmp/new.pdf');
  22. *
  23. * // Send to client for inline display
  24. * $pdf->send();
  25. *
  26. * // Send to client as file download
  27. * $pdf->send('test.pdf');
  28. *
  29. * Setting options:
  30. *
  31. * $pdf = new WkHtmlToPdf($options); // Set global PDF options
  32. * $pdf->setOptions($options); // Set global PDF options (alternative)
  33. * $pdf->setPageOptions($options); // Set global default page options
  34. * $pdf->addPage($page, $options); // Set page options (overrides default page options)
  35. *
  36. * Example options:
  37. *
  38. * // See "wkhtmltopdf -H" for all available options
  39. * $options=array(
  40. * 'no-outline',
  41. * 'margin-top' =>0,
  42. * 'margin-right' =>0,
  43. * );
  44. *
  45. * Extra global options:
  46. *
  47. * bin: path to the wkhtmltopdf binary. Defaults to /usr/bin/wkhtmltopdf.
  48. * tmp: path to tmp directory. Defaults to PHP temp dir.
  49. *
  50. * Error handling:
  51. *
  52. * saveAs() and save() will return false on error. In this case the detailed error message
  53. * from wkhtmltopdf can be obtained through getError().
  54. *
  55. * @author Michael H?rtl <haertl.mike@gmail.com> (sponsored by PeoplePerHour.com)
  56. * @version 1.0.0
  57. * @license http://www.opensource.org/licenses/MIT
  58. */
  59. class WkHtmlToPdf
  60. {
  61. protected $bin='/usr/bin/wkhtmltopdf';
  62. protected $options=array();
  63. protected $pageOptions=array();
  64. protected $objects=array();
  65. protected $tmp;
  66. protected $tmpFile;
  67. protected $error;
  68. /**
  69. * @param array $options global options for wkhtmltopdf (optional)
  70. */
  71. public function __construct($options=array())
  72. {
  73. if($options!==array())
  74. $this->setOptions($options);
  75. }
  76. /**
  77. * Remove temporary PDF file when script completes
  78. */
  79. public function __destruct()
  80. {
  81. if($this->tmpFile!==null)
  82. unlink($this->tmpFile);
  83. }
  84. /**
  85. * Add a page object to the output
  86. *
  87. * @param string $input either a URL or a PDF filename
  88. * @param array $options optional options for this page
  89. */
  90. public function addPage($input,$options=array())
  91. {
  92. $options['input']=$input;
  93. $this->objects[]=array_merge($this->pageOptions,$options);
  94. }
  95. /**
  96. * Add a cover page object to the output
  97. *
  98. * @param string $input either a URL or a PDF filename
  99. * @param array $options optional options for this page
  100. */
  101. public function addCover($input,$options=array())
  102. {
  103. $options['input']="cover $input";
  104. $this->objects[]=array_merge($this->pageOptions,$options);
  105. }
  106. /**
  107. * Add a TOC object to the output
  108. *
  109. * @param string $input either a URL or a PDF filename
  110. * @param array $options optional options for this page
  111. */
  112. public function addToc($options=array())
  113. {
  114. $options['input']="toc";
  115. $this->objects[]=$options;
  116. }
  117. /**
  118. * Save the PDF to given filename (triggers PDF creation)
  119. *
  120. * @param string $filename to save PDF as
  121. * @return bool wether PDF was created successfully
  122. */
  123. public function saveAs($filename)
  124. {
  125. if(($pdfFile=$this->getPdfFilename())===false)
  126. return false;
  127. copy($pdfFile,$filename);
  128. return true;
  129. }
  130. /**
  131. * Send PDF to client, either inline or as download (triggers PDF creation)
  132. *
  133. * @param mixed $filename the filename to send. If empty, the PDF is streamed.
  134. * @return bool wether PDF was created successfully
  135. */
  136. public function send($filename=null)
  137. {
  138. if(($pdfFile=$this->getPdfFilename())===false)
  139. return false;
  140. header('Pragma: public');
  141. header('Expires: 0');
  142. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  143. header('Content-Type: application/pdf');
  144. header('Content-Transfer-Encoding: binary');
  145. header('Content-Length: '.filesize($pdfFile));
  146. if($filename!==null)
  147. header("Content-Disposition: attachment; filename=\"$filename\"");
  148. readfile($pdfFile);
  149. return true;
  150. }
  151. /**
  152. * Set global option(s)
  153. *
  154. * @param array $options list of global options to set as name/value pairs
  155. */
  156. public function setOptions($options)
  157. {
  158. foreach($options as $key=>$val)
  159. if($key==='bin')
  160. $this->bin=$val;
  161. elseif($key==='tmp')
  162. $this->tmp=$val;
  163. elseif(is_int($key))
  164. $this->options[]=$val;
  165. else
  166. $this->options[$key]=$val;
  167. }
  168. /**
  169. * @param array $options that should be applied to all pages as name/value pairs
  170. */
  171. public function setPageOptions($options=array())
  172. {
  173. $this->pageOptions=$options;
  174. }
  175. /**
  176. * @return mixed the detailled error message including the wkhtmltopdf command or null if none
  177. */
  178. public function getError()
  179. {
  180. return $this->error;
  181. }
  182. /**
  183. * @return mixed the temporary PDF filename or false on error (triggers PDf creation)
  184. */
  185. protected function getPdfFilename()
  186. {
  187. if($this->tmpFile===null)
  188. {
  189. if($this->tmp===null)
  190. $this->tmp=sys_get_temp_dir();
  191. $tmpFile=tempnam($this->tmp,'tmp_WkHtmlToPdf_');
  192. if($this->createPdf($tmpFile)===true)
  193. $this->tmpFile=$tmpFile;
  194. else
  195. return false;
  196. }
  197. return $this->tmpFile;
  198. }
  199. /**
  200. * @param string $filename the filename of the output file
  201. * @return string the wkhtmltopdf command string
  202. */
  203. protected function getCommand($filename)
  204. {
  205. $command=$this->bin;
  206. $command.=$this->renderOptions($this->options);
  207. foreach($this->objects as $object)
  208. {
  209. $command.=' '.$object['input'];
  210. unset($object['input']);
  211. $command.=$this->renderOptions($object);
  212. }
  213. return $command.' '.$filename;
  214. }
  215. /**
  216. * Create the temporary PDF file
  217. */
  218. protected function createPdf($fileName)
  219. {
  220. $command=$this->getCommand($fileName);
  221. // we use proc_open with pipes to fetch error output
  222. $descriptors=array(
  223. 1=>array('pipe','w'),
  224. 2=>array('pipe','w'),
  225. );
  226. $process=proc_open($command, $descriptors, $pipes);
  227. if(is_resource($process)) {
  228. $stdout=stream_get_contents($pipes[1]);
  229. $stderr=stream_get_contents($pipes[2]);
  230. fclose($pipes[1]);
  231. fclose($pipes[2]);
  232. $result=proc_close($process);
  233. if($result!==0)
  234. $this->error="Could not run command $command:\n$stderr";
  235. } else
  236. $this->error="Could not run command $command";
  237. return $this->error===null;
  238. }
  239. /**
  240. * @param array $options for a wkhtml, either global or for an object
  241. * @return string the string with options
  242. */
  243. protected function renderOptions($options)
  244. {
  245. $out='';
  246. foreach($options as $key=>$val)
  247. if(is_numeric($key))
  248. $out.=" --$val";
  249. else
  250. $out.=" --$key $val";
  251. return $out;
  252. }
  253. }