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 sample output.

See the code that produced the sample output.

Continue reading ‘Paths, Vector Graphics and PHP images in FPDF’ »

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.

Continue reading ‘Creating PDFs with PHP, part 5: Text’ »

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. PDF calls them Forms, since they were originally used to draw the boxes on a printed form.

See the code.

Continue reading ‘Creating PDFs with PHP, part 4: Images’ »

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 newline makes it easier to read the resulting PDF

But to make things easier, we can keep track of the last page:

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

There are lots of commands, 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.

See the code.

Continue reading ‘Creating PDFs with PHP, part 3: Drawing’ »

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 interactive forms, Javascript routines and metadata (author, subject, keywords) would go here.
1 0 obj
<<
  /Type /Catalog
  /Pages 0 2 R % reference to object number 2
>>
endobj
One useful optional entry in the catalog is the /OpenAction that can be used to set the zoom level and opening page. /OpenAction [3 0 R /Fit] starts at the page described in object 3 and zooms in to fit the page on the screen.

See the code.

Continue reading ‘Creating PDFs with PHP, part 2: A Blank Page’ »

I wanted to allow my webservices to create PDF files, and I figured it couldn't be too hard—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 769 pages. The place to start is fPDF, which is open source and pretty easy to understand, and its derivative tFPDF that lets you use and embed True Type fonts (it's the 21st century; who uses anything but True Type fonts?). Using it is simple:

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();

One gotcha is that you need to create the unifont directory within the fonts folder, and copy tFPDF's ttfonts.php file into that.

The result is here.

Continue reading ‘Creating PDFs with PHP: Syntax’ »

I've been trying on and off to get my head around continuations in Scheme, which is the language that gnucash uses for reports. It's one of the modern versions of Lisp. And then I came across a throwaway line in the YQL documentation about JSONP with a callback is a also called continuation-passing style. A little Googling finds this fascinating lecture by Douglas Crockford, a short summary by John Lam that makes it clear that the continuation idea is obvious to anyone who's done any asynchronous Javascript like AJAX, and a more intellectual discussion by Matt Might that goes through the Lisp side.

Now that it turns out that I already understand continuations, I'll have to go back and prove that to myself. And yes, Javascript is Lisp in C's clothing.

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 use when you get it, but there's a PDF (!) 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!). Continue reading ‘A Search Box for the Website’ »

I'm not a fan of having to create a Google Custom Search Engine to limit searches to one site with an HTML form (which seems to be necessary for mobile sites), so let's play with Bing:


<form method="get" action="http://www.bing.com/search" >
	<input name="q" type="text"/>
	<input type="submit" value="Search with Bing"/>
	<input name="q1" value="site:http://bililite.nfshost.com/blog" type="hidden"/>
</form>

And it works, but fails again on the mobile site, just like Google. This may get better as the search engines figure it out, but I'm going to have to write my own search engine portal. What a pain.

Actually, not such a pain. Something like:

header('Location: http://www.bing.com/search?q='.urlencode($_GET['q']).'+'.urlencode($_GET['q1']))

for Bing and

header('Location: http://www.google.com/search?q='.urlencode($_GET['q']).'+site:'.urlencode($_GET['sitesearch']))

for Google. And that's what I did for the Young Israel site

Earlier, I noted that the old, simple Google search:


<form method="get" action="http://www.google.com/search" >
	<input name="q" type="text"/>
	<input type="submit" value="Search with Google"/>
	<input name="sitesearch" value="http://bililite.nfshost.com/blog" type="hidden"/>
</form>

is deprecated and doesn't work from Google's mobile site. Google does have an API for custom searches that has all sorts of fancy parameters to manipulate, but it requires signing up for a key and constructing the search ahead of time. However, some experimentation shows that not including a key brings back the old, simple search query (with a new URL and some new parameters), so we're back in business:


<form method="get" action="http://www.google.com/cse" >
	<input name="q" type="text"/>
	<input type="submit" value="Search with Google"/>
	<input name="as_sitesearch" value="http://bililite.nfshost.com/blog" type="hidden"/>
</form>

And there's all sorts of interesting things hidden in there, like sorting by date (Google tries to guess that, but doesn't do so well):


<form method="get" action="http://www.google.com/cse" >
	<input name="q" type="text"/>
	<input type="submit" value="Search with Google"/>
	<input name="as_sitesearch" value="http://bililite.nfshost.com/blog" type="hidden"/>
	<input name="sort" value="date:d" type="hidden"/>
</form>

And the entire Web Search API is deprecated, so I'll have to change the googleSearch widget to use the JSON Custom Search API eventually.

Overall, sweet. At least until Google changes things again.

Addendum: it looks as though the mobile site does require the cx parameter to limit the search to a specific site. Oh, well. It's not that hard to create a custom search.