]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/IO/Pdf.php
7d7a432bb27814f76277fac04180888333af8ffa
[galette.git] / galette / lib / Galette / IO / Pdf.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * PDF class for galette
7 * Traps tcpdf errors by overloading tcpdf::error method
8 * Adds convenient method to convert color html codes
9 * Adds a _parsegif function to convert gif to png
10 *
11 * PHP version 5
12 *
13 * Copyright © 2007-2014 The Galette Team
14 *
15 * This file is part of Galette (http://galette.tuxfamily.org).
16 *
17 * Galette is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * Galette is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
29 *
30 * @category IO
31 * @package Galette
32 *
33 * @author John Perr <johnperr@abul.org>
34 * @author Johan Cwiklinski <johan@x-tnd.be>
35 * @copyright 2007-2014 The Galette Team
36 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
37 * @version SVN: $Id$
38 * @link http://galette.tuxfamily.org
39 * @since Available since 0.7dev - 2007-07-21
40 */
41
42 namespace Galette\IO;
43
44 use Galette\Core\Preferences;
45 use Galette\Entity\PdfModel;
46 use Analog\Analog;
47
48 /*
49 * TCPDF configuration file for Galette
50 */
51 require_once GALETTE_CONFIG_PATH . 'galette_tcpdf_config.php';
52
53 /**
54 * Require TCPDF class
55 */
56 require_once GALETTE_TCPDF_PATH . '/tcpdf.php';
57
58 /**
59 * PDF class for galette
60 *
61 * @category IO
62 * @name PDF
63 * @package Galette
64 * @abstract Class for expanding TCPDF.
65 * @author John Perr <johnperr@abul.org>
66 * @author Johan Cwiklinski <johan@x-tnd.be>
67 * @copyright 2007-2014 The Galette Team
68 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
69 * @link http://galette.tuxfamily.org
70 * @since Available since 0.7dev - 2007-07-21
71 */
72
73 class Pdf extends \TCPDF
74 {
75
76 const FONT = 'DejaVuSans';
77 const FONT_SIZE = 10;
78
79 protected $preferences;
80 private $model;
81 private $paginated = false;
82 private $filename;
83
84 /**
85 * Main constructor, set creator and author
86 *
87 * @param Preferences $prefs Preferences
88 * @param PdfModel $model Related model
89 */
90 public function __construct(Preferences $prefs, $model = null)
91 {
92 $this->preferences = $prefs;
93 parent::__construct('P', 'mm', 'A4', true, 'UTF-8');
94 //set some values
95 $this->SetCreator(PDF_CREATOR);
96 $this->SetFont(self::FONT, '', self::FONT_SIZE);
97 $name = preg_replace(
98 '/%s/',
99 $this->preferences->pref_nom,
100 _T("Association %s")
101 );
102 $this->SetAuthor(
103 $name . ' (using Galette ' . GALETTE_VERSION .
104 'and TCPDF ' . TCPDF_VERSION . ')'
105 );
106
107 if ($model !== null) {
108 if ($model instanceof PdfModel) {
109 $this->model = $model;
110 $this->SetTitle($this->model->htitle);
111 } else {
112 throw new \UnexpectedValueException(
113 'Provided model must be an instance of PdfModel!'
114 );
115 }
116 }
117 }
118
119 /**
120 * Set show pagination
121 *
122 * @return void
123 */
124 public function showPagination()
125 {
126 $this->paginated = true;
127 }
128
129 /**
130 * Destructor
131 */
132 public function __destruct()
133 {
134 parent::__destruct();
135 }
136
137 /**
138 * This method is automatically called in case of fatal error;
139 * it simply outputs the message and halts the execution.
140 * An inherited class may override it to customize the error
141 * handling but should always halt the script, or the resulting
142 * document would probably be invalid.
143 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
144 * 2007-07-21 :: John Perr : changed function to return error to session
145 * 2017-02-14 :: Johan Cwiklinski : use slim's flash message; do not rely on session for redirect
146 *
147 * @param string $msg The error message
148 *
149 * @return void
150 * @access public
151 * @since 1.0
152 */
153 public function Error($msg) // phpcs:ignore PSR1.Methods.CamelCapsMethodName
154 {
155 global $container;
156
157 Analog::log(
158 'PDF error: ' .$msg,
159 Analog::ERROR
160 );
161
162 $container->get('flash')->addMessage(
163 'error_detected',
164 $msg
165 );
166
167 $redirect = (isset($_SERVER['HTTP_REFERER']) ?
168 $_SERVER['HTTP_REFERER'] :
169 $container->get('router')->pathFor('slash'));
170 header('Location: ' . $redirect);
171 die();
172 }
173
174 /**
175 * Converts color from HTML format #RRVVBB
176 * to RGB 3 colors array.
177 *
178 * @param string $hex6 7 chars string #RRVVBB
179 *
180 * @return array
181 */
182 public function colorHex2Dec($hex6)
183 {
184 $dec = array(
185 "R" => hexdec(substr($hex6, 1, 2)),
186 "G" => hexdec(substr($hex6, 3, 2)),
187 "B" => hexdec(substr($hex6, 5, 2))
188 );
189 return $dec;
190 }
191
192 /** FIXME: is this function used somewhere? */
193 /**
194 * Extract info from a GIF file
195 * (In fact: converts gif image to png and feeds it to _parsepng)
196 *
197 * @param string $file path to the gif file
198 *
199 * @return void
200 * @access protected
201 */
202 protected function parsegif($file)
203 {
204 $a=GetImageSize($file);
205 if (empty($a)) {
206 $this->Error(_T("Missing or incorrect image file ") . $file);
207 }
208 if ($a[2]!=1) {
209 $this->Error(_T("Not a GIF file ") . $file);
210 }
211
212 // Tentative d'ouverture du fichier
213 if (function_exists('gd_info')) {
214 $data = @imagecreatefromgif($file);
215
216 // Test d'échec & Affichage d'un message d'erreur
217 if (!$data) {
218 $this->Error(_T("Error loading ").$file);
219 }
220 if (imagepng($data, GALETTE_ROOT . 'tempimages/gif2png.png')) {
221 return $this->_parsepng(GALETTE_ROOT . 'tempimages/gif2png.png');
222 } else {
223 $this->Error(_T("Error creating temporary png file from ").$file);
224 }
225 } else {
226 $this->Error(_T("Unable to convert GIF file ").$file);
227 }
228 }
229
230 /**
231 * Draws PDF page Header
232 *
233 * @return void
234 */
235 public function Header() // phpcs:ignore PSR1.Methods.CamelCapsMethodName
236 {
237 //just ovverride default header to prevent black line at top
238 }
239
240 /**
241 * Draws PDF page footer
242 *
243 * @return void
244 */
245 public function Footer() // phpcs:ignore PSR1.Methods.CamelCapsMethodName
246 {
247 $this->SetY(-20);
248 if (isset($this->model)) {
249 $hfooter = '';
250 if (trim($this->model->hstyles) !== '') {
251 $hfooter .= "<style>\n" . $this->model->hstyles . "\n</style>\n\n";
252 }
253 $hfooter .= $this->model->hfooter;
254 $this->writeHtml($hfooter);
255 } else {
256 $this->SetFont(self::FONT, '', self::FONT_SIZE);
257 $this->SetTextColor(0, 0, 0);
258
259 $name = preg_replace(
260 '/%s/',
261 $this->preferences->pref_nom,
262 _T("Association %s")
263 );
264
265 /** FIXME: get configured postal address */
266 $coordonnees_line1 = $name . ' - ' . $this->preferences->pref_adresse;
267 /** FIXME: pref_adresse2 should be removed */
268 if (trim($this->preferences->pref_adresse2) != '') {
269 $coordonnees_line1 .= ', ' . $this->preferences->pref_adresse2;
270 }
271 $coordonnees_line2 = $this->preferences->pref_cp . ' ' .
272 $this->preferences->pref_ville;
273
274 $this->Cell(
275 0,
276 4,
277 $coordonnees_line1,
278 0,
279 1,
280 'C',
281 0,
282 $this->preferences->pref_website
283 );
284 $this->Cell(
285 0,
286 4,
287 $coordonnees_line2,
288 0,
289 0,
290 'C',
291 0,
292 $this->preferences->pref_website
293 );
294
295 if ($this->paginated) {
296 $this->SetFont(self::FONT, '', self::FONT_SIZE - 3);
297 $this->Ln();
298 $this->Cell(
299 0,
300 4,
301 $this->getAliasRightShift() . $this->PageNo() .
302 '/' . $this->getAliasNbPages(),
303 0,
304 1,
305 'R'
306 );
307 }
308 }
309 }
310
311 /**
312 * Draws PDF page header
313 *
314 * @param string $title Additionnal title to display just after logo
315 *
316 * @return void
317 */
318 public function PageHeader($title = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName
319 {
320 if (isset($this->model)) {
321 $html = null;
322 if (trim($this->model->hstyles) !== '') {
323 $html .= "<style>\n" . $this->model->hstyles . "\n</style>\n\n";
324 }
325 $html .= $this->model->hheader;
326 $this->writeHtml($html, true, false, true, false, '');
327 if (trim($this->model->title) !== '') {
328 $htitle = '';
329 if (trim($this->model->hstyles) !== '') {
330 $htitle .= "<style>\n" . $this->model->hstyles .
331 "\n</style>\n\n";
332 }
333 $htitle .= '<div id="pdf_title">' . $this->model->htitle . '</div>';
334 $this->writeHtml($htitle);
335 }
336 if (trim($this->model->subtitle) !== '') {
337 $hsubtitle = '';
338 if (trim($this->model->hstyles) !== '') {
339 $hsubtitle .= "<style>\n" . $this->model->hstyles .
340 "\n</style>\n\n";
341 }
342 $hsubtitle .= '<div id="pdf_subtitle">' . $this->model->hsubtitle .
343 '</div>';
344 $this->writeHtml($hsubtitle);
345 }
346 if (trim($this->model->title) !== ''
347 || trim($this->model->subtitle) !== ''
348 ) {
349 $this->Ln(5);
350 }
351 } else {
352 //default header
353 $print_logo = new \Galette\Core\PrintLogo();
354 $logofile = $print_logo->getPath();
355
356 // Set logo size to max width 30 mm or max height 25 mm
357 $ratio = $print_logo->getWidth()/$print_logo->getHeight();
358 if ($ratio < 1) {
359 if ($print_logo->getHeight() > 16) {
360 $hlogo = 25;
361 } else {
362 $hlogo = $print_logo->getHeight();
363 }
364 $wlogo = round($hlogo*$ratio);
365 } else {
366 if ($print_logo->getWidth() > 16) {
367 $wlogo = 30;
368 } else {
369 $wlogo = $print_logo->getWidth();
370 }
371 $hlogo = round($wlogo/$ratio);
372 }
373
374 $this->SetFont(self::FONT, 'B', self::FONT_SIZE + 4);
375 $this->SetTextColor(0, 0, 0);
376
377 $y = $this->GetY();
378 $this->Ln(2);
379 $ystart = $this->GetY();
380
381 $this->Cell(
382 0,
383 6,
384 $this->preferences->pref_nom,
385 0,
386 1,
387 'L',
388 0,
389 $this->preferences->pref_website
390 );
391 $this->SetFont(self::FONT, 'B', self::FONT_SIZE + 2);
392
393 if ($title !== null) {
394 $this->Cell(0, 6, $title, 0, 1, 'L', 0);
395 }
396 $yend = $this->getY();//store position at the end of the text
397
398 $this->SetY($ystart);
399 $x = 190 - $wlogo; //right align
400 $this->Image($logofile, $x, $this->GetY(), $wlogo, $hlogo);
401 $this->y += $hlogo + 3;
402 //if position after logo is < than position after text,
403 //we have to change y
404 if ($this->getY() < $yend) {
405 $this->setY($yend);
406 }
407 }
408 }
409
410 /**
411 * Draws body from model
412 *
413 * @return void
414 */
415 public function PageBody() // phpcs:ignore PSR1.Methods.CamelCapsMethodName
416 {
417 $hbody = '';
418 if (trim($this->model->hstyles) !== '') {
419 $hbody .= "<style>\n" . $this->model->hstyles . "\n</style>\n\n";
420 }
421 $hbody .= $this->model->hbody;
422 $this->writeHtml($hbody);
423 }
424
425 /**
426 * Fix text size
427 *
428 * @param string $text Text content
429 * @param integer $maxsize Maximal size
430 * @param integer $fontsize Font size
431 * @param string $fontstyle Font style (defaults to '')
432 * @param string $fontname Font name (defaults to static::FONT)
433 *
434 * @return void
435 */
436 protected function fixSize($text, $maxsize, $fontsize, $fontstyle = '', $fontname = null)
437 {
438 if ($fontname === null) {
439 $fontname = static::FONT;
440 }
441 $this->SetFontSize($fontsize);
442 while ($this->GetStringWidth($text, $fontname, $fontstyle, $fontsize) > $maxsize) {
443 $fontsize--;
444 $this->SetFontSize($fontsize);
445 }
446 }
447
448 /**
449 * Cut a string
450 *
451 * @param string $str Original string
452 * @param integer $length Max length
453 *
454 * @return string
455 */
456 protected function cut($str, $length)
457 {
458 $length = $length -2; //keep a margin
459 if ($this->GetStringWidth($str) > $length) {
460 while ($this->GetStringWidth($str . '...') > $length) {
461 $str = mb_substr($str, 0, -1, 'UTF-8');
462 }
463 $str .= '...';
464 }
465 return $str;
466 }
467
468 /**
469 * Stretch a header string
470 *
471 * @param string $str Original string
472 * @param integer $length Max length
473 *
474 * @return string
475 */
476 protected function stretchHead($str, $length)
477 {
478 $this->SetFont(self::FONT, 'B', self::LIST_FONT);
479 $stretch = 100;
480 if ($this->GetStringWidth($str) > $length) {
481 while ($this->GetStringWidth($str) > $length) {
482 $this->setFontStretching(--$stretch);
483 }
484 }
485 return $str;
486 }
487
488 /**
489 * Get filename
490 *
491 * @return string
492 */
493 public function getFilename()
494 {
495 return $this->filename;
496 }
497
498 /**
499 * Download PDF from browser
500 *
501 * @return void
502 */
503 public function download()
504 {
505 $this->Output($this->filename, 'D');
506 }
507 }