Generating PDF Forms From a Flex Application With PHP

July 21, 2008

Tutorials

For end-users, the ability to output a PDF from your application is expected as a standard feature. But as a developer, implementing PDF output can be a major expense and headache, especially when developing for the web. You could just instruct the user to print the necessary pages from a browser to a PDF virtual printer, but that requires extra software on the client, and the browser will add information to the printed page.

Traditional programmatic PDF creation can be painstaking, often requiring the developer to do very complex calculations just to place text on the page. PDF generation on the web usually requires root access and complicated installations on the server. But all that is about to change. Leveraging the power of PHP and a PDF generation library called dompdf, you can create a simple, seamless user experience in Adobe Flex. Flex enables you to create an outstanding presentation to the end-user, with instant validation of user-supplied data and the full power of ActionScript 3. Through AMFPHP, you’ll gain access to PHP’s full toolset, enabling virtually limitless applications.

Installing AMFPHP

Your application will be using AMFPHP to communicate with your server. AMFPHP is an open source bridge between the Adobe Action Message Format (AMF) and the PHP programming language. Here’s how it works, in a nutshell:

  • AMFPHP exposes regular PHP methods to Adobe Flash Player (or Adobe AIR) as remote services.
  • Using ActionScript and MXML (an XML-based markup language) in Adobe Flex Builder, you define the services and how to access them.
  • When the services are called in your application, Flash Player serializes the requests into the AMF binary format and sends them to AMFPHP.
  • AMFPHP receives the AMF data and converts it to the corresponding data type in PHP. Then, the remote method is executed.
  • If there is a return value for the PHP function, AMFPHP serializes the value into AMF, and then sends it to Flash Player or the AIR wrapper.

To download and install AMFPHP, go to http://www.amfphp.org, and then click the download link. A list of available packages appears on SourceForge. This article and the accompanying project use the AMFPHP 1.9 release from January 20, 2008.

After you’ve saved the AMFPHP archive, you must extract the contents into your server’s web root. As a result, all the AMFPHP files should be beneath the webroot/amfphp/ directory. To check the installation, go to http://YourServer/amfphp/gateway.php. You should see a page similar to Figure 1.

If you don’t see a confirmation page, double-check your file structure and permissions. If you get an “Internal Error” (500), try removing the .htaccess file from the /amfphp directory.

If you still need help, go to AMFPHP’s installation page. There, you can find documentation and video tutorials on AMFPHP.

Now that AMFPHP is installed and functioning, you must create services for AMFPHP to expose to your application. AMFPHP can use virtually any PHP method, but you must do a bit of preparation to make sure it can recognize your services. Begin by creating a new PHP file and naming it pdfService.php. On the second line, right after your opening <?php tag, type:

    
require_once("../../dompdf/dompdf_config.inc.php");
class pdfService
{
	function pdfService() {

You will be using dompdf later, and the require_once statement will give you access to it after you install it in the /webRoot/dompdf directory. Note that the class name, file name, and constructor name all match; this is required for AMFPHP to function properly. Next, you need to create a method table, which tells AMFPHP which functions will be made available remotely:

    
		$this->methodTable = array(
			"newPDF" => array(
"description" => "Creates a new PDF",
				"access" => "remote"
			)
		);	
	}

Your method table declares one function, newPDF, to be available remotely. Now, you need to create that function and close out your file:

    
function newPDF($nfo) {
		$name = $nfo[0];
		$addy1 = $nfo[1];
		$addy2 = $nfo[2];
		$csz = $nfo[3] . ', ' . $nfo[4] . ' ' . $nfo[5];
		$emContName = $nfo[6];
		$emContPhone = $nfo[7];
		$age = $nfo[8];
		$html =
		    '<html><body><style type="text/css">
			.addy {
				line-height: 0px;
				text-decoration: underline;
			}
			.addyLabel {
				line-height: 0px;
			}
			</style>
			<body>
			<div align="center"><img src="DM_logo.jpg" width="222" height="120" /></div>
			<h2 align="center">Rabies Awareness 5K Fun Run Entry Form</h2>
			<p align="center"> </p>
			<h3 align="left">Name: <u>' . $name . '</u></h3>
			<table width="600" border="0">
			  <tr>
			    <td width="60"><h3 class="addyLabel">Address:</h3>
			    <h3 class="addyLabel"> </h3>
			    <h3 class="addyLabel"> </h3></td>
			    <td width="515"><h3 class="addy">' . $addy1 . '</h3>
			      <h3 class="addy">' . $addy2 . '</h3>
			    <h3 class="addy">' . $csz . '</h3>
			    </td>
			  </tr>
			</table>
			<h3 align="left">Age: <u>' . $age . '</u></h3>
			<h3 align="left">Emergency Contact Name: <u>' . $emContName . '</u></h3>
			<h3 align="left">Emergency Contact Phone: <u>' . $emContPhone . '</u></h3>
			<p align="left">I agree to hold harmless Dunder Mifflin Paper Company and Michael Scott in the event I become injured, deceased, or infected with rabies during the fun run.<br />
			</p>
			<p align="left"> </p>
			<p align="left">________________________________</p>
			<p align="left">(Signature)</p>
			<p align="left"> </p>
			<h3 align="left"><u>' . $name . '</u></h3>
			<p align="left">(Printed Name)</p>
			<h3 align="left"> </h3>
			<p align="left"> </p>
			</body></html>';
		$dompdf = new DOMPDF();
		$dompdf->load_html($html);
		$dompdf->render();
		$outfile = $dompdf->output();
		$fp = fopen('../../dompdf/files/file.pdf', 'w');
		fwrite($fp, $outfile);
		fclose($fp);
		$fileref = 'http://flexandair.com/dompdf/files/file.pdf';
		return $fileref;
	}
}		
?>

As you can see, this function accepts an array and inserts the values into an HTML template. The HTML is then passed to dompdf, which renders it into PDF and saves it on the server. The function then returns the full URL of the saved file.

You’ll need to change the address in the $fileref variable to match your server. This function simply overwrites any existing file.pdf, so that previous submissions are not stored.
Now that you have a complete pdfService.php file, you need to upload it so AMFPHP can use it. Place the file in your /webRoot/amfphp/services/ folder.

Installing dompdf

Dompdf is one of the best PDF tools for PHP you can get. It is easy to install, even on shared hosting, and its ability to render HTML into PDF is genius. Dompdf also supports many CSS 2.1 properties and selectors. This means you can create HTML templates for PDF forms, and you can even use template engines like Smarty.

To get started, download the latest version of dompdf. You’ll then need to extract and upload the files to your web server. This article assumes you have copied the dompdf installation to your /webRoot/dompdf directory.
Next, you need to create a directory to store the PDF file generated for the end-user. Under the /webRoot/dompdf directory, create a folder called “files”, and make sure the web server user has write permissions.

So, where do you stand now? You should have a working installation of AMFPHP, with an exposed pdfService, which contains a remotely accessible newPDF method. You should also have dompdf installed and available for your newPDF method to use to create the PDF from the inline HTML in pdfService.php. You’re now ready to create the Flex application for your end-users.

Creating the Flex Application

This sample Flex application will produce a sign-up sheet and waiver for the annual Dunder Mifflin Rabies Awareness 5K Fun Run. The user will enter some personal information, click a button, and download a completed PDF form, which they can sign and bring with them on the day of the event. To get started, open up Flex Builder and click the “File” menu item. Mouse over new, then select Flex Project. You’ll be presented with the new project dialog box. Name your project (the accompanying sample is named flexPDF), make sure “Web application (runs in Flash Player)” is selected, and click “Finish.” Flex Builder will create your project in the workspace, and open your main MXML file in the main pane.

First, you’ll open up the source code and make some changes. If your main Flex Builder pane is showing the design view, click the “Source” button in the upper left. In the source view, change the application’s layout tag to layout=”vertical”.

Next, you’ll need to set up communication with AMFPHP so you can access its exposed methods. Do that with a <mx:RemoteObject> entry:

    
<mx:RemoteObject 
        id="roPDFService" 
        destination="AMFPHP1_9" source="pdfService"
        showBusyCursor="true"
        endpoint="http://YOUR_SERVER/amfphp/gateway.php"
        fault="" makeObjectsBindable="true">
   <mx:method name="newPDF" result="handleFile()" />
