Question

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!

Was it helpful?

Solution

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.

OTHER TIPS

Thinking over this a bit more. Take the widest Glyphs of the font your using and based that as the width for each character. It wont be accurate but it will prevent pushing text past the mark.

$pdf = new Zend_Pdf();
$font      = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_COURIER); 
$font_size = $pdf->getFontSize();


$letters = array();
foreach(range(0, 127) as $idx)
{
    array_push($letters, chr($idx));
}
$max_width = max($font->widthsForGlyphs($letters));

// Text wrapping settings
$text_font_size = $max_width; // widest possible glyph
$text_max_width = 238;        // 238px

// Text wrapping calcs
$posible_character_limit = round($text_max_width / $text_font_size);
$text = wordwrap($text, $posible_character_limit, "@newline@");
$text = explode('@newline@', $text);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top