weekly release 5.0dev
[moodle.git] / filter / tex / latex.php
blob7c6b3665b053d60ccb3c2e00f91a99da04ac9909
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
8 class latex {
10 var $temp_dir;
11 var $error;
13 /** @var bool To store value of supported_platform. */
14 protected $supported_platform;
16 /**
17 * Constructor - create temporary directories and build paths to
18 * external 'helper' binaries.
19 * Other platforms could/should be added
21 public function __construct() {
22 // Construct directory structure.
23 $this->temp_dir = make_request_directory();
26 /**
27 * Old syntax of class constructor. Deprecated in PHP7.
29 * @deprecated since Moodle 3.1
31 public function latex() {
32 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
33 self::__construct();
36 /**
37 * Accessor function for support_platform field.
38 * @return boolean value of supported_platform
40 function supported() {
41 return $this->supported_platform;
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
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";
61 $doc .= "\\end{document}\n";
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);
67 return $doc;
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
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 " );
85 return $return_code;
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
98 function render($formula, $filename, $fontsize=12, $density=240, $background='', $log=null ) {
100 global $CFG;
102 // quick check - will this work?
103 $pathlatex = get_config('filter_tex', 'pathlatex');
104 if (empty($pathlatex)) {
105 return false;
107 $pathlatex = escapeshellarg(trim($pathlatex, " '\""));
109 $doc = $this->construct_latex_document( $formula, $fontsize );
111 // construct some file paths
112 $convertformat = get_config('filter_tex', 'convertformat');
113 if (!strpos($filename, ".{$convertformat}")) {
114 $convertformat = 'png';
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}";
122 // Change directory to temp dir so that we can work with relative paths.
123 chdir($this->temp_dir);
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 );
130 // run latex on document
131 $command = "$pathlatex --interaction=nonstopmode --halt-on-error $tex";
133 if ($this->execute($command, $log)) { // It allways False on Windows
134 // return false;
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;
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 = "";
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";
157 if ($this->execute($command, $log )) {
158 return false;
161 return $img;