Creating Professional Documents the Easy Way

Have you ever tried to create a professional quality document programmatically from within PHP? It’s a real pain and surprisingly tricky to do. There are limited options for PHP developers to produce files in the formats most often used in business. There are libraries for producing PDF files in PHP but they are complex and are difficult to use for producing documents. The options to create Microsoft Word files seem to be even more limited. It is possible to create RTF files from PHP but as these are not the most commonly used format in business the user will have to convert the file to another format before saving it. I’m working on a project where one of the requirements is for a user to download a a professional quality document with variable content, which is determined by their answers to a questionnaire. I was looking at having to create RTF files before I stumbled on a great service offered by Live Docx that seems to offer the holy grail of programmatically creating professional quality documents with minimum hassle.

What is LiveDocx?

The Live Docx service allows a programmer to take a document template and through a SOAP based web service populate it with content. The resulting document can then be retrieved to the web server in one of many common formats including PDF, MS Word .doc and .docx and RTF. Best of all the service is completely free at the lowest level, but depending on the requirements of your application it may be more appropriate to use one of the paid for levels of service. The templates can be created in .doc, .docx, .rtf and .txd formats and use mail merge to populate the content. The great advantage is that you can use all of the features of a word processing program to design your document, ending up with a fully professional quality document. Best of all you don’t even need to write any code to access the web service. The latest release of the Zend Framework (1.10) includes a component, Zend_Service_LiveDocx, to access and work with the service and there’s also a .NET library to download for Microsoft developers.

Creating Templates

As mentioned earlier the LiveDocx service uses mail merge fields to perform its magic. You create a document that contains one or more merge fields and then assign content to these fields programmatically at run time. The LiveDocx servers replace the mail merge fields with the content in the same way as a word processing program would, making the completed document available to you. Templates can either be stored directly on the LiveDocx servers or on your own webserver. The SOAP service allows to you specify the name of the template you want to use and whether it is stored locally or remotely. Which one is  chosen will largely depend on the application but storing templates on your own web server does mean that the template must be sent to LiveDocx along with the content before the completed document can be retrieved. This obviously significantly increases the amount of information that needs to be transmitted over the internet but the demands of your application may dictate that templates be stored locally.

Using Zend_Service_LiveDocx

The Zend Framework LiveDocx component is very easy to use and complete documentation can be found here. The Zend Framework download also includes several examples of using the service. Basically you instantiate a Zend_Service_LiveDocx_MailMerge object, setting your username, password and the template you wish to use, before assigning content to the object in much the same way that you would with Smarty. You then call a createDocument() method to produce the document on the remote server followed by a retrieveDocument() method to get the document produced back as a string. The latter method takes an argument which specifies the type of file that should be created. That’s basically it. The code below is a simple view class that I wrote for my application that uses the Zend_Service_LiveDocx to produce a document.


<?php
 /**
 * View for the download plan functionality. Uses the LiveDocx web service and the ZendFramework LiveDocx SOAP integration to produce a file.
 * @author Jeremy Cook
 * @version 1.0
 * @see http://framework.zend.com/manual/en/zend.service.livedocx.html
 */
 class planView {
 /**
 * Array of content to be used in the plan
 *
 * @var array
 */
 protected $content = array();
 /**
 * The type of file to be requested from LiveDocx
 *
 * @var string
 */
 protected $fileType;
 /**
 * Name of the template on LiveDocx to use
 *
 * @var string
 */
 protected $template;
 /**
 * Username for LiveDocx
 *
 * @var string
 */
 protected $username;
 /**
 * Password for LiveDocx
 *
 * @var string
 */
 protected $password;
 /**
 * Name to give the downloaded file
 *
 * @var string
 */
 protected $filename;
 /**
 * Constructor
 *
 * @param array $content
 * @param string $fileType Type of file to be requested from LiveDocx
 * @param string $template Name of the remote template to use on LiveDocx
 * @param string $filename Name to use for the file produced by LiveDocx
 * @param string $username
 * @param string $password
 * @return planView
 */
 public function __construct (array $content, $fileType, $template, $filename, $username, $password) {
    $this->content = $content;
    $this->fileType = $fileType;
    $this->template = $template;
    $this->filename = $filename;
    $this->username = $username;
    $this->password = $password;
 }
 /**
 * Method to produce the plan document
 *
 * @throws Zend_Service_LiveDocx_Exception
 * @throws RuntimeException
 */
 public function getPlan () {
    require_once 'Zend/Service/LiveDocx/MailMerge.php';
    $doc = new Zend_Service_LiveDocx_MailMerge();
    $doc->setUsername($this->username)->setPassword($this->password);
    $doc->setRemoteTemplate($this->template);
    foreach ($this->content as $key => $value) {
       $doc->assign($key, $value);
    }
    $doc->createDocument();
    $document = $doc->retrieveDocument($this->fileType);
    unset($doc);
    $output = fopen('php://output', 'w');
    if (!$output)
       throw new RuntimeException('Unable to bind PHP output as a file handle');
    switch ($this->fileType) {
       case 'pdf':
          header ('Content-Type: application/pdf');
          break;
       case 'doc':
       case 'docx':
          header ('Content-Type: application/vnd.ms-word');
          break;
    }
    $filename = str_replace(' ', '_', $this->filename) . ".{$this->fileType}";
    header ("Content-Disposition: attachment; filename=\"$filename\"");

    //Make sure the browser doesn't cache the file
    //Set headers to ensure the document is not cached.
    header ("Expires: Mon, 26 Jul 1990 05:00:00 GMT");
    header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    header ("Cache-Control: no-cache, must-revalidate");
    header ("Pragma: no-cache");
    //Write the document to php output
    fwrite($output, $document);
    fclose($output);
 }
 }
