Extension:Metapost

From MediaWiki.org

Jump to: navigation, search

           

Manual on MediaWiki Extensions
List of MediaWiki Extensions
Crystal Clear action run.png
Metapost

Release status: stable

Implementation  Tag
Description Include METAPOST graphics on a Mediawiki page using the <metapost> tag
Author(s)  LEE Sau Dan (李守敦)
Last Version  1.7 (2008-11-13T05:43:08)
MediaWiki  1.9 - 1.12
License No license specified
Download http://www.cs.hku.hk/~sdlee/Metapost/Metapost.php.gz

check usage (experimental)

Contents

[edit] What can this extension do?

This extension adds a new tag <metapost> to allow wiki page authors to include METAPOST graphics into wiki pages.

The following software is required for this extension to work:

  1. METAPOST (version >= 0.993)
  2. ImageMagick (version >= 6.2)
  3. Ghostscript

[edit] Usage

The following wiki page content will render a page like this one.

This is a demo:

<metapost>
input boxes;%

beginfig(0)
  path p[];
  p1 = fullcircle xscaled 30 yscaled 100 rotated 75;
  p2 = ((.2,0) .. (.5,.1) .. (.7,.4) .. (.9,.7)
        .. (.9,1) .. (.5,1) ..(.3, .7) .. (.3, .4) .. cycle)
      scaled 70 shifted (-50,-50);
  fill p1 withcolor .5[red, white];
  fill p2 withcolor .5[red+green, white];

  begingroup
    save t,u;
    (t1-0, u1) = subpath(0,2) of p1 intersectiontimes p2;
    (t2-2, u2) = subpath(2,4) of p1 intersectiontimes p2;
    (t3-4, u3) = subpath(4,6) of p1 intersectiontimes p2;
    (t4-6, u4) = subpath(6,8) of p1 intersectiontimes p2;

    p3 = subpath(t2, t3) of p1
         -- subpath(t1, 0) of p1 & subpath(8,t4) of p1
	 -- cycle;
  endgroup;

  draw p3;

  boxit.line1(btex MetaPost diagram etex);
  boxit.line2(btex containing \TeX\ symbols etex);
  boxit.line3(btex embedded in etex);
  boxit.line4(btex a Wikipedia page etex);

  (0,0) = line1.s - line2.n
        = line2.s - line3.n
        = line3.s - line4.n;

  drawoptions(withcolor .5blue);
  drawunboxed(line1);
  drawoptions(withcolor .5red);
  drawunboxed(line2);
  drawoptions(withcolor .5green);
  drawunboxed(line3);
  drawoptions(withcolor .5(red+blue));
  drawunboxed(line4);

  drawoptions(withcolor .5(blue+green));
  draw btex $\int_{-\infty}^{+\infty} e^{-{1\over2}z^2} dz
             = \sqrt{2\pi}$ etex
       rotated 15
       shifted(-30,0);
endfig;

</metapost>

That's all folks!

[edit] Installation

  1. Download the gzipped source code
  2. Decompress the file and rename it to Metapost.php
  3. Put Metapost.php into your MediaWiki extensions directory.
  4. Change LocalSettings.php to load the extension. (See #Changes to LocalSettings.php)

[edit] Parameters

These parameters can be set in LocalSettings.php after Metapost.php is loaded:

Parameter Description Default Value
$wgMetaPostSettings->metapostCommand Command to invoke METAPOST "/usr/bin/mpost"
$wgMetaPostSettings->convertCommand Command to invoke ImageMagick's convert command "/usr/bin/convert"
$wgMetaPostSettings->convertDPIDefault DPI (dots-per-inch) to use to render the image. "144"


[edit] Changes to LocalSettings.php

require_once("$IP/extensions/Metapost.php");

[edit] Code

Please fetch it from http://www.cs.hku.hk/~sdlee/Metapost/Metapost.php.gz

[edit] New version of this extension

As the above link leads nowhere and the implementation is nowhere to be found, I endeavored to write a new version of this extension, as following:

<?php
/**
 * Parser hook extension adds a <metapost> tag to wiki markup for rendering
 * Metapost diagrams within a wiki page.
 * Adapted from MetaUML extension.
 *
 * Parameters:
 *   density=width               passed to ImageMagic to set rendering density.
 *   input=pkg_1,pkg_2,...pkg_n  Metapost packages to load in prologue.
 *   redraw                      Force redraw.
 *   format                      Target format to use (PNG/SVG), later is
 *                               only supported by metapost-1.2 and is not
 *                               yet implemented here.
 *
 */
 
// Make sure we are being called properly
if( !defined( 'MEDIAWIKI' ) ) {
    echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
    die( -1 );
}
 
class MetapostSettings {
    var $metapostCommand, $convertCommand;
    var $density, $format, $inputs, $redraw;
 
    function __construct() {
      global $wgImageMagickConvertCommand;
 
      $this->metapostCommand = "/usr/bin/mpost";
      $this->convertCommand = $wgImageMagickConvertCommand;
      $this->density = "100%";
      $this->format = "png";
      $this->inputs = array();
      $this->redraw = false;
    }
}
 
$wgMetapostSettings = new MetapostSettings();
 
//Avoid unstubbing $wgParser too early on modern (1.12+) MW versions, as per r35980
if (defined('MW_SUPPORTS_PARSERFIRSTCALLINIT')) {
        $wgHooks['ParserFirstCallInit'][] = 'wfMetapostExtension';
} else {
        $wgExtensionFunctions[] = 'wfMetapostExtension';
}
 
$wgExtensionCredits['parserhook'][] = array(
        'name' => 'Metapost',
        'version' => '0.2',
        'author' => 'Alex Dubov',
        'url' => 'http://www.mediawiki.org/wiki/Extension:Metapost',
        'description' => 'Renders a Metapost diagram.'
);
 
 
function wfMetapostExtension() {
  global $wgParser;
  $wgParser->setHook('metapost', 'renderMetapost');
  return true;
}
 
class MetapostRenderer {
  var $settings;
  var $src, $hash;
  var $src_path, $ltx_src_path, $dst_path, $dst_url;
  var $old_cwd, $cwd;
 
 function get_hash_path() {
    $path = '/' . substr($this->hash, 0, 1)
                . '/' . substr($this->hash, 1, 1)
                . '/' . substr($this->hash, 2, 1);
    return $path;
  }
 
  function __construct($src) {
    global $wgMathDirectory, $wgMathPath, $wgTmpDirectory;
 
    $this->settings = new MetaPostSettings;
    $this->old_cwd = getcwd();
    $this->src = $src;
    $this->hash = hash('md5', $src);
 
    wfDebugLog('Metapost', "Calculated src hash: " . $this->hash);
 
    $dest_dir = $wgMathDirectory . $this->get_hash_path();
 
    if (!is_dir($dest_dir)) {
      mkdir($dest_dir, 0755, true);
    }
 
    if (!is_dir($wgTmpDirectory)) {
      mkdir($wgTmpDirectory, 0755);
    }
 
    $this->src_path = $wgTmpDirectory . '/' . $this->hash;
    $this->ltx_src_path = $wgTmpDirectory . "/ltx-" . $this->hash;
    $this->dst_path = $dest_dir . '/' . $this->hash . '.'
                      . $this->settings->format;
    $this->dst_url = $wgMathPath . $this->get_hash_path() . '/' . $this->hash
                     . '.' . $this->settings->format;
    chdir($wgTmpDirectory);
    wfDebugLog('Metapost', "src_path: " . $this->src_path);
    wfDebugLog('Metapost', "dst_path: " . $this->dst_path);
    wfDebugLog('Metapost', "dst_url: " . $this->dst_url);
  }
 
  function __destruct() {
    $er = error_reporting(E_ALL ^ E_WARNING);
    unlink($this->src_path . ".mp");
    unlink($this->src_path . ".mpx");
    unlink($this->ltx_src_path . ".mpx");
    unlink($this->ltx_src_path . ".tmp");
    unlink($this->src_path . ".1");
    unlink($this->src_path . ".log");
    chdir($this->old_cwd);
    error_reporting($er);
  }
 
  function create_metapost() {
    $fd = fopen($this->src_path . ".mp", "wb");
    fwrite($fd, "prologues := 1;\nnonstopmode;\n\n");
    foreach ($this->settings->inputs as $inp) {
      fwrite($fd, "input " . $inp . ";\n");
    }
    fwrite($fd, "\nbeginfig(1);\n");
    fwrite($fd, $this->src);
    fwrite($fd, "\nendfig;\n\nend\n");
    fclose($fd);
  }
 
  function create_image() {
    $mpost_cmd = wfEscapeShellArg($this->settings->metapostCommand)
                 . " " . $this->src_path . ".mp";
    wfDebugLog('Metapost', "mpost stage: " . $mpost_cmd);
    exec($mpost_cmd);
    if (!file_exists($this->src_path . ".1")) {
      return false;
    }
 
    if (file_exists($this->ltx_src_path . ".tmp")) {
        wfDebugLog('Metapost', "mpost stage ltx: " . $mpost_cmd);
        exec($mpost_cmd);
    }
    if (!file_exists($this->src_path . ".1")) {
      return false;
    }
 
    $conv_cmd = wfEscapeShellArg($this->settings->convertCommand)
                . " -density " . wfEscapeShellArg($this->settings->density)
                . " -trim -transparent \"#FFFFFF\" "
                . wfEscapeShellArg($this->src_path . ".1")
                . " " . wfEscapeShellArg($this->dst_path);
    wfDebugLog('Metapost', "convert stage: " . $conv_cmd);
    exec($conv_cmd);
    if (!file_exists($this->dst_path)) {
      return false;
    }
 
    return true;
  }
 
  function get_image_url() {
    if (is_file($this->dst_path) && ($this->settings->redraw == true)) {
      unlink($this->dst_path);
    }
 
    if (is_file($this->dst_path)) {
      return $this->dst_url;
    } else {
      $this->create_metapost();
      if ($this->create_image()) {
        return $this->dst_url;
      } else {
        return false;
      }
    }
  }
}
 
# The callback function for converting the input text to HTML output
function renderMetapost($src, $argv) {
  $renderer = new MetapostRenderer($src);
  global $wgMetapostSettings;
 
  if (array_key_exists('density', $argv)) {
    $renderer->settings->density = $argv['density'];
  }
 
  if (array_key_exists('redraw', $argv)) {
    $renderer->settings->redraw = true;
  }
 
  if (array_key_exists('input', $argv)) {
    $c_inp = explode(',', $argv['input']);
    $renderer->settings->inputs = array_unique(array_merge($wgMetapostSettings->inputs,
                                                           $c_inp));
  }
 
  if (array_key_exists('format', $argv)) {
    if ($argv['format'] != "png") {
      return "[Formats other than PNG are not yet supported]";
    }
  }
 
  $url = $renderer->get_image_url();
 
  if ($url == false) {
    $text = "[An error occured in Metapost extension]";
  } else {
    $text = "<img src='$url'>";
  }
 
  return $text;
}

[edit] See also

Extension:GraphViz
Another cool extension for embedding graphics. Indeed, the source code of the Metapost extension is branched off from the source code of the GraphiViz extension!
Gnuplot extension
Useful for plotting graphs against a function or a set of data