Merge "Use cell based table rendering"

This commit is contained in:
jenkins-bot 2024-10-29 05:22:13 +00:00 committed by Gerrit Code Review
commit cc5c21dd54
6 changed files with 162 additions and 59 deletions

View file

@ -62,3 +62,20 @@ div.mwe-math-element {
overflow-x: auto; overflow-x: auto;
max-width: 100%; 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;
}

View file

@ -515,34 +515,18 @@ class BaseParsing {
$vspacing = null, $style = null, $cases = null, $numbered = null ) { $vspacing = null, $style = null, $cases = null, $numbered = null ) {
$resInner = ''; $resInner = '';
$mtr = new MMLmtr(); $mtr = new MMLmtr();
$mtd = new MMLmtd(); $tableArgs = [ "columnspacing" => "1em", "rowspacing" => "4pt" ];
$tableArgs = [ "columnspacing" => "1em", "rowspacing" => "4pt", 'rowlines' => '' ]; $boarder = $node->getBoarder();
$columnInfo = trim( $node->getColumnSpecs()->render(), "{} \n\r\t\v\x00" );
if ( $align ) { if ( $align ) {
$tableArgs['columnalign'] = $align; $tableArgs['columnalign'] = $align;
} elseif ( $columnInfo ) { } elseif ( $node->hasColumnInfo() ) {
$align = ''; $tableArgs['columnalign'] = $node->getAlignInfo();
foreach ( str_split( $columnInfo ) as $chr ) {
switch ( $chr ) {
case 'r':
$align .= 'right ';
break;
case 'l':
$align .= 'left ';
break;
case 'c':
$align .= 'center ';
break;
}
}
$tableArgs['columnalign'] = $align;
} }
$mencloseArgs = [ 'notation' => '' ]; $rowNo = 0;
$lines = $node->getLines();
$lineNumber = 0;
foreach ( $node as $row ) { foreach ( $node as $row ) {
$resInner .= $mtr->getStart(); $resInner .= $mtr->getStart();
$solid = false; $colNo = 0;
foreach ( $row as $cell ) { foreach ( $row as $cell ) {
$usedArg = clone $cell; $usedArg = clone $cell;
if ( $usedArg instanceof TexArray && if ( $usedArg instanceof TexArray &&
@ -551,42 +535,34 @@ class BaseParsing {
$usedArg[0]->getArg() === '\\hline ' $usedArg[0]->getArg() === '\\hline '
) { ) {
$usedArg->pop(); $usedArg->pop();
if ( $lineNumber === 0 ) { if ( $rowNo === $node->getLength() - 1 &&
$mencloseArgs['notation'] .= 'top ';
} elseif ( $lineNumber === $node->getLength() - 1 &&
$usedArg->getLength() === 0 $usedArg->getLength() === 0
) { ) {
$mencloseArgs['notation'] .= 'bottom ';
// remove the started row // remove the started row
$resInner = substr( $resInner, 0, -1 * strlen( $mtr->getStart() ) ); $resInner = substr( $resInner, 0, -1 * strlen( $mtr->getStart() ) );
continue 2; 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' $resInner .= $mtd->encapsulateRaw( $usedArg->renderMML( $passedArgs, [ 'inMatrix'
=> true ] => true ]
) ); ) );
} $colNo++;
if ( $lineNumber > 0 ) {
$tableArgs['rowlines'] .= $solid ? 'solid ' : 'none ';
} }
$resInner .= $mtr->getEnd(); $resInner .= $mtr->getEnd();
$lineNumber++; $rowNo++;
}
if ( !str_contains( $tableArgs['rowlines'], 'solid' ) ) {
unset( $tableArgs['rowlines'] );
} }
$mrow = new MMLmrow(); $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 ); $mtable = new MMLmtable( "", $tableArgs );
if ( $cases || ( $open != null && $close != null ) ) { if ( $cases || ( $open != null && $close != null ) ) {
$bm = new BaseMethods(); $bm = new BaseMethods();
@ -607,15 +583,9 @@ class BaseParsing {
$mmlMoClose = $mmlMoClose->encapsulateRaw( $close ); $mmlMoClose = $mmlMoClose->encapsulateRaw( $close );
} }
$resInner = $mmlMoOpen . $mtable->encapsulateRaw( $resInner ) . $mmlMoClose; $resInner = $mmlMoOpen . $mtable->encapsulateRaw( $resInner ) . $mmlMoClose;
} else { return $mrow->encapsulateRaw( $resInner );
$resInner = $mtable->encapsulateRaw( $resInner );
} }
if ( $mencloseArgs['notation'] ) { return $mtable->encapsulateRaw( $resInner );
$menclose = new MMLmenclose( "", $mencloseArgs );
return $mrow->encapsulateRaw( $menclose->encapsulateRaw( $resInner ) );
}
return $mrow->encapsulateRaw( $resInner );
} }
public static function namedOp( $node, $passedArgs, $operatorContent, $name, $id = null ) { public static function namedOp( $node, $passedArgs, $operatorContent, $name, $id = null ) {

View file

@ -13,4 +13,8 @@ class TexClass {
public const INNER = "INNER"; public const INNER = "INNER";
public const VCENTER = "VCENTER"; public const VCENTER = "VCENTER";
public const NONE = "-1"; 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";
} }

View file

@ -11,9 +11,15 @@ class Matrix extends TexArray {
/** @var string */ /** @var string */
private $top; private $top;
private array $lines = [];
private ?TexArray $columnSpecs = null; private ?TexArray $columnSpecs = null;
private ?string $renderedColumSpecs = null;
private ?array $boarder = null;
private ?string $alignInfo = null;
/** /**
* @param string $top * @param string $top
* @param TexArray $mainarg * @param TexArray $mainarg
@ -24,6 +30,7 @@ class Matrix extends TexArray {
if ( !$row instanceof TexArray ) { if ( !$row instanceof TexArray ) {
throw new InvalidArgumentException( 'Nested arguments have to be type of TexArray' ); throw new InvalidArgumentException( 'Nested arguments have to be type of TexArray' );
} }
$this->lines[] = $row->containsFunc( '\hline' );
} }
if ( $mainarg instanceof Matrix ) { if ( $mainarg instanceof Matrix ) {
$this->args = $mainarg->args; $this->args = $mainarg->args;
@ -34,6 +41,10 @@ class Matrix extends TexArray {
$this->top = $top; $this->top = $top;
} }
public function getLines(): array {
return $this->lines;
}
/** /**
* @return string * @return string
*/ */
@ -46,15 +57,32 @@ class Matrix extends TexArray {
return $this; return $this;
} }
public function getColumnSpecs(): TexArray { public function getRenderedColumnSpecs(): string {
return $this->columnSpecs ?? new TexArray(); if ( $this->renderedColumSpecs == null ) {
$this->renderColumnSpecs();
}
return $this->renderedColumSpecs;
} }
public function setColumnSpecs( TexArray $specs ): Matrix { public function setColumnSpecs( TexArray $specs ): Matrix {
$this->columnSpecs = $specs; $this->columnSpecs = $specs;
$this->renderedColumSpecs = null;
$this->alignInfo = null;
$this->boarder = null;
return $this; return $this;
} }
public function hasColumnInfo(): bool {
return $this->getRenderedColumnSpecs() !== '';
}
public function getAlignInfo(): string {
if ( $this->alignInfo == null ) {
$this->renderColumnSpecs();
}
return $this->alignInfo;
}
/** /**
* @return TexArray * @return TexArray
*/ */
@ -139,4 +167,42 @@ class Matrix extends TexArray {
return parent::getIterator(); 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;
}
} }

View file

@ -149,7 +149,7 @@ class BaseParsingTest extends TestCase {
new TexArray( new TexArray( new Literal( '\\hline ' ), new Literal( 'a' new TexArray( new TexArray( new Literal( '\\hline ' ), new Literal( 'a'
) ) ) ) ); ) ) ) ) );
$result = BaseParsing::matrix( $matrix, [], null, 'matrix', '002A' ); $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 ); $this->assertStringContainsString( '<mi>a</mi>', $result );
} }
@ -161,8 +161,8 @@ class BaseParsingTest extends TestCase {
\\hline \\hline
\\end{array}' )[0]; \\end{array}' )[0];
$result = BaseParsing::matrix( $matrix, [], null, 'matrix', '002A' ); $result = BaseParsing::matrix( $matrix, [], null, 'matrix', '002A' );
$this->assertStringContainsString( 'solid none', $result ); $this->assertStringContainsString( 'class="mwe-math-matrix-top"', $result );
$this->assertStringContainsString( 'top bottom', $result ); $this->assertStringContainsString( 'class="mwe-math-matrix-top mwe-math-matrix-bottom"', $result );
} }
public function testHandleOperatorName() { public function testHandleOperatorName() {

View file

@ -99,6 +99,52 @@ class MatrixTest extends MediaWikiUnitTestCase {
public function testColSpec() { public function testColSpec() {
$this->sampleMatrix->setColumnSpecs( TexArray::newCurly( new Literal( '2' ) ) ); $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 );
} }
} }