Passed
Push — 1.11.x ( a27203...6b98ca )
by Angel Fernando Quiroz
11:27
created

PDF::html_to_pdf()   F

Complexity

Conditions 32
Paths 13715

Size

Total Lines 181
Code Lines 100

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 32
eloc 100
c 0
b 0
f 0
nc 13715
nop 8
dl 0
loc 181
rs 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/* See license terms in /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
5
use Mpdf\Mpdf;
6
use Mpdf\MpdfException;
7
use Mpdf\Utils\UtfString;
8
9
/**
10
 * Class PDF.
11
 */
12
class PDF
13
{
14
    /** @var Mpdf */
15
    public $pdf;
16
    public $custom_header = [];
17
    public $custom_footer = [];
18
    public $params = [];
19
    public $template;
20
21
    /**
22
     * Creates the Mpdf object.
23
     *
24
     * @param string   $pageFormat  format A4 A4-L see
25
     *                              http://mpdf1.com/manual/index.php?tid=184&searchstring=format
26
     * @param string   $orientation orientation "P" = Portrait "L" = Landscape
27
     * @param array    $params
28
     * @param Template $template
29
     *
30
     * @throws MpdfException
31
     */
32
    public function __construct(
33
        $pageFormat = 'A4',
34
        $orientation = 'P',
35
        $params = [],
36
        $template = null
37
    ) {
38
        $this->template = $template;
39
        /* More info @ http://mpdf1.com/manual/index.php?tid=184&searchstring=Mpdf */
40
        if (!in_array($orientation, ['P', 'L'])) {
41
            $orientation = 'P';
42
        }
43
        //left, right, top, bottom, margin_header, margin footer
44
45
        $params['left'] = $params['left'] ?? 15;
46
        $params['right'] = $params['right'] ?? 15;
47
        $params['top'] = $params['top'] ?? 30;
48
        $params['bottom'] = $params['bottom'] ?? 30;
49
        $params['margin_footer'] = $params['margin_footer'] ?? 8;
50
51
        $this->params['filename'] = $params['filename'] ?? api_get_local_time();
52
        $this->params['pdf_title'] = $params['pdf_title'] ?? '';
53
        $this->params['pdf_description'] = $params['pdf_description'] ?? '';
54
        $this->params['course_info'] = $params['course_info'] ?? api_get_course_info();
55
        $this->params['session_info'] = $params['session_info'] ?? api_get_session_info(api_get_session_id());
56
        $this->params['course_code'] = $params['course_code'] ?? api_get_course_id();
57
        $this->params['add_signatures'] = $params['add_signatures'] ?? [];
58
        $this->params['show_real_course_teachers'] = $params['show_real_course_teachers'] ?? false;
59
        $this->params['student_info'] = $params['student_info'] ?? false;
60
        $this->params['show_grade_generated_date'] = $params['show_grade_generated_date'] ?? false;
61
        $this->params['show_teacher_as_myself'] = $params['show_teacher_as_myself'] ?? true;
62
        $localTime = api_get_local_time();
63
        $this->params['pdf_date'] = $params['pdf_date'] ?? api_format_date($localTime, DATE_TIME_FORMAT_LONG);
64
        $this->params['pdf_date_only'] = $params['pdf_date'] ?? api_format_date($localTime, DATE_FORMAT_LONG);
65
66
        $this->pdf = new Mpdf(
67
            [
68
                'mode' => 'UTF-8',
69
                'format' => $pageFormat,
70
                'default_font_size' => '',
71
                'default_font' => '',
72
                'margin_left' => $params['left'],
73
                'margin_right' => $params['right'],
74
                'margin_top' => $params['top'],
75
                'margin_bottom' => $params['bottom'],
76
                'margin_header' => 8,
77
                'margin_footer' => 8,
78
                'orientation' => $orientation,
79
                'tempDir' => api_get_path(SYS_ARCHIVE_PATH).'mpdf/',
80
            ]
81
        );
82
83
        $this->pdf->margin_footer = $params['margin_footer'];
84
85
        // Default value is 96 set in the mpdf library file config.php
86
        $value = api_get_configuration_value('pdf_img_dpi');
87
        if (!empty($value)) {
88
            $this->pdf->img_dpi = (int) $value;
89
        }
90
    }
91
92
    /**
93
     * Export the given HTML to PDF, using a global template.
94
     *
95
     * @uses \export/table_pdf.tpl
96
     *
97
     * @param string     $content
98
     * @param bool|false $saveToFile
99
     * @param bool|false $returnHtml
100
     * @param bool       $addDefaultCss (bootstrap/default/base.css)
101
     * @param array
102
     *
103
     * @throws MpdfException
104
     *
105
     * @return string|null
106
     */
107
    public function html_to_pdf_with_template(
108
        $content,
109
        $saveToFile = false,
110
        $returnHtml = false,
111
        $addDefaultCss = false,
112
        $extraRows = []
113
    ) {
114
        if (empty($this->template)) {
115
            $tpl = new Template('', false, false, false, false, true, false);
116
        } else {
117
            $tpl = $this->template;
118
        }
119
120
        // Assignments
121
        $tpl->assign('pdf_content', $content);
122
123
        // Showing only the current teacher/admin instead the all teacher list name see BT#4080
124
        $teacher_list = null;
125
        if (isset($this->params['show_real_course_teachers']) &&
126
            $this->params['show_real_course_teachers']
127
        ) {
128
            if (isset($this->params['session_info']) &&
129
                !empty($this->params['session_info'])
130
            ) {
131
                $teacher_list = SessionManager::getCoachesByCourseSessionToString(
132
                    $this->params['session_info']['id'],
133
                    $this->params['course_info']['real_id']
134
                );
135
            } else {
136
                $teacher_list = CourseManager::getTeacherListFromCourseCodeToString(
137
                    $this->params['course_code']
138
                );
139
            }
140
        } else {
141
            $user_info = api_get_user_info();
142
            if ($this->params['show_teacher_as_myself']) {
143
                $teacher_list = $user_info['complete_name'];
144
            }
145
        }
146
147
        $tpl->assign('pdf_course', $this->params['course_code']);
148
        $tpl->assign('pdf_course_info', $this->params['course_info']);
149
        $tpl->assign('pdf_session_info', $this->params['session_info']);
150
        $tpl->assign('pdf_date', $this->params['pdf_date']);
151
        $tpl->assign('pdf_date_only', $this->params['pdf_date_only']);
152
        $tpl->assign('pdf_teachers', $teacher_list);
153
        $tpl->assign('pdf_title', $this->params['pdf_title']);
154
        $tpl->assign('pdf_description', $this->params['pdf_description']);
155
        $tpl->assign('pdf_student_info', $this->params['student_info']);
156
        $tpl->assign('show_grade_generated_date', $this->params['show_grade_generated_date']);
157
        $tpl->assign('add_signatures', $this->params['add_signatures']);
158
        $tpl->assign('extra_rows', $extraRows);
159
160
        // Getting template
161
        $tableTemplate = $tpl->get_template('export/table_pdf.tpl');
162
        $html = $tpl->fetch($tableTemplate);
163
        $html = api_utf8_encode($html);
164
165
        if ($returnHtml) {
166
            return $html;
167
        }
168
169
        $css = api_get_print_css();
170
171
        self::content_to_pdf(
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::content_to_pdf() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

171
        self::/** @scrutinizer ignore-call */ 
172
              content_to_pdf(
Loading history...
172
            $html,
173
            $css,
174
            $this->params['filename'],
175
            $this->params['course_code'],
176
            'D',
177
            $saveToFile,
178
            null,
179
            $returnHtml,
180
            $addDefaultCss
181
        );
182
    }
183
184
    /**
185
     * Converts HTML files to PDF.
186
     *
187
     * @param mixed  $html_file_array could be a html file path or an array
188
     *                                with paths example:
189
     *                                /var/www/myfile.html or array('/myfile.html','myotherfile.html') or
190
     *                                even an indexed array with both 'title' and 'path' indexes
191
     *                                for each element like
192
     *                                array(
193
     *                                0 => array('title'=>'Hello','path'=>'file.html'),
194
     *                                1 => array('title'=>'Bye','path'=>'file2.html')
195
     *                                );
196
     * @param string $pdf_name        pdf name
197
     * @param null   $course_code     (if you are using html that are located
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $course_code is correct as it would always require null to be passed?
Loading history...
198
     *                                in the document tool you must provide this)
199
     * @param bool   $print_title     add title
200
     * @param bool   $complete_style  show header and footer if true
201
     * @param bool   $addStyle
202
     * @param string $mainTitle
203
     * @param bool   $generateToFile  Optional. When it is TRUE, then the output file is move to app/cache
204
     *
205
     * @throws MpdfException
206
     *
207
     * @return false|null
208
     */
209
    public function html_to_pdf(
210
        $html_file_array,
211
        $pdf_name = '',
212
        $course_code = null,
213
        $print_title = false,
214
        $complete_style = true,
215
        $addStyle = true,
216
        $mainTitle = '',
217
        $generateToFile = false
218
    ) {
219
        if (empty($html_file_array)) {
220
            return false;
221
        }
222
223
        if (is_array($html_file_array)) {
224
            if (count($html_file_array) == 0) {
225
                return false;
226
            }
227
        } else {
228
            if (!file_exists($html_file_array)) {
229
                return false;
230
            }
231
            // Converting the string into an array
232
            $html_file_array = [$html_file_array];
233
        }
234
235
        if (!empty($course_code)) {
236
            $course_data = api_get_course_info($course_code);
237
        } else {
238
            $course_data = api_get_course_info();
239
        }
240
241
        // Clean styles and javascript document
242
        $clean_search = [
243
            '@<script[^>]*?>.*?</script>@si',
244
            '@<style[^>]*?>.*?</style>@si',
245
        ];
246
247
        // Formatting the pdf
248
        self::format_pdf($course_data, $complete_style);
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::format_pdf() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

248
        self::/** @scrutinizer ignore-call */ 
249
              format_pdf($course_data, $complete_style);
Loading history...
249
250
        $counter = 1;
251
        foreach ($html_file_array as $file) {
252
            // Add a page break per file
253
            $page_break = '<pagebreak>';
254
            if ($counter == count($html_file_array)) {
255
                $page_break = '';
256
            }
257
258
            // if the array provided contained subarrays with 'title' entry,
259
            // then print the title in the PDF
260
            if (is_array($file) && isset($file['title'])) {
261
                $html_title = $file['title'];
262
                $file = $file['path'];
263
            } else {
264
                //we suppose we've only been sent a file path
265
                $html_title = basename($file);
266
            }
267
268
            $counter++;
269
270
            if (empty($file) && !empty($html_title)) {
271
                // this is a chapter, print title & skip the rest
272
                if ($counter === 2 && !empty($mainTitle)) {
273
                    $this->pdf->WriteHTML(
274
                        '<html><body><h2 style="text-align: center">'.$mainTitle.'</h2></body></html>'
275
                    );
276
                }
277
                if ($print_title) {
278
                    $this->pdf->WriteHTML(
279
                        '<html><body><h3>'.$html_title.'</h3></body></html>'.$page_break
280
                    );
281
                }
282
                continue;
283
            } else {
284
                if ($counter === 2 && !empty($mainTitle)) {
285
                    $this->pdf->WriteHTML(
286
                        '<html><body><h2 style="text-align: center">'.$mainTitle.'</h2></body></html>'
287
                    );
288
                }
289
            }
290
291
            if (!file_exists($file)) {
292
                continue;
293
            }
294
295
            if ($addStyle) {
296
                $basicStyles = [
297
                    api_get_path(SYS_PATH).'web/assets/bootstrap/dist/css/bootstrap.min.css',
298
                    api_get_path(SYS_PATH).'web/css/base.css',
299
                    api_get_path(SYS_PATH).'web/css/themes/'.api_get_visual_theme().'/default.css',
300
                    api_get_print_css(false),
301
                ];
302
                foreach ($basicStyles as $style) {
303
                    if (file_exists($style)) {
304
                        $cssContent = file_get_contents($style);
305
                        try {
306
                            @$this->pdf->WriteHTML($cssContent, 1);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for WriteHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

306
                            /** @scrutinizer ignore-unhandled */ @$this->pdf->WriteHTML($cssContent, 1);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
307
                        } catch (MpdfException $e) {
308
                            error_log($e);
309
                        }
310
                    }
311
                }
312
            }
313
314
            // it's not a chapter but the file exists, print its title
315
            if ($print_title) {
316
                @$this->pdf->WriteHTML(
317
                    '<html><body><h3>'.$html_title.'</h3></body></html>'
318
                );
319
            }
320
321
            $file_info = pathinfo($file);
322
            $extension = $file_info['extension'];
323
324
            if (in_array($extension, ['html', 'htm'])) {
325
                $dirName = $file_info['dirname'];
326
                $filename = $file_info['basename'];
327
                $filename = str_replace('_', ' ', $filename);
328
329
                if ($extension === 'html') {
330
                    $filename = basename($filename, '.html');
331
                } elseif ($extension === 'htm') {
332
                    $filename = basename($filename, '.htm');
333
                }
334
335
                $document_html = @file_get_contents($file);
336
                $document_html = preg_replace($clean_search, '', $document_html);
337
338
                //absolute path for frames.css //TODO: necessary?
339
                $absolute_css_path = api_get_path(WEB_CODE_PATH).'css/'.api_get_setting('stylesheets').'/frames.css';
340
                $document_html = str_replace('href="./css/frames.css"', $absolute_css_path, $document_html);
341
342
                if (!empty($course_data['path'])) {
343
                    $document_html = str_replace('../', '', $document_html);
344
345
                    // Fix app/upload links convert web to system paths
346
                    $document_html = str_replace(
347
                        api_get_path(WEB_UPLOAD_PATH),
348
                        api_get_path(SYS_UPLOAD_PATH),
349
                        $document_html
350
                    );
351
                }
352
353
                $document_html = self::fixImagesPaths($document_html, $course_data, $dirName);
354
355
                // The library Mpdf expects UTF-8 encoded input data.
356
                api_set_encoding_html($document_html, 'UTF-8');
357
                // TODO: Maybe it is better idea the title to be passed through
358
                $title = api_get_title_html($document_html, 'UTF-8', 'UTF-8');
359
                // $_GET[] too, as it is done with file name.
360
                // At the moment the title is retrieved from the html document itself.
361
                if (empty($title)) {
362
                    $title = $filename; // Here file name is expected to contain ASCII symbols only.
363
                }
364
                if (!empty($document_html)) {
365
                    @$this->pdf->WriteHTML($document_html.$page_break);
366
                }
367
            } elseif (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
368
                // Images
369
                $image = Display::img($file);
370
                @$this->pdf->WriteHTML('<html><body>'.$image.'</body></html>'.$page_break);
371
            }
372
        }
373
        if (empty($pdf_name)) {
374
            $output_file = 'pdf_'.date('Y-m-d-his').'.pdf';
375
        } else {
376
            $pdf_name = api_replace_dangerous_char($pdf_name);
377
            $output_file = $pdf_name.'.pdf';
378
        }
379
        // F to save the pdf in a file
380
        if ($generateToFile) {
381
            @$this->pdf->Output(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for Output(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

381
            /** @scrutinizer ignore-unhandled */ @$this->pdf->Output(

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
382
                api_get_path(SYS_ARCHIVE_PATH).$output_file,
383
                'F'
384
            );
385
        } else {
386
            @$this->pdf->Output($output_file, 'D');
387
        }
388
389
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
390
    }
391
392
    /**
393
     * Converts a html string to PDF.
394
     *
395
     * @param string $document_html  valid html
396
     * @param string $css            CSS content of a CSS file
397
     * @param string $pdf_name       pdf name
398
     * @param string $course_code    course code
399
     *                               (if you are using html that are located in the document tool you must provide this)
400
     * @param string $outputMode     the MPDF output mode can be:
401
     * @param bool   $saveInFile
402
     * @param string $fileToSave
403
     * @param bool   $returnHtml
404
     * @param bool   $addDefaultCss
405
     * @param bool   $completeHeader
406
     *
407
     * @throws MpdfException
408
     *
409
     * 'I' (print on standard output),
410
     * 'D' (download file) (this is the default value),
411
     * 'F' (save to local file) or
412
     * 'S' (return as a string)
413
     *
414
     * @return string Web path
415
     */
416
    public function content_to_pdf(
417
        $document_html,
418
        $css = '',
419
        $pdf_name = '',
420
        $course_code = null,
421
        $outputMode = 'D',
422
        $saveInFile = false,
423
        $fileToSave = null,
424
        $returnHtml = false,
425
        $addDefaultCss = false,
426
        $completeHeader = true
427
    ) {
428
        $urlAppend = api_get_configuration_value('url_append');
429
430
        if (empty($document_html)) {
431
            return false;
432
        }
433
434
        // clean styles and javascript document
435
        $clean_search = [
436
            '@<script[^>]*?>.*?</script>@si',
437
            '@<style[^>]*?>.*?</style>@siU',
438
        ];
439
440
        // Formatting the pdf
441
        $course_data = api_get_course_info($course_code);
442
        self::format_pdf($course_data, $completeHeader);
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::format_pdf() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

442
        self::/** @scrutinizer ignore-call */ 
443
              format_pdf($course_data, $completeHeader);
Loading history...
443
        $document_html = preg_replace($clean_search, '', $document_html);
444
445
        //absolute path for frames.css //TODO: necessary?
446
        $absolute_css_path = api_get_path(WEB_CSS_PATH).api_get_setting('stylesheets').'/frames.css';
447
        $document_html = str_replace('href="./css/frames.css"', 'href="'.$absolute_css_path.'"', $document_html);
448
        $document_html = str_replace('../../', '', $document_html);
449
        $document_html = str_replace('../', '', $document_html);
450
        $document_html = str_replace(
451
            (empty($urlAppend) ? '' : $urlAppend.'/').'courses/'.$course_code.'/document/',
452
            '',
453
            $document_html
454
        );
455
456
        if (!empty($course_data['path'])) {
457
            $document_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document/';
458
459
            $doc = new DOMDocument();
460
            @$doc->loadHTML($document_html);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for loadHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

460
            /** @scrutinizer ignore-unhandled */ @$doc->loadHTML($document_html);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
461
462
            //Fixing only images @todo do the same thing with other elements
463
            $elements = $doc->getElementsByTagName('img');
464
            $protocol = api_get_protocol();
465
            $replaced = [];
466
            if (!empty($elements)) {
467
                foreach ($elements as $item) {
468
                    $old_src = $item->getAttribute('src');
469
470
                    if (in_array($old_src, $replaced)) {
471
                        continue;
472
                    }
473
474
                    if (strpos($old_src, $protocol) === false) {
475
                        if (strpos($old_src, '/main/default_course_document') === false) {
476
                            if (strpos($old_src, '/main/inc/lib/') === false &&
477
                                strpos($old_src, '/app/upload/') === false
478
                            ) {
479
                                $old_src_fixed = str_replace(
480
                                    api_get_path(REL_COURSE_PATH).$course_data['path'].'/document/',
481
                                    '',
482
                                    $old_src
483
                                );
484
                                $old_src_fixed = str_replace(
485
                                    'courses/'.$course_data['path'].'/document/',
486
                                    '',
487
                                    $old_src_fixed
488
                                );
489
                                $new_path = $document_path.$old_src_fixed;
490
                                $document_html = str_replace($old_src, $new_path, $document_html);
491
                                $replaced[] = $old_src;
492
                            }
493
                        }
494
                    }
495
                }
496
            }
497
        }
498
499
        // Use sys path to correct export images
500
        $document_html = str_replace(
501
            api_get_path(WEB_CODE_PATH).'img/',
502
            api_get_path(SYS_CODE_PATH).'img/',
503
            $document_html
504
        );
505
506
        $theme = api_get_visual_theme();
507
        $document_html = str_replace(
508
            api_get_path(WEB_CSS_PATH).'themes/'.$theme,
509
            api_get_path(SYS_PUBLIC_PATH).'css/themes/'.$theme,
510
            $document_html
511
        );
512
513
        $document_html = str_replace(api_get_path(WEB_UPLOAD_PATH), api_get_path(SYS_UPLOAD_PATH), $document_html);
514
        $document_html = str_replace(api_get_path(WEB_ARCHIVE_PATH), api_get_path(SYS_ARCHIVE_PATH), $document_html);
515
516
        // The library Mpdf expects UTF-8 encoded input data.
517
        api_set_encoding_html($document_html, 'UTF-8');
518
        // At the moment the title is retrieved from the html document itself.
519
        if ($returnHtml) {
520
            return "<style>$css</style>".$document_html;
521
        }
522
523
        if (!empty($css)) {
524
            try {
525
                @$this->pdf->WriteHTML($css, 1);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for WriteHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

525
                /** @scrutinizer ignore-unhandled */ @$this->pdf->WriteHTML($css, 1);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
526
            } catch (MpdfException $e) {
527
                error_log($e);
528
            }
529
        }
530
531
        $cssBootstrap = file_get_contents(api_get_path(SYS_PATH).'web/assets/bootstrap/dist/css/bootstrap.min.css');
532
        if ($addDefaultCss) {
533
            $cssContent = api_get_print_css();
534
            try {
535
                @$this->pdf->WriteHTML($cssBootstrap, 1);
536
                @$this->pdf->WriteHTML($cssContent, 1);
537
            } catch (MpdfException $e) {
538
                error_log($e);
539
            }
540
        }
541
542
        try {
543
            @$this->pdf->WriteHTML($document_html);
544
        } catch (MpdfException $e) {
545
            error_log($e);
546
        }
547
548
        if (empty($pdf_name)) {
549
            $output_file = 'pdf_'.date('Y-m-d-his').'.pdf';
550
        } else {
551
            $pdf_name = api_replace_dangerous_char($pdf_name);
552
            $output_file = $pdf_name.'.pdf';
553
        }
554
555
        if ($outputMode === 'F') {
556
            $output_file = api_get_path(SYS_ARCHIVE_PATH).$output_file;
557
        }
558
559
        if ($saveInFile) {
560
            $fileToSave = !empty($fileToSave) ? $fileToSave : api_get_path(SYS_ARCHIVE_PATH).uniqid();
561
            @$this->pdf->Output(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for Output(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

561
            /** @scrutinizer ignore-unhandled */ @$this->pdf->Output(

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
562
                $fileToSave,
563
                $outputMode
564
            ); // F to save the pdf in a file
565
        } else {
566
            @$this->pdf->Output(
567
                $output_file,
568
                $outputMode
569
            ); // F to save the pdf in a file
570
        }
571
572
        if ($outputMode != 'F') {
573
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
574
        }
575
576
        return $output_file;
577
    }
578
579
    /**
580
     * Gets the watermark from the platform or a course.
581
     *
582
     * @param   string  course code (optional)
583
     * @param   mixed   web path of the watermark image, false if there is nothing to return
584
     *
585
     * @return string
586
     */
587
    public static function get_watermark($course_code = null)
588
    {
589
        $web_path = false;
590
        $urlId = api_get_current_access_url_id();
591
        if (!empty($course_code) && api_get_setting('pdf_export_watermark_by_course') === 'true') {
592
            $course_info = api_get_course_info($course_code);
593
            // course path
594
            $store_path = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/'.$urlId.'_pdf_watermark.png';
595
            if (file_exists($store_path)) {
596
                $web_path = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/'.$urlId.'_pdf_watermark.png';
597
            }
598
        } else {
599
            // course path
600
            $store_path = api_get_path(SYS_CODE_PATH).'default_course_document/images/'.$urlId.'_pdf_watermark.png';
601
            if (file_exists($store_path)) {
602
                $web_path = api_get_path(WEB_CODE_PATH).'default_course_document/images/'.$urlId.'_pdf_watermark.png';
603
            }
604
        }
605
606
        return $web_path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $web_path could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
607
    }
608
609
    /**
610
     * Deletes the watermark from the Platform or Course.
611
     *
612
     * @param string $course_code course code (optional)
613
     * @param   mixed   web path of the watermark image, false if there is nothing to return
614
     *
615
     * @return bool
616
     */
617
    public static function delete_watermark($course_code = null)
618
    {
619
        $urlId = api_get_current_access_url_id();
620
        if (!empty($course_code) && api_get_setting('pdf_export_watermark_by_course') == 'true') {
621
            $course_info = api_get_course_info($course_code);
622
            // course path
623
            $store_path = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/'.$urlId.'_pdf_watermark.png';
624
        } else {
625
            // course path
626
            $store_path = api_get_path(SYS_CODE_PATH).'default_course_document/images/'.$urlId.'_pdf_watermark.png';
627
        }
628
        if (file_exists($store_path)) {
629
            unlink($store_path);
630
631
            return true;
632
        }
633
634
        return false;
635
    }
636
637
    /**
638
     * Uploads the pdf watermark in the main/default_course_document directory or in the course directory.
639
     *
640
     * @param string $filename    filename
641
     * @param string $source_file path of the file
642
     * @param string $course_code
643
     *
644
     * @return mixed web path of the file if sucess, false otherwise
645
     */
646
    public static function upload_watermark($filename, $source_file, $course_code = null)
647
    {
648
        $urlId = api_get_current_access_url_id();
649
        if (!empty($course_code) && api_get_setting('pdf_export_watermark_by_course') == 'true') {
650
            $course_info = api_get_course_info($course_code);
651
            $store_path = api_get_path(SYS_COURSE_PATH).$course_info['path']; // course path
652
            $web_path = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/pdf_watermark.png';
653
        } else {
654
            $store_path = api_get_path(SYS_CODE_PATH).'default_course_document/images'; // course path
655
            $web_path = api_get_path(WEB_CODE_PATH).'default_course_document/images/'.$urlId.'_pdf_watermark.png';
656
        }
657
        $course_image = $store_path.'/'.$urlId.'_pdf_watermark.png';
658
659
        if (file_exists($course_image)) {
660
            @unlink($course_image);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

660
            /** @scrutinizer ignore-unhandled */ @unlink($course_image);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
661
        }
662
        $my_image = new Image($source_file);
663
        $result = $my_image->send_image($course_image, -1, 'png');
664
        if ($result) {
665
            $result = $web_path;
666
        }
667
668
        return $result;
669
    }
670
671
    /**
672
     * Returns the default header.
673
     */
674
    public function get_header($course_code = null)
675
    {
676
        /*$header = api_get_setting('pdf_export_watermark_text');
677
    	if (!empty($course_code) && api_get_setting('pdf_export_watermark_by_course') == 'true') {
678
            $header = api_get_course_setting('pdf_export_watermark_text');
679
        }
680
        return $header;*/
681
    }
682
683
    /**
684
     * Sets the PDF footer.
685
     */
686
    public function set_footer()
687
    {
688
        $this->pdf->defaultfooterfontsize = 12; // in pts
689
        $this->pdf->defaultfooterfontstyle = 'B'; // blank, B, I, or BI
690
        $this->pdf->defaultfooterline = 1; // 1 to include line below header/above footer
691
692
        $view = new Template('', false, false, false, true, false, false);
693
        $template = $view->get_template('export/pdf_footer.tpl');
694
        $footerHTML = $view->fetch($template);
695
696
        $this->pdf->SetHTMLFooter($footerHTML, 'E'); //Even pages
697
        $this->pdf->SetHTMLFooter($footerHTML, 'O'); //Odd pages
698
    }
699
700
    public function setCertificateFooter()
701
    {
702
        $this->pdf->defaultfooterfontsize = 12; // in pts
703
        $this->pdf->defaultfooterfontstyle = 'B'; // blank, B, I, or BI
704
        $this->pdf->defaultfooterline = 1; // 1 to include line below header/above footer
705
706
        $view = new Template('', false, false, false, true, false, false);
707
        $template = $view->get_template('export/pdf_certificate_footer.tpl');
708
        $footerHTML = $view->fetch($template);
709
710
        $this->pdf->SetHTMLFooter($footerHTML, 'E'); //Even pages
711
        $this->pdf->SetHTMLFooter($footerHTML, 'O'); //Odd pages
712
    }
713
714
    /**
715
     * Sets the PDF header.
716
     *
717
     * @param array $courseInfo
718
     */
719
    public function set_header($courseInfo)
720
    {
721
        $this->pdf->defaultheaderfontsize = 10; // in pts
722
        $this->pdf->defaultheaderfontstyle = 'BI'; // blank, B, I, or BI
723
        $this->pdf->defaultheaderline = 1; // 1 to include line below header/above footer
724
725
        $userId = api_get_user_id();
726
        if (!empty($courseInfo['code'])) {
727
            $teacher_list = CourseManager::get_teacher_list_from_course_code($courseInfo['code']);
728
729
            $teachers = '';
730
            if (!empty($teacher_list)) {
731
                foreach ($teacher_list as $teacher) {
732
                    if ($teacher['user_id'] != $userId) {
733
                        continue;
734
                    }
735
736
                    // Do not show the teacher list see BT#4080 only the current teacher name
737
                    $teachers = api_get_person_name($teacher['firstname'], $teacher['lastname']);
738
                }
739
            }
740
741
            $organization = ChamiloApi::getPlatformLogo('', [], true, true);
742
            // Use custom logo image.
743
            $pdfLogo = api_get_setting('pdf_logo_header');
744
            if ($pdfLogo === 'true') {
745
                $visualTheme = api_get_visual_theme();
746
                $img = api_get_path(SYS_CSS_PATH).'themes/'.$visualTheme.'/images/pdf_logo_header.png';
747
                if (file_exists($img)) {
748
                    $organization = "<img src='$img'>";
749
                }
750
            }
751
752
            $view = new Template('', false, false, false, true, false, false);
753
            $view->assign('teacher_name', $teachers);
754
            $view->assign('organization', $organization);
755
            $template = $view->get_template('export/pdf_header.tpl');
756
            $headerHTML = $view->fetch($template);
757
758
            $this->pdf->SetHTMLHeader($headerHTML, 'E');
759
            $this->pdf->SetHTMLHeader($headerHTML, 'O');
760
        }
761
    }
762
763
    /**
764
     * @param string $header html content
765
     */
766
    public function set_custom_header($header)
767
    {
768
        $this->custom_header = $header;
769
    }
770
771
    /**
772
     * @param array $footer html content
773
     */
774
    public function set_custom_footer($footer)
775
    {
776
        $this->custom_footer = $footer;
777
    }
778
779
    /**
780
     * Pre-formats a PDF to the right size and, if not stated otherwise, with
781
     * header, footer and watermark (if any).
782
     *
783
     * @param array $courseInfo General course information (to fill headers)
784
     * @param bool  $complete   Whether we want headers, footers and watermark or not
785
     */
786
    public function format_pdf($courseInfo, $complete = true)
787
    {
788
        $courseCode = null;
789
        if (!empty($courseInfo)) {
790
            $courseCode = $courseInfo['code'];
791
        }
792
793
        /*$pdf->SetAuthor('Documents Chamilo');
794
        $pdf->SetTitle('title');
795
        $pdf->SetSubject('Exported from Chamilo Documents');
796
        $pdf->SetKeywords('Chamilo Documents');
797
        */
798
        // TODO: To be read from the html document.
799
        $this->pdf->directionality = api_get_text_direction();
800
        // Use different Odd/Even headers and footers and mirror margins
801
        $this->pdf->mirrorMargins = 1;
802
803
        // Add decoration only if not stated otherwise
804
        if ($complete) {
805
            // Adding watermark
806
            if (api_get_setting('pdf_export_watermark_enable') === 'true') {
807
                $watermark_file = self::get_watermark($courseCode);
808
                if ($watermark_file) {
809
                    //http://mpdf1.com/manual/index.php?tid=269&searchstring=watermark
810
                    $this->pdf->SetWatermarkImage($watermark_file);
811
                    $this->pdf->showWatermarkImage = true;
812
                } else {
813
                    $watermark_file = self::get_watermark();
814
815
                    if ($watermark_file) {
816
                        $this->pdf->SetWatermarkImage($watermark_file);
817
                        $this->pdf->showWatermarkImage = true;
818
                    }
819
                }
820
821
                $watermark_text = api_get_setting('pdf_export_watermark_text');
822
                if ($courseCode && 'true' === api_get_setting('pdf_export_watermark_by_course')) {
823
                    $courseWaterMark = api_get_course_setting('pdf_export_watermark_text');
824
                    if (!empty($courseWaterMark) && -1 != $courseWaterMark) {
825
                        $watermark_text = $courseWaterMark;
826
                    }
827
                }
828
829
                if (!empty($watermark_text)) {
830
                    $this->pdf->SetWatermarkText(
831
                        UtfString::strcode2utf($watermark_text),
832
                        0.1
833
                    );
834
                    $this->pdf->showWatermarkText = true;
835
                }
836
            }
837
838
            if (empty($this->custom_header)) {
839
                self::set_header($courseInfo);
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::set_header() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

839
                self::/** @scrutinizer ignore-call */ 
840
                      set_header($courseInfo);
Loading history...
840
            } else {
841
                $this->pdf->SetHTMLHeader($this->custom_header, 'E');
842
                $this->pdf->SetHTMLHeader($this->custom_header, 'O');
843
            }
844
845
            if (empty($this->custom_footer)) {
846
                self::set_footer();
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::set_footer() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

846
                self::/** @scrutinizer ignore-call */ 
847
                      set_footer();
Loading history...
847
            } else {
848
                $this->pdf->SetHTMLFooter($this->custom_footer);
849
            }
850
        }
851
    }
852
853
    /**
854
     * Generate a PDF file from $html in SYS_APP_PATH.
855
     *
856
     * @param string $html     PDF content
857
     * @param string $fileName File name
858
     * @param string $dest     Optional. Directory to move file
859
     *
860
     * @return string The PDF path
861
     */
862
    public function exportFromHtmlToFile($html, $fileName, $dest = null)
863
    {
864
        $this->template = $this->template ?: new Template('', false, false, false, false, false, false);
865
866
        $pdfPath = self::content_to_pdf(
0 ignored issues
show
Bug Best Practice introduced by
The method PDF::content_to_pdf() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

866
        /** @scrutinizer ignore-call */ 
867
        $pdfPath = self::content_to_pdf(
Loading history...
867
            $html,
868
            api_get_print_css(),
869
            $fileName,
870
            $this->params['course_code'],
871
            'F'
872
        );
873
874
        if (!$dest) {
875
            return $pdfPath;
876
        }
877
878
        move($pdfPath, $dest);
879
880
        return $dest.basename($pdfPath);
881
    }
882
883
    /**
884
     * Create a PDF and save it into the documents area.
885
     *
886
     * @param string $htmlContent HTML Content
887
     * @param string $fileName    The file name
888
     * @param int    $courseId    The course ID
889
     * @param int    $sessionId   Optional. The session ID
890
     */
891
    public function exportFromHtmlToDocumentsArea(
892
        $htmlContent,
893
        $fileName,
894
        $courseId,
895
        $sessionId = 0
896
    ) {
897
        $userId = api_get_user_id();
898
        $courseInfo = api_get_course_info_by_id($courseId);
899
        $courseDirectory = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document/';
900
901
        $docPath = $this->exportFromHtmlToFile(
902
            $htmlContent,
903
            $fileName,
904
            $courseDirectory
905
        );
906
907
        $docId = add_document(
908
            $courseInfo,
909
            str_replace($courseDirectory, '/', $docPath),
910
            'file',
911
            filesize($docPath),
912
            $fileName,
913
            null,
914
            false,
915
            true,
916
            null,
917
            $sessionId,
918
            $userId
919
        );
920
921
        api_item_property_update(
922
            $courseInfo,
923
            TOOL_DOCUMENT,
924
            $docId,
925
            'DocumentAdded',
926
            $userId
927
        );
928
929
        Display::addFlash(Display::return_message(get_lang('ItemAdded')));
930
    }
931
932
    /**
933
     * @param string $theme
934
     * @param bool   $fullPage
935
     *
936
     * @throws MpdfException
937
     */
938
    public function setBackground($theme, $fullPage = false)
939
    {
940
        $themeName = empty($theme) ? api_get_visual_theme() : $theme;
941
        $themeDir = Template::getThemeDir($themeName);
942
        $customLetterhead = $themeDir.'images/letterhead.png';
943
        $urlPathLetterhead = api_get_path(SYS_CSS_PATH).$customLetterhead;
944
945
        if (file_exists($urlPathLetterhead)) {
946
            $urlWebLetterhead = 'url('.api_get_path(WEB_CSS_PATH).$customLetterhead.')';
947
        } else {
948
            $urlWebLetterhead = 'url('.api_get_path(WEB_CSS_PATH).'themes/chamilo/images/letterhead.png)';
949
        }
950
951
        if ($fullPage) {
952
            $this->pdf->SetDisplayMode('fullpage');
953
            $this->pdf->SetDefaultBodyCSS('background', $urlWebLetterhead);
954
            $this->pdf->SetDefaultBodyCSS('background-image-resize', '6');
955
        }
956
    }
957
958
    /**
959
     * Fix images source paths to allow export to pdf.
960
     *
961
     * @param string $documentHtml
962
     * @param string $dirName
963
     *
964
     * @return string
965
     */
966
    private static function fixImagesPaths($documentHtml, array $courseInfo, $dirName = '')
967
    {
968
        $documentHtml = '<?xml encoding="utf-8" ?>'.$documentHtml;
969
        $doc = new DOMDocument();
970
        @$doc->loadHTML($documentHtml);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for loadHTML(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

970
        /** @scrutinizer ignore-unhandled */ @$doc->loadHTML($documentHtml);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
971
972
        $elements = $doc->getElementsByTagName('img');
973
974
        if (empty($elements)) {
975
            return $doc->saveHTML();
976
        }
977
978
        $protocol = api_get_protocol();
979
        $sysCodePath = api_get_path(SYS_CODE_PATH);
980
        $sysCoursePath = api_get_path(SYS_COURSE_PATH);
981
        $sysUploadPath = api_get_path(SYS_UPLOAD_PATH);
982
983
        $documentPath = $courseInfo ? $sysCoursePath.$courseInfo['path'].'/document/' : '';
984
985
        $notFoundImagePath = Display::return_icon(
986
            'closed-circle.png',
987
            get_lang('FileNotFound'),
988
            [],
989
            ICON_SIZE_TINY,
990
            false,
991
            true
992
        );
993
994
        /** @var DOMElement $element */
995
        foreach ($elements as $element) {
996
            $src = $element->getAttribute('src');
997
            $src = trim($src);
998
999
            if (api_filename_has_blacklisted_stream_wrapper($src)) {
1000
                $element->setAttribute('src', $notFoundImagePath);
1001
                continue;
1002
            }
1003
1004
            if (strpos($src, $protocol) !== false) {
1005
                continue;
1006
            }
1007
1008
            // It's a reference to a file in the system, do not change it
1009
            if (file_exists($src)) {
1010
                continue;
1011
            }
1012
1013
            if (strpos($src, '/main/default_course_document') === 0) {
1014
                $element->setAttribute(
1015
                    'src',
1016
                    str_replace('/main/default_course_document', $sysCodePath.'default_course_document', $src)
1017
                );
1018
                continue;
1019
            }
1020
1021
            if (strpos($src, '/main/img') === 0) {
1022
                $element->setAttribute(
1023
                    'src',
1024
                    str_replace('/main/img/', $sysCodePath.'img/', $src)
1025
                );
1026
                continue;
1027
            }
1028
1029
            if (strpos($src, '/app/upload/') === 0) {
1030
                $element->setAttribute(
1031
                    'src',
1032
                    str_replace('/app/upload/', $sysUploadPath, $src)
1033
                );
1034
                continue;
1035
            }
1036
1037
            if (empty($courseInfo)) {
1038
                continue;
1039
            }
1040
1041
            if (api_get_path(REL_PATH) != '/') {
1042
                $oldSrcFixed = str_replace(
1043
                    api_get_path(REL_PATH).'courses/'.$courseInfo['path'].'/document/',
1044
                    '',
1045
                    $src
1046
                );
1047
1048
                // Try with the dirname if exists
1049
                if ($oldSrcFixed == $src) {
1050
                    if (file_exists($dirName.'/'.$src)) {
1051
                        $documentPath = '';
1052
                        $oldSrcFixed = $dirName.'/'.$src;
1053
                    }
1054
                }
1055
            } else {
1056
                if (strpos($src, 'courses/'.$courseInfo['path'].'/document/') !== false) {
1057
                    $oldSrcFixed = str_replace('courses/'.$courseInfo['path'].'/document/', '', $src);
1058
                } else {
1059
                    // Try with the dirname if exists
1060
                    $documentPath = '';
1061
                    if (file_exists($dirName.'/'.$src)) {
1062
                        $oldSrcFixed = $dirName.'/'.$src;
1063
                    } else {
1064
                        $oldSrcFixed = $src;
1065
                    }
1066
                }
1067
            }
1068
1069
            $element->setAttribute('src', $documentPath.$oldSrcFixed);
1070
        }
1071
1072
        return $doc->saveHTML();
1073
    }
1074
}
1075