2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * TeX filter library functions.
22 * @copyright 2004 Zbigniew Fiedorowicz fiedorow@math.ohio-state.edu
23 * Originally based on code provided by Bruno Vernier bruno@vsbeducation.ca
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') ||
die();
29 function filter_tex_get_executable($debug=false) {
32 if ((PHP_OS
== "WINNT") ||
(PHP_OS
== "WIN32") ||
(PHP_OS
== "Windows")) {
33 return "$CFG->dirroot/filter/tex/mimetex.exe";
36 if ($pathmimetex = get_config('filter_tex', 'pathmimetex')) {
37 if (is_executable($pathmimetex)) {
40 throw new \
moodle_exception('mimetexnotexecutable', 'error');
44 $custom_commandpath = "$CFG->dirroot/filter/tex/mimetex";
45 if (file_exists($custom_commandpath)) {
46 if (is_executable($custom_commandpath)) {
47 return $custom_commandpath;
49 throw new \
moodle_exception('mimetexnotexecutable', 'error');
54 case "Darwin": return "$CFG->dirroot/filter/tex/mimetex.darwin";
55 case "FreeBSD": return "$CFG->dirroot/filter/tex/mimetex.freebsd";
57 if (php_uname('m') == 'aarch64') {
58 return "$CFG->dirroot/filter/tex/mimetex.linux.aarch64";
61 return "$CFG->dirroot/filter/tex/mimetex.linux";
64 throw new \
moodle_exception('mimetexisnotexist', 'error');
68 * Check the formula expression against the list of denied keywords.
70 * List of allowed could be more complete but also harder to maintain.
72 * @param string $texexp Formula expression to check.
73 * @return string Formula expression with denied keywords replaced with 'forbiddenkeyword'.
75 function filter_tex_sanitize_formula(string $texexp): string {
78 'include', 'command', 'loop', 'repeat', 'open', 'toks', 'output',
79 'input', 'catcode', 'name', '^^',
80 '\def', '\edef', '\gdef', '\xdef',
81 '\every', '\errhelp', '\errorstopmode', '\scrollmode', '\nonstopmode',
82 '\batchmode', '\read', '\write', 'csname', '\newhelp', '\uppercase',
83 '\lowercase', '\relax', '\aftergroup',
84 '\afterassignment', '\expandafter', '\noexpand', '\special',
85 '\let', '\futurelet', '\else', '\fi', '\chardef', '\makeatletter', '\afterground',
86 '\noexpand', '\line', '\mathcode', '\item', '\section', '\mbox', '\declarerobustcommand',
90 $allowlist = ['inputenc'];
92 // Prepare the denylist for regular expression.
93 $denylist = array_map(function($value){
94 return '/' . preg_quote($value, '/') . '/i';
97 // Prepare the allowlist for regular expression.
98 $allowlist = array_map(function($value){
99 return '/\bforbiddenkeyword_(' . preg_quote($value, '/') . ')\b/i';
102 // First, mangle all denied words.
103 $texexp = preg_replace_callback($denylist,
105 return 'forbiddenkeyword_' . $matches[0];
110 // Then, change back the allowed words.
111 $texexp = preg_replace_callback($allowlist,
121 function filter_tex_get_cmd($pathname, $texexp) {
122 $texexp = filter_tex_sanitize_formula($texexp);
123 $texexp = escapeshellarg($texexp);
124 $executable = filter_tex_get_executable(false);
126 if ((PHP_OS
== "WINNT") ||
(PHP_OS
== "WIN32") ||
(PHP_OS
== "Windows")) {
127 $executable = str_replace(' ', '^ ', $executable);
128 return "$executable ++ -e \"$pathname\" -- $texexp";
131 return "\"$executable\" -e \"$pathname\" -- $texexp";
136 * Purge all caches when settings changed.
138 function filter_tex_updatedcallback($name) {
140 reset_text_filters_cache();
142 if (file_exists("$CFG->dataroot/filter/tex")) {
143 remove_dir("$CFG->dataroot/filter/tex");
145 if (file_exists("$CFG->dataroot/filter/algebra")) {
146 remove_dir("$CFG->dataroot/filter/algebra");
148 if (file_exists("$CFG->tempdir/latex")) {
149 remove_dir("$CFG->tempdir/latex");
152 $DB->delete_records('cache_filters', array('filter'=>'tex'));
153 $DB->delete_records('cache_filters', array('filter'=>'algebra'));
155 $pathlatex = get_config('filter_tex', 'pathlatex');
156 if ($pathlatex === false) {
157 // detailed settings not present yet
161 $pathlatex = trim($pathlatex, " '\"");
162 $pathdvips = trim(get_config('filter_tex', 'pathdvips'), " '\"");
163 $pathconvert = trim(get_config('filter_tex', 'pathconvert'), " '\"");
164 $pathdvisvgm = trim(get_config('filter_tex', 'pathdvisvgm'), " '\"");
166 $supportedformats = array('gif');
167 if ((is_file($pathlatex) && is_executable($pathlatex)) &&
168 (is_file($pathdvips) && is_executable($pathdvips))) {
169 if (is_file($pathconvert) && is_executable($pathconvert)) {
170 $supportedformats[] = 'png';
172 if (is_file($pathdvisvgm) && is_executable($pathdvisvgm)) {
173 $supportedformats[] = 'svg';
176 if (!in_array(get_config('filter_tex', 'convertformat'), $supportedformats)) {
177 set_config('convertformat', array_pop($supportedformats), 'filter_tex');