My favorites | Sign in
Project Home Downloads Wiki Issues
Search
for
IntegrationWithPhp  
Updated Dec 23, 2011 by uuf6...@gmail.com
<?php

	// Automated configuration. Modify these if they fail. (they shouldn't ;) )
	$GLOBALS['WKPDF_BASE_PATH']=str_replace(str_replace('\\','/',getcwd().'/'),'',dirname(str_replace('\\','/',__FILE__))).'/';
	$GLOBALS['WKPDF_BASE_SITE']='http://'.$_SERVER['SERVER_NAME'].'/';

	/**
	 * @author Christian Sciberras
	 * @see <a href="http://code.google.com/p/wkhtmltopdf/">http://code.google.com/p/wkhtmltopdf/</a>
	 * @copyright 2010 Christian Sciberras / Covac Software.
	 * @license LGPL
	 * @example
	 *   <font color="#008800"><i>//-- Create sample PDF and embed in browser. --//</i></font><br>
	 *   <br>
	 *   <font color="#008800"><i>// Include WKPDF class.</i></font><br>
	 *   <font color="#0000FF">require_once</font>(<font color="#FF0000">'wkhtmltopdf/wkhtmltopdf.php'</font>);<br>
	 *   <font color="#008800"><i>// Create PDF object.</i></font><br>
	 *   <font color="#EE00EE">$pdf</font>=new <b>WKPDF</b>();<br>
	 *   <font color="#008800"><i>// Set PDF's HTML</i></font><br>
	 *   <font color="#EE00EE">$pdf</font>-><font color="#0000FF">set_html</font>(<font color="#FF0000">'Hello &lt;b&gt;Mars&lt;/b&gt;!'</font>);<br>
	 *   <font color="#008800"><i>// Convert HTML to PDF</i></font><br>
	 *   <font color="#EE00EE">$pdf</font>-><font color="#0000FF">render</font>();<br>
	 *   <font color="#008800"><i>// Output PDF. The file name is suggested to the browser.</i></font><br>
	 *   <font color="#EE00EE">$pdf</font>-><font color="#0000FF">output</font>(<b>WKPDF</b>::<font color="#EE00EE">$PDF_EMBEDDED</font>,<font color="#FF0000">'sample.pdf'</font>);<br>
	 * @version
	 *   0.0 Chris - Created class.<br>
	 *   0.1 Chris - Variable paths fixes.<br>
	 *   0.2 Chris - Better error handlng (via exceptions).<br>
	 * <font color="#FF0000"><b>IMPORTANT: Make sure that there is a folder in %LIBRARY_PATH%/tmp that is writable!</b></font>
	 * <br><br>
	 * <b>Features/Bugs/Contact</b><br>
	 * Found a bug? Want a modification? Contact me at <a href="mailto:uuf6429@gmail.com">uuf6429@gmail.com</a> or <a href="mailto:contact@covac-software.com">contact@covac-software.com</a>...
	 *   guaranteed to get a reply within 2 hours at most (daytime GMT+1).
	 */
	class WKPDF {
		/**
		 * Private use variables.
		 */
		private $html='';
		private $cmd='';
		private $tmp='';
		private $pdf='';
		private $status='';
		private $orient='Portrait';
		private $size='A4';
		private $toc=false;
		private $copies=1;
		private $grayscale=false;
		private $title='';
		private static $cpu='';
		/**
		 * Advanced execution routine.
		 * @param string $cmd The command to execute.
		 * @param string $input Any input not in arguments.
		 * @return array An array of execution data; stdout, stderr and return "error" code.
		 */
		private static function _pipeExec($cmd,$input=''){
			$proc=proc_open($cmd,array(0=>array('pipe','r'),1=>array('pipe','w'),2=>array('pipe','w')),$pipes);
			fwrite($pipes[0],$input);
			fclose($pipes[0]);
			$stdout=stream_get_contents($pipes[1]);
			fclose($pipes[1]);
			$stderr=stream_get_contents($pipes[2]);
			fclose($pipes[2]);
			$rtn=proc_close($proc);
			return array(
					'stdout'=>$stdout,
					'stderr'=>$stderr,
					'return'=>$rtn
				);
		}
		/**
		 * Function that attempts to return the kind of CPU.
		 * @return string CPU kind ('amd64' or 'i386').
		 */
		private static function _getCPU(){
			if(self::$cpu==''){
				if(`grep -i amd /proc/cpuinfo`!='')			self::$cpu='amd64';
				elseif(`grep -i intel /proc/cpuinfo`!='')	self::$cpu='i386';
				else throw new Exception('WKPDF couldn\'t determine CPU ("'.`grep -i vendor_id /proc/cpuinfo`.'").');
			}
			return self::$cpu;
		}
		/**
		 * Force the client to download PDF file when finish() is called.
		 */
		public static $PDF_DOWNLOAD='D';
		/**
		 * Returns the PDF file as a string when finish() is called.
		 */
		public static $PDF_ASSTRING='S';
		/**
		 * When possible, force the client to embed PDF file when finish() is called.
		 */
		public static $PDF_EMBEDDED='I';
		/**
		 * PDF file is saved into the server space when finish() is called. The path is returned.
		 */
		public static $PDF_SAVEFILE='F';
		/**
		 * PDF generated as landscape (vertical).
		 */
		public static $PDF_PORTRAIT='Portrait';
		/**
		 * PDF generated as landscape (horizontal).
		 */
		public static $PDF_LANDSCAPE='Landscape';
		/**
		 * Constructor: initialize command line and reserve temporary file.
		 */
		public function __construct(){
			$this->cmd=$GLOBALS['WKPDF_BASE_PATH'].'wkhtmltopdf-'.self::_getCPU();
			if(!file_exists($this->cmd))throw new Exception('WKPDF static executable "'.htmlspecialchars($this->cmd,ENT_QUOTES).'" was not found.');
			do{
				$this->tmp=$GLOBALS['WKPDF_BASE_PATH'].'tmp/'.mt_rand().'.html';
			} while(file_exists($this->tmp));
		}
		/**
		 * Set orientation, use constants from this class.
		 * By default orientation is portrait.
		 * @param string $mode Use constants from this class.
		 */
		public function set_orientation($mode){
			$this->orient=$mode;
		}
		/**
		 * Set page/paper size.
		 * By default page size is A4.
		 * @param string $size Formal paper size (eg; A4, letter...)
		 */
		public function set_page_size($size){
			$this->size=$size;
		}
		/**
		 * Whether to automatically generate a TOC (table of contents) or not.
		 * By default TOC is disabled.
		 * @param boolean $enabled True use TOC, false disable TOC.
		 */
		public function set_toc($enabled){
			$this->toc=$enabled;
		}
		/**
		 * Set the number of copies to be printed.
		 * By default it is one.
		 * @param integer $count Number of page copies.
		 */
		public function set_copies($count){
			$this->copies=$count;
		}
		/**
		 * Whether to print in grayscale or not.
		 * By default it is OFF.
		 * @param boolean True to print in grayscale, false in full color.
		 */
		public function set_grayscale($mode){
			$this->grayscale=$mode;
		}
		/**
		 * Set PDF title. If empty, HTML <title> of first document is used.
		 * By default it is empty.
		 * @param string Title text.
		 */
		public function set_title($text){
			$this->title=$text;
		}
		/**
		 * Set html content.
		 * @param string $html New html content. It *replaces* any previous content.
		 */
		public function set_html($html){
			$this->html=$html;
			file_put_contents($this->tmp,$html);
		}
		/**
		 * Returns WKPDF print status.
		 * @return string WPDF print status.
		 */
		public function get_status(){
			return $this->status;
		}
		/**
		 * Attempts to return the library's full help.
		 * @return string WKHTMLTOPDF HTML help.
		 */
		public function get_help(){
			$tmp=self::_pipeExec('"'.$this->cmd.'" --extended-help');
			return $tmp['stdout'];
		}
		/**
		 * Convert HTML to PDF.
		 */
		public function render(){
			$web=$GLOBALS['WKPDF_BASE_SITE'].$GLOBALS['WKPDF_BASE_PATH'].'tmp/'.basename($this->tmp);
			$this->pdf=self::_pipeExec(
				'"'.$this->cmd.'"'
				.(($this->copies>1)?' --copies '.$this->copies:'')				// number of copies
				.' --orientation '.$this->orient								// orientation
				.' --page-size '.$this->size									// page size
				.($this->toc?' --toc':'')										// table of contents
				.($this->grayscale?' --grayscale':'')							// grayscale
				.(($this->title!='')?' --title "'.$this->title.'"':'')			// title
				.' "'.$web.'" -'												// URL and optional to write to STDOUT
			);
			if(strpos(strtolower($this->pdf['stderr']),'error')!==false)throw new Exception('WKPDF system error: <pre>'.$this->pdf['stderr'].'</pre>');
			if($this->pdf['stdout']=='')throw new Exception('WKPDF didn\'t return any data. <pre>'.$this->pdf['stderr'].'</pre>');
			if(((int)$this->pdf['return'])>1)throw new Exception('WKPDF shell error, return code '.(int)$this->pdf['return'].'.');
			$this->status=$this->pdf['stderr'];
			$this->pdf=$this->pdf['stdout'];
			unlink($this->tmp);
		}
		/**
		 * Return PDF with various options.
		 * @param string $mode How two output (constants from this same class).
		 * @param string $file The PDF's filename (the usage depends on $mode.
		 * @return string|boolean Depending on $mode, this may be success (boolean) or PDF (string).
		 */
		public function output($mode,$file){
			switch($mode){
				case self::$PDF_DOWNLOAD:
					if(!headers_sent()){
						header('Content-Description: File Transfer');
						header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
						header('Pragma: public');
						header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
						header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
						// force download dialog
						header('Content-Type: application/force-download');
						header('Content-Type: application/octet-stream', false);
						header('Content-Type: application/download', false);
						header('Content-Type: application/pdf', false);
						// use the Content-Disposition header to supply a recommended filename
						header('Content-Disposition: attachment; filename="'.basename($file).'";');
						header('Content-Transfer-Encoding: binary');
						header('Content-Length: '.strlen($this->pdf));
						echo $this->pdf;
					}else{
						throw new Exception('WKPDF download headers were already sent.');
					}
					break;
				case self::$PDF_ASSTRING:
					return $this->pdf;
					break;
				case self::$PDF_EMBEDDED:
					if(!headers_sent()){
						header('Content-Type: application/pdf');
						header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
						header('Pragma: public');
						header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
						header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
						header('Content-Length: '.strlen($this->pdf));
						header('Content-Disposition: inline; filename="'.basename($file).'";');
						echo $this->pdf;
					}else{
						throw new Exception('WKPDF embed headers were already sent.');
					}
					break;
				case self::$PDF_SAVEFILE:
					return file_put_contents($file,$this->pdf);
					break;
				default:
					throw new Exception('WKPDF invalid mode "'.htmlspecialchars($mode,ENT_QUOTES).'".');
			}
			return false;
		}
	}
?>
Comment by nsbing...@gmail.com, Mar 23, 2010

I attempting to use this script with wkhtmltopdf-0.9.3-OS-X.i368 and am receiving the following:

{{{kCGErrorFailure: Set a breakpoint @ CGErrorBreakpoint() to catch errors as they are logged. RegisterApplication?(), FAILED TO establish the default connection to the WindowServer?, CGSDefaultConnection() is NULL.}}}

Calling directly from the command-line works fine.

System Info: OSX 10.6.2 Apache/2.2.14 PHP 5.3.0

Comment by pierino....@gmail.com, Mar 31, 2010

I think you should use $SERVER['SERVER_ADDR'] instead $SERVER['SERVER_NAME'], because sometimes the server hasn't a dns, but only the hostname (in intranet or testing servers, for example). Or maybe $SERVER['SERVER_ADDR'].':'.$SERVER['SERVER_PORT'] ?

Other issues: - the render() command seems to render the page at the address of the actual page. But what if the page requires authentication? It will likely print a pdf with a login request. - the root slash in "href" and "src" sometimes won't be resolved correctly, for example if your server has more network interfaces or if you access the site over a vpn, so i prefer replace with the correct ip address.

Comment by grzelak....@gmail.com, May 12, 2010

It would be better to change private properties and methods into protected one. It would make subclassing possible, allowing some customization of render process.

Comment by mcouill...@gmail.com, May 24, 2010

I'm currently using this with PHP via a simple shell_exec() command on a W2k3 server running IIS 6. So far so good.

shell_exec("wkhtmltopdf.exe input_filename output_filename --orientation Landscape");

Then I check for the existence of output_filename and send it to the browser if available.

Comment by romanurn...@gmail.com, May 26, 2010

is there any chance that this is working with safe mode = on? I'm getting hundrets of errors...

Comment by project member antial...@gmail.com, Jun 14, 2010

@romanurnaut: No

Comment by garcia.r...@gmail.com, Jun 17, 2010

Can you put an example in how to use the class you wrote???

Comment by doku...@gmail.com, Jul 27, 2010

Any sample of wkhtmltoimage? And could this work on a rented server?

Comment by Marwe...@gmail.com, Aug 19, 2010

How to use the class above:

$html = file_get_contents("http://www.google.com");

//echo $html;

$pdf = new WKPDF();

$pdf->set_html($html);

$pdf->render();

$pdf->output(WKPDF::$PDF_EMBEDDED,'sample.pdf');

I tmp folder must exist in the same directory as the class or the wk.. file.

Comment by cubadac...@gmail.com, Aug 23, 2010

What about using this on a rented server?

Comment by Marwe...@gmail.com, Aug 25, 2010

No problem as long as you chmod the files and folder correct.

Comment by moham...@gmail.com, Aug 30, 2010

is it possible to run this in a shared server? if so does it needs any thing to be installed on server? the server is linux.

Comment by rya...@gmail.com, Sep 12, 2010

When I run the above code, all I get is an HTML file created in the /tmp folder. What am I doing wrong?

Comment by gdtestac...@gmail.com, Sep 14, 2010

I don't understand why you have to use the command to determine the CPU? There is no need that I can find honestly other then just wanting to use a reference point (which you can create any other type of reference point) for requiring the CPU int he test script. This causes the test script to fail on virtual systems where it is unable to determine the CPU type.

Comment by maxime.c...@gmail.com, Sep 15, 2010

hi,

I try to use your code on WampServer? (Vista) with your exemple (hello mars! ^^) I replace the line 113

$this->cmd=$GLOBALS['WKPDF_BASE_PATH'].'wkhtmltopdf-'.self::getCPU();
by
$this->cmd=$GLOBALS['WKPDF_BASE_PATH'].'wkhtmltopdf.exe';
to use my file but I have the error

Fatal error: Uncaught exception 'Exception' with message 'WKPDF system error: <pre>Loading pages (1/6) [> ] 0% [======> ] 10% [============================================================] 100% Error: Failed loading page http:/wkhtmltopdf.exe --orientation Portrait --page-size A4 http://localhost/wkhtmltopdf/tmp/1141092626.html (sometimes it will work just to ignore this error with --load-error-handling ignore) </pre>' in C:\wamp\www\phppdf\wkhtmltopdf\wkhtmltopdf.php:205 Stack trace: #0 C:\wamp\www\phppdf\index.php(15): WKPDF->render() #1 {main} thrown in C:\wamp\www\phppdf\wkhtmltopdf\wkhtmltopdf.php on line 205

The html file is created in /tmp but no pdf :-(

Thanks

Comment by santhosh...@gmail.com, Sep 16, 2010

can anyone tell me after copying the given php. how should i run it?

Comment by andr...@gmail.com, Sep 20, 2010

I've tried to use this script on Debian. wkhtmltopdf works in the command line. When I used the PHP script above, I get the error :

Fatal error: Uncaught exception 'Exception' with message 'WKPDF didn't return any data. <pre>wkhtmltopdf: cannot connect to X server </pre>' in /var/www2/infolegale/pdf/wkpdf.php:231 Stack trace: #0 /var/www2/infolegale/pdf/test_wkpdf.php(11): WKPDF->render() #1 {main} thrown in /var/www2/infolegale/pdf/wkpdf.php on line 231

The xvfb server is ruuning. I have used "Xvfb :0 -screen 0 1600x1200x16 &" to launch it. In order that the script work in the command line, I have declared : DISPLAY=127.0.0.1:0 export DISPLAY

Before doing that, I got the same error "cannot connect to X server" as in my script. So probably there is still something wrong with the DISPLAY variable. I have tried to set it in my php script, or in /etc/init.d/apache2 but it did'nt work. Any hint ?

Comment by mamirula...@gmail.com, Sep 22, 2010

I am facing this issue, if I change the last argument from '-' to a filename it executed perfectly.

what is wrong with '-' as last argument?

Fatal error: Uncaught exception 'Exception' with message 'WKPDF embed headers were already sent.' in /home2/nanosoft/public_html/webify/preptel/wkhtmltopdf/wkhtmltopdf.php:266 Stack trace: #0 /home2/nanosoft/public_html/webify/preptel/pdf.php(15): WKPDF->output('I', 'sample.pdf') #1 {main} thrown in /home2/nanosoft/public_html/webify/preptel/wkhtmltopdf/wkhtmltopdf.php on line 266

Comment by andr...@gmail.com, Sep 23, 2010

(response to myself above) After some mails with Jakok (see http://www.daimi.au.dk/~jakobt/#about), I realize that the i386 static lib (wkhtmltopdf-0.10.0_beta5-static-i386.tar.lzma) does not work on my x86_64 GNU/Linux machine BUT the amd-64 version works ! (wkhtmltopdf-0.10.0_beta5-static-amd64.tar.lzma). As this static lib does'nt need an X server, I get rid of this DISPLAY issue. So if the i386 does not work for you, give a try to the amd64 version !

Comment by alexpo...@gmail.com, Sep 27, 2010
<?php

//simple wrapper we're using for dev, might be handy for someone:
//very happy to have found this project btw :D

if (!$_GET['url']) die("looking for ?url=http://www.....");

$url = escapeshellarg($_GET['url']);

$blah = shell_exec("/path/to/wkhtmltopdf-i386 $url /tmp/tmp_pdf.pdf");

$str = file_get_contents("/tmp/tmp_pdf.pdf");

header('Content-Type: application/pdf');
header('Content-Length: '.strlen($str));
header('Content-Disposition: inline; filename="pdf.pdf"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
ini_set('zlib.output_compression','0');
die($str);
Comment by jegzbaby@hotmail.com, Sep 28, 2010

Hi

I am using an IIS 6.0 server.

I am getting this error; Fatal error: Uncaught exception 'Exception' with message 'WKPDF didn't return any data. <pre>The filename, directory name, or volume label syntax is incorrect. </pre>' in...

I know I can run basic command line commands like 'ipconfig' from my server setup, but it seems to be a problem when I try to execute a ".exe"

Any ideas what can do to solve this problem?? or is there another approach I can to get wkhtmltopdf working with a PHP application

Thanks for your time

Comment by michalch...@gmail.com, Oct 19, 2010

I found that for large PDF files PHP would just hang when generating them. I found using non-blocking reading of stdout and stderr in pipeExec, as given at http://php.net/manual/en/function.proc-open.php#89338 seemed to fix it:

<?php 
/**
 * Advanced execution routine.
 * @param string $cmd The command to execute.
 * @param string $input Any input not in arguments.
 * @return array An array of execution data; stdout, stderr and return "error" code.
 */
private static function _pipeExec($cmd,$input=''){
        $pipes = array();
		$proc=proc_open($cmd,array(0=>array('pipe','r'),1=>array('pipe','w'),2=>array('pipe','w')),$pipes,null,null,array('binary_pipes'=>true));
        fwrite($pipes[0],$input);
        fclose($pipes[0]);

		// From http://php.net/manual/en/function.proc-open.php#89338
        $read_output = $read_error = false;
        $buffer_len  = $prev_buffer_len = 0;
        $ms          = 10;
        $stdout      = '';
        $read_output = true;
        $stderr       = '';
        $read_error  = true;
        stream_set_blocking($pipes[1], 0);
        stream_set_blocking($pipes[2], 0);

        // dual reading of STDOUT and STDERR stops one full pipe blocking the other, because the external script is waiting
        while ($read_error != false or $read_output != false)
        {
            if ($read_output != false)
            {
                if(feof($pipes[1]))
                {
                    fclose($pipes[1]);
                    $read_output = false;
                }
                else
                {
                    $str = fgets($pipes[1], 1024);
                    $len = strlen($str);
                    if ($len)
                    {
                        $stdout .= $str;
                        $buffer_len += $len;
                    }
                }
            }
           
            if ($read_error != false)
            {
                if(feof($pipes[2]))
                {
                    fclose($pipes[2]);
                    $read_error = false;
                }
                else
                {
                    $str = fgets($pipes[2], 1024);
                    $len = strlen($str);
                    if ($len)
                    {
                        $stderr .= $str;
                        $buffer_len += $len;
                    }
                }
            }
           
            if ($buffer_len > $prev_buffer_len)
            {
                $prev_buffer_len = $buffer_len;
                $ms = 10;
            }
            else
            {
                usleep($ms * 1000); // sleep for $ms milliseconds
                if ($ms < 160)
                {
                    $ms = $ms * 2;
                }
            }
        }

        $rtn=proc_close($proc);
        return array(
                        'stdout'=>$stdout,
                        'stderr'=>$stderr,
                        'return'=>$rtn
                );
}
?>
Comment by aurimas....@gmail.com, Dec 16, 2010

Hi,

I have rewritten this code snippet: removed $GLOBALS and other code which I didn't liked. I placed my code to github.com & so everyone can easilly download, checkout & make changes ;)

https://github.com/aur1mas/Wkhtmltopdf

I hope it will be useful for somebody ;)

Comment by zandybie...@gmail.com, Jan 25, 2011

@aurimas.baubkus : For long content; exec function hangs :-(. with a simple function call "system" works.

protected function exec($cmd, $input = "") {

$result = array('stdout' => ''); system( $cmd, &$result['stdout'] ); return $result;
}

Comment by zandybie...@gmail.com, Jan 25, 2011
protected function _exec($cmd, $input = "")
{
    $result = array('stdout' => '', 'stderr' => '', 'return' => '');	
    system( $cmd, &$result['stdout'] );	
    return $result;
}
Comment by jesse...@gmail.com, Mar 17, 2011

Hi, do you have a version of this php script that outputs an image instead of a PDF?

Comment by mheijk...@gmail.com, May 2, 2011

This didn't work on a Xeon-machine.

Adding the following:

elseif(`grep -i xeon /proc/cpuinfo` != '') self::$cpu='amd64';

After the AMD-line did the trick.

Comment by grzegorz...@gmail.com, May 19, 2011

i have : wkhtmltopdf: cannot connect to X server

so how to use from php ?

Comment by ringm...@gmail.com, May 24, 2011

As a general rule, if you're getting the "wkhtmltopdf: cannot connect to X server" error it means you've probably installed wkhtmltopdf via a package manager. This script is designed to work with the static library.

Comment by melito...@gmail.com, Jun 8, 2011

what can be wrong - all I get is a new html file and error message "PHP Fatal error: Uncaught exception 'Exception' with message 'WKHTMLTOPDF didn't return any data' in /home/......"

could someone help?

Comment by Ruda.Dob...@gmail.com, Jun 11, 2011

The same problem : "PHP Fatal error: Uncaught exception 'Exception' with message 'WKHTMLTOPDF didn't return any data' in /home/......" Without any explaination. All folders are accessible, plattform is i386 Neither shell_exec("wkhtmltopdf-i386 -H") returns nothing.

Any ideas?

Comment by Ruda.Dob...@gmail.com, Jun 11, 2011

EDIT: Problem solved! This script does not right recognised the cpu type...it returns i386 for amd64 plattform. So I overrided it to amd64 and now it work...

Comment by jiwani.f...@gmail.com, Jun 11, 2011

Had to change to get it to run:

private static $cpu='amd64';

I get an html file in the tmp dir, but I am getting this error during rendering:

<pre> Fatal error: Uncaught exception 'Exception' with message 'WKPDF system error: Loading pages (1/6) [> ] 0% [======> ] 10% [=======> ] 13% [==========> ] 17% [==================> ] 31% [============================================================] 100% Counting pages (2/6) [============================================================] Object 1 of 1 Resolving links (4/6) [============================================================] Object 1 of 1 Loading headers and footers (5/6) Printing pages (6/6) [> ] Preparing [============================================================] in /httpdocs/includes/wkpdf.class.php on line 291 </pre>

Comment by superd...@gmail.com, Jul 8, 2011

An alternative is to use the Snappy library. https://github.com/knplabs/snappy

Comment by valentin...@soluzioneazienda.it, Jul 28, 2011

cpu function corrected:

	/**
	 * Function that attempts to return the kind of CPU.
	 * @return string CPU kind ('amd64' or 'i386').
	 */
	private static function _getCPU() {
		if ( self::$cpu == '' ) {
			if ( `grep -i lm /proc/cpuinfo` != '' )
				self::$cpu = 'amd64';
			else
				self::$cpu = 'i386';
		}
		return self::$cpu;
	}
Comment by d...@meanstreak.co.uk, Aug 2, 2011

Why not use uname -m to determine if 64-bit or not?

/**
 * Function that attempts to return the kind of CPU.
 * @return string CPU kind ('amd64' or 'i386').
 */
private static function _getCPU() {
    if (self::$cpu=='') {
        $arch = `uname -m`;
        if (preg_match("/^x(86_)*64$/", $arch))
            self::$cpu = 'amd64';
        elseif (preg_match("/^(i[3-6]|x)86$/", $arch))
            self::$cpu = 'i386';
        else throw new Exception('WKPDF couldn\'t determine CPU ("'.`grep -i vendor_id /proc/cpuinfo`.'").');
    }
    return self::$cpu;
}
Comment by sendmeta...@gmail.com, Nov 6, 2011

Hi i need to know, how to execute javascript and get back the result. Is it possible with wkhtmltoimage.

Thanks

Comment by hjdevrie...@gmail.com, Nov 16, 2011

Is it possible to execute the wkhtmltopdf executable from a remote server? My hosting does not support running any executables. Or if i cannot do that, is it possible to use this on another way to run the exec? I really have to know this.

Comment by cedric.s...@gmail.com, Dec 16, 2011

Hello !

Is there a way to get error codes meaning ? I can't find it anywhere in the documentation and i'm getting an "WKPDF shell error, return code 3." message without knowing the meaning of that code 3 error.

Thanks

Comment by rguill...@toog.fr, Feb 8, 2012

+1 for the lm test proposed by valentin...@soluzioneazienda.it on Jul 28, 2011 : "lm" stands for Long Mode which equates to a 64-bit CPU

Comment by earthtec...@gmail.com, Mar 23, 2012

Hello!

Please provide the code to create an image from html.

Thanks.

Comment by d...@meanstreak.co.uk, Mar 28, 2012

@rguill...@toog.fr

I executed:-

grep -i lm /proc/cpuinfo

on a 32-bit machine and got in return:-

flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc up pebs bts pni dtes64 monitor ds_cpl tm2 cid cx16 xtpr lahf_lm

(note presence of 'lm' in result)

so it's not a correct solution.

Comment by martin.a...@gmail.com, Mar 29, 2012

Version 0.6

<?php

// Automated configuration. Modify these if they fail. (they shouldn't ;) )
$GLOBALS['WKPDF_BASE_PATH']=str_replace(str_replace('\\','/',getcwd().'/'),'',dirname(str_replace('\\','/',__FILE__))).'/';
$GLOBALS['WKPDF_BASE_SITE']='http://'.$_SERVER['SERVER_NAME'].'/';
$GLOBALS['WKPDF_WINI_PATH']=''; // hope and wish that command line works, the other method with absolute path causes path space issues
//substr($_SERVER['SystemRoot'],0,strpos($_SERVER['SystemRoot'],'\\')).'\\Program Files\\wkhtmltopdf\\';

/**
 * @author Christian Sciberras
 * @see <a href="http://code.google.com/p/wkhtmltopdf/">http://code.google.com/p/wkhtmltopdf/</a>
 * @copyright 2010 Christian Sciberras / Keen Advertising / Covac Software.
 * @license None. There are no restrictions on use, however keep copyright intact.
 *   Modification is allowed, keep track of modifications below in this comment block.
 * @example
 *   <font color="#008800"><i>//-- Create sample PDF and embed in browser. --//</i></font><br>
 *   <br>
 *   <font color="#008800"><i>// Include WKPDF class.</i></font><br>
 *   <font color="#0000FF">require_once</font>(<font color="#FF0000">'wkhtmltopdf/wkhtmltopdf.php'</font>);<br>
 *   <font color="#008800"><i>// Create PDF object.</i></font><br>
 *   <font color="#EE00EE">$pdf</font>=new <b>WKPDF</b>();<br>
 *   <font color="#008800"><i>// Set PDF's HTML</i></font><br>
 *   <font color="#EE00EE">$pdf</font>-><font color="#0000FF">set_html</font>(<font color="#FF0000">'Hello &lt;b&gt;Mars&lt;/b&gt;!'</font>);<br>
 *   <font color="#008800"><i>// Convert HTML to PDF</i></font><br>
 *   <font color="#EE00EE">$pdf</font>-><font color="#0000FF">render</font>();<br>
 *   <font color="#008800"><i>// Output PDF. The file name is suggested to the browser.</i></font><br>
 *   <font color="#EE00EE">$pdf</font>-><font color="#0000FF">output</font>(<b>WKPDF</b>::<font color="#EE00EE">$PDF_EMBEDDED</font>,<font color="#FF0000">'sample.pdf'</font>);<br>
 * @version
 *   0.6 Anand, Chris - Added support for custom arguments (the functions args_add, args_remove and args_clear).
 *   0.5 Pat Brooks - Identifying certain return error codes.
 *   0.4 Adam, Pierino, Chris - Protected fields, IP instead domain, pages from multiple sources.<br>
 *   0.3 Chris - CPU and CPU vendor detection to suggest proper executable.<br>
 *   0.2 Chris - Better error handlng (via exceptions).<br>
 *   0.1 Chris - Variable paths fixes.<br>
 *   0.0 Chris - Created class.<br>
 * <font color="#FF0000"><b>IMPORTANT: Make sure that there is a folder in %LIBRARY_PATH%/tmp that is writable!</b></font>
 * <br><br>
 * <b>Features/Bugs/Contact</b><br>
 * Found a bug? Want a modification? Contact me at <a href="mailto:uuf6429@gmail.com">uuf6429@gmail.com</a> or <a href="mailto:contact@covac-software.com">contact@covac-software.com</a>...
 */
class WKPDF {
	/**
	 * protected use variables.
	 */
	protected $cmd='';
	protected $tmp='';
	protected $pdf='';
	protected $status='';
	protected $orient='Portrait';
	protected $size='A4';
	protected $toc=false;
	protected $copies=1;
	protected $grayscale=false;
	protected $title='';
	protected $args=array();
	protected static $_cpu='';
	protected static $_os='';
	protected static $_cmd='';
	/**
	 * Advanced execution routine.
	 * @param string $cmd The command to execute.
	 * @param string $input Any input not in arguments.
	 * @return array An array of execution data; stdout, stderr and return "error" code.
	 */
	protected static function _pipeExec($cmd,$input=''){
		$proc=proc_open($cmd,array(0=>array('pipe','r'),1=>array('pipe','w'),2=>array('pipe','w')),$pipes);
		fwrite($pipes[0],$input);
		fclose($pipes[0]);
		$stdout=stream_get_contents($pipes[1]); // max execusion time exceeded issue
		fclose($pipes[1]);
		$stderr=stream_get_contents($pipes[2]);
		fclose($pipes[2]);
		$rtn=proc_close($proc);
		return array(
			'stdout'=>$stdout,
			'stderr'=>$stderr,
			'return'=>(int)$rtn
		);
	}
	/**
	 * Function that attempts to return the kind of platform.
	 * @return string OS kind ('win', 'lin' or 'osx').
	 */
	protected static function _getOS(){
		if(self::$_os==''){
			if(stristr(PHP_OS,'WIN') && !stristr(PHP_OS,'DAR'))	self::$_os='win';
			elseif(`uname | grep -i linux`!='')					self::$_os='lin';
			elseif(`uname | grep -i darwin`!='')				self::$_os='osx';
			else throw new Exception('WKPDF couldn\'t determine OS.');
		}
		return self::$_os;
	}
	/**
	 * Function that attempts to return the kind of CPU.
	 * @return string CPU kind ('amd', 'intel' or 'ppc').
	 */
	protected static function _getCPU(){
		if(self::$_cpu==''){
			switch(self::_getOS()){
				case 'lin':
// TODO: use uname -m or -a, by the way, this is about 32bit vs 64bit not amd vs intel!
					if(`grep -i amd /proc/cpuinfo`!='')			self::$_cpu='amd';
					elseif(`grep -i intel /proc/cpuinfo`!='')	self::$_cpu='intel';
					break;
				case 'osx':
// TODO: use uname -m or -a
					if(`machine | grep -i ppc`!='')				self::$_cpu='ppc';
					// TODO: I don't like this check on a single character
					elseif(`machine | grep -i i`!='')			self::$_cpu='intel';
					break;
				case 'win':
					self::$_cpu='';
					break;
				default:
					throw new Exception('WKPDF CPU detection on '.self::_getOS().' OS not supported.');
					break;
			}
			if(self::$_cpu=='')throw new Exception('WKPDF couldn\'t determine CPU ('.self::_getOS().' OS).');
		}
		return self::$_cpu;
	}
	/**
	 * Function that attempts to return the correct executable path.
	 * @return string WKHTMLTOPDF path.
	 */
	protected static function _getCMD(){
		if(self::$_cmd==''){
			// the end filename is in the form of "(%PATH%)wkhtmltopdf[-(win|osx|lin)-(amd|intel|ppc)][.exe]"
			if(self::_getOS()=='win'){
				//self::$_cmd=$GLOBALS['WKPDF_WINI_PATH'].'wkhtmltopdf-'.self::_getOS().'-'.self::_getCPU().'.exe';
				self::$_cmd=$GLOBALS['WKPDF_WINI_PATH'].'wkhtmltopdf.exe';
			}else{
				self::$_cmd=$GLOBALS['WKPDF_BASE_PATH'].'wkhtmltopdf-'.self::_getOS().'-'.self::_getCPU();
			}
		}
		switch(self::_getOS()){
			case 'win': // checks file existence (permissions on windows isn't much of a problem)
				// for windows, the command is "exists %filename%" ... try it out?
				break;
			case 'lin': case 'osx': // checks file existence and permissions using LS
			$exists=self::_pipeExec('test -f "'.self::$_cmd.'"');
			if($exists['return']>0)
				throw new Exception('WKPDF executable couldn\'t be found ("'.htmlspecialchars(self::$_cmd).'").');
			$exists=explode(' ',str_replace('  ',' ',str_replace('	',' ',$exists['stdout']))); // perms, unused, group, user, ...
			if(count($exists)>1) // "test" command ran, keep testing settings, otherwise just ignore tests...
				if(strstr($exists[0],'rwxrwxrwx')===false)
					if(($exists[2]!=get_current_user())||($exists[3]!=get_current_user()))
						throw new Exception('WKPDF executable permissions are not 0777 or user/group does not match with current user/group.');
			break;
		}
		return self::$_cmd;
	}
	/**
	 * Force the client to download PDF file when finish() is called.
	 */
	public static $PDF_DOWNLOAD='D';
	/**
	 * Returns the PDF file as a string when finish() is called.
	 */
	public static $PDF_ASSTRING='S';
	/**
	 * When possible, force the client to embed PDF file when finish() is called.
	 */
	public static $PDF_EMBEDDED='I';
	/**
	 * PDF file is saved into the server space when finish() is called. The path is returned.
	 */
	public static $PDF_SAVEFILE='F';
	/**
	 * PDF generated as landscape (vertical).
	 */
	public static $PDF_PORTRAIT='Portrait';
	/**
	 * PDF generated as landscape (horizontal).
	 */
	public static $PDF_LANDSCAPE='Landscape';
	/**
	 * Constructor: initialize command line and reserve temporary file.
	 */
	public function __construct(){
		$this->cmd=self::_getCMD();
	}
	/**
	 * In case where platform detection fails (or you want to disable it), this function is able to let you use your own path to the executable.
	 * @param string $cmd The command line pointing to the WKHTMLTOPDF executable.
	 */
	public function set_cmd($cmd){
		$this->cmd=$cmd;
	}
	/**
	 * Set orientation, use constants from this class.
	 * By default orientation is portrait.
	 * @param string $mode Use constants from this class.
	 */
	public function set_orientation($mode){
		$this->orient=$mode;
	}
	/**
	 * Set page/paper size.
	 * By default page size is A4.
	 * @param string $size Formal paper size (eg; A4, letter...)
	 */
	public function set_page_size($size){
		$this->size=$size;
	}
	/**
	 * Whether to automatically generate a TOC (table of contents) or not.
	 * By default TOC is disabled.
	 * @param boolean $enabled True use TOC, false disable TOC.
	 */
	public function set_toc($enabled){
		$this->toc=$enabled;
	}
	/**
	 * Set the number of copies to be printed.
	 * By default it is one.
	 * @param integer $count Number of page copies.
	 */
	public function set_copies($count){
		$this->copies=$count;
	}
	/**
	 * Whether to print in grayscale or not.
	 * By default it is OFF.
	 * @param boolean True to print in grayscale, false in full color.
	 */
	public function set_grayscale($mode){
		$this->grayscale=$mode;
	}
	/**
	 * Set PDF title. If empty, HTML <title> of first document is used.
	 * By default it is empty.
	 * @param string Title text.
	 */
	public function set_title($text){
		$this->title=$text;
	}
	/**
	 * Set html content.
	 * @param string $html New html content. It *replaces* any previous content.
	 */
	public function set_html($html){
		if($this->tmp!='')throw new Exception('WKPDF html has already been set.');
		do{
			$this->tmp=$GLOBALS['WKPDF_BASE_PATH'].'tmp/'.mt_rand().'.html';
		} while(file_exists($this->tmp));
		if(!file_put_contents($this->tmp,$html))throw new Exception('WKPDF write temporary file failed.');
		$this->url=$GLOBALS['WKPDF_BASE_SITE'].$GLOBALS['WKPDF_BASE_PATH'].'tmp/'.basename($this->tmp);
	}
	/**
	 * Use a URL/path instead of HTML source.
	 * @param string $url Fully qualified URL or absolute path.
	 */
	public function set_url($url){
		$this->url=$url;
	}
	/**
	 * Returns WKPDF print status.
	 * @return string WPDF print status.
	 */
	public function get_status(){
		return $this->status;
	}
	/**
	 * Attempts to return the library's full help.
	 * @return string WKHTMLTOPDF HTML help.
	 */
	public function get_help(){
		$tmp=self::_pipeExec('"'.$this->cmd.'" --extended-help');
		return $tmp['stdout'];
	}
	/**
	 * Print error in output.
	 * @param string $msg The error message.
	 * @param array $out The output.
	 */
	protected static function _retError($msg,$out){
		throw new Exception($msg.'<table>'
			.'<tr><td>RESULT:</td><td><code>'.htmlspecialchars($out['return'],ENT_QUOTES).'</code></td></tr>'
			.'<tr><td>STDERR:</td><td><code>'.htmlspecialchars($out['stderr'],ENT_QUOTES).'</code></td></tr>'
			.'<tr><td>STDOUT:</td><td><code>'.htmlspecialchars($out['stdout'],ENT_QUOTES).'</code></td></tr>'
			.'</table>');
	}
	/**
	 * Add custom argument to the list.
	 * @param string $switch Argument name (eg: --header).
	 * @param string $value Argument value (eg: <b>hi</b>).
	 */
	public function args_add($switch,$value){
		$this->args[$switch]=$value;
	}
	/**
	 * Remove an existing custom argument.
	 * @param string $switch Argument name (eg: --header).
	 */
	public function args_remove($switch){
		if(isset($this->args[$switch]))unset($this->args[$switch]);
	}
	/**
	 * Removes all custom arguments.
	 */
	public function args_clear(){
		$this->args[$switch]=array();
	}
	/**
	 * Helper function that builds the list of arguments.<br>
	 * Do not call this directly.
	 * @return string List of custom arguments.
	 */
	protected function args_build(){
		$res='';
		foreach($this->args as $switch=>$value)$res.=' '.$switch.' '.escapeshellarg($value);
		return $res;
	}
	/**
	 * Convert HTML to PDF.
	 * @return boolean Whether successful or not.
	 */
	public function render(){
		$this->pdf=self::_pipeExec(
			$this->cmd
				.(($this->copies>1)?' --copies '.(int)$this->copies:'')				// number of copies
				.' --orientation '.escapeshellarg($this->orient)					// orientation
				.' --page-size '.escapeshellarg($this->size)						// page size
				.($this->toc?' --toc':'')											// table of contents
				.($this->grayscale?' --grayscale':'')								// grayscale
				.(($this->title!='')?' --title '.escapeshellarg($this->title):'')	// title
				.$this->args_build()												// custom arguments
				.' '.escapeshellarg($this->url).' -'								// URL and use STDOUT
		);			//if(strpos(strtolower($this->pdf['stderr']),'error')!==false)self::_retError('WKPDF system error.',$this->pdf);
		if($this->pdf['stdout']=='')self::_retError('WKPDF program error.',$this->pdf);
		$this->status=$this->pdf['stderr'];
		$this->pdf=$this->pdf['stdout'];
		if($this->tmp!='')unlink($this->tmp);
		switch($this->pdf['return']){
			case 7:
				self::_retError('WKPDF system error 7: malformed executable.',$this->pdf);
				return false;
			case 3:
				self::_retError('WKPDF error 401: unauthorized.',$this->pdf);
				return false;
			case 2:
				self::_retError('WKPDF error 404: file not found.',$this->pdf);
				return false;
			case 0:
				return true;
			default:
				self::_retError('WKPDF error '.$this->pdf['return'].'.',$this->pdf);
				return false;
		}
	}
	/**
	 * Return PDF with various options.
	 * @param string $mode How two output (constants from this same class).
	 * @param string $file The PDF's filename (the usage depends on $mode.
	 * @return string|boolean Depending on $mode, this may be success (boolean) or PDF (string).
	 */
	public function output($mode,$file){
		switch($mode){
			case self::$PDF_DOWNLOAD:
				if(!headers_sent()){
					header('Content-Description: File Transfer');
					header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
					header('Pragma: public');
					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
					// force download dialog
					header('Content-Type: application/force-download');
					header('Content-Type: application/octet-stream', false);
					header('Content-Type: application/download', false);
					header('Content-Type: application/pdf', false);
					// use the Content-Disposition header to supply a recommended filename
					header('Content-Disposition: attachment; filename="'.basename($file).'";');
					header('Content-Transfer-Encoding: binary');
					header('Content-Length: '.strlen($this->pdf));
					echo $this->pdf;
				}else{
					throw new Exception('WKPDF download headers were already sent.');
				}
				break;
			case self::$PDF_ASSTRING:
				return $this->pdf;
				break;
			case self::$PDF_EMBEDDED:
				if(!headers_sent()){
					header('Content-Type: application/pdf');
					header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
					header('Pragma: public');
					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
					header('Content-Length: '.strlen($this->pdf));
					header('Content-Disposition: inline; filename="'.basename($file).'";');
					echo $this->pdf;
				}else{
					throw new Exception('WKPDF embed headers were already sent.');
				}
				break;
			case self::$PDF_SAVEFILE:
				return file_put_contents($file,$this->pdf);
				break;
			default:
				throw new Exception('WKPDF invalid mode "'.htmlspecialchars($mode,ENT_QUOTES).'".');
		}
		return false;
	}
}

/**
 * This class, which extends WKPDF is for generating a PDF with multiple pages without using CSS page-break.
 */
class WKPDF_MULTI extends WKPDF {
	/**
	 * An array of HTML files, this is used internally.
	 * @var array Array of URLs to files.
	 */
	private $html_files=array();
	/**
	 * An array of URLs.
	 * @var array Array of URLs to pages.
	 */
	private $html_urls=array();
	/**
	 * This function doesn't make sense in this context.
	 */
	public function set_html(){
		die('Calling set_html() not allowed for WKPDF_MULTI class.');
	}
	/**
	 * This function doesn't make sense in this context.
	 */
	public function set_url(){
		die('Calling set_url() not allowed for WKPDF_MULTI class.');
	}
	/**
	 * Add a new HTML page.
	 * @param string $html Content of HTML page.
	 */
	public function add_html($html){
		do{
			$file=$GLOBALS['WKPDF_BASE_PATH'].'tmp/'.mt_rand().'.html';
		} while(file_exists($file));
		if(!file_put_contents($file,$html))throw new Exception('WKPDF write temporary file failed.');
		$this->html_urls[]=$GLOBALS['WKPDF_BASE_SITE'].$GLOBALS['WKPDF_BASE_PATH'].'tmp/'.basename($file);
		$this->html_files[]=$file;
	}
	/**
	 * Add a new page from URL.
	 * @param string $html URL to HTML page.
	 */
	public function add_url($url){
		$this->html_urls[]=$url;
	}
	/**
	 * Cleans TMP folder from used files.
	 */
	protected function clean_tmp(){
		foreach($this->html_files as $file)unlink($file);
	}
	/**
	 * Convert HTML pages to PDF.
	 */
	public function render(){
		$urls='"'.implode('" "',$this->html_urls).'"';
		if($urls=='""'){
			$this->add_html('<html><body><!--EMPTY PDF--></body></html>');
			$urls='"'.implode('" "',$this->html_urls).'"';
		}
		$this->pdf=self::_pipeExec(
			$this->cmd
				.(($this->copies>1)?' --copies '.$this->copies:'')				// number of copies
				.' --orientation '.$this->orient								// orientation
				.' --page-size '.$this->size									// page size
				.($this->toc?' --toc':'')										// table of contents
				.($this->grayscale?' --grayscale':'')							// grayscale
				.(($this->title!='')?' --title "'.$this->title.'"':'')			// title
				.' '.$urls.' -'													// URL and use STDOUT
		);
		if($this->pdf['stdout']=='')self::_retError('WKPDF program error.',$this->pdf);
		if(((int)$this->pdf['return'])>1)self::_retError('WKPDF shell error.',$this->pdf);
		$this->status=$this->pdf['stderr'];
		$this->pdf=$this->pdf['stdout'];
		$this->clean_tmp();
	}
}
?>
Comment by spitfire...@gmail.com, Apr 10, 2012

Here's a revised class fixing a few bugs, adding page headers and footers, and allowing for the use of either the server binary or mreiferson's PHP native extension ( https://github.com/mreiferson/php-wkhtmltox/ )

<?php

/**
 * This class was produced by PHP-4-Business.co.uk and is based on the classes from
 * aur1mas <aur1mas@devnet.lt>  --  https://github.com/aur1mas/Wkhtmltopdf
 * 	and
 * uuf6429@gmail.com / contact@covac-software.com  --  http://code.google.com/p/wkhtmltopdf/wiki/IntegrationWithPhp
 *
 * Authorship and copyright of those classes is unclear - they both claim authorship although the code is largely identical!
 * From:
 * 		https://github.com/aur1mas/Wkhtmltopdf
 * 		@author Aurimas Baubkus aka aur1mas <aur1mas@devnet.lt>
 * 		@license Released under "New BSD license"
 * and
 * 		http://code.google.com/p/wkhtmltopdf/wiki/IntegrationWithPhp
 * 		@copyright 2010 Christian Sciberras / Covac Software.
 * 		@license None. There are no restrictions on use, however keep copyright intact.
 * 			Modification is allowed, keep track of modifications below in this comment block.
 *
 *
 *
 * Manual for wkhtmltopdf 0.10.0 rc2
 * 	http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html
 *
 * Raw settings
 * 	http://www.cs.au.dk/~jakobt/libwkhtmltox_0.10.0_doc/pagesettings.html
 *
 *
 * This class allows to use either the binary program ( http://code.google.com/p/wkhtmltopdf/ )
 * or the PHP extension ( https://github.com/mreiferson/php-wkhtmltox )
 *
 * Note: to use many of the useful parameters (e.g. headers and footers) you need a patched version of QT.
 * This is included within the statically compiled binary, but if you compile the binary yourself or compile the
 * PHP extension then you must patch QT yourself before compilation (see http://code.google.com/p/wkhtmltopdf/wiki/compilation)
 *
 *
 * Written and tested on Centos 5.4 + PHP 5.2.12
 *
 *
 * Sample Usage
 * ------------
 * 
 *     try {
 *         $wkhtmltopdf = new wkhtmltopdf(array('path' => APPLICATION_PATH . '/../public/uploads/'));
 *         $wkhtmltopdf->setTitle("Title");
 *         $wkhtmltopdf->setHtml("Content");
 *         $wkhtmltopdf->output(wkhtmltopdf::MODE_DOWNLOAD, "file.pdf");
 *     } catch (Exception $e) {
 *         echo $e->getMessage();
 *     }
 * 
 *
 *
 * Alternatively
 * -------------
 * 	$headerhtml & $footerhtml will be repeated on each page
 *
 * 
 *     $headerhtml = '<html><head></head><body><table width="100%" border="0"><tr><td width="100%"><img src="' . $_SERVER['HTTP_HOST'] . '/logo.png" /><span style="float:right;font-size:12px">Some Text</span></td></tr></table></body></html>';
 *
 *     $footerhtml = '<html><head></head><body><table width="100%" border="0"><tr><td width="100%" style="text-align:center;font-size:10px;color:blue;">1 Anystreet, Anytown, Anycounty&nbsp;&nbsp;&nbsp;tel: 01234 567890&nbsp;&nbsp;&nbsp;mail@address.co.uk</td></tr></table></body></html>';
 *
 *     $wkhtmloptions .= '--header-spacing 5 --footer-spacing 2 --grayscale --margin-top 15';
 *
 *     $pdf = new wkhtmltopdf(array( 'title'         => 'Title',
 *                                   'html'          => 'Content',
 *     	                             'tmppath'       => $_SERVER['DOCUMENT_ROOT'].'tmp',
 *     	                             'binpath'       => $_SERVER['DOCUMENT_ROOT'].'bin/',
 *     	                             'header_html'   => $headerhtml,
 *     	                             'footer_html'   => $footerhtml,
 *     	                             'options'       => $wkhtmloptions,
 *     	                            ) );
 *     $pdf->output('I', 'document.pdf');
 * 
 *
 *
 *
 * PHP extension
 * -------------
 * To use the PHP extension instantiate the object with the 'php' param.
 * Note: when using the PHP extension you must use the appropriate 'raw' parameters (e.g. 'header.center') and NOT
 * the binary ones (e.g. '--header-center').
 *    also make sure you put the param in the correct array - some settings are "global" and some are "object" (see the manual)
 *
 *     $wkhtmloptions['global'] = array( 'colorMode' => 'grayscale', 'margin.top' => '15mm' );
 *     $wkhtmloptions['object'] = array( 'header.spacing' => '5mm', 'footer.spacing' => '2mm' );
 *
 *     $pdf = new wkhtmltopdf(array( 'title'         => 'Title',
 *                                   'html'          => 'Content',
 *                                   'tmppath'       => $_SERVER['DOCUMENT_ROOT'].'tmp',
 *                                   'header_html'   => $headerhtml,
 *                                   'footer_html'   => $footerhtml,
 *     	                             'options'       => $wkhtmloptions,
 *                                 ) ,'php');
 *
 *     $pdf->output('I', 'document.pdf');
 *
 *
 *
 *
 * When using output() the mode param takes one of 4 values:
 *
 * 'D'  (const MODE_DOWNLOAD = 'D')  - Force the client to download PDF file
 * 'S'  (const MODE_STRING = 'S')    - Returns the PDF file as a string
 * 'I'  (const MODE_EMBEDDED = 'I')  - When possible, force the client to embed PDF file
 * 'F'  (const MODE_SAVE = 'F')      - PDF file is saved on the server. The path+filename is returned.
 *
 * But note that the user's browser settings may override what you ask for!
 *
 *
 */



/**
 * @version 1.01
 */
class wkhtmltopdf
{
    /**
     * Setters / getters properties
     */
    protected $_method = null;
    protected $_html = null;
    protected $_httpurl = null;
    protected $_orientation = null;
    protected $_pageSize = null;
    protected $_toc = false;
    protected $_copies = 1;
    protected $_grayscale = false;
    protected $_title = null;
    protected $_headerHtml;
    protected $_footerHtml;
    protected $_httpusername;
    protected $_httppassword;
    protected $_options;

    /**
     * What type of input are we processing?  (url / disc file / html string)
     *
     * @var unknown_type
     */
    protected $_have_httpurl = false;
    protected $_have_htmlfile = false;
    protected $_have_html = false;

    /**
     * Location of wkhtmltopdf executable
     */
    protected $_binpath = '/usr/bin/';
    protected $_binname = 'wkhtmltopdf';

    /**
     * Location of HTML file
     */
    protected $_htmlfilepath = '/tmp/';
    protected $_htmlfilename = null;
    protected $_tmphtmlfilename = null;

   	/**
     * Directory to use for temporary files
     */
    protected $_tmpfilepath = '/tmp/';

    /**
     * Temporary files holding header / footer HTML
     */
    protected $_have_headerhtml = false;
    protected $_have_footerhtml = false;
    protected $_headerfilename = null;
    protected $_footerfilename = null;

    /**
     * Available page orientations
     */
    const ORIENTATION_PORTRAIT = 'Portrait';    // vertical
    const ORIENTATION_LANDSCAPE = 'Landscape';  // horizontal

    /**
     * Page sizes
     */
    const SIZE_A4 = 'A4';
    const SIZE_LETTER = 'letter';

    /**
     * PDF get modes
     */
    const MODE_DOWNLOAD = 'D';									// Force the client to download PDF file
    const MODE_STRING = 'S';										// Returns the PDF file as a string
    const MODE_EMBEDDED = 'I';									// When possible, force the client to embed PDF file
    const MODE_SAVE = 'F';											// PDF file is saved on the server. The path+filename is returned.

    /**
     * Constructor: initialize command line and reserve temporary file.
     * @param array $options
     * @param string $method	method to call wkhtmltopdf: 'exec'=binary executable, 'php'=php extension
     * @return bool FALSE on failure
     */
    public function __construct(array $options = array(), $method='exec')
    {
        switch ($method) {
    			case  'exec':
		        if (array_key_exists('binpath', $options)) {
		            $this->setBinPath($options['binpath']);
		        }

		        if (array_key_exists('binfile', $options)) {
		            $this->setBinFile($options['binfile']);
		        }

		        /* Check the binary executable exists */
						$this->getBin();
						break;

    			case 'php':
    				break;

    			default:
    				throw new Exception('WKPDF unknown method "'.htmlspecialchars($method,ENT_QUOTES).'"');
    				return false;
				}

				$this->setMethod($method);

				// Common to both 'exec' and 'php' method

				if (array_key_exists('html', $options)) {
			      $this->setHtml($options['html']);
				}

				if (array_key_exists('orientation', $options)) {
				    $this->setOrientation($options['orientation']);
				} else {
				    $this->setOrientation(self::ORIENTATION_PORTRAIT);
				}

				if (array_key_exists('page_size', $options)) {
            $this->setPageSize($options['page_size']);
        } else {
            $this->setPageSize(self::SIZE_A4);
        }

        if (array_key_exists('toc', $options)) {
            $this->setTOC($options['toc']);
        }

        if (array_key_exists('grayscale', $options)) {
            $this->setGrayscale($options['grayscale']);
        }

        if (array_key_exists('title', $options)) {
            $this->setTitle($options['title']);
        }

        if (array_key_exists('header_html', $options)) {
				    $this->setHeaderHtml($options['header_html']);
				}

				if (array_key_exists('footer_html', $options)) {
				    $this->setFooterHtml($options['footer_html']);
				}

				if (array_key_exists('tmppath', $options)) {
				    $this->setTmpPath($options['tmppath']);
				}

				if (array_key_exists('options', $options)) {
            $this->setOptions($options['options']);
        }

    }

    /**
     * Attempts to return the library's full help info
     *
     * @return string
     */
    public function getHelp()
    {
        $r = $this->_exec($this->getBin() . " --extended-help");
        return $r['stdout'];
    }

    /**
     * Set method to call wkhtmltopdf
     *
     * @return null
     */
    public function setMethod($method)
    {
        $this->_method = $method;
        return;
    }

    /**
     * Get method to call wkhtmltopdf
     *
     * @return string
     */
    public function getMethod()
    {
    	return $this->_method;
    }

    /**
     * Set path to binary executable directory
     *
     * @throws Exception
     * @return null
     */
    public function setBinPath($path)
    {
        if (realpath($path) === false)
            throw new Exception('Path must be absolute ("'.htmlspecialchars($path,ENT_QUOTES).'")');

        $this->_binpath = realpath((string)$path) . DIRECTORY_SEPARATOR;
        return;
    }

    /**
     * Get path to binary executable
     *
     * @return string
     */
    public function getBinPath()
    {
    	return $this->_binpath;
    }

    /**
     * Set filename of binary executable
     *
     * @return null
     */
    public function setBinFile($name)
    {
        $this->_binname = (string)$name;
    		return;
    }

    /**
     * Get filename of binary executable
     *
     * @return string
     */
    public function getBinFile()
    {
    	return $this->_binname;
    }

    /**
     * Get the binary executable
     *
     * @throws Exception
     * @return string
     */
    public function getBin()
    {
        $bin = $this->getBinPath() . $this->getBinFile();

        if (realpath($bin) === false)
            throw new Exception('Path must be absolute ("'.htmlspecialchars($bin,ENT_QUOTES).'")');
        if (file_exists($bin) === false)
            throw new Exception('WKPDF static executable "'.htmlspecialchars($bin,ENT_QUOTES).'" was not found');

        return $bin;
    }

    /**
     * Set absolute path where to store temporary HTML files
     *
     * @throws Exception
     * @param string $path
     * @return null
     */
    public function setTmpPath($path)
    {
        if (realpath($path) === false)
            throw new Exception('Path must be absolute ("'.htmlspecialchars($path,ENT_QUOTES).'")');

        $this->_tmpfilepath = realpath($path) . DIRECTORY_SEPARATOR;
        return;
    }

    /**
     * Get path where to store temporary HTML files
     *
     * @return string
     */
    public function getTmpPath()
    {
        return $this->_tmpfilepath;
    }

    /**
     * Set absolute path where to read HTML file
     *
     * @throws Exception
     * @param string $path
     * @return null
     */
    public function setHtmlPath($path)
    {
        if (realpath($path) === false)
            throw new Exception('Path must be absolute ("'.htmlspecialchars($path,ENT_QUOTES).'")');

        $this->_htmlfilepath = realpath($path) . DIRECTORY_SEPARATOR;
        return;
    }

    /**
     * Get path where to read HTML file
     *
     * @return string
     */
    public function getHtmlPath()
    {
        return $this->_htmlfilepath;
    }

    /**
     * Set filename holding HTML
     *
     * @return null
     */
    public function setHtmlFile($name)
    {
        $this->_htmlfilename = (string)$name;
        $this->_have_htmlfile = true;
    		return;
    }

    /**
     * Get filename holding HTML
     *
     * @return string
     */
    public function getHtmlFile()
    {
    	return $this->_htmlfilename;
    }

    /**
     * Get the path+filename holding HTML
     *
     * @throws Exception
     * @return string
     */
    public function getHtmlPathFile()
    {
    		$file = $this->getHtmlPath() . $this->getHtmlFile();

        if (realpath($file) === false)
            throw new Exception('Path must be absolute ("'.htmlspecialchars($file,ENT_QUOTES).'")');
        if (file_exists($file) === false)
            throw new Exception('HTML file "'.htmlspecialchars($file,ENT_QUOTES).'" was not found');

        return $file;
    }

    /**
     * Set page orientation (default is portrait)
     *
     * @param string $orientation
     * @return null
     */
    public function setOrientation($orientation)
    {
        $this->_orientation = (string)$orientation;
        return;
    }

    /**
     * Returns page orientation
     *
     * @return string
     */
    public function getOrientation()
    {
        return $this->_orientation;
    }

    /**
     * Set page/paper size (default is A4)
     * @param string $size
     * @return null
     */
    public function setPageSize($size)
    {
        $this->_pageSize = (string)$size;
        return;
    }

    /**
     * Returns page size
     *
     * @return int
     */
    public function getPageSize()
    {
        return $this->_pageSize;
    }

    /**
     * Automatically generate a TOC (table of contents) or not (default is disabled)
     *
     * @param boolean $toc
     * @return Wkhtmltopdf
     */
    public function setTOC($toc = true)
    {
        $this->_toc = (boolean)$toc;
        return;
    }

    /**
     * Get value of whether automatic Table Of Contents generation is set
     *
     * @return boolean
     */
    public function getTOC()
    {
        return $this->_toc;
    }

    /**
     * Set the number of copies to make (default is 1)
     *
     * @param int $copies
     * @return null
     */
    public function setCopies($copies)
    {
        $this->_copies = (int)$copies;
        return;
    }

    /**
     * Get number of copies to make
     *
     * @return int
     */
    public function getCopies()
    {
        return $this->_copies;
    }

    /**
     * Whether to print in grayscale or not (default is off)
     *
     * @param boolean $mode
     * @return null
     */
    public function setGrayscale($mode)
    {
        $this->_grayscale = (boolean)$mode;
        return;
    }

    /**
     * Get if page will be printed in grayscale
     *
     * @return boolean
     */
    public function getGrayscale()
    {
        return $this->_grayscale;
    }

    /**
     * Set PDF title (default is HTML <title> of first document)
     *
     * @param string $title
     * @return null
     */
    public function setTitle($title)
    {
        $this->_title = (string)$title;
        return;
    }

    /**
     * Get PDF document title
     *
     * @throws Exception
     * @return string
     */
    public function getTitle()
    {
        return $this->_title;
    }

    /**
     *  Set header html (default is null)
     *
     * @param string $header
     * @return null
     */
    public function setHeaderHtml($header)
    {
        $this->_headerHtml = (string)$header;
        $this->_have_headerhtml = true;
        return;
    }

    /**
     * Get header html
     *
     * @return string
     */
    public function getHeaderHtml()
    {
        return $this->_headerHtml;
    }

    /**
     *  Set footer html (default is null)
     *
     * @param string $footer
     * @return null
     */
    public function setFooterHtml($footer)
    {
        $this->_footerHtml = (string)$footer;
        $this->_have_footerhtml = true;
        return;
    }

    /**
     * Get footer html
     *
     * @return string
     */
    public function getFooterHtml()
    {
        return $this->_footerHtml;
    }

    /**
     * Set http username
     *
     * @param string $username
     * @return null
     */
    public function setUsername($username)
    {
        $this->_httpusername = (string)$username;
        return;
    }

    /**
     * Get http username
     *
     * @return string
     */
    public function getUsername()
    {
        return $this->_httpusername;
    }

    /**
     * Set http password
     *
     * @param string $password
     * @return null
     */
    public function setPassword($password)
    {
        $this->_httppassword = (string)$password;
        return;
    }

    /**
     * Get http password
     *
     * @return string
     */
    public function getPassword()
    {
        return $this->_httppassword;
    }

		/**
     *  Set any other WKTMLTOPDF options you need
     *
     * @param string $options
     * @return null
     */
    public function setOptions($options)
    {
        $this->_options = $options;
        return;
    }

    /**
     * Get any other WKTMLTOPDF options you need
     *
     * @return string
     */
    public function getOptions()
    {
        return $this->_options;
    }

    /**
     * Set URL to render
     *
     * @param string $html
     * @return null
     */
    public function setHttpUrl($url)
    {
        $this->_httpurl = (string) $url;
        $this->_have_httpurl = true;
        return;
    }

    /**
     * Get URL to render
     *
     * @return string
     */
    public function getHttpUrl()
    {
        return $this->_httpurl;
    }

    /**
     * Set HTML content to render (replaces any previous content)
     *
     * @param string $html
     * @return null
     */
    public function setHtml($html)
    {
        $this->_html = (string)$html;
        $this->_have_html = true;
        return;
    }

    /**
     * Get current HTML content
     *
     * @return string
     */
    public function getHtml()
    {
        return $this->_html;
    }

    /**
     * Create a temporary file & store the html content
     *
     * @return string	Full path to file
     */
    protected function _createFile($html)
    {
        $file = $this->_makeFilename();

        file_put_contents($file, $html);
        chmod($file, 0764);

        return $file;
    }

    /**
     * Create a temporary filename
     *
     * @throws Exception
     * @return string
     */
    protected function _makeFilename()
    {
        if (($path = $this->getTmpPath()) == '') {
            throw new Exception("Path to directory where to store files is not set");
        }
        if (realpath($path) === false)
            throw new Exception('Path must be absolute ("'.htmlspecialchars($path,ENT_QUOTES).'")');

        do {
            $file = mt_rand() . '.html';
        } while(file_exists($path.$file));

        return $path.$file;
    }

    /**
     * Delete a temporary file
     *
     * @param string fn	Filename to delete (optional)
     * @return null
     */
    protected function _deleteFile($fn='')
    {
        if ($fn !== '') {
        	unlink($fn);

        } else {
        	// delete our temporary files
        	if ($this->_have_html && $this->_tmphtmlfilename) {
						unlink($this->_tmphtmlfilename);
	        }
	        if ($this->_have_headerhtml && $this->_headerfilename) {
	        	unlink($this->_headerfilename);
	        }
	        if ($this->_have_footerhtml && $this->_footerfilename) {
	        	unlink($this->_footerfilename);
	        }
        }

        return;
    }

    /**
     * Returns command to execute
     *
     * @param string	filename of input html
     * @return string
     */
    protected function _getCommand($in)
    {
        $command = '';

        switch ($this->getMethod()) {
        	case 'exec':
        			$command = $this->getBin();

        			$command .= " --orientation " . $this->getOrientation();
			        $command .= " --page-size " . $this->getPageSize();
			        $command .= ($this->getTOC()) ? " --toc" : "";
			        $command .= ($this->getGrayscale()) ? " --grayscale" : "";
			        $command .= ($this->getTitle()) ? ' --title "' . $this->getTitle() . '"' : "";
			        $command .= ($this->getCopies() > 1) ? " --copies " . $this->getCopies() : "";
			        $command .= (strlen($this->getPassword()) > 0) ? " --password " . $this->getPassword() . "" : "";
			        $command .= (strlen($this->getUsername()) > 0) ? " --username " . $this->getUsername() . "" : "";
			        $command .= $this->_have_headerhtml ? " --margin-top 20 --header-html \"" . $this->_headerfilename . "\"" : "";
			        $command .= $this->_have_footerhtml ? " --margin-bottom 20 --footer-html \"" . $this->_footerfilename . "\"" : "";
			        $command .= ($this->getOptions()) ? " {$this->getOptions()} " : "";

			        /*
			         * ignore some errors with some urls as recommended with this wkhtmltopdf error message:
			         * 	Error: Failed loading page <url> (sometimes it will work just to ignore this error with --load-error-handling ignore)
			         */
			        if ($this->getHttpUrl()) {
			            // $command .= ' --load-error-handling ignore';
			        }

			        $command .= ' "'.$in.'" ';
			        $command .= " -";

			        break;

        	case 'php':
        			$command = array();

        			$command['global']["orientation"] = $this->getOrientation();
			        $command['global']["size.paperSize"] = $this->getPageSize();
			        ($this->getTOC()) ? $command['global']["toc"] = 1 : "";
			        ($this->getGrayscale()) ? $command['global']["colorMode"] = "Grayscale" : "";
			        ($this->getTitle()) ? $command['global']["documentTitle "] = $this->getTitle() : "";
			        ($this->getCopies() > 1) ? $command['global']["copies "] = $this->getCopies() : "";
			        (strlen($this->getPassword()) > 0) ? $command['object']["load.password"] = $this->getPassword() : "";
			        (strlen($this->getUsername()) > 0) ? $command['object']["load.username "] = $this->getUsername() : "";
			        if ($this->_have_headerhtml) {
			        	$command['global']["margin.top"] = "20mm";
			        	$command['object']["header.htmlUrl"] = 'file://'.$this->_headerfilename;
			        }
			        if ($this->_have_footerhtml) {
			        	$command['global']["margin.bottom"] = "20mm";
			        	$command['object']["footer.htmlUrl"] = 'file://'.$this->_footerfilename;
			        }
			        $options = $this->getOptions();
			        $command['global'] = array_merge($command['global'], $options['global']);
			        $command['object'] = array_merge($command['object'], $options['object']);

        			break;
				}

        return $command;
    }

    /**
     * Convert HTML to PDF.
     *
     * @todo use file cache
     *
     * @throws Exception
     * @return string
     */
    protected function _render()
    {
        if ($this->_have_httpurl) {														// source is url
            $input = $this->getHttpUrl();

        } elseif ($this->_have_htmlfile) {										// source is predefined disc file
        		$input = $this->getHtmlPathFile();

        } elseif ($this->_have_html) {												// source is html string
            $input = $this->_tmphtmlfilename = $this->_createFile($this->getHtml());

        } else {
            throw new Exception("HTML content or source URL not set");
        }

        if ($this->_have_headerhtml) {
        	$this->_headerfilename = $this->_createFile($this->getHeaderHtml());
        }
        if ($this->_have_footerhtml) {
        	$this->_footerfilename = $this->_createFile($this->getFooterHtml());
        }

        $command = $this->_getCommand($input);

        // error_log((is_array($command)?print_r($command,true):$command));	// for debug

        switch ($this->getMethod()) {
        	case 'exec':

        			/* Deprecated - use _pipeExec
			        	$content = $this->_exec(str_replace('%input%', $input, $this->_getCommand())); */

			        $content = $this->_pipeExec($command);

			        if (strpos(strtolower($content['stderr']), 'error'))
									throw new Exception("System error <pre>" . $content['stderr'] . "</pre>");

			        if (strlen($content['stdout']) === 0)
									throw new Exception("WKHTMLTOPDF didn't return any data");

			        if ((int)$content['return'] > 1)
			            throw new Exception("Shell error, return code: " . (int)$content['return']);

			        $data = $content['stdout'];

			        break;

        	case 'php':
        			$command['global']['out'] = $pdffile = $this->_makeFilename();
        			$command['object']['page'] = $input;

        			// error_log(print_r($command['global'],true));	// for debug
        			// error_log(print_r($command['object'],true));	// for debug

        			wkhtmltox_convert('pdf', $command['global'], array($command['object']));

            	$data = file_get_contents($pdffile);
            	$this->_deleteFile($pdffile);

            	break;
        }

        return (isset($data)?$data:false);
    }

    /**
     * Executes the command :  Deprecated - use _pipeExec
     *
     * @param string $cmd   command to execute
     * @param string $input other input (not arguments)
     * @return array
     */
    protected function _exec($cmd, $input = "")
    {
        $result = array('stdout' => '', 'stderr' => '', 'return' => '');

        $proc = proc_open($cmd, array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes);
        fwrite($pipes[0], $input);
        fclose($pipes[0]);

        $result['stdout'] = stream_get_contents($pipes[1]);
        fclose($pipes[1]);

        $result['stderr'] = stream_get_contents($pipes[2]);
        fclose($pipes[2]);

        $result['return'] = proc_close($proc);

        return $result;
    }

    /**
		 * Advanced execution routine.
		 *
		 * @param string $cmd The command to execute.
		 * @param string $input Any input not in arguments.
		 * @return array An array of execution data; stdout, stderr and return "error" code.
		 */
		private static function _pipeExec($cmd, $input=''){
				$pipes = array();
        $proc = proc_open($cmd, array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes, null, null, array('binary_pipes'=>true));
				fwrite($pipes[0], $input);
				fclose($pipes[0]);

        // From http://php.net/manual/en/function.proc-open.php#89338
        $read_output = $read_error = false;
        $buffer_len  = $prev_buffer_len = 0;
        $ms          = 10;
        $stdout      = '';
        $read_output = true;
        $stderr      = '';
        $read_error  = true;
        stream_set_blocking($pipes[1], 0);
        stream_set_blocking($pipes[2], 0);

        // dual reading of STDOUT and STDERR stops one full pipe blocking the other, because the external script is waiting
        while ($read_error != false or $read_output != false){
            if ($read_output != false){
                if(feof($pipes[1])){
                    fclose($pipes[1]);
                    $read_output = false;
                } else {
                    $str = fgets($pipes[1], 1024);
                    $len = strlen($str);
                    if ($len){
                        $stdout .= $str;
                        $buffer_len += $len;
                    }
                }
            }

            if ($read_error != false){
                if(feof($pipes[2])){
                    fclose($pipes[2]);
                    $read_error = false;
                } else {
                    $str = fgets($pipes[2], 1024);
                    $len = strlen($str);
                    if ($len){
                        $stderr .= $str;
                        $buffer_len += $len;
                    }
                }
            }

            if ($buffer_len > $prev_buffer_len){
                $prev_buffer_len = $buffer_len;
                $ms = 10;
            } else {
                usleep($ms * 1000); // sleep for $ms milliseconds
                if ($ms < 160){
                    $ms = $ms * 2;
                }
            }
        }

        $rtn = proc_close($proc);
        return array(
                      'stdout' => $stdout,
                      'stderr' => $stderr,
                      'return' => $rtn
                		);
		}

		/**
     * Return PDF with various options.
     *
     * @param int $mode		     	How to output (constants from this same class - c.f. 'PDF get modes')
     * @param string $filename	The PDF's filename (usage depends on $mode)
     */
    public function output($mode, $filename='')
    {
        switch ($mode) {
            case self::MODE_DOWNLOAD:
                if (!headers_sent()) {
                    $result = $this->_render();
                    header("Content-Description: File Transfer");
                    header("Cache-Control: public; must-revalidate, max-age=0"); // HTTP/1.1
                    header("Pragme: public");
                    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
                    header("Last-Modified: " . gmdate('D, d m Y H:i:s') . " GMT");
                    header("Content-Type: application/force-download");
                    header("Content-Type: application/octet-stream", false);
                    header("Content-Type: application/download", false);
                    header("Content-Type: application/pdf", false);
                    header('Content-Disposition: attachment; filename="' . basename($filename) .'";');
                    header("Content-Transfer-Encoding: binary");
                    header("Content-Length:" . strlen($result));
                    echo $result;
                   	$this->_deleteFile();
                    exit();
                } else {
                    throw new Exception("Headers already sent");
                }
                break;
            case self::MODE_STRING:
                return $this->_render();
                break;
            case self::MODE_EMBEDDED:
                if (!headers_sent()) {
                    $result = $this->_render();
                    header("Content-type: application/pdf");
                    header("Cache-control: public, must-revalidate, max-age=0"); // HTTP/1.1
                    header("Pragme: public");
                    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
                    header("Last-Modified: " . gmdate('D, d m Y H:i:s') . " GMT");
                    header("Content-Length: " . strlen($result));
                    header('Content-Disposition: inline; filename="' . basename($filename) .'";');
                    echo $result;
                   	$this->_deleteFile();
                    exit();
                } else {
                    throw new Exception("Headers already sent");
                }
                break;
            case self::MODE_SAVE:
                file_put_contents($filename, $this->_render());
                $this->_deleteFile();
                break;
            default:
                throw new Exception("Mode: " . $mode . " is not supported");
        }
    }
}

?>
Comment by haertl.mike@gmail.com, Apr 25, 2012

You can also check out this slim wrapper class. I tried to create a very clean and slim OOP interface: https://github.com/mikehaertl/phpwkhtmltopdf

Comment by Jubair.S...@p3in.com, May 22, 2012

what's a good way to send email pdf attachement? am I saving the file or embedding it?


Sign in to add a comment
Powered by Google Project Hosting