Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.20% |
65 / 69 |
|
76.92% |
10 / 13 |
CRAP | |
0.00% |
0 / 1 |
MathNativeMML | |
94.20% |
65 / 69 |
|
76.92% |
10 / 13 |
21.09 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
renderReferenceEntry | |
85.71% |
12 / 14 |
|
0.00% |
0 / 1 |
2.01 | |||
addLinksToMathML | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
3 | |||
getMainConfig | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getHookContainer | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
doRender | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
6 | |||
getChecker | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getHtmlOutput | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
readFromCache | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
writeCache | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setHookContainer | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setMainConfig | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setChecker | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * MediaWiki math extension |
4 | * |
5 | * @copyright 2002-2023 various MediaWiki contributors |
6 | * @license GPL-2.0-or-later |
7 | */ |
8 | |
9 | namespace MediaWiki\Extension\Math; |
10 | |
11 | use DOMDocument; |
12 | use DOMXPath; |
13 | use MediaWiki\Config\Config; |
14 | use MediaWiki\Extension\Math\InputCheck\LocalChecker; |
15 | use MediaWiki\Extension\Math\WikiTexVC\MMLnodes\MMLmath; |
16 | use MediaWiki\HookContainer\HookContainer; |
17 | use MediaWiki\MediaWikiServices; |
18 | use StatusValue; |
19 | use Wikimedia\ObjectCache\WANObjectCache; |
20 | |
21 | /** |
22 | * Converts LaTeX to MathML using PHP |
23 | */ |
24 | class MathNativeMML extends MathMathML { |
25 | private LocalChecker $checker; |
26 | private Config $mainConfig; |
27 | private HookContainer $hookContainer; |
28 | |
29 | /** @inheritDoc */ |
30 | public function __construct( $tex = '', $params = [], $cache = null, $mathConfig = null ) { |
31 | parent::__construct( $tex, $params, $cache, $mathConfig ); |
32 | $this->setMode( MathConfig::MODE_NATIVE_MML ); |
33 | } |
34 | |
35 | public static function renderReferenceEntry( |
36 | array &$entry, |
37 | ?MathConfig $mathConfig = null, |
38 | ?HookContainer $hookContainer = null, |
39 | ?Config $config = null ): bool { |
40 | $mathConfig ??= Math::getMathConfig(); |
41 | $hookContainer ??= MediaWikiServices::getInstance()->getHookContainer(); |
42 | $config ??= MediaWikiServices::getInstance()->getMainConfig(); |
43 | $renderer = new MathNativeMML( $entry['input'], $entry['params'], WANObjectCache::newEmpty(), $mathConfig ); |
44 | $renderer->setRawError( true ); |
45 | $renderer->setHookContainer( $hookContainer ); |
46 | $renderer->setMainConfig( $config ); |
47 | $renderer->setChecker( new LocalChecker( WANObjectCache::newEmpty(), $entry['input'], 'tex' ) ); |
48 | $result = $renderer->render(); |
49 | $entry['output'] = $renderer->getMathml(); |
50 | if ( !$result ) { |
51 | $entry['skipped'] = true; |
52 | $entry['error'] = $renderer->getLastError(); |
53 | } |
54 | return $result; |
55 | } |
56 | |
57 | /** |
58 | * Adds hyperlinks to MathML elements |
59 | * @param string $qid Identifier for symbol mapping |
60 | * @param string $mathml Input MathML HTML content |
61 | * @return string Modified MathML with either anchor tags or hrefs |
62 | */ |
63 | private function addLinksToMathML( string $qid, string $mathml ): string { |
64 | $services = MediaWikiServices::getInstance(); |
65 | $connector = $services->getService( 'Math.WikibaseConnector' ); |
66 | $language = $services->getContentLanguageCode()->toString(); |
67 | $qmap = $connector->getUrlFromSymbol( $qid, $language ); |
68 | $dom = new DOMDocument(); |
69 | $dom->loadXML( $mathml ); |
70 | $xpath = new DOMXPath( $dom ); |
71 | $xpath->registerNamespace( 'mathml', 'http://www.w3.org/1998/Math/MathML' ); |
72 | $linkableElements = $xpath->query( '//mathml:mi | //mathml:mo | //mathml:mtext' ); |
73 | foreach ( $linkableElements as $linkableElement ) { |
74 | $textValue = $linkableElement->nodeValue; |
75 | if ( empty( $qmap[$textValue]['url'] ) ) { |
76 | continue; |
77 | } |
78 | $a = $dom->createElement( 'a' ); |
79 | $a->setAttribute( 'href', $qmap[$textValue]['url'] ); |
80 | $a->setAttribute( 'title', $qmap[$textValue]['title'] ); |
81 | $a->nodeValue = $linkableElement->nodeValue; |
82 | $linkableElement->nodeValue = ""; |
83 | $linkableElement->appendChild( $a ); |
84 | } |
85 | return $dom->saveXML(); |
86 | } |
87 | |
88 | public function getMainConfig(): Config { |
89 | $this->mainConfig ??= MediaWikiServices::getInstance()->getMainConfig(); |
90 | return $this->mainConfig; |
91 | } |
92 | |
93 | public function getHookContainer(): HookContainer { |
94 | $this->hookContainer ??= MediaWikiServices::getInstance()->getHookContainer(); |
95 | return $this->hookContainer; |
96 | } |
97 | |
98 | protected function doRender(): StatusValue { |
99 | $checker = $this->getChecker(); |
100 | $checker->setContext( $this ); |
101 | $checker->setHookContainer( $this->getHookContainer() ); |
102 | $presentation = $checker->getPresentationMathMLFragment(); |
103 | $config = $this->getMainConfig(); |
104 | $attributes = [ 'class' => 'mwe-math-element' ]; |
105 | if ( $this->getID() !== '' ) { |
106 | $attributes['id'] = $this->getID(); |
107 | } |
108 | if ( $this->getMathStyle() == 'display' ) { |
109 | $attributes['display'] = 'block'; |
110 | } |
111 | $root = new MMLmath( "", $attributes ); |
112 | $mathElement = $root->encapsulateRaw( $presentation ?? '' ); |
113 | if ( isset( $this->params['qid'] ) && |
114 | preg_match( '/Q\d+/', $this->params['qid'] ) && |
115 | $config->get( "MathEnableFormulaLinks" ) ) { |
116 | $this->setMathml( $this->addLinksToMathML( |
117 | $this->params['qid'], |
118 | $mathElement ) ); |
119 | } else { |
120 | $this->setMathml( $mathElement ); |
121 | } |
122 | return StatusValue::newGood(); |
123 | } |
124 | |
125 | protected function getChecker(): LocalChecker { |
126 | $this->checker ??= Math::getCheckerFactory() |
127 | ->newLocalChecker( $this->tex, $this->getInputType(), $this->isPurge() ); |
128 | return $this->checker; |
129 | } |
130 | |
131 | /** |
132 | * @inheritDoc |
133 | */ |
134 | public function getHtmlOutput( bool $svg = true ): string { |
135 | return $this->getMathml(); |
136 | } |
137 | |
138 | public function readFromCache(): bool { |
139 | return false; |
140 | } |
141 | |
142 | /** @inheritDoc */ |
143 | public function writeCache() { |
144 | return true; |
145 | } |
146 | |
147 | private function setHookContainer( HookContainer $hookContainer ) { |
148 | $this->hookContainer = $hookContainer; |
149 | } |
150 | |
151 | private function setMainConfig( Config $config ) { |
152 | $this->mainConfig = $config; |
153 | } |
154 | |
155 | private function setChecker( LocalChecker $checker ) { |
156 | $this->checker = $checker; |
157 | } |
158 | } |