Merge "Mathoid CLI interface"
This commit is contained in:
commit
13640dfeea
|
@ -189,7 +189,7 @@ class MathHooks {
|
||||||
*/
|
*/
|
||||||
static function mathTagHook( $content, $attributes, $parser ) {
|
static function mathTagHook( $content, $attributes, $parser ) {
|
||||||
static $n = 1;
|
static $n = 1;
|
||||||
if ( trim( $content ) === '' ) { // bug 8372
|
if ( trim( $content ) === '' ) { // bug 8372 https://phabricator.wikimedia.org/rSVN18870
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,6 +346,10 @@ class MathHooks {
|
||||||
if ( in_array( $type, [ 'mysql', 'sqlite', 'postgres' ] ) ) {
|
if ( in_array( $type, [ 'mysql', 'sqlite', 'postgres' ] ) ) {
|
||||||
$sql = __DIR__ . '/db/mathoid.' . $type . '.sql';
|
$sql = __DIR__ . '/db/mathoid.' . $type . '.sql';
|
||||||
$updater->addExtensionTable( 'mathoid', $sql );
|
$updater->addExtensionTable( 'mathoid', $sql );
|
||||||
|
if ( $type == 'mysql' ) {
|
||||||
|
$sql = __DIR__ . '/db/patches/mathoid.add_png.mysql.sql';
|
||||||
|
$updater->addExtensionField( 'mathoid', 'math_png', $sql );
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Exception( "Math extension does not currently support $type database for Mathoid." );
|
throw new Exception( "Math extension does not currently support $type database for Mathoid." );
|
||||||
}
|
}
|
||||||
|
@ -373,17 +377,14 @@ class MathHooks {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function onParserAfterTidy( &$parser, &$text ) {
|
public static function onParserAfterTidy( &$parser, &$text ) {
|
||||||
$rbis = [];
|
global $wgMathoidCli;
|
||||||
foreach ( self::$tags as $key => $tag ) {
|
if ( $wgMathoidCli ) {
|
||||||
/** @var MathRenderer $renderer */
|
MathMathMLCli::batchEvaluate( self::$tags );
|
||||||
$renderer = $tag[0];
|
} else {
|
||||||
$rbi = new MathRestbaseInterface( $renderer->getTex(), $renderer->getInputType() );
|
MathMathML::batchEvaluate( self::$tags );
|
||||||
$renderer->setRestbaseInterface( $rbi );
|
|
||||||
$rbis[] = $rbi;
|
|
||||||
}
|
}
|
||||||
MathRestbaseInterface::batchEvaluate( $rbis );
|
|
||||||
foreach ( self::$tags as $key => $tag ) {
|
foreach ( self::$tags as $key => $tag ) {
|
||||||
$value = call_user_func_array( [ "MathHooks","mathPostTagHook" ], $tag );
|
$value = call_user_func_array( [ "MathHooks", "mathPostTagHook" ], $tag );
|
||||||
// Workaround for https://phabricator.wikimedia.org/T103269
|
// Workaround for https://phabricator.wikimedia.org/T103269
|
||||||
$text = preg_replace( '/(<mw:editsection[^>]*>.*?)' . preg_quote( $key ) .
|
$text = preg_replace( '/(<mw:editsection[^>]*>.*?)' . preg_quote( $key ) .
|
||||||
'(.*?)<\/mw:editsection>/',
|
'(.*?)<\/mw:editsection>/',
|
||||||
|
|
|
@ -49,6 +49,18 @@ class MathMathML extends MathRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function batchEvaluate( &$tags ) {
|
||||||
|
$rbis = [];
|
||||||
|
foreach ( $tags as $key => $tag ) {
|
||||||
|
/** @var MathRenderer $renderer */
|
||||||
|
$renderer = $tag[0];
|
||||||
|
$rbi = new MathRestbaseInterface( $renderer->getTex(), $renderer->getInputType() );
|
||||||
|
$renderer->setRestbaseInterface( $rbi );
|
||||||
|
$rbis[] = $rbi;
|
||||||
|
}
|
||||||
|
MathRestbaseInterface::batchEvaluate( $rbis );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the allowed root elements the rendered math tag might have.
|
* Gets the allowed root elements the rendered math tag might have.
|
||||||
*
|
*
|
||||||
|
@ -257,11 +269,7 @@ class MathMathML extends MathRenderer {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function doRender() {
|
protected function doRender() {
|
||||||
if ( $this->getTex() === '' ) {
|
if ( $this->isEmpty() ) {
|
||||||
LoggerFactory::getInstance( 'Math' )->debug(
|
|
||||||
'Rendering was requested, but no TeX string is specified.'
|
|
||||||
);
|
|
||||||
$this->lastError = $this->getError( 'math_empty_tex' );
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$res = '';
|
$res = '';
|
||||||
|
@ -502,12 +510,12 @@ class MathMathML extends MathRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $jsonResult
|
* @param object $jsonResult json result
|
||||||
* @param $host
|
* @param string $host name
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function processJsonResult( $jsonResult, $host ) {
|
protected function processJsonResult( $jsonResult, $host ) {
|
||||||
if ( $this->getMode() == 'latexml' || $this->inputType == 'pmml' ||
|
if ( $this->getMode() == 'latexml' || $this->inputType == 'pmml' ||
|
||||||
$this->isValidMathML( $jsonResult->mml )
|
$this->isValidMathML( $jsonResult->mml )
|
||||||
) {
|
) {
|
||||||
|
@ -536,4 +544,17 @@ class MathMathML extends MathRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isEmpty() {
|
||||||
|
if ( $this->userInputTex === '' ) {
|
||||||
|
LoggerFactory::getInstance( 'Math' )
|
||||||
|
->debug( 'Rendering was requested, but no TeX string is specified.' );
|
||||||
|
$this->lastError = $this->getError( 'math_empty_tex' );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use MediaWiki\Logger\LoggerFactory;
|
||||||
|
use \MediaWiki\MediaWikiServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: Moritz
|
||||||
|
* Date: 15.08.2017
|
||||||
|
* Time: 09:33
|
||||||
|
*/
|
||||||
|
class MathMathMLCli extends MathMathML {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array &$tags math tags
|
||||||
|
* @return bool
|
||||||
|
* @throws MWException
|
||||||
|
*/
|
||||||
|
public static function batchEvaluate( &$tags ) {
|
||||||
|
$req = [];
|
||||||
|
foreach ( $tags as $key => $tag ) {
|
||||||
|
/** @var MathMathMLCli $renderer */
|
||||||
|
$renderer = $tag[0];
|
||||||
|
// checking if the rendering is in the database is no security issue since only the md5
|
||||||
|
// hash of the user input string will be sent to the database
|
||||||
|
if ( !$renderer->isInDatabase() ) {
|
||||||
|
$req[] = $renderer->getMathoidCliQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( count( $req ) === 0 ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$exitCode = 1;
|
||||||
|
$res = self::evaluateWithCli( $req, $exitCode );
|
||||||
|
foreach ( $tags as $key => $tag ) {
|
||||||
|
/** @var MathMathMLCli $renderer */
|
||||||
|
$renderer = $tag[0];
|
||||||
|
if ( !$renderer->isInDatabase() ) {
|
||||||
|
$renderer->initializeFromCliResponse( $res );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Object $res
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function initializeFromCliResponse( $res ) {
|
||||||
|
global $wgMathoidCli;
|
||||||
|
if ( !property_exists( $res, $this->getMd5() ) ) {
|
||||||
|
$this->lastError =
|
||||||
|
$this->getError( 'math_mathoid_error', 'cli',
|
||||||
|
var_export( get_object_vars( $res ) ) );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( $this->isEmpty() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$response = $res->{$this->getMd5()};
|
||||||
|
if ( !$response->success ) {
|
||||||
|
$this->lastError = $this->renderError( $response );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->texSecure = true;
|
||||||
|
$this->tex = $response->sanetex;
|
||||||
|
// The host name is only relevant for the debugging. So using file:// to indicate that the
|
||||||
|
// cli interface seems to be OK.
|
||||||
|
$this->processJsonResult( $response, 'file://' . $wgMathoidCli[0] );
|
||||||
|
$this->mathStyle = $response->mathoidStyle;
|
||||||
|
$this->png = implode( array_map( "chr", $response->png->data ) );
|
||||||
|
$this->changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderError( $response ) {
|
||||||
|
$msg = $response->error;
|
||||||
|
try {
|
||||||
|
switch ( $response->detail->status ) {
|
||||||
|
case "F":
|
||||||
|
$msg .= "\n Found {$response->detail->details}" .
|
||||||
|
$this->appendLocationInfo( $response );
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
case "C":
|
||||||
|
$msg .= $this->appendLocationInfo( $response );
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
// we do not know any cases that triggers this error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( Exception $e ) {
|
||||||
|
// use default error message
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getError( 'math_mathoid_error', 'cli', $msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getMathoidCliQuery() {
|
||||||
|
return [
|
||||||
|
'query' => [
|
||||||
|
'q' => $this->getTex(),
|
||||||
|
'type' => $this->getInputType(),
|
||||||
|
'hash' => $this->getMd5(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $req request
|
||||||
|
* @param int &$exitCode exit code
|
||||||
|
* @return mixed
|
||||||
|
* @throws MWException
|
||||||
|
*/
|
||||||
|
public static function evaluateWithCli( $req, &$exitCode = null ) {
|
||||||
|
global $wgMathoidCli;
|
||||||
|
$json_req = json_encode( $req );
|
||||||
|
$cmd = MediaWikiServices::getInstance()->getShellCommandFactory()->create();
|
||||||
|
$cmd->params( $wgMathoidCli );
|
||||||
|
$cmd->input( $json_req );
|
||||||
|
$result = $cmd->execute();
|
||||||
|
if ( $result->getExitCode() != 0 ) {
|
||||||
|
LoggerFactory::getInstance( 'Math' )->error( 'Can not process {req} with config
|
||||||
|
{conf} returns {res}', [
|
||||||
|
'req' => $req,
|
||||||
|
'conf' => var_export( $wgMathoidCli, true ),
|
||||||
|
'res' => var_export( $result, true ),
|
||||||
|
] );
|
||||||
|
throw new MWException( "Mathoid cli '$wgMathoidCli[0]' is not executable." );
|
||||||
|
}
|
||||||
|
$res = json_decode( $result->getStdout() );
|
||||||
|
if ( !$res ) {
|
||||||
|
throw new MWException( "Mathoid cli response '$res' is no valid JSON file." );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render( $forceReRendering = false ) {
|
||||||
|
if ( $this->getLastError() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doCheck() {
|
||||||
|
// avoid that restbase is called if check is set to always
|
||||||
|
return $this->texSecure;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initializeFromDatabaseRow( $rpage ) {
|
||||||
|
if ( !empty( $rpage->math_svg ) ) {
|
||||||
|
$this->png = $rpage->math_png;
|
||||||
|
}
|
||||||
|
parent::initializeFromDatabaseRow( $rpage ); // TODO: Change the autogenerated stub
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function dbOutArray() {
|
||||||
|
$out = parent::dbOutArray();
|
||||||
|
$out['math_png'] = $this->png;
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function dbInArray() {
|
||||||
|
$out = parent::dbInArray();
|
||||||
|
$out[] = 'math_png';
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPng() {
|
||||||
|
if ( !$this->png ) {
|
||||||
|
$this->initializeFromCliResponse( self::evaluateWithCli( [
|
||||||
|
$this->getMathoidCliQuery(),
|
||||||
|
] ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getPng();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $response object from cli
|
||||||
|
* @return string containing the location information
|
||||||
|
*/
|
||||||
|
private function appendLocationInfo( $response ) {
|
||||||
|
return "in {$response->detail->line}:{$response->detail->column}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,8 @@ abstract class MathRenderer {
|
||||||
protected $mathml = '';
|
protected $mathml = '';
|
||||||
/** @var string SVG layout only (no semantics) */
|
/** @var string SVG layout only (no semantics) */
|
||||||
protected $svg = '';
|
protected $svg = '';
|
||||||
|
/** @var string PNG image only (no semantics) */
|
||||||
|
protected $png = '';
|
||||||
/** @var string the original user input string (which was used to calculate the inputhash) */
|
/** @var string the original user input string (which was used to calculate the inputhash) */
|
||||||
protected $userInputTex = '';
|
protected $userInputTex = '';
|
||||||
// FURTHER PROPERTIES OF THE MATHEMATICAL CONTENT
|
// FURTHER PROPERTIES OF THE MATHEMATICAL CONTENT
|
||||||
|
@ -141,7 +143,7 @@ abstract class MathRenderer {
|
||||||
* @return MathRenderer appropriate renderer for mode
|
* @return MathRenderer appropriate renderer for mode
|
||||||
*/
|
*/
|
||||||
public static function getRenderer( $tex, $params = [], $mode = 'png' ) {
|
public static function getRenderer( $tex, $params = [], $mode = 'png' ) {
|
||||||
global $wgDefaultUserOptions, $wgMathEnableExperimentalInputFormats;
|
global $wgDefaultUserOptions, $wgMathEnableExperimentalInputFormats, $wgMathoidCli;
|
||||||
|
|
||||||
if ( isset( $params['forcemathmode'] ) ) {
|
if ( isset( $params['forcemathmode'] ) ) {
|
||||||
$mode = $params['forcemathmode'];
|
$mode = $params['forcemathmode'];
|
||||||
|
@ -173,7 +175,11 @@ abstract class MathRenderer {
|
||||||
break;
|
break;
|
||||||
case 'mathml':
|
case 'mathml':
|
||||||
default:
|
default:
|
||||||
$renderer = new MathMathML( $tex, $params );
|
if ( $wgMathoidCli ) {
|
||||||
|
$renderer = new MathMathMLCli( $tex, $params );
|
||||||
|
} else {
|
||||||
|
$renderer = new MathMathML( $tex, $params );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LoggerFactory::getInstance( 'Math' )->debug( 'Start rendering $' . $renderer->tex .
|
LoggerFactory::getInstance( 'Math' )->debug( 'Start rendering $' . $renderer->tex .
|
||||||
'$ in mode ' . $mode );
|
'$ in mode ' . $mode );
|
||||||
|
@ -706,4 +712,12 @@ abstract class MathRenderer {
|
||||||
$this->lastError = $checker->getError();
|
$this->lastError = $checker->getError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPng() {
|
||||||
|
return $this->png;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class SpecialMathShowImage extends SpecialPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
function execute( $par ) {
|
function execute( $par ) {
|
||||||
global $wgMathEnableExperimentalInputFormats;
|
global $wgMathEnableExperimentalInputFormats, $wgMathoidCli;
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$hash = $request->getText( 'hash', '' );
|
$hash = $request->getText( 'hash', '' );
|
||||||
$tex = $request->getText( 'tex', '' );
|
$tex = $request->getText( 'tex', '' );
|
||||||
|
@ -51,7 +51,9 @@ class SpecialMathShowImage extends SpecialPage {
|
||||||
} else {
|
} else {
|
||||||
$asciimath = '';
|
$asciimath = '';
|
||||||
}
|
}
|
||||||
$this->mode = MathHooks::mathModeToString( $request->getText( 'mode' ), 'mathml' );
|
$mode = $request->getText( 'mode' );
|
||||||
|
$this->mode = MathHooks::mathModeToString( $mode, 'mathml' );
|
||||||
|
|
||||||
if ( !in_array( $this->mode, MathRenderer::getValidModes() ) ) {
|
if ( !in_array( $this->mode, MathRenderer::getValidModes() ) ) {
|
||||||
// Fallback to the default if an invalid mode was specified
|
// Fallback to the default if an invalid mode was specified
|
||||||
$this->mode = 'mathml';
|
$this->mode = 'mathml';
|
||||||
|
@ -61,22 +63,18 @@ class SpecialMathShowImage extends SpecialPage {
|
||||||
echo $this->printSvgError( 'No Inputhash specified' );
|
echo $this->printSvgError( 'No Inputhash specified' );
|
||||||
} else {
|
} else {
|
||||||
if ( $tex === '' && $asciimath === '' ) {
|
if ( $tex === '' && $asciimath === '' ) {
|
||||||
switch ( $this->mode ) {
|
if ( $wgMathoidCli && $this->mode === 'png' ) {
|
||||||
case 'png':
|
$this->renderer = MathRenderer::getRenderer( '', [], 'mathml' );
|
||||||
$this->renderer = MathTexvc::newFromMd5( $hash );
|
} else {
|
||||||
break;
|
$this->renderer = MathRenderer::getRenderer( '', [], $this->mode );
|
||||||
case 'latexml':
|
|
||||||
$this->renderer = MathLaTeXML::newFromMd5( $hash );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$this->renderer = MathMathML::newFromMd5( $hash );
|
|
||||||
}
|
}
|
||||||
|
$this->renderer->setMd5( $hash );
|
||||||
$this->noRender = $request->getBool( 'noRender', false );
|
$this->noRender = $request->getBool( 'noRender', false );
|
||||||
$isInDatabase = $this->renderer->readFromDatabase();
|
$isInDatabase = $this->renderer->readFromDatabase();
|
||||||
if ( $isInDatabase || $this->noRender ) {
|
if ( $isInDatabase || $this->noRender ) {
|
||||||
$success = $isInDatabase;
|
$success = $isInDatabase;
|
||||||
} else {
|
} else {
|
||||||
if ( $this->mode == 'png' ) {
|
if ( $this->mode == 'png' && !$wgMathoidCli ) {
|
||||||
// get the texvc input from the mathoid database table
|
// get the texvc input from the mathoid database table
|
||||||
// and render the conventional way
|
// and render the conventional way
|
||||||
$mmlRenderer = MathMathML::newFromMd5( $hash );
|
$mmlRenderer = MathMathML::newFromMd5( $hash );
|
||||||
|
|
|
@ -16,5 +16,7 @@ CREATE TABLE /*_*/mathoid (
|
||||||
-- MW_MATHSTYLE_(INLINE_DISPLAYSTYLE|DISPLAY|INLINE)
|
-- MW_MATHSTYLE_(INLINE_DISPLAYSTYLE|DISPLAY|INLINE)
|
||||||
math_style tinyint,
|
math_style tinyint,
|
||||||
-- type of the Math input (TeX, MathML, AsciiMath...)
|
-- type of the Math input (TeX, MathML, AsciiMath...)
|
||||||
math_input_type tinyint
|
math_input_type tinyint,
|
||||||
|
-- png output of mathoid (for higher DPI settings this files might become larger than 64KiB)
|
||||||
|
math_png mediumblob
|
||||||
) /*$wgDBTableOptions*/;
|
) /*$wgDBTableOptions*/;
|
||||||
|
|
|
@ -16,5 +16,7 @@ CREATE TABLE /*_*/mathoid (
|
||||||
-- MW_MATHSTYLE_(INLINE_DISPLAYSTYLE|DISPLAY|INLINE)
|
-- MW_MATHSTYLE_(INLINE_DISPLAYSTYLE|DISPLAY|INLINE)
|
||||||
math_style SMALLINT,
|
math_style SMALLINT,
|
||||||
-- type of the Math input (TeX, MathML, AsciiMath...)
|
-- type of the Math input (TeX, MathML, AsciiMath...)
|
||||||
math_input_type SMALLINT
|
math_input_type SMALLINT,
|
||||||
|
-- png output of mathoid
|
||||||
|
math_png BYTEA
|
||||||
) /*$wgDBTableOptions*/;
|
) /*$wgDBTableOptions*/;
|
||||||
|
|
|
@ -16,5 +16,7 @@ CREATE TABLE /*_*/mathoid (
|
||||||
-- MW_MATHSTYLE_(INLINE_DISPLAYSTYLE|DISPLAY|INLINE)
|
-- MW_MATHSTYLE_(INLINE_DISPLAYSTYLE|DISPLAY|INLINE)
|
||||||
math_style tinyint,
|
math_style tinyint,
|
||||||
-- type of the Math input (TeX, MathML, AsciiMath...)
|
-- type of the Math input (TeX, MathML, AsciiMath...)
|
||||||
math_input_type tinyint
|
math_input_type tinyint,
|
||||||
|
-- png output of mathoid
|
||||||
|
math_png blob
|
||||||
) /*$wgDBTableOptions*/;
|
) /*$wgDBTableOptions*/;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE /*_*/mathoid ADD math_png mediumblob;
|
|
@ -21,6 +21,7 @@
|
||||||
"MathTexvc": "MathTexvc.php",
|
"MathTexvc": "MathTexvc.php",
|
||||||
"MathSource": "MathSource.php",
|
"MathSource": "MathSource.php",
|
||||||
"MathMathML": "MathMathML.php",
|
"MathMathML": "MathMathML.php",
|
||||||
|
"MathMathMLCli": "MathMathMLCli.php",
|
||||||
"MathLaTeXML": "MathLaTeXML.php",
|
"MathLaTeXML": "MathLaTeXML.php",
|
||||||
"MathInputCheck": "MathInputCheck.php",
|
"MathInputCheck": "MathInputCheck.php",
|
||||||
"MathInputCheckTexvc": "MathInputCheckTexvc.php",
|
"MathInputCheckTexvc": "MathInputCheckTexvc.php",
|
||||||
|
@ -109,6 +110,7 @@
|
||||||
"MathConcurrentReqs": 50,
|
"MathConcurrentReqs": 50,
|
||||||
"MathPath": false,
|
"MathPath": false,
|
||||||
"MathTexvcCheckExecutable": false,
|
"MathTexvcCheckExecutable": false,
|
||||||
|
"MathoidCli": false,
|
||||||
"MathValidModes": [
|
"MathValidModes": [
|
||||||
"png",
|
"png",
|
||||||
"source",
|
"source",
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
if ( !document.implementation.hasFeature( 'http://www.w3.org/TR/SVG11/feature#Image', '1.1' ) ) {
|
if ( !document.implementation.hasFeature( 'http://www.w3.org/TR/SVG11/feature#Image', '1.1' ) ) {
|
||||||
$( '.mwe-math-fallback-image-inline, .mwe-math-fallback-image-display' ).each( function () {
|
$( '.mwe-math-fallback-image-inline, .mwe-math-fallback-image-display' ).each( function () {
|
||||||
this.src = this.src.replace( 'media/math/render/svg/', 'media/math/render/png/' );
|
this.src = this.src.replace( 'media/math/render/svg/', 'media/math/render/png/' );
|
||||||
|
this.src = this.src.replace( 'mode=mathml', 'mode=mathml-png' );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
}( jQuery ) );
|
}( jQuery ) );
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the MathML output format.
|
* Test the MathML output format.
|
||||||
*
|
*
|
||||||
|
@ -30,6 +31,11 @@ class MathMathMLTest extends MediaWikiTestCase {
|
||||||
self::$timeout = $timeout;
|
self::$timeout = $timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp(); // TODO: Change the autogenerated stub
|
||||||
|
$this->setMwGlobals( 'wgMathoidCli', false );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests behavior of makeRequest() that communicates with the host.
|
* Tests behavior of makeRequest() that communicates with the host.
|
||||||
* Testcase: Invalid request.
|
* Testcase: Invalid request.
|
||||||
|
@ -61,7 +67,9 @@ class MathMathMLTest extends MediaWikiTestCase {
|
||||||
*/
|
*/
|
||||||
public function testMakeRequestSuccess() {
|
public function testMakeRequestSuccess() {
|
||||||
self::setMockValues( true, true, false );
|
self::setMockValues( true, true, false );
|
||||||
|
self::$content = 'test content';
|
||||||
$url = 'http://example.com/valid';
|
$url = 'http://example.com/valid';
|
||||||
|
/** @var MathMathML */
|
||||||
$renderer = $this->getMockBuilder( 'MathMathML' )
|
$renderer = $this->getMockBuilder( 'MathMathML' )
|
||||||
->setMethods( null )
|
->setMethods( null )
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
|
@ -69,8 +77,8 @@ class MathMathMLTest extends MediaWikiTestCase {
|
||||||
$requestReturn = $renderer->makeRequest( $url, 'a+b', $res, $error,
|
$requestReturn = $renderer->makeRequest( $url, 'a+b', $res, $error,
|
||||||
'MathMLHttpRequestTester' );
|
'MathMLHttpRequestTester' );
|
||||||
$this->assertEquals( true, $requestReturn, "successful call return" );
|
$this->assertEquals( true, $requestReturn, "successful call return" );
|
||||||
$this->assertTrue( $res, 'successfull call' );
|
$this->assertEquals( 'test content', $res, 'successful call' );
|
||||||
$this->assertEquals( $error, '', "successfull call errormessage" );
|
$this->assertEquals( $error, '', "successful call error-message" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers MathInputCheckTexvc
|
||||||
|
*
|
||||||
|
* @group Math
|
||||||
|
*
|
||||||
|
* @licence GNU GPL v2+
|
||||||
|
*/
|
||||||
|
class MathoidCliTest extends MediaWikiTestCase {
|
||||||
|
private $goodInput = '\sin\left(\frac12x\right)';
|
||||||
|
private $badInput = '\newcommand{\text{do evil things}}';
|
||||||
|
protected static $hasMathoidCli;
|
||||||
|
|
||||||
|
public static function setUpBeforeClass() {
|
||||||
|
global $wgMathoidCli;
|
||||||
|
if ( is_array( $wgMathoidCli ) && is_executable( $wgMathoidCli[0] ) ) {
|
||||||
|
self::$hasMathoidCli = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the fixture, for example, opens a network connection.
|
||||||
|
* This method is called before a test is executed.
|
||||||
|
*/
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
if ( !self::$hasMathoidCli ) {
|
||||||
|
$this->markTestSkipped( "No mathoid cli configured on server" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGood() {
|
||||||
|
$mml = new MathMathMLCli( $this->goodInput );
|
||||||
|
$input = [ 'good' => [ $mml ] ];
|
||||||
|
MathMathMLCli::batchEvaluate( $input );
|
||||||
|
$this->assertTrue( $mml->render(), 'assert that renders' );
|
||||||
|
$this->assertContains( '</mo>', $mml->getMathml() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUndefinedFunctionError() {
|
||||||
|
$mml = new MathMathMLCli( $this->badInput );
|
||||||
|
$input = [ 'bad' => [ $mml ] ];
|
||||||
|
MathMathMLCli::batchEvaluate( $input );
|
||||||
|
$this->assertFalse( $mml->render(), 'assert that fails' );
|
||||||
|
$this->assertContains( 'newcommand', $mml->getLastError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSyntaxError() {
|
||||||
|
$mml = new MathMathMLCli( '^' );
|
||||||
|
$input = [ 'bad' => [ $mml ] ];
|
||||||
|
MathMathMLCli::batchEvaluate( $input );
|
||||||
|
$this->assertFalse( $mml->render(), 'assert that fails' );
|
||||||
|
$this->assertContains( 'SyntaxError', $mml->getLastError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCeError() {
|
||||||
|
$mml = new MathMathMLCli( '\ce{H2O}' );
|
||||||
|
$input = [ 'bad' => [ $mml ] ];
|
||||||
|
MathMathMLCli::batchEvaluate( $input );
|
||||||
|
$this->assertFalse( $mml->render(), 'assert that fails' );
|
||||||
|
$this->assertContains( 'SyntaxError', $mml->getLastError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmpty() {
|
||||||
|
$mml = new MathMathMLCli( '' );
|
||||||
|
$input = [ 'bad' => [ $mml ] ];
|
||||||
|
MathMathMLCli::batchEvaluate( $input );
|
||||||
|
$this->assertFalse( $mml->render(), 'assert that renders' );
|
||||||
|
$this->assertFalse( $mml->isTexSecure() );
|
||||||
|
$this->assertContains( 'empty', $mml->getLastError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: Moritz
|
||||||
|
* Date: 14.08.2017
|
||||||
|
* Time: 12:09
|
||||||
|
*/
|
||||||
|
require_once __DIR__ . '/../../../maintenance/Maintenance.php';
|
||||||
|
|
||||||
|
class DummyTest extends Maintenance {
|
||||||
|
const REFERENCE_PAGE = 'mediawikiwiki:Extension:Math/CoverageTest';
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
$this->requireExtension( 'Math' );
|
||||||
|
$this->mDescription = 'Test Mathoid CLI';
|
||||||
|
$this->addArg( 'page', "The page used for the testset generation.", false );
|
||||||
|
$this->addOption( 'offset', "If set the first n equations on the page are skipped", false,
|
||||||
|
true, "o" );
|
||||||
|
$this->addOption( 'length', "If set the only n equations were processed", false, true,
|
||||||
|
"l" );
|
||||||
|
$this->addOption( 'user', "User with rights to view the page", false, true, "u" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getMathTagsFromPage( $titleString ) {
|
||||||
|
global $wgEnableScaryTranscluding;
|
||||||
|
$title = Title::newFromText( $titleString );
|
||||||
|
if ( $title->exists() ) {
|
||||||
|
$article = new Article( $title );
|
||||||
|
$wikiText = $article->getPage()->getContent()->getNativeData();
|
||||||
|
} else {
|
||||||
|
if ( $title == self::REFERENCE_PAGE ) {
|
||||||
|
$wgEnableScaryTranscluding = true;
|
||||||
|
$parser = new Parser();
|
||||||
|
$wikiText = $parser->interwikiTransclude( $title, 'raw' );
|
||||||
|
} else {
|
||||||
|
return 'Page does not exist';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$wikiText = Sanitizer::removeHTMLcomments( $wikiText );
|
||||||
|
$wikiText = preg_replace( '#<nowiki>(.*)</nowiki>#', '', $wikiText );
|
||||||
|
$math = [];
|
||||||
|
Parser::extractTagsAndParams( [ 'math' ], $wikiText, $math );
|
||||||
|
|
||||||
|
return $math;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() {
|
||||||
|
echo "This test accesses the Mathoid CLI.\n";
|
||||||
|
$page = $this->getArg( 0, self::REFERENCE_PAGE );
|
||||||
|
$offset = $this->getOption( 'offset', 0 );
|
||||||
|
$length = $this->getOption( 'length', PHP_INT_MAX );
|
||||||
|
$userName = $this->getOption( 'user', 'Maintenance script' );
|
||||||
|
$wgUser = User::newFromName( $userName );
|
||||||
|
$allEquations = self::getMathTagsFromPage( $page );
|
||||||
|
if ( !is_array( $allEquations ) ) {
|
||||||
|
echo "Could not get equations from page '$page'\n";
|
||||||
|
echo $allEquations . PHP_EOL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
echo 'got ' . count( $allEquations ) . " math tags. Start processing.";
|
||||||
|
}
|
||||||
|
$i = 0;
|
||||||
|
$rend = [];
|
||||||
|
foreach ( array_slice( $allEquations, $offset, $length, true ) as $input ) {
|
||||||
|
$output = MathRenderer::renderMath( $input[1], $input[2], 'mathml' );
|
||||||
|
$rend[] = [ MathRenderer::getRenderer( $input[1], $input[2], 'mathml' ), $input ];
|
||||||
|
$output = preg_replace( '#src="(.*?)/(([a-f]|\d)*).png"#', 'src="\2.png"', $output );
|
||||||
|
$parserTests[] = [ (string)$input[1], $output ];
|
||||||
|
$i ++;
|
||||||
|
echo '.';
|
||||||
|
}
|
||||||
|
echo "Generated $i tests\n";
|
||||||
|
MathMathMLCli::batchEvaluate( $rend );
|
||||||
|
$retval = null;
|
||||||
|
$stdout = "[
|
||||||
|
{
|
||||||
|
\"query\": {
|
||||||
|
\"q\": \"E=mc^{2}\"
|
||||||
|
}}]";
|
||||||
|
|
||||||
|
// $f = TempFSFile::factory( 'mathoid', 'json', wfTempDir() );
|
||||||
|
// $f->autocollect();
|
||||||
|
// $fhandle = fopen( $f->getPath(), 'w' );
|
||||||
|
// if ( $fhandle ) {
|
||||||
|
// fwrite( $fhandle, $stdout );
|
||||||
|
// fclose( $fhandle );
|
||||||
|
// }
|
||||||
|
// $contents =
|
||||||
|
// wfShellExec( '/tmp/mathoid/cli.js -c /tmp/mathoid/config.dev.yaml ' . $f->getPath(),
|
||||||
|
// $retval );
|
||||||
|
// $contents =
|
||||||
|
// wfShellExecMath( '/tmp/mathoid/cli.js -c /tmp/mathoid/config.dev.yaml ',
|
||||||
|
// $retval, [], [], [], $stdout );
|
||||||
|
|
||||||
|
// if ( $retval == 0 ) {
|
||||||
|
// $res = json_decode( $contents, true );
|
||||||
|
// echo "JSON result" . var_export( $res, false ) . "\n";
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$maintClass = 'DummyTest';
|
||||||
|
require_once RUN_MAINTENANCE_IF_MAIN;
|
Loading…
Reference in New Issue