From 9e725bc168d63a5a4034622b5e6a7868014c1b67 Mon Sep 17 00:00:00 2001 From: Tomo Tsuyuki Date: Fri, 17 Feb 2023 15:30:25 +1100 Subject: [PATCH] MDL-45301 assign: Add font options for EditPDF --- admin/settings/courses.php | 17 ++++++++++ admin/settings/language.php | 2 ++ backup/moodle2/backup_stepslib.php | 2 +- backup/moodle2/restore_stepslib.php | 3 ++ config-dist.php | 6 +++- .../external/course_summary_exporter.php | 4 +++ course/edit_form.php | 17 ++++++++++ course/externallib.php | 1 + course/lib.php | 2 +- course/tests/courselib_test.php | 2 +- lang/en/admin.php | 2 ++ lang/en/course.php | 2 ++ lib/db/install.xml | 1 + lib/db/upgrade.php | 14 +++++++++ lib/pdflib.php | 28 +++++++++++++++++ lib/tests/pdflib_test.php | 31 +++++++++++++++++++ .../editpdf/classes/document_services.php | 15 +++++++++ mod/assign/feedback/editpdf/classes/pdf.php | 18 ++++++++--- version.php | 2 +- 19 files changed, 160 insertions(+), 9 deletions(-) diff --git a/admin/settings/courses.php b/admin/settings/courses.php index e00d83173640c..1acab26cfb310 100644 --- a/admin/settings/courses.php +++ b/admin/settings/courses.php @@ -22,6 +22,10 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/pdflib.php'); + use core_admin\local\settings\filesize; $capabilities = array( @@ -174,6 +178,19 @@ $temp->add(new admin_setting_configselect('moodlecourse/maxbytes', new lang_string('maximumupload'), new lang_string('coursehelpmaximumupload'), key($choices), $choices)); + if (!empty($CFG->enablepdfexportfont)) { + $pdf = new \pdf; + $fontlist = $pdf->get_export_fontlist(); + // Show the option if the font is defined more than one. + if (count($fontlist) > 1) { + $temp->add(new admin_setting_configselect('moodlecourse/pdfexportfont', + new lang_string('pdfexportfont', 'course'), + new lang_string('pdfexportfont_help', 'course'), + 'freesans', $fontlist + )); + } + } + // Completion tracking. $temp->add(new admin_setting_heading('progress', new lang_string('completion','completion'), '')); $temp->add(new admin_setting_configselect('moodlecourse/enablecompletion', new lang_string('completion', 'completion'), diff --git a/admin/settings/language.php b/admin/settings/language.php index 97fb85d8f15a1..962a8c821b826 100644 --- a/admin/settings/language.php +++ b/admin/settings/language.php @@ -18,6 +18,8 @@ $temp->add(new admin_setting_configcheckbox('langstringcache', new lang_string('langstringcache', 'admin'), new lang_string('configlangstringcache', 'admin'), 1)); $temp->add(new admin_setting_configtext('locale', new lang_string('localetext', 'admin'), new lang_string('configlocale', 'admin'), '', PARAM_FILE)); $temp->add(new admin_setting_configselect('latinexcelexport', new lang_string('latinexcelexport', 'admin'), new lang_string('configlatinexcelexport', 'admin'), '0', array('0'=>'Unicode','1'=>'Latin'))); + $temp->add(new admin_setting_configcheckbox('enablepdfexportfont', new lang_string('enablepdfexportfont', 'admin'), + new lang_string('enablepdfexportfont_desc', 'admin'), 0)); $temp->add(new setting_scheduled_task_status('langimporttaskstatus', '\tool_langimport\task\update_langpacks_task')); $ADMIN->add('language', $temp); diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index 8f82b94d3fc15..2c94486690ee9 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -459,7 +459,7 @@ protected function define_structure() { 'timecreated', 'timemodified', 'requested', 'showactivitydates', - 'showcompletionconditions', + 'showcompletionconditions', 'pdfexportfont', 'enablecompletion', 'completionstartonenrol', 'completionnotify')); $category = new backup_nested_element('category', array('id'), array( diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index b1b9768079a6c..7b0382a39eb6d 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -1933,6 +1933,9 @@ public function process_course($data) { $showactivitydatesdefault = ($courseconfig->showactivitydates ?? null); $data->showactivitydates = $data->showactivitydates ?? $showactivitydatesdefault; + $pdffontdefault = ($courseconfig->pdfexportfont ?? null); + $data->pdfexportfont = $data->pdfexportfont ?? $pdffontdefault; + $languages = get_string_manager()->get_list_of_translations(); // Get languages for quick search if (isset($data->lang) && !array_key_exists($data->lang, $languages)) { $data->lang = ''; diff --git a/config-dist.php b/config-dist.php index e81cedced28ac..3d09fde245d7b 100644 --- a/config-dist.php +++ b/config-dist.php @@ -655,8 +655,12 @@ // Font used in exported PDF files. When generating a PDF, Moodle embeds a subset of // the font in the PDF file so it will be readable on the widest range of devices. // The default font is 'freesans' which is part of the GNU FreeFont collection. +// The font used to export can be set per-course - a drop down list in the course +// settings shows all the options specified in the array here. The key must be the +// font name (e.g., "kozminproregular") and the value is a friendly name, (e.g., +// "Kozmin Pro Regular"). // -// $CFG->pdfexportfont = 'freesans'; +// $CFG->pdfexportfont = ['freesans' => 'FreeSans']; // // Use the following flag to enable messagingallusers and set the default preference // value for existing users to allow them to be contacted by other site users. diff --git a/course/classes/external/course_summary_exporter.php b/course/classes/external/course_summary_exporter.php index e50f11bdf6b56..64260851ba4ce 100644 --- a/course/classes/external/course_summary_exporter.php +++ b/course/classes/external/course_summary_exporter.php @@ -117,6 +117,10 @@ public static function define_properties() { 'type' => PARAM_BOOL, 'null' => NULL_ALLOWED ], + 'pdfexportfont' => [ + 'type' => PARAM_TEXT, + 'null' => NULL_ALLOWED + ], ); } diff --git a/course/edit_form.php b/course/edit_form.php index 0c6d9028ff1c8..fdcdad7f299f9 100644 --- a/course/edit_form.php +++ b/course/edit_form.php @@ -4,6 +4,7 @@ require_once($CFG->libdir.'/formslib.php'); require_once($CFG->libdir.'/completionlib.php'); +require_once($CFG->libdir . '/pdflib.php'); /** * The form for handling editing a course. @@ -318,6 +319,22 @@ function definition() { $mform->addHelpButton('maxbytes', 'maximumupload'); $mform->setDefault('maxbytes', $courseconfig->maxbytes); + // PDF font. + if (!empty($CFG->enablepdfexportfont)) { + $pdf = new \pdf; + $fontlist = $pdf->get_export_fontlist(); + // Show the option if the font is defined more than one. + if (count($fontlist) > 1) { + $defaultfont = $courseconfig->pdfexportfont ?? 'freesans'; + if (empty($fontlist[$defaultfont])) { + $defaultfont = current($fontlist); + } + $mform->addElement('select', 'pdfexportfont', get_string('pdfexportfont', 'course'), $fontlist); + $mform->addHelpButton('pdfexportfont', 'pdfexportfont', 'course'); + $mform->setDefault('pdfexportfont', $defaultfont); + } + } + // Completion tracking. if (completion_info::is_enabled_for_site()) { $mform->addElement('header', 'completionhdr', get_string('completion', 'completion')); diff --git a/course/externallib.php b/course/externallib.php index 372c73a045cf6..84f961f14b419 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -621,6 +621,7 @@ public static function get_courses($options = array()) { // For backward-compartibility $courseinfo['numsections'] = $courseformatoptions['numsections']; } + $courseinfo['pdfexportfont'] = $course->pdfexportfont; $handler = core_course\customfield\course_handler::create(); if ($customfields = $handler->export_instance_data($course->id)) { diff --git a/course/lib.php b/course/lib.php index 9608ce599ced2..0a57d990575a2 100644 --- a/course/lib.php +++ b/course/lib.php @@ -4747,7 +4747,7 @@ function course_get_recent_courses(int $userid = null, int $limit = 0, int $offs $basefields = [ 'id', 'idnumber', 'summary', 'summaryformat', 'startdate', 'enddate', 'category', 'shortname', 'fullname', 'timeaccess', 'component', 'visible', - 'showactivitydates', 'showcompletionconditions', + 'showactivitydates', 'showcompletionconditions', 'pdfexportfont' ]; if (empty($sort)) { diff --git a/course/tests/courselib_test.php b/course/tests/courselib_test.php index 1b2e5e677f565..0c17b7a925076 100644 --- a/course/tests/courselib_test.php +++ b/course/tests/courselib_test.php @@ -5751,7 +5751,7 @@ function course_get_recent_courses_sort_validation_provider() { 'shortname DESC, xyz ASC', 'Invalid field in the sort parameter, allowed fields: id, idnumber, summary, summaryformat, ' . 'startdate, enddate, category, shortname, fullname, timeaccess, component, visible, ' . - 'showactivitydates, showcompletionconditions.', + 'showactivitydates, showcompletionconditions, pdfexportfont.', ], 'Sort uses invalid value for the sorting direction' => [ diff --git a/lang/en/admin.php b/lang/en/admin.php index bc34156ec17b7..79bdbef41189a 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -577,6 +577,8 @@ $string['enablegravatar'] = 'Enable Gravatar'; $string['enablegravatar_help'] = 'When enabled Moodle will attempt to fetch a user profile picture from Gravatar if the user has not uploaded an image.'; $string['enablemobilewebservice'] = 'Enable web services for mobile devices'; +$string['enablepdfexportfont'] = 'Enable PDF fonts'; +$string['enablepdfexportfont_desc'] = 'If your site has courses in different languages which need other fonts in generated PDF files, you can provide the option to set the font in the course settings. You need to specify available fonts in $CFG->pdfexportfont in config.php. If $CFG->pdfexportfont defines none or one font, the setting in the course is not shown.'; $string['enablerecordcache'] = 'Enable record cache'; $string['enablerssfeeds'] = 'Enable RSS feeds'; $string['enablesearchareas'] = 'Enable search areas'; diff --git a/lang/en/course.php b/lang/en/course.php index f25a35ed3626e..58af0966b8386 100644 --- a/lang/en/course.php +++ b/lang/en/course.php @@ -108,6 +108,8 @@ $string['participants:perpage'] = 'Number of participants per page'; $string['participants:perpage_help'] = 'The number of users shown per page on the participants page in each course.'; $string['participantsnavigation'] = 'Participants tertiary navigation.'; +$string['pdfexportfont'] = 'PDF font'; +$string['pdfexportfont_help'] = 'The font to be used for generated PDF files, such as assignment submissions.'; $string['privacy:perpage'] = 'The number of courses to show per page.'; $string['privacy:completionpath'] = 'Course completion'; $string['privacy:favouritespath'] = 'Course starred information'; diff --git a/lib/db/install.xml b/lib/db/install.xml index baa18195b80e0..617dd9da70d0d 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -105,6 +105,7 @@ + diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index f1f9593284577..b13671979170d 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2984,5 +2984,19 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2023020800.00); } + if ($oldversion < 2023021700.01) { + // Define field pdfexportfont to be added to course. + $table = new xmldb_table('course'); + $field = new xmldb_field('pdfexportfont', XMLDB_TYPE_CHAR, '50', null, false, false, null, 'showcompletionconditions'); + + // Conditionally launch add field pdfexportfont. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2023021700.01); + } + return true; } diff --git a/lib/pdflib.php b/lib/pdflib.php index 03b011e2ba2ec..64a76c6fd4448 100644 --- a/lib/pdflib.php +++ b/lib/pdflib.php @@ -250,4 +250,32 @@ public function get_font_families() { return $families; } + + /** + * Get font list from config. + * @return array|string[] + */ + public function get_export_fontlist(): array { + global $CFG; + $fontlist = []; + if (!empty($CFG->pdfexportfont)) { + if (is_array($CFG->pdfexportfont)) { + $fontlist = $CFG->pdfexportfont; + } else { + $fontlist[$CFG->pdfexportfont] = $CFG->pdfexportfont; + } + } + // Verify fonts. + $availablefonts = $this->get_font_families(); + foreach ($fontlist as $key => $value) { + if (empty($availablefonts[$key])) { + unset($fontlist[$key]); + } + } + if (empty($fontlist)) { + // Default font if there is no value set in CFG. + $fontlist = ['freesans' => 'FreeSans']; + } + return $fontlist; + } } diff --git a/lib/tests/pdflib_test.php b/lib/tests/pdflib_test.php index fea0e2dc34b90..ec57baff78e6d 100644 --- a/lib/tests/pdflib_test.php +++ b/lib/tests/pdflib_test.php @@ -66,4 +66,35 @@ public function test_qrcode() { $this->assertGreaterThan(100000, strlen($res)); $this->assertLessThan(120000, strlen($res)); } + + /** + * Test get_export_fontlist function. + * + * @covers ::get_export_fontlist + * + * @return void + */ + public function test_get_export_fontlist(): void { + global $CFG; + require_once($CFG->libdir.'/pdflib.php'); + + $this->resetAfterTest(); + + $pdf = new \pdf(); + $fontlist = $pdf->get_export_fontlist(); + $this->assertCount(1, $fontlist); + $this->assertArrayHasKey('freesans', $fontlist); + + $CFG->pdfexportfont = [ + 'kozminproregular' => 'Kozmin Pro Regular', + 'stsongstdlight' => 'STSong stdlight', + 'invalidfont' => 'Invalid' + ]; + $fontlist = $pdf->get_export_fontlist(); + $this->assertCount(2, $fontlist); + $this->assertArrayNotHasKey('freesans', $fontlist); + $this->assertArrayHasKey('kozminproregular', $fontlist); + $this->assertArrayHasKey('stsongstdlight', $fontlist); + $this->assertArrayNotHasKey('invalidfont', $fontlist); + } } diff --git a/mod/assign/feedback/editpdf/classes/document_services.php b/mod/assign/feedback/editpdf/classes/document_services.php index 739ac40d79579..4986b0a6478b0 100644 --- a/mod/assign/feedback/editpdf/classes/document_services.php +++ b/mod/assign/feedback/editpdf/classes/document_services.php @@ -645,6 +645,7 @@ protected static function get_downloadable_feedback_filename($assignment, $useri * @return stored_file */ public static function generate_feedback_document($assignment, $userid, $attemptnumber) { + global $CFG; $assignment = self::get_assignment_from_param($assignment); @@ -674,6 +675,20 @@ public static function generate_feedback_document($assignment, $userid, $attempt $pdf = new pdf(); + // Set fontname from course setting if it's enabled. + if (!empty($CFG->enablepdfexportfont)) { + $fontlist = $pdf->get_export_fontlist(); + // Load font from course if it's more than 1. + if (count($fontlist) > 1) { + $course = $assignment->get_course(); + if (!empty($course->pdfexportfont)) { + $pdf->set_export_font_name($course->pdfexportfont); + } + } else { + $pdf->set_export_font_name(current($fontlist)); + } + } + $fs = get_file_storage(); $stamptmpdir = make_temp_directory('assignfeedback_editpdf/stamps/' . self::hash($assignment, $userid, $attemptnumber)); $grade = $assignment->get_user_grade($userid, true, $attemptnumber); diff --git a/mod/assign/feedback/editpdf/classes/pdf.php b/mod/assign/feedback/editpdf/classes/pdf.php index 612d00007a910..fd187be70be51 100644 --- a/mod/assign/feedback/editpdf/classes/pdf.php +++ b/mod/assign/feedback/editpdf/classes/pdf.php @@ -50,6 +50,8 @@ class pdf extends TcpdfFpdi { 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'; @@ -81,15 +83,23 @@ class pdf extends TcpdfFpdi { * @return string */ private function get_export_font_name() { - global $CFG; - $fontname = 'freesans'; - if (!empty($CFG->pdfexportfont)) { - $fontname = $CFG->pdfexportfont; + 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 diff --git a/version.php b/version.php index a6956bfcedea5..3dc7255f8c7f4 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2023021700.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2023021700.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.2dev (Build: 20230217)'; // Human-friendly version name