Serve png mode from mathoid

* Use the exactly same routines to deliver png images that are used in
  mathml mode.
* Change the output to use mathoids png image rather than the mathml
  and svg output.
* Locally tested on Firefox and Chrome: Depending on the mode either
  the SVG or the PNG path is used.

Bug: T74240
Change-Id: I4b1cac92eb9a02190f316faab6621940951603d5
This commit is contained in:
Moritz Schubotz (physikerwelt) 2018-05-18 19:08:51 +02:00
parent 22d63b1973
commit b5cf0e0b77
No known key found for this signature in database
GPG Key ID: 73D26C61BAB32E94
9 changed files with 2369 additions and 1850 deletions

View File

@ -31,7 +31,8 @@
"MathValidator": "src/MathValidator.php", "MathValidator": "src/MathValidator.php",
"MathFormatter": "src/MathFormatter.php", "MathFormatter": "src/MathFormatter.php",
"MathWikidataHook": "src/MathWikidataHook.php", "MathWikidataHook": "src/MathWikidataHook.php",
"MathMLRdfBuilder": "src/MathMLRdfBuilder.php" "MathMLRdfBuilder": "src/MathMLRdfBuilder.php",
"MathPng": "src/MathPng.php"
}, },
"DefaultUserOptions": { "DefaultUserOptions": {
"math": "mathml" "math": "mathml"

View File

@ -78,8 +78,12 @@ class MathGenerateTests extends Maintenance {
$i = 0; $i = 0;
foreach ( array_slice( $allEquations, $offset, $length, true ) as $input ) { foreach ( array_slice( $allEquations, $offset, $length, true ) as $input ) {
$output = MathRenderer::renderMath( $input[1], $input[2], 'png' ); $output = MathRenderer::renderMath( $input[1], $input[2], 'png' );
$output = preg_replace( '#src="(.*?)/(([a-f]|\d)*).png"#', 'src="\2.png"', $output ); $output = preg_replace( '#src="(.*?)/(([a-f]|\d)*)"#', 'src="\2"', $output );
$parserTests[] = [ (string)$input[1], $output ]; $parserTests[] = [
'input' => (string)$input[1],
'params' => $input[2],
'output' => $output
];
$i++; $i++;
echo '.'; echo '.';
} }

View File

@ -15,6 +15,7 @@ class MathMathML extends MathRenderer {
protected $defaultAllowedRootElements = [ 'math' ]; protected $defaultAllowedRootElements = [ 'math' ];
protected $restbaseInputTypes = [ 'tex', 'inline-tex', 'chem' ]; protected $restbaseInputTypes = [ 'tex', 'inline-tex', 'chem' ];
protected $restbaseRenderingModes = [ 'mathml', 'png' ];
protected $allowedRootElements = []; protected $allowedRootElements = [];
protected $hosts; protected $hosts;
@ -26,6 +27,9 @@ class MathMathML extends MathRenderer {
*/ */
private $svgPath = false; private $svgPath = false;
/** @var string|bool */
private $pngPath = false;
private $mathoidStyle; private $mathoidStyle;
public function __construct( $tex = '', $params = [] ) { public function __construct( $tex = '', $params = [] ) {
@ -104,7 +108,7 @@ class MathMathML extends MathRenderer {
$this->setPurge( true ); $this->setPurge( true );
} }
if ( in_array( $this->inputType, $this->restbaseInputTypes ) && if ( in_array( $this->inputType, $this->restbaseInputTypes ) &&
$this->mode == 'mathml' in_array( $this->mode, $this->restbaseRenderingModes )
) { ) {
if ( !$this->rbi ) { if ( !$this->rbi ) {
$this->rbi = $this->rbi =
@ -116,6 +120,7 @@ class MathMathML extends MathRenderer {
$this->mathml = $rbi->getMathML(); $this->mathml = $rbi->getMathML();
$this->mathoidStyle = $rbi->getMathoidStyle(); $this->mathoidStyle = $rbi->getMathoidStyle();
$this->svgPath = $rbi->getFullSvgUrl(); $this->svgPath = $rbi->getFullSvgUrl();
$this->pngPath = $rbi->getFullPngUrl();
} elseif ( $this->lastError === '' ) { } elseif ( $this->lastError === '' ) {
$this->doCheck(); $this->doCheck();
} }
@ -351,6 +356,9 @@ class MathMathML extends MathRenderer {
* @return Title|string * @return Title|string
*/ */
private function getFallbackImageUrl( $noRender = false ) { private function getFallbackImageUrl( $noRender = false ) {
if ( 'png' === $this->getMode() && $this->pngPath ) {
return $this->pngPath;
}
if ( $this->svgPath ) { if ( $this->svgPath ) {
return $this->svgPath; return $this->svgPath;
} }
@ -399,7 +407,7 @@ class MathMathML extends MathRenderer {
* is false the class name will be calculated by getClassName * is false the class name will be calculated by getClassName
* @return string XML the image html tag * @return string XML the image html tag
*/ */
private function getFallbackImage( $noRender = false, $classOverride = false ) { protected function getFallbackImage( $noRender = false, $classOverride = false ) {
$attribs = [ $attribs = [
'src' => $this->getFallbackImageUrl( $noRender ) 'src' => $this->getFallbackImageUrl( $noRender )
]; ];

19
src/MathPng.php Normal file
View File

@ -0,0 +1,19 @@
<?php
/**
* MediaWiki math extension
*
* @copyright 2002-2018 various MediaWiki contributors
* @license GPL-2.0-or-later
*/
class MathPng extends MathMathML {
public function __construct( $tex = '', array $params = [] ) {
parent::__construct( $tex, $params );
$this->setMode( 'png' );
}
public function getHtmlOutput() {
return $this->getFallbackImage();
}
}

View File

@ -167,7 +167,7 @@ abstract class MathRenderer {
$renderer = new MathSource( $tex, $params ); $renderer = new MathSource( $tex, $params );
break; break;
case 'png': case 'png':
$renderer = new MathTexvc( $tex, $params ); $renderer = new MathPng( $tex, $params );
break; break;
case 'latexml': case 'latexml':
$renderer = new MathLaTeXML( $tex, $params ); $renderer = new MathLaTeXML( $tex, $params );

View File

@ -293,6 +293,16 @@ class MathRestbaseInterface {
return $this->getUrl( "media/math/render/svg/{$this->hash}", false ); return $this->getUrl( "media/math/render/svg/{$this->hash}", false );
} }
/**
* Gets a publicly accessible link to the generated SVG image.
* @return string
* @throws MWException
*/
public function getFullPngUrl() {
$this->calculateHash();
return $this->getUrl( "media/math/render/png/{$this->hash}", false );
}
/** /**
* @return string * @return string
*/ */

View File

@ -125,7 +125,7 @@ class MathMathMLTest extends MediaWikiTestCase {
$t = new Title( "test" ); $t = new Title( "test" );
$res = $p->parse( '[[test|<math forcemathmode="png">a+b</math>]]', $t, $po )->getText(); $res = $p->parse( '[[test|<math forcemathmode="png">a+b</math>]]', $t, $po )->getText();
$this->assertContains( '</a>', $res ); $this->assertContains( '</a>', $res );
$this->assertContains( '.png', $res ); $this->assertContains( 'png', $res );
} }
/** /**

File diff suppressed because it is too large Load Diff

View File

@ -24,74 +24,36 @@
* @license GPL-2.0-or-later * @license GPL-2.0-or-later
*/ */
class MathCoverageTest extends MediaWikiTestCase { class MathCoverageTest extends MediaWikiTestCase {
protected static $hasTexvc;
protected static $texvcPath;
public static function setUpBeforeClass() {
global $wgTexvc;
if ( is_executable( $wgTexvc ) ) {
wfDebugLog( __CLASS__, " using build in texvc from "
. "\$wgMathTexvcCheckExecutable = $wgTexvc" );
# Using build-in
self::$hasTexvc = true;
self::$texvcPath = $wgTexvc;
} else {
# Attempt to compile
wfDebugLog( __CLASS__, " compiling texvc..." );
$cmd = 'cd ' . dirname( __DIR__ ) . '/math; make --always-make 2>&1';
wfShellExec( $cmd, $retval );
if ( $retval === 0 ) {
self::$hasTexvc = true;
self::$texvcPath = dirname( __DIR__ ) . '/math/texvc';
wfDebugLog( __CLASS__, ' compiled texvc at ' . self::$texvcPath );
} else {
wfDebugLog( __CLASS__, ' ocaml not available or compilation of texvc failed' );
}
}
}
/**
* 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::$hasTexvc ) {
$this->markTestSkipped( "No texvc installed on server" );
} else {
$this->setMwGlobals( 'wgTexvc',
self::$texvcPath );
}
}
/** /**
* Loops over all test cases provided by the provider function. * Loops over all test cases provided by the provider function.
* Compares each the rendering result of each input with the expected output. * Compares each the rendering result of each input with the expected output.
* @dataProvider provideCoverage * @dataProvider provideCoverage
*/ */
public function testCoverage( $input, $output ) { public function testCoverage( $input, $options, $output ) {
// TODO: Make rendering mode configurable // TODO: Make rendering mode configurable
// TODO: Provide test-ids // TODO: Provide test-ids
// TODO: Link to the wikipage that contains the reference rendering // TODO: Link to the wikipage that contains the reference rendering
$this->assertEquals( $this->assertEquals(
$this->normalize( $output ), $this->normalize( $output ),
$this->normalize( MathRenderer::renderMath( $input, [], 'png' ) ), $this->normalize( MathRenderer::renderMath( $input, $options, 'png' ) ),
"Failed to render $input" "Failed to render ${input}"
); );
} }
/** /**
* Gets the test-data from the file ParserTest.json * Gets the test-data from the file ParserTest.json
* @return string[] [ $input, $output ] where $input is the test input string * @return array where $input is the test input string
* and $output is the rendered html5-output string * and $output is the rendered html5-output string
*/ */
public function provideCoverage() { public function provideCoverage() {
return json_decode( file_get_contents( __DIR__ . '/ParserTest.json' ) ); $testcases = json_decode( file_get_contents( __DIR__ . '/../ParserTest.json' ), true );
// uncomment for fast testing
// $testcases = array_slice($testcases,0,3);
return $testcases;
} }
private function normalize( $input ) { private function normalize( $input ) {
return preg_replace( '#src="(.*?)/(([a-f]|\d)*).png"#', 'src="\2.png"', $input ); return preg_replace( '#src="(.*?)/(([a-f]|\d)*)"#', 'src="\2"', $input );
} }
} }