1<?php 2/** 3 * LaTeX Rendering Class 4 * Copyright (C) 2003 Benjamin Zeiss <zeiss@math.uni-goettingen.de> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * -------------------------------------------------------------------- 20 * @author Benjamin Zeiss <zeiss@math.uni-goettingen.de> 21 * @version v0.8 22 * @package latexrender 23 * 24 */ 25 26class LatexRender { 27 28 // ==================================================================================== 29 // Variable Definitions 30 // ==================================================================================== 31 var $_picture_path = ""; 32 var $_picture_path_httpd = ""; 33 // i was too lazy to write mutator functions for every single program used 34 // just access it outside the class or change it here if nescessary 35 36 ///////////////////////////////////////// 37 ////// NOTE - DO NOT CHANGE THESE SETTINGS. USE THE CONFIG MANAGER 38 ////// IN DOKUWIKI INSTEAD; THESE ARE OVERWRITTEN. 39 var $_tmp_dir = ""; 40 var $_keep_tmp = false; // keep temporary files? (good for debug) 41 var $_latex_path = "latex"; 42 var $_dvips_path = "dvips"; 43 var $_convert_path = "convert"; 44 var $_identify_path = "identify"; 45 var $_xsize_limit = 1000; 46 var $_ysize_limit = 500; 47 var $_string_length_limit = 2000; 48 var $_image_format = "png"; //change to png if you prefer 49 //////////////////////////////////// 50 51 var $_font_size = 10; 52 var $_latexclass = "article"; //install extarticle class if you wish to have smaller font sizes 53 var $_tmp_filename; 54 // this most certainly needs to be extended. in the long term it is planned to use 55 // a positive list for more security. this is hopefully enough for now. i'd be glad 56 // to receive more bad tags ! 57 var $_latex_tags_blacklist = array( 58 "include","def","command","loop","repeat","open","toks","output","input", 59 "catcode","name","^^", 60 "\\every","\\errhelp","\\errorstopmode","\\scrollmode","\\nonstopmode","\\batchmode", 61 "\\read","\\write","csname","\\newhelp","\\uppercase", "\\lowercase","\\relax","\\aftergroup", 62 "\\afterassignment","\\expandafter","\\noexpand","\\special" 63 ); 64 var $_errorcode = 0; 65 var $_errorextra = ""; 66 var $_filename; 67 68 69 // ==================================================================================== 70 // constructor 71 // ==================================================================================== 72 73 /** 74 * Initializes the class 75 * 76 * @param string path where the rendered pictures should be stored 77 * @param string same path, but from the httpd chroot 78 */ 79 function __construct($picture_path,$picture_path_httpd,$tmp_dir) { 80 $this->_picture_path = $picture_path; 81 $this->_picture_path_httpd = $picture_path_httpd; 82 $this->_tmp_dir = $tmp_dir; 83 } 84 85 // ==================================================================================== 86 // public functions 87 // ==================================================================================== 88 89 /** 90 * Picture path Mutator function 91 * 92 * @param string sets the current picture path to a new location 93 */ 94 function setPicturePath($name) { 95 $this->_picture_path = $name; 96 } 97 98 /** 99 * Picture path Mutator function 100 * 101 * @returns the current picture path 102 */ 103 function getPicturePath() { 104 return $this->_picture_path; 105 } 106 107 /** 108 * Picture path HTTPD Mutator function 109 * 110 * @param string sets the current httpd picture path to a new location 111 */ 112 function setPicturePathHTTPD($name) { 113 $this->_picture_path_httpd = $name; 114 } 115 116 /** 117 * Picture path HTTPD Mutator function 118 * 119 * @returns the current picture path 120 */ 121 function getPicturePathHTTPD() { 122 return $this->_picture_path_httpd; 123 } 124 125 /** 126 * Tries to match the LaTeX Formula given as argument against the 127 * formula cache. If the picture has not been rendered before, it'll 128 * try to render the formula and drop it in the picture cache directory. 129 * 130 * @param string formula in LaTeX format 131 * @returns the webserver based URL to a picture which contains the 132 * requested LaTeX formula. If anything fails, the resultvalue is false. 133 */ 134 function getFormulaURL($latex_formula) { 135 // circumvent certain security functions of web-software which 136 // is pretty pointless right here 137 $latex_formula = preg_replace("/>/i", ">", $latex_formula); 138 $latex_formula = preg_replace("/</i", "<", $latex_formula); 139 140 $this->latex_document = $this->_preamble."\n".trim($latex_formula)."\n".$this->_postamble; 141 142 $formula_hash = md5($latex_formula); 143 144 $filename = "img".$formula_hash.'.'.$this->_image_format; 145 $full_path_filename = $this->getPicturePath()."/".$filename; 146 $this->_filename = $full_path_filename; 147 148 if (is_readable($full_path_filename)) { 149 return $this->getPicturePathHTTPD()."/".$filename; 150 } else { 151 // security filter: reject too-long formulas 152 if (strlen($latex_formula) > $this->_string_length_limit) { 153 $this->_errorcode = 1; 154 $this->_errorextra = ': '.strlen($latex_formula); 155 return false; 156 } 157 158 // security filter: try to match against LaTeX-Tags Blacklist 159 for ($i=0;$i<sizeof($this->_latex_tags_blacklist);$i++) { 160 if (stristr($latex_formula,$this->_latex_tags_blacklist[$i])) { 161 $this->_errorcode = 2; 162 return false; 163 } 164 } 165 166 // security checks assume correct formula, let's render it 167 if ($this->renderLatex($this->latex_document,$full_path_filename)) { 168 return $this->getPicturePathHTTPD().$filename; 169 } else { 170 // uncomment if required 171 // $this->_errorcode = 3; 172 return false; 173 } 174 } 175 } 176 177 // ==================================================================================== 178 // private functions 179 // ==================================================================================== 180 181 /** 182 * returns the dimensions of a picture file using 'identify' of the 183 * imagemagick tools. The resulting array can be adressed with either 184 * $dim[0] / $dim[1] or $dim["x"] / $dim["y"] 185 * 186 * @param string path to a picture 187 * @returns array containing the picture dimensions 188 */ 189 function getDimensions($filename) { 190 $output=$this->myexec($this->_identify_path." ".$filename, $status); 191 $result=explode(" ",$output); 192 $dim=explode("x",$result[2]); 193 $dim["x"] = $dim[0]; 194 $dim["y"] = $dim[1]; 195 196 return $dim; 197 } 198 199 /** 200 * Renders a LaTeX formula by the using the following method: 201 * - write the formula into a wrapped tex-file in a temporary directory 202 * and change to it 203 * - Create a DVI file using latex (tetex) 204 * - Convert DVI file to Postscript (PS) using dvips (tetex) 205 * - convert, trim and add transparancy by using 'convert' from the 206 * imagemagick package. 207 * - Save the resulting image to the picture cache directory using an 208 * md5 hash as filename. Already rendered formulas can be found directly 209 * this way. 210 * 211 * @param string LaTeX formula 212 * @returns true if the picture has been successfully saved to the picture 213 * cache directory 214 */ 215 function renderLatex($latex_document,$destination) { 216 217 $current_dir = getcwd(); 218 219 chdir($this->_tmp_dir); 220 221 $this->_tmp_filename = md5(rand().$destination); 222 223 $this->_cmdout = " >> ".$this->_tmp_filename.".cmd 2>&1"; 224 // create temporary latex file 225 $fp = fopen($this->_tmp_dir."/".$this->_tmp_filename.".tex","w"); 226 fputs($fp,$latex_document); 227 fclose($fp); 228 229 // create temporary dvi file 230 $command = $this->_latex_path." --interaction=nonstopmode ".$this->_tmp_filename.".tex"; 231 $this->myexec($command,$status_latex); 232 233 // LaTeXing only fails if DVI doesn't exist. - let's ignore some minor errors. 234 if (!file_exists($this->_tmp_filename.".dvi")) 235 { 236 if( ! $this->_keep_tmp) 237 $this->cleanTemporaryDirectory(); 238 chdir($current_dir); 239 $this->_errorcode = 4; /// Error 4: latexing failed 240 return false; 241 } 242 243 // convert dvi file to postscript using dvips 244 // -E removed from dvips 245 $command = $this->_dvips_path." ".$this->_tmp_filename.".dvi"." -o ".$this->_tmp_filename.".ps"; 246 $this->myexec($command,$status_dvips); 247 248 249 // imagemagick convert ps to image and trim picture 250 $command = $this->_convert_path." ".$this->_tmp_filename.".ps ". 251 $this->_tmp_filename.".".$this->_image_format; 252 $this->myexec($command,$status_convert); 253 254 255 if ($status_dvips || $status_convert) { 256 if( ! $this->_keep_tmp) 257 $this->cleanTemporaryDirectory(); 258 chdir($current_dir); 259 $this->_errorcode = 6; 260 return false; 261 } 262 263 // test picture for correct dimensions 264 $dim = $this->getDimensions($this->_tmp_filename.".".$this->_image_format); 265 266 if ( ($dim["x"] > $this->_xsize_limit) or ($dim["y"] > $this->_ysize_limit)) { 267 if( ! $this->_keep_tmp) 268 $this->cleanTemporaryDirectory(); 269 chdir($current_dir); 270 $this->_errorcode = 5; // image too big. 271 $this->_errorextra = ": " . $dim["x"] . "x" . $dim["y"]; 272 return false; 273 } 274 275 // copy temporary formula file to cahed formula directory 276 $status_code = copy($this->_tmp_filename.".".$this->_image_format,$destination); 277 chdir($current_dir); 278 279 if( ! $this->_keep_tmp) 280 $this->cleanTemporaryDirectory(); 281 282 if (!$status_code) { $this->_errorcode = 7; return false; } 283 284 return true; 285 } 286 287 //// Run command and append it to _cmdoutput if that variable exists. (for debug). 288 function myexec($cmd,&$status) { 289 $cmd = "$cmd 2>&1"; 290 $lastline = exec($cmd,$output,$status); 291 292// //strip trailing empty lines from output 293// for($i = count($output)-1 ; $i > 0 ; $i -= 1) 294// if($output[$i]) break; 295// $lastline = $output[$i]; 296 297 if(isset($this->_cmdoutput)) 298 $this->_cmdoutput .= "\n>>>>> $cmd\n".trim(implode(PHP_EOL,$output)).PHP_EOL." --- exit status ".$status; 299 300 return $lastline; 301 } 302 303 /** 304 * Cleans the temporary directory 305 */ 306 function cleanTemporaryDirectory() { 307// $current_dir = getcwd(); 308// chdir($this->_tmp_dir); 309 310 @unlink($this->_tmp_dir."/".$this->_tmp_filename.".tex"); 311 @unlink($this->_tmp_dir."/".$this->_tmp_filename.".aux"); 312 @unlink($this->_tmp_dir."/".$this->_tmp_filename.".log"); 313 @unlink($this->_tmp_dir."/".$this->_tmp_filename.".dvi"); 314 @unlink($this->_tmp_dir."/".$this->_tmp_filename.".ps"); 315 @unlink($this->_tmp_dir."/".$this->_tmp_filename.".".$this->_image_format); 316 317// chdir($current_dir); 318 } 319 320} 321