Zend_Pdf calculating length of text string in current font for line wrapping

Steve picture Steve · Aug 16, 2009 · Viewed 9.6k times · Source

Does anyone have an easy way to calculate how many points across a page a piece of text will consume in a particular font and size? (easy = minimal lines of code + computationally cheap). Zend_Pdf doesn't appear to have a function that does this, except for some very expensive calls for each character to getGlyphForCharacter(), getUnitsPerEm() and getWidthsForGlyph().

I'm generating a multi page PDF with several tables on each page, and need to wrap the text within the columns. It's already taking a few seconds to create it, and I don't want it to take too much longer or I'll have to start messing around with background tasks or progress bars etc.

The only solution I came up with is pre-computing the width (in points) of each character in each font size used, then adding these up over each string. Still quite costly.

Am I missing something? Or have you got anything simpler?

thanks!

Answer

David Snabel-Caunt picture David Snabel-Caunt · Aug 17, 2009

There's a way to calculate widths exactly, rather than using Gorilla3D's worst case algorithm.

Try this code from http://devzone.zend.com/article/2525-Zend_Pdf-tutorial#comments-2535

I've used it in my application to calculate offsets for right-aligned text and it works

/**
* Returns the total width in points of the string using the specified font and
* size.
*
* This is not the most efficient way to perform this calculation. I'm
* concentrating optimization efforts on the upcoming layout manager class.
* Similar calculations exist inside the layout manager class, but widths are
* generally calculated only after determining line fragments.
* 
* @link http://devzone.zend.com/article/2525-Zend_Pdf-tutorial#comments-2535 
* @param string $string
* @param Zend_Pdf_Resource_Font $font
* @param float $fontSize Font size in points
* @return float
*/
function widthForStringUsingFontSize($string, $font, $fontSize)
{
     $drawingString = iconv('UTF-8', 'UTF-16BE//IGNORE', $string);
     $characters = array();
     for ($i = 0; $i < strlen($drawingString); $i++) {
         $characters[] = (ord($drawingString[$i++]) << 8 ) | ord($drawingString[$i]);
     }
     $glyphs = $font->glyphNumbersForCharacters($characters);
     $widths = $font->widthsForGlyphs($glyphs);
     $stringWidth = (array_sum($widths) / $font->getUnitsPerEm()) * $fontSize;
     return $stringWidth;
 }

With regard to performance, I haven't used this intensively in a script but I can imagine it's slow. I'd suggest writing the PDFs to disk, if possible, so repeat views are very fast, and caching/hard coding data where possible.