MDL-67547 dataformat_pdf: method to convert images to supported format.
[moodle.git] / dataformat / pdf / classes / writer.php
CommitLineData
5b787899
SR
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
25namespace dataformat_pdf;
26
27defined('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 */
36class 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
1de3b819
PH
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
5b787899
SR
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();
94 }
95
118a1094
PH
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
233a51ad
PH
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
118a1094
PH
122 /**
123 * Write a single record
124 *
125 * @param array $record
126 * @param int $rownum
127 */
5b787899
SR
128 public function write_record($record, $rownum) {
129 $rowheight = 0;
130
118a1094 131 $record = $this->format_record($record);
5b787899 132 foreach ($record as $cell) {
233a51ad
PH
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 $pdf2->AddPage('L');
139 $pdf2->writeHTMLCell($this->colwidth, 0, '', '', $cell, 1, 1, false, true, 'L');
140 $rowheight = max($rowheight, $pdf2->getY() - $pdf2->getMargins()['top']);
141 $pdf2->rollbackTransaction();
5b787899
SR
142 }
143
144 $margins = $this->pdf->getMargins();
145 if ($this->pdf->GetY() + $rowheight + $margins['bottom'] > $this->pdf->getPageHeight()) {
146 $this->pdf->AddPage('L');
147 $this->print_heading();
148 }
149
b3f4d77e
JP
150 // Get the last key for this record.
151 end($record);
152 $lastkey = key($record);
153
154 // Reset the record pointer.
155 reset($record);
156
157 // Loop through each element.
158 foreach ($record as $key => $cell) {
159 // Determine whether we're at the last element of the record.
160 $nextposition = ($lastkey === $key) ? 1 : 0;
161 // Write the element.
118a1094 162 $this->pdf->writeHTMLCell($this->colwidth, $rowheight, '', '', $cell, 1, $nextposition, false, true, 'L');
5b787899
SR
163 }
164 }
165
166 public function close_output() {
167 $filename = $this->filename . $this->get_extension();
168
169 $this->pdf->Output($filename, 'D');
170 }
171
1de3b819
PH
172 /**
173 * Write data to disk
174 *
175 * @return bool
176 */
177 public function close_output_to_file(): bool {
178 $this->pdf->Output($this->filepath, 'F');
179
180 return true;
181 }
182
5b787899
SR
183 /**
184 * Prints the heading row.
185 */
186 private function print_heading() {
187 $fontfamily = $this->pdf->getFontFamily();
188 $fontstyle = $this->pdf->getFontStyle();
189 $this->pdf->SetFont($fontfamily, 'B');
190 $rowheight = 0;
191 foreach ($this->columns as $columns) {
192 $rowheight = max($rowheight, $this->pdf->getStringHeight($this->colwidth, $columns, false, true, '', 1));
193 }
194
195 $total = count($this->columns);
196 $counter = 1;
197 foreach ($this->columns as $columns) {
198 $nextposition = ($counter == $total) ? 1 : 0;
199 $this->pdf->Multicell($this->colwidth, $rowheight, $columns, 1, 'C', true, $nextposition);
200 $counter++;
201 }
202
203 $this->pdf->SetFont($fontfamily, $fontstyle);
204 }
205}