?>

Some ‘Gotchas’

The two main problems I had in using the LiveDocx service had nothing to do with LiveDocx or Zend_Service_LiveDocx (which has great documentation). The problems I had were in making MS Word do what I wanted in the template creation. Admittedly this may have more to do with the fact that I’d never created a mail merge template before but I thought I’d mention the two issues here. The issues were in adding merge fields to the document and creating bookmarks.

Adding Merge Fields

Merge fields are denoted by the text {MERGEFIELD fieldName} (where ‘fieldName’ is the unique name of the field). What the documentation doesn’t say however is that the curly braces cannot be simply typed on the keyboard. In Word you need to enter ctrl + F9 to get a special set of merge field curly braces. In Word 2007 you can also go to the insert tab then Quick Parts, [field] and select merge field. Add a name for the merge field and it is inserted into the document where your cursor is placed. This second method makes the field look like «fieldName» but this is completely equivalent to the first method with the curly braces.

Adding Bookmarks

With LiveDocx a bookmarked section allows you to iterate over a section of a template, assigning content from an array to merge fields inside the bookmark on each iteration. This enables you to produce sections of documents that have the same structure but different content. This is obviously very handy as varying amounts of content can be assigned to the section of the template. One of the examples in the Zend Framework utilises this to produce a telephone bill where a varying number of calls can be assigned to a bookmarked section, producing a table of all the calls in the final document. The array has to be multi-dimensional, with the top level array being numeric and each nested array being associative where the key is the name of the merge field and the value is the content to be inserted. What the documentation doesn’t tell you is how to create the bookmarks (there’s supposed to be a screenshot in the documentation which is missing) and I spent a few frustrating hours before I finally found out how to do it. Here are the steps.

  1. Go to the insert tab in Word and select bookmark.
  2. Create a new bookmark that begins with blockStart_ (ie. blockStart_bookmarkName).
  3. The bookmark will be invisible in the document but go ahead and add your merge fields with whatever formatting you like.
  4. After the last merge field for the section add another bookmark but start it with blockEnd_ (ie. blockEnd_bookmarkName).
  5. When you assign content to the bookmarked section make sure that the array is assigned to the name of the bookmark, like the following:

<?php

//Assume $doc is a Zend_Service_LiveDocx_MailMerge object and $array is the multidimensional array to be assigned to the bookmark.

$doc->assign('bookmarkName', $array);

?>

Conclusion

I hope you find this brief introduction to LiveDocx useful. I’ve found it to be a great service that’s easy to use and I’ll certainly use it again the next time I need to produce professional quality documents from within PHP.

5 thoughts on “Creating Professional Documents the Easy Way

    1. Hi Fatih,

      I don’t think this is possible. LiveDocx works on the basis of placeholder fields being replaced with content at runtime. Obviously without a template there wouldn’t be any placeholders to replace. Perhaps there are other libraries that allow you to create word documents from scratch but it’s not what LiveDocx is about.

  1. Great idea to explain how to create bookmark in MS Word. The same explanation based on the same example is duplicated many times all over the web but no one -except you of course- is able to teach about bookmark (and not “block” as it’s written!
    Thanks a lot.

  2. Jeremy,
    me again 😉 one more question about LiveDocx: do you always use it or do you have another better solution to generate PDF on the fly?
    Thanks

Leave a Reply