Create RTF Documents Dynamically





Create RTF Documents Dynamically

Use PHP to generate Rich Text Format (RTF) documents dynamically.

Rich Text Format (RTF) is a text format used by word processors, notably Microsoft Word, and some text editors and viewers to store highly styled documents. If you want to generate documents dynamically with all of the features of a word processor, RTF gives you an opportunity to do that.

Start with a word processing document in an editor such as Microsoft Word. Figure shows the document used in this hack.

The original Microsoft Word document


The sections of the document with the %% markers around them are where I want the dynamic data to go. I could have picked any special characters, but %% has the advantage of being pretty distinct and unusual; further, the percent signs aren't encoded in RTF.

With the document in hand, use the Save As command to save the file as RTF. Then, using that RTF file as a template, you can start writing the PHP code that will generate the RTF.

The Code

Save the file shown in Figure as rtf.php.

Using escape sequences to represent RTF
<? header( "content-type: application/msword" );
$customerName = "First customer";
$customerID = "cust_0001";
$data = array(

	array( trans => "123", desc => "Books", amount => '$123.25' ),
	array( trans => "345", desc => "Stamps", amount => '$22.93' ),
	array( trans => "1531", desc => "Candles", amount => '$56.27' )
);
?>
{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\
deflang1033\deflangfe1033
{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}
Times New Roman;}{\f1\fswiss\fcharset0\fprq2{\*\panose
020b0604020202020204}Arial;}
{\f180\froman\fcharset238\fprq2 Times New Roman CE;}{\f181\froman\fcharset204\
fprq2 Times New Roman Cyr;}
{\f183\froman\fcharset161\fprq2 Times New Roman Greek;}{\f184\froman\fcharset162\
fprq2 Times New Roman Tur;}
{\f185\froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\f186\froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\f187\froman\fcharset186\fprq2 Times New Roman Baltic;}
{\f188\froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\f190\fswiss\fcharset238\fprq2 Arial CE;}{\f191\fswiss\fcharset204\fprq2 Arial
Cyr;}
{\f193\fswiss\fcharset161\fprq2 Arial Greek;}{\f194\fswiss\fcharset162\fprq2
Arial Tur;}
{\f195\fswiss\fcharset177\fprq2 Arial (Hebrew);}
{\f196\fswiss\fcharset178\fprq2 Arial (Arabic);}
{\f197\fswiss\fcharset186\fprq2 Arial Baltic;}{\f198\fswiss\fcharset163\fprq2
Arial (Vietnamese);}
}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\
green255\blue0;
\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\
green255\blue255;
\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\
blue128;
\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;
\red192\green192\blue192;}{\stylesheet{\ql
\li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0
\fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}
{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\*
\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\
trpaddfr3\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\
tsbrdrdgr\tsbrdrh\tsbrdrv
\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0
\fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11
\ssemihidden Normal Table;}{\*\ts15\tsrowd\trbrdrt\brdrs\brdrw10

\trbrdrl\brdrs\brdrw10 \trbrdrb\brdrs\brdrw10
\trbrdrr\brdrs\brdrw10 \trbrdrh\brdrs\brdrw10 \trbrdrv\brdrs\brdrw10
\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\
tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\
tsbrdrh\tsbrdrv
\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0
\fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \sbasedon11 \snext15
\styrsid6312866 Table Grid;}}{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\
rsidtbl \rsid6312866}
{\*\generator Microsoft Word 11.0.6359;}{\info{\title Customer <? print(
$customerName ) ?> }
{\author jherring}{\operator jherring}{\creatim\yr2005\mo4\dy30\hr21\min46}
{\revtim\yr2005\mo4\dy30\hr21\min46}{\version2}{\edmins0}{\nofpages1}{\
nofwords15}{\nofchars91}
{\*\company Macromedia Inc.}{\nofcharsws105}{\vern24703}}
\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\
formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1800\dgvorigin1440\
dghshow1\dgvshow1
\jexpand\viewkind1\viewscale125\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\
htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\
nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\nojkernpunct\
rsidroot6312866 \fet0
\sectd \linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj
{\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}
{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}
{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}
{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}
{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}
{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}
{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang
{\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb
(}
{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta
)}}
\pard\plain \ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\
itap0
\fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033
{\f1\insrsid6312866\charrsid6312866 Customer: <? print( $customerName) ?>
\par Customer ID: <? print( $customerID ) ?>
\par
\par }\trowd \irow0\irowband0\ts15\trgaph108\trleft-108\trftsWidth1\trftsWidthB3\
trftsWidthA3\trautofit1\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\
trpaddfr3\tbllkhdrrows\tbllklastrow\tbllkhdrcols\tbllklastcol \clvertalt\clbrdrt\
brdrtbl \clbrdrl
\brdrtbl \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrtbl \cltxlrtb\clftsWidth3\
clwWidth2088\clshdrawnil
\cellx1980\clvertalt\clbrdrt\brdrtbl \clbrdrl\brdrtbl \clbrdrb\brdrs\brdrw10 \
clbrdrr\brdrtbl
\cltxlrtb\clftsWidth3\clwWidth4680\clshdrawnil \cellx6660
\clvertalt\clbrdrt\brdrtbl \clbrdrl\brdrtbl \clbrdrb\brdrs\brdrw10

\clbrdrr\brdrtbl \cltxlrtb\clftsWidth3\clwWidth2088\clshdrawnil \cellx8748\pard \
ql
\li0\ri0\widctlpar\intbl\aspalpha\aspnum\faauto\adjustright\rin0\lin0 {
\b\f1\insrsid6312866\charrsid6312866 Transaction #\cell Description\cell Amount\
cell }\pard
\ql \li0\ri0\widctlpar\intbl\aspalpha\aspnum\faauto\adjustright\rin0\lin0
{\b\f1\insrsid6312866\charrsid6312866 \trowd \irow0\irowband0
\ts15\trgaph108\trleft-108\trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\
trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tbllkhdrrows\
tbllklastrow\tbllkhdrcols\tbllklastcol \clvertalt\clbrdrt\brdrtbl \clbrdrl\
brdrtbl \clbrdrb
\brdrs\brdrw10 \clbrdrr\brdrtbl \cltxlrtb\clftsWidth3\clwWidth2088\clshdrawnil
\cellx1980\clvertalt\clbrdrt\brdrtbl \clbrdrl\brdrtbl
\clbrdrb\brdrs\brdrw10 \clbrdrr\brdrtbl \cltxlrtb\clftsWidth3\clwWidth4680\
clshdrawnil
\cellx6660\clvertalt\clbrdrt
\brdrtbl \clbrdrl\brdrtbl \clbrdrb\brdrs\brdrw10 \clbrdrr\brdrtbl
\cltxlrtb\clftsWidth3\clwWidth2088\clshdrawnil \cellx8748\row }\trowd
\irow1\irowband1\lastrow
<? foreach( $data as $row ) { ?>
\ts15\trgaph108\trleft-108\trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\
trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tbllkhdrrows\
tbllklastrow\tbllkhdrcols\tbllklastcol \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\
brdrtbl \clbrdrb
\brdrtbl \clbrdrr\brdrtbl \cltxlrtb\clftsWidth3\clwWidth2088\clshdrawnil
\cellx1980\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrtbl \clbrdrb\brdrtbl
\clbrdrr\brdrtbl \cltxlrtb\clftsWidth3\clwWidth4680\clshdrawnil \cellx6660\
clvertalt\clbrdrt
\brdrs\brdrw10 \clbrdrl\brdrtbl \clbrdrb\brdrtbl \clbrdrr\brdrtbl
\cltxlrtb\clftsWidth3\clwWidth2088\clshdrawnil \cellx8748\pard \ql
\li0\ri0\widctlpar\intbl\aspalpha\aspnum\faauto\adjustright\rin0\lin0
{\f1\insrsid6312866 <? print( $row['trans'] ) ?>\cell <? print( $row['desc'] ) ?>
\cell
<? print( $row['amount'] ) ?>\cell }\pard \ql
\li0\ri0\widctlpar\intbl\aspalpha\aspnum\faauto\adjustright\rin0\lin0 {\f1\
insrsid6312866
\trowd \irow1\irowband1\lastrow
\ts15\trgaph108\trleft-108\trftsWidth1\trftsWidthB3\trftsWidthA3\trautofit1\
trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tbllkhdrrows\
tbllklastrow\tbllkhdrcols\tbllklastcol \clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\
brdrtbl \clbrdrb
\brdrtbl \clbrdrr\brdrtbl \cltxlrtb\clftsWidth3\clwWidth2088\clshdrawnil
\cellx1980\clvertalt\clbrdrt\brdrs\brdrw10 \clbrdrl\brdrtbl \clbrdrb\brdrtbl
\clbrdrr\brdrtbl \cltxlrtb\clftsWidth3\clwWidth4680\clshdrawnil \cellx6660\
clvertalt\clbrdrt
\brdrs\brdrw10 \clbrdrl\brdrtbl \clbrdrb\brdrtbl \clbrdrr\brdrtbl
\cltxlrtb\clftsWidth3\clwWidth2088\clshdrawnil \cellx8748\row }
<? } ?>
\pard \ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0
{\f1\insrsid6312866\charrsid6312866
\par }}

The code you see is just the output of the Word RTF file with a header function at the top and some PHP segments to add the code where it's required. The header function tells the browser that this is an RTF file and not some seriously malformed HTML or text.

RTF is a complex file format, especially when it comes to rendering documents with heavy formatting. You Don't need to understand the RTF format completely to generate documents. You can simply do what I did, which is to use your word processor as you normally would, but put markers in the document that you can use to identify where the dynamic content should go. In other words, this hack is about pure grunt work, rather than some mystical transformation process. Boring, yes, but still pretty useful!

Running the Hack

When I navigate to the PHP script, the first thing I see in Internet Explorer is the dialog in Figure.

The security dialog that pops up as the page is loaded


This security dialog allows the user to decide how RTF files generated by the server should be handled. I choose to open the file, and I see the embedded Word control shown in Figure.

The sections marked with %% are replaced with live data from the server that could easily have come from a database.

See Also

  • "Create Excel Spreadsheets Dynamically" [Hack #49]

The dynamic RTF file shown in Internet Explorer




 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows