Extension:Metapost

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manual - list
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
Parameters

$wgMetaPostSettings->metapostCommand $wgMetaPostSettings->convertCommand $wgMetaPostSettings->convertDPIDefault

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 = '/' . $this->hash[0]
          . '/' . $this->hash[1]
          . '/' . $this->hash[2];
    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] MikTex on Windows

Getting this new version to work with MikTex on a windows installation requires these steps:

  • MikTex/bin is in the path (and the machine is rebooted so that Apache picks up the change)
  • Rather than passing the the full path to the mp file in create_image we have to chdir and use the file name only:
  function create_image() {
        global $wgTmpDirectory;
    $mpost_cmd = wfEscapeShellArg($this->settings->metapostCommand)
                 . " " . $this->hash . ".mp";
    wfDebugLog('Metapost', "mpost stage: " . $mpost_cmd);
    chdir($wgTmpDirectory);
    exec($mpost_cmd);
    if (!file_exists($this->hash . ".0")) {
      return false;
    }
  • The $wgTmpDirectory global has to be pointed to a path with no spaces
  • The $wgImageMagickConvertCommand global needed to point to the convert function
  • References to files ending in .1 should be changed to .0 (for some reason this is the terminus that MikTex uses)

[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
Personal tools
Namespaces
Variants
Actions
Site
Support
Download
Development
Communication
Print/export
Toolbox