Skip to content

Commit 742e3bf

Browse files
committed
Implement feature to render a PDF page from string
1 parent 9d2c07d commit 742e3bf

File tree

1 file changed

+108
-62
lines changed

1 file changed

+108
-62
lines changed

WkHtmlToPdf.php

Lines changed: 108 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,66 +9,85 @@
99
* binary is preferred but this class should also work with the non static version,
1010
* even though a lot of features will be missing.
1111
*
12-
* Basic use:
12+
* Basic use
13+
* ---------
1314
*
1415
* $pdf = new WkHtmlToPdf;
16+
*
17+
* // Add a HTML file, a HTML string, a page from URL or a PDF file
18+
* $pdf->addPage('/home/joe/page.html');
19+
* $pdf->addPage('<html>....</html>');
1520
* $pdf->addPage('http://google.com');
1621
* $pdf->addPage('/home/joe/my.pdf');
22+
*
23+
* // Add a cover (same sources as above are possible)
1724
* $pdf->addCover('mycover.pdf');
25+
*
26+
* // Add a Table of contents
1827
* $pdf->addToc();
1928
*
20-
* // Save the PDF
29+
* // Save the PDF, or ...
2130
* $pdf->saveAs('/tmp/new.pdf');
2231
*
23-
* // Send to client for inline display
32+
* // ... send to client for inline display or ...
2433
* $pdf->send();
2534
*
26-
* // Send to client as file download
35+
* // ... send to client as file download
2736
* $pdf->send('test.pdf');
2837
*
29-
* Setting options:
38+
*
39+
* Setting options
40+
* ---------------
41+
*
42+
* The wkhtmltopdf binary has some global options (e.g. to set the document's DPI) and options
43+
* for each PDF page (e.g. to supply a custom CSS file). Please see "wkhtmltopdf -H" to get a
44+
* list of all available options.
45+
*
46+
* In addition this class also supports global page options: You can set default page options
47+
* that will be applied to every page you add. You can also override these defaults per page:
3048
*
3149
* $pdf = new WkHtmlToPdf($options); // Set global PDF options
3250
* $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)
51+
* $pdf->setPageOptions($options); // Set default page options
52+
* $pdf->addPage($page, $options); // Add page with options (overrides default page options)
3553
*
36-
* Example options:
3754
*
38-
* // See "wkhtmltopdf -H" for all available options
39-
* $options=array(
40-
* 'no-outline',
41-
* 'margin-top' =>0,
42-
* 'margin-right' =>0,
43-
* );
55+
* Special global options
56+
* ----------------------
4457
*
45-
* Extra global options:
58+
* You can use these special global options to set up some file paths:
4659
*
4760
* bin: path to the wkhtmltopdf binary. Defaults to /usr/bin/wkhtmltopdf.
4861
* tmp: path to tmp directory. Defaults to PHP temp dir.
4962
*
50-
* Error handling:
5163
*
52-
* saveAs() and save() will return false on error. In this case the detailed error message
53-
* from wkhtmltopdf can be obtained through getError().
64+
* Error handling
65+
* --------------
66+
*
67+
* saveAs() and save() will return false on error. In this case the detailed error message
68+
* from wkhtmltopdf can be obtained through getError().
5469
*
5570
* @author Michael Härtl <haertl.mike@gmail.com> (sponsored by PeoplePerHour.com)
56-
* @version 1.0.0
71+
* @version 1.1.0
5772
* @license http://www.opensource.org/licenses/MIT
5873
*/
5974
class WkHtmlToPdf
6075
{
61-
protected $bin='/usr/bin/wkhtmltopdf';
76+
protected $bin = '/usr/bin/wkhtmltopdf';
6277

63-
protected $options=array();
64-
protected $pageOptions=array();
65-
protected $objects=array();
78+
protected $options = array();
79+
protected $pageOptions = array();
80+
protected $objects = array();
6681

6782
protected $tmp;
6883
protected $tmpFile;
84+
protected $tmpFiles = array();
6985

7086
protected $error;
7187

88+
// Regular expression to detect HTML strings
89+
const REGEX_HTML = '/<html>/i';
90+
7291
/**
7392
* @param array $options global options for wkhtmltopdf (optional)
7493
*/
@@ -79,24 +98,27 @@ public function __construct($options=array())
7998
}
8099

