Jump to content

User:WimW/math

From mediawiki.org

introduction

[edit]

I have used this math.php in MediaWiki 1.15 when it still was part of the mediaWiki system, included in the "included"-folder. In the next system I have set up (1.22), it had been - quite logically - been promoted to become a regular extension. At the same time, it had become quite impossible to use on Windows.

Note that there may be a good reason for this:

The reason that MediaWiki normally renders math via an intermediate Ocaml script, is because TeX is not inherently safe. The intermediate texvc script removes all unsafe markup. Extension:TrustedMath

Since both my wiki-systems are not open for editing by unidentified contributors, I personally dare to accept that risk.

I therefore took the old math unit and wrapped it into the example tag extension. Source code below.

installation

[edit]
  • copy the math.php unit as below into your extensions directory
  • add the following line to LocalSettings.php:
require_once "$IP/extensions/math.php";
  • add the following lines to LocalSettings.php and correct them according to your local installation paths:
$wgMathDirectory 	= 'D:/internet/wiki/math';
$wgMathPath 		= '/wiki/math/';
$wgTmpDirectory   	= "D:/internet/wiki/temp";

#ImageMagick
$wgImageMagickConvertCommand 	= "C:/xampp/ImageMagick/convert";
$wgImageMagickIdentifyCommand   = 'C:/xampp/ImageMagick/identify.exe';

#Tex
$wgLaTexCommand                 = 'C:/xampp/miktex/miktex/bin/latex.exe';
$wgDvipsCommand                 = 'C:/xampp/miktex/miktex/bin/dvips.exe';

Note that all my paths point to C:\xampp\ect. This is no longer a standard xampp-installation, but I have lost track what is and what isn't during my countless tries to get texvc to work.


source code

[edit]

math.php

<?php
/** adapted by W. de Winter, Alterra, Wageningen - January 2014
* 	Now functions as a regular tag-extension, suiteable for Media Wiki 1.22
*	Required variables in LocalSettings.php (& example values):
*       $wgMathDirectory 	= 'D:/internet/wiki/math';
*       $wgMathPath 		= '/wiki/math/';
*       $wgTmpDirectory   	= "D:/internet/wiki/temp";
*       #ImageMagick
*       $wgImageMagickConvertCommand 	= "C:/xampp/ImageMagick/convert";
*       $wgImageMagickIdentifyCommand   = 'C:/xampp/ImageMagick/identify.exe'; 
*       #Tex
*       $wgLaTexCommand                 = 'C:/xampp/miktex/miktex/bin/latex.exe';
*       $wgDvipsCommand                 = 'C:/xampp/miktex/miktex/bin/dvips.exe';
**/
/** replaces original file as advised by
* http://www.mediawiki.org/wiki/Manual:Running_MediaWiki_on_Windows#Mathematics_Support
*/ 
/**
 * LaTeX Rendering Class
 * Copyright (C) 2003  Benjamin Zeiss <zeiss@math.uni-goettingen.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * --------------------------------------------------------------------
 * @author Benjamin Zeiss <zeiss@math.uni-goettingen.de>
 * @version v0.8
 * @package latexrender
 *
 */
 
/**
 * Error code:
 * 1. Too long formulas
 * 2. formula contains tags in the blacklist
 * 3. formula incorrect, can't be render
 * 4. can't exec tex command
 *    maybe directory unwritable,can't create temporary files
 * 6. formula image too big
 * 7. can't copy image file to cahed formula directory
 *    maybe ImageMagick fail
 */
 

 
$wgHooks['ParserFirstCallInit'][] = 'wfSampleParserInit';
 
// Hook our callback function into the parser
function wfSampleParserInit( Parser $parser ) {
	// When the parser sees the <sample> tag, it executes 
	// the wfSampleRender function (see below)
	$parser->setHook( 'math', 'wfSampleRender' );
        // Always return true from this function. The return value does not denote
        // success or otherwise have meaning - it just must always be true.
	return true;
}
 
// Execute 
function wfSampleRender( $input, array $args, Parser $parser, PPFrame $frame ) {
	
	$math = new MathRenderer();
	$html = $math->renderMath($input);
	return $html;
}


 
class LatexRender {
 
    // ====================================================================================
    // Variable Definitions
    // ====================================================================================
    var $_picture_path = "";
    var $_picture_path_httpd = "";
    var $_tmp_dir = "";
    // i was too lazy to write mutator functions for every single program used
    // just access it outside the class or change it here if nescessary
    var $_latex_path    = 'latex.exe';
    var $_dvips_path    = 'dvips.exe';
    var $_convert_path  = 'convert.exe';
    var $_identify_path = 'identify.exe';
 
    var $_formula_density = 120;
    var $_xsize_limit = 700;
    var $_ysize_limit = 700;
    var $_string_length_limit = 800;
    var $_font_size = 10;
    var $_latexclass = "article"; //install extarticle class if you wish to have smaller font sizes
    var $_tmp_filename;
        var $_image_format = "png"; //change to gif if you prefer but it's not clear
    // this most certainly needs to be extended. in the long term it is planned to use
    // a positive list for more security. this is hopefully enough for now. i'd be glad
    // to receive more bad tags !
    var $_latex_tags_blacklist = array(
        "include","def","command","loop","repeat","open","toks","output","input",
        "catcode","name","^^",
        "\\every","\\errhelp","\\errorstopmode","\\scrollmode","\\nonstopmode","\\batchmode",
        "\\read","\\write","csname","\\newhelp","\\uppercase", "\\lowercase","\\relax","\\aftergroup",
        "\\afterassignment","\\expandafter","\\noexpand","\\special"
        );
    var $_errorcode = 0;
        var $_errorextra = "";
 
 
    // ====================================================================================
    // constructor
    // ====================================================================================
 
    /**
     * Initializes the class
     *
     * @param string path where the rendered pictures should be stored
     * @param string same path, but from the httpd chroot
     */
    function LatexRender($picture_path,$picture_path_httpd,$tmp_dir) {
        $this->_picture_path = $picture_path;
        $this->_picture_path_httpd = $picture_path_httpd;
        $this->_tmp_dir = $tmp_dir;
        $this->_tmp_filename = md5(rand());
    }
 
    // ====================================================================================
    // public functions
    // ====================================================================================
 
    /**
     * Picture path Mutator function
     *
     * @param string sets the current picture path to a new location
     */
    function setPicturePath($name) {
        $this->_picture_path = $name;
    }
 
    /**
     * Picture path Mutator function
     *
     * @returns the current picture path
     */
    function getPicturePath() {
        return $this->_picture_path;
    }
 
    /**
     * Picture path HTTPD Mutator function
     *
     * @param string sets the current httpd picture path to a new location
     */
    function setPicturePathHTTPD($name) {
        $this->_picture_path_httpd = $name;
    }
 
    /**
     * Picture path HTTPD Mutator function
     *
     * @returns the current picture path
     */
    function getPicturePathHTTPD() {
        return $this->_picture_path_httpd;
    }
 
    /**
     * Tries to match the LaTeX Formula given as argument against the
     * formula cache. If the picture has not been rendered before, it'll
     * try to render the formula and drop it in the picture cache directory.
     *
     * @param string formula in LaTeX format
     * @returns the webserver based URL to a picture which contains the
     * requested LaTeX formula. If anything fails, the resultvalue is false.
     */
    function getFormulaURL($latex_formula) {
        // circumvent certain security functions of web-software which
        // is pretty pointless right here
 
        $latex_formula = preg_replace("/>/i", ">", $latex_formula);
        $latex_formula = preg_replace("/</i", "<", $latex_formula);
 
        $formula_hash = md5($latex_formula);
 
        $filename = 'math-' . $formula_hash.".".$this->_image_format;
        $full_path_filename = $this->getPicturePath()."/".$filename;
 
        if (is_file($full_path_filename)) {
            return $this->getPicturePathHTTPD()."/".$filename;
        } else {
            // security filter: reject too long formulas
            if (strlen($latex_formula) > $this->_string_length_limit) {
                $this->_errorcode = 1;
                return false;
            }
 
            // security filter: try to match against LaTeX-Tags Blacklist
            for ($i=0;$i<sizeof($this->_latex_tags_blacklist);$i++) {
                if (stristr($latex_formula,$this->_latex_tags_blacklist[$i])) {
                        $this->_errorcode = 2;
                    return false;
                }
            }
 
            // security checks assume correct formula, let's render it
            if ($this->renderLatex($latex_formula)) {
               return $this->getPicturePathHTTPD()."/".$filename;
            } else {
//                $this->_errorcode = 3;
//				$this->_errorextra = 
                return false;
            }
        }
    }
 
    // ====================================================================================
    // private functions
    // ====================================================================================
 
    /**
     * wraps a minimalistic LaTeX document around the formula and returns a string
     * containing the whole document as string. Customize if you want other fonts for
     * example.
     *
     * @param string formula in LaTeX format
     * @returns minimalistic LaTeX document containing the given formula
     */
    function wrap_formula($latex_formula) {
#        $string  = "\documentclass[".$this->_font_size."pt]{".$this->_latexclass."}\n";
#        $string .= "\usepackage[latin1]{inputenc}\n";
        $string  = "\documentclass{".$this->_latexclass."}\n";
        $string .= "\usepackage{amsmath}\n";
        $string .= "\usepackage{amsfonts}\n";
        $string .= "\usepackage{amssymb}\n";
        $string .= "\pagestyle{empty}\n";
        $string .= "\begin{document}\n";
        $string .= "$".$latex_formula."$\n";
        $string .= "\\end{document}\n";
 
        return $string;
    }
 
    /**
     * returns the dimensions of a picture file using 'identify' of the
     * imagemagick tools. The resulting array can be adressed with either
     * $dim[0] / $dim[1] or $dim["x"] / $dim["y"]
     *
     * @param string path to a picture
     * @returns array containing the picture dimensions
     */
    function getDimensions($filename) {
        $output=exec($this->_identify_path." ".$filename);
              //For some reason this didn't work for me, I used
              //$commander = "identify ".$filename;
              //$output=exec($commander);
              //instead. This should work if Identify works on the commandline
        $result=explode(" ",$output);
        $dim=explode("x",$result[2]);
        $dim["x"] = $dim[0];
        $dim["y"] = $dim[1];
 
        return $dim;
    }
 
    /**
     * Renders a LaTeX formula by the using the following method:
     *  - write the formula into a wrapped tex-file in a temporary directory
     *    and change to it
     *  - Create a DVI file using latex (tetex)
     *  - Convert DVI file to Postscript (PS) using dvips (tetex)
     *  - convert, trim and add transparancy by using 'convert' from the
     *    imagemagick package.
     *  - Save the resulting image to the picture cache directory using an
     *    md5 hash as filename. Already rendered formulas can be found directly
     *    this way.
     *
     * @param string LaTeX formula
     * @returns true if the picture has been successfully saved to the picture
     *          cache directory
     */
    function renderLatex($latex_formula) {
        $latex_document = $this->wrap_formula($latex_formula);
 
        $current_dir = getcwd();
 
        chdir($this->_tmp_dir);
 
        // create temporary latex file
        $fp = fopen($this->_tmp_dir."/".$this->_tmp_filename.".tex","a+");
        fputs($fp,$latex_document);
        fclose($fp);
 
        // create temporary dvi file
        // The \"'s are used in case the path has spaces in it (same as below for dvi)
        $command = "\"".$this->_latex_path."\" --interaction=nonstopmode ".$this->_tmp_filename.".tex";
            //In my case this didn't output in the right directory (amongst other things)
            //so I hardcoded everything in (If you use this, adjust it to your directories)
            //$command = "\"C:\Program Files\MiKTeX\miktex\bin\latex\" --output-directory=D:\Wiki\www\images\\tmp\\ --interaction=nonstopmode D:\Wiki\www\images\\tmp\\".$this->_tmp_filename.".tex";
 
        $status_code = exec($command);
 
        if (!$status_code) { 
                $this->cleanTemporaryDirectory(); 
                chdir($current_dir); 
                $this->_errorcode = 4; 
				$this->_errorextra = "error executing LaTeX: " .$command;
                return false; 
        }
 
        // convert dvi file to postscript using dvips
        $command = "\"".$this->_dvips_path."\" -q -E ".$this->_tmp_filename.".dvi"." -o ".$this->_tmp_filename.".ps";
        $status_code = exec($command);
 
        // imagemagick convert ps to image and trim picture
        $command = $this->_convert_path." -density ".$this->_formula_density.
                    " -trim -transparent \"#FFFFFF\" ".$this->_tmp_filename.".ps ".
                    $this->_tmp_filename.".".$this->_image_format;
 
        $status_code = exec($command);
		
        /** if (!$status_code) { status tests always false...
                chdir($current_dir); 
                $this->_errorcode = 5; 
				$this->_errorextra = "conversion error: ".$command;
                return false; 
        } **/
 
        // test picture for correct dimensions
        $dim = $this->getDimensions($this->_tmp_filename.".".$this->_image_format);
 
        if ( ($dim["x"] > $this->_xsize_limit) or ($dim["y"] > $this->_ysize_limit)) {
            $this->cleanTemporaryDirectory();
            chdir($current_dir);
            $this->_errorcode = 6;
            $this->_errorextra = "dimension error: " . $dim["x"] . "x" . number_format($dim["y"],0,"","");
            return false;
        }
 
        // copy temporary formula file to cached formula directory
        $latex_hash = md5($latex_formula);
        $filename = $this->getPicturePath()."/math-".$latex_hash.".".$this->_image_format;
 
        $status_code = copy($this->_tmp_filename.".".$this->_image_format,$filename);
 
        $this->cleanTemporaryDirectory();
 
        if (!$status_code) { 
                chdir($current_dir); 
                $this->_errorcode = 7; 
				$this->_errorextra = "error copying result: ".$this->_tmp_filename.".".$this->_image_format." --> ".$filename;
                return false; 
        }
        chdir($current_dir);
 
        return true;
    }
 
    /**
     * Cleans the temporary directory
     */
    function cleanTemporaryDirectory() {

        $current_dir = getcwd();
        chdir($this->_tmp_dir);
 
        unlink($this->_tmp_dir."/".$this->_tmp_filename.".tex");
        unlink($this->_tmp_dir."/".$this->_tmp_filename.".aux");
        unlink($this->_tmp_dir."/".$this->_tmp_filename.".log");
        unlink($this->_tmp_dir."/".$this->_tmp_filename.".dvi");
        unlink($this->_tmp_dir."/".$this->_tmp_filename.".ps");
        unlink($this->_tmp_dir."/".$this->_tmp_filename.".".$this->_image_format);
 
        chdir($current_dir);
    }
 
}
 
 
 
/**
 * LaTeX Rendering Class - Calling function
 * Copyright (C) 2003  Benjamin Zeiss <zeiss@math.uni-goettingen.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * --------------------------------------------------------------------
 * @author Benjamin Zeiss <zeiss@math.uni-goettingen.de>
 * @version v0.8
 * @package latexrender
 * Revised by Steve Mayer
 * This file can be included in many PHP programs by using something like (see example.php to see how it can be used)
 *              include_once('/full_path_here_to/latexrender/latex.php');
 *              $text_to_be_converted=latex_content($text_to_be_converted);
 * $text_to_be_converted will then contain the link to the appropriate image
 * or an error code as follows (the 500 values can be altered in class.latexrender.php):
 *      0 OK
 *      1 Formula longer than 500 characters
 *      2 Includes a blacklisted tag
 *      3 (Not used) Latex rendering failed
 *      4 Cannot create DVI file
 *      5 Picture larger than 500 x 500 followed by x x y dimensions
 *      6 Cannot copy image to pictures directory
 */
class MathRenderer {
 function renderMath($latex_formula) {
 
        global $wgMathDirectory, $wgMathPath, $wgTmpDirectory,
                        $wgLaTexCommand, 
                        $wgDvipsCommand,
                        $wgImageMagickConvertCommand, 
                        $wgImageMagickIdentifyCommand;
 
        $latex_formula = '\displaystyle ' . $latex_formula;
        $latex = new LatexRender ($wgMathDirectory, $wgMathPath, $wgTmpDirectory);
 
        #check Math dir
        if(!file_exists($wgMathDirectory)) @mkdir($wgMathDirectory);
        if(!file_exists($wgTmpDirectory)) @mkdir($wgTmpDirectory);
 
 
        $latex->_latex_path          = $wgLaTexCommand;
        $latex->_dvips_path          = $wgDvipsCommand;
        $latex->_convert_path        = $wgImageMagickConvertCommand;
        $latex->_identify_path       = $wgImageMagickIdentifyCommand;
 
        $url = $latex->getFormulaURL($latex_formula);
 
        $alt_latex_formula = htmlentities($latex_formula, ENT_QUOTES);
        $alt_latex_formula = str_replace("\r","",$alt_latex_formula);
        $alt_latex_formula = str_replace("\n","",$alt_latex_formula);
        $alt_latex_formula = str_replace('\displaystyle ','',$alt_latex_formula);
 
    if ($url != false)
        $text = "<img src='$url' title='$alt_latex_formula' alt='$alt_latex_formula' class=\"tex\" />";
    else
        $text = "[Error parsing LaTeX formula. Error $latex->_errorcode: $latex->_errorextra]";
 
    return $text;
 }
}
 
?>