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