7eb522e5d68fca07a7fc72e3bee78c08e7292ea8
[moodle.git] / filter / tex / latex.php
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         /**
14          * Constructor - create temporary directories and build paths to
15          * external 'helper' binaries.
16          * Other platforms could/should be added
17          */
18         public function __construct() {
19             global $CFG;
21             // construct directory structure
22             $this->temp_dir = $CFG->tempdir . "/latex";
23             make_temp_directory('latex');
24         }
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         }
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         }
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             global $CFG;
53             $formula = filter_tex_sanitize_formula($formula);
55             // $fontsize don't affects to formula's size. $density can change size
56             $doc =  "\\documentclass[{$fontsize}pt]{article}\n";
57             $doc .= get_config('filter_tex', 'latexpreamble');
58             $doc .= "\\pagestyle{empty}\n";
59             $doc .= "\\begin{document}\n";
60 //dlnsk            $doc .= "$ {$formula} $\n";
61             if (preg_match("/^[[:space:]]*\\\\begin\\{(gather|align|alignat|multline).?\\}/i",$formula)) {
62                $doc .= "$formula\n";
63             } else {
64                $doc .= "$ {$formula} $\n";
65             }
66             $doc .= "\\end{document}\n";
67             return $doc;
68         }
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         }
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 ) {
100             global $CFG;
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, " '\""));
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';
115             }
116             $filename = str_replace(".{$convertformat}", '', $filename);
117             $tex = "{$this->temp_dir}/$filename.tex";
118             $dvi = "{$this->temp_dir}/$filename.dvi";
119             $ps  = "{$this->temp_dir}/$filename.ps";
120             $img = "{$this->temp_dir}/$filename.{$convertformat}";
122             // turn the latex doc into a .tex file in the temp area
123             $fh = fopen( $tex, 'w' );
124             fputs( $fh, $doc );
125             fclose( $fh );
127             // run latex on document
128             $command = "$pathlatex --interaction=nonstopmode --halt-on-error $tex";
129             chdir( $this->temp_dir );
130             if ($this->execute($command, $log)) { // It allways False on Windows
131 //                return false;
132             }
134             // run dvips (.dvi to .ps)
135             $pathdvips = escapeshellarg(trim(get_config('filter_tex', 'pathdvips'), " '\""));
136             $command = "$pathdvips -E $dvi -o $ps";
137             if ($this->execute($command, $log )) {
138                 return false;
139             }
141             // Run convert on document (.ps to .gif/.png) or run dvisvgm (.ps to .svg).
142             if ($background) {
143                 $bg_opt = "-transparent \"$background\""; // Makes transparent background
144             } else {
145                 $bg_opt = "";
146             }
147             if ($convertformat == 'svg') {
148                 $pathdvisvgm = escapeshellarg(trim(get_config('filter_tex', 'pathdvisvgm'), " '\""));
149                 $command = "$pathdvisvgm -E $ps -o $img";
150             } else {
151                 $pathconvert = escapeshellarg(trim(get_config('filter_tex', 'pathconvert'), " '\""));
152                 $command = "$pathconvert -density $density -trim $bg_opt $ps $img";
153             }
154             if ($this->execute($command, $log )) {
155                 return false;
156             }
158             return $img;
159         }
161         /**
162          * Delete files created in temporary area
163          * Don't forget to copy the final gif/png before calling this
164          * @param string $filename file base (no extension)
165          */
166         function clean_up( $filename ) {
167             global $CFG;
169             unlink( "{$this->temp_dir}/$filename.tex" );
170             unlink( "{$this->temp_dir}/$filename.dvi" );
171             unlink( "{$this->temp_dir}/$filename.ps" );
172             $convertformat = get_config('filter_tex', 'convertformat');
173             unlink( "{$this->temp_dir}/$filename.{$convertformat}" );
174             unlink( "{$this->temp_dir}/$filename.aux" );
175             unlink( "{$this->temp_dir}/$filename.log" );
176             return;
177         }
179     }