Friday, February 8, 2013

Printing a PDF like Google Docs


The goal is to have users able to click a link, and get a new tab/window with the Print dialog open, ready to go, in the same manner as how Google Docs' print function works. It would also be nice if the new tab closed after the print dialog did (i.e. after user has either printed or cancelled).

Turns out, with a little bit of javascript and a beta of DOMPDF, this is very possible! I used 0.6.0 beta 3 for this. Earlier versions may work, but I didn't try them.

For this to work, we need two outputs. The first script (or action) will output the raw PDF document. The second is just some "wrapper" HTML, to render the PDF as an inline object and handle closing the tab/window after the printing dialog is dismissed.

PDF output: (for this example, we'll assume this is at http://localhost/pdf.php)

require_once('path/to/dompdf/dompdf_config.inc.php');

header('Content-type: application/pdf');

$trigger = "
    <script type='text/javascript'>
        this.print();
        this.closeDoc(true);
    </script>
";

$dompdf = new DOMPDF();
$dompdf->load_html("<html><body><b>Hello, web printing!</b>{$trigger}</body></html>");
$dompdf->render();

echo $dompdf->output();


Wrapper html:



<html>
    <body marginwidth="0" marginheight="0" style="background-color: rgb(38,38,38)">
        <embed width="100%" height="100%" name="pdfObject" id="pdfObject" src="http://localhost/pdf.php" type="application/pdf">

        
    </body>

    <script> 
        window.onfocus=function(){ window.close();}
    </script> 

</html>

Opening the wrapper in a browser, you'll get an embedded copy of the PDF file, and the browser's print window should open automatically. Once you've finished with the print dialog, the tab/window should close. I've only tested this with Chrome, so your mileage may vary. IE does seem to have issues with the window.close in the wrapper.

The way it works, is by embedding a little bit of javascript in the PDF itself - that's the $trigger variable in the first output. This causes the PDF viewer to open the print dialog. In the second output, putting a window.close in the window.onfocus event is what causes the window/tab to close out once the print dialog is dismissed. This is a little hackish, but seems to work OK in Chrome.

If the <script> tag in the wrapper is placed inside the body, the focus event does seem to fire prior to the print dialog closing. Having a script tag outside the body isn't exactly kosher, so, it's up to you and your user's needs whether or not it's worth it.

http://www.snazzware.com/blog/pdfprint/index.html

The full source for the example (including dompdf 0.6.0 beta) can be downloaded here:

http://www.snazzware.com/blog/pdfprint.tar.gz




Saturday, June 30, 2012

Google Cloud Print with the Zend Framework

This is a quick tutorial on how to use Google Cloud Print from PHP under the Zend Framework. Parts of this are based on other online tutorials (including Mimeo's tutorial). See also https://developers.google.com/cloud-print/docs/overview

First, we need to get a cloud printer set up. Follow the directions here: http://support.google.com/cloudprint/bin/answer.py?&answer=1686197

Now, we need to determine the GUID of the cloud printer you set up. The following code will query the cloudprint service and dump the results of the "search" command:

<?

require_once 'Zend/Loader/Autoloader.php';

$autoloader = Zend_Loader_Autoloader::getInstance();

$username = 'username@gmail.com';
$password = 'password';

$client = Zend_Gdata_ClientLogin::getHttpClient($username, $password, 'cloudprint');

// Get token, add headers, set uri
$Client_Login_Token = $client->getClientLoginToken();
$client->setHeaders('Authorization','GoogleLogin auth='.$Client_Login_Token);
$client->setUri('http://www.google.com/cloudprint/interface/search');

$response = $client->request(Zend_Http_Client::POST);

$PrinterResponse = json_decode($response->getBody());

if (isset($PrinterResponse->printers)) {
    foreach ($PrinterResponse->printers as $printer) {
        echo ("{$printer->id}\t{$printer->name}\r\n");
    }
} else {
    echo "Something went wrong!\r\n";
    print_r($PrinterResponse);
}


?>

The output (from command line) will look something like this:

4d267d11-4135-0710-d0b2-18aacfeff12d Microsoft XPS Document Writer
__google__docs Save to Google Docs
1c2ea9f6-2b17-0cc4-507f-b59cf1592763 Fax
390272cc-fe27-b4fe-c60f-eba7db8c6b63 Send To OneNote 2007

The printer that I'll be using for this tutorial is the first one, "Microsoft XPS Document Writer". Locate your printer in the output, and make a note of the GUID (e.g. 4d267d11-4135-0710-d0b2-18aacfeff12d

For my purposes, I did not need to print PDFs to cloud print, I needed to print some existing html-based reports. To get HTML in to a format that cloud print would accept, I used the DOMPDF library.

DOMPDF comes with its own autoloader, but it can easily be made to work with Zend's autoloader:


require_once('path/to/dompdf/dompdf_config.inc.php');


$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->pushAutoloader('DOMPDF_autoload','');


Once DOMPDF is set up, we can create a PDF from some HTML:


$dompdf = new DOMPDF();
$dompdf->load_html('<html><body><b>Hello, Google Cloud Print!</b></body></html>');
$dompdf->render();
$data = $dompdf->output();


Cloud Print current supports content types of url, application/pdf, image/jpeg, and image/png. For the URL type, the resource you reference must be a pdf, jpeg, or png. If you already have your data in one of these three formats, you don't need to bother with DOMPDF.

Here is the complete example, which renders some HTML to a PDF and sends it to Google Cloud Print:


<?

// Configuration

$username = 'username@gmail.com';
$password = 'password';
$printerid = '4d267d11-4135-0710-d0b2-18aacfeff12d';
$jobtitle = 'Description of the print job';


// Autoloader
require_once 'Zend/Loader/Autoloader.php';
require_once('/path/to/dompdf/dompdf_config.inc.php');

$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->pushAutoloader('DOMPDF_autoload','');

// HTML to PDF
$dompdf = new DOMPDF();
$dompdf->load_html('<html><body><b>Hello, Google Cloud Print!</b></body></html>');
$dompdf->render();
$data = $dompdf->output();

// Set up Gdata client and log in
$client = \Zend_Gdata_ClientLogin::getHttpClient($username, $password, 'cloudprint');
$Client_Login_Token = $client->getClientLoginToken();
$client->setHeaders('Authorization','GoogleLogin auth='.$Client_Login_Token);
$client->setUri('http://www.google.com/cloudprint/interface/submit');

// Set parameters
$client->setParameterPost('printerid',$printerid);
$client->setParameterPost('title', $jobtitle);
$client->setParameterPost('contentTransferEncoding','base64');
$client->setParameterPost('content',base64_encode($data));
$client->setParameterPost('contentType','application/pdf');

// Submit and get response
$response = $client->request(\Zend_Http_Client::POST);

// Check response
$PrinterResponse = json_decode($response->getBody());

if (isset($PrinterResponse->success) && $PrinterResponse->success==1) {
    echo "Success! The document should print shortly.\r\n";
} else {
    echo "Failure!\r\n";
    print_r($PrinterResponse);
}


?>

On my setup, when I execute this, after a few seconds a "Save the file as" dialog appears on the Windows computer that I configured the XPS Document Writer on. Saving the file and opening it, I see the following: