mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
synced 2024-11-27 17:01:07 +00:00
New rendering option LaTeXML
Introduces a new rending option to render TeX to MathML via LaTeXML. Bug: 43222 Change-Id: I5d29e219c0d3b907e22ea0bb3b30f000d8a7a9f8
This commit is contained in:
parent
06d249fea2
commit
927340d345
|
@ -81,7 +81,7 @@ class MathHooks {
|
|||
* @return array of strings
|
||||
*/
|
||||
private static function getMathNames() {
|
||||
global $wgUseMathJax;
|
||||
global $wgUseMathJax, $wgUseLaTeXML;
|
||||
$names = array(
|
||||
MW_MATH_PNG => wfMessage( 'mw_math_png' )->escaped(),
|
||||
MW_MATH_SOURCE => wfMessage( 'mw_math_source' )->escaped(),
|
||||
|
@ -89,7 +89,9 @@ class MathHooks {
|
|||
if ( $wgUseMathJax ) {
|
||||
$names[MW_MATH_MATHJAX] = wfMessage( 'mw_math_mathjax' )->escaped();
|
||||
}
|
||||
|
||||
if ( $wgUseLaTeXML ) {
|
||||
$names[MW_MATH_LATEXML] = wfMessage( 'mw_math_latexml' )->escaped();
|
||||
}
|
||||
return $names;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ $messages['en'] = array(
|
|||
'mw_math_png' => 'Always render PNG',
|
||||
'mw_math_source' => 'Leave it as TeX (for text browsers)',
|
||||
'mw_math_mathjax' => 'MathJax (experimental; best for most browsers)',
|
||||
'mw_math_latexml' => 'LaTeXML (experimental; uses MathML)',
|
||||
|
||||
// Math errors
|
||||
'math_failure' => 'Failed to parse',
|
||||
|
@ -34,6 +35,10 @@ $messages['en'] = array(
|
|||
'math_bad_output' => 'Cannot write to or create math output directory',
|
||||
'math_notexvc' => 'Missing texvc executable; please see math/README to configure.',
|
||||
'math_output_error' => 'Cannot store math image on filesystem.',
|
||||
'math_latexml_timeout' => 'LaTeXML Timeout from \'$1\'',
|
||||
'math_latexml_invalidresponse' => 'LaTeXML Invalid response from server \'$1\':',
|
||||
'math_latexml_invalidxml' => 'LaTeXML MathML is invalid XML.',
|
||||
'math_latexml_invalidjson' => 'LaTeXML Server response is invalid JSON.',
|
||||
);
|
||||
|
||||
/** Message documentation (Message documentation)
|
||||
|
@ -41,6 +46,7 @@ $messages['en'] = array(
|
|||
* @author Kizito
|
||||
* @author Shirayuki
|
||||
* @author Siebrand
|
||||
* @author Physikerwelt
|
||||
*/
|
||||
$messages['qqq'] = array(
|
||||
'math-desc' => '{{desc|name=Math|url=http://www.mediawiki.org/wiki/Extension:Math}}',
|
||||
|
@ -53,19 +59,28 @@ Used as label for radio button.
|
|||
|
||||
See also:
|
||||
* {{msg-mw|Mw math source}}
|
||||
* {{msg-mw|Mw math mathjax}}',
|
||||
* {{msg-mw|Mw math mathjax}}
|
||||
* {{msg-mw|Mw math latexml}}',
|
||||
'mw_math_source' => 'In user preferences (math). All mw_math_* messages MUST be different, things will break otherwise!
|
||||
|
||||
Used as label for radio button.
|
||||
Used as label for source radio button.
|
||||
|
||||
See also:
|
||||
* {{msg-mw|Mw math png}}
|
||||
* {{msg-mw|Mw math mathjax}}
|
||||
* {{msg-mw|Mw math latexml}}',
|
||||
'mw_math_mathjax' => 'Used as label for mathjax radio button.
|
||||
|
||||
See also:
|
||||
* {{msg-mw|Mw math png}}
|
||||
* {{msg-mw|Mw math source}}
|
||||
* {{msg-mw|Mw math latexml}}',
|
||||
'mw_math_latexml' => 'Used as label for latexml radio button.
|
||||
|
||||
See also:
|
||||
* {{msg-mw|Mw math png}}
|
||||
* {{msg-mw|Mw math source}}
|
||||
* {{msg-mw|Mw math mathjax}}',
|
||||
'mw_math_mathjax' => 'Used as label for radio button.
|
||||
|
||||
See also:
|
||||
* {{msg-mw|Mw math png}}
|
||||
* {{msg-mw|Mw math source}}',
|
||||
'math_failure' => 'Used as error message.
|
||||
|
||||
This message is followed by "(", Error message(*1), Additional message, "): " and Source code.
|
||||
|
@ -79,7 +94,12 @@ This message is followed by "(", Error message(*1), Additional message, "): " a
|
|||
* {{msg-mw|Math bad tmpdir}}
|
||||
* {{msg-mw|Math bad output}}
|
||||
* {{msg-mw|Math notexvc}}
|
||||
* {{msg-mw|Math output error}}',
|
||||
* {{msg-mw|Math output error}}
|
||||
* {{msg-mw|Math latexml timeout}}
|
||||
* {{msg-mw|Math latexml noresponse}}
|
||||
* {{msg-mw|Math latexml invalidxml}}
|
||||
* {{msg-mw|Math latexml invalidjson}}
|
||||
',
|
||||
'math_unknown_error' => 'Used as error message for unknown texvc error.
|
||||
|
||||
This message follows the message {{msg-mw|Math failure}}.
|
||||
|
@ -113,6 +133,18 @@ This message follows the message {{msg-mw|Math failure}}.',
|
|||
'math_output_error' => 'Used as error message if the texvc output file could not be stored.
|
||||
|
||||
This message follows the message {{msg-mw|Math failure}}.',
|
||||
'math_latexml_timeout' => 'Used as error message.
|
||||
|
||||
This message follows the message {{msg-mw|Math failure}}. ',
|
||||
'math_latexml_invalidresponse' => 'Used as error message.
|
||||
|
||||
This message follows the message {{msg-mw|Math failure}}. ',
|
||||
'math_latexml_invalidxml' => 'Used as error message.
|
||||
|
||||
This message follows the message {{msg-mw|Math failure}}. ',
|
||||
'math_latexml_invalidjson' => 'Used as error message.
|
||||
|
||||
This message follows the message {{msg-mw|Math failure}}. ',
|
||||
);
|
||||
|
||||
/** Achinese (Acèh)
|
||||
|
|
30
Math.php
30
Math.php
|
@ -22,7 +22,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
|
|||
$wgExtensionCredits['parserhook'][] = array(
|
||||
'path' => __FILE__,
|
||||
'name' => 'Math',
|
||||
'version' => '1.0',
|
||||
'version' => '1.1',
|
||||
'author' => array( 'Tomasz Wegrzanowski', 'Brion Vibber', '...' ),
|
||||
'descriptionmsg' => 'math-desc',
|
||||
'url' => 'https://www.mediawiki.org/wiki/Extension:Math',
|
||||
|
@ -38,6 +38,7 @@ define( 'MW_MATH_SOURCE', 3 );
|
|||
define( 'MW_MATH_MODERN', 4 ); /// @deprecated
|
||||
define( 'MW_MATH_MATHML', 5 ); /// @deprecated
|
||||
define( 'MW_MATH_MATHJAX', 6 ); /// new in 1.19/1.20
|
||||
define( 'MW_MATH_LATEXML', 7 ); /// new in 1.22
|
||||
/**@}*/
|
||||
|
||||
/** For back-compat */
|
||||
|
@ -101,6 +102,32 @@ $wgMathDirectory = false;
|
|||
*/
|
||||
$wgUseMathJax = false;
|
||||
|
||||
/**
|
||||
* Use of LaTeXML for details see
|
||||
* <http://latexml.mathweb.org/help>
|
||||
*
|
||||
* If you want or need to run your own server, follow these installation
|
||||
* instructions and override $wgLaTeXMLUrl:
|
||||
* <https://svn.mathweb.org/repos/LaTeXML/branches/arXMLiv/INSTALL>
|
||||
*
|
||||
* If you expect heavy load you can specify multiple servers. In that case one
|
||||
* server is randomly chosen for each rendering process. Specify the list of
|
||||
* servers in an array e.g $wgLaTeXMLUrl = array ( 'http://latexml.example.com/convert',
|
||||
* 'http://latexml2.example.com/convert');
|
||||
*/
|
||||
$wgLaTeXMLUrl = 'http://latexml.mathweb.org/convert';
|
||||
|
||||
/**
|
||||
* Allows to use LaTeXML as renderer for mathematical equation.
|
||||
*/
|
||||
$wgUseLaTeXML = false;
|
||||
|
||||
/**
|
||||
* The timeout for the HTTP-Request sent to the LaTeXML to render an equation,
|
||||
* in seconds.
|
||||
*/
|
||||
$wgLaTeXMLTimeout = 240;
|
||||
|
||||
////////// end of config settings.
|
||||
|
||||
$wgDefaultUserOptions['math'] = MW_MATH_PNG;
|
||||
|
@ -119,6 +146,7 @@ $wgAutoloadClasses['MathRenderer'] = $dir . 'MathRenderer.php';
|
|||
$wgAutoloadClasses['MathTexvc'] = $dir . 'MathTexvc.php';
|
||||
$wgAutoloadClasses['MathSource'] = $dir . 'MathSource.php';
|
||||
$wgAutoloadClasses['MathMathJax'] = $dir . 'MathMathJax.php';
|
||||
$wgAutoloadClasses['MathLaTeXML'] = $dir . 'MathLaTeXML.php';
|
||||
$wgExtensionMessagesFiles['Math'] = $dir . 'Math.i18n.php';
|
||||
|
||||
$wgParserTestFiles[] = $dir . 'mathParserTests.txt';
|
||||
|
|
247
MathLaTeXML.php
Normal file
247
MathLaTeXML.php
Normal file
|
@ -0,0 +1,247 @@
|
|||
<?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 MathRenderer {
|
||||
|
||||
/**
|
||||
* @var String settings for LaTeXML daemon
|
||||
*/
|
||||
private $LaTeXMLSettings = '';
|
||||
const DEFAULT_LATEXML_SETTING = 'format=xhtml&whatsin=math&whatsout=math&pmml&cmml&nodefaultresources&preload=LaTeX.pool&preload=article.cls&preload=amsmath.sty&preload=amsthm.sty&preload=amstext.sty&preload=amssymb.sty&preload=eucal.sty&preload=[dvipsnames]xcolor.sty&preload=url.sty&preload=hyperref.sty&preload=[ids]latexml.sty&preload=texvc';
|
||||
|
||||
/**
|
||||
* Gets the settings for the LaTeXML daemon.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLaTeXMLSettings() {
|
||||
if ( $this->LaTeXMLSettings ) {
|
||||
return $this->LaTeXMLSettings;
|
||||
} else {
|
||||
return self::DEFAULT_LATEXML_SETTING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $settings
|
||||
*/
|
||||
public function setLaTeXMLSettings( $settings ) {
|
||||
$this->LaTeXMLSettings = $settings;
|
||||
}
|
||||
|
||||
/* (non-PHPdoc)
|
||||
* @see MathRenderer::render()
|
||||
*/
|
||||
public function render( $forceReRendering = false ) {
|
||||
if ( $forceReRendering ) {
|
||||
$this->setPurge( true );
|
||||
}
|
||||
if ( $this->renderingRequired() ) {
|
||||
$res = $this->doRender( );
|
||||
if ( ! $res ) {
|
||||
return $this->getLastError();
|
||||
}
|
||||
}
|
||||
return $this->getMathMLTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to checks if the math tag must be rendered.
|
||||
* @return boolean
|
||||
*/
|
||||
private function renderingRequired() {
|
||||
if ( $this->isPurge() ) {
|
||||
wfDebugLog( "Math", "Rerendering was requested." );
|
||||
return true;
|
||||
} else {
|
||||
$dbres = $this->readFromDatabase();
|
||||
if ( $dbres ) {
|
||||
if ( self::isValidMathML( $this->getMathml() ) ) {
|
||||
wfDebugLog( "Math", "Valid entry found in database." );
|
||||
return false;
|
||||
} else {
|
||||
wfDebugLog( "Math", "Malformatted entry found in database" );
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
wfDebugLog( "Math", "No entry found in database." );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a HTTP Post request to the given host.
|
||||
* Uses $wgLaTeXMLTimeout as timeout.
|
||||
* Generates error messages on failure
|
||||
* @see Http::post()
|
||||
*
|
||||
* @param string $host
|
||||
* @param string $post the encoded post request
|
||||
* @param mixed $res the result
|
||||
* @param mixed $error the formatted error message or null
|
||||
* @param String $httpRequestClass class name of MWHttpRequest (needed for testing only)
|
||||
* @return boolean success
|
||||
*/
|
||||
public function makeRequest( $host, $post, &$res, &$error = '', $httpRequestClass = 'MWHttpRequest' ) {
|
||||
global $wgLaTeXMLTimeout;
|
||||
$error = '';
|
||||
$res = null;
|
||||
$options = array( 'method' => 'POST', 'postData' => $post, 'timeout' => $wgLaTeXMLTimeout );
|
||||
$req = $httpRequestClass::factory( $host, $options );
|
||||
$status = $req->execute();
|
||||
if ( $status->isGood() ) {
|
||||
$res = $req->getContent();
|
||||
return true;
|
||||
} else {
|
||||
if ( $status->hasMessage( 'http-timed-out' ) ) {
|
||||
$error = $this->getError( 'math_latexml_timeout', $host );
|
||||
$res = false;
|
||||
wfDebugLog( "Math", "\nLaTeXML Timeout:"
|
||||
. var_export( array( 'post' => $post, 'host' => $host
|
||||
, 'wgLaTeXMLTimeout' => $wgLaTeXMLTimeout ), true ) . "\n\n" );
|
||||
} else {
|
||||
// for any other unkonwn http error
|
||||
$errormsg = $status->getHtml();
|
||||
$error = $this->getError( 'math_latexml_invalidresponse', $host, $errormsg );
|
||||
wfDebugLog( "Math", "\nLaTeXML NoResponse:"
|
||||
. var_export( array( 'post' => $post, 'host' => $host
|
||||
, 'errormsg' => $errormsg ), true ) . "\n\n" );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-PHPdoc)
|
||||
* @see MathRenderer::writeCache()
|
||||
*/
|
||||
public function writeCache() {
|
||||
if ( $this->isChanged() ) {
|
||||
$this->writeToDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a LaTeXML daemon.
|
||||
* If more than one demon are availible one is chosen from the
|
||||
* $wgLaTeXMLUrl array.
|
||||
* @return string
|
||||
*/
|
||||
private static function pickHost() {
|
||||
global $wgLaTeXMLUrl;
|
||||
if ( is_array( $wgLaTeXMLUrl ) ) {
|
||||
$host = array_rand( $wgLaTeXMLUrl );
|
||||
} else {
|
||||
$host = $wgLaTeXMLUrl;
|
||||
}
|
||||
wfDebugLog( "Math", "picking host " . $host );
|
||||
return $host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual web request to convert TeX to MathML.
|
||||
* @return boolean
|
||||
*/
|
||||
private function doRender( ) {
|
||||
$host = self::pickHost();
|
||||
$texcmd = urlencode( $this->tex );
|
||||
$post = $this->getLaTeXMLSettings();
|
||||
$post .= '&tex=' . $texcmd;
|
||||
$this->lastError = '';
|
||||
if ( $this->makeRequest( $host, $post, $res, $this->lastError ) ) {
|
||||
$result = json_decode( $res );
|
||||
if ( json_last_error() === JSON_ERROR_NONE ) {
|
||||
if ( self::isValidMathML( $result->result ) ) {
|
||||
$this->setMathml( $result->result );
|
||||
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_latexml_invalidxml', $host );
|
||||
wfDebugLog( "Math", "\nLaTeXML InvalidMathML:"
|
||||
. var_export( array( 'post' => $post, 'host' => $host
|
||||
, 'result' => $result ), true ) . "\n\n" );
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->lastError = $this->getError( 'math_latexml_invalidjson', $host );
|
||||
wfDebugLog( "Math", "\nLaTeXML InvalidJSON:"
|
||||
. var_export( array( 'post' => $post, 'host' => $host
|
||||
, 'res' => $res ), true ) . "\n\n" );
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Error message has already been set.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the input is valid MathML,
|
||||
* and if the root element has the name math
|
||||
* @param string $XML
|
||||
* @return boolean
|
||||
*/
|
||||
static public function isValidMathML( $XML ) {
|
||||
$out = false;
|
||||
$prevInternalErrors = libxml_use_internal_errors( true );
|
||||
$xmlObject = simplexml_load_string( $XML );
|
||||
if ( !$xmlObject ) {
|
||||
wfDebugLog( "Math", "XML validation error:\n " . var_export( $XML, true ) . "\n" );
|
||||
foreach ( libxml_get_errors() as $error ) {
|
||||
wfDebugLog( "Math", "\t" . $error->message );
|
||||
}
|
||||
libxml_clear_errors();
|
||||
} else {
|
||||
$name = $xmlObject->getName();
|
||||
if ( $name == "math" or $name == "table" or $name == "div" ) {
|
||||
$out = true;
|
||||
} else {
|
||||
wfDebugLog( "Math", "got wrong root element " . $name );
|
||||
}
|
||||
}
|
||||
libxml_use_internal_errors( $prevInternalErrors );
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal version of @link self::embedMathML
|
||||
* @return string
|
||||
* @return html element with rendered math
|
||||
*/
|
||||
private 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)
|
||||
* @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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -25,19 +25,24 @@ abstract class MathRenderer {
|
|||
* The following variables should made private, as soon it can be verified
|
||||
* that they are not being directly accessed by other extensions.
|
||||
*/
|
||||
var $mode = MW_MATH_PNG;
|
||||
var $tex = '';
|
||||
protected $mode = MW_MATH_PNG;
|
||||
protected $tex = '';
|
||||
/**
|
||||
* is calculated by texvc.
|
||||
* @var string
|
||||
*/
|
||||
var $hash = '';
|
||||
var $html = '';
|
||||
var $mathml = '';
|
||||
var $conservativeness = 0;
|
||||
var $params = '';
|
||||
var $changed = false;
|
||||
protected $hash = '';
|
||||
protected $html = '';
|
||||
protected $mathml = '';
|
||||
protected $conservativeness = 0;
|
||||
protected $params = '';
|
||||
protected $changed = false;
|
||||
/**
|
||||
* @var boolean forces rerendering if set to true
|
||||
*/
|
||||
protected $purge = false;
|
||||
protected $recall;
|
||||
protected $lastError = '';
|
||||
|
||||
/**
|
||||
* Constructs a base MathRenderer
|
||||
|
@ -45,7 +50,7 @@ abstract class MathRenderer {
|
|||
* @param string $tex (optional) LaTeX markup
|
||||
* @param array $params (optional) HTML attributes
|
||||
*/
|
||||
public function __construct( $tex='', $params = array() ) {
|
||||
public function __construct( $tex = '', $params = array() ) {
|
||||
$this->tex = $tex;
|
||||
$this->params = $params;
|
||||
}
|
||||
|
@ -73,7 +78,7 @@ abstract class MathRenderer {
|
|||
*/
|
||||
public static function getRenderer( $tex, $params = array(), $mode = MW_MATH_PNG ) {
|
||||
global $wgDefaultUserOptions;
|
||||
$validModes = array( MW_MATH_PNG, MW_MATH_SOURCE, MW_MATH_MATHJAX );
|
||||
$validModes = array( MW_MATH_PNG, MW_MATH_SOURCE, MW_MATH_MATHJAX, MW_MATH_LATEXML );
|
||||
if ( !in_array( $mode, $validModes ) )
|
||||
$mode = $wgDefaultUserOptions['math'];
|
||||
switch ( $mode ) {
|
||||
|
@ -83,11 +88,14 @@ abstract class MathRenderer {
|
|||
case MW_MATH_MATHJAX:
|
||||
$renderer = new MathMathJax( $tex, $params );
|
||||
break;
|
||||
case MW_MATH_LATEXML:
|
||||
$renderer = new MathLaTeXML( $tex, $params );
|
||||
break;
|
||||
case MW_MATH_PNG:
|
||||
default:
|
||||
$renderer = new MathTexvc( $tex, $params );
|
||||
}
|
||||
wfDebugLog ( "Math", 'start rendering $' . $renderer->tex . '$' );
|
||||
wfDebugLog ( "Math", 'start rendering $' . $renderer->tex . '$ in mode ' . $mode );
|
||||
return $renderer;
|
||||
}
|
||||
|
||||
|
@ -152,10 +160,12 @@ abstract class MathRenderer {
|
|||
$this->hash = $xhash['md5'];
|
||||
$this->conservativeness = $rpage->math_html_conservativeness;
|
||||
$this->html = $rpage->math_html;
|
||||
$this->mathml = $rpage->math_mathml;
|
||||
$this->mathml = utf8_decode( $rpage->math_mathml);
|
||||
if ( StringUtils::isUtf8( $this->mathml ) ) {
|
||||
$this->recall = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
# Missing from the database and/or the render cache
|
||||
$this->recall = false;
|
||||
|
@ -189,7 +199,7 @@ abstract class MathRenderer {
|
|||
'math_outputhash' => $outmd5_sql ,
|
||||
'math_html_conservativeness' => $this->conservativeness,
|
||||
'math_html' => $this->html,
|
||||
'math_mathml' => $this->mathml,
|
||||
'math_mathml' => utf8_encode( $this->mathml ),
|
||||
),
|
||||
__METHOD__
|
||||
);
|
||||
|
@ -210,6 +220,8 @@ abstract class MathRenderer {
|
|||
$attribs = Sanitizer::mergeAttributes( $attribs, $overrides );
|
||||
return $attribs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes cache. Does nothing by default
|
||||
*/
|
||||
|
@ -332,11 +344,11 @@ abstract class MathRenderer {
|
|||
* @param array() $params
|
||||
*/
|
||||
public function setParams( $params ) {
|
||||
//$changed is not set to true here, because the attributes do not affect
|
||||
//the rendering in the current implementation.
|
||||
//If this behavior will change in the future $this->tex is no longer a
|
||||
//primary key and the input hash cannot be calculate form $this->tex
|
||||
//only. See the discussion 'Tag extensions in Block mode' on wikitech-l.
|
||||
// $changed is not set to true here, because the attributes do not affect
|
||||
// the rendering in the current implementation.
|
||||
// If this behavior will change in the future $this->tex is no longer a
|
||||
// primary key and the input hash cannot be calculate form $this->tex
|
||||
// only. See the discussion 'Tag extensions in Block mode' on wikitech-l.
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
|
@ -349,5 +361,32 @@ abstract class MathRenderer {
|
|||
return $this->changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is an explicit user request to rerender the math-tag.
|
||||
* @return boolean
|
||||
*/
|
||||
function isPurge( ) {
|
||||
if ( $this->purge ) {
|
||||
return true;
|
||||
}
|
||||
// TODO: Figure out if ?action=purge
|
||||
// until this issue is resolved we use ?mathpurge=true instead
|
||||
global $wgRequest;
|
||||
return ( $wgRequest->getVal( 'mathpurge' ) === "true" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets purge. If set to true the render is forced to rerender and must not
|
||||
* use a cached version.
|
||||
* @return boolean
|
||||
*/
|
||||
function setPurge( $purge = true ) {
|
||||
$this->changed = true;
|
||||
$this->purge = $purge;
|
||||
}
|
||||
|
||||
function getLastError(){
|
||||
return $this->lastError;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
README
10
README
|
@ -7,6 +7,16 @@ This version (for MediaWiki 1.19) has some changes since previous versions:
|
|||
See the README in the math subdirectory for more info on setting up the
|
||||
low-level conversion tools.
|
||||
|
||||
MathML support:
|
||||
If you prefer MathML rather than images you can use LaTeXML to convert the
|
||||
math tags to MathML. To use that feature you have to enable LaTeXML by setting
|
||||
$wgUseLaTeXML = true;
|
||||
It is possible to choose LaTeXML as default option (for anonymous user) by setting
|
||||
$wgDefaultUserOptions['math'] = MW_MATH_LATEXML;
|
||||
in the LocalSettings.php file.
|
||||
The LaTeXML option requires php5-curl to be installed. Without php5-curl no proper
|
||||
error handling can be guaranteed.
|
||||
|
||||
MathJax configuration:
|
||||
Client-side configuration of MathJax can be done by specifying a mathJax.config
|
||||
table, which takes a table as described in:
|
||||
|
|
|
@ -16,7 +16,7 @@ CREATE TABLE /*_*/math (
|
|||
-- HTML output from texvc, if any
|
||||
math_html text,
|
||||
|
||||
-- MathML output from texvc, if any
|
||||
-- MathML output from texvc, or from LaTeXML
|
||||
math_mathml text
|
||||
) /*$wgDBTableOptions*/;
|
||||
|
||||
|
|
|
@ -72,8 +72,8 @@ class MathDatabaseTest extends MediaWikiTestCase {
|
|||
// comparing the class object does now work due to null values etc.
|
||||
// $this->assertEquals($this->renderer,$renderer2);
|
||||
$this->assertEquals( $this->renderer->getTex(), $renderer2->getTex(), "test if tex is the same" );
|
||||
$this->assertEquals( $this->renderer->mathml, $renderer2->mathml, "Check MathML encoding" );
|
||||
$this->assertEquals( $this->renderer->html, $renderer2->html );
|
||||
$this->assertEquals( $this->renderer->getMathml(), $renderer2->getMathml(), "Check MathML encoding" );
|
||||
$this->assertEquals( $this->renderer->getHtml(), $renderer2->getHtml() );
|
||||
}
|
||||
|
||||
|
||||
|
|
146
tests/MathLaTeXMLTest.php
Normal file
146
tests/MathLaTeXMLTest.php
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
/**
|
||||
* Test the LaTeXML output format.
|
||||
*
|
||||
* @group Math
|
||||
*/
|
||||
class MathLaTeXMLTest extends MediaWikiTestCase {
|
||||
|
||||
// State-variables for HTTP Mockup classes
|
||||
public static $content = null;
|
||||
public static $good = false;
|
||||
public static $html = false;
|
||||
public static $timeout = false;
|
||||
|
||||
/**
|
||||
* Set the mock values for the HTTP Mockup classes
|
||||
*
|
||||
* @param boolean $good
|
||||
* @param mixed $html HTML of the error message or false if no error is present.
|
||||
* @param boolean $timeout true if
|
||||
*/
|
||||
public static function setMockValues( $good, $html, $timeout ) {
|
||||
self::$good = $good;
|
||||
self::$html = $html;
|
||||
self::$timeout = $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests behavior of makeRequest() that communicates with the host.
|
||||
* Testcase: Invalid request.
|
||||
* @covers MathTexvc::makeRequest
|
||||
*/
|
||||
public function testMakeRequestInvalid() {
|
||||
self::setMockValues( false, false, false );
|
||||
$url = 'http://example.com/invalid';
|
||||
|
||||
$renderer = $this->getMockBuilder( 'MathLaTeXML' )
|
||||
->setMethods( NULL )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$requestReturn = $renderer->makeRequest( $url, 'a+b', $res, $error
|
||||
, 'LaTeXMLHttpRequestTester' );
|
||||
$this->assertEquals( false, $requestReturn
|
||||
, "requestReturn is false if HTTP::post returns false." );
|
||||
$this->assertEquals( false, $res
|
||||
, "res is false if HTTP:post returns false." );
|
||||
$errmsg = wfMessage( 'math_latexml_invalidresponse' , $url )
|
||||
->inContentLanguage()->escaped();
|
||||
$this->assertContains( $errmsg, $error
|
||||
, "return an error if HTTP::post returns false" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests behavior of makeRequest() that communicates with the host.
|
||||
* Testcase: Valid request.
|
||||
* @covers MathTexvc::makeRequest
|
||||
*/
|
||||
public function testMakeRequestSuccess() {
|
||||
self::setMockValues( true, true, false );
|
||||
$url = 'http://example.com/valid';
|
||||
$renderer = $this->getMockBuilder( 'MathLaTeXML' )
|
||||
->setMethods( NULL )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$requestReturn = $renderer->makeRequest( $url, 'a+b', $res, $error
|
||||
, 'LaTeXMLHttpRequestTester' );
|
||||
$this->assertEquals( true, $requestReturn, "successful call return" );
|
||||
$this->isTrue( $res, "successfull call" );
|
||||
$this->assertEquals( $error,'', "successfull call errormessage" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests behavior of makeRequest() that communicates with the host.
|
||||
* Testcase: Timeout.
|
||||
* @covers MathTexvc::makeRequest
|
||||
*/
|
||||
public function testMakeRequestTimeout() {
|
||||
self::setMockValues( false, true, true );
|
||||
$url = 'http://example.com/timeout';
|
||||
$renderer = $this->getMockBuilder( 'MathLaTeXML' )
|
||||
->setMethods( NULL )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$requestReturn = $renderer->makeRequest( $url, '$\longcommand$', $res
|
||||
, $error, 'LaTeXMLHttpRequestTester' );
|
||||
$this->assertEquals( false, $requestReturn, "timeout call return" );
|
||||
$this->assertEquals( false, $res, "timeout call return" );
|
||||
$errmsg = wfMessage( 'math_latexml_timeout', $url )
|
||||
->inContentLanguage()->escaped();
|
||||
$this->assertContains( $errmsg, $error, "timeout call errormessage" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the basic functionallity
|
||||
* i.e. if the span element is generated right.
|
||||
*/
|
||||
public function testIntegration() {
|
||||
global $wgLaTeXMLTimeout;
|
||||
$wgLaTeXMLTimeout = 20;
|
||||
$renderer = MathRenderer::getRenderer( "a+b", array(), MW_MATH_LATEXML );
|
||||
$real = $renderer->render( true );
|
||||
$expected = '<span class="tex" dir="ltr" id="a_b"><math xmlns="http://www.w3.org/1998/Math/MathML" id="p1.1.m1" class="ltx_Math" alttext="a+b" xml:id="p1.1.m1.1" display="inline" xref="p1.1.m1.1.cmml"> <semantics xml:id="p1.1.m1.1a" xref="p1.1.m1.1.cmml"> <mrow xml:id="p1.1.m1.1.4" xref="p1.1.m1.1.4.cmml"> <mi xml:id="p1.1.m1.1.1" xref="p1.1.m1.1.1.cmml">a</mi> <mo xml:id="p1.1.m1.1.2" xref="p1.1.m1.1.2.cmml">+</mo> <mi xml:id="p1.1.m1.1.3" xref="p1.1.m1.1.3.cmml">b</mi> </mrow> <annotation-xml xml:id="p1.1.m1.1.cmml" encoding="MathML-Content" xref="p1.1.m1.1"> <apply xml:id="p1.1.m1.1.4.cmml" xref="p1.1.m1.1.4"> <plus xml:id="p1.1.m1.1.2.cmml" xref="p1.1.m1.1.2"/> <ci xml:id="p1.1.m1.1.1.cmml" xref="p1.1.m1.1.1">a</ci> <ci xml:id="p1.1.m1.1.3.cmml" xref="p1.1.m1.1.3">b</ci> </apply> </annotation-xml> <annotation xml:id="p1.1.m1.1b" encoding="application/x-tex" xref="p1.1.m1.1.cmml">a+b</annotation> </semantics> </math></span>';
|
||||
$this->assertEquals( $expected, $real
|
||||
, "Rendering of a+b in plain Text mode" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for testing
|
||||
* @author physikerwelt
|
||||
* @see MWHttpRequestTester
|
||||
*
|
||||
*/
|
||||
class LaTeXMLHttpRequestTester {
|
||||
public static function factory() {
|
||||
return new LaTeXMLHttpRequestTester();
|
||||
}
|
||||
public static function execute() {
|
||||
return new LaTeXMLTestStatus();
|
||||
}
|
||||
public static function getContent() {
|
||||
return MathLaTeXMLTest::$content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for testing
|
||||
* @author physikerwelt
|
||||
* @see Status
|
||||
*/
|
||||
class LaTeXMLTestStatus {
|
||||
static function isGood() {
|
||||
return MathLaTeXMLTest::$good;
|
||||
}
|
||||
|
||||
static function hasMessage( $s ) {
|
||||
if ( $s == 'http-timed-out' ) {
|
||||
return MathLaTeXMLTest::$timeout;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static function getHtml() {
|
||||
return MathLaTeXMLTest::$html;
|
||||
}
|
||||
}
|
|
@ -63,4 +63,14 @@ class MathRendererTest extends MediaWikiTestCase {
|
|||
$this->assertEquals( $renderer->isChanged(), true
|
||||
, "assumes that changing a hash sets changed to true");
|
||||
}
|
||||
|
||||
public function testSetPurge(){
|
||||
$renderer = $this->getMockBuilder( 'MathRenderer' )
|
||||
->setMethods( array( 'render' ) )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$renderer->setPurge();
|
||||
$this->assertEquals( $renderer->isPurge(), true, "Test purge." );
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue