source: spip-zone/_plugins_/spipdf/trunk/spipdf_fonctions.php @ 106511

Last change on this file since 106511 was 106511, checked in by marcimat@…, 3 years ago

Lorsqu’on utilise un script CLI pour générer un PDF, avoir les liens absolus, sinon il n’y arrive pas (avec mpdf tout du moins). On met un exemple ici pour s’en rappeler.
+ notice PHP en moins.

File size: 14.2 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                'html2pdf' => array( // ne gére pas le float d'image et les captions
257                        'float' => false,
258                        'caption' => true,
259                ),
260                'dompdf' => array( // domPDF beta 0.6 EXPERIMENTAL
261                        'float' => false,
262                        'caption' => true,
263                        'traite_balise_page' => true,
264                ),
265        );
266
267        // choix de la classe de génération via la balise <page lib>
268        if (preg_match('/\<page\s*.lib_pdf=["|\'](.*)["|\']/iUms', $html, $match_librairie)
269                && !empty($match_librairie[1])
270                && array_key_exists(strtolower($match_librairie[1]), $possible_librairies)
271        ){
272                $librairie_pdf = strtolower($match_librairie[1]);
273        } else {
274                $librairie_pdf = 'mpdf';
275        }
276
277        // tester si la librairie dans un sous-dossier de lib/ à la racine du spip ou dans le dossier squelettes/ ou dans un plugin
278        $dir_librairie_pdf = find_in_path("lib/$librairie_pdf/");
279        if (!$dir_librairie_pdf){
280                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/');
281        }
282
283        // nettoyer le HTML et gérer les placements d'image en fonction de la librairie utilisée
284        $html = spipdf_nettoyer_html($html, $possible_librairies[$librairie_pdf]);
285
286        // Debug = voir le html sans génération de PDF
287        if (isset($_GET['debug_spipdf'])){
288                echo $html;
289                exit;
290        }
291
292        // du A4 par defaut
293        $format_page = _SPIPDF_FORMAT;
294
295        // traiter la balise page pour les librairies qui ne la comprennent pas
296        if (!empty($possible_librairies[$librairie_pdf]['traite_balise_page'])){
297                $html = traite_balise_page($html);
298
299                // dans balise_page, on ne récupère que quelques possibilité dont le format
300                if (!empty($GLOBALS['valeurs_page'])){
301                        if (!empty($GLOBALS['valeurs_page']['format'])){
302                                $format_page = $GLOBALS['valeurs_page']['format'];
303                        }
304                        if (!empty($GLOBALS['valeurs_page']['backtop'])){
305                                $backtop = $GLOBALS['valeurs_page']['backtop'];
306                        } else {
307                                $backtop = _SPIPDF_MARGIN_TOP;
308                        }
309                        if (!empty($GLOBALS['valeurs_page']['backbottom'])){
310                                $backbottom = $GLOBALS['valeurs_page']['backbottom'];
311                        } else {
312                                $backbottom = _SPIPDF_MARGIN_BOTTOM;
313                        }
314                        if (!empty($GLOBALS['valeurs_page']['backleft'])){
315                                $backleft = $GLOBALS['valeurs_page']['backleft'];
316                        } else {
317                                $backleft = _SPIPDF_MARGIN_LEFT;
318                        }
319                        if (!empty($GLOBALS['valeurs_page']['backright'])){
320                                $backright = $GLOBALS['valeurs_page']['backright'];
321                        } else {
322                                $backright = _SPIPDF_MARGIN_RIGHT;
323                        }
324                        if (!empty($GLOBALS['valeurs_page']['margin_header'])){
325                                $margin_header = $GLOBALS['valeurs_page']['margin_header'];
326                        } else {
327                                $margin_header = _SPIPDF_MARGIN_HEADER;
328                        }
329                        if (!empty($GLOBALS['valeurs_page']['margin_footer'])){
330                                $margin_footer = $GLOBALS['valeurs_page']['margin_footer'];
331                        } else {
332                                $margin_footer = _SPIPDF_MARGIN_FOOTER;
333                        }
334                }
335        }
336
337        if ($librairie_pdf=='mpdf'){ // la librairie mPDF
338
339                // si il y a des options dans la balise page
340                // http://mpdf1.com/manual/index.php?tid=307
341
342                // le chemin relatif vers mPDF
343                define('_MPDF_PATH', $dir_librairie_pdf);
344                include_once _MPDF_PATH . 'mpdf.php';
345
346                // la classe mPDF
347                $mpdf = new mPDF(SPIPDF_CHARSET, $format_page, 0, '', $backleft, $backright, $backtop, $backbottom, $margin_header, $margin_footer);
348                $mpdf->WriteHTML($html);
349                /**
350                 * Si un nom de fichier est fourni, on enregistre le fichier,
351                 * sinon envoyer le code binaire du PDF dans le flux
352                 */
353                $html = $mpdf->Output($file, $file ? 'F' : 'S');
354                $echap_special_pdf_chars = true;
355        } elseif ($librairie_pdf=='dompdf') { // la librairie dompdf beta 0.6 // EXPERIMENTAL
356
357                // le chemin relatif vers mPDF
358                require_once $dir_librairie_pdf . 'dompdf_config.inc.php';
359
360                $dompdf = new DOMPDF();
361                $dompdf->load_html($html, SPIPDF_CHARSET);
362                $dompdf->set_paper($format_page);
363                $dompdf->render();
364                $html = $dompdf->output();
365               
366                if ($file) {
367                        include_spip('inc/flock');
368                        ecrire_fichier($file, $html);
369                }
370                // envoyer le code binaire du PDF dans le flux
371                $echap_special_pdf_chars = true;
372        } else { // la librairie HTML2PDF par défaut
373
374                // appel de la classe HTML2pdf
375                require_once $dir_librairie_pdf . 'html2pdf.class.php';
376                try {
377                        if ($flux['args']['contexte']['lang'] == '')
378                                $lang = 'fr';
379                        else
380                                $lang = $flux['args']['contexte']['lang'];
381                        // les paramétres d'orientation et de format son écrasé par ceux défini dans la balise <page> du squelette
382                        $html2pdf = new HTML2PDF('P', $format_page, $lang, SPIPDF_UNICODE, SPIPDF_CHARSET);
383
384                        // mode debug de HTML2PDF
385                        if (defined('SPIPDF_DEBUG_HTML2PDF')){
386                                $html2pdf->setModeDebug();
387                        }
388                        // police différente selon unicode ou latin
389                        if (SPIPDF_UNICODE){
390                                $police_caractere = 'FreeSans';
391                        } else {
392                                $police_caractere = 'Arial';
393                        }
394                        $html2pdf->setDefaultFont($police_caractere);
395                        $html2pdf->writeHTML($html);
396
397                        $html = $html2pdf->Output($file, $file ? 'F' : 'S'); // envoyer le code binaire du PDF dans le flux
398                        $echap_special_pdf_chars = true;
399                } catch (HTML2PDF_exception $e) {
400                        echo $e;
401                }
402        }
403
404        // On échappe les suites de caractères <? pour éviter des erreurs d'évaluation PHP (seront remis en place avec affichage_final)
405        // l'erreur d'évaluation est liée à la directive short_open_tag=On dans la configuration de PHP
406        if (!empty($echap_special_pdf_chars)
407                and strpos($html, '<' . '?')!==false
408        ){
409                $html = str_replace('<' . '?', "<\2\2?", $html);
410        }
411
412        return $html;
413}
414
415/**
416 * On rétablit les <? du code PDF si necessaire
417 * on n'agit que sur les pages non html.
418 *
419 * @param string $texte
420 *
421 * @return string
422 */
423function spipdf_affichage_final($texte){
424        if ($GLOBALS['html']==false
425                and strpos($texte, "<\2\2?")!==false
426        ){
427                $texte = str_replace("<\2\2?", '<' . '?', $texte);
428        }
429
430        return $texte;
431}
432
433/**
434 * Ne pas permettre d'aller chercher un fond en sous-repertoire dans spipdf.html.
435 *
436 * @param $fond
437 *
438 * @return mixed
439 */
440function spipdf_securise_fond($fond){
441        $fond = str_replace('/', '_', $fond);
442        $fond = str_replace('\\', '_', $fond);
443
444        return $fond;
445}
Note: See TracBrowser for help on using the repository browser.