Gitlab CSE Unil

Commit b3d5baea authored by M. Chardon's avatar M. Chardon
Browse files

- suppression des stamps

- generation des stamps depuis fontawesome pour la génération du PDF
parent 7d0ead4c
......@@ -62,7 +62,6 @@ class backup_assignfeedback_editpdfplus_subplugin extends backup_subplugin {
// We only need to backup the files in the final pdf area, and the readonly page images - the others can be regenerated.
$subpluginelementfiles->annotate_files('assignfeedback_editpdfplus', \assignfeedback_editpdfplus\document_services::FINAL_PDF_FILEAREA, 'gradeid');
$subpluginelementfiles->annotate_files('assignfeedback_editpdfplus', \assignfeedback_editpdfplus\document_services::PAGE_IMAGE_READONLY_FILEAREA, 'gradeid');
$subpluginelementfiles->annotate_files('assignfeedback_editpdfplus', 'stamps', 'gradeid');
return $subplugin;
}
......
......@@ -68,7 +68,6 @@ class restore_assignfeedback_editpdfplus_subplugin extends restore_subplugin {
// In this case the id is the old gradeid which will be mapped.
$this->add_related_files('assignfeedback_editpdfplus', \assignfeedback_editpdfplus\document_services::FINAL_PDF_FILEAREA, 'grade', null, $data->gradeid);
$this->add_related_files('assignfeedback_editpdfplus', \assignfeedback_editpdfplus\document_services::PAGE_IMAGE_READONLY_FILEAREA, 'grade', null, $data->gradeid);
$this->add_related_files('assignfeedback_editpdfplus', 'stamps', 'grade', null, $data->gradeid);
}
/**
......
......@@ -610,24 +610,12 @@ EOD;
}
$file = $document->get_combined_file();
$tmpdir = make_temp_directory('assignfeedback_editpdfplus/final/' . self::hash($assignment, $userid, $attemptnumber));
$combined = $tmpdir . '/' . self::COMBINED_PDF_FILENAME;
$file->copy_content_to($combined); // Copy the file.
$pdf = new pdf();
$fs = get_file_storage();
$stamptmpdir = make_temp_directory('assignfeedback_editpdfplus/stamps/' . self::hash($assignment, $userid, $attemptnumber));
$grade = $assignment->get_user_grade($userid, true, $attemptnumber);
// Copy any new stamps to this instance.
if ($files = $fs->get_area_files($assignment->get_context()->id, 'assignfeedback_editpdfplus', 'stamps', $grade->id, "filename", false)) {
foreach ($files as $file) {
$filename = $stamptmpdir . '/' . $file->get_filename();
$file->copy_content_to($filename); // Copy the file.
}
}
$pagecount = $pdf->set_pdf($combined);
$grade = $assignment->get_user_grade($userid, true, $attemptnumber);
if (!$refresh) {
......@@ -642,7 +630,7 @@ EOD;
$annotations = page_editor::get_annotations($grade->id, $i, false);
foreach ($annotations as $annotation) {
$pdf->add_annotation($annotation, $annotation->path, $stamptmpdir, $compteur);
$pdf->add_annotation($annotation, $annotation->path, $compteur);
if ($annotation->textannot && !$annotation->parent_annot) {
$annotation_index[$annotation->id] = $compteur;
$compteur++;
......@@ -705,15 +693,12 @@ EOD;
$finalcomment = page_editor::get_feedback_comments($grade->id);
$pdf->writeHTMLCell(0, 0, 40, 70, $finalcomment->commenttext);
fulldelete($stamptmpdir);
$filename = self::get_downloadable_feedback_filename($assignment, $userid, $attemptnumber);
$filename = clean_param($filename, PARAM_FILE);
$generatedpdf = $tmpdir . '/' . $filename;
$pdf->save_pdf($generatedpdf);
$record = new \stdClass();
$record->contextid = $assignment->get_context()->id;
......@@ -723,8 +708,8 @@ EOD;
$record->filepath = '/';
$record->filename = $filename;
// Only keep one current version of the generated pdf.
$fs = get_file_storage();
$fs->delete_area_files($record->contextid, $record->component, $record->filearea, $record->itemid);
$file = $fs->create_file_from_pathname($record, $generatedpdf);
......
......@@ -25,6 +25,9 @@
namespace assignfeedback_editpdfplus;
use \assignfeedback_editpdfplus\utils_color;
use \assignfeedback_editpdfplus\utils_stamp;
defined('MOODLE_INTERNAL') || die();
global $CFG;
......@@ -49,9 +52,6 @@ class pdf extends \FPDI {
/** @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;
......@@ -226,91 +226,22 @@ class pdf extends \FPDI {
}
}
/**
* Get RGB color from label of color or hexa code
* @param string $colour
* @return array()
*/
private function get_colour_for_pdf($colour) {
if (substr($colour, 0, 1) == '#') {
$colourarray = $this->hex2RGB($colour, false);
} else {
switch ($colour) {
case 'orange':
$colourarray = array(255, 207, 53);
break;
case 'yellow':
$colourarray = array(255, 207, 53);
break;
case 'yellowlemon':
$colourarray = array(255, 255, 0);
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:
$colourarray = array(239, 69, 64);
break;
}
}
return $colourarray;
}
/**
* Convert a hexa decimal color code to its RGB equivalent
*
* @param string $hexStr (hexadecimal color value)
* @param boolean $returnAsString (if set true, returns the value separated by the separator character. Otherwise returns associative array)
* @param string $seperator (to separate RGB values. Applicable only if second parameter is true.)
* @return array or string (depending on second parameter. Returns False if invalid hex color value)
*/
private function hex2RGB($hexStr, $returnAsString = false, $seperator = ',') {
$hexStr = preg_replace("/[^0-9A-Fa-f]/", '', $hexStr); // Gets a proper hex string
$rgbArray = array();
if (strlen($hexStr) == 6) { //If a proper hex code, convert using bitwise operation. No overhead... faster
$colorVal = hexdec($hexStr);
$rgbArray['red'] = 0xFF & ($colorVal >> 0x10);
$rgbArray['green'] = 0xFF & ($colorVal >> 0x8);
$rgbArray['blue'] = 0xFF & $colorVal;
} elseif (strlen($hexStr) == 3) { //if shorthand notation, need some string manipulations
$rgbArray['red'] = hexdec(str_repeat(substr($hexStr, 0, 1), 2));
$rgbArray['green'] = hexdec(str_repeat(substr($hexStr, 1, 1), 2));
$rgbArray['blue'] = hexdec(str_repeat(substr($hexStr, 2, 1), 2));
} else {
return false; //Invalid hex color code
}
return $returnAsString ? implode($seperator, $rgbArray) : $rgbArray; // returns the rgb string or the associative array
}
/**
* 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 \assignfeedback_editpdfplus\annotation $annotation
* @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.
* @param type $annotation_index
* @return bool true if successful (always)
*/
public function add_annotation(annotation $annotation, $path, $imagefolder, $annotation_index) {
global $CFG;
public function add_annotation(annotation $annotation, $path, $annotation_index) {
if (!$this->filename) {
return false;
}
$colour = $annotation->colour;
$colourarray = $this->get_colour_for_pdf($colour);
$colourarray = utils_color::getColorRGB($colour); //$this->get_colour_for_pdf($colour);
$this->SetDrawColorArray($colourarray);
$sx = $this->scale * $annotation->x;
......@@ -328,7 +259,7 @@ class pdf extends \FPDI {
if (!$colourcartridge) {
$colourcartridge = $typetool->cartridge_color;
}
$colourcartridgearray = $this->get_colour_for_pdf($colourcartridge);
$colourcartridgearray = utils_color::getColorRGB($colourcartridge); //$this->get_colour_for_pdf($colourcartridge);
$this->SetTextColorArray($colourcartridgearray);
$this->SetFontSize(10);
......@@ -393,16 +324,6 @@ class pdf extends \FPDI {
}
}
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;
case 'highlightplus':
$w = abs($sx - $ex);
$h = 8.0 * $this->scale;
......@@ -462,34 +383,40 @@ class pdf extends \FPDI {
break;
case 'stampcomment':
if ($annotation->displayrotation == 1) {
$imgfile = $CFG->dirroot . '/mod/assign/feedback/editpdfplus/pix/twoway_v_pdf.png';
$h = 20; //abs($sy - $ey);
$w = $h * 37 / 96;
$sx = min($sx, $ex) + 8;
$sy = min($sy, $ey) + 2;
$imgFileFromFA = utils_stamp::getPngFromFont("arrows-v");
//$imgfile = $CFG->dirroot . '/mod/assign/feedback/editpdfplus/pix/twoway_v_pdf.png';
//$h = 20; //abs($sy - $ey);
//$w = $h * 37 / 96;
$sx = min($sx, $ex) - 6;
} else {
$imgfile = $CFG->dirroot . '/mod/assign/feedback/editpdfplus/pix/twoway_h_pdf.png';
$w = 20; //abs($sx - $ex);
$h = $w * 37 / 96;
$sx = min($sx, $ex) + 2;
$sy = min($sy, $ey) + 8;
$imgFileFromFA = utils_stamp::getPngFromFont("arrows-h");
//$imgfile = $CFG->dirroot . '/mod/assign/feedback/editpdfplus/pix/twoway_h_pdf.png';
//$w = 20; //abs($sx - $ex);
//$h = $w * 37 / 96;
$sx = min($sx, $ex) - 1;
}
$sy = min($sy, $ey);
$w = 30 * $this->scale;
$h = 30 * $this->scale;
if ($imgFileFromFA) {
$this->Image($imgFileFromFA, $sx, $sy, $w, $h);
}
// Stamp is always more than 40px, so no need to check width/height.
$this->Image($imgfile, $sx, $sy, $w, $h);
$scartx = ($annotation->cartridgex + $annotation->x) * $this->scale;
$scarty = ($annotation->cartridgey + $annotation->y) * $this->scale;
$this->SetXY($scartx, $scarty);
break;
case 'commentplus':
$imgfile = $CFG->dirroot . '/mod/assign/feedback/editpdfplus/pix/comment.png';
$imgFileFromFA = utils_stamp::getPngFromFont("commenting");
//$imgfile = $CFG->dirroot . '/mod/assign/feedback/editpdfplus/pix/comment.png';
$w = 16 * $this->scale;
$h = 16 * $this->scale;
$sx = min($sx, $ex);
$sy = min($sy, $ey);
$this->SetXY($sx + $w + 2, $sy);
// Stamp is always more than 40px, so no need to check width/height.
$this->Image($imgfile, $sx, $sy, $w, $h);
if ($imgFileFromFA) {
$this->Image($imgFileFromFA, $sx, $sy, $w, $h);
}
break;
case 'stampplus':
$w = abs($sx - $ex);
......@@ -510,13 +437,13 @@ class pdf extends \FPDI {
if (!$colourcartridge) {
$colourcartridge = $typetool->color;
}
$colourcartridgearray = $this->get_colour_for_pdf($colourcartridge);
$colourcartridgearray = utils_color::getColorRGB($colourcartridge); //$this->get_colour_for_pdf($colourcartridge);
$this->SetTextColorArray($colourcartridgearray);
$cartouche = $toolObject->label;
//$this->Cell($w);
//Texte centré dans une cellule 20*10 mm encadrée et retour à la ligne
$this->Cell(strlen($cartouche)*6+4, 10, $cartouche, 1, 1, 'C');
$this->Cell(strlen($cartouche) * 6 + 4, 10, $cartouche, 1, 1, 'C');
break;
default: // Line.
......
......@@ -347,7 +347,6 @@ class assignfeedback_editpdfplus_renderer extends plugin_renderer_base {
'assignmentid' => $widget->assignment,
'userid' => $widget->userid,
'attemptnumber' => $widget->attemptnumber,
'stampfiles' => $widget->stampfiles,
'readonly' => $widget->readonly));
$this->page->requires->yui_module('moodle-assignfeedback_editpdfplus-editor', 'M.assignfeedback_editpdfplus.editor.init', $editorparams);
......
This diff is collapsed.
<?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/>.
/**
* This file contains the editor class for the assignfeedback_editpdfplus plugin
*
* @package assignfeedback_editpdfplus
* @copyright 2018 Université de Lausanne
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignfeedback_editpdfplus;
use \assignfeedback_editpdfplus\utils_color;
/**
* This class performs crud operations on stamp using font FontAwesome.
*
* No capability checks are done - they should be done by the calling class.
*
* Inspired by https://github.com/exiang/php-font-awesome-to-png
*
* @package assignfeedback_editpdfplus
* @copyright 2018 Marion Chardon
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class utils_stamp {
/** Path of fontawesome font scss */
const FA_SCSS_PATH = '/lib/fonts/font-awesome-4.7.0/scss/_variables.scss';
/** Path of fontawesome font */
const FA_PATH = '/lib/fonts/fontawesome-webfont.ttf';
/** File area for the stamps */
const STAMPS_FILEAREA = '/temp/assignfeedback_editpdfplus/stamps/';
/**
* Convert a FontAwesome icon into a PNG file
* Main method
*
* @param String $iconName The name of the FontAwesome icon, for example if you need fa-user, give "user"
* @param String $color The color in which you want to display the PNG, default black
* @param type $fontSize The size of the font in which you want to display the PNG, default 48
* @return String The path of the PNG file generate, or null if the convertion failed
*/
public static function getPngFromFont($iconName, $color = "black", $fontSize = 48) {
global $CFG;
//get hexa code from the key iconName
$charFAHexaCode = self::getUniCode($iconName);
if (!$charFAHexaCode) {
return null;
}
//create final file
$outputDirectory = $CFG->dataroot . self::STAMPS_FILEAREA;
$fileName = sprintf("%s%s.png", $outputDirectory, $iconName);
//get RGB color
$colorRGB = utils_color::hex2RGB($color);
//put image into file and return
return self::putIconIntoFile($charFAHexaCode, $fileName, $colorRGB, $fontSize);
}
/**
* Write a chart into a PNG file
*
* @param String $charFACode Chart to represent (unicode)
* @param String $fileName The PNG final file
* @param array $colorRGB The color in which you want to display the PNG, default black
* @param int $fontSize The size of the font in which you want to display the PNG
* @return String The path of the PNG file generate, or null if the convertion failed
*/
public static function putIconIntoFile($charFACode, $fileName, $colorRGB, $fontSize) {
global $CFG;
//Calculate sizes
$width = $height = $fontSize * 3;
$padding = (int) ceil(($fontSize / 25));
// Create the image
$imageTmp = imagecreatetruecolor($width, $height);
imagealphablending($imageTmp, false);
// Create font colors
$fontColor = imagecolorallocate($imageTmp, $colorRGB['r'], $colorRGB['g'], $colorRGB['b']);
$background = imagecolorallocatealpha($imageTmp, 255, 0, 255, 127);
// Draw rectangle
imagefilledrectangle($imageTmp, 0, 0, $width, $height, $background);
imagealphablending($imageTmp, true);
// Add the text using the Font
$fontPath = $CFG->dirroot . self::FA_PATH;
list($fontX, $fontY) = self::imageTTFCenter($imageTmp, $charFACode, $fontPath, $fontSize);
imagettftext($imageTmp, $fontSize, 0, $fontX, $fontY, $fontColor, $fontPath, $charFACode);
imagealphablending($imageTmp, false);
imagesavealpha($imageTmp, true);
//Center the image
self::imageTrim($imageTmp, $background, $padding);
//Resize the image
self::imageCanvas($imageTmp, $fontSize, $background, $padding);
//convert image to PNG
imagepng($imageTmp, $fileName);
//purge memory
imagedestroy($imageTmp);
return $fileName;
}
/**
* Calculate and set the center position of an image given by the size of a text in its font representation
*
* @param resource $image Reference image
* @param String $text Text to insert into the image
* @param String $font Font path
* @param int $size Font size into display the text
* @param int $angle Angle into display the text, default 45°
* @return array the center position
*/
public static function imageTTFCenter($image, $text, $font, $size, $angle = 45) {
$xi = imagesx($image);
$yi = imagesy($image);
// create a bounding box for the first text
$box = imagettfbbox($size, $angle, $font, $text);
// get dimension of the box
$xr = abs(max($box[2], $box[4]));
$yr = abs(max($box[5], $box[7]));
// compute centering
$x = intval(($xi - $xr) / 2);
$y = intval(($yi + $yr) / 2);
return array($x, $y);
}
/**
* Position and assemblate image and background
*
* @param resource $image Final image to arrange
* @param int $background Color and alpha background
* @param int $padding Space between chart and border
*/
public static function imageTrim(&$image, $background, $padding = null) {
// Calculate padding for each side.
if (isset($padding)) {
$pp = explode(' ', $padding);
if (isset($pp[3])) {
$p = array((int) $pp[0], (int) $pp[1], (int) $pp[2], (int) $pp[3]);
} else if (isset($pp[2])) {
$p = array((int) $pp[0], (int) $pp[1], (int) $pp[2], (int) $pp[1]);
} else if (isset($pp[1])) {
$p = array((int) $pp[0], (int) $pp[1], (int) $pp[0], (int) $pp[1]);
} else {
$p = array_fill(0, 4, (int) $pp[0]);
}
} else {
$p = array_fill(0, 4, 0);
}
// Get the image width and height.
$imageWidth = imagesx($image);
$imageHeight = imagesy($image);
// Set the X variables.
$xmin = $imageWidth;
$xmax = 0;
// Start scanning for the edges.
for ($iy = 0; $iy < $imageHeight; $iy++) {
$first = true;
for ($ix = 0; $ix < $imageWidth; $ix++) {
if (imagecolorat($image, $ix, $iy) == $background) {
continue;
}
$xmin = ($xmin > $ix) ? $ix : $xmin;
$xmax = ($xmax < $ix) ? $ix : $xmax;
$ymin = (!isset($ymin)) ? $iy : $ymin;
$ymax = $iy;
$ix = ($first) ? $xmax : $ix;
$first = false;
}
}
// The new width and height of the image. (not including padding)
$imageNewWidth = 1 + $xmax - $xmin; // Image width in pixels
$imageNewHeight = 1 + $ymax - $ymin; // Image height in pixels
// Make another image to place the trimmed version in.
$image2 = imagecreatetruecolor($imageNewWidth + $p[1] + $p[3], $imageNewHeight + $p[0] + $p[2]);
// Make the background of the new image the same as the background of the old one.
$bg2 = imagecolorallocatealpha($image2, ($background >> 16) & 0xFF, ($background >> 8) & 0xFF, $background & 0xFF, 127);
imagefill($image2, 0, 0, $bg2);
imagealphablending($image2, true);
// Copy it over to the new image.
imagecopy($image2, $image, $p[3], $p[0], $xmin, $ymin, $imageNewWidth, $imageNewHeight);
// To finish up, we replace the old image which is referenced.
imagealphablending($image2, false);
imagesavealpha($image2, true);
$image = $image2;
}
/**
* Crop an image according to the size parameter
*
* @param resource $image the image to resize
* @param int $size New size
* @param int $background color and alpha background
* @param int $padding space between image content and border
*/
public static function imageCanvas(&$image, $size, $background, $padding) {
$sourceWidth = imagesx($image);
$ssourceHeight = imagesy($image);
//create a second image to put the result with operation on the $image
$image2 = imagecreatetruecolor($size, $size);
$background2 = imagecolorallocatealpha($image2, ($background >> 16) & 0xFF, ($background >> 8) & 0xFF, $background & 0xFF, 127);
imagefill($image2, 0, 0, $background2);
imagealphablending($image2, true);
// init
$sourceX = $sourceY = 0;
$destinationWidth = $destinationtHeight = $size;
// if source size is smaller than output size
if ($sourceWidth < $size && $ssourceHeight < $size) {
$destinationWidth = $sourceWidth;
$destinationtHeight = $ssourceHeight;
}
// if source is bigger than output
else {
// use padding
// if horizontal long
if ($sourceWidth > $ssourceHeight) {
$destinationWidth = $size - $padding;
$destinationtHeight = (int) (($destinationWidth / $sourceWidth) * $ssourceHeight);
}
// if vertically long or equal(square)
else {
$destinationtHeight = $size - $padding;
$destinationWidth = (int) (($destinationtHeight / $ssourceHeight) * $sourceWidth);
}
}
//Calculate new coordonates
$destinationX = (int) (($size - $destinationWidth) / 2);
$destinationY = (int) (($size - $destinationtHeight) / 2);
//Copy and resize the fist image into image2
imagecopyresampled($image2, $image, $destinationX, $destinationY, $sourceX, $sourceY, $destinationWidth, $destinationtHeight, $sourceWidth, $ssourceHeight);
//finalize image2
imagealphablending($image2, false);
imagesavealpha($image2, true);
//replace image with image2
$image = $image2;
}
/**
* Get unicode of a icon reprented in FontAwesome, using the scss file
*
* @param String $iconName
* @return String unicode or null if no correspondance
*/
public static function getUniCode($iconName) {
global $CFG;
//open scss file
$readBuffer = file_get_contents($CFG->dirroot . self::FA_SCSS_PATH);
$lines = explode("\n", $readBuffer);
//get all lines to search the hexa code from key
foreach ($lines as $l) {
if (!strstr($l, '$fa-var-')) {
continue;
}
$lArray = explode(":", $l);
$key = str_replace('$fa-var-', '', trim($lArray[0]));
if ($key