Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * pdf data format writer
19
 *
20
 * @package    dataformat_pdf
21
 * @copyright  2019 Shamim Rezaie <shamim@moodle.com>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace dataformat_pdf;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
/**
30
 * pdf data format writer
31
 *
32
 * @package    dataformat_pdf
33
 * @copyright  2019 Shamim Rezaie <shamim@moodle.com>
34
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 */
36
class writer extends \core\dataformat\base {
37
 
38
    public $mimetype = "application/pdf";
39
 
40
    public $extension = ".pdf";
41
 
42
    /**
43
     * @var \pdf The pdf object that is used to generate the pdf file.
44
     */
45
    protected $pdf;
46
 
47
    /**
48
     * @var float Each column's width in the current sheet.
49
     */
50
    protected $colwidth;
51
 
52
    /**
53
     * @var string[] Title of columns in the current sheet.
54
     */
55
    protected $columns;
56
 
57
    /**
58
     * writer constructor.
59
     */
60
    public function __construct() {
61
        global $CFG;
62
        require_once($CFG->libdir . '/pdflib.php');
63
 
64
        $this->pdf = new \pdf();
65
        $this->pdf->setPrintHeader(false);
66
        $this->pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
67
 
68
        // Set background color for headings.
69
        $this->pdf->SetFillColor(238, 238, 238);
70
    }
71
 
72
    public function send_http_headers() {
73
    }
74
 
75
    /**
76
     * Start output to file, note that the actual writing of the file is done in {@see close_output_to_file()}
77
     */
78
    public function start_output_to_file(): void {
79
        $this->start_output();
80
    }
81
 
82
    public function start_output() {
83
        $this->pdf->AddPage('L');
84
    }
85
 
86
    public function start_sheet($columns) {
87
        $margins = $this->pdf->getMargins();
88
        $pagewidth = $this->pdf->getPageWidth() - $margins['left'] - $margins['right'];
89
 
90
        $this->colwidth = $pagewidth / count($columns);
91
        $this->columns = $columns;
92
 
93
        $this->print_heading($this->pdf);
94
    }
95
 
96
    /**
97
     * Method to define whether the dataformat supports export of HTML
98
     *
99
     * @return bool
100
     */
101
    public function supports_html(): bool {
102
        return true;
103
    }
104
 
105
    /**
106
     * When exporting images, we need to return their Base64 encoded content. Otherwise TCPDF will create a HTTP
107
     * request for them, which will lead to the login page (i.e. not the image it expects) and throw an exception
108
     *
109
     * Note: ideally we would copy the file to a temp location and return it's path, but a bug in TCPDF currently
110
     * prevents that
111
     *
112
     * @param \stored_file $file
113
     * @return string|null
114
     */
115
    protected function export_html_image_source(\stored_file $file): ?string {
116
        // Set upper dimensions for embedded images.
117
        $resizedimage = $file->resize_image(400, 300);
118
 
119
        return '@' . base64_encode($resizedimage);
120
    }
121
 
122
    /**
123
     * Write a single record
124
     *
125
     * @param array $record
126
     * @param int $rownum
127
     */
128
    public function write_record($record, $rownum) {
129
        $rowheight = 0;
130
 
131
        $record = $this->format_record($record);
132
        foreach ($record as $cell) {
133
            // We need to calculate the row height (accounting for any content). Unfortunately TCPDF doesn't provide an easy
134
            // method to do that, so we create a second PDF inside a transaction, add cell content and use the largest cell by
135
            // height. Solution similar to that at https://stackoverflow.com/a/1943096.
136
            $pdf2 = clone $this->pdf;
137
            $pdf2->startTransaction();
138
            $numpages = $pdf2->getNumPages();
139
            $pdf2->AddPage('L');
140
            $this->print_heading($pdf2);
141
            $pdf2->writeHTMLCell($this->colwidth, 0, '', '', $cell, 1, 1, false, true, 'L');
142
            $pagesadded = $pdf2->getNumPages() - $numpages;
143
            $margins = $pdf2->getMargins();
144
            $pageheight = $pdf2->getPageHeight() - $margins['top'] - $margins['bottom'];
145
            $cellheight = ($pagesadded - 1) * $pageheight + $pdf2->getY() - $margins['top'] - $this->get_heading_height();
146
            $rowheight = max($rowheight, $cellheight);
147
            $pdf2->rollbackTransaction();
148
        }
149
 
150
        $margins = $this->pdf->getMargins();
151
        if ($this->pdf->getNumPages() > 1 &&
152
                ($this->pdf->GetY() + $rowheight + $margins['bottom'] > $this->pdf->getPageHeight())) {
153
            $this->pdf->AddPage('L');
154
            $this->print_heading($this->pdf);
155
        }
156
 
157
        // Get the last key for this record.
158
        end($record);
159
        $lastkey = key($record);
160
 
161
        // Reset the record pointer.
162
        reset($record);
163
 
164
        // Loop through each element.
165
        foreach ($record as $key => $cell) {
166
            // Determine whether we're at the last element of the record.
167
            $nextposition = ($lastkey === $key) ? 1 : 0;
168
            // Write the element.
169
            $this->pdf->writeHTMLCell($this->colwidth, $rowheight, '', '', $cell, 1, $nextposition, false, true, 'L');
170
        }
171
    }
172
 
173
    public function close_output() {
174
        $filename = $this->filename . $this->get_extension();
175
 
176
        $this->pdf->Output($filename, 'D');
177
    }
178
 
179
    /**
180
     * Write data to disk
181
     *
182
     * @return bool
183
     */
184
    public function close_output_to_file(): bool {
185
        $this->pdf->Output($this->filepath, 'F');
186
 
187
        return true;
188
    }
189
 
190
    /**
191
     * Prints the heading row for a given PDF.
192
     *
193
     * @param \pdf $pdf A pdf to print headings in
194
     */
195
    private function print_heading(\pdf $pdf) {
196
        $fontfamily = $pdf->getFontFamily();
197
        $fontstyle = $pdf->getFontStyle();
198
        $pdf->SetFont($fontfamily, 'B');
199
 
200
        $total = count($this->columns);
201
        $counter = 1;
202
        foreach ($this->columns as $columns) {
203
            $nextposition = ($counter == $total) ? 1 : 0;
204
            $pdf->Multicell($this->colwidth, $this->get_heading_height(), $columns, 1, 'C', true, $nextposition);
205
            $counter++;
206
        }
207
 
208
        $pdf->SetFont($fontfamily, $fontstyle);
209
    }
210
 
211
    /**
212
     * Returns the heading height.
213
     *
214
     * @return int
215
     */
216
    private function get_heading_height() {
217
        $height = 0;
218
        foreach ($this->columns as $columns) {
219
            $height = max($height, $this->pdf->getStringHeight($this->colwidth, $columns, false, true, '', 1));
220
        }
221
        return $height;
222
    }
223
}