81100
/**
82-
* Remove temporary PDF file when script completes
101+
* Remove temporary PDF file and pages when script completes
83102
*/
84103
public function __destruct()
85104
{
86105
if($this->tmpFile!==null)
87106
unlink($this->tmpFile);
107+
108+
foreach($this->tmpFiles as $tmp)
109+
unlink($tmp);
88110
}
89111

90112
/**
91113
* Add a page object to the output
92114
*
93-
* @param string $input either a URL or a PDF filename
115+
* @param string $input either a URL, a HTML string or a PDF/HTML filename
94116
* @param array $options optional options for this page
95117
*/
96118
public function addPage($input,$options=array())
97119
{
98-
$options['input']=$input;
99-
$this->objects[]=array_merge($this->pageOptions,$options);
120+
$options['input'] = preg_match(self::REGEX_HTML, $input) ? $this->createTmpFile($input) : $input;
121+
$this->objects[] = array_merge($this->pageOptions,$options);
100122
}
101123

102124
/**
@@ -107,20 +129,19 @@ public function addPage($input,$options=array())
107129
*/
108130
public function addCover($input,$options=array())
109131
{
110-
$options['input']="cover $input";
111-
$this->objects[]=array_merge($this->pageOptions,$options);
132+
$options['input'] = "cover $input";
133+
$this->objects[] = array_merge($this->pageOptions,$options);
112134
}
113135

114136
/**
115137
* Add a TOC object to the output
116138
*
117-
* @param string $input either a URL or a PDF filename
118-
* @param array $options optional options for this page
139+
* @param array $options optional options for the table of contents
119140
*/
120141
public function addToc($options=array())
121142
{
122-
$options['input']="toc";
123-
$this->objects[]=$options;
143+
$options['input'] = "toc";
144+
$this->objects[] = $options;
124145
}
125146

126147
/**
@@ -131,7 +152,7 @@ public function addToc($options=array())
131152
*/
132153
public function saveAs($filename)
133154
{
134-
if(($pdfFile=$this->getPdfFilename())===false)
155+
if(($pdfFile = $this->getPdfFilename())===false)
135156
return false;
136157

137158
copy($pdfFile,$filename);
@@ -146,7 +167,7 @@ public function saveAs($filename)
146167
*/
147168
public function send($filename=null)
148169
{
149-
if(($pdfFile=$this->getPdfFilename())===false)
170+
if(($pdfFile = $this->getPdfFilename())===false)
150171
return false;
151172

152173
header('Pragma: public');
@@ -172,21 +193,21 @@ public function setOptions($options)
172193
{
173194
foreach($options as $key=>$val)
174195
if($key==='bin')
175-
$this->bin=$val;
196+
$this->bin = $val;
176197
elseif($key==='tmp')
177-
$this->tmp=$val;
198+
$this->tmp = $val;
178199
elseif(is_int($key))
179-
$this->options[]=$val;
200+
$this->options[] = $val;
180201
else
181-
$this->options[$key]=$val;
202+
$this->options[$key] = $val;
182203
}
183204

184205
/**
185206
* @param array $options that should be applied to all pages as name/value pairs
186207
*/
187208
public function setPageOptions($options=array())
188209
{
189-
$this->pageOptions=$options;
210+
$this->pageOptions = $options;
190211
}
191212

192213
/**
@@ -197,20 +218,28 @@ public function getError()
197218
return $this->error;
198219
}
199220

221+
/**
222+
* @return string path to temp directory
223+
*/
224+
public function getTmpDir()
225+
{
226+
if($this->tmp===null)
227+
$this->tmp = sys_get_temp_dir();
228+
229+
return $this->tmp;
230+
}
231+
200232
/**
201233
* @return mixed the temporary PDF filename or false on error (triggers PDf creation)
202234
*/
203235
protected function getPdfFilename()
204236
{
205237
if($this->tmpFile===null)
206238
{
207-
if($this->tmp===null)
208-
$this->tmp=sys_get_temp_dir();
209-
210-
$tmpFile=tempnam($this->tmp,'tmp_WkHtmlToPdf_');
239+
$tmpFile = tempnam($this->getTmpDir(),'tmp_WkHtmlToPdf_');
211240

212241
if($this->createPdf($tmpFile)===true)
213-
$this->tmpFile=$tmpFile;
242+
$this->tmpFile = $tmpFile;
214243
else
215244
return false;
216245
}
@@ -224,15 +253,15 @@ protected function getPdfFilename()
224253
*/
225254
protected function getCommand($filename)
226255
{
227-
$command=$this->bin;
256+
$command = $this->bin;
228257

229-
$command.=$this->renderOptions($this->options);
258+
$command .= $this->renderOptions($this->options);
230259

231260
foreach($this->objects as $object)
232261
{
233-
$command.=' '.$object['input'];
262+
$command .= ' '.$object['input'];
234263
unset($object['input']);
235-
$command.=$this->renderOptions($object);
264+
$command .= $this->renderOptions($object);
236265
}
237266

238267
return $command.' '.$filename;
@@ -243,44 +272,61 @@ protected function getCommand($filename)
243272
*/
244273
protected function createPdf($fileName)
245274
{
246-
$command=$this->getCommand($fileName);
275+
$command = $this->getCommand($fileName);
247276

248277
// we use proc_open with pipes to fetch error output
249-
$descriptors=array(
250-
1=>array('pipe','w'),
251-
2=>array('pipe','w'),
278+
$descriptors = array(
279+
1 => array('pipe','w'),
280+
2 => array('pipe','w'),
252281
);
253-
$process=proc_open($command, $descriptors, $pipes);
282+
$process = proc_open($command, $descriptors, $pipes);
254283

255284
if(is_resource($process)) {
256285

257-
$stdout=stream_get_contents($pipes[1]);
258-
$stderr=stream_get_contents($pipes[2]);
286+
$stdout = stream_get_contents($pipes[1]);
287+
$stderr = stream_get_contents($pipes[2]);
259288
fclose($pipes[1]);
260289
fclose($pipes[2]);
261290

262-
$result=proc_close($process);
291+
$result = proc_close($process);
263292

264293
if($result!==0)
265-
$this->error="Could not run command $command:\n$stderr";
294+
$this->error = "Could not run command $command:\n$stderr";
266295
} else
267-
$this->error="Could not run command $command";
296+
$this->error = "Could not run command $command";
268297

269298
return $this->error===null;
270299
}
271300

301+
/**
302+
* Create a tmp file with given content
303+
*
304+
* @param string $content the file content
305+
* @return string the path to the created file
306+
*/
307+
protected function createTmpFile($content)
308+
{
309+
$tmpFile = tempnam($this->getTmpDir(),'tmp_WkHtmlToPdf_');
310+
rename($tmpFile, ($tmpFile.='.html'));
311+
file_put_contents($tmpFile, $content);
312+
313+
$this->tmpFiles[] = $tmpFile;
314+
315+
return $tmpFile;
316+
}
317+
272318
/**
273319
* @param array $options for a wkhtml, either global or for an object
274320
* @return string the string with options
275321
*/
276322
protected function renderOptions($options)
277323
{
278-
$out='';
324+
$out = '';
279325
foreach($options as $key=>$val)
280326
if(is_numeric($key))
281-
$out.=" --$val";
327+
$out .= " --$val";
282328
else
283-
$out.=" --$key $val";
329+
$out .= " --$key $val";
284330

285331
return $out;
286332
}

0 commit comments

Comments
 (0)