<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hacking at 0300 &#187; PHP</title>
	<atom:link href="http://bililite.nfshost.com/blog/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://bililite.nfshost.com/blog</link>
	<description>Thoughts on web design and programming from a very occasional volunteer webmaster</description>
	<lastBuildDate>Fri, 03 Feb 2012 10:05:17 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>The New Amazon Advertising API</title>
		<link>http://bililite.nfshost.com/blog/2011/11/01/the-new-amazon-advertising-api/</link>
		<comments>http://bililite.nfshost.com/blog/2011/11/01/the-new-amazon-advertising-api/#comments</comments>
		<pubDate>Wed, 02 Nov 2011 01:59:32 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=2072</guid>
		<description><![CDATA[I use Ulrich Mierendorff's aws_signed_request to create my Amazon Wishlist Widget. But Amazon just changed the terms of their API (which they do with frightening regularity). Luckily, it's a small change: each request now requires an Associate Tag in addition to the AWS key and encryption with the AWS secret key. But if you want [...]]]></description>
			<content:encoded><![CDATA[<p>I use Ulrich Mierendorff's <a href="http://mierendo.com/software/aws_signed_query/">aws_signed_request</a> to create my <a href="/blog/2010/10/31/hacking-my-way-to-an-amazon-wishlist-widget/" title="Hacking My Way to an Amazon Wishlist Widget">Amazon Wishlist Widget</a>. But Amazon just <a href="https://affiliate-program.amazon.com/gp/advertising/api/detail/api-changes.html">changed the terms of their API</a> (which they do with <a href="http://aws.amazon.com/releasenotes/4038">frightening regularity</a>). Luckily, it's a small change: each request now requires an Associate Tag in addition to the AWS key and encryption with the AWS secret key. But if you want to make money off Amazon, you need an Associate Tag anyway. So in the <code>$params</code> that are passed to <code>aws_signed_request</code> be sure to include a parameter <code>"AssociateTag" =&gt; $tag</code>. And now it works again. For now.</p>
<p>I guess the idea is to up the ante in <a href="http://www.thestreet.com/story/11052898/1/amazon-sales-tax-the-battle-state-by-state.html">Amazon's battle against state sales taxes</a>&mdash;they've cut off the associates in states they're in conflict with, and now those users can't use the search API at all.</p>
<p>I hope they don't declare war on Missouri next.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/11/01/the-new-amazon-advertising-api/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fractions and Units in PHP</title>
		<link>http://bililite.nfshost.com/blog/2011/08/11/fractions-and-units-in-php/</link>
		<comments>http://bililite.nfshost.com/blog/2011/08/11/fractions-and-units-in-php/#comments</comments>
		<pubDate>Fri, 12 Aug 2011 05:35:15 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Medical Informatics]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1858</guid>
		<description><![CDATA[For the bililite webservices, I kept all the data in what I would call "standard" American medical units, centimeters for height, kilograms for weight, mmHg for blood pressure, mg/dl for bilirubin. But lots of doctors use pounds and inches, and it would be nice to allow those as well. I could have separate data entry [...]]]></description>
			<content:encoded><![CDATA[<p>For the <a href="/webservices">bililite webservices</a>, I kept all the data in what I would call "standard" American medical units, centimeters for height, kilograms for weight, mmHg for blood pressure, mg/dl for bilirubin. But lots of doctors use pounds and inches, and it would be nice to allow those as well. I could have separate data entry forms for different units, but I decided it would be easier and more useful to allow units on the numbers. I could allow fractions as well (which some of my medical assistants still insist on recording; 21 5/8 instead of 21.625. My EMR blows a gasket with that, but <em>my</em> program would do better). And then, I could allow mixed units, like a weight of "6 pounds 5 1/2 ounces".</p>
<p>I didn't find anything exactly right on the web, but <a href="http://stackoverflow.com/questions/1954018/php-convert-decimal-into-fraction-and-back/1954324#1954324">symcbean on stackoverflow</a> had a clever idea for evaluating fractions: turn "2 1/2" into "2+1/2" then use <a href="http://php.net/eval"><code>eval</code></a>.</p>
<span id="more-1858"></span>
<p><a href="/blog/blogfiles/units.php">Try the code</a>.</p>
<pre><code class="language-php">function convertCK($usIn){
	// convert input numbers with optional units (pounds, inches etc.) to centimeters and kilograms
	$factors = array(
		'f' =&gt; 30.48, // feet
		"'" =&gt; 30.48,
		'i' =&gt; 2.54, // inches
		'"' =&gt; 2.54,
		'p' =&gt; 0.45359237, // pounds
		'l' =&gt; 0.45359237, // lb
		'#' =&gt; 0.45359237,
		'o' =&gt; 0.0283495231, // ounces
		'g' =&gt; 0.001 // grams
	);
	$usIn = trim($usIn);
	// turn spaces in mixed numbers to pluses, for the eval hack
	$usIn = preg_replace ('%(\d)\s+(\d)%', '$1+$2', $usIn);
	preg_match_all('%(?&lt;num&gt;[0-9./+]+)\s*(?&lt;unit&gt;\D*)%', $usIn, $values, PREG_SET_ORDER);
	$ret = 0;
	foreach ($values as $value){
		$unit = strtolower($value['unit']{0});
		$factor = isset($factors[$unit]) ? $factors[$unit] : 1;
		$ret += $factor*eval("return $value[num];");
	}
	return $ret;
}</code></pre>
<p>The code above is simplified by the fact that all the units I care about have unique first letters, but if you wanted more units you could simply add more entries in the <code>factors</code> array and make the <code class="language-php">$unit = strtolower($value['unit']{0});</code> line more sophisticated. <code>usIn</code> is used as a variable name to remind me that it is unsafe&mdash;it may come from user input and has to be sanitized with the regexp before it can be trusted.</p>
<p>It allows the American notation for tic marks for feet and inches, since getting that wrong <a href="http://www.youtube.com/watch?v=Xlf5ucFanpY">can be disastrous</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/08/11/fractions-and-units-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Don&#8217;t Reinvent the Wheel, PDF Style</title>
		<link>http://bililite.nfshost.com/blog/2011/07/12/dont-reinvent-the-wheel-pdf/</link>
		<comments>http://bililite.nfshost.com/blog/2011/07/12/dont-reinvent-the-wheel-pdf/#comments</comments>
		<pubDate>Tue, 12 Jul 2011 19:37:09 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PDF]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1819</guid>
		<description><![CDATA[After playing with creating PDFs with PHP using fPDF for a while, and trying to get everything to work consistently, I discovered tcpdf, which is a fork of fpdf that includes everything that anyone has ever added to the original. And I mean everything; this thing is huge! I printed out the source to see [...]]]></description>
			<content:encoded><![CDATA[<p>After playing with <a href="/blog/2011/04/01/creating-pdfs-with-php-part-5/">creating PDFs with PHP</a> using <a href="http://fpdf.org"><code>fPDF</code></a> <a href="/blog/2011/04/11/paths-vector-graphics-and-php-images-in-fpdf/">for a while</a>, and trying to get everything to work consistently, I discovered <a href="http://tcpdf.org"><code>tcpdf</code></a>, which is a fork of <code>fpdf</code> that includes everything that anyone has ever added to the original. And I mean <em>everything</em>; this thing is huge! I printed out the source to see how it differed from the original, and it ran more than 500 pages. Good thing they're so generous to me at work.</p>
<p>Most of the size is due to the SVG and HTML formatting, which I don't need, but the biggest advantage is that Unicode font subsetting works. Mostly.</p>
<p><a href="http://fpdf.org/en/script/script92.php"><code>tfpdf</code></a>, the Unicode-enabled version that comes with <code>fpdf</code>, supports Unicode fonts but <a href="http://www.fpdf.org/phorum/read.php?f=1&#038;i=54323&#038;t=54231">they don't show up on the iPhone</a>. Apple's PDF viewer is somehow different from Adobe's and reads the fonts differently. <code>tcpdf</code> does <a href="/blog/blogfiles/pdf/pdftest.php?which=tcpdf-x1">a better job</a> (displays in Adobe Reader but generates an error for the HumaneJenson font): the <a href="/blog/blogfiles/pdf/pdftest.php?which=tcpdf-x2&#038;font=Droid">Droid fonts</a> work on the iPhone, though the <a href="/blog/blogfiles/pdf/pdftest.php?which=tcpdf-x2&#038;font=DejaVu">DejaVu fonts</a> do not. Try those last links on the iPhone; the built-in Helvetica fonts show up but DejaVu does not. Try refreshing <a href="/blog/blogfiles/pdf/pdftest.php?which=tcpdf-x2">the test page</a> multiple times; it randomly selects fonts to display each time. Some fonts generate errors in Adobe Reader but display, some don't display at all and some don't display on the iPhone. It all seems very random, but at least I have a set of open-source true type fonts that I can include.</p>
<p>It also does most of the things I need: PNG graphics with transparency, form fields like text boxes (I played with that one for weeks with tfpdf, but it never worked the way I wanted it to), rotating text. The API is clunky and <a href="http://www.tcpdf.org/doc/classTCPDF.html">poorly documented</a> and I definitely like <a href="/blog/2011/04/11/paths-vector-graphics-and-php-images-in-fpdf/">my routines</a> better, but this is <em>done</em> and someone else maintains it. A huge advantage. I can write my own interface routines to be more elegant if I want.</p>
<span id="more-1819"></span>
<h3>Using <code>tcpdf</code></h3>
<p>Unlike <code>tfpdf</code>, you have to parse the true type fonts first to create PHP files. In the fonts folder there's a utils folder; copy the contents of that into your font folder then run the script <code>makeallttffonts.php</code> and it will create the necessary files (as above, they're not perfect).</p>
<p>The basic page is similar to <code>fpdf</code>:</p>
<pre><code class="language-php">define ('K_PATH_FONTS', '/path/to/fonts/'); // some constants are named differently
$pdf = new TCPDF('P', 'in', 'letter');
$pdf->setPrintHeader(false); // it automatically includes a header and footer (just a line by default) unless told otherwise
$pdf->setPrintFooter(false);
$pdf->AddPage();
$pdf->AddFont('DroidSerif-Regular'); // using true type fonts
$pdf->SetFont('DroidSerif-Regular', '', 12);
$pdf->Write(0, 'Hello, world ');
$pdf->Output();</code></pre>
<p>Graphics can use URL's directly, which is nice (it uses <a href="http://us.php.net/curl">CURL</a> internally). It's inefficient, in that it reloads the image from the internet each time it's used rather than caching it. If that matters, download the image yourself and pass the file. Images are placed based on their upper left corner, and there's no way to get the size of the image to be able to calculate the center (that's another argument for downloading an image yourself: you can use <a href="http://us.php.net/manual/en/function.getimagesize.php">getimagesize</a> on the resulting file). I wrote a minor hack to use the class's protected methods to center an image (see <a href="/blog/blogfiles/pdf/pdftest.php?which=tcpdf-x5">the result</a>):</p>
<pre><code class="language-php">class pdf_imageinfo extends TCPDF{
  // adds a function to return the image information
  function lastimageinfo(){
    return $this->images[$this->imagekeys[count($this->imagekeys)-1]];
  }
}

$pdf = new pdf_imageinfo('P', 'in', 'letter');
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->AddPage();

markImage($pdf, 'http://bililite.com/images/silk/bug.png', 2, 2);
markImage($pdf, 'http://bililite.nfshost.com/blog/blogfiles/pdf/dice.png', 4, 6);
$pdf->Output();

function markImage($pdf, $url, $x, $y){
  // place an image file $url centered at ($x, $y) with crosshairs
  $pdf->Image($url, -1000); // hide the image. Not too inefficient since we're going to use it anyway, though tcpdf will call cURL each time
  $info = $pdf->lastimageinfo();
  $w = $info['w']/$pdf->GetScaleFactor(); // sizes are in pixels; we want user units
  $h = $info['h']/$pdf->GetScaleFactor();
  $pdf->SetAlpha(1);
  $pdf->Line ($x,$y-$h,$x,$y+$h); // draw crosshairs to show center
  $pdf->Line ($x-$w,$y,$x+$w,$y);
  $pdf->SetAlpha(0.5); // demonstrates using transparency in images
  $pdf->Image($url, $x-$w/2, $y-$h/2);
}</code></pre>
<p>Text is a little different from <code>fpdf</code>. The <code>Text</code> function in <code>fpdf</code> places the baseline of the text, but in <code>tcpdf</code> it's aligned with the top left, unless the <a href="http://www.tcpdf.org/doc/classTCPDF.html#abf1daa47cbb1f2abe310d19124a1e35e"><code>calign</code> parameter</a> is set, which is 14 parameters down the list. Since there's no way to specify named parameters in the call that means a long, inconvenient call: <code class="language-php">$pdf->Text ($x, $y, $s, FALSE,FALSE,TRUE,0,0,'',FALSE,'',0,FALSE,'L');</code>. In addition, there's a cell margin and cell padding that's added, so if you want to precisely place the text you have to set those to zero (margin is defaulted to 0 but padding is not) (these are the same as in CSS: margin surrounds border which surrounds padding).</p>
<p>Rotating is straightforward: call <a href="http://www.tcpdf.org/doc/classTCPDF.html#aa07b31137128fee6f4d505cb1e7052ec"><code>StartTransform</code></a>, <a href="http://www.tcpdf.org/doc/classTCPDF.html#aef1e19d18b0a82b1c45b53d3f17ae2b5"><code>Rotate</code> (in degrees!)</a>, then <a href="http://www.tcpdf.org/doc/classTCPDF.html#a26529ccdd9f9ec9d0ea0c40980776760"><code>StopTransfom</code></a>. So to rotate text around the left end of the baseline (see <a href="/blog/blogfiles/pdf/pdftest.php?which=tcpdf-x4&#038;s=Hello, world">the result</a>):</p>
<pre><code class="language-php">$pdf = new TCPDF('P', 'in', 'letter');
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->AddPage();
$num = 6;
$pdf->SetCellPaddings(0);
$s = 'Hello, world';
// draw crosshairs
$pdf->Line(4,3,4,5);
$pdf->Line(3,4,5,4);
$pdf->Line(4,5,4,7);
$pdf->Line(3,6,5,6);
// Draw rotated text
for ($i = 0; $i < $num; ++$i){
  $angle = 360*$i/$num;
  $color = array (0, 0, 0, 127*$i/$num);
  text ($pdf, $s, 4, 4, $angle, 'DroidSans', 22, $color);
  text ($pdf, 'Hello', 4, 6, $angle, 'Lilly', 7, $color);
}
$pdf->Output();

function text ($pdf, $s, $x, $y, $angle, $font, $size, $color){
  $pdf->StartTransform();
  $pdf->SetFont($font, '', $size);
  $pdf->SetTextColor ($color[0], $color[1], $color[2]);
  $pdf->SetAlpha ( (127-$color[3])/127 ); // change from PHP to PDF units for transparency
  $pdf->Rotate ($angle, $x, $y); // rotate origin
  $pdf->Text ($x, $y, $s, FALSE,FALSE,TRUE,0,0,'',FALSE,'',0,FALSE,'L'); 
  $pdf->StopTransform();	
}</code></pre>
<p>Text fields are straightforward; set the default properties (borders, colors) with <a href="http://www.tcpdf.org/doc/classTCPDF.html#a1611451af253619217095e279f52fa52"><code>SetFormDefaultProp</code></a>; the documentation for the properties are in the <a href="http://partners.adobe.com/public/developer/en/acrobat/sdk/pdf/javascript/AcroJS.pdf#page=388">Adobe Javascript manual</a>. Create a text field with <a href="http://www.tcpdf.org/doc/classTCPDF.html#a71d382a4356e7fb3f9fa72e53642c09c"><code>TextField</code></a>, though setting the properties in that function doesn't work for me. One trick is that Reader uses a font size of 0 to indicate that the size should adjust to fit. Unfortunately, <code>tcpdf</code> uses a font size of 0 to indicate no change, but there's a hack: on output it rounds numbers to 2 decimal places. So setting the font size to 0.001 works as well.</p>
<p>The PDF's I'm creating are generally used as printable forms, with underlines for the fillable fields, but with text boxes that can be filled in online. So I want a text field with no border but with the underline overprinted. This does that (see <a href="/blog/blogfiles/pdf/pdftest.php?which=tcpdf-x3&#038;s=Name: _________________________ ID: ______________">the result</a>):</p>
<pre><code class="language-php">$s = $_GET['s'];
preg_match_all('%_+|[^_]+%', $s, $matches);
// $matches[0] is the array of text, split into underlines and other
define ('K_PATH_FONTS', '/path/to/fonts/');
$pdf = new TCPDF('P', 'in', 'letter');
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->SetCellPaddings(0); // so text aligns precisely
$pdf->SetFont('Helvetica', '', 12); // always use a built-in font for text fields, since there's no way to know what characters will be used
$height = 14/$pdf->GetScaleFactor(); // 14-point line height
$pdf->AddPage();
$pdf->setFormDefaultProp(array('lineWidth'=>0)); // no border
$fieldcount=0;
foreach ($matches[0] as $text){
  if (strpos($text, '_') !== FALSE){
    // It's an underline. Create a text field of the same size
    $x = $pdf->GetX();
    $width = $pdf->GetStringWidth($text);
    $pdf->SetFontSize(0.001); // automagic font size hack
    $pdf->TextField ('text'.($fieldcount++), $width, $height);
    $pdf->SetX($x); // reset the location and font
    $pdf->SetFontSize(12);
  }
  $pdf->Write($height, $text);

}
$pdf->Output();</code></pre>
<p>Now I can go and get the <a href="/webservices">webservices</a> PDF's working correctly!</p>
]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/07/12/dont-reinvent-the-wheel-pdf/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paths, Vector Graphics and PHP images in FPDF</title>
		<link>http://bililite.nfshost.com/blog/2011/04/11/paths-vector-graphics-and-php-images-in-fpdf/</link>
		<comments>http://bililite.nfshost.com/blog/2011/04/11/paths-vector-graphics-and-php-images-in-fpdf/#comments</comments>
		<pubDate>Mon, 11 Apr 2011 16:47:26 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PDF]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1735</guid>
		<description><![CDATA[Looking at FPDF and at my PDF tutorial, it is clear that there are a few things that PDF's can do that aren't part of the API of FPDF. However, FPDF is easily extensible to include everything I might find useful, so I put together a package of those routines. See the code. See the [...]]]></description>
			<content:encoded><![CDATA[<p>Looking at <a href="http://fpdf.org">FPDF</a> and at my <a href="/blog/2011/03/18/creating-pdfs-with-php/">PDF tutorial</a>, it is clear that there are a few things that PDF's can do that aren't part of the API of FPDF. However, FPDF is easily extensible to include everything I might find useful, so I put together a package of those routines. 
<p><a href="/blog/blogfiles/pdf/pdftest.php?which=GraphicsPDF&amp;output=source">See the code</a>.</p>
<p><a href="/blog/blogfiles/pdf/graphicsTest.php">See the sample output</a>.</p>
<p><a href="/blog/blogfiles/pdf/pdftest.php?which=graphicsTest&amp;output=source">See the code that produced the sample output</a>.</p>
<span id="more-1735"></span>
<h3>Path Drawing</h3>
<p>Create a "path" by moving a virtual stylus around the page, then draw it or fill it in. The main advantage of using the path drawing routines rather than <a href="http://www.fpdf.org/en/doc/line.htm"><code class="language-php">$pdf->Line</code></a> is that PDF creates nice line joins at the angles, rather than just overlaying the lines: <img alt="Triange formed with path commands" src="/blog/blogfiles/pdf/cooltriangle.png" title="Path-drawn Triangle" class="alignnone" width="79" height="141" /> as opposed to <img alt="Triangle formed from individual lines" src="/blog/blogfiles/pdf/uncooltriangle.png" title="Individual Lines" class="alignnone" width="79" height="115" />.</p>
<dl>
<dt><code>MoveTo ($x, $y)</code></dt>
<dd>Moves the stylus to <code>($x, $y)</code> (in user units) without drawing the path from the previous point. <code>$x</code> and <code>$y</code> must be numbers. Paths must start with a <code>MoveTo</code> to set the original stylus location or the result is undefined.</dd>
<dt><code>LineTo ($x, $y)</code></dt>
<dd>Draw a line from the current stylus location to <code>($x, $y)</code>, which becomes the new stylus location. Note that this only creates the line in the path; it does not actually draw the line on the page.</dd>
<dt><code>CurveTo ($x1, $y1, $x2, $y2, $x3, $y3)</code></dt>
<dd>Draw a <a href="http://en.wikipedia.org/wiki/B%C3%A9zier_curve">cubic Bï¿½zier curve</a> from the current location to <code>($x3, $y3)</code>, using <code>($x1, $y1)</code> and <code>($x2, $y2)</code> as control points. See <a href="http://processingjs.nihongoresources.com/bezierinfo/">Mike "Pomax" Kamermans</a>'s site for more information on drawing with Bï¿½zier curves.</dd>
<dt><code>ClosePath()</code></dt>
<dd>Draw a line from the current location to the last <code>MoveTo</code> point (if not the same) and mark the path as closed so the first and last lines join nicely.</dd>
<dt><code>DrawPath($style='D')</code></dt>
<dd>Actually draw the path on the page. <code>$style</code> can be:
  <ul>
  <li><code>'D'</code> (or an empty string) to draw the path with the current <a href="http://www.fpdf.org/en/doc/setdrawcolor.htm">drawing color</a>.</li>
  <li><code>'F'</code> to fill the path with the current <a href="http://www.fpdf.org/en/doc/setfillcolor.htm">fill color</a>.</li>
  <li><code>'DF'</code> or <code>'FD'</code> to do both.</li>
  </ul>
</dl>
<h3>Drawing</h3>
<dl>
<dt><code>Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='')</code></dt>
<dd>Enhanced <a href="http://www.fpdf.org/en/doc/image.htm">Image</a> to allow the use of <a href="http://us.php.net/manual/en/book.image.php">PHP GD images</a> directly, including the alpha channel (using the code from <a href="http://www.fpdf.org/en/script/script83.php">Valentin Schmidt</a>). Uses temporary PNG files, so requires write-access to the current directory.</dd>
<dd>For example,
<pre><code class="language-php">$im = imagecreate(110, 20);
$background_color = imagecolorallocate($im, 0, 0, 0);
$text_color = imagecolorallocate($im, 233, 14, 91);
imagestring($im, 1, 5, 5,  "A Simple Text String", $text_color);
$pdf->Image($im);</code></pre>
will insert the image <img alt="Image of text" src="http://us.php.net/manual/en/images/21009b70229598c6a80eef8b45bf282b-imagecreate.png" title="A Simple Text String" class="alignnone" width="110" height="25" /> onto the page.</dd>
<dd>Also enhanced to allow the use of vector-based images; see <code>OpenImage</code> and <code>CloseImage</code> below.</dd>
<dt><code>Text($x, $y, $txt, $theta=0)</code></dt>
<dd>Enhanced <a href="http://www.fpdf.org/en/doc/text.htm"><code>Text</code></a> to allow rotation of the baseline by <code>$theta</code> radians (counter clockwise).</dd>
<dt><code>Rect($x, $y, $w, $h, $style='', $theta=0)</code></dt>
<dd>Enhanced <a href="http://www.fpdf.org/en/doc/rect.htm"><code>Rect</code></a> to allow rotation of the rectangle by <code>$theta</code> radians (counter clockwise) around the upper left corner.</dd>
<dt><code>PlaceImage($file, $x, $y, $w=0, $h=0, $theta=0, $type='')</code></dt>
<dd>Similar to <a href="http://www.fpdf.org/en/doc/image.htm"><code>Image</code></a> but puts the image with <code>($x, $y)</code> at the <em>center</em> rather than the upper left of the image, and rotates the image by <code>$theta</code> radians (counter clockwise) around the center. Unlike <code>Image</code>, does not allow <code>$x=NULL</code> to place the image in the text flow, and does not create links.</dd>
<dt><code>Arc($x, $y, $style='', $a, $b=NULL, $theta=0, $lambda1=0, $lambda2=0)</code></dt>
<dd>Draw an arc or wedge of an ellipse. <code>($x, $y)</code> is the center of the ellipse, <code>$a</code> is the semimajor axis (half the width) and <code>$b</code> is the semiminor axis (half the height). If <code>$b===NULL</code>, then <code>$b</code> is set to <code>$a</code> (a circle with radius <code>$a</code>). The ellipse is rotated <code>$theta</code> radians counter clockwise from the horizontal. The arc goes from <code>$lambda1</code> radians from the major axis (not the horizontal) to <code>$lambda2</code> radians; if they are equal then the whole ellipse is drawn. <code>$style</code> is the same as for <a href="http://www.fpdf.org/en/doc/rect.htm">Rect</a>: <code>'D'</code> to draw the arc, <code>'F'</code> to fill in the wedge, <code>'DF'</code> or <code>'FD'</code> to do both.</dd>
<dd>The calculations to estimate circles with Bï¿½zier curves comes from <a href="http://www.spaceroots.org/documents/ellipse/">L. Maisonobe</a>.</dd>
<dt><code>Polygon($x, $y, $n, $style='', $a, $b=NULL, $skip=1, $theta=0, $lambda=0)</code></dt>
<dd>Draw a regular polygon with <code>$n</code> sides, inscribed in the ellipse that would be drawn with <code>Arc($x, $y, '', $a, $b, $theta)</code>. The first vertex is placed at <code>$lambda</code> to the major axis. <code>$skip</code> determines the shape of the polygon: if <code>$skip==1</code>, draw a regular, convex polygon. If <code>$skip&gt;1</code>, draw a star by drawing the lines from each vertex <code><em>i</em></code> to <code><em>i</em>+$skip</code>. The higher <code>$skip</code> is, the tighter the star (up to <code>$n/2</code>).</dd>
<dd>So <code class="language-php">$pdf->Polygon(1, 1, 5, 'D', 10, 10, 1)</code> draws a pentagon with a radius of 10, and <code class="language-php">$pdf->Polygon(1, 1, 5, 'D', 10, 10, 2)</code> draws a five-pointed star with a radius of 10.</dd>
<dt><code>Cloverleaf($x, $y, $n, $style='', $a, $b=NULL, $skip=1, $theta=0, $lambda=0)</code></dt>
<dd>Same as <code>Polygon</code>, but draws curves with loops at the vertices.</dd>
</dl>
<h3>Transparent Drawing</h3>
<p>In addition to allowing alpha-channel transparency in images with the enhanced <code>Image</code>, you can use transparent colors for drawing, filling and text. The code is based on that of <a href="http://www.fpdf.org/en/script/script74.php">Martin Hall-May</a>.</p>
<dl>
<dt><code>SetDrawColor($r, $g=null, $b=null, $alpha=0)</code></dt>
<dd>Enhanced <a href="http://www.fpdf.org/en/doc/setdrawcolor.htm">SetDrawColor</a> to allow alpha channel transparency. <code>$alpha==0</code> is opaque and <code>$alpha==127</code> is fully transparent. Note that this is backward from CSS opacity, which uses 0 for transparent and 1 for opaque.</dd>
<dt><code>SetFillColor($r, $g=null, $b=null, $alpha=0)</code></dt>
<dd>Enhanced <a href="http://www.fpdf.org/en/doc/setfillcolor.htm">SetFillColor</a> to allow alpha channel transparency. <code>$alpha==0</code> is opaque and <code>$alpha==127</code> is fully transparent.</dd>
<dt><code>SetTextColor($r, $g=null, $b=null, $alpha=0)</code></dt>
<dd>Enhanced <a href="http://www.fpdf.org/en/doc/settextcolor.htm">SetTextColor</a> to allow alpha channel transparency. <code>$alpha==0</code> is opaque and <code>$alpha==127</code> is fully transparent.</dd>
</dl>
<h3>Vector-based images</h3>
<p>In addition to bitmapped images like JPEG or PNG images (or GD images), PDF supports vector graphics similar to SVG (a class to allow using SVG directly would be useful; I may get the time). Create a new image with <code>OpenImage</code>, then any of the drawing commands will draw into the image being created rather than onto the page. Use <code>Image</code> or <code>PlaceImage</code> to put the image onto the page.<p>
<p>For example,</p>
<pre><code class="language-php">$pdf = new GraphicsPDF('P', 'in', 'letter'); // use inches for user units
$pdf->OpenImage('star', 2, 2); // create a 2 x 2 image
$pdf->SetFillColor (0, 0, 255);
$pdf->Rect(0, 0, 2, 2, 'F'); // fill it with blue
$pdf->SetFillColor (255);
$pdf->Polygon(1, 1, 5, 'F', 1, 1, 2, 0, deg2rad(90)); // put a white star in the center
$pdf->CloseImage();
$pdf->AddPage();
$pdf->Image('star', 1, 1); // put the image in various sizes
$pdf->Image('star', 3, 1, 0.25);
$pdf->Image('star', 5, 1, 3); 
$pdf->Image('star', 5, 7, 4);
$pdf->Output();</code></pre>
<p>produces <a href="/blog/blogfiles/pdf/pdftest.php?which=GraphicsPDF">this</a>.</p>
<dl>
<dt><code>OpenImage($name, $w, $h)</code></dt>
<dd>Start creating a vector image named <code>$name</code> with width <code>$w</code> and height <code>$h</code>. All drawing code will go into the image, not the page (however, state-changing calls like <a href="http://www.fpdf.org/en/doc/setdrawcolor.htm"><code>SetDrawColor</code></a> and <a href="http://www.fpdf.org/en/doc/setlinewidth.htm"><code>SetLineWidth</code></a> will carry over to the page).</dd>
<dt><code>CloseImage()</code></dt>
<dd>Stop recording the current image and return to drawing directly on the page.</dd>
</dl>
<h3>Curve Calculations</h3>
<p><code>Arc</code>, <code>Polygon</code> and <code>Cloverleaf</code> involve a fair amount of heavy math, so I exposed methods to return simply the boundary points for the shapes rather than drawing them, to be used for building paths and the like.</p>
<p>Each of these returns an array of arrays. Each subarray has 4, 8 or 0 elements. 0 elements indicates that one shape has ended and another needs to start; think of a six-pointed star consisting of two triangles. 4 elements indicates a line: <code>[x0, y0, x1, y1]</code> is a line from <code>(x0, y0)</code> to <code>(x1, y1)</code>. 8 elements indicates a curve:  <code>[x0, y0, x1, y1, x2, y2, x3, y3]</code> is a curve from <code>(x0, y0)</code> to <code>(x3, y3)</code> with control points <code>(x1, y1)</code> and <code>(x2, y2)</code>.</p>
<dl>
<dt><code>EllipseCurves($x, $y, $a, $b=NULL, $theta=0, $lambda1=0, $lambda2=0)</code></dt>
<dd>Generate the curves for the ellipse that would be drawn by <code>Arc($x, $y, 'D', $a, $b, $theta, $lambda1, $lambda2)</code>.</dd>
<dt><code>PolygonLines($x, $y, $n, $a, $b=NULL, $skip=1, $theta=0, $lambda=0)</code></dt>
<dd>Generate the lines that would be drawn by <code>Polygon($x, $y, 'D', $n, $a, $b, $skip, $theta, $lambda)</code>.</dd>
<dt><code>CloverleafCurves($x, $y, $n, $a, $b=NULL, $skip=1, $theta=0, $lambda=0)</code></dt>
<dd>Generate the curves that would be drawn by <code>Cloverleaf($x, $y, 'D', $n, $a, $b, $skip, $theta, $lambda)</code>.</dd>
</dl>
<p>Hope this is useful to someone.</p>
]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/04/11/paths-vector-graphics-and-php-images-in-fpdf/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating PDFs with PHP, part 5: Text</title>
		<link>http://bililite.nfshost.com/blog/2011/04/01/creating-pdfs-with-php-part-5/</link>
		<comments>http://bililite.nfshost.com/blog/2011/04/01/creating-pdfs-with-php-part-5/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 23:21:40 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PDF]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1733</guid>
		<description><![CDATA[Now we need to add text. That's the most useful part of a PDF, and the easiest. Also the hardest. Sometimes life is like that. See the code. Text, the easy part The easy part is putting the text on the page. Use the same cm command to set the origin (there is another transformation [...]]]></description>
			<content:encoded><![CDATA[<p>Now we need to add text. That's the most useful part of a PDF, and the easiest. Also the hardest. Sometimes life is like that.<p>
<p><a href="/blog/blogfiles/pdf/pdftest.php?which=4&#038;output=source">See the code</a>.</p>
<span id="more-1733"></span>
<h3>Text, the easy part</h3>
<p>The easy part is putting the text on the page. Use the same <code>cm</code> command to set the origin (there is another transformation matrix just for text which is useful for long runs of text, but we're just putting in a line at a time), then <code>BT</code> (begin text) to start PDF thinking about text (I don't know why you can't just use the text commands, but this is one of the rules), then <code class="language-pdf">(string to print) Tj</code> (Tj for Text just-print-it? I don't know), then  <code>ET</code> (end text).</p>
<h3>Text, the hard part</h3>
<p>If you do this, you'll get nothing showing. That's because we haven't told PDF what font we want to use, and describing fonts is an arcane art that I haven't begun to understand. You can look at the code for <a href="http://fpdf.org">FPDF</a> and <a href="http://www.fpdf.org/en/script/script92.php">tFPDF</a> for details. In short, the process is similar to using images:</p>
<dl>
<dt>Define the font</dt>
<dd>This is called the "font descriptor", and involves listing the drawing commands for each glyph (the printed representation of a character) and the font metrics (the dimensions of each glyph and how they interact with their neighbors). PDF only can handle 256 characters per font, so real Unicode fonts need "composite fonts" with multiple subsets of each font.</dd>
<dd>Luckily for us, Adobe Reader comes with 14 fonts built-in (and, since Reader is the reference standard for PDF readers, you can count on them being built-in for any reader). These are:
<ul>
     <li>Courier</li>
     <li>Courier-Bold</li>
     <li>Courier-Oblique</li>
     <li>Courier-BoldOblique</li>
     <li>Helvetica</li>
     <li>Helvetica-Bold</li>
     <li>Helvetica-Oblique</li>
     <li>Helvetica-BoldOblique</li>
     <li>Times-Roman</li>
     <li>Times-Bold</li>
     <li>Times-Italic</li>
     <li>Times-BoldItalic</li>
     <li>Symbol</li>
     <li>ZapfDingbats</li>
</ul>
For any of these (and they only have the Ascii characters!) the font descriptor is simple:</dd>
<dd><pre><code class="language-pdf">10 0 obj
&lt;&lt;
/Type /Font
/Subtype /Type1 % they're type 1 fonts. They just are
/BaseFont /Helvetica % or whatever the font name is
&gt;&gt;
endobj
</code></pre></dd>
<dt>Define a name for the font</dt>
<dd>In the resource dictionary, determine a name for the font (this is arbitrary, but there's no reason not to use the real font name) in the <code class="language-pdf">/XObject</code> subdictionary:</dd>
<dd><pre><code class="language-pdf">2 0 obj
&lt;&lt;
  % ... other resource definitions
  /Font &lt;&lt; 
    /Helvetica 10 0 R % the name of object 6, defined above
    % ... other font descriptors
  &gt;&gt;
&gt;&gt;</code></pre></dd>
<dt>Use the font</dt>
<dd>In the content stream for the page, include the command</dd>
<dd><pre><code class="language-pdf">/Helvetica 12 Tf</code></pre> to set the font to Helvetica, 12 point</dd>
</dl>
<p>So the total commands to print Hello World in green 12 point Helvetica at <code>(100,100)</code> is:</p>
<pre><code class="language-pdf">q % push the graphics state
1 0 0 1 100 100 cm % translate origin
0 1 0 rg % set the fill color (text is filled, by default)
BT % start text
/Helvetica 12 Tf % set the font
(Hello, World) Tj % draw the text
ET % end text
Q % restore graphic state
</code></pre>
<h3>Example</h3>
<p><a href="http://bililite.nfshost.com/blog/blogfiles/pdf/pdftest.php?count=1&#038;which=4">See the example</a>.</p>
<pre><code class="language-php">$pdf = new bililitePDF_4();
$pdf-&gt;newpage();
$count = 8;
for ($i = 0; $i &lt; $count; ++$i){
	$pdf-&gt;placetext('Hello, world', 'Helvetica-Bold', 30, 200, 600, 2*M_PI*$i/$count, 255*$i/$count, 255*$i/$count, 255*$i/$count);
}
$pdf-&gt;placetext('ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz', 'ZapfDingbats', 12, 10, 200);
$pdf-&gt;output();</code></pre>
<p>This ends the use of my toy class. All further discussion and development will be with the  real <a href="http://fpdf.org">FPDF</a>.

]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/04/01/creating-pdfs-with-php-part-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating PDFs with PHP, part 4: Images</title>
		<link>http://bililite.nfshost.com/blog/2011/04/01/creating-pdfs-with-php-part-4/</link>
		<comments>http://bililite.nfshost.com/blog/2011/04/01/creating-pdfs-with-php-part-4/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 08:10:52 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PDF]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1731</guid>
		<description><![CDATA[Now that we can draw in our PDF, we want to add images. There are two kinds of images, bitmapped and vector. In PDF, images are called XObjects (The X stands for external, meaning defined outside the page). Vector images are easier, since they are just packages of PDF drawing commands, a sort of macro. [...]]]></description>
			<content:encoded><![CDATA[<p>Now that we can draw in our PDF, we want to add images. There are two kinds of images, <a href="http://graphicssoft.about.com/od/aboutgraphics/a/bitmapvector.htm">bitmapped and vector</a>. In PDF, images are called <code>XObject</code>s (The X stands for external, meaning defined outside the page). Vector images are easier, since they are just packages of PDF drawing commands, a sort of macro. PDF calls them <code>Form</code>s, since they were originally used to draw the boxes on a printed form.</p>
<p><a href="/blog/blogfiles/pdf/pdftest.php?which=3&#038;output=source">See the code</a>.</p>
<span id="more-1731"></span>
<h3>Vector graphics</h3>
<p>Using an XObject Form involves three parts:</p>
<dl>
<dt>Define the image</dt>
<dd>Create a content stream with the drawing commands, like:</dd>
<dd><pre><code class="language-pdf">6 0 obj
&lt;&lt;
/Subtype /Form % defines the type of image
/Resources 2 0 R % just like the resource dictionary for a page; no reason not to use the same one
/BBox [0 0 40 40] % the bounding box of the image, as [left bottom right top]. No reason not to use [0 0 width height]
/Length 47
&gt;&gt;
stream
5 w
0 0 40 40 re % draw a black box
0 0 0 RG S
endstream
endobj</code></pre></dd>
<dt>Define a name for the image</dt>
<dd>In the resource dictionary, determine a name for the image (this is arbitrary) in the <code class="language-pdf">/XObject</code> subdictionary:</dd>
<dd><pre><code class="language-pdf">2 0 obj
&lt;&lt;
  % ... other resource definitions
  /XObject &lt;&lt; 
    /Image1 6 0 R % the name of object 6, defined above
    % ... other images
  &gt;&gt;
&gt;&gt;</code></pre></dd>
<dt>Use the image</dt>
<dd>In the content stream for the page, include the command</dd>
<dd><pre><code class="language-pdf">/Image1 Do</code></pre></dd>
</dl>
<p>The question is obvious: how do you tell PDF <em>where</em> on the page to draw the image? The answer is, you don't. The drawing commands draw a box from <code>(0,0)</code> to <code>(40,40)</code>, and that's where it's drawn. But you object: that makes them worthless!</p>
<p>The trick is to redefine the coordinate system so that <code>(0,0)</code> ends up where you want. PDF uses <a href="http://en.wikipedia.org/wiki/Affine_transformation">affine transformations</a>:</p>
<pre><code><em>x?</em> = a<em>x</em>+c<em>y</em>+e
<em>y?</em> = b<em>x</em>+d<em>y</em>+f</code></pre>
<p>where <em>x</em> is the x-coordinate in the new coordinate system (where the image "thinks" its drawing) and <em>x?</em> is the x-coordinate in the original coordinate system. So to put the image with the lower left corner (its <code>(0,0)</code> point) at <code>(50,100)</code>, you would want to have <code><em>x?</em> = <em>x</em>+50</code> and <code><em>y?</em> = <em>y</em>+100</code>, or <code>a=1 b=0 c=0 d=1 e=50 f=100</code>. The PDF command for that is <code>cm</code> (for coordinate matrix), so the drawing command would be:</p>
<pre><code class="language-pdf">1 0 0 1 50 100 cm
/Image1 Do</code></pre>
<p>Coordinate transformations are cumulative, so repeating the translation for another image would push it too far. You want to reset the transformation matrix before each drawing. You could calculate the inverse matrix (for the translation above, it's easy: <code class="language-pdf">1 0 0 1 -50 -100 cm</code>, but other transformations are more complicated) but PDF has an easier way: a graphics state stack. <code>q</code> pushes the current graphics state, including the transformation matrix, and <code>Q</code> pops it, so the final drawing command would be:</p>
<pre><code class="language-pdf">q
1 0 0 1 50 100 cm
/Image1 Do
Q</code></pre>
<p>Other transformations are possible, and the work of getting the math right has already been done:</p>
<dl>
<dt>Translation of the origin to <code>(<em>x</em>,<em>y</em>)</code></dt>
<dd>As above, <code>1 0 0 1 <em>x</em> <em>y</em> cm</code>.</dd>
<dt>Rotation around the origin by angle <em>theta</em></dt>
<dd><code><em>cos(theta)</em> <em>sin(theta)</em> <em>&ndash;sin(theta)</em> <em>cos(theta)</em> 0 0 cm</code>.</dd>
<dt>Scale (change the size of) the x-direction by a factor <em>xscale</em> and the y-direction by <em>yscale</em></dt>
<dd><code><em>xscale</em> 0 0 <em>yscale</em> 0 0 cm</code></dd>
</dl>
<p>These do the transformation around the origin (and doing them in the above order has the expected results; doing the transforms in some other order means translating in scaled units or along rotated axes, which is rarely what you want). More often, we want to place and rotate around the center of the image, so after the transformation, translate backwards half the height and width (in the new coordinate system): <code>1 0 0 1 <em>-width/2</em> <em>-height/2</em> cm</code>.</p>
<h3>Bitmapped graphics</h3>
<p>Bitmapped images are simply streams of bits, with a given color space (grayscale, <a href="http://en.wikipedia.org/wiki/CMYK_color_model">CMYK</a> or RGB) and a given number of bits per color component (grayscale has one component, CMYK has four and RGB has three). The easiest to use is RGB, with 8 bits per component, since that translates directly from the PHP image routines.</p>
<p>Using a bitmapped image is exactly the same as for a vector image, except the content stream is different:</p>
<pre><code class="language-pdf">7 0 obj
&lt;&lt;
/Subtype /Image % defines the type of image
/Width 50 % no bounding box, just a width and height
/Height 50
/ColorSpace /DeviceRGB
/BitsPerComponent 8
/Length 2500
&gt;&gt;
stream
...generate the bytes
endstream
endobj</code></pre>
<p>And "generate the bytes" for a PHP image is straightforward:</p>
<pre><code class="language-php">for ($row = 0; $row < $height; ++$row) for ($col = 0; $col < $width; ++$col){
  $colorindex = imagecolorat($im, $col, $row);
  $colors = imagecolorsforindex($im, $colorindex);
  $image-&gt;contents .= sprintf('%c%c%c', $colors['red'], $colors['green'], $colors['blue']);
}</code></pre>
<p>There is a huge gotcha here: the image is always scaled to be 1 pixel square. I don't know why; the width and height are there in the image definition, but that's the way it is. So you <em>have</em> to scale the image with <code><em>width</em> 0 0 <em>height</em> 0 0 cm</code> (or multiply <em>width</em> and <em>height</em> by some scale factor) before displaying with <code class="language-pdf">/bitmappedImage1 Do</code>. Then, since the units are now scaled, to place and rotate around the center you have to use <code>1 0 0 1 <em>-0.5</em> <em>-0.5</em> cm</code> (half the original (1x1) sizes). Trust me, this works.</p>
<p>The <a href="/blog/blogfiles/pdf/pdftest.php?which=3&#038;output=source">final code</a> is pretty simple.</p>
<h3>Example</h3>
<p><a href="/blog/blogfiles/pdf/pdftest.php?count=1&#038;which=3">See the example</a>.</p>
<p>Create the apple vector image:</p>
<pre><code class="language-php">$pdf->newForm();
// http://en.wikipedia.org/wiki/File:Apple_logo_black.svg, same as last example but now going into an Xform
$pdf->moveto(28.70919,92.37034);
$pdf->curveto(32.22477,92.37025,36.46696,91.64368,41.43575,90.19065);
$pdf->curveto(46.45132,88.73743999999999,49.77944,88.01088,51.42013,88.01096);
$pdf->curveto(53.52944,88.01088,56.97475,88.83118999999999,61.756057,90.4719);
$pdf->curveto(66.537237,92.11243,70.685667,92.93275,74.201377,92.93284);
$pdf->curveto(79.966907,92.93275,85.099717,91.38587,89.599807,88.29221);
$pdf->curveto(92.130957,86.51088,94.638767,84.09682000000001,97.123244,81.05002);
$pdf->curveto(93.373147,77.862449,90.630967,75.02649,88.896687,72.54219);
$pdf->curveto(85.755967,68.04213,84.185657,63.07338,84.185747,57.63594);
$pdf->curveto(84.185657,51.682770000000005,85.849717,46.31558,89.177937,41.53438);
$pdf->curveto(92.505957,36.75309,96.302824,33.729659999999996,100.56855,32.46407000000001);
$pdf->curveto(98.787204,26.69842,95.834084,20.674989999999994,91.709187,14.393749999999997);
$pdf->curveto(85.474717,4.971879999999999,79.287227,0.26094000000000506,73.146687,0.26094000000000506);
$pdf->curveto(70.709107,0.26094000000000506,67.334107,1.0343799999999987,63.021687,2.581249999999997);
$pdf->curveto(58.755997,4.128129999999999,55.14663,4.9015600000000035,52.19356,4.901570000000007);
$pdf->curveto(49.24038,4.9015600000000035,45.79507,4.104690000000005,41.85763,2.510940000000005);
$pdf->curveto(37.96696,0.8703200000000066,34.8029,0.05001000000000033,32.36544,0.04999999999999716);
$pdf->curveto(25.00603,0.05001000000000033,17.78729,6.284369999999996,10.70919,18.75313);
$pdf->curveto(3.63105,31.081220000000002,0.09199,43.17496,0.092,55.03438);
$pdf->curveto(0.09199,66.04993999999999,2.7873,75.02649,8.1779402,81.96409);
$pdf->curveto(13.61542,88.9015,20.45916,92.37025,28.70919,92.37034);
$pdf->moveto(73.006057,120.07346);
$pdf->curveto(73.193477,119.46397,73.310667,118.92491,73.357627,118.45627);
$pdf->curveto(73.404417,117.98741,73.427857,117.51866,73.427937,117.05002);
$pdf->curveto(73.427857,114.04991,72.724727,110.76867,71.318557,107.20627);
$pdf->curveto(69.912237,103.64367,67.685677,100.33899,64.638877,97.29221);
$pdf->curveto(62.013807,94.71399,59.412247,92.97962,56.83419,92.08909);
$pdf->curveto(55.1935,91.57337,52.70913,91.17493,49.38106,90.89377);
$pdf->curveto(49.47476,98.01868,51.32632,104.18272999999999,54.93575,109.38596);
$pdf->curveto(58.591927,114.58897,64.615367,118.15147,73.006057,120.07346);
$pdf->fill (0,149,182); // biondi blue
$pdf->appendForm('apple', 101, 121);</code></pre>
<p>And if we have these images: <strong>smiley.png</strong> <img alt="smiley" src="/blog/blogfiles/pdf/smiley.png" title="smiley" class="alignnone" width="58" height="59" />and <strong>smiley2.png</strong> <img alt="long smiley" src="/blog/blogfiles/pdf/smiley2.png" title="smiley2" class="alignnone" width="174" height="59" /> we can create bitmapped images:</p>
<pre><code class="language-php">$im = imagecreatefrompng('smiley.png');
$pdf->appendImage($im);
$im2 = imagecreatefrompng('smiley2.png');
$pdf->appendImage($im2);</code></pre>
<p>And use the images:</p>
<pre><code class="language-php">$pdf->placeimage('apple', 0, 0, 0, 1, 3); // centered at origin, stretched vertically
$pdf->placeimage('apple', 300, 300); // image placed straight
$pdf->placeimage('apple', 300, 500, M_PI/2); // rotated up 90 degrees
$pdf->placeimage($im, 200, 200); // image placed straight
$pdf->placeimage($im2, 200, 400, deg2rad(60)); // rotated 60 degrees</code></pre>
<h3>Transparency</h3>
<p>You will notice there is no mention of the <a href="http://en.wikipedia.org/wiki/Alpha_compositing">alpha channel</a> in the images or other forms of transparency. This is a format from 1993, after all. We were lucky to have transparent colors in our GIFs. But it is possible; see the <a href="http://fpdf.org/en/script/script74.php">transparency</a> and <a href="http://fpdf.org/en/script/script83.php">alpha channel</a> extensions to <a href="http://fpdf.org">FPDF</a>. I just am not going to go into it here.</p>
<h3>Compression</h3>
<p>The classes presented here allow for <a href="http://en.wikipedia.org/wiki/Gzip">gzip</a> compression, but there are better ways to compress images. PDF supports <a href="http://en.wikipedia.org/wiki/Portable_Network_Graphics">png</a> and <a href="http://en.wikipedia.org/wiki/Jpeg">jpeg</a> compressed images, as well as color palettes to reduce the bits per pixel. See the  <a href="http://fpdf.org">FPDF</a> functions <code>Image</code>, <code>_parsejpeg</code> and <code>_parsepng</code> for details.</p>
<p><a href="/blog/2011/04/01/creating-pdfs-with-php-part-5/">Continued&hellip;</a></p>


]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/04/01/creating-pdfs-with-php-part-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating PDFs with PHP, part 3: Drawing</title>
		<link>http://bililite.nfshost.com/blog/2011/03/31/creating-pdfs-with-php-part-3/</link>
		<comments>http://bililite.nfshost.com/blog/2011/03/31/creating-pdfs-with-php-part-3/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 03:18:07 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PDF]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1729</guid>
		<description><![CDATA[Now that we can create blank PDF's, it's time to add some stuff. Vector drawing commands (lines and shapes) are simple; you just add the commands to the page content stream. In terms of the original class that would be: $this->pages[count($this->pages)-1]->contents .= "the command\n"; // we just need some whitespace at the end, but the [...]]]></description>
			<content:encoded><![CDATA[<p>Now that we can create blank PDF's, it's time to add some stuff. Vector drawing commands (lines and shapes) are simple; you just add the commands to the page content stream. In terms of the <a href="/blog/blogfiles/pdf/pdftest.php?output=source&#038;which=1">original class</a> that would be:</p>
<pre><code class="language-php">$this->pages[count($this->pages)-1]->contents .= "the command\n";
// we just need some whitespace at the end, but the newline makes it easier to read the resulting PDF</code></pre>
<p>But to make things easier, we can keep track of the last page:</p>
<pre><code class="language-php">function newpage(){
  parent::newpage();
  $this->currentPage = $this->pages[count($this->pages)-1];
}
// and now adding commands is:
$this->currentPage->contents .= "the command\n";
// this also has the advantage that we can manipulate currentPage to add commands to other content streams</code></pre>
<p>There are <a href="http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf#page=131">lots of commands</a>, all of which are postfix (parameters come before operators). There are no math operators or stack manipulation operators; any calculation has to be done before generating the PDF and numbers inserted directly.</p>
<p><a href="/blog/blogfiles/pdf/pdftest.php?which=2&#038;output=source">See the code</a>.</p>
<span id="more-1729"></span>
<p>Drawing is done by <a href="http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf#page=161">creating a path</a>, then filling or stroking (outlining) it. Some useful commands include:</p>
<dl>
<dt><code><em>x y</em> m</code></dt>
<dd>Move to point <em>(x, y)</em>.</dd>
<dt><code><em>x y</em> l</code></dt>
<dd>Draw a straight line from the current point to <em>(x, y)</em>.</dd>
<dt><code>h</code></dt>
<dd>Close the current path by drawing a line back to the last point moved to. You have to explicitly close a path to fill it, and even stroking it looks better if it is closed since PDF will make the last line join the first with an elegant miter. Just drawing a line to the starting point (like <code class="language-pdf">0 0 m 1 0 l 0 1 l 0 0 l</code>) doesn't count.</dd>
<dt><code><em>x1 y1 x2 y2 x3 y3</em> c</code></dt>
<c>Draw a cubic <a href="http://en.wikipedia.org/wiki/Bézier_curve">Bézier curve</a> from the current point to <em>(x3, y3)</em>, with <em>(x1, y1)</em> and <em>(x2, y2)</em> as control points. You can play with Bézier curves and manipulate control points to get a feel for them at <a href="http://processingjs.nihongoresources.com/bezierinfo/">Mike Kamermans's site</a>.
<dt><code><em>x y w h</em> re</code></dt>
<dd>Draw a closed rectangle from <em>(x, y)</em> with width <em>w</em> and height <em>h</em>. Shorthand for 
<code><em>x</em> <em>y</em> m <em>(x + w)</em> <em>y</em> l
<em>(x + w)</em> <em>(y + h)</em> l
<em>x</em> <em>(y + h)</em> l
h</code>. But PDF will do the math for you.</dd>
<dt><code><em>r g b</em> RG</code></dt>
<dd>Set the stroking color to <em>(r, g, b)</em> where the color components are fractional, from 0 to 1, unlike PHP where the components go from 0 to 255.</dd>
<dt><code><em>r g b</em> rg</code></dt>
<dd>Set the fill color to <em>(r, g, b)</em>.</dd>
<dt><code><em>w</em> w</code></dt>
<dd>Set the line width for stroking to <em>w</em>.</dd>
<dt><code>S</code></dt>
<dd>Stroke the current path. The fill and stroke commands discard the current path. That means you can't fill then fill a path; you have to recreate it.</dd>
<dt><code>f</code></dt>
<dd>Fill the current path.</dd>
</dl>
<p>So extending the basic class to allow drawing is <a href="/blog/blogfiles/pdf/pdftest.php?which=2&#038;output=source">straightforward</a>. The only gotcha is that the coordinate system is backwards: the origin is at the lower left corner and the y axis goes up (like a mathematical graph), unlike PHP imaging and every other computer graphics package I've ever used, where the origin is at the top left.<p>
<p><a href="/blog/blogfiles/pdf/pdftest.php?count=1&#038;which=2">See a sample page</a>.</p>
<h3>How'd you do that cool Apple logo?</h3>
<p>Luckily, <a href="http://en.wikipedia.org/wiki/Scalable_Vector_Graphics">SVG</a> uses Bézier curves as well, and can be easily read (paths like <code class="language-pdf">M 1,2 C 3,4 5,6 7,8</code> in SVG becomes <code class="language-pdf">1 2 m 3 4 5 6 7 8 c</code> in PDF). You just have to invert the y-values to reflect the reflected coordinate system. So finding a <a href="http://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg">good SVG image</a> lets you create a PDF image:</p>
<pre><code class="language-php">$pdf->moveto(28.70919,92.37034);
$pdf->curveto(32.22477,92.37025,36.46696,91.64368,41.43575,90.19065);
$pdf->curveto(46.45132,88.73743999999999,49.77944,88.01088,51.42013,88.01096);
$pdf->curveto(53.52944,88.01088,56.97475,88.83118999999999,61.756057,90.4719);
$pdf->curveto(66.537237,92.11243,70.685667,92.93275,74.201377,92.93284);
$pdf->curveto(79.966907,92.93275,85.099717,91.38587,89.599807,88.29221);
$pdf->curveto(92.130957,86.51088,94.638767,84.09682000000001,97.123244,81.05002);
$pdf->curveto(93.373147,77.862449,90.630967,75.02649,88.896687,72.54219);
$pdf->curveto(85.755967,68.04213,84.185657,63.07338,84.185747,57.63594);
$pdf->curveto(84.185657,51.682770000000005,85.849717,46.31558,89.177937,41.53438);
$pdf->curveto(92.505957,36.75309,96.302824,33.729659999999996,100.56855,32.46407000000001);
$pdf->curveto(98.787204,26.69842,95.834084,20.674989999999994,91.709187,14.393749999999997);
$pdf->curveto(85.474717,4.971879999999999,79.287227,0.26094000000000506,73.146687,0.26094000000000506);
$pdf->curveto(70.709107,0.26094000000000506,67.334107,1.0343799999999987,63.021687,2.581249999999997);
$pdf->curveto(58.755997,4.128129999999999,55.14663,4.9015600000000035,52.19356,4.901570000000007);
$pdf->curveto(49.24038,4.9015600000000035,45.79507,4.104690000000005,41.85763,2.510940000000005);
$pdf->curveto(37.96696,0.8703200000000066,34.8029,0.05001000000000033,32.36544,0.04999999999999716);
$pdf->curveto(25.00603,0.05001000000000033,17.78729,6.284369999999996,10.70919,18.75313);
$pdf->curveto(3.63105,31.081220000000002,0.09199,43.17496,0.092,55.03438);
$pdf->curveto(0.09199,66.04993999999999,2.7873,75.02649,8.1779402,81.96409);
$pdf->curveto(13.61542,88.9015,20.45916,92.37025,28.70919,92.37034);
$pdf->moveto(73.006057,120.07346);
$pdf->curveto(73.193477,119.46397,73.310667,118.92491,73.357627,118.45627);
$pdf->curveto(73.404417,117.98741,73.427857,117.51866,73.427937,117.05002);
$pdf->curveto(73.427857,114.04991,72.724727,110.76867,71.318557,107.20627);
$pdf->curveto(69.912237,103.64367,67.685677,100.33899,64.638877,97.29221);
$pdf->curveto(62.013807,94.71399,59.412247,92.97962,56.83419,92.08909);
$pdf->curveto(55.1935,91.57337,52.70913,91.17493,49.38106,90.89377);
$pdf->curveto(49.47476,98.01868,51.32632,104.18272999999999,54.93575,109.38596);
$pdf->curveto(58.591927,114.58897,64.615367,118.15147,73.006057,120.07346);
$pdf->fill (0,149,182); // biondi blue</code></pre>
<p>Yes, the image is trademarked. But I think this use is OK, since I'm acknowledging it as the Apple logo. If Apple's lawyer's find me, I'll be thrilled to be so important.</p>
<p>The code for the rectangle and S-curve is:</p>
<pre><code class="language-php">// filled rectangle
$pdf->moveto(100, 100);
$pdf->lineto(100,200);
$pdf->lineto(200,200);
$pdf->lineto(200,100);
$pdf->closepath();
$pdf->fill(0,255,0);
// rectangle outline, using the re command
$pdf->rect(100, 100, 100, 100);
$pdf->linewidth(10);
$pdf->stroke(255, 0, 0);
// Bézier curve
$pdf->moveto(200, 250);
$pdf->curveto(100, 200, 300, 200, 200, 150);
$pdf->stroke(0, 0, 0);</code></pre>
<h3>I don't need no Bézier curves&mdash;I need circles!</h3>
<p>Bézier curves are nice mathematically; they're polynomials, easily differentiated and easy to splice together in esthetically pleasing ways. But they're not circles. And you can't get an exact circle with cubic Béziers. The best you can do is approximate a set of arcs and join them together. To get a good approximation, each arc has to be 90 degrees or less. You match the slopes at the endpoints and the curvature at the midpoint, and you can do very well:  according to <a href="http://">Don Lancaster</a> the worst error is one part in a thousand. If you need better than that, you shouldn't be getting your drafting advice from a pediatrician. The math for an arbitrary arc of an arbitrarily oriented ellipse is straightforward but hairy; <a href="http://www.spaceroots.org/documents/ellipse/">L. Maisonobe</a> has the details. Since I'm so nice, I implemented it in PHP:</p>
<pre><code class="language-php">define ('M_TAU', 2*M_PI); // http://www.math.utah.edu/~palais/pi.html
function ellipsearc ($x, $y, $a, $b=NULL, $theta=0, $lambda1=0, $lambda2=0){
	// get the Bézier points of the elliptical arc with the ellipse centered at $x, $y, with major and minor semi-axes $a and $b,
	// and major axis at $theta to the x-axis,
	// with the arc starting at angle $lambda1 from the major axis (not the x-axis!) and ending at $lambda2
	// all angles are in radians.
	// returns an array of arrays, with each sub-array being a segment
	// of the final curve, with 8 elements, the x and y coordinates of
	// the control points.

	// normalize parameters
	if ($b === NULL) $b = $a;
	while ($lambda1 &lt; 0) $lambda1 += M_TAU;
	while ($lambda1 &gt;= M_TAU)  $lambda1 -= M_TAU;
	while ($lambda2 &lt;= 0) $lambda2 += M_TAU;
	while ($lambda2 &gt; M_TAU)  $lambda2 -= M_TAU;
	if ($lambda2 &lt; $lambda1){
		// goes through 0; split at 0;
		return array_merge(
			ellipsearc($x, $y, $a, $b, $theta, $lambda1, M_TAU),
			ellipsearc($x, $y, $a, $b, $theta, 0, $lambda2)
		);
	}else if ($lambda2 - $lambda1 &gt; M_PI_2){
		// Draw arcs less than 90 degrees, so bisect the arc
		return array_merge(
			ellipsearc($x, $y, $a, $b, $theta, $lambda1, ($lambda2+$lambda1)/2),
			ellipsearc($x, $y, $a, $b, $theta, ($lambda2+$lambda1)/2, $lambda2)
		);
	}
	
	// scale angles.
	$eta1 = scaleangle($lambda1, $a, $b);
	$eta2 = scaleangle($lambda2, $a, $b);
	
	// find the control points
	$x0 = ellipsepoint($x, $y, $a, $b, $theta, $eta1);
	$x3 = ellipsepoint($x, $y, $a, $b, $theta, $eta2);
	$delta = $eta2-$eta1;
	$tan = tan($delta/2);
	$alpha = sin($delta)*(sqrt(4+3*$tan*$tan)-1)/3; // Maisonobe's equation 15
	// formulae from Maisonobe's section 3.4.1
	$x1 = controlpoint($x0, $alpha);
	$x2 = controlpoint($x3, -$alpha);
	return array(array(
		$x0[0], $x0[1],
		$x1[0], $x1[1],
		$x2[0], $x2[1],
		$x3[0], $x3[1]
	));
}
function scaleangle($lambda, $a, $b){
	// parameterized angle from Maisonobe's section 2.2.1
	return atan2(sin($lambda)/$b, cos($lambda)/$a);
}
function ellipsepoint($x, $y, $a, $b, $theta, $eta){
	// parameterization of an ellipse. Maisonobe's equation 3 and 4
	// returns a 4-element array, with the coordinates first then the derivative
	$costheta = cos($theta);
	$sintheta = sin($theta);
	$coseta = cos($eta);
	$sineta = sin($eta);
	return array(
		$x+$a*$costheta*$coseta-$b*$sintheta*$sineta,
		$y+$a*$sintheta*$coseta+$b*$costheta*$sineta,
		  -$a*$costheta*$sineta-$b*$sintheta*$coseta,
		  -$a*$sintheta*$sineta+$b*$costheta*$coseta,
	);
}
function controlpoint($x, $alpha){
	// returns a point (as a 2-element array) that is $alpha from $x [0,1] along the vector $x[2,3]
	return array(
		$x[0]+$alpha*$x[2],
		$x[1]+$alpha*$x[3]	
	);
}</code></pre>
<p>For example, <code class="language-php">ellipsearc (100, 120, 45)</code> returns the curves for a circle centered at <code>(100,120)</code> with radius <code>45</code>, <code class="language-php">ellipsearc (100, 120, 50, 40)</code> gives an ellipse
centered at <code>(100,120)</code> with major axis horizontal, with major semiaxis (half the width) <code>50</code> and minor semiaxis (half the height) <code>40</code>. The "major axis" can be shorter than the minor axis; the designation is just of the axis that determines the orientation. <code class="language-php">ellipsearc (100, 120, 50, 40, deg2rad(60))</code> gives an ellipse
centered at <code>(100,120)</code> with major axis at 60 degrees from the horizontal, with major semiaxis (half the width) <code>50</code> and minor semiaxis (half the height) <code>40</code>. <code class="language-php">ellipsearc (100, 120, 45, NULL, 0, deg2rad(45), deg2rad(135))</code> returns the curves for an arc of a circle centered at <code>(100,120)</code> with radius <code>45</code>, with the arc extending from 45 degrees to 135 degrees.</p>
<p>It returns an array of arrays, like:</p>
<pre><code class="language-php">[
  [10,10,20,20,30,30,40,40], // a Bézier curve from (10,10) to (40,40) with control points (20,20) and (30, 30)
  [40,40,50,50,60,60,70,70],
  [80,80,90,90,100,100,10,10]
]</code></pre>
<p>So to draw the ellipse from the sample, use</p>
<pre><code class="language-php">$arcs = ellipsearc (200, 600, 150, 50);
$pdf->moveto($arc[0][0], $arc[0][1]);
foreach ($arcs as $arc) $pdf->curveto($arc[2], $arc[3], $arc[4], $arc[5], $arc[6], $arc[7]);
$pdf->closepath();
$pdf->stroke (0, 127, 127);</code></pre>
<p>And to draw a wedge (e.g. pie chart):</p>
<pre><code class="language-php">$arcs = ellipsearc (200, 600, 150, 150, 0, 0, deg2rad(60));
$pdf->moveto(200,600); // the center
$pdf->lineto($arc[0][0], $arc[0][1]);
foreach ($arcs as $arc) $pdf->curveto($arc[2], $arc[3], $arc[4], $arc[5], $arc[6], $arc[7]);
$pdf->closepath();
$pdf->fill(0, 127, 127);</code></pre>
<p>Hope this helps someone; it was interesting for me.</p>
<p><a href="/blog/2011/04/01/creating-pdfs-with-php-part-4/">Continued&hellip;</a></p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/03/31/creating-pdfs-with-php-part-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating PDFs with PHP, part 2: A Blank Page</title>
		<link>http://bililite.nfshost.com/blog/2011/03/24/creating-pdfs-with-php-part-2/</link>
		<comments>http://bililite.nfshost.com/blog/2011/03/24/creating-pdfs-with-php-part-2/#comments</comments>
		<pubDate>Fri, 25 Mar 2011 04:25:37 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PDF]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1699</guid>
		<description><![CDATA[Continuing my attempt to dissect FDPF to understand PDF's, we'll create the simplest PDF: a blank page. We need a couple of objects: Catalog This serves as the root object and describes the data structures in the document, which for our purposes is just the collection of printed pages. Other things, like the data for [...]]]></description>
			<content:encoded><![CDATA[<p>Continuing <a href="/blog/2011/03/18/creating-pdfs-with-php/">my attempt</a> to dissect <a href="http://fpdf.org">FDPF</a> to understand PDF's, we'll create the simplest PDF: a blank page.</p>
<p>We need a couple of objects:</p>
<dl>
<dt><a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf?page=73">Catalog</a></dt>
<dd>This serves as the root object and describes the data structures in the document, which for our purposes is just the collection of printed pages. Other things, like the data for interactive forms, Javascript routines and metadata (author, subject, keywords) would go here.</dd>
<dd><pre><code class="language-pdf">1 0 obj
&lt;&lt;
  /Type /Catalog
  /Pages 0 2 R % reference to object number 2
&gt;&gt;
endobj</code></pre></dd>
<dd>One useful optional entry in the catalog is the <code class="language-pdf">/OpenAction</code> that can be used to set the <a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf?page=365">zoom level and opening page</a>. 
 <code class="language-pdf">/OpenAction [3 0 R /Fit]</code> starts at the page described in object 3 and zooms in to fit the page on the screen.</dd>
</dl><!--need to split the DL to put the more comment in-->
<p><a href="/blog/blogfiles/pdf/pdftest.php?which=1&amp;output=source">See the code</a>.</p>
<span id="more-1699"></span>
<dl>
<dt><a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf?page=76">Pages</a></dt>
<dd>The collection of pages. This is just an array of references to the actual pages or to sub-collections of pages. The manual talks about organizing the pages in a balanced tree to make skipping around faster, but we're not that fancy. Our tree is flat. It also includes a <code class="language-pdf">/Count</code> entry with the total number of pages (which is not the same as the number of items in the array, since the array could contain references to other Pages collections).</dd>
<dd><pre><code class="language-pdf">2 0 obj
&lt;&lt;
  /Type /Pages
  /Kids [ 4 0 R ] % One page. They were too cute by half, calling the child objects "Kids"
  /Count 1
&gt;&gt;
endobj</code></pre></dd>
<dt><a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf?page=83">Resources</a></dt>
<dd>The resource dictionary that assigns names to all the "stuff" we need on the page&mdash;images, fonts, "graphic states" (sets of graphic commands to set the color, pen size, line styles, transparency, etc.) and other things. The page-drawing language (called a "content stream" does not refer to objects in the PDF by number (like <code class="language-pdf">2 0 R</code>) but by the name given in the page's resource dictionary. Since our page doesn't have any content, the dictionary is empty:</dd>
<dd><pre><code class="language-pdf">3 0 obj
&lt;&lt;
&gt;&gt;
endobj</code></pre></dd>
<dd>PDF originally required a <code class="language-pdf">/ProcSet</code> entry listing the types of commands used on a page, but <a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf?page=547">that's now obsolete</a>; all the commands are always available. fPDF still includes it, though.</dd>
<dt><a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf?page=77">Page</a></dt>
<dd>A description of a single page. Mostly just a reference to its contents, which is a stream and as such cannot be directly included (the definition could have allowed the page itself  to be the content stream, but that would have been too easy), and the page size, as a rectangle of point (1/72 inch) measures that sets the coordinate system for drawing. Also includes references to its parent <code>Pages</code> collection and the resource dictionary (you could have a separate resource dictionary for every page, but I can't imagine why you'd want to ).</dd>
<dd><pre><code class="language-pdf">4 0 obj
&lt;&lt;
  /Type /Page
  /Parent 2 0 R
  /Resources 3 0 R
  /Contents 5 0 R
  /MediaBox [ 0 0 612 792 ] % letter-size, in points.
&gt;&gt;
endobj</code></pre></dd>
<dt><a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf?page=81">Contents</a></dt>
<dd>The actual content stream for drawing the page. We don't do anything, so this is empty:</dd>
<dd><pre><code class="language-pdf">5 0 obj
&lt;&lt;
  /Length 0
&gt;&gt;
stream
endstream
endobj
</code></pre></dd>
</dl>
<p>We can put these parts together to create a <a href="/blog/blogfiles/pdf/pdftest.php?which=1&amp;output=source">class that produces blank pages</a>:</p>
<pre><code class="language-php">$pdf = new bililitePDF_1();
$pdf->newPage();
$pdf->output();
</code></pre>
<p>And you can see the result <a href="/blog/blogfiles/pdf/pdftest.php?which=1&#038;count=1">here</a>. Or even create <a href="/blog/blogfiles/pdf/pdftest.php?which=1&#038;count=3">multiple pages</a>. 150 lines of code to do nothing! Whoohoo! But it's a start to putting some real content in those pages.</p>
<p><a href="/blog/2011/03/31/creating-pdfs-with-php-part-3/">Continued&hellip;</a></p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/03/24/creating-pdfs-with-php-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating PDFs with PHP: Syntax</title>
		<link>http://bililite.nfshost.com/blog/2011/03/18/creating-pdfs-with-php/</link>
		<comments>http://bililite.nfshost.com/blog/2011/03/18/creating-pdfs-with-php/#comments</comments>
		<pubDate>Fri, 18 Mar 2011 11:06:27 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PDF]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1628</guid>
		<description><![CDATA[I wanted to allow my webservices to create PDF files, and I figured it couldn't be too hard&#8212;after all, it's just a bunch of graphics commands in a text file, right? Foolish me. The reference manual is 756 pages long, not including the javascript reference, another 692 pages. The place to start is fPDF, which [...]]]></description>
			<content:encoded><![CDATA[<p>I wanted to allow my <a href="/webservices">webservices</a> to create PDF files, and I figured it couldn't be too hard&mdash;after all, it's just a bunch of graphics commands in a text file, right? Foolish me. The <a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf">reference manual</a> is <em>756</em> pages long, not including the <a href="http://partners.adobe.com/public/developer/en/acrobat/sdk/pdf/javascript/AcroJS.pdf">javascript reference</a>, another <em>692</em> pages. The place to start is <a href="http://fpdf.org/">fPDF</a>, which is open source and pretty easy to understand, and its derivative <a href="http://fpdf.org/en/script/script92.php">tFPDF</a> that lets you use and embed True Type fonts (it's the 21<sup>st</sup> century; who uses anything but True Type fonts?). Using it is simple:</p>
<pre><code class="language-php">define('_SYSTEM_TTFONTS', '/path/to/your/truetype/fonts/'); // Took a bit of experimenting to find the right values for these
define('FPDF_FONTPATH', _SYSTEM_TTFONTS);
putenv('GDFONTPATH='._SYSTEM_TTFONTS); // so we can use GD images as well
$pdf=new tFPDF();
$pdf->AddPage();
$pdf->SetFont('Arial','B',16);
$pdf->Cell(40,10,'Hello World!');
$pdf->Output();</code></pre>
<p>One gotcha is that you need to create the <code>unifont</code> directory within the fonts folder, and copy tFPDF's <code>ttfonts.php</code> file into that.</p>
<p>The result is <a href="/blog/blogfiles/pdftest.php">here</a>.</p>
<span id="more-1628"></span>
<h3>PDF Syntax</h3>
<p>But to do much more you have to know what a PDF file looks like. The language itself is relatively simple (this has been further simplified so I can remember it):<p>
<p>Comments start with <code>%</code> and go to the end of the line. Whitespace is, in general, the only delimiter. There are 6 data types:<p>
<dl>
<dt>Number</dt>
<dd>Can be real or integer; no exponential notation. E.g.: <code class="language-pdf">1</code>, <code class="language-pdf">-1.2</code></dd>
<dt>String</dt>
<dd>Ascii only! This is a format from <a href="http://en.wikipedia.org/wiki/Pdf">1993</a>, after all. Delimited by parentheses, with the backslash as an
escape character. It's smart enough to count parenthesis so you technically don't have to escape them if they're matched, but that's not useful in real life for me. I use regular expressions, and <a href="http://stackoverflow.com/questions/546433/regular-expression-to-match-outer-brackets">regular expressions can't count</a>. Just escape the parentheses. E.g. <code class="language-pdf">(Hello, World)</code>,  <code class="language-pdf">(Escaped \(parentheses\) )</code><pre><code class="language-pdf">(Strings can
contain newlines)</code></pre></dd>
<dd>
<dd>If you want Unicode, the string has to be <a href="http://en.wikipedia.org/wiki/UTF-16/UCS-2#Byte_order_encoding_schemes">UTF-16 (big endian) encoded with a byte order mark</a> (!), enclosed in angle brackets: &#1513;&#1500;&#1493;&#1501;, &#1506;&#1493;&#1500;&#1501; is <code class="language-pdf">&lt;FEFF05E905DC05D505DD002C05E205D505DC05DD&gt;</code>. A pain but it's codable:
<pre><code class="language-php">function textstring($s){
  // Assumes $s is UTF8
  // use mb strings with explicit encodings to avoid problems with overloaded
  // regular string functions
  if (mb_strlen($s, 'UTF-8') == mb_strlen($s, '8bit')){
    // Ascii
    return '('.str_replace(array('\\', '(', ')',), array ('\\', '\(','\)'), $s).')';
  }else{
    $ret = '';
    $s = mb_convert_encoding ($s, 'UTF-16BE', 'UTF-8');
    foreach (str_split($s) as $char) $ret .= sprintf('%02X', ord($char)); // str_split should not be overloaded according to the manual
    return "&lt;FEFF$ret&gt;";
  }
}</code></pre></dd>
<dt>Name</dt>
<dd>These are the symbols used to represent external objects (like fonts or images) or keywords and indices into associative arrays. A name is a slash followed by "regular characters" which for all intents and purposes means alphanumerics. E.g. <code class="language-pdf">/Name</code>, <code class="language-pdf">/Image3</code></dd>
<dd>Technically, names can include any character if encoded as <code>#</code> then 2-digit hex value, with all characters 8-bit. Programmatically:
<pre><code class="language-php">define (PDF_DELIMITERS, '()<>{}%#/');
function name ($data){
	$ret = '/';
	foreach(str_split($data) as $c){
		$ord = ord($c);
		if ($ord == 0){
			 // str_split will give nulls for empty strings; ignore them
		}elseif ($ord &lt; 33 /* whitespace and control characters */ || $ord &gt; 126 /* hi bit set */ || strpos(PDF_DELIMITERS, $c) !== FALSE){
			$ret .= sprintf('#%02X', $ord);
		}else{
			$ret .= $c;
		}
	}
	return $ret;
}</code></pre></dd>
<dt>Dictionary</dt>
<dd>An associative array, with the key for each pair being a name and the data being any single data item. Delimited by <code class="language-pdf">&lt;&lt;</code> and <code class="language-pdf">&gt;&gt;</code>, with the key/data pairs just listed (the order is irrelevant). E.g. 
<pre><code class="language-pdf">&lt;&lt;
  /Name (textbox)
  /Width 20
  /Rules &lt;&lt; /Numeric /Yes /Positive /No &gt;&gt; % dictionaries are data and can be nested. 
                % This subdictionary has two entries, /Numeric and /Positive
&gt;&gt;
</code></pre>
<dd>Programmatically,
<pre><code class="language-php">function dictionary($arr){
	$ret = "&lt;&lt;\n";
	foreach($arr as $key=>$value) $ret .= this-&gt;name($key)." $value\n";
	return "$ret&gt;&gt;\n";
}</code></pre></dd>
<dt>Array</dt>
<dd>A linear list of data, delimited by square brackets. The order matters. E.g. <code class="language-pdf">[ 1 2 3 ]</code>, <code class="language-pdf">[ /First 2 /Third (Fourth) &lt;&lt;/dic 1&gt;&gt; ]</code></dd>
<dd>That's simply<pre><code class="language-php">function pdfarray($arr){
	$ret = "[\n";
	foreach($arr as $value) $ret .= " $value ";
	return "$ret]\n";
}</code></pre></dd>
<dt>Stream</dt>
<dd>A string of bytes, surrounded by the words <code class="language-pdf">stream</code> and <code class="language-pdf">endstream</code> on lines by themselves, preceded by a dictionary that describes the string. At a minimum the dictionary needs a <code class="language-pdf">/Length</code> entry with the length of the string in bytes. Yes, that's redundant in a delimited string, but that's the definition. Adobe Reader is smart enough to figure out the length if you leave out this entry. The bytes are not necessarily Ascii and are not defined by the PDF definition. Pages are streams, with the dictionary providing the metadata about the page and the string being the list of drawing commands (which is its own language). E.g.
<pre><code class="language-pdf">&lt;&lt;
  /Length 54
  /Fonts [/F1 /F2 /F3]
  /Width 37
  /Height 92
&gt;&gt; % the following is *not* the actual PDF drawing language
stream
1 2 moveto
4 5 lineto
/F1 setfont
(Hello, World) text
endstream</code></pre></dd>
<dd>It's possible to store a compressed string if there's a <code class="language-pdf">/Filter</code> entry in the dictionary whose value is the name of the compression algorithm used. The <a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#page=31">types of algorithms are built-in</a>; there are a couple for image files and <a href="http://www.faqs.org/rfcs/rfc1950">zlib</a> for text (that filter name is <code class="language-pdf">/FlateDecode</code> for reasons I can't fathom) (Yes, it would make sense to just gzip the whole file rather than pieces, but I'm not in charge). The compression makes the PDF harder to debug, so FPDF includes a function <code class="language-php">$pdf->SetCompression(false)</code> to not use it. The <code class="language-pdf">/Length</code> refers to the final, compressed length.</dd>
<dd>Programmatically,
<pre><code class="language-php">function stream($arr, $data){
	if ($this-&gt;bCompress){ // assumes this is a flag set somewhere
		$data = gzcompress($data);
		$arr['Filter'] = this-&gt;name('FlateDecode');
	}
	$arr['Length'] = strlen($data);
	return this-&gt;dictionary($arr)."stream\n$data\nendstream";
}</code></pre></dd>
</dl>
<p>In an ordinary programming language, you would declare variables to hold the data above. In PDF, these are called "indirect objects" and they are numbered, not named. In fact, they get two numbers, the object number and the "generation number" which is used when the PDF file is updated. Since we're generating PDFs from scratch, all our generation numbers are zero. Indirect objects are assigned with <code>object-number generation-number <strong>obj</strong> datum <strong>endobj</strong></code>. E.g.</p>
<pre><code class="language-pdf">1 0 obj
  [ (array) (of) (strings) ]
endobj</code></pre>
<p>creates object 1. You can use an indirect object anywhere data is required, with <code>object-number generation-number <strong>R</strong></code>. That's <code>R</code> as in reference. As you may have noticed, PDF uses a lot of <a href="http://en.wikipedia.org/wiki/Reverse_Polish_notation">reverse Polish notation</a>, from its origins in <a href="http://en.wikipedia.org/wiki/PostScript">Postscript</a> and <a href="http://en.wikipedia.org/wiki/Forth_(programming_language)">Forth</a>. E.g.</p>
<pre><code class="language-pdf">2 0 obj
  <<
    /Words 1 0 R % Use the array we declared above
    /Language 3 0 R % Forward references are fine
  >>
endobj

3 0 obj
  /English
endobj</code></pre>
<p>Streams have to be indirect objects on their own; they can't be members of a dictionary or array. But <em>references</em> to streams are legal. Thus:</p>
<pre><code class="language-pdf">% Illegal
1 0 obj
  &lt;&lt;
    /Type /Page
    /Content
      &lt;&lt;/Length 11&gt;&gt;
stream
      1 2 lineto
endstream
  &gt;&gt;
endobj

% Legal
2 0 obj
  &lt;&lt;
    /Type /Page
    /Content 3 0 R
  &gt;&gt;
endobj
3 0 obj
&lt;&lt;/Length 11&gt;&gt;
stream
      1 2 lineto
endstream
endobj</code></pre>
<p>The object numbers start at 1 and go up (technically they don't need to be continuous, but it's a headache otherwise). Object 0 is a special magic object with generation number 65,535 that acts as the head of a linked list of deleted objects, used for updating PDF files.</p>
<h3>PDF File Structure</h3>
<p>A PDF file is just a list of indirect objects, with a Catalog dictionary containing a reference to a Pages dictionary that contains an array of
references to Page dictionaries that each contain references to their Contents streams, which is the drawing instructions for that page. The Page dictionary also contains a reference to a Resources dictionary, which associates names with other objects like fonts, graphic states and images. The drawing instructions use those names, not the object references.</p>
<p>A logical way to organize this would be just to list the objects and have the reading program parse the file and create an array of objects. PDF assumes that you don't have enough memory for that, so the file itself contains the table of byte offsets of each object and acts as its own internal representation. Thus when creating the PDF, FPDF does something similar to:</p>
<pre><code class="language-php">function newobject($data){
  $objectNumber = count($this->objectOffsets)+1; // need to start from object 1
  $this->objectOffsets[] = strlen($this->buffer); // keep track of where this object starts in the final file
  $this->buffer .= "$objectNumber 0 obj \n $data \n endobj \n";
}</code></pre>
<p>The actual file starts with a comment with the PDF version number (the ISO standard is 1.7): <code class="language-pdf">%PDF-1.7</code>, then the objects (in any order; the object number is determined by the <code class="language-pdf">n 0 obj</code> statement). This is followed by the crossreference table (the above mentioned table of byte offsets) that has a fixed byte-level format, to make access faster:</p>
<pre><code class="language-php">function xref(){
  $numObjects = count($this->objectOffsets) + 1; // include the magic object 0
  $this->xrefOffset = strlen($this->buffer); // we'll need this later
  $this->buffer .= "xref\n";
  $this->buffer .= "0 $numObjects\n";
  $this->buffer .= "0000000000 65535 f \n"; // the magic object 0
  // output the offset, the generation number (always 0) and "n" for "in use"
  // use sprintf to make sure it has exactly the right number of bytes
  foreach ($this->objectOffsets as $offset) $this->buffer .= sprintf("%010d %05d n \n",$offset);
}</code></pre>
<p>After that is the "trailer," a dictionary that tells the PDF reader how many objects there are and which one is the "root," the main Catalog dictionary, and a pointer to the start of the crossreference table, and a comment to mark the end of the file (yes, there's lots of redundancy here):</p>
<pre><code class="language-php">function trailer(){
  $xrefOffset = $this->xrefOffset;
  $numObjects = count($this->objectOffsets) + 1; // include the magic object 0
  $root = $this->rootObject; // this needs to have been set at some point
  $this->buffer .= 'trailer << ';
  $this->buffer .= "/Size $numObjects ";
  $this->buffer .= "/Root $root 0 R ";
  $this->buffer .= ">>\n";
  $this->buffer .= "startxref\n$xrefOffset\n%%EOF";
}</code></pre>
<p>You can put more information in the trailer dictionary; see <a href="http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#page=51">page 43 of the PDF spec, table 15</a>.</p>
<p>And then dump the buffer (with the appropriate content header for the web) and you're done!. Of course, this page doesn't say anything about what goes into those objects, but at least it's a start to understanding what FPDF does and debugging the resulting document.</p>
<p><a href="/blog/2011/03/24/creating-pdfs-with-php-part-2/">Continued&hellip;</a></p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/03/18/creating-pdfs-with-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Search Box for the Website</title>
		<link>http://bililite.nfshost.com/blog/2011/02/25/a-search-box-for-the-website/</link>
		<comments>http://bililite.nfshost.com/blog/2011/02/25/a-search-box-for-the-website/#comments</comments>
		<pubDate>Fri, 25 Feb 2011 08:10:43 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.nfshost.com/blog/?p=1609</guid>
		<description><![CDATA[I've been playing with search engines and for the search API I'm going with Bing; Google limits their free API to 100 queries a day and requires creating a custom search engine. Bing requires signing up and getting an "AppID" but from there it's unlimited. Documentation is, well, Microsoftian: impossible to find and hard to [...]]]></description>
			<content:encoded><![CDATA[<p>I've been <a href="/blog/2011/02/24/trying-to-search-with-bing-and-failing/">playing with search engines</a> and for the search API I'm going with Bing; Google <a href="http://news.ycombinator.com/item?id=1864625">limits their free API to 100 queries a day and requires creating a custom search engine</a>. Bing requires <a href="http://www.bing.com/developers/createapp.aspx">signing up and getting an "AppID"</a> but from there it's unlimited. <a href="http://msdn.microsoft.com/en-us/library/dd251056.aspx">Documentation</a> is, well, Microsoftian: impossible to find and hard to use when you get it, but there's a <a href="http://www.bing.com/developers/s/API%20Basics.pdf">PDF</a> (!) that explains things pretty well. I decided to do everything on the server rather than being fancy with AJAX; the user has to wait either way and usually wants to leave the current page anyway (that's why he's searching!).<span id="more-1609"></span> This is what I came up with:</p>
<pre>&lt;?
<code class="language-php">
$appid = 'your app id here'; // for Bing Search API
$q = $_GET['q'];
if (get_magic_quotes_gpc()) $q = stripslashes($q);
// note that $q is unsafe!
</code>?&gt;

<code class="language-html">&lt;form method="get" action=""&gt;
  &lt;input name="q" type="text" value="&lt;?=htmlentities($q)?&gt;" /&gt;
  &lt;input type="submit" value="Search" /&gt;
&lt;/form&gt;</code>

&lt;?
<code class="language-php">
function sitesearch ($query, $site, $appid, $count=NULL){
  $url = "http://api.search.live.net/json.aspx?AppID=$appid&#038;sources=Web&#038;query=".urlencode("$query site:$site");
  if ($count) $url .= "&#038;web.count=$count";
  $ch = curl_init($url);
  curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE);
  $result = curl_exec($ch);
  curl_close($ch);
  return json_decode($result);
}

function showerrors($errors){
  echo "&lt;p&gt;Search Error&lt;p&gt;\n";
  foreach ($errors as $error) echo "&lt;p&gt;&lt;a href='{$error-&gt;HelpUrl}'&gt;{$error-&gt;Message}&lt;/a&gt;\n";
}

function showresponse ($q, $total, $results){
  $count = count($results);
  if ($count==0){
    $resultwords = 'No results';
  }else if ($total &gt; $count){
    $resultwords = "More than $count results";
  }else if ($count == 1){
    $resultwords = '1 result';
  }else{
    $resultwords = "$count results";
  }
  echo "&lt;p&gt;$resultwords found for &lt;strong&gt;".htmlentities($q)."&lt;/strong&gt;&lt;/p&gt;\n";
  if ($count == 0) return;
  echo "&lt;dl&gt;\n";
  foreach ($results as $result) echo "&lt;dt&gt;&lt;a href='{$result-&gt;Url}'&gt;{$result-&gt;Title}&lt;/a&gt;&lt;/dt&gt;\n&lt;dd&gt;{$result-&gt;Description}&lt;/dd&gt;\n";
  echo "&lt;/dl&gt;\n";    
}

if ($q){
  // get search results
  $result = sitesearch ($q, $_SERVER['HTTP_HOST'], $appid, 10);
  if ($result-&gt;SearchResponse-&gt;Errors){
    showerrors($result-&gt;SearchResponse-&gt;Errors);
  }else{
    showresponse($q, $result-&gt;SearchResponse-&gt;Web-&gt;Total, $result-&gt;SearchResponse-&gt;Web-&gt;Results);
  }
  $searchURL = 'http://www.bing.com/search?q='.urlencode("$q site:{$_SERVER['HTTP_HOST']}");
  echo "&lt;p&gt;&lt;a href='$searchURL'&gt;See all results&lt;/a&gt;&lt;p&gt;\n";
}</code></pre>
<p>The code works, though I have to say that Google's search gives better results. The advantage of a stable API outweighs that, I think. You can see it in action on the <a href="/">home page</a>.</p>
<p>One incredibly stupid thing for Microsoft to miss though: <a href="http://bing.com">bing.com</a> is not a valid URL; you have to use <a href="http://www.bing.com">www.bing.com</a>. Who does that anymore?</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.nfshost.com/blog/2011/02/25/a-search-box-for-the-website/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

