mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
synced 2024-11-23 23:25:02 +00:00
Use cell based table rendering
* Add hline search in matrix construction: ** This avoids a second scan later for the cost of some memory overhead * Parse | in array column specification * Add CSS based table lines as done by TMML, cf. https://github.com/w3c/mathml-core/issues/245 Bug: T377167 Change-Id: I6e29b47b1731638ea9b06de3006ce2834e4f0c68
This commit is contained in:
parent
7acc46ef46
commit
dab5364b31
|
@ -62,3 +62,20 @@ div.mwe-math-element {
|
|||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Polyfill for MathML matrix elements with menclose https://github.com/w3c/mathml-core/issues/245 */
|
||||
mtd.mwe-math-matrix-top {
|
||||
border-top: 0.06em solid;
|
||||
}
|
||||
|
||||
mtd.mwe-math-matrix-bottom {
|
||||
border-bottom: 0.06em solid;
|
||||
}
|
||||
|
||||
mtd.mwe-math-matrix-left {
|
||||
border-left: 0.06em solid;
|
||||
}
|
||||
|
||||
mtd.mwe-math-matrix-right {
|
||||
border-right: 0.06em solid;
|
||||
}
|
||||
|
|
|
@ -515,34 +515,18 @@ class BaseParsing {
|
|||
$vspacing = null, $style = null, $cases = null, $numbered = null ) {
|
||||
$resInner = '';
|
||||
$mtr = new MMLmtr();
|
||||
$mtd = new MMLmtd();
|
||||
$tableArgs = [ "columnspacing" => "1em", "rowspacing" => "4pt", 'rowlines' => '' ];
|
||||
$columnInfo = trim( $node->getColumnSpecs()->render(), "{} \n\r\t\v\x00" );
|
||||
$tableArgs = [ "columnspacing" => "1em", "rowspacing" => "4pt" ];
|
||||
$boarder = $node->getBoarder();
|
||||
if ( $align ) {
|
||||
$tableArgs['columnalign'] = $align;
|
||||
} elseif ( $columnInfo ) {
|
||||
$align = '';
|
||||
foreach ( str_split( $columnInfo ) as $chr ) {
|
||||
switch ( $chr ) {
|
||||
case 'r':
|
||||
$align .= 'right ';
|
||||
break;
|
||||
case 'l':
|
||||
$align .= 'left ';
|
||||
break;
|
||||
case 'c':
|
||||
$align .= 'center ';
|
||||
break;
|
||||
} elseif ( $node->hasColumnInfo() ) {
|
||||
$tableArgs['columnalign'] = $node->getAlignInfo();
|
||||
}
|
||||
}
|
||||
$tableArgs['columnalign'] = $align;
|
||||
}
|
||||
$mencloseArgs = [ 'notation' => '' ];
|
||||
|
||||
$lineNumber = 0;
|
||||
$rowNo = 0;
|
||||
$lines = $node->getLines();
|
||||
foreach ( $node as $row ) {
|
||||
$resInner .= $mtr->getStart();
|
||||
$solid = false;
|
||||
$colNo = 0;
|
||||
foreach ( $row as $cell ) {
|
||||
$usedArg = clone $cell;
|
||||
if ( $usedArg instanceof TexArray &&
|
||||
|
@ -551,42 +535,34 @@ class BaseParsing {
|
|||
$usedArg[0]->getArg() === '\\hline '
|
||||
) {
|
||||
$usedArg->pop();
|
||||
if ( $lineNumber === 0 ) {
|
||||
$mencloseArgs['notation'] .= 'top ';
|
||||
} elseif ( $lineNumber === $node->getLength() - 1 &&
|
||||
if ( $rowNo === $node->getLength() - 1 &&
|
||||
$usedArg->getLength() === 0
|
||||
) {
|
||||
$mencloseArgs['notation'] .= 'bottom ';
|
||||
// remove the started row
|
||||
$resInner = substr( $resInner, 0, -1 * strlen( $mtr->getStart() ) );
|
||||
continue 2;
|
||||
}
|
||||
$solid = true;
|
||||
}
|
||||
$mtdAttributes = [];
|
||||
$texclass = $lines[$rowNo] ? TexClass::TOP : '';
|
||||
$texclass .= $lines[$rowNo + 1] ?? false ? ' ' . TexClass::BOTTOM : '';
|
||||
$texclass .= $boarder[$colNo] ?? false ? ' ' . TexClass::LEFT : '';
|
||||
$texclass .= $boarder[$colNo + 1 ] ?? false ? ' ' . TexClass::RIGHT : '';
|
||||
$texclass = trim( $texclass );
|
||||
if ( $texclass ) {
|
||||
$mtdAttributes['class'] = $texclass;
|
||||
}
|
||||
$mtd = new MMLmtd( '', $mtdAttributes );
|
||||
|
||||
$resInner .= $mtd->encapsulateRaw( $usedArg->renderMML( $passedArgs, [ 'inMatrix'
|
||||
=> true ]
|
||||
) );
|
||||
}
|
||||
if ( $lineNumber > 0 ) {
|
||||
$tableArgs['rowlines'] .= $solid ? 'solid ' : 'none ';
|
||||
$colNo++;
|
||||
}
|
||||
$resInner .= $mtr->getEnd();
|
||||
$lineNumber++;
|
||||
}
|
||||
if ( !str_contains( $tableArgs['rowlines'], 'solid' ) ) {
|
||||
unset( $tableArgs['rowlines'] );
|
||||
$rowNo++;
|
||||
}
|
||||
$mrow = new MMLmrow();
|
||||
if ( $columnInfo ) {
|
||||
// TBD this is just simple check, create a parsing function for hlines when there are more cases
|
||||
if ( str_contains( $columnInfo, "|" ) ) {
|
||||
|
||||
$mencloseArgs['notation'] .= "left right";
|
||||
// it seems this is creted when left and right is solely coming from columninfo
|
||||
$tableArgs = array_merge( $tableArgs, [ "columnlines" => "solid" ] );
|
||||
}
|
||||
|
||||
}
|
||||
$mtable = new MMLmtable( "", $tableArgs );
|
||||
if ( $cases || ( $open != null && $close != null ) ) {
|
||||
$bm = new BaseMethods();
|
||||
|
@ -607,16 +583,10 @@ class BaseParsing {
|
|||
$mmlMoClose = $mmlMoClose->encapsulateRaw( $close );
|
||||
}
|
||||
$resInner = $mmlMoOpen . $mtable->encapsulateRaw( $resInner ) . $mmlMoClose;
|
||||
} else {
|
||||
$resInner = $mtable->encapsulateRaw( $resInner );
|
||||
}
|
||||
if ( $mencloseArgs['notation'] ) {
|
||||
$menclose = new MMLmenclose( "", $mencloseArgs );
|
||||
return $mrow->encapsulateRaw( $menclose->encapsulateRaw( $resInner ) );
|
||||
|
||||
}
|
||||
return $mrow->encapsulateRaw( $resInner );
|
||||
}
|
||||
return $mtable->encapsulateRaw( $resInner );
|
||||
}
|
||||
|
||||
public static function namedOp( $node, $passedArgs, $operatorContent, $name, $id = null ) {
|
||||
/* Determine whether the named function should have an added apply function. The operatorContent is defined
|
||||
|
|
|
@ -13,4 +13,8 @@ class TexClass {
|
|||
public const INNER = "INNER";
|
||||
public const VCENTER = "VCENTER";
|
||||
public const NONE = "-1";
|
||||
public const TOP = "mwe-math-matrix-top";
|
||||
public const BOTTOM = "mwe-math-matrix-bottom";
|
||||
public const LEFT = "mwe-math-matrix-left";
|
||||
public const RIGHT = "mwe-math-matrix-right";
|
||||
}
|
||||
|
|
|
@ -11,9 +11,15 @@ class Matrix extends TexArray {
|
|||
|
||||
/** @var string */
|
||||
private $top;
|
||||
private array $lines = [];
|
||||
|
||||
private ?TexArray $columnSpecs = null;
|
||||
|
||||
private ?string $renderedColumSpecs = null;
|
||||
private ?array $boarder = null;
|
||||
|
||||
private ?string $alignInfo = null;
|
||||
|
||||
/**
|
||||
* @param string $top
|
||||
* @param TexArray $mainarg
|
||||
|
@ -24,6 +30,7 @@ class Matrix extends TexArray {
|
|||
if ( !$row instanceof TexArray ) {
|
||||
throw new InvalidArgumentException( 'Nested arguments have to be type of TexArray' );
|
||||
}
|
||||
$this->lines[] = $row->containsFunc( '\hline' );
|
||||
}
|
||||
if ( $mainarg instanceof Matrix ) {
|
||||
$this->args = $mainarg->args;
|
||||
|
@ -34,6 +41,10 @@ class Matrix extends TexArray {
|
|||
$this->top = $top;
|
||||
}
|
||||
|
||||
public function getLines(): array {
|
||||
return $this->lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
@ -46,15 +57,32 @@ class Matrix extends TexArray {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getColumnSpecs(): TexArray {
|
||||
return $this->columnSpecs ?? new TexArray();
|
||||
public function getRenderedColumnSpecs(): string {
|
||||
if ( $this->renderedColumSpecs == null ) {
|
||||
$this->renderColumnSpecs();
|
||||
}
|
||||
return $this->renderedColumSpecs;
|
||||
}
|
||||
|
||||
public function setColumnSpecs( TexArray $specs ): Matrix {
|
||||
$this->columnSpecs = $specs;
|
||||
$this->renderedColumSpecs = null;
|
||||
$this->alignInfo = null;
|
||||
$this->boarder = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasColumnInfo(): bool {
|
||||
return $this->getRenderedColumnSpecs() !== '';
|
||||
}
|
||||
|
||||
public function getAlignInfo(): string {
|
||||
if ( $this->alignInfo == null ) {
|
||||
$this->renderColumnSpecs();
|
||||
}
|
||||
return $this->alignInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TexArray
|
||||
*/
|
||||
|
@ -139,4 +167,42 @@ class Matrix extends TexArray {
|
|||
return parent::getIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function renderColumnSpecs(): void {
|
||||
$colSpecs = $this->columnSpecs ?? new TexArray();
|
||||
$this->renderedColumSpecs = trim( $colSpecs->render(), "{} \n\r\t\v\x00" );
|
||||
$align = '';
|
||||
$colNo = 0;
|
||||
$this->boarder = [];
|
||||
foreach ( str_split( $this->renderedColumSpecs ) as $chr ) {
|
||||
switch ( $chr ) {
|
||||
case '|':
|
||||
$this->boarder[$colNo] = true;
|
||||
break;
|
||||
case 'r':
|
||||
$align .= 'right ';
|
||||
$colNo++;
|
||||
break;
|
||||
case 'l':
|
||||
$align .= 'left ';
|
||||
$colNo++;
|
||||
break;
|
||||
case 'c':
|
||||
$colNo++;
|
||||
$align .= 'center ';
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->alignInfo = $align;
|
||||
}
|
||||
|
||||
public function getBoarder(): array {
|
||||
if ( $this->boarder == null ) {
|
||||
$this->renderColumnSpecs();
|
||||
}
|
||||
return $this->boarder;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ class BaseParsingTest extends TestCase {
|
|||
new TexArray( new TexArray( new Literal( '\\hline ' ), new Literal( 'a'
|
||||
) ) ) ) );
|
||||
$result = BaseParsing::matrix( $matrix, [], null, 'matrix', '002A' );
|
||||
$this->assertStringContainsString( 'solid', $result );
|
||||
$this->assertStringContainsString( 'class="mwe-math-matrix-top"', $result );
|
||||
$this->assertStringContainsString( '<mi>a</mi>', $result );
|
||||
}
|
||||
|
||||
|
@ -161,8 +161,8 @@ class BaseParsingTest extends TestCase {
|
|||
\\hline
|
||||
\\end{array}' )[0];
|
||||
$result = BaseParsing::matrix( $matrix, [], null, 'matrix', '002A' );
|
||||
$this->assertStringContainsString( 'solid none', $result );
|
||||
$this->assertStringContainsString( 'top bottom', $result );
|
||||
$this->assertStringContainsString( 'class="mwe-math-matrix-top"', $result );
|
||||
$this->assertStringContainsString( 'class="mwe-math-matrix-top mwe-math-matrix-bottom"', $result );
|
||||
}
|
||||
|
||||
public function testHandleOperatorName() {
|
||||
|
|
|
@ -99,6 +99,52 @@ class MatrixTest extends MediaWikiUnitTestCase {
|
|||
|
||||
public function testColSpec() {
|
||||
$this->sampleMatrix->setColumnSpecs( TexArray::newCurly( new Literal( '2' ) ) );
|
||||
$this->assertEquals( '{2}', $this->sampleMatrix->getColumnSpecs()->render() );
|
||||
$this->assertSame( '2', $this->sampleMatrix->getRenderedColumnSpecs() );
|
||||
}
|
||||
|
||||
public function testAdvColSpec() {
|
||||
$this->sampleMatrix->setColumnSpecs( TexArray::newCurly( new Literal( '{r|l}' ) ) );
|
||||
$this->assertSame( 'r|l', $this->sampleMatrix->getRenderedColumnSpecs() );
|
||||
$this->assertEquals( 'right left ', $this->sampleMatrix->getAlignInfo() );
|
||||
$this->assertEquals( [ 1 => true ], $this->sampleMatrix->getBoarder() );
|
||||
}
|
||||
|
||||
public function testGetLines() {
|
||||
$real = $this->sampleMatrix->getLines();
|
||||
$this->assertNotEmpty( $real );
|
||||
$this->assertFalse( $real[0] );
|
||||
}
|
||||
|
||||
public function testLinesBottom() {
|
||||
$matrix = new Matrix( 'matrix',
|
||||
new TexArray( new TexArray( new Literal( 'a' ) ),
|
||||
new TexArray( new TexArray( new Literal( '\\hline ' ) ) ) ) );
|
||||
$real = $matrix->getLines();
|
||||
$this->assertNotEmpty( $real );
|
||||
$this->assertFalse( $real[0] );
|
||||
$this->assertTrue( $real[1] );
|
||||
$this->assertCount( 2, $real );
|
||||
}
|
||||
|
||||
public function testLinesTop() {
|
||||
$matrix = new Matrix( 'matrix',
|
||||
new TexArray( new TexArray( new TexArray( new Literal( '\\hline ' ), new Literal( 'a'
|
||||
) ) ) ) );
|
||||
$real = $matrix->getLines();
|
||||
$this->assertNotEmpty( $real );
|
||||
$this->assertTrue( $real[0] );
|
||||
$this->assertCount( 1, $real );
|
||||
}
|
||||
|
||||
public function testLinesLast() {
|
||||
$matrix = new Matrix( 'matrix',
|
||||
new TexArray( new TexArray( new Literal( 'a' ) ),
|
||||
new TexArray( new TexArray( new Literal( '\\hline ' ), new Literal( 'a'
|
||||
) ) ) ) );
|
||||
$real = $matrix->getLines();
|
||||
$this->assertNotEmpty( $real );
|
||||
$this->assertFalse( $real[0] );
|
||||
$this->assertTrue( $real[1] );
|
||||
$this->assertCount( 2, $real );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue