diff --git a/admin/settings/courses.php b/admin/settings/courses.php index e00d8317364..1acab26cfb3 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 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { $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 97fb85d8f15..962a8c821b8 100644 --- a/admin/settings/language.php +++ b/admin/settings/language.php @@ -18,6 +18,8 @@ if ($hassiteconfig) { $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 8f82b94d3fc..2c94486690e 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -459,7 +459,7 @@ class backup_course_structure_step extends backup_structure_step { '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 b1b9768079a..7b0382a39eb 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -1933,6 +1933,9 @@ class restore_course_structure_step extends restore_structure_step { $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 e81cedced28..3d09fde245d 100644 --- a/config-dist.php +++ b/config-dist.php @@ -655,8 +655,12 @@ $CFG->admin = 'admin'; // 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 e50f11bdf6b..64260851ba4 100644 --- a/course/classes/external/course_summary_exporter.php +++ b/course/classes/external/course_summary_exporter.php @@ -117,6 +117,10 @@ class course_summary_exporter extends \core\external\exporter { '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 0c6d9028ff1..fdcdad7f299 100644 --- a/course/edit_form.php +++ b/course/edit_form.php @@ -4,6 +4,7 @@ defined('MOODLE_INTERNAL') || die; 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 @@ class course_edit_form extends moodleform { $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 372c73a045c..84f961f14b4 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -621,6 +621,7 @@ class core_course_external extends external_api { // 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 9608ce599ce..0a57d990575 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 1b2e5e677f5..0c17b7a9250 100644 --- a/course/tests/courselib_test.php +++ b/course/tests/courselib_test.php @@ -5751,7 +5751,7 @@ class courselib_test extends advanced_testcase { '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 bc34156ec17..79bdbef4118 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -577,6 +577,8 @@ $string['enableglobalsearch_desc'] = 'If enabled, data will be indexed and synch $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 f25a35ed362..58af0966b83 100644 --- a/lang/en/course.php +++ b/lang/en/course.php @@ -108,6 +108,8 @@ $string['noteachinginfomessage'] = 'Hi {$a->userfirstname}, $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 baa18195b80..617dd9da70d 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 f1f95932845..b1367197917 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2984,5 +2984,19 @@ privatefiles,moodle|/user/files.php'; 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 03b011e2ba2..64a76c6fd44 100644 --- a/lib/pdflib.php +++ b/lib/pdflib.php @@ -250,4 +250,32 @@ class pdf extends TCPDF { 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 fea0e2dc34b..ec57baff78e 100644 --- a/lib/tests/pdflib_test.php +++ b/lib/tests/pdflib_test.php @@ -66,4 +66,35 @@ class pdflib_test extends \advanced_testcase { $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 739ac40d795..4986b0a6478 100644 --- a/mod/assign/feedback/editpdf/classes/document_services.php +++ b/mod/assign/feedback/editpdf/classes/document_services.php @@ -645,6 +645,7 @@ EOD; * @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 @@ EOD; $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 612d00007a9..fd187be70be 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 a6956bfcede..3dc7255f8c7 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