1 |
efrain |
1 |
<?php
|
|
|
2 |
// latex.php
|
|
|
3 |
// render TeX stuff using latex - this will not work on all platforms
|
|
|
4 |
// or configurations. Only works on Linux and Mac with appropriate
|
|
|
5 |
// software installed.
|
|
|
6 |
// Much of this inspired/copied from Benjamin Zeiss' work
|
|
|
7 |
|
|
|
8 |
class latex {
|
|
|
9 |
|
|
|
10 |
var $temp_dir;
|
|
|
11 |
var $error;
|
|
|
12 |
|
|
|
13 |
/** @var bool To store value of supported_platform. */
|
|
|
14 |
protected $supported_platform;
|
|
|
15 |
|
|
|
16 |
/**
|
|
|
17 |
* Constructor - create temporary directories and build paths to
|
|
|
18 |
* external 'helper' binaries.
|
|
|
19 |
* Other platforms could/should be added
|
|
|
20 |
*/
|
|
|
21 |
public function __construct() {
|
|
|
22 |
// Construct directory structure.
|
|
|
23 |
$this->temp_dir = make_request_directory();
|
|
|
24 |
}
|
|
|
25 |
|
|
|
26 |
/**
|
|
|
27 |
* Old syntax of class constructor. Deprecated in PHP7.
|
|
|
28 |
*
|
|
|
29 |
* @deprecated since Moodle 3.1
|
|
|
30 |
*/
|
|
|
31 |
public function latex() {
|
|
|
32 |
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
|
|
|
33 |
self::__construct();
|
|
|
34 |
}
|
|
|
35 |
|
|
|
36 |
/**
|
|
|
37 |
* Accessor function for support_platform field.
|
|
|
38 |
* @return boolean value of supported_platform
|
|
|
39 |
*/
|
|
|
40 |
function supported() {
|
|
|
41 |
return $this->supported_platform;
|
|
|
42 |
}
|
|
|
43 |
|
|
|
44 |
/**
|
|
|
45 |
* Turn the bit of TeX into a valid latex document
|
|
|
46 |
* @param string $forumula the TeX formula
|
|
|
47 |
* @param int $fontsize the font size
|
|
|
48 |
* @return string the latex document
|
|
|
49 |
*/
|
|
|
50 |
function construct_latex_document($formula, $fontsize = 12) {
|
|
|
51 |
// $fontsize don't affects to formula's size. $density can change size
|
|
|
52 |
$doc = "\\documentclass[{$fontsize}pt]{article}\n";
|
|
|
53 |
$doc .= get_config('filter_tex', 'latexpreamble');
|
|
|
54 |
$doc .= "\\pagestyle{empty}\n";
|
|
|
55 |
$doc .= "\\begin{document}\n";
|
|
|
56 |
if (preg_match("/^[[:space:]]*\\\\begin\\{(gather|align|alignat|multline).?\\}/i", $formula)) {
|
|
|
57 |
$doc .= "$formula\n";
|
|
|
58 |
} else {
|
|
|
59 |
$doc .= "$ {$formula} $\n";
|
|
|
60 |
}
|
|
|
61 |
$doc .= "\\end{document}\n";
|
|
|
62 |
|
|
|
63 |
// Sanitize the whole document (rather than just the formula) to make sure no one can bypass sanitization
|
|
|
64 |
// by using \newcommand in preamble to give an alias to a blocked command.
|
|
|
65 |
$doc = filter_tex_sanitize_formula($doc);
|
|
|
66 |
|
|
|
67 |
return $doc;
|
|
|
68 |
}
|
|
|
69 |
|
|
|
70 |
/**
|
|
|
71 |
* execute an external command, with optional logging
|
|
|
72 |
* @param string $command command to execute
|
|
|
73 |
* @param file $log valid open file handle - log info will be written to this file
|
|
|
74 |
* @return return code from execution of command
|
|
|
75 |
*/
|
|
|
76 |
function execute($command, $log=null ) {
|
|
|
77 |
$output = array();
|
|
|
78 |
exec( $command, $output, $return_code );
|
|
|
79 |
if ($log) {
|
|
|
80 |
fwrite( $log, "COMMAND: $command \n" );
|
|
|
81 |
$outputs = implode( "\n", $output );
|
|
|
82 |
fwrite( $log, "OUTPUT: $outputs \n" );
|
|
|
83 |
fwrite( $log, "RETURN_CODE: $return_code\n " );
|
|
|
84 |
}
|
|
|
85 |
return $return_code;
|
|
|
86 |
}
|
|
|
87 |
|
|
|
88 |
/**
|
|
|
89 |
* Render TeX string into gif/png
|
|
|
90 |
* @param string $formula TeX formula
|
|
|
91 |
* @param string $filename filename for output (including extension)
|
|
|
92 |
* @param int $fontsize font size
|
|
|
93 |
* @param int $density density value for .ps to .gif/.png conversion
|
|
|
94 |
* @param string $background background color (e.g, #FFFFFF).
|
|
|
95 |
* @param file $log valid open file handle for optional logging (debugging only)
|
|
|
96 |
* @return bool true if successful
|
|
|
97 |
*/
|
|
|
98 |
function render($formula, $filename, $fontsize=12, $density=240, $background='', $log=null ) {
|
|
|
99 |
|
|
|
100 |
global $CFG;
|
|
|
101 |
|
|
|
102 |
// quick check - will this work?
|
|
|
103 |
$pathlatex = get_config('filter_tex', 'pathlatex');
|
|
|
104 |
if (empty($pathlatex)) {
|
|
|
105 |
return false;
|
|
|
106 |
}
|
|
|
107 |
$pathlatex = escapeshellarg(trim($pathlatex, " '\""));
|
|
|
108 |
|
|
|
109 |
$doc = $this->construct_latex_document( $formula, $fontsize );
|
|
|
110 |
|
|
|
111 |
// construct some file paths
|
|
|
112 |
$convertformat = get_config('filter_tex', 'convertformat');
|
|
|
113 |
if (!strpos($filename, ".{$convertformat}")) {
|
|
|
114 |
$convertformat = 'png';
|
|
|
115 |
}
|
|
|
116 |
$filename = str_replace(".{$convertformat}", '', $filename);
|
|
|
117 |
$tex = "$filename.tex"; // Absolute paths won't work with openin_any = p setting.
|
|
|
118 |
$dvi = "{$this->temp_dir}/$filename.dvi";
|
|
|
119 |
$ps = "{$this->temp_dir}/$filename.ps";
|
|
|
120 |
$img = "{$this->temp_dir}/$filename.{$convertformat}";
|
|
|
121 |
|
|
|
122 |
// Change directory to temp dir so that we can work with relative paths.
|
|
|
123 |
chdir($this->temp_dir);
|
|
|
124 |
|
|
|
125 |
// turn the latex doc into a .tex file in the temp area
|
|
|
126 |
$fh = fopen( $tex, 'w' );
|
|
|
127 |
fputs( $fh, $doc );
|
|
|
128 |
fclose( $fh );
|
|
|
129 |
|
|
|
130 |
// run latex on document
|
|
|
131 |
$command = "$pathlatex --interaction=nonstopmode --halt-on-error $tex";
|
|
|
132 |
|
|
|
133 |
if ($this->execute($command, $log)) { // It allways False on Windows
|
|
|
134 |
// return false;
|
|
|
135 |
}
|
|
|
136 |
|
|
|
137 |
// run dvips (.dvi to .ps)
|
|
|
138 |
$pathdvips = escapeshellarg(trim(get_config('filter_tex', 'pathdvips'), " '\""));
|
|
|
139 |
$command = "$pathdvips -q -E $dvi -o $ps";
|
|
|
140 |
if ($this->execute($command, $log )) {
|
|
|
141 |
return false;
|
|
|
142 |
}
|
|
|
143 |
|
|
|
144 |
// Run convert on document (.ps to .gif/.png) or run dvisvgm (.ps to .svg).
|
|
|
145 |
if ($background) {
|
|
|
146 |
$bg_opt = "-transparent \"$background\""; // Makes transparent background
|
|
|
147 |
} else {
|
|
|
148 |
$bg_opt = "";
|
|
|
149 |
}
|
|
|
150 |
if ($convertformat == 'svg') {
|
|
|
151 |
$pathdvisvgm = escapeshellarg(trim(get_config('filter_tex', 'pathdvisvgm'), " '\""));
|
|
|
152 |
$command = "$pathdvisvgm -E $ps -o $img";
|
|
|
153 |
} else {
|
|
|
154 |
$pathconvert = escapeshellarg(trim(get_config('filter_tex', 'pathconvert'), " '\""));
|
|
|
155 |
$command = "$pathconvert -density $density -trim $bg_opt $ps $img";
|
|
|
156 |
}
|
|
|
157 |
if ($this->execute($command, $log )) {
|
|
|
158 |
return false;
|
|
|
159 |
}
|
|
|
160 |
|
|
|
161 |
return $img;
|
|
|
162 |
}
|
|
|
163 |
}
|