See Release Notes
Long Term Support Release
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body