mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
synced 2024-11-27 17:01:07 +00:00
Merge "Use cell based table rendering"
This commit is contained in:
commit
cc5c21dd54
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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 ) {
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue