Wiki:Context tag implementation

From Wiki
Jump to navigation Jump to search

Integrating ConTeXt in Mediawiki Software

The contextgarden.net wiki was enhanced in several ways to serve better for the needs of documenting ConTeXt. One way is the inline rendering of ConTeXt code.

Inline processing of ConTeXt code

The user of the contextgarden.net wiki can put ConTeXt code between the wiki-tags <context>...</context>, such as:


<context>
This is \ConTeXt\ code.
</context>

This will be rendered directly by ConTeXt and the result (more precisely: a bitmap of the result) will be displayed in the webbrowser instead of the input. To achieve this, I have created a little extension that I document here.


In LocalSettings.php the wiki-software loads the extension:

if(file_exists("localext/texshowwiki.php")) {
       include_once("texshowwiki.php");
} else {
       error_log("texshowwiki.php not loaded",0);
}

the extension is (you see it) in localext/texshowwiki.php. The name is a bit misleading, it is historic. Let's take a look at the extension:

<?php
  require_once( "wikitex.php");
  $wgExtensionFunctions[] = 'TW_Register';

  function TW_Register() 
  {
    global $wgParser;
    $wgParser->setHook( 'context', 'TW_show_context' );
  }
 
  function TW_show_context($string) {
    return wikitex($string);
  }

?>

First we define a new hook. When the mediawiki parser encounters the new tag 'context', it should feed the contents of the text that is enclosed in the <context>...</context> tags to the method TW_show_context. This is what the file "wikitex.php" looks like:

<?php
function wikitex($inputstring) {
  require("contextpng.php");
  // needs iconv compiled in php!!
  $somestring = iconv( "UTF-8","ISO-8859-1", $inputstring);
  $md5=md5($somestring);

  $destfile=$imagedir ."/".  $md5 .".png";
  $ret = "<img src=\"/wikiteximage/" . $md5 . ".png\" />";

  if (!file_exists($destfile)) { 
    $towrite="\\starttext\n" . $somestring . "\\stoptext ";
    $path=getenv('PATH');
    putenv("PATH=$path:" . $texpath);

    if (!file_exists($maindir)) { 
      mkdir($maindir, 0777); 
    }
    if (!file_exists($imagedir)) { 
      mkdir($imagedir, 0777); 
    }

    $tempdir=rtrim(`$mktemp`) ;
    $filename = $tempdir . "/wikitex.tex";

    if (is_writable ($tempdir )) { 
      if (! $handle =fopen ($filename ,'a' )) { 
	error_log("Cannot open file ($filename)") ;
        return "(internal error: cannot open file $filename)";
      }
      if ( fwrite ($handle ,$towrite ) === FALSE ) { 
	error_log ("Cannot write to file ($filename)") ;
        return "internal error: cannot write to file $filename";
      } 
    }
    fclose ($handle); 
    if (! is_writable ($tempdir)) {
	error_log ("The file $filename is not writable") ;
        return "internal error: the file $filename is not writable";
    }
    $olddir=getcwd();

    if (! chdir($tempdir)) {
      error_log ("chdir error");
      return "internal error: chdir failed";
      chdir ($olddir);
    }
    system($texexec,$texret);
    if ($texret == 0) {
      system($pdfcrop,$cropret);
      if ($cropret != 0) {
	error_log ("pdfcrop error");
	chdir ($olddir);
        return "internal error: pdfcrop failed";
      } 
      system($gs,$gsret);
      if ($gsret != 0) {
	error_log ("gs error");
	chdir ($olddir);
        return "internal error: gs failed";
      } 
      system($convert,$convertret);
      if ($convertret != 0) {
	error_log ("convert error");
	chdir ($olddir);
        return "internal error: convert failed";
      } 
      if (! copy("cropped.png",$destfile)) { 
	error_log ("copy error");
	chdir ($olddir);
        return "internal error: copy error";
      }
    } else {
      error_log ("texexec error") ;
      $ret= "(texexec error, something is wrong with the input)";
    }
    chdir ($olddir);
   }
  

  return $ret ;
}
?>

The file contextpng.php defines some constants which are not important now (see below). First, I calculate a checksum for the input string. This is used to create a unique filename for each possible input. If we already have a bitmap with this name, we know that the input has not changed and we can display the image without re-rendering the contents. If there is no filename with that checksum, we have to render the contents. Now we write the contents into a file, surrounded by \starttext...\stoptext. Then we run texexec, pdfcrop, ghostscript and convert to generate and process the image. To see which commands are used, here is the contents of the file contextpng.php:

<?php
 $texpath="/opt/tetex/current/bin/i686-pc-linux-gnu";
 $maindir="/tmp/wikitex";
 $mktemp="/bin/mktemp -d -p $maindir XXXXXX" ;
 $texexec="/opt/context/current/bin/texexec --nonstop --batch --environment=env-empty wikitex.tex > output 2>&1";
 $pdfcrop="/opt/local/bin/pdfcrop --pdftexcmd=pdfetex --gscmd=/opt/gs/8.14/bin/gs wikitex.pdf cropped.pdf >/dev/null 2>&1";
 $gs="/opt/gs/8.14/bin/gs -sOutputFile=dcropped.png -r300 -dTextAlphaBits=4 -sDEVICE=png256 -dBATCH -dNOPAUSE cropped.pdf >/dev/null 2>&1";
 $convert="/usr/bin/convert -blur 0.20 -scale '50%' dcropped.png cropped.png";
 $imagedir= $_SERVER["DOCUMENT_ROOT"] . "/wikiteximage";
?>

That's it. All the mediwiki software passes back to the user is a reference to the image ("<img src="/wikiteximage/<md5string>.png" />"). The rest is done behind the scenes.

More questions? Please contact pg<at>contextgarden.net and I'll update this page.