source: spip-zone/_plugins_/spipdf/branches/mpdf/spipdf_fonctions.php @ 117543

Last change on this file since 117543 was 117543, checked in by tofulm, 6 months ago

On ajoute une compatibilite avec la lib mpdf >= v7
On utilise composer pour charger la lib, d'ou la presence maintenant du
dossier vendor.
pour rester compatible avec la lib mpdf < v7, on change la valeur de
l'attribut en mpdf8

File size: 15.4 KB
Line 
1<?php
2
3/**
4 * Génération d'article SPIP au format PDF.
5 *
6 * @package      spiPDF
7 * @author       Yves Tannier [grafactory.net]
8 * @copyright    2010-2017 Yves Tannier
9 *
10 * @link         https://contrib.spip.net/3719
11 * @link         https://zone.spip.org/trac/spip-zone/browser/_plugins_/spipdf
12 * @link         http://www.grafactory.net/
13 *
14 * @license      GPL Gnu Public Licence
15 *
16 * @version      1.2.0
17 */
18
19if (!defined('_ECRIRE_INC_VERSION')){
20        return;
21}
22
23// Options pour les marges des PDF, valables seulement pour la librairie mPDF
24// définissez vos options par défaut directement dans votre mes_options.php
25if (!defined('_SPIPDF_FORMAT')){
26        define('_SPIPDF_FORMAT', 'A4');
27}
28if (!defined('_SPIPDF_MARGIN_TOP')){
29        define('_SPIPDF_MARGIN_TOP', 20);
30}
31if (!defined('_SPIPDF_MARGIN_RIGHT')){
32        define('_SPIPDF_MARGIN_RIGHT', 20);
33}
34if (!defined('_SPIPDF_MARGIN_BOTTOM')){
35        define('_SPIPDF_MARGIN_BOTTOM', 15);
36}
37if (!defined('_SPIPDF_MARGIN_LEFT')){
38        define('_SPIPDF_MARGIN_LEFT', 15);
39}
40if (!defined('_SPIPDF_MARGIN_HEADER')){
41        define('_SPIPDF_MARGIN_HEADER', 2);
42}
43if (!defined('_SPIPDF_MARGIN_FOOTER')){
44        define('_SPIPDF_MARGIN_FOOTER', 2);
45}
46
47// Charset (qui peut être défini dans un fichier d'options
48if (!defined('SPIPDF_CHARSET')){
49        define('SPIPDF_CHARSET', 'UTF-8');
50        //define('SPIPDF_CHARSET', 'ISO-8859-15');
51}
52
53// utilisé pour le constructeur de HTML2PDF
54if (SPIPDF_CHARSET=='UTF-8'){
55        define('SPIPDF_UNICODE', true);
56} else {
57        define('SPIPDF_UNICODE', false);
58}
59
60// pour les function unicode2charset
61include_spip('inc/charsets');
62
63// repris dans le plugin article_pdf => a modifier
64// https://zone.spip.org/trac/spip-zone/browser/_plugins_/article_pdf
65function spipdf_first_clean($texte){
66
67        // supprimer les remarques HTML (du Couteau Suisse ?)
68        $texte = preg_replace(',<!-- .* -->,msU', '', $texte);
69
70        $trans = array();
71        $trans["<br />\n"] = '<BR>'; // Pour éviter que le \n ne se tranforme en espace dans les <DIV class=spip_code> (TT, tag SPIP : code)
72
73        // gestion d'un encodage latin1
74        if (SPIPDF_CHARSET=='ISO-8859-15' || SPIPDF_CHARSET=='iso-8859-15'){
75                $trans['&#176;'] = '°';
76                $trans['&#339;'] = 'oe';
77                $trans['&#8211;'] = '-';
78                $trans['&#8216;'] = '\'';
79                $trans['&#8217;'] = '\'';
80                $trans['&#8220;'] = '"';
81                $trans['&#8221;'] = '"';
82                $trans['&#8230;'] = '...';
83                $trans['&#8364;'] = 'Euros';
84                $trans['&ucirc;'] = 'û';
85                $trans['->'] = '-»';
86                $trans['<-'] = '«-';
87                $trans['&mdash;'] = '-';
88                $trans['&deg;'] = '°';
89                $trans['œ'] = 'oe';
90                $trans['Œ'] = 'OE';
91                $trans['…'] = '...';
92                $trans['&euro;'] = 'Euros';
93                $trans['€'] = 'Euros';
94                $trans['&copy;'] = '©';
95        }
96        // pas d'insécable
97        $trans['&nbsp;'] = ' ';
98
99        // certains titles font paniquer l'analyse
100        // TODO : a peaufiner car ils sont necessaires pour les signets
101        // <bookmark title="Compatibilité" level="0" ></bookmark>
102        // http://wiki.spipu.net/doku.php?id=html2pdf:fr:v4:bookmark
103        //$texte = preg_replace(',title=".*",msU', 'title=""', $texte);
104
105        $texte = strtr($texte, $trans);
106        if (SPIPDF_CHARSET=='UTF-8'){
107                $texte = charset2unicode($texte);
108        } else {
109                $texte = unicode2charset(charset2unicode($texte), SPIPDF_CHARSET); // Repasser tout dans le charset demandé
110        }
111
112        // Décoder les codes HTML dans le charset final
113        $texte = html_entity_decode($texte, ENT_NOQUOTES, SPIPDF_CHARSET);
114
115        return $texte;
116}
117
118//function spipdf_remplaceSpan($matches) { return str_replace('img', 'img style="padding:5px;" style="float:'.$matches[1].'"', $matches[2]); }
119function spipdf_remplaceSpan_wfloat($matches){
120        return str_replace('img', 'img style="padding:5px;" class="pdf_img_float_' . $matches[1] . '"', $matches[2]);
121}
122
123function spipdf_remplaceSpan($matches){
124        return str_replace('img', 'img style="padding:5px;" align="' . $matches[1] . '"', $matches[2]);
125}
126
127function spipdf_remplaceSpanCenter($matches){
128        return $matches[1];
129}
130
131//function spipdf_remplaceDt($matches) { return str_replace('img', 'img style="padding:5px;" style="float:'.$matches[1].'"', $matches[2]); }
132function spipdf_remplaceDt_wfloat($matches){
133        return str_replace('img', 'img style="padding:5px;" class="pdf_img_float_' . $matches[1] . '"', $matches[2]);
134}
135
136function spipdf_remplaceDt($matches){
137        return str_replace('img', 'img style="padding:5px;" align="' . $matches[1] . '"', $matches[2]);
138}
139
140function spipdf_remplaceIdParName($matches){
141        return str_replace('id=\'', 'name=\'', $matches[0]);
142}
143
144function spipdf_remplaceFloatPuce($matches){
145        return str_replace('style=\'', 'style=\'float:left;', $matches[0]);
146}
147
148function spipdf_remplaceDtCenter($matches){
149        return $matches[1];
150}
151
152function spipdf_remplaceCaption($matches){
153        $table = '<table style="border:none;"' . $matches[1] . '<tr><td style="text-align: center;border:none;">' . $matches[2] . '</td></tr>';
154        $table .= '<tr><td style="border:none;">';
155        $table .= '<table' . $matches[1] . $matches[3] . '</table>';
156        $table .= '</td></tr></table>';
157
158        return $table;
159}
160
161function spipdf_nettoyer_html($html, $params_pdf = array()){
162
163        // supprimer les spans autour des images
164        $patterns_float = '/<span class=\'spip_document_[0-9]+ spip_documents.*>(.*)<\/span>/iUms';
165        $html = preg_replace_callback($patterns_float, 'spipdf_remplaceSpanCenter', $html);
166
167        // supprimer les spans autour des images et récupérer le placement
168        $patterns_float = '/<span class=\'spip_document_[0-9]+ spip_documents.*float:(.*);.*>(.*)<\/span>/iUms';
169        $html = preg_replace_callback($patterns_float, !empty($params_pdf['float']) ? 'spipdf_remplaceSpan' : 'spipdf_remplaceSpan_wfloat', $html);
170
171
172        // supprimer les dl autour des images et récupérer le placement
173        $patterns_float = '/<dl class=\'spip_document_[0-9]+ spip_documents.*float:(.*);.*<dt>(.*)<\/dt>.*<\/dl>/iUms';
174        $html = preg_replace_callback($patterns_float, !empty($params_pdf['float']) ? 'spipdf_remplaceDt' : 'spipdf_remplaceDt_wfloat', $html);
175        // replacer id par name pour les notes
176        $patterns_note = '/<a[^>]*href[^>]*class=\'spip_note\'[^>]*>/iUms';
177        $html = preg_replace_callback($patterns_note, 'spipdf_remplaceIdParName', $html);
178
179        // float sur les puces graphiques
180        $patterns_puce = '/<img[^>]*class=[\'"]puce[\'"] alt=[\'"]-[\'"][^>]*>/iUms';
181        $html = preg_replace($patterns_puce, '-', $html);
182
183        // supprimer les dl autour des images centrer
184        $patterns_float = '/<dl class=\'spip_document_[0-9]+ spip_documents.*<dt>(.*)<\/dt>.*<\/dl>/iUms';
185        $html = preg_replace_callback($patterns_float, 'spipdf_remplaceDtCenter', $html);
186
187        // remplacer les captions
188        if (!empty($params_pdf['caption'])){
189                $patterns_caption = '/<table(.*)<caption>(.*)<\/caption>(.*)<\/table>/iUms';
190                $html = preg_replace_callback($patterns_caption, 'spipdf_remplaceCaption', $html);
191        }
192
193        // tableaux centré
194        $html = preg_replace('/<table/iUms', '<table align="center"', $html);
195
196        // balise cadre
197        $patterns_cadre = '/<textarea[^>]*class=[\'"]spip_cadre[\'"] [^>]*>(.*)<\/textarea>/iUms';
198        $html = preg_replace($patterns_cadre, '<div class="spip_cadre">$2</div>', $html);
199
200        // gestion des caractères spéciaux et de charset
201        $html = spipdf_first_clean($html);
202
203        return $html;
204}
205
206// traiter la balise page
207function traite_balise_page($html){
208
209        // on teste la balise page
210        if (preg_match('/<page\s(.*)>/iUms', $html, $matches)){
211                // on crée un tableau avec (beurk) Global pour accèder aux valeurs de pages
212                if (!empty($matches[1])){
213                        $balise_page = $matches[1];
214                        $pattern = '/(.*)="(.*)"/iUms';
215                        getBalise($matches);
216                        $balise_page = preg_replace_callback($pattern, 'getBalise', $balise_page);
217
218                        // supprimer <page> et </page>
219                        $html = preg_replace('/<\/?page\s(.*)>/iUms', '', $html);
220                        $html = preg_replace('/<\/page>/iUms', '', $html);
221
222                        return $html;
223                }
224        } else {
225                return $html;
226        }
227}
228
229//On sort cette fonction de la fonction traite_balise_page
230function getBalise($matches){
231        $matches = array_pad($matches, 3, null);
232        $matches[2] = str_replace('mm', '', $matches[2]);
233        $GLOBALS['valeurs_page'][trim($matches[1])] = trim($matches[2]);
234}
235
236/**
237 * traitement principal. avec ce pipeline, le PDF est mis en cache et recalculé "normalement"
238 *
239 *
240 * @param string $html
241 *   Le contenu HTML à transformer en PDF
242 * @param string/bool $file
243 *   Nom du fichier vers lequel enregistrer (uniquement fonctionnel avec mpdf pour l'instant)
244 * @return string
245 *   Contenu binaire du PDF généré
246 */
247function spipdf_html2pdf($html, $file = false){
248
249        // les librairies possibles
250        $possible_librairies = array(
251                'mpdf' => array( // gére le float d'image mais pas les captions de tableau
252                        'float' => true,
253                        'caption' => true,
254                        'traite_balise_page' => true,
255                ),
256                'mpdf8' => array( // mpdf v8 ou plus charger via autoloader
257                        'float' => true,
258                        'caption' => true,
259                        'traite_balise_page' => true,
260                ),
261                'html2pdf' => array( // ne gére pas le float d'image et les captions
262                        'float' => false,
263                        'caption' => true,
264                ),
265                'dompdf' => array( // domPDF beta 0.6 EXPERIMENTAL
266                        'float' => false,
267                        'caption' => true,
268                        'traite_balise_page' => true,
269                ),
270        );
271
272        // choix de la classe de génération via la balise <page lib>
273        if (preg_match('/\<page\s*.lib_pdf=["|\'](.*)["|\']/iUms', $html, $match_librairie)
274                && !empty($match_librairie[1])
275                && array_key_exists(strtolower($match_librairie[1]), $possible_librairies)
276        ){
277                $librairie_pdf = strtolower($match_librairie[1]);
278        } else {
279                $librairie_pdf = 'mpdf';
280        }
281
282        if ($librairie_pdf !== 'mpdf8') {
283                // tester si la librairie dans un sous-dossier de lib/ à la racine du spip ou dans le dossier squelettes/ ou dans un plugin
284                $dir_librairie_pdf = find_in_path("lib/$librairie_pdf/");
285                if (!$dir_librairie_pdf){
286                        die('Impossible de trouver la librairie de génération de PDF ' . $librairie_pdf . '. vérifiez que vous l\'avez bien téléchargée et installée dans lib/ ou squelettes/lib/');
287                }
288        }
289
290        // nettoyer le HTML et gérer les placements d'image en fonction de la librairie utilisée
291        //$html = spipdf_nettoyer_html($html, $possible_librairies[$librairie_pdf]);
292
293        // Debug = voir le html sans génération de PDF
294        if (isset($_GET['debug_spipdf'])){
295                echo $html;
296                exit;
297        }
298
299        // du A4 par defaut
300        $format_page = _SPIPDF_FORMAT;
301
302        // traiter la balise page pour les librairies qui ne la comprennent pas
303        if (!empty($possible_librairies[$librairie_pdf]['traite_balise_page'])){
304                $html = traite_balise_page($html);
305
306                // dans balise_page, on ne récupère que quelques possibilité dont le format
307                if (!empty($GLOBALS['valeurs_page'])){
308                        if (!empty($GLOBALS['valeurs_page']['format'])){
309                                $format_page = $GLOBALS['valeurs_page']['format'];
310                        }
311                        if (!empty($GLOBALS['valeurs_page']['backtop'])){
312                                $backtop = $GLOBALS['valeurs_page']['backtop'];
313                        } else {
314                                $backtop = _SPIPDF_MARGIN_TOP;
315                        }
316                        if (!empty($GLOBALS['valeurs_page']['backbottom'])){
317                                $backbottom = $GLOBALS['valeurs_page']['backbottom'];
318                        } else {
319                                $backbottom = _SPIPDF_MARGIN_BOTTOM;
320                        }
321                        if (!empty($GLOBALS['valeurs_page']['backleft'])){
322                                $backleft = $GLOBALS['valeurs_page']['backleft'];
323                        } else {
324                                $backleft = _SPIPDF_MARGIN_LEFT;
325                        }
326                        if (!empty($GLOBALS['valeurs_page']['backright'])){
327                                $backright = $GLOBALS['valeurs_page']['backright'];
328                        } else {
329                                $backright = _SPIPDF_MARGIN_RIGHT;
330                        }
331                        if (!empty($GLOBALS['valeurs_page']['margin_header'])){
332                                $margin_header = $GLOBALS['valeurs_page']['margin_header'];
333                        } else {
334                                $margin_header = _SPIPDF_MARGIN_HEADER;
335                        }
336                        if (!empty($GLOBALS['valeurs_page']['margin_footer'])){
337                                $margin_footer = $GLOBALS['valeurs_page']['margin_footer'];
338                        } else {
339                                $margin_footer = _SPIPDF_MARGIN_FOOTER;
340                        }
341                }
342        }
343
344        if ($librairie_pdf=='mpdf'){ // la librairie mPDF
345
346                // si il y a des options dans la balise page
347                // http://mpdf1.com/manual/index.php?tid=307
348
349                // le chemin relatif vers mPDF
350                if (!defined('_MPDF_PATH')) {
351                        define('_MPDF_PATH', $dir_librairie_pdf);
352                }
353                // les fichiers tmp dans le tmp/ de spip
354                if (!defined('_MPDF_TEMP_PATH')) {
355                        define("_MPDF_TEMP_PATH", sous_repertoire(_DIR_TMP, 'mpdf'));
356                }
357                if (!defined('_MPDF_TTFONTDATAPATH')) {
358                        define('_MPDF_TTFONTDATAPATH', sous_repertoire(_DIR_CACHE, 'ttfontdata'));
359                }
360
361                include_once _MPDF_PATH . 'mpdf.php';
362
363                // la classe mPDF
364                $mpdf = new mPDF(SPIPDF_CHARSET, $format_page, 0, '', $backleft, $backright, $backtop, $backbottom, $margin_header, $margin_footer);
365                $mpdf->WriteHTML($html);
366                /**
367                 * Si un nom de fichier est fourni, on enregistre le fichier,
368                 * sinon envoyer le code binaire du PDF dans le flux
369                 */
370                $html = $mpdf->Output($file, $file ? 'F' : 'S');
371                $echap_special_pdf_chars = true;
372        } elseif ($librairie_pdf=='mpdf8'){
373                // lib mpdf 8 charger via autoloader
374                // https://mpdf.github.io/installation-setup/installation-v7-x.html
375
376                // les fichiers tmp dans le tmp/ de spip
377                if (!defined('_MPDF_TEMP_PATH')) {
378                        define("_MPDF_TEMP_PATH", sous_repertoire(_DIR_TMP, 'mpdf'));
379                }
380
381                include_once _DIR_PLUGIN_SPIPDF.'vendor/autoload.php';
382
383                $options = array(
384                        'tempDir'    => _MPDF_TEMP_PATH,
385                        'charset_in' => SPIPDF_CHARSET,
386                        'format'     => $format_page,
387                        //'mode' => '1',
388                );
389
390                $mpdf = new \Mpdf\Mpdf($options);
391                $mpdf->WriteHTML($html);
392
393                /**
394                 * Si un nom de fichier est fourni, on enregistre le fichier,
395                 * sinon envoyer le code binaire du PDF dans le flux
396                 */
397                $html = $mpdf->Output($file, $file ? 'F' : 'S');
398                $echap_special_pdf_chars = true;
399        } elseif ($librairie_pdf=='dompdf') { // la librairie dompdf beta 0.6 // EXPERIMENTAL
400
401                // le chemin relatif vers mPDF
402                require_once $dir_librairie_pdf . 'dompdf_config.inc.php';
403
404                $dompdf = new DOMPDF();
405                $dompdf->load_html($html, SPIPDF_CHARSET);
406                $dompdf->set_paper($format_page);
407                $dompdf->render();
408                $html = $dompdf->output();
409
410                if ($file) {
411                        include_spip('inc/flock');
412                        ecrire_fichier($file, $html);
413                }
414                // envoyer le code binaire du PDF dans le flux
415                $echap_special_pdf_chars = true;
416        } else { // la librairie HTML2PDF par défaut
417
418                // appel de la classe HTML2pdf
419                require_once $dir_librairie_pdf . 'html2pdf.class.php';
420                try {
421                        if ($flux['args']['contexte']['lang'] == '')
422                                $lang = 'fr';
423                        else
424                                $lang = $flux['args']['contexte']['lang'];
425                        // les paramétres d'orientation et de format son écrasé par ceux défini dans la balise <page> du squelette
426                        $html2pdf = new HTML2PDF('P', $format_page, $lang, SPIPDF_UNICODE, SPIPDF_CHARSET);
427
428                        // mode debug de HTML2PDF
429                        if (defined('SPIPDF_DEBUG_HTML2PDF')){
430                                $html2pdf->setModeDebug();
431                        }
432                        // police différente selon unicode ou latin
433                        if (SPIPDF_UNICODE){
434                                $police_caractere = 'FreeSans';
435                        } else {
436                                $police_caractere = 'Arial';
437                        }
438                        $html2pdf->setDefaultFont($police_caractere);
439                        $html2pdf->writeHTML($html);
440
441                        $html = $html2pdf->Output($file, $file ? 'F' : 'S'); // envoyer le code binaire du PDF dans le flux
442                        $echap_special_pdf_chars = true;
443                } catch (HTML2PDF_exception $e) {
444                        echo $e;
445                }
446        }
447
448        // On échappe les suites de caractères <? pour éviter des erreurs d'évaluation PHP (seront remis en place avec affichage_final)
449        // l'erreur d'évaluation est liée à la directive short_open_tag=On dans la configuration de PHP
450        if (!empty($echap_special_pdf_chars)
451                and strpos($html, '<' . '?')!==false
452        ){
453                $html = str_replace('<' . '?', "<\2\2?", $html);
454        }
455
456        return $html;
457}
458
459/**
460 * On rétablit les <? du code PDF si necessaire
461 * on n'agit que sur les pages non html.
462 *
463 * @param string $texte
464 *
465 * @return string
466 */
467function spipdf_affichage_final($texte){
468        if ($GLOBALS['html']==false
469                and strpos($texte, "<\2\2?")!==false
470        ){
471                $texte = str_replace("<\2\2?", '<' . '?', $texte);
472        }
473
474        return $texte;
475}
476
477/**
478 * Ne pas permettre d'aller chercher un fond en sous-repertoire dans spipdf.html.
479 *
480 * @param $fond
481 *
482 * @return mixed
483 */
484function spipdf_securise_fond($fond){
485        $fond = str_replace('/', '_', $fond);
486        $fond = str_replace('\\', '_', $fond);
487
488        return $fond;
489}
Note: See TracBrowser for help on using the repository browser.