]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/IO/Pdf.php
Fix attendence sheet, refs #1346
[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 protected $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 - 2);
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 $address = $this->preferences->getPostalAddress();
266
267 $this->MultiCell(
268 0,
269 4,
270 $address,
271 0,
272 'C'
273 );
274
275 if ($this->paginated) {
276 $this->SetFont(self::FONT, '', self::FONT_SIZE - 3);
277 $this->Ln();
278 $this->Cell(
279 0,
280 4,
281 $this->getAliasRightShift() . $this->PageNo() .
282 '/' . $this->getAliasNbPages(),
283 0,
284 1,
285 'R'
286 );
287 }
288 }
289 }
290
291 /**
292 * Draws PDF page header
293 *
294 * @param string $title Additionnal title to display just after logo
295 *
296 * @return void
297 */
298 public function PageHeader($title = null) // phpcs:ignore PSR1.Methods.CamelCapsMethodName
299 {
300 if (isset($this->model)) {
301 $html = null;
302 if (trim($this->model->hstyles) !== '') {
303 $html .= "<style>\n" . $this->model->hstyles . "\n</style>\n\n";
304 }
305 $html .= $this->model->hheader;
306 $this->writeHtml($html, true, false, true, false, '');
307
308 if ($title !== null) {
309 $this->writeHtml('<h2 style="text-align:center;">' . $title . '</h2>');
310 }
311
312 if (trim($this->model->title) !== '') {
313 $htitle = '';
314 if (trim($this->model->hstyles) !== '') {
315 $htitle .= "<style>\n" . $this->model->hstyles .
316 "\n</style>\n\n";
317 }
318 $htitle .= '<div id="pdf_title">' . $this->model->htitle . '</div>';
319 $this->writeHtml($htitle);
320 }
321 if (trim($this->model->subtitle) !== '') {
322 $hsubtitle = '';
323 if (trim($this->model->hstyles) !== '') {
324 $hsubtitle .= "<style>\n" . $this->model->hstyles .
325 "\n</style>\n\n";
326 }
327 $hsubtitle .= '<div id="pdf_subtitle">' . $this->model->hsubtitle .
328 '</div>';
329 $this->writeHtml($hsubtitle);
330 }
331 if (trim($this->model->title) !== ''
332 || trim($this->model->subtitle) !== ''
333 ) {
334 $this->Ln(5);
335 }
336 } else {
337 //default header
338 $print_logo = new \Galette\Core\PrintLogo();
339 $logofile = $print_logo->getPath();
340
341 // Set logo size to max width 30 mm or max height 25 mm
342 $ratio = $print_logo->getWidth()/$print_logo->getHeight();
343 if ($ratio < 1) {
344 if ($print_logo->getHeight() > 16) {
345 $hlogo = 25;
346 } else {
347 $hlogo = $print_logo->getHeight();
348 }
349 $wlogo = round($hlogo*$ratio);
350 } else {
351 if ($print_logo->getWidth() > 16) {
352 $wlogo = 30;
353 } else {
354 $wlogo = $print_logo->getWidth();
355 }
356 $hlogo = round($wlogo/$ratio);
357 }
358
359 $this->SetFont(self::FONT, 'B', self::FONT_SIZE + 4);
360 $this->SetTextColor(0, 0, 0);
361
362 $y = $this->GetY();
363 $this->Ln(2);
364 $ystart = $this->GetY();
365
366 $this->MultiCell(
367 180 - $wlogo,
368 6,
369 $this->preferences->pref_nom,
370 0,
371 'L'
372 );
373 $this->SetFont(self::FONT, 'B', self::FONT_SIZE + 2);
374
375 if ($title !== null) {
376 $this->Cell(0, 6, $title, 0, 1, 'L', 0);
377 }
378 $yend = $this->getY();//store position at the end of the text
379
380 $this->SetY($ystart);
381 $x = 190 - $wlogo; //right align
382 $this->Image($logofile, $x, $this->GetY(), $wlogo, $hlogo);
383 $this->y += $hlogo + 3;
384 //if position after logo is < than position after text,
385 //we have to change y
386 if ($this->getY() < $yend) {
387 $this->setY($yend);
388 }
389 }
390 }
391
392 /**
393 * Draws body from model
394 *
395 * @return void
396 */
397 public function PageBody() // phpcs:ignore PSR1.Methods.CamelCapsMethodName
398 {
399 $hbody = '';
400 if (trim($this->model->hstyles) !== '') {
401 $hbody .= "<style>\n" . $this->model->hstyles . "\n</style>\n\n";
402 }
403 $hbody .= $this->model->hbody;
404 $this->writeHtml($hbody);
405 }
406
407 /**
408 * Fix text size
409 *
410 * @param string $text Text content
411 * @param integer $maxsize Maximal size
412 * @param integer $fontsize Font size
413 * @param string $fontstyle Font style (defaults to '')
414 * @param string $fontname Font name (defaults to static::FONT)
415 *
416 * @return void
417 */
418 protected function fixSize($text, $maxsize, $fontsize, $fontstyle = '', $fontname = null)
419 {
420 if ($fontname === null) {
421 $fontname = static::FONT;
422 }
423 $this->SetFontSize($fontsize);
424 while ($this->GetStringWidth($text, $fontname, $fontstyle, $fontsize) > $maxsize) {
425 $fontsize--;
426 $this->SetFontSize($fontsize);
427 }
428 }
429
430 /**
431 * Cut a string
432 *
433 * @param string $str Original string
434 * @param integer $length Max length
435 *
436 * @return string
437 */
438 protected function cut($str, $length)
439 {
440 $length = $length -2; //keep a margin
441 if ($this->GetStringWidth($str) > $length) {
442 while ($this->GetStringWidth($str . '...') > $length) {
443 $str = mb_substr($str, 0, -1, 'UTF-8');
444 }
445 $str .= '...';
446 }
447 return $str;
448 }
449
450 /**
451 * Stretch a header string
452 *
453 * @param string $str Original string
454 * @param integer $length Max length
455 *
456 * @return string
457 */
458 protected function stretchHead($str, $length)
459 {
460 $this->SetFont(self::FONT, 'B', self::LIST_FONT);
461 $stretch = 100;
462 if ($this->GetStringWidth($str) > $length) {
463 while ($this->GetStringWidth($str) > $length) {
464 $this->setFontStretching(--$stretch);
465 }
466 }
467 return $str;
468 }
469
470 /**
471 * Get filename
472 *
473 * @return string
474 */
475 public function getFilename()
476 {
477 return $this->filename;
478 }
479
480 /**
481 * Download PDF from browser
482 *
483 * @return void
484 */
485 public function download()
486 {
487 $this->Output($this->filename, 'D');
488 }
489 }