SimplePdf API
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
  • Download

Namespaces

  • SimplePdf

Classes

  • Page
  1 <?php
  2 namespace SimplePdf;
  3 
  4 /**
  5  * Extension of ZendPdf\Page which allows using arbitrary units (e.g. inches or centimeters) and works from
  6  * top to bottom, instead of default PDF/PostScript geometry. It also adds some basic formatting functionality,
  7  * like word wrap, margins and text alignment.
  8  *
  9  * @author Robbert Klarenbeek <robbertkl@renbeek.nl>
 10  * @copyright 2013 Robbert Klarenbeek
 11  * @license http://www.opensource.org/licenses/mit-license.php MIT License
 12  */
 13 class Page extends \ZendPdf\Page
 14 {
 15     /**
 16      * Constant for left aligned text
 17      *
 18      * @var float
 19      */
 20     const TEXT_ALIGN_LEFT = 0;
 21 
 22     /**
 23      * Constant for center aligned text
 24      *
 25      * @var float
 26      */
 27     const TEXT_ALIGN_CENTER = 0.5;
 28 
 29     /**
 30      * Constant for right aligned text
 31      *
 32      * @var float
 33      */
 34     const TEXT_ALIGN_RIGHT = 1;
 35 
 36     /**
 37      * Constant for point (native) units
 38      *
 39      * @var string
 40      */
 41     const UNITS_POINT = 1;
 42 
 43     /**
 44      * Constant for inch units (a point is 1/72 of an inch)
 45      *
 46      * @var string
 47      */
 48     const UNITS_INCH = 72;
 49 
 50     /**
 51      * Constant for centimeter units (72/2.54)
 52      *
 53      * @var string
 54      */
 55     const UNITS_CENTIMETER = 28.34645669291339;
 56 
 57     /**
 58      * Constant for millimeter units (7.2/2.54)
 59      *
 60      * @var string
 61      */
 62     const UNITS_MILLIMETER = 2.834645669291339;
 63 
 64     /**
 65      * Factor to convert native units (points) from/to user specified units
 66      *
 67      * @var float
 68      */
 69     protected $unitConversion = 1;
 70 
 71     /**
 72      * How far lines should be apart vertically, with 1.0 being 'normal' distance
 73      *
 74      * @var float
 75      */
 76     protected $lineSpacing = 1.0;
 77 
 78     /**
 79      * Margin on the left side of the page, stored in points
 80      *
 81      * @var float
 82      */
 83     protected $marginLeft = 0;
 84 
 85     /**
 86      * Margin on the right side of the page, stored in points
 87      *
 88      * @var float
 89      */
 90     protected $marginRight = 0;
 91 
 92     /**
 93      * Margin on the top edge, stored in points
 94      *
 95      * @var float
 96      */
 97     protected $marginTop = 0;
 98 
 99     /**
100      * Margin on the bottom edge, stored in points
101      *
102      * @var float
103      */
104     protected $marginBottom = 0;
105 
106     /**
107      * Create a new PDF page, with A4 size and default font Helvetica, size 12
108      *
109      * @param string $size page size (see \ZendPdf\Page), default A4 size
110      * @param float $unitConversion conversion factor for custom units, default self::UNITS_CENTIMETER
111      */
112     public function __construct($size = self::SIZE_A4, $unitConversion = self::UNITS_CENTIMETER)
113     {
114         parent::__construct($size);
115         $this->setUnitConversion($unitConversion);
116         $this->setFont(\ZendPdf\Font::fontWithName(\ZendPdf\Font::FONT_HELVETICA), 12);
117     }
118 
119     /**
120      * Get the current conversion factor to convert from/to native units (points)
121      *
122      * @return float current conversion factor
123      */
124     public function getUnitConversion()
125     {
126         return $this->unitConversion;
127     }
128 
129     /**
130      * Sets the conversion factor to use to convert from/to native units (points)
131      *
132      * @param float $unitConversion new conversion factor
133      */
134     public function setUnitConversion($unitConversion)
135     {
136         $this->unitConversion = $unitConversion;
137     }
138 
139     /**
140      * Convert a value in the given units to points
141      *
142      * @param float &$number number (in the given units) to convert, BY REF
143      */
144     public function convertToPoints(&$number)
145     {
146         $number *= $this->getUnitConversion();
147     }
148 
149     /**
150      * Convert a value in points to the given units
151      *
152      * @param float &$number number (in points) to convert, BY REF
153      */
154     public function convertFromPoints(&$number)
155     {
156         $number /= $this->getUnitConversion();
157     }
158 
159     /**
160      * Convert (x,y)-coordinates in the user space (top to bottom, in the given units, relative to the margins)
161      * to native geometry (points, bottom to top)
162      *
163      * @param float &$x x-coordinate (in the given units, from the left margin) to convert, BY REF
164      * @param float &$y y-coordinate (in the given units, from the top margin) to convert, BY REF
165      */
166     public function convertCoordinatesFromUserSpace(&$x, &$y)
167     {
168         $x += $this->getLeftMargin();
169         $this->convertToPoints($x);
170 
171         $y += $this->getTopMargin();
172         $y = $this->getHeight() - $y;
173         $this->convertToPoints($y);
174     }
175 
176     /**
177      * Convert (x,y)-coordinates in native geometry (points, bottom to top) to the
178      * user space (top to bottom, in the given units, relative to the margins)
179      *
180      * @param float &$x x-coordinate (in points, from the left) to convert, BY REF
181      * @param float &$y y-coordinate (in points, from the bottom) to convert, BY REF
182      */
183     public function convertCoordinatesToUserSpace(&$x, &$y)
184     {
185         $this->convertFromPoints($x);
186         $x -= $this->getLeftMargin();
187 
188         $this->convertFromPoints($y);
189         $y = $this->getHeight() - $y;
190         $x -= $this->getLeftMargin();
191     }
192 
193     /**
194      * Get the current line spacing
195      *
196      * @return float line spacing value, 1.0 being 'normal' distance
197      */
198     public function getLineSpacing()
199     {
200         return $this->lineSpacing;
201     }
202 
203     /**
204      * Sets the line spacing to use for future writeText() / writeLine() calls
205      *
206      * @param float $lineSpacing new line spacing value to use, 1.0 being 'normal' distance
207      */
208     public function setLineSpacing($lineSpacing)
209     {
210         $this->lineSpacing = $lineSpacing;
211     }
212 
213     /**
214      * Get the left margin of the page, in the given units
215      *
216      * @return float left page margin, in the given units
217      */
218     public function getLeftMargin()
219     {
220         $marginLeft = $this->marginLeft;
221         $this->convertFromPoints($marginLeft);
222         return $marginLeft;
223     }
224 
225     /**
226      * Set a new left margin, in the given units
227      *
228      * @param float $margin new left margin, in the given units
229      */
230     public function setLeftMargin($margin)
231     {
232         $this->convertToPoints($margin);
233         $this->marginLeft = $margin;
234     }
235 
236     /**
237      * Get the right margin of the page, in the given units
238      *
239      * @return float right page margin, in the given units
240      */
241     public function getRightMargin()
242     {
243         $marginRight = $this->marginRight;
244         $this->convertFromPoints($marginRight);
245         return $marginRight;
246     }
247 
248     /**
249      * Set a new right margin, in the given units
250      *
251      * @param float $margin new right margin, in the given units
252      */
253     public function setRightMargin($margin)
254     {
255         $this->convertToPoints($margin);
256         $this->marginRight = $margin;
257     }
258 
259     /**
260      * Get the top margin of the page, in the given units
261      *
262      * @return float top page margin, in the given units
263      */
264     public function getTopMargin()
265     {
266         $marginTop = $this->marginTop;
267         $this->convertFromPoints($marginTop);
268         return $marginTop;
269     }
270 
271     /**
272      * Set a new top margin, in the given units
273      *
274      * @param float $margin new top margin, in the given units
275      */
276     public function setTopMargin($margin)
277     {
278         $this->convertToPoints($margin);
279         $this->marginTop = $margin;
280     }
281 
282     /**
283      * Get the bottom margin of the page, in the given units
284      *
285      * @return float bottom page margin, in the given units
286      */
287     public function getBottomMargin()
288     {
289         $marginBottom = $this->marginBottom;
290         $this->convertFromPoints($marginBottom);
291         return $marginBottom;
292     }
293 
294     /**
295      * Set a new bottom margin, in the given units
296      *
297      * @param float $margin new bottom margin, in the given units
298      */
299     public function setBottomMargin($margin)
300     {
301         $this->convertToPoints($margin);
302         $this->marginBottom = $margin;
303     }
304 
305     /**
306      * Set new margin, in the given units
307      *
308      * @param float $marginLeft new left margin, in the given units
309      * @param float $marginRight new right margin, in the given units
310      * @param float $marginTop new top margin, in the given units
311      * @param float $marginBottom new bottom margin, in the given units
312      */
313     public function setMargins($marginLeft, $marginRight, $marginTop, $marginBottom)
314     {
315         $this->setLeftMargin($marginLeft);
316         $this->setRightMargin($marginRight);
317         $this->setTopMargin($marginTop);
318         $this->setBottomMargin($marginBottom);
319     }
320 
321     /**
322      * Set a new margin for all sides, in the given units
323      *
324      * @param float $margin new margin to set on all sides, in the given units
325      */
326     public function setAllMargins($margin)
327     {
328         $this->setMargins($margin, $margin, $margin, $margin);
329     }
330 
331     /**
332      * Get page width in the given units
333      *
334      * @return float width of the page in the given units
335      */
336     public function getWidth()
337     {
338         $width = parent::getWidth();
339         $this->convertFromPoints($width);
340         return $width;
341     }
342 
343     /**
344      * Get the width (in the given units) of the page area excluding the set margins (if any)
345      *
346      * @return float page width in the given units, excluding margins
347      */
348     public function getInnerWidth()
349     {
350         return $this->getWidth() - $this->getLeftMargin() - $this->getRightMargin();
351     }
352 
353     /**
354      * Get page height in the given units
355      *
356      * @return float height of the page in the given units
357      */
358     public function getHeight()
359     {
360         $height = parent::getHeight();
361         $this->convertFromPoints($height);
362         return $height;
363     }
364 
365     /**
366      * Get the height (in the given units) of the page area excluding the set margins (if any)
367      *
368      * @return float page height in the given units, excluding margins
369      */
370     public function getInnerHeight()
371     {
372         return $this->getHeight() - $this->getTopMargin() - $this->getBottomMargin();
373     }
374 
375     /**
376      * Sets a new font family and, optionally, a new font size as well
377      *
378      * @param \ZendPdf\Resource\Font\AbstractFont $font font object to use
379      * @param float $fontSize new font size, leave it out to keep the current font size
380      */
381     public function setFont(\ZendPdf\Resource\Font\AbstractFont $font, $fontSize = null)
382     {
383         if (is_null($fontSize)) {
384             $fontSize = $this->getFontSize();
385         }
386 
387         parent::setFont($font, $fontSize);
388     }
389 
390     /**
391      * Change the font size, without changing the font family
392      *
393      * @param float $fontSize new font size to use
394      */
395     public function setFontSize($fontSize)
396     {
397         $this->setFont($this->getFont(), $fontSize);
398     }
399 
400     /**
401      * Draw a line from 1 point to another
402      *
403      * @param float $x1 x-coordinate (in the given units) of the point from where to draw the line
404      * @param float $y1 y-coordinate (in the given units) of the point from where to draw the line
405      * @param float $x2 x-coordinate (in the given units) of the point to where to draw the line
406      * @param float $y2 y-coordinate (in the given units) of the point to where to draw the line
407      */
408     public function drawLine($x1, $y1, $x2, $y2)
409     {
410         $this->convertCoordinatesFromUserSpace($x1, $y1);
411         $this->convertCoordinatesFromUserSpace($x2, $y2);
412         parent::drawLine($x1, $y1, $x2, $y2);
413     }
414 
415     /**
416      * Write a (multiline / optionally wrapping) text to the page
417      *
418      * @param float $x x-coordinate (in the given units) of the anchor point of the text
419      * @param float $y y-coordinate (in the given units) of the anchor point of the text
420      * @param string $text text to write to the PDF (can contain newlines)
421      * @param float $anchorPoint horizontal position (0..1) to anchor each line, defaults to self::TEXT_ALIGN_LEFT
422      * @param float $wrapWidth width (in the given units) to wrap text at, or leave out for no wrapping
423      */
424     public function writeText($x, $y, $text, $anchorPoint = self::TEXT_ALIGN_LEFT, $wrapWidth = 0)
425     {
426         if ($wrapWidth > 0) {
427             $text = $this->wordWrapText($text, $wrapWidth);
428         }
429 
430         $lineHeight = $this->getLineHeight();
431         foreach (explode(PHP_EOL, $text) as $index => $line) {
432             if (empty($line)) {
433                 continue;
434             }
435 
436             $anchorOffset = ($anchorPoint == 0) ? 0 : -$anchorPoint * $this->getTextWidth($line);
437             $this->writeLine($x + $anchorOffset, $y + $index * $lineHeight, $line);
438         }
439     }
440 
441     /**
442      * Write a single line of text to the page
443      *
444      * @param float $x x-coordinate (in the given units) of the top-left corner where the text should start
445      * @param float $y y-coordinate (in the given units) of the top-left corner where the text should start
446      * @param string $line line to write to the page, should not contain newlines (and will NOT be wrapped)
447      */
448     public function writeLine($x, $y, $line)
449     {
450         $this->convertCoordinatesFromUserSpace($x, $y);
451         $y -= $this->getFontSize();
452         $this->drawText($line, $x, $y, 'UTF-8');
453     }
454 
455     /**
456      * Word-wrap a text to a certain width, using the current font properties
457      *
458      * @param string $text text to wrap (can already contain some newlines)
459      * @param string $wrapWidth width (in the given units) to wrap the text to
460      * @return string the same text but with newlines inserted at the specified $wrapWidth
461      */
462     public function wordWrapText($text, $wrapWidth)
463     {
464         $wrappedText = '';
465         foreach (explode(PHP_EOL, $text) as $line) {
466             $words = explode(' ', $line);
467             $currentLine = array_shift($words);
468             while (count($words) > 0) {
469                 $word = array_shift($words);
470                 if ($this->getTextWidth($currentLine . ' ' . $word) > $wrapWidth) {
471                     $wrappedText .= PHP_EOL . $currentLine;
472                     $currentLine = $word;
473                 } else {
474                     $currentLine .= ' ' . $word;
475                 }
476             }
477             $wrappedText .= PHP_EOL . $currentLine;
478         }
479         return ltrim($wrappedText, PHP_EOL);
480     }
481 
482     /**
483      * Get the line height (the offset between consecutive lines)
484      *
485      * @return float distance between consecutive lines in the given units
486      */
487     public function getLineHeight()
488     {
489         $lineHeight = $this->getFontSize() * 1.2 * $this->getLineSpacing();
490         $this->convertFromPoints($lineHeight);
491         return $lineHeight;
492     }
493 
494     /**
495      * Calculates how much (horizontal) space a text would use if written to the page, using
496      * the current font properties
497      *
498      * @param string $text text to calculate the width for (should not contain newlines)
499      * @return float width (in the given units) that the text would use if written to the page
500      */
501     public function getTextWidth($text)
502     {
503         $font = $this->getFont();
504         $fontSize = $this->getFontSize();
505         $text = iconv('UTF-8', 'UTF-16BE', $text);
506         $chars = array();
507         for ($i = 0; $i < strlen($text); $i++) {
508             $chars[] = (ord($text[$i++]) << 8) | ord($text[$i]);
509         }
510         $glyphs = $font->glyphNumbersForCharacters($chars);
511         $widths = $font->widthsForGlyphs($glyphs);
512         $textWidth = $fontSize * array_sum($widths) / $font->getUnitsPerEm();
513         $this->convertFromPoints($textWidth);
514         return $textWidth;
515     }
516 }
517 
SimplePdf API API documentation generated by ApiGen 2.8.0