Rev 1 | AutorÃa | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** Library code for manipulating PDFs** @package assignfeedback_editpdf* @copyright 2012 Davo Smith* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/namespace assignfeedback_editpdf;use setasign\Fpdi\Tcpdf\Fpdi;defined('MOODLE_INTERNAL') || die();global $CFG;require_once($CFG->libdir.'/pdflib.php');require_once($CFG->dirroot.'/mod/assign/feedback/editpdf/fpdi/src/autoload.php');/*** Library code for manipulating PDFs** @package assignfeedback_editpdf* @copyright 2012 Davo Smith* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class pdf extends Fpdi {/** @var int the number of the current page in the PDF being processed */protected $currentpage = 0;/** @var int the total number of pages in the PDF being processed */protected $pagecount = 0;/** @var float used to scale the pixel position of annotations (in the database) to the position in the final PDF */protected $scale = 0.0;/** @var string the path in which to store generated page images */protected $imagefolder = null;/** @var string the path to the PDF currently being processed */protected $filename = null;/** @var string the fontname used when the PDF being processed */protected $fontname = null;/** No errors */const GSPATH_OK = 'ok';/** Not set */const GSPATH_EMPTY = 'empty';/** Does not exist */const GSPATH_DOESNOTEXIST = 'doesnotexist';/** Is a dir */const GSPATH_ISDIR = 'isdir';/** Not executable */const GSPATH_NOTEXECUTABLE = 'notexecutable';/** Test file missing */const GSPATH_NOTESTFILE = 'notestfile';/** Any other error */const GSPATH_ERROR = 'error';/** Min. width an annotation should have */const MIN_ANNOTATION_WIDTH = 5;/** Min. height an annotation should have */const MIN_ANNOTATION_HEIGHT = 5;/** Blank PDF file used during error. */const BLANK_PDF = '/mod/assign/feedback/editpdf/fixtures/blank.pdf';/** Page image file name prefix*/const IMAGE_PAGE = 'image_page';/*** Get the name of the font to use in generated PDF files.* If $CFG->pdfexportfont is set - use it, otherwise use "freesans" as this* open licensed font has wide support for different language charsets.** @return string*/private function get_export_font_name() {$fontname = 'freesans';if (!empty($this->fontname)) {$fontname = $this->fontname;}return $fontname;}/*** Set font name.** @param string $fontname Font name which is* @return void*/public function set_export_font_name($fontname): void {$this->fontname = $fontname;}/*** Combine the given PDF files into a single PDF. Optionally add a coversheet and coversheet fields.* @param string[] $pdflist the filenames of the files to combine* @param string $outfilename the filename to write to* @return int the number of pages in the combined PDF*/public function combine_pdfs($pdflist, $outfilename) {raise_memory_limit(MEMORY_EXTRA);$olddebug = error_reporting(0);$this->setPageUnit('pt');$this->setPrintHeader(false);$this->setPrintFooter(false);$this->scale = 72.0 / 100.0;// Use font supporting the widest range of characters.$this->SetFont($this->get_export_font_name(), '', 16.0 * $this->scale, '', true);$this->SetTextColor(0, 0, 0);$totalpagecount = 0;foreach ($pdflist as $file) {$pagecount = $this->setSourceFile($file);$totalpagecount += $pagecount;for ($i = 1; $i<=$pagecount; $i++) {$this->create_page_from_source($i);}}$this->save_pdf($outfilename);error_reporting($olddebug);return $totalpagecount;}/*** The number of the current page in the PDF being processed* @return int*/public function current_page() {return $this->currentpage;}/*** The total number of pages in the PDF being processed* @return int*/public function page_count() {return $this->pagecount;}/*** Load the specified PDF and set the initial output configuration* Used when processing comments and outputting a new PDF* @param string $filename the path to the PDF to load* @return int the number of pages in the PDF*/public function load_pdf($filename) {raise_memory_limit(MEMORY_EXTRA);$olddebug = error_reporting(0);$this->setPageUnit('pt');$this->scale = 72.0 / 100.0;$this->SetFont($this->get_export_font_name(), '', 16.0 * $this->scale, '', true);$this->SetFillColor(255, 255, 176);$this->SetDrawColor(0, 0, 0);$this->SetLineWidth(1.0 * $this->scale);$this->SetTextColor(0, 0, 0);$this->setPrintHeader(false);$this->setPrintFooter(false);$this->pagecount = $this->setSourceFile($filename);$this->filename = $filename;error_reporting($olddebug);return $this->pagecount;}/*** Sets the name of the PDF to process, but only loads the file if the* pagecount is zero (in order to count the number of pages)* Used when generating page images (but not a new PDF)* @param string $filename the path to the PDF to process* @param int $pagecount optional the number of pages in the PDF, if known* @return int the number of pages in the PDF*/public function set_pdf($filename, $pagecount = 0) {if ($pagecount == 0) {return $this->load_pdf($filename);} else {$this->filename = $filename;$this->pagecount = $pagecount;return $pagecount;}}/*** Copy the next page from the source file and set it as the current page* @return bool true if successful*/public function copy_page() {if (!$this->filename) {return false;}if ($this->currentpage>=$this->pagecount) {return false;}$this->currentpage++;$this->create_page_from_source($this->currentpage);return true;}/*** Create a page from a source PDF.** @param int $pageno*/protected function create_page_from_source($pageno) {// Get the size (and deduce the orientation) of the next page.$template = $this->importPage($pageno);$size = $this->getTemplateSize($template);// Create a page of the required size / orientation.$this->AddPage($size['orientation'], array($size['width'], $size['height']));// Prevent new page creation when comments are at the bottom of a page.$this->setPageOrientation($size['orientation'], false, 0);// Fill in the page with the original contents from the student.$this->useTemplate($template);}/*** Copy all the remaining pages in the file*/public function copy_remaining_pages() {$morepages = true;while ($morepages) {$morepages = $this->copy_page();}}/*** Append all comments to the end of the document.** @param array $allcomments All comments, indexed by page number (starting at 0).* @return array|bool An array of links to comments, or false.*/public function append_comments($allcomments) {if (!$this->filename) {return false;}$this->SetFontSize(12 * $this->scale);$this->SetMargins(100 * $this->scale, 120 * $this->scale, -1, true);$this->SetAutoPageBreak(true, 100 * $this->scale);$this->setHeaderFont(array($this->get_export_font_name(), '', 24 * $this->scale, '', true));$this->setHeaderMargin(24 * $this->scale);$this->setHeaderData('', 0, '', get_string('commentindex', 'assignfeedback_editpdf'));// Add a new page to the document with an appropriate header.$this->setPrintHeader(true);$this->AddPage();// Add the comments.$commentlinks = array();foreach ($allcomments as $pageno => $comments) {foreach ($comments as $index => $comment) {// Create a link to the current location, which will be added to the marker.$commentlink = $this->AddLink();$this->SetLink($commentlink, -1);$commentlinks[$pageno][$index] = $commentlink;// Also create a link back to the marker, which will be added here.$markerlink = $this->AddLink();$this->SetLink($markerlink, $comment->y * $this->scale, $pageno + 1);$label = get_string('commentlabel', 'assignfeedback_editpdf', array('pnum' => $pageno + 1, 'cnum' => $index + 1));$this->Cell(50 * $this->scale, 0, $label, 0, 0, '', false, $markerlink);$this->MultiCell(0, 0, $comment->rawtext, 0, 'L');$this->Ln(12 * $this->scale);}// Add an extra line break between pages.$this->Ln(12 * $this->scale);}return $commentlinks;}/*** Add a comment marker to the specified page.** @param int $pageno The page number to add markers to (starting at 0).* @param int $index The comment index.* @param int $x The x-coordinate of the marker (in pixels).* @param int $y The y-coordinate of the marker (in pixels).* @param int $link The link identifier pointing to the full comment text.* @param string $colour The fill colour of the marker (red, yellow, green, blue, white, clear).* @return bool Success status.*/public function add_comment_marker($pageno, $index, $x, $y, $link, $colour = 'yellow') {if (!$this->filename) {return false;}$fill = '';$fillopacity = 0.9;switch ($colour) {case 'red':$fill = 'rgb(249, 181, 179)';break;case 'green':$fill = 'rgb(214, 234, 178)';break;case 'blue':$fill = 'rgb(203, 217, 237)';break;case 'white':$fill = 'rgb(255, 255, 255)';break;case 'clear':$fillopacity = 0;break;default: /* Yellow */$fill = 'rgb(255, 236, 174)';}$marker = '@<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.5 -0.5 12 12" preserveAspectRatio="xMinYMin meet">' .'<path d="M11 0H1C.4 0 0 .4 0 1v6c0 .6.4 1 1 1h1v4l4-4h5c.6 0 1-.4 1-1V1c0-.6-.4-1-1-1z" fill="' . $fill . '" ' .'fill-opacity="' . $fillopacity . '" stroke="rgb(153, 153, 153)" stroke-width="0.5"/></svg>';$label = get_string('commentlabel', 'assignfeedback_editpdf', array('pnum' => $pageno + 1, 'cnum' => $index + 1));$x *= $this->scale;$y *= $this->scale;$size = 24 * $this->scale;$this->SetDrawColor(51, 51, 51);$this->SetFontSize(10 * $this->scale);$this->setPage($pageno + 1);// Add the marker image.$this->ImageSVG($marker, $x - 0.5, $y - 0.5, $size, $size, $link);// Add the label.$this->MultiCell($size * 0.95, 0, $label, 0, 'C', false, 1, $x, $y, true, 0, false, true, $size * 0.60, 'M', true);return true;}/*** Add a comment to the current page* @param string $text the text of the comment* @param int $x the x-coordinate of the comment (in pixels)* @param int $y the y-coordinate of the comment (in pixels)* @param int $width the width of the comment (in pixels)* @param string $colour optional the background colour of the comment (red, yellow, green, blue, white, clear)* @return bool true if successful (always)*/public function add_comment($text, $x, $y, $width, $colour = 'yellow') {if (!$this->filename) {return false;}$this->SetDrawColor(51, 51, 51);switch ($colour) {case 'red':$this->SetFillColor(249, 181, 179);break;case 'green':$this->SetFillColor(214, 234, 178);break;case 'blue':$this->SetFillColor(203, 217, 237);break;case 'white':$this->SetFillColor(255, 255, 255);break;default: /* Yellow */$this->SetFillColor(255, 236, 174);break;}$x *= $this->scale;$y *= $this->scale;$width *= $this->scale;$text = str_replace('<', '<', $text);$text = str_replace('>', '>', $text);// Draw the text with a border, but no background colour (using a background colour would cause the fill to// appear behind any existing content on the page, hence the extra filled rectangle drawn below).$this->MultiCell($width, 1.0, $text, 0, 'L', 0, 4, $x, $y); /* width, height, text, border, justify, fill, ln, x, y */if ($colour != 'clear') {$newy = $this->GetY();// Now we know the final size of the comment, draw a rectangle with the background colour.$this->Rect($x, $y, $width, $newy - $y, 'DF');// Re-draw the text over the top of the background rectangle.$this->MultiCell($width, 1.0, $text, 0, 'L', 0, 4, $x, $y); /* width, height, text, border, justify, fill, ln, x, y */}return true;}/*** Add an annotation to the current page* @param int $sx starting x-coordinate (in pixels)* @param int $sy starting y-coordinate (in pixels)* @param int $ex ending x-coordinate (in pixels)* @param int $ey ending y-coordinate (in pixels)* @param string $colour optional the colour of the annotation (red, yellow, green, blue, white, black)* @param string $type optional the type of annotation (line, oval, rectangle, highlight, pen, stamp)* @param int[]|string $path optional for 'pen' annotations this is an array of x and y coordinates for* the line, for 'stamp' annotations it is the name of the stamp file (without the path)* @param string $imagefolder - Folder containing stamp images.* @return bool true if successful (always)*/public function add_annotation($sx, $sy, $ex, $ey, $colour, $type, $path, $imagefolder) {global $CFG;if (!$this->filename) {return false;}switch ($colour) {case 'yellow':$colourarray = array(255, 207, 53);break;case 'green':$colourarray = array(153, 202, 62);break;case 'blue':$colourarray = array(125, 159, 211);break;case 'white':$colourarray = array(255, 255, 255);break;case 'black':$colourarray = array(51, 51, 51);break;default: /* Red */$colour = 'red';$colourarray = array(239, 69, 64);break;}$this->SetDrawColorArray($colourarray);$sx *= $this->scale;$sy *= $this->scale;$ex *= $this->scale;$ey *= $this->scale;$this->SetLineWidth(3.0 * $this->scale);switch ($type) {case 'oval':$rx = abs($sx - $ex) / 2;$ry = abs($sy - $ey) / 2;$sx = min($sx, $ex) + $rx;$sy = min($sy, $ey) + $ry;// $rx and $ry should be >= min width and heightif ($rx < self::MIN_ANNOTATION_WIDTH) {$rx = self::MIN_ANNOTATION_WIDTH;}if ($ry < self::MIN_ANNOTATION_HEIGHT) {$ry = self::MIN_ANNOTATION_HEIGHT;}$this->Ellipse($sx, $sy, $rx, $ry);break;case 'rectangle':$w = abs($sx - $ex);$h = abs($sy - $ey);$sx = min($sx, $ex);$sy = min($sy, $ey);// Width or height should be >= min width and heightif ($w < self::MIN_ANNOTATION_WIDTH) {$w = self::MIN_ANNOTATION_WIDTH;}if ($h < self::MIN_ANNOTATION_HEIGHT) {$h = self::MIN_ANNOTATION_HEIGHT;}$this->Rect($sx, $sy, $w, $h);break;case 'highlight':$w = abs($sx - $ex);$h = 8.0 * $this->scale;$sx = min($sx, $ex);$sy = min($sy, $ey) + ($h * 0.5);$this->SetAlpha(0.5, 'Normal', 0.5, 'Normal');$this->SetLineWidth(8.0 * $this->scale);// width should be >= min widthif ($w < self::MIN_ANNOTATION_WIDTH) {$w = self::MIN_ANNOTATION_WIDTH;}$this->Rect($sx, $sy, $w, $h);$this->SetAlpha(1.0, 'Normal', 1.0, 'Normal');break;case 'pen':if ($path) {$scalepath = array();$points = preg_split('/[,:]/', $path);foreach ($points as $point) {$scalepath[] = intval($point) * $this->scale;}if (!empty($scalepath)) {$this->PolyLine($scalepath, 'S');}}break;case 'stamp':$imgfile = $imagefolder . '/' . clean_filename($path);$w = abs($sx - $ex);$h = abs($sy - $ey);$sx = min($sx, $ex);$sy = min($sy, $ey);// Stamp is always more than 40px, so no need to check width/height.$this->Image($imgfile, $sx, $sy, $w, $h);break;default: // Line.$this->Line($sx, $sy, $ex, $ey);break;}$this->SetDrawColor(0, 0, 0);$this->SetLineWidth(1.0 * $this->scale);return true;}/*** Save the completed PDF to the given file* @param string $filename the filename for the PDF (including the full path)*/public function save_pdf($filename) {$olddebug = error_reporting(0);$this->Output($filename, 'F');error_reporting($olddebug);}/*** Set the path to the folder in which to generate page image files* @param string $folder*/public function set_image_folder($folder) {$this->imagefolder = $folder;}/*** Generate images from the PDF* @return array Array of filename of the generated images*/public function get_images(): array {$this->precheck_generate_image();$imagefile = $this->imagefolder . '/' . self::IMAGE_PAGE;$command = $this->get_command_for_image(-1, $imagefile);exec($command);$images = array();for ($i = 0; $i < $this->pagecount; $i++) {// Image file is created from 1, so need to change to 0.$file = $imagefile . ($i + 1) . '.png';$newfile = $imagefile . $i . '.png';if (file_exists($file)) {rename($file, $newfile);} else {// Converter added '-' and zerofill for the pagenumber.$length = strlen($this->pagecount);$file = $imagefile . '-' . str_pad(($i + 1), $length, '0', STR_PAD_LEFT) . '.png';if (file_exists($file)) {rename($file, $newfile);} else {$newfile = self::get_error_image($this->imagefolder, $i);}}$images[$i] = basename($newfile);}return $images;}/*** Generate an image of the specified page in the PDF* @param int $pageno the page to generate the image of* @throws \moodle_exception* @throws \coding_exception* @return string the filename of the generated image*/public function get_image($pageno) {$this->precheck_generate_image();$imagefile = $this->imagefolder . '/' . self::IMAGE_PAGE . $pageno . '.png';$generate = true;if (file_exists($imagefile)) {if (filemtime($imagefile) > filemtime($this->filename)) {// Make sure the image is newer than the PDF file.$generate = false;}}if ($generate) {$command = $this->get_command_for_image($pageno, $imagefile);$output = null;$result = exec($command, $output);if (!file_exists($imagefile)) {$fullerror = '<pre>'.get_string('command', 'assignfeedback_editpdf')."\n";$fullerror .= $command . "\n\n";$fullerror .= get_string('result', 'assignfeedback_editpdf')."\n";$fullerror .= htmlspecialchars($result, ENT_COMPAT) . "\n\n";$fullerror .= get_string('output', 'assignfeedback_editpdf')."\n";$fullerror .= htmlspecialchars(implode("\n", $output), ENT_COMPAT) . '</pre>';throw new \moodle_exception('errorgenerateimage', 'assignfeedback_editpdf', '', $fullerror);}}return self::IMAGE_PAGE . $pageno . '.png';}/*** Make sure the file name and image folder are ready before generate image.* @return bool*/protected function precheck_generate_image() {if (!$this->filename) {throw new \coding_exception('Attempting to generate a page image without first setting the PDF filename');}if (!$this->imagefolder) {throw new \coding_exception('Attempting to generate a page image without first specifying the image output folder');}if (!is_dir($this->imagefolder)) {throw new \coding_exception('The specified image output folder is not a valid folder');}return true;}/*** Gets the command to use to extract as image the given $pageno page number* from a PDF document into the $imagefile file.* @param int $pageno Page number to extract from document. -1 means for all pages.* @param string $imagefile Target filename for the PNG image as absolute path.* @return string The command to use to extract a page as PNG image.*/private function get_command_for_image(int $pageno, string $imagefile): string {global $CFG;// First, quickest convertion option.if (!empty($CFG->pathtopdftoppm) && is_executable($CFG->pathtopdftoppm)) {return $this->get_pdftoppm_command_for_image($pageno, $imagefile);}// Otherwise, rely on default behaviour.return $this->get_gs_command_for_image($pageno, $imagefile);}/*** Gets the pdftoppm command to use to extract as image the given $pageno page number* from a PDF document into the $imagefile file.* @param int $pageno Page number to extract from document. -1 means for all pages.* @param string $imagefile Target filename for the PNG image as absolute path.* @return string The pdftoppm command to use to extract a page as PNG image.*/private function get_pdftoppm_command_for_image(int $pageno, string $imagefile): string {global $CFG;$pdftoppmexec = \escapeshellarg($CFG->pathtopdftoppm);$imageres = \escapeshellarg(100);$filename = \escapeshellarg($this->filename);$pagenoinc = \escapeshellarg($pageno + 1);if ($pageno >= 0) {// Convert 1 page.$imagefile = substr($imagefile, 0, -4); // Pdftoppm tool automatically adds extension file.$frompageno = $pagenoinc;$topageno = $pagenoinc;$singlefile = '-singlefile';} else {// Convert all pages at once.$frompageno = 1;$topageno = $this->pagecount;$singlefile = '';}$imagefilearg = \escapeshellarg($imagefile);return "$pdftoppmexec -q -r $imageres -f $frompageno -l $topageno -png $singlefile $filename $imagefilearg";}/*** Gets the ghostscript (gs) command to use to extract as image the given $pageno page number* from a PDF document into the $imagefile file.* @param int $pageno Page number to extract from document. -1 means for all pages.* @param string $imagefile Target filename for the PNG image as absolute path.* @return string The ghostscript (gs) command to use to extract a page as PNG image.*/private function get_gs_command_for_image(int $pageno, string $imagefile): string {global $CFG;$gsexec = \escapeshellarg($CFG->pathtogs);$imageres = \escapeshellarg(100);$imagefilearg = \escapeshellarg($imagefile);$filename = \escapeshellarg($this->filename);$pagenoinc = \escapeshellarg($pageno + 1);if ($pageno >= 0) {// Convert 1 page.$firstpage = $pagenoinc;$lastpage = $pagenoinc;} else {// Convert all pages at once.$imagefilearg = \escapeshellarg($imagefile . '%d.png');$firstpage = 1;$lastpage = $this->pagecount;}return "$gsexec -q -sDEVICE=png16m -dSAFER -dBATCH -dNOPAUSE -r$imageres -dFirstPage=$firstpage -dLastPage=$lastpage "."-dDOINTERPOLATE -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -sOutputFile=$imagefilearg $filename";}/*** Check to see if PDF is version 1.4 (or below); if not: use ghostscript to convert it** @param \stored_file $file* @return string path to copy or converted pdf (false == fail)*/public static function ensure_pdf_compatible(\stored_file $file) {global $CFG;// Copy the stored_file to local disk for checking.$temparea = make_request_directory();$tempsrc = $temparea . "/source.pdf";$file->copy_content_to($tempsrc);return self::ensure_pdf_file_compatible($tempsrc);}/*** Flatten and convert file using ghostscript then load pdf.** @param string $tempsrc The path to the file on disk.* @return string path to copy or converted pdf (false == fail)*/public static function ensure_pdf_file_compatible($tempsrc) {global $CFG;$temparea = make_request_directory();$tempdst = $temparea . "/target.pdf";$gsexec = \escapeshellarg($CFG->pathtogs);$tempdstarg = \escapeshellarg($tempdst);$tempsrcarg = \escapeshellarg($tempsrc);$command = "$gsexec -q -sDEVICE=pdfwrite -dPreserveAnnots=false -dSAFER -dBATCH -dNOPAUSE -dCompatibilityLevel=1.4 ". "-sOutputFile=$tempdstarg $tempsrcarg";exec($command);if (!file_exists($tempdst)) {// Something has gone wrong in the conversion.return false;}$pdf = new pdf();$pagecount = 0;try {$pagecount = $pdf->load_pdf($tempdst);} catch (\Exception $e) {// PDF was not valid - try running it through ghostscript to clean it up.$pagecount = 0;}$pdf->Close(); // PDF loaded and never saved/outputted needs to be closed.if ($pagecount <= 0) {// Could not parse the converted pdf.return false;}return $tempdst;}/*** Generate an localised error image for the given pagenumber.** @param string $errorimagefolder path of the folder where error image needs to be created.* @param int $pageno page number for which error image needs to be created.** @return string File name* @throws \coding_exception*/public static function get_error_image($errorimagefolder, $pageno) {global $CFG;$errorfile = $CFG->dirroot . self::BLANK_PDF;if (!file_exists($errorfile)) {throw new \coding_exception("Blank PDF not found", "File path" . $errorfile);}$tmperrorimagefolder = make_request_directory();$pdf = new pdf();$pdf->set_pdf($errorfile);$pdf->copy_page();$pdf->add_comment(get_string('errorpdfpage', 'assignfeedback_editpdf'), 250, 300, 200, "red");$generatedpdf = $tmperrorimagefolder . '/' . 'error.pdf';$pdf->save_pdf($generatedpdf);$pdf = new pdf();$pdf->set_pdf($generatedpdf);$pdf->set_image_folder($tmperrorimagefolder);$image = $pdf->get_image(0);$pdf->Close(); // PDF loaded and never saved/outputted needs to be closed.$newimg = self::IMAGE_PAGE . $pageno . '.png';copy($tmperrorimagefolder . '/' . $image, $errorimagefolder . '/' . $newimg);return $newimg;}/*** Test that the configured path to ghostscript is correct and working.* @param bool $generateimage - If true - a test image will be generated to verify the install.* @return \stdClass*/public static function test_gs_path($generateimage = true) {global $CFG;$ret = (object)array('status' => self::GSPATH_OK,'message' => null,);$gspath = $CFG->pathtogs;if (empty($gspath)) {$ret->status = self::GSPATH_EMPTY;return $ret;}if (!file_exists($gspath)) {$ret->status = self::GSPATH_DOESNOTEXIST;return $ret;}if (is_dir($gspath)) {$ret->status = self::GSPATH_ISDIR;return $ret;}if (!is_executable($gspath)) {$ret->status = self::GSPATH_NOTEXECUTABLE;return $ret;}if (!$generateimage) {return $ret;}$testfile = $CFG->dirroot.'/mod/assign/feedback/editpdf/tests/fixtures/testgs.pdf';if (!file_exists($testfile)) {$ret->status = self::GSPATH_NOTESTFILE;return $ret;}$testimagefolder = \make_temp_directory('assignfeedback_editpdf_test');$filepath = $testimagefolder . '/' . self::IMAGE_PAGE . '0.png';// Delete any previous test images, if they exist.if (file_exists($filepath)) {unlink($filepath);}$pdf = new pdf();$pdf->set_pdf($testfile);$pdf->set_image_folder($testimagefolder);try {$pdf->get_image(0);} catch (\moodle_exception $e) {$ret->status = self::GSPATH_ERROR;$ret->message = $e->getMessage();}$pdf->Close(); // PDF loaded and never saved/outputted needs to be closed.return $ret;}/*** If the test image has been generated correctly - send it direct to the browser.*/public static function send_test_image() {global $CFG;header('Content-type: image/png');require_once($CFG->libdir.'/filelib.php');$testimagefolder = \make_temp_directory('assignfeedback_editpdf_test');$testimage = $testimagefolder . '/' . self::IMAGE_PAGE . '0.png';send_file($testimage, basename($testimage), 0);die();}/*** This function add an image file to PDF page.* @param \stored_file $imagestoredfile Image file to be added*/public function add_image_page($imagestoredfile) {$imageinfo = $imagestoredfile->get_imageinfo();$imagecontent = $imagestoredfile->get_content();$this->currentpage++;$template = $this->importPage($this->currentpage);$size = $this->getTemplateSize($template);$orientation = 'P';if ($imageinfo["width"] > $imageinfo["height"]) {if ($size['width'] < $size['height']) {$temp = $size['width'];$size['width'] = $size['height'];$size['height'] = $temp;}$orientation = 'L';} else if ($imageinfo["width"] < $imageinfo["height"]) {if ($size['width'] > $size['height']) {$temp = $size['width'];$size['width'] = $size['height'];$size['height'] = $temp;}}$this->SetHeaderMargin(0);$this->SetFooterMargin(0);$this->SetMargins(0, 0, 0, true);$this->setPrintFooter(false);$this->setPrintHeader(false);$this->AddPage($orientation, $size);$this->SetAutoPageBreak(false, 0);$this->Image('@' . $imagecontent, 0, 0, $size['width'], $size['height'],'', '', '', false, null, '', false, false, 0);}}