mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
synced 2024-11-28 17:30:40 +00:00
6cb52f9190
Currently SVGs are generated only in MathML mode but not in LaTeXML mode. Since LaTeXML can benefit from the SVGs as well this change introduces the functionality to generate SVG images from LaTeXML MathML input. Generating SVG images for LaTeXML is an essential part of Math 2.0. It enables to include LaTeXMLs Content MathML and provides fallback images for visitors with MathML disabled browsers. Change-Id: If13a8b0825bf12dbfe4920ddb7ad552df9bc095f
213 lines
6.6 KiB
PHP
213 lines
6.6 KiB
PHP
<?php
|
|
/**
|
|
* MediaWiki math extension
|
|
*
|
|
* (c)2012 Moritz Schubotz
|
|
* GPLv2 license; info in main package.
|
|
*
|
|
* Contains the driver function for the LaTeXML daemon
|
|
* @file
|
|
*/
|
|
|
|
class MathLaTeXML extends MathMathML {
|
|
protected $defaultAllowedRootElements = array( 'math', 'div', 'table', 'query' );
|
|
/** @var String settings for LaTeXML daemon */
|
|
private $LaTeXMLSettings = '';
|
|
|
|
public function __construct( $tex = '', $params = array() ) {
|
|
global $wgMathLaTeXMLUrl;
|
|
parent::__construct( $tex, $params );
|
|
$this->hosts = $wgMathLaTeXMLUrl;
|
|
$this->setMode( MW_MATH_LATEXML );
|
|
}
|
|
/**
|
|
* Converts an array with LaTeXML settings to a URL encoded String.
|
|
* If the argument is a string the input will be returned.
|
|
* Thus the function has projector properties and can be applied a second time safely.
|
|
* @param (string|array) $array
|
|
* @return string
|
|
*/
|
|
public function serializeSettings( $array ) {
|
|
if ( !is_array( $array ) ) {
|
|
return $array;
|
|
} else {
|
|
// removes the [1] [2]... for the unnamed subarrays since LaTeXML
|
|
// assigns multiple values to one key e.g.
|
|
// preload=amsmath.sty&preload=amsthm.sty&preload=amstext.sty
|
|
$cgi_string = wfArrayToCgi( $array );
|
|
$cgi_string = preg_replace( '|\%5B\d+\%5D|', '', $cgi_string );
|
|
$cgi_string = preg_replace( '|&\d+=|', '&', $cgi_string );
|
|
return $cgi_string;
|
|
}
|
|
}
|
|
/**
|
|
* Gets the settings for the LaTeXML daemon.
|
|
* @global (array|string) $wgMathDefaultLaTeXMLSetting
|
|
* @return string
|
|
*/
|
|
public function getLaTeXMLSettings() {
|
|
global $wgMathDefaultLaTeXMLSetting;
|
|
if ( $this->LaTeXMLSettings ) {
|
|
return $this->LaTeXMLSettings;
|
|
} else {
|
|
return $wgMathDefaultLaTeXMLSetting;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the settings for the LaTeXML daemon.
|
|
* The settings affect only the current instance of the class.
|
|
* For a list of possible settings see:
|
|
* http://dlmf.nist.gov/LaTeXML/manual/commands/latexmlpost.xhtml
|
|
* An empty value indicates to use the default settings.
|
|
* @param string|array $settings
|
|
*/
|
|
public function setLaTeXMLSettings( $settings ) {
|
|
$this->LaTeXMLSettings = $settings;
|
|
}
|
|
|
|
/**
|
|
* Calculates the HTTP POST Data for the request. Depends on the settings
|
|
* and the input string only.
|
|
* @return string HTTP POST data
|
|
*/
|
|
public function getLaTeXMLPostData() {
|
|
$tex = $this->getTex();
|
|
if ( $this->getMathStyle() == MW_MATHSTYLE_INLINE_DISPLAYSTYLE ) {
|
|
// In MW_MATHSTYLE_INLINE_DISPLAYSTYLE the old
|
|
// texvc behavior is reproduced:
|
|
// The equation is rendered in displaystyle
|
|
// (texvc used $$ $tex $$ to render)
|
|
// but the equation is not centered.
|
|
$tex = '{\displaystyle ' . $tex . '}';
|
|
}
|
|
$texcmd = rawurlencode( $tex );
|
|
$settings = $this->serializeSettings( $this->getLaTeXMLSettings( ) );
|
|
$postData = $settings . '&tex=' . $texcmd;
|
|
wfDebugLog( "Math", 'Get post data: ' . $postData );
|
|
return $postData;
|
|
}
|
|
|
|
/**
|
|
* Does the actual web request to convert TeX to MathML.
|
|
* @return boolean
|
|
*/
|
|
protected function doRender() {
|
|
global $wgMathDebug;
|
|
wfProfileIn( __METHOD__ );
|
|
if ( trim( $this->getTex() ) === '' ) {
|
|
wfDebugLog( "Math", "Rendering was requested, but no TeX string is specified." );
|
|
$this->lastError = $this->getError( 'math_empty_tex' );
|
|
return false;
|
|
}
|
|
$res = '';
|
|
$host = $this->pickHost();
|
|
$post = $this->getLaTeXMLPostData();
|
|
// There is an API-inconsistency between different versions of the LaTeXML daemon
|
|
// some versions require the literal prefix other don't allow it.
|
|
if ( ! strpos( $host, '/convert' ) ){
|
|
$post = preg_replace( '/&tex=/' , '&tex=literal:', $post , 1);
|
|
}
|
|
$this->lastError = '';
|
|
$requestResult = $this->makeRequest( $host, $post, $res, $this->lastError );
|
|
if ( $requestResult ) {
|
|
$jsonResult = json_decode( $res );
|
|
if ( $jsonResult && json_last_error() === JSON_ERROR_NONE ) {
|
|
if ( $this->isValidMathML( $jsonResult->result ) ) {
|
|
$this->setMathml( $jsonResult->result );
|
|
if ( $wgMathDebug ) {
|
|
$this->setLog( $jsonResult->log );
|
|
$this->setStatusCode( $jsonResult->status_code );
|
|
}
|
|
return true;
|
|
} else {
|
|
// Do not print bad mathml. It's probably too verbose and might
|
|
// mess up the browser output.
|
|
$this->lastError = $this->getError( 'math_invalidxml', $this->getModeStr(), $host );
|
|
wfDebugLog( "Math", "\nLaTeXML InvalidMathML:"
|
|
. var_export( array( 'post' => $post, 'host' => $host
|
|
, 'result' => $res ), true ) . "\n\n" );
|
|
wfProfileOut( __METHOD__ );
|
|
return false;
|
|
}
|
|
} else {
|
|
$this->lastError = $this->getError( 'math_invalidjson', $this->getModeStr(), $host );
|
|
wfDebugLog( "Math", "\nLaTeXML InvalidJSON:"
|
|
. var_export( array( 'post' => $post, 'host' => $host
|
|
, 'res' => $res ), true ) . "\n\n" );
|
|
wfProfileOut( __METHOD__ );
|
|
return false;
|
|
}
|
|
} else {
|
|
// Error message has already been set.
|
|
wfProfileOut( __METHOD__ );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Internal version of @link self::embedMathML
|
|
* @return string
|
|
* @return html element with rendered math
|
|
*/
|
|
protected function getMathMLTag() {
|
|
return self::embedMathML( $this->getMathml(), urldecode( $this->getTex() ) );
|
|
}
|
|
|
|
/**
|
|
* Embeds the MathML-XML element in a HTML span element with class tex
|
|
* @param string $mml : the MathML string
|
|
* @param string $tagId : optional tagID for references like (pagename#equation2)
|
|
* @param bool $attribs
|
|
* @return html element with rendered math
|
|
*/
|
|
public static function embedMathML( $mml, $tagId = '', $attribs = false ) {
|
|
$mml = str_replace( "\n", " ", $mml );
|
|
if ( ! $attribs ) {
|
|
$attribs = array( 'class' => 'tex', 'dir' => 'ltr' );
|
|
if ( $tagId ) {
|
|
$attribs['id'] = $tagId;
|
|
}
|
|
$attribs = Sanitizer::validateTagAttributes( $attribs, 'span' );
|
|
}
|
|
return Xml::tags( 'span', $attribs, $mml );
|
|
}
|
|
|
|
/**
|
|
* Calculates the SVG image based on the MathML input
|
|
* No cache is used.
|
|
* @return boolean
|
|
*/
|
|
public function calulateSvg() {
|
|
$renderer = new MathMathML( $this->getTex() );
|
|
$renderer->setMathml( $this->getMathml() );
|
|
$renderer->setMode( MW_MATH_LATEXML );
|
|
$renderer->setPurge( true );
|
|
$res = $renderer->render();
|
|
if ( $res == true ) {
|
|
$this->svg = $renderer->getSvg();
|
|
} else {
|
|
$lastError = $renderer->getLastError();
|
|
wfDebugLog( 'Math', 'failed to convert LaTeXML-MathML to SVG:' . $lastError );
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* Gets the SVG image
|
|
* Lazy evaluation: If no SVG image exists it's generated on the fly
|
|
* @return string XML-Document of the rendered SVG
|
|
*/
|
|
public function getSvg() {
|
|
if ( $this->isPurge() || $this->svg == '' ) {
|
|
$this->calulateSvg();
|
|
}
|
|
return $this->svg;
|
|
}
|
|
|
|
protected function getMathTableName() {
|
|
return 'mathlatexml';
|
|
}
|
|
}
|
|
|