GIT repositories

Index page of all the GIT repositories that are clonable form this server via HTTPS. Übersichtsseite aller GIT-Repositories, die von diesem Server aus über git clone (HTTPS) erreichbar sind.

Services

A bunch of service scripts to convert, analyse and generate data. Ein paar Services zum Konvertieren, Analysieren und Generieren von Daten.

GNU octave web interface

A web interface for GNU Octave, which allows to run scientific calculations from netbooks, tables or smartphones. The interface provides a web form generator for Octave script parameters with pre-validation, automatic script list generation, as well presenting of output text, figures and files in a output HTML page. Ein Webinterface für GNU-Octave, mit dem wissenschaftliche Berechnungen von Netbooks, Tablets oder Smartphones aus durchgeführt werden können. Die Schnittstelle beinhaltet einen Formulargenerator für Octave-Scriptparameter, mit Einheiten und Einfabevalidierung. Textausgabe, Abbildungen und generierte Dateien werden abgefangen und in einer HTML-Seite dem Nutzer als Ergebnis zur Verfügung gestellt.

LaTex formula Rendering Klasse

LaTex formula rendering class

This class renders a LaTex formula to an image with transparent background. The image files are cached, so that the shell processes are not started twice if the formula was already requested - that's good for the performance. The sample script returns not something like <img src="..." />, but directly the image binary. The sample source code shows how to use it. Note that this very likely won't work if you have only a webspace. You need Latex installed, ImageMagick, and need the possibility to create restricted user accounts (for security reasons, Latex can execute shell commands).

Diese Klasse rendert eine LaTex-Formel zu einer Bilddatei mit transparentem Hintergrund. Die Bilder werden gecached, so dass ein existierendes Bild direkt gesendet wird und nicht der Konvertierungsprozess mehrmalig angestoßen wird. Das Beispiel-Skript sendet direkt eine Bilddatei an den Browser. Beachte, dass die Klasse wahrscheinlich nicht auf einem einfachen Webspace funktioniert, weil Latex und ImageMagick installiert sein muss. Weiterhin ist es aus Sicherheitsgründen wichtig, Latex als eingeschränkter Nutzer auszuführen (Latex kann shell-Kommandos ausführen).

Sample source code

Anwendungsbeispiel

<?php
 
require_once(__DIR__ . '/swlib/swlib.class.php');
 
use sw\ResourceFile;
use sw\LaTexRenderer;
use sw\Tracer;
use \Exception;
 
// Input check
if (isset($_GET['f'])) {
  if (get_magic_quotes_gpc()) {
    $_GET['f'] = stripslashes($_GET['f']);
  }
  $_GET['f'] = trim($_GET['f'], " \t\n\r");
  try {
    // Configure the renderer
    // Note that you should run this binaries with another user than your
    // normal web server, one with restricted user. I use a shell script,
    // "sudo_restricted.sh", which contains a line like
    // "sudo -n -H -u <restricted user> $1 $2 $3 $4 $5 $6 $7 $8 $9"
    // The user is of cause NOT root, but a restricted used registered in
    // the sudoers file. The HOME directory of this user is the current one.
    $sudo = dirname(__FILE__) . '/sudo_restricted.sh ';
    LaTexRenderer::config(array(
      'cache_dir' => dirname(__FILE__) . '/cache',
      'latex_bin' => $sudo . '/usr/bin/latex',
      'dvips_bin' => $sudo . '/usr/bin/dvips',
      'convert_bin' => $sudo . '/usr/bin/convert',
      'cache_prefix' => 'texf_',
      'temp_dir' => '',
      'font_size' => 16,
      'packages' => array(
        'amsmath', 'amsfonts', 'amssymb', 'latexsym', 'color'
      )
    ));
    $render = new LaTexRenderer();
    $file = dirname(__FILE__) . '/cache/' . $render->renderFormulaToImage($_GET['f']);
    $rc = new ResourceFile($file, '/');
    $rc->download();
  } catch (Exception $e) {
    header('501 Internal Server Error');
  }
} else {
// Html header and footer
  print <<<HERE
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html><head><title>LaTeX Equations and Graphics in PHP</title></head><body>
    <pre><form action="{$_SERVER['PHP_SELF']}" method="get">
    <textarea rows="20" cols="60" name="f">{$_GET['f']}</textarea><br/>
    <input type="submit" /></form>
    </pre></body></html>
HERE;
}

Class source code

Klassen-Quelltext

<?php
 
/**
 * Renders LaTeX to a png image. The rendering class has an own cache based on
 * the checkums of the fomulas. If a new formula is entered specified, then the
 * rendering process will run, and create the cache file. The callback
 * onTexFilter() can be overloaded to add an own filter, e.g. to prevent too long
 * texts, shell commands or require HTTP authentication to render formulas.
 * @gpackage de.atwillys.sw.php.swLib
 * @author Stefan Wilhelm
 * @copyright Stefan Wilhelm, 2010
 * @license GPL
 * @version 1.0
 */
 
namespace sw;
 
class LaTeXRenderer {
 
  /**
   * Stores the class configuration
   * @staticvar array
   */
  private static $config = array(
      'latex_bin' => '/usr/texbin/latex',
      'dvips_bin' => '/usr/texbin/dvips',
      'convert_bin' => '/opt/local/bin/convert',
      'cache_dir' => '',
      'cache_prefix' => 'texf_',
      'temp_dir' => '',
      'font_size' => 16,
      'packages' => array(
          'amsmath', 'amsfonts', 'amssymb', 'latexsym', 'color'
      )
  );
 
  /**
   * Sets and returns the class configuration
   * @param array $config
   * @return array
   */
  public static function config($config=null) {
    if (!empty($config)) {
      if (!is_array($config)) {
        throw new LException('Class ":class" must be configured using an array', array(':class' => __CLASS__));
      } else {
        self::$config = array_merge(self::$config, $config);
      }
    }
    return self::$config;
  }
 
  /**
   * Overload this function to filter the input TeX sources, or e.g. using
   * HTTP authentication to enable it. You can return a text that renders
   * an error message.
   * @param string $tex
   * @return string
   */
  protected function onTexFilter($tex) {
    return $tex;
  }
 
  /**
   * Constructor, automatically initializes the undefined configuration
   * if the temp_directory is empty.
   */
  public function __construct() {
    if (empty(self::$config['cache_dir']))
      self::config(array('cache_dir' => './cache'));
    if (empty(self::$config['temp_dir']))
      self::config(array('temp_dir' => '/tmp'));
  }
 
  /**
   * Render a formula to an image
   * @return string
   */
  public function renderFormulaToImage($tex) {
    return $this->renderToImage("\\documentclass[10pt]{article}\n\$packages\\pagestyle{empty}\n\\begin{document}\n\\begin{displaymath}\n$tex\n\\end{displaymath}\n\\end{document}\n");
  }
 
  /**
   * Renders a LaTeX source to an image (png) if the md5 checksum of the
   * source text already exists, a cached image representation is returned.
   * The images are located in in the cache directory.
   * @param string $tex
   * @return string
   */
  private function renderToImage($tex) {
    $tex = $this->onTexFilter($tex);
    $fileName = trim(self::$config['cache_prefix'] . md5($tex));
    $cacheFilePath = self::$config['cache_dir'] . "/$fileName.png";
 
    if (!is_file($cacheFilePath)) {
      $exception = null;
      try {
        $packages = self::$config['packages'];
        foreach ($packages as $key => $package) {
          $packages[$key] = "\usepackage{" . $package . "}";
        }
        $packages = implode("\n", $packages);
        $font_size = self::$config['font_size'];
        $tex = str_replace('$packages', $packages, $tex);
        $current_dir = getcwd();
        $tmpDir = self::$config['temp_dir'];
        $cacheDir = self::$config['cache_dir'];
 
        chdir($tmpDir);
        file_put_contents("$tmpDir/$fileName.tex", $tex);
 
        $output = array();
        exec(self::$config['latex_bin'] . " --interaction=nonstopmode $tmpDir/$fileName.tex", $output, $return_var);
        @unlink("$tmpDir/$fileName.tex");
        @unlink("$tmpDir/$fileName.aux");
        @unlink("$tmpDir/$fileName.log");
 
        if (!is_file("$tmpDir/$fileName.dvi")) {
          $error = '';
          foreach ($output as $line) {
            $line = trim($line);
            if (!empty($line)) {
              if (substr($line, 0, 1) == '!') {
                $error = trim($line, "!. ");
              } else if (!empty($error)) {
                $line = ltrim($line, ' l.0123456789');
                throw new LException("LaTeX: \":error\" in line :line", array(':error' => $error, ':line' => $line));
              }
            }
          }
          if (!empty($error)) {
            throw new LException('LaTeX: :error', array(':error' => $error));
          } else {
            throw new LException('LaTeX did not generate output file');
          }
        }
 
        $output = array();
        exec(self::$config['dvips_bin'] . " -E $tmpDir/$fileName.dvi -o $tmpDir/$fileName.ps", $output, $return_var);
        @unlink("$tmpDir/$fileName.dvi");
        if (!is_file("$tmpDir/$fileName.ps")) {
          throw new LException('LaTeX/dvips conversion failed: :output', array(':output' => implode("\n", $output)));
        }
 
        $output = array();
        chmod("$tmpDir/$fileName.ps", 0666);
        exec(escapeshellcmd(self::$config['convert_bin'] . " -define registry:temporary-path=$tmpDir -density 120 $tmpDir/$fileName.ps $tmpDir/$fileName.png"));
        @unlink("$tmpDir/$fileName.ps");
        if (is_file("$tmpDir/$fileName.png")) {
          copy("$tmpDir/$fileName.png", "$cacheDir/$fileName.png");
          @unlink("$tmpDir/$fileName.png");
        } else {
          throw new LException('Conversion from to ps to png failed: :output', array(':output' => implode("\n", $output)));
        }
      } catch (\Exception $e) {
        $exception = $e;
      }
 
      chdir($current_dir);
 
      // Rethrow the exception after cleanup and directory restored
      if (!empty($exception)) {
        throw $exception;
      }
    }
 
    return "$fileName.png";
  }
 
}