</mx:RemoteObject>

Be sure to replace the endpoint URL with the one for your server. You’ll notice you have declared a method within the remote object called newPDF. This name must match the method name within your pdfService.php file. You have also specified a function called handleFile() to handle the result of the PHP method. Remember that your newPDF PHP function returns the URL of the user’s completed PDF. To create the handleFile function, create a new <mx:Script> block:

    
<mx:Script>
	<![CDATA[
		import flash.net.FileReference;
		
		private var fileToDownload:FileReference = new FileReference();
				
		private function handleFile():void {
			var url:String = roPDFService.newPDF.lastResult.toString();
			var request:URLRequest = new URLRequest();
			request.url = url;			
			fileToDownload.download(request, "FunRunForm.pdf");
		}
	]]>
</mx:Script>

This script block imports a necessary Actionscript class, flash.net.FileReference, declares a new FileReference, and creates a function to download the requested file. The URL to the user’s PDF form is contained in the property of your remote object called “lastResult.” That URL is used to form a URLRequest, and then the download method of the FileReference is called. The second parameter, “FunRunForm.pdf,” is the default name for the file when the user is prompted to save it.

NOTE: Be sure to declare your FileReference variable at the root of your script block, NOT inside a function. If it is declared inside a function, the file will not actually be saved to the user’s computer.
Next, you need to set up the code to collect your user’s information and send it to AMFPHP. To do that, go back outside your script block and add a form with some <mx:TextInput> controls:
<mx:Label text=”Rabies Awareness 5K Fun Run Sign-Up” fontSize=”16″ color=”#000000″/>

    
	<mx:Form>
		<mx:FormItem label="First Name:" color="#000000" fontSize="12">
			<mx:TextInput id="fNameInput"/>
		</mx:FormItem>
		<mx:FormItem label="Last Name:" fontSize="12" color="#000405">
			<mx:TextInput id="lNameInput"/>
		</mx:FormItem>
		<mx:FormItem label="Address 1:" fontSize="12" color="#000202">
			<mx:TextInput id="addy1Input"/>
		</mx:FormItem>
		<mx:FormItem label="Address 2:" fontSize="12" color="#000506">
			<mx:TextInput id="addy2Input"/>
		</mx:FormItem>
		<mx:FormItem label="City:" fontSize="12" color="#000607">
			<mx:TextInput id="cityInput"/>
		</mx:FormItem>
		<mx:FormItem label="State:" fontSize="12" color="#000203">
			<mx:TextInput id="stateInput"/>
		</mx:FormItem>
		<mx:FormItem label="Zip:" fontSize="12" color="#000304">
			<mx:TextInput id="zipInput"/>
		</mx:FormItem>
		<mx:FormItem label="Emergency Contact Name:" fontSize="12" color="#000506">
			<mx:TextInput id="emContName"/>
		</mx:FormItem>
		<mx:FormItem label="Emergency Contact Phone:" fontSize="12" color="#000101">
			<mx:TextInput id="emContPhone"/>
		</mx:FormItem>
		<mx:FormItem label="Your Age:" fontSize="12" color="#000101">
			<mx:TextInput id="ageInput"/>
		</mx:FormItem>
	</mx:Form>
	<mx:Button x="382" y="57" label="Get PDF" click="makePDF()"/>

Be sure to also create a “Get PDF” button (shown above) with a click handler function called makePDF(). Back inside your script block, create the makePDF function:

    
private function makePDF():void {
			var nfo:Array = new Array();
			nfo[0] = fNameInput.text + ' ' + lNameInput.text;
			nfo[1] = addy1Input.text;
			nfo[2] = addy2Input.text;
			nfo[3] = cityInput.text;
			nfo[4] = stateInput.text;
			nfo[5] = zipInput.text;
			nfo[6] = emContName.text;
			nfo[7] = emContPhone.text;
			nfo[8] = ageInput.text;
			roPDFService.newPDF.send(nfo);
		}

The makePDF function takes the user’s input, packs it into an array, and sends it to AMFPHP using your roPDFService remote object. The user will see a busy cursor while the request is sent and processed, and the application will be listening for the result. Once the result (the URL to the PDF) is received, the handleFile() method will be called, and users will be prompted to save the file to their computer.

NOTE: You’ll notice that you’re concatenating the first and last name into one string before sending it to AMFPHP. They are kept as separate input fields, in case you need to use the first and last name without a space in your PHP code. For example, to archive the PDF forms, you might want to name them using the user’s first and last name.

Publishing the Application

To make the application available to your end-users, you’ll need to publish it on your server. The Flash Player security sandbox requires that you either publish the application on the same server as your AMFPHP installation, or create a cross-domain xml file and place it in the web root of the remote server. For more information on the cross-domain.xml structure and purpose, see this article on Adobe’s Developer Network.

To try out the sample application outlined in this article, visit this link.

Next Steps

Now that you’ve got a working setup with Flex, AMFPHP, and dompdf, the sky is the limit for your applications. The HTML template we used in the sample was created in Dreamweaver, and the code was simply pasted into the newPDF function. With a little effort, you can create virtually any kind of form with your favorite HTML/CSS editor.
Dompdf can output a handful of fonts by default, but you can import new fonts and extend dompdf’s functionality fairly easily. See the full documentation for some great tips.

You now also have AMFPHP up and running, which means you can access virtually any PHP method from your Flash, Flex, or AIR application. This opens up an entire universe of new functionality. For an application like the one you’ve just created, you may want to consider:

  • Saving the user’s info to a database using PHP and allowing you to then reproduce the PDF on-demand
  • Archiving the completed forms on the server for future reference and indexing them using folder names, or a database for a complete record-keeping solution
  • Emailing the PDF forms to the user or other appropriate party using PHP’s powerful email functions
    Whatever your application’s requirements may be, you can now spend more time making a great application, and less time producing PDF forms thanks to these powerful, open-source tools.

About Cal Evans

Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term "computer" loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC's. For the past 10 years Cal has worked with PHP and MySQL on Linux OSX, and when necessary, Windows. He has built on a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys building and managing development teams using his widely imitated but never patented management style of "management by wandering around". Cal is currently based in Nashville, TN and is gainfully unemployed as the Chief Marketing Officer of Blue Parabola, LLC. Cal is happily married to wife 1.28, the lovely and talented Kathy. Together they have 2 kids who were both bright enough not to pursue a career in IT. Cal blogs at http://blog.calevans.com and is the founder and host of Day Camp 4 Developers

View all posts by Cal Evans

7 Responses to “Generating PDF Forms From a Flex Application With PHP”

  1. _____anonymous_____ Says:

    Very useful tutorial. Thanks for contributing this.

    However, $this->methodTable is not required for AMFPHP 1.9 and in fact is ignored:

    http://www.5etdemi.com/blog/archives/2006/12/amfphp-19-beta-get-it-now/

    Cheers,
    Darren.

  2. _____anonymous_____ Says:

    Thanks, Darren. You are absolutely right.

  3. _____anonymous_____ Says:

    Here is the link to try out the app:
    http://flexandair.com/flex/pdf/flexPDF.html

  4. _____anonymous_____ Says:

    It seems a bit odd to me to rely on dompf, which I understand is not developed anymore and has a security vulnerability (which you can bypass according to the site). Are there any alternative projects that are still "alive"?

  5. yimosama Says:

    HELLO,

    I tried the example, but didn’t work, I don’t know what I’m doing wrong, I followed every step mentioned above,there’s a way where I can download the source code of this project.

    Thanks in advance

  6. _____anonymous_____ Says:

    Hey
    i am halfway through building an application using php and flex. I would like to generate a report in pdf, but is amf the best solution if not what is..?

    Thanx

  7. ncacace1 Says:

    Firstly thanks for your post, I like the idea of how you have used AMFPHP which makes it a whole lot easier to pass Data to the PDF engine.

    I would use a datetimestamp to generate a random time stamp though to avoid an instance where two users may overwrite each others files.

    You then pass the timestamp back to the user and at this point add a human readable filename.

    Add to the service the ability to delete all pdf files older than today’s date to avoid accumulating data on your server and your home and hosed.

    Again thanks for the post