Fix mathcal in chrome

* map to unicode chars

Bug: T352536
Change-Id: Iab04043df5cc04484d348b0c896a50c94ef79c16
This commit is contained in:
Stegmujo 2023-12-01 14:29:36 +00:00
parent 001e11bd7a
commit 3d0dfe1e43
No known key found for this signature in database
GPG key ID: BB616B7CC84186BE
4 changed files with 68 additions and 25 deletions

View file

@ -202,7 +202,7 @@ class BaseParsing {
$menclose = new MMLmenclose( "", [ "notation" => $notation ] ); $menclose = new MMLmenclose( "", [ "notation" => $notation ] );
$inner = $menclose->encapsulateRaw( $inner = $menclose->encapsulateRaw(
$node->getArg2()->renderMML() ) . $mpAdded->encapsulateRaw( $node->getArg1()->renderMML() ); $node->getArg2()->renderMML() ) . $mpAdded->encapsulateRaw( $node->getArg1()->renderMML() );
return $mrow->encapsulateRaw( $msup->encapsulateRaw( $inner ) ); return $mrow->encapsulateRaw( $msup->encapsulateRaw( $inner ) );
} }
@ -236,7 +236,7 @@ class BaseParsing {
$inner = $mrow->encapsulateRaw( $whatIsThis . $inner = $mrow->encapsulateRaw( $whatIsThis .
$mstyle->encapsulateRaw( $mrow->encapsulateRaw( $node->getArg1()->renderMML() ) ) ) . $mstyle->encapsulateRaw( $mrow->encapsulateRaw( $node->getArg1()->renderMML() ) ) ) .
$mrow->encapsulateRaw( $whatIsThis . $mstyle->encapsulateRaw( $mrow->encapsulateRaw( $whatIsThis . $mstyle->encapsulateRaw(
$mrow->encapsulateRaw( $node->getArg2()->renderMML() ) ) ); $mrow->encapsulateRaw( $node->getArg2()->renderMML() ) ) );
return $mrow->encapsulateRaw( $mfrac->encapsulateRaw( $inner ) ); return $mrow->encapsulateRaw( $mfrac->encapsulateRaw( $inner ) );
} }
@ -253,7 +253,7 @@ class BaseParsing {
} }
public static function genFrac( $node, $passedArgs, $operatorContent, $name, public static function genFrac( $node, $passedArgs, $operatorContent, $name,
$left = null, $right = null, $thick = null, $style = null ) { $left = null, $right = null, $thick = null, $style = null ) {
// Actually this is in AMSMethods, consider refactoring left, right, thick, style // Actually this is in AMSMethods, consider refactoring left, right, thick, style
$bm = new BaseMethods(); $bm = new BaseMethods();
$ret = $bm->checkAndParseDelimiter( $name, $node, $passedArgs, $operatorContent, true ); $ret = $bm->checkAndParseDelimiter( $name, $node, $passedArgs, $operatorContent, true );
@ -527,14 +527,14 @@ class BaseParsing {
$mspace = new MMLmspace( "", [ "width" => "0px","height" => ".25em", $mspace = new MMLmspace( "", [ "width" => "0px","height" => ".25em",
"depth" => "0px","mathbackground" => "black" ] ); "depth" => "0px","mathbackground" => "black" ] );
return $mtext->encapsulateRaw( " " ) . return $mtext->encapsulateRaw( " " ) .
$mrowRel->encapsulateRaw( $mover->encapsulateRaw( $mrowRel->encapsulateRaw( $mover->encapsulateRaw(
$mrowOp->encapsulateRaw( $mrowOp->encapsulateRaw(
$mrowOrd->encapsulateRaw( $mpadded->encapsulateRaw( $mrowOrd->encapsulateRaw( $mpadded->encapsulateRaw(
$mo->encapsulateRaw( "⟵" ) ) ) . $mo->encapsulateRaw( "⟵" ) ) ) .
$mspace->getEmpty() ) . $mspace->getEmpty() ) .
$mrowOrd->encapsulateRaw( $mrowOrd->encapsulateRaw(
$mo->encapsulateRaw( "⟶" ) $mo->encapsulateRaw( "⟶" )
) ) ); ) ) );
} }
// Removed all token based parsing, since macro resolution for the supported macros can be hardcoded in php // Removed all token based parsing, since macro resolution for the supported macros can be hardcoded in php
@ -672,7 +672,7 @@ class BaseParsing {
if ( $node instanceof Fun2 ) { if ( $node instanceof Fun2 ) {
return $start . $mfrac->encapsulateRaw( $mrow->encapsulateRaw( return $start . $mfrac->encapsulateRaw( $mrow->encapsulateRaw(
$node->getArg1()->renderMML() ) . $mrow->encapsulateRaw( $node->getArg2()->renderMML() ) ) $node->getArg1()->renderMML() ) . $mrow->encapsulateRaw( $node->getArg2()->renderMML() ) )
. $tail; . $tail;
} }
$inner = ""; $inner = "";
foreach ( $node->getArgs() as $arg ) { foreach ( $node->getArgs() as $arg ) {
@ -842,10 +842,16 @@ class BaseParsing {
$mrow = new MMLmrow( TexClass::ORD, [] ); $mrow = new MMLmrow( TexClass::ORD, [] );
$args = MMLParsingUtil::getFontArgs( $name, $mathvariant, $passedArgs ); $args = MMLParsingUtil::getFontArgs( $name, $mathvariant, $passedArgs );
$state = []; $state = [];
// Unicode fixes for the operators
if ( $mathvariant == Variants::DOUBLESTRUCK ) { if ( $mathvariant == Variants::DOUBLESTRUCK ) {
// Unicode fix for the operators
$state = [ "double-struck-literals" => true ]; $state = [ "double-struck-literals" => true ];
} elseif ( $mathvariant == Variants::CALLIGRAPHIC ) {
$state = [ "calligraphic" => true ];
} elseif ( $mathvariant == Variants::BOLDCALLIGRAPHIC ) {
$state = [ "bold-calligraphic" => true ];
} }
if ( $node instanceof Fun1nb ) { if ( $node instanceof Fun1nb ) {
// Only one mrow from Fun1nb !? // Only one mrow from Fun1nb !?
return $mrow->encapsulateRaw( $node->getArg()->renderMML( $args, $state ) ); return $mrow->encapsulateRaw( $node->getArg()->renderMML( $args, $state ) );
@ -979,7 +985,7 @@ class BaseParsing {
} }
$mrow = new MMLmrow( TexClass::ORD, [] ); $mrow = new MMLmrow( TexClass::ORD, [] );
$opParsed = ( $operatorContent != null && $operatorContent["limits"] ) $opParsed = ( $operatorContent != null && $operatorContent["limits"] )
? $operatorContent["limits"]->renderMML( $argsOp ) : ""; ? $operatorContent["limits"]->renderMML( $argsOp ) : "";
if ( $node instanceof DQ ) { if ( $node instanceof DQ ) {
$munder = new MMLmunder(); $munder = new MMLmunder();
@ -987,7 +993,7 @@ class BaseParsing {
} elseif ( $node instanceof FQ ) { } elseif ( $node instanceof FQ ) {
$munderOver = new MMLmunderover(); $munderOver = new MMLmunderover();
return $munderOver->encapsulateRaw( $opParsed . $mrow->encapsulateRaw( $node->getDown()->renderMML() ) return $munderOver->encapsulateRaw( $opParsed . $mrow->encapsulateRaw( $node->getDown()->renderMML() )
. $mrow->encapsulateRaw( $node->getUp()->renderMML() ) ); . $mrow->encapsulateRaw( $node->getUp()->renderMML() ) );
} }
} }
@ -1036,7 +1042,8 @@ class BaseParsing {
$end2 = $mrowEnd->encapsulateRaw( $operatorContent["sideset"]->getUp()->renderMML() ); $end2 = $mrowEnd->encapsulateRaw( $operatorContent["sideset"]->getUp()->renderMML() );
return $mmlMrow->encapsulateRaw( $mmlMunderOver->encapsulateRaw( $mstyle->encapsulateRaw( return $mmlMrow->encapsulateRaw( $mmlMunderOver->encapsulateRaw( $mstyle->encapsulateRaw(
$mmlMultiscripts->encapsulateRaw( $opParsed . $in2 . "<mprescripts/>" . $in1 ) ) . $end1 . $end2 ) ); $mmlMultiscripts->encapsulateRaw( $opParsed . $in2 . "<mprescripts/>" . $in1 ) )
. $end1 . $end2 ) );
} }
$merror = new MMLmerror(); $merror = new MMLmerror();

View file

@ -126,9 +126,33 @@ class MMLParsingUtil {
'y' => '&#x1D56A;', 'z' => '&#x1D56B;' 'y' => '&#x1D56A;', 'z' => '&#x1D56B;'
]; ];
// Replace each character in the input string with its double-struck Unicode equivalent return self::matchAlphanumeric( $inputString, $map );
return preg_replace_callback( '/[A-Za-z0-9]/', static function ( $matches ) use ( $map ) { }
return $map[$matches[0]] ?? $matches[0];
public static function mapToCaligraphicUnicode( $inputString ) {
$map = [
'0' => '&#x1D7CE;', '1' => '&#x1D7CF;', '2' => '&#x1D7D0;', '3' => '&#x1D7D1;', '4' => '&#x1D7D2;',
'5' => '&#x1D7D3;', '6' => '&#x1D7D4;', '7' => '&#x1D7D5;', '8' => '&#x1D7D6;', '9' => '&#x1D7D7;',
'A' => '&#x1D49C;', 'B' => '&#x212C;', 'C' => '&#x1D49E;', 'D' => '&#x1D49F;', 'E' => '&#x2130;',
'F' => '&#x2131;', 'G' => '&#x1D4A2;', 'H' => '&#x210B;', 'I' => '&#x2110;', 'J' => '&#x1D4A5;',
'K' => '&#x1D4A6;', 'L' => '&#x2112;', 'M' => '&#x2133;', 'N' => '&#x1D4A9;', 'O' => '&#x1D4AA;',
'P' => '&#x1D4AB;', 'Q' => '&#x1D4AC;', 'R' => '&#x211B;', 'S' => '&#x1D4AE;', 'T' => '&#x1D4AF;',
'U' => '&#x1D4B0;', 'V' => '&#x1D4B1;', 'W' => '&#x1D4B2;', 'X' => '&#x1D4B3;', 'Y' => '&#x1D4B4;',
'Z' => '&#x1D4B5;', 'a' => '&#x1D4B6;', 'b' => '&#x1D4B7;', 'c' => '&#x1D4B8;', 'd' => '&#x1D4B9;',
'e' => '&#x212F;', 'f' => '&#x1D4BB;', 'g' => '&#x210A;', 'h' => '&#x1D4BD;', 'i' => '&#x1D4BE;',
'j' => '&#x1D4BF;', 'k' => '&#x1D4C0;', 'l' => '&#x1D4C1;', 'm' => '&#x1D4C2;', 'n' => '&#x1D4C3;',
'o' => '&#x2134;', 'p' => '&#x1D4C5;', 'q' => '&#x1D4C6;', 'r' => '&#x1D4C7;', 's' => '&#x1D4C8;',
't' => '&#x1D4C9;', 'u' => '&#x1D4CA;', 'v' => '&#x1D4CB;', 'w' => '&#x1D4CC;', 'x' => '&#x1D4CD;',
'y' => '&#x1D4CE;', 'z' => '&#x1D4CF;'
];
return self::matchAlphanumeric( $inputString, $map );
}
public static function matchAlphanumeric( $inputString, $map ) {
// Replace each character in the input string with its caligraphic Unicode equivalent
return preg_replace_callback( '/[A-Za-z0-9]/u', static function ( $matches ) use ( $map ) {
return $map[$matches[0]] ?? $matches[0];
}, $inputString ); }, $inputString );
} }
} }

View file

@ -30,12 +30,16 @@ class Literal extends TexNode {
array_push( $this->extendedLiterals, '\\infty', '\\emptyset' ); array_push( $this->extendedLiterals, '\\infty', '\\emptyset' );
} }
public function changeInputDoubleStruckChars( $input, $state ) { public function changeUnicodeFontInput( $input, $state ) {
/** If it's definitely a literal, and it is double struck, map it to double-struck unicode /**
* for correct rendering in Chrome, see https://phabricator.wikimedia.org/T352196 * In some font modifications, it is required to explicitly use unicode
* characters instead of (only) attributes in MathML to indicate the font.
* This is mostly because of Chrome behaviour. I.e. see: https://phabricator.wikimedia.org/T352196
*/ */
if ( isset( $state["double-struck-literals"] ) ) { if ( isset( $state["double-struck-literals"] ) ) {
return MMLParsingUtil::mapToDoubleStruckUnicode( $input ); return MMLParsingUtil::mapToDoubleStruckUnicode( $input );
} elseif ( isset( $state["calligraphic"] ) ) {
return MMLParsingUtil::mapToCaligraphicUnicode( $input );
} }
return $input; return $input;
} }
@ -48,7 +52,7 @@ class Literal extends TexNode {
} }
if ( is_numeric( $this->arg ) ) { if ( is_numeric( $this->arg ) ) {
$mn = new MMLmn( "", $arguments ); $mn = new MMLmn( "", $arguments );
return $mn->encapsulateRaw( $this->changeInputDoubleStruckChars( $this->arg, $state ) ); return $mn->encapsulateRaw( $this->changeUnicodeFontInput( $this->arg, $state ) );
} }
// is important to split and find chars within curly and differentiate, see tc 459 // is important to split and find chars within curly and differentiate, see tc 459
$foundOperatorContent = MMLutil::initalParseLiteralExpression( $this->arg ); $foundOperatorContent = MMLutil::initalParseLiteralExpression( $this->arg );
@ -95,8 +99,8 @@ class Literal extends TexNode {
// Sieve for Makros // Sieve for Makros
$ret = BaseMethods::checkAndParse( $inputP, $arguments, $ret = BaseMethods::checkAndParse( $inputP, $arguments,
array_merge( $operatorContent ?? [], $state ?? [] ), array_merge( $operatorContent ?? [], $state ?? [] ),
$this, false ); $this, false );
if ( $ret ) { if ( $ret ) {
return $ret; return $ret;
@ -114,7 +118,7 @@ class Literal extends TexNode {
// If falling through all sieves just create an MI element // If falling through all sieves just create an MI element
$mi = new MMLmi( "", $arguments ); $mi = new MMLmi( "", $arguments );
return $mi->encapsulateRaw( $this->changeInputDoubleStruckChars( $input, $state ) ); // $this->arg return $mi->encapsulateRaw( $this->changeUnicodeFontInput( $input, $state ) ); // $this->arg
} }
/** /**
@ -162,7 +166,7 @@ class Literal extends TexNode {
if ( preg_match( $regexp, $s ) == 1 ) { if ( preg_match( $regexp, $s ) == 1 ) {
return [ $s ]; return [ $s ];
} elseif ( in_array( $s, $lit, true ) ) { } elseif ( in_array( $s, $lit, true ) ) {
return [ $s ]; return [ $s ];
} else { } else {
return []; return [];
} }

View file

@ -15,6 +15,14 @@ use MediaWikiUnitTestCase;
* @covers \MediaWiki\Extension\Math\WikiTexVC\TexVC * @covers \MediaWiki\Extension\Math\WikiTexVC\TexVC
*/ */
class MMLRenderTest extends MediaWikiUnitTestCase { class MMLRenderTest extends MediaWikiUnitTestCase {
public function testMathCalUnicode() {
$input = "\\mathcal{O}, \\mathcal{K}, \\mathcal{t}, \\mathcal{c}";
$mathMLtexVC = $this->generateMML( $input );
$this->assertStringContainsString( '&#x1D4AA;', $mathMLtexVC );
$this->assertStringContainsString( '&#x1D4A6;', $mathMLtexVC );
$this->assertStringContainsString( '&#x1D4C9;', $mathMLtexVC );
$this->assertStringContainsString( '&#x1D4B8;', $mathMLtexVC );
}
public function testDoubleStruckLiteralUnicode() { public function testDoubleStruckLiteralUnicode() {
$input = "\\mathbb{Q}, \\R, \\Complex, \\mathbb{4}"; $input = "\\mathbb{Q}, \\R, \\Complex, \\mathbb{4}";