<?php
/**
 * LaTeX Rendering Class
 * 
 * Primary Author: Wizardry and Steamworks <wizardry.steamworks@outlook.com>
 * Original Author: Benjamin Zeiss <zeiss@math.uni-goettingen.de>
 * 
 * @license GPL 2
 */

class LatexRender {
    protected $_picture_path = "";
    protected $_picture_path_httpd = "";
    protected $_tmp_dir = "";

    public $_keep_tmp = false;
    public $_latex_path = "latex";
    public $_dvips_path = "dvips";
    public $_convert_path = "convert";
    public $_identify_path = "identify";
    public $_xsize_limit = 1000;
    public $_ysize_limit = 500;
    public $_string_length_limit = 2000;
    public $_image_format = "png";
    public $_font_size = 10;
    public $_latexclass = "article";
    public $_tmp_filename;
    public $_latex_tags_blacklist = [];
    public $_errorcode = 0;
    public $_errorextra = "";
    public $_filename;
    public $_cmdoutput = "";
    public $_preamble = "";
    public $_postamble = "";

    public function __construct($picture_path, $picture_path_httpd, $tmp_dir) {
        $this->_picture_path = $picture_path;
        $this->_picture_path_httpd = $picture_path_httpd;
        $this->_tmp_dir = $tmp_dir;
    }

    public function getPicturePath() { return $this->_picture_path; }
    public function getPicturePathHTTPD() { return $this->_picture_path_httpd; }

    public function getFormulaURL($latex_formula) {
        $latex_formula = str_ireplace(['&gt;', '&lt;'], ['>', '<'], $latex_formula);
        $this->latex_document = $this->_preamble . "\n" . trim($latex_formula) . "\n" . $this->_postamble;

        $formula_hash = md5($latex_formula);
        $filename = "img" . $formula_hash . '.' . $this->_image_format;
        $full_path_filename = $this->getPicturePath() . "/" . $filename;
        $this->_filename = $full_path_filename;

        if (is_readable($full_path_filename)) {
            return $this->getPicturePathHTTPD() . "/" . $filename;
        }

        if (strlen($latex_formula) > $this->_string_length_limit) {
            $this->_errorcode = 1;
            $this->_errorextra = ': ' . strlen($latex_formula);
            return false;
        }

        foreach ($this->_latex_tags_blacklist as $tag) {
            if (stristr($latex_formula, $tag)) {
                $this->_errorcode = 2;
                return false;
            }
        }

        if ($this->renderLatex($this->latex_document, $full_path_filename)) {
            return $this->getPicturePathHTTPD() . $filename;
        }

        return false;
    }

    protected function getDimensions($filename) {
        $output = $this->myexec($this->_identify_path . " " . escapeshellarg($filename), $status);
        $result = explode(" ", $output);
        if (!isset($result[2])) return ["x" => 0, "y" => 0];
        $dim = explode("x", $result[2]);
        return ["x" => (int)$dim[0], "y" => (int)$dim[1]];
    }

    public function renderLatex($latex_document, $destination) {
        $current_dir = getcwd();
        if (!is_dir($this->_tmp_dir)) return false;
        chdir($this->_tmp_dir);

        $this->_tmp_filename = md5(uniqid(rand(), true));
        file_put_contents($this->_tmp_filename . ".tex", $latex_document);

        $this->myexec($this->_latex_path . " --interaction=nonstopmode " . $this->_tmp_filename . ".tex", $status_latex);

        if (!file_exists($this->_tmp_filename . ".dvi")) {
            if (!$this->_keep_tmp) $this->cleanTemporaryDirectory();
            chdir($current_dir);
            $this->_errorcode = 4;
            return false;
        }

        $this->myexec($this->_dvips_path . " " . $this->_tmp_filename . ".dvi -o " . $this->_tmp_filename . ".ps", $status_dvips);
        $this->myexec($this->_convert_path . " " . $this->_tmp_filename . ".ps " . $this->_tmp_filename . "." . $this->_image_format, $status_convert);

        if ($status_dvips || $status_convert) {
            if (!$this->_keep_tmp) $this->cleanTemporaryDirectory();
            chdir($current_dir);
            $this->_errorcode = 6;
            return false;
        }

        $dim = $this->getDimensions($this->_tmp_filename . "." . $this->_image_format);
        if ($dim["x"] > $this->_xsize_limit || $dim["y"] > $this->_ysize_limit) {
            if (!$this->_keep_tmp) $this->cleanTemporaryDirectory();
            chdir($current_dir);
            $this->_errorcode = 5;
            $this->_errorextra = ": " . $dim["x"] . "x" . $dim["y"];
            return false;
        }

        $status_code = copy($this->_tmp_filename . "." . $this->_image_format, $destination);
        chdir($current_dir);

        if (!$this->_keep_tmp) $this->cleanTemporaryDirectory();
        if (!$status_code) { $this->_errorcode = 7; return false; }

        return true;
    }

    protected function myexec($cmd, &$status) {
        $cmd = "$cmd 2>&1";
        $lastline = exec($cmd, $output, $status);
        $this->_cmdoutput .= "\n>>>>> $cmd\n" . trim(implode(PHP_EOL, $output)) . PHP_EOL . "  --- exit status " . $status;
        return $lastline;
    }

    public function cleanTemporaryDirectory() {
        foreach (['tex', 'aux', 'log', 'dvi', 'ps', $this->_image_format] as $ext) {
            @unlink($this->_tmp_dir . "/" . $this->_tmp_filename . "." . $ext);
        }
    }
}
