Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
14.71% |
10 / 68 |
|
33.33% |
3 / 9 |
CRAP | |
0.00% |
0 / 1 |
MathLaTeXML | |
14.93% |
10 / 67 |
|
33.33% |
3 / 9 |
320.02 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
serializeSettings | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
getLaTeXMLSettings | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
setLaTeXMLSettings | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPostData | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
doRender | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
42 | |||
calculateSvg | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
getSvg | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
20 | |||
getMathTableName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Math; |
4 | |
5 | use MediaWiki\Extension\Math\Hooks\HookRunner; |
6 | use MediaWiki\Logger\LoggerFactory; |
7 | use MediaWiki\MediaWikiServices; |
8 | use StatusValue; |
9 | |
10 | /** |
11 | * Contains the driver function for the LaTeXML daemon |
12 | * |
13 | * @copyright 2012 Moritz Schubotz |
14 | * @license GPL-2.0-or-later |
15 | */ |
16 | class MathLaTeXML extends MathMathML { |
17 | |
18 | /** @var string[] */ |
19 | protected $defaultAllowedRootElements = [ 'math', 'div', 'table', 'query' ]; |
20 | |
21 | /** @var string settings for LaTeXML daemon */ |
22 | private $LaTeXMLSettings = ''; |
23 | |
24 | public function __construct( $tex = '', $params = [], $cache = null ) { |
25 | global $wgMathLaTeXMLUrl; |
26 | parent::__construct( $tex, $params, $cache ); |
27 | $this->host = $wgMathLaTeXMLUrl; |
28 | $this->setMode( MathConfig::MODE_LATEXML ); |
29 | } |
30 | |
31 | /** |
32 | * Converts an array with LaTeXML settings to a URL encoded String. |
33 | * If the argument is a string the input will be returned. |
34 | * Thus the function has projector properties and can be applied a second time safely. |
35 | * @param string|array $array |
36 | * @return string |
37 | */ |
38 | public function serializeSettings( $array ) { |
39 | if ( !is_array( $array ) ) { |
40 | return $array; |
41 | } |
42 | |
43 | // removes the [1] [2]... for the unnamed subarrays since LaTeXML |
44 | // assigns multiple values to one key e.g. |
45 | // preload=amsmath.sty&preload=amsthm.sty&preload=amstext.sty |
46 | $cgi_string = wfArrayToCgi( $array ); |
47 | $cgi_string = preg_replace( '|\%5B\d+\%5D|', '', $cgi_string ); |
48 | $cgi_string = preg_replace( '|&\d+=|', '&', $cgi_string ); |
49 | |
50 | return $cgi_string; |
51 | } |
52 | |
53 | /** |
54 | * Gets the settings for the LaTeXML daemon. |
55 | * @return string |
56 | */ |
57 | public function getLaTeXMLSettings() { |
58 | global $wgMathDefaultLaTeXMLSetting; |
59 | return $this->LaTeXMLSettings ?: $wgMathDefaultLaTeXMLSetting; |
60 | } |
61 | |
62 | /** |
63 | * Sets the settings for the LaTeXML daemon. |
64 | * The settings affect only the current instance of the class. |
65 | * For a list of possible settings see: |
66 | * http://dlmf.nist.gov/LaTeXML/manual/commands/latexmlpost.xhtml |
67 | * An empty value indicates to use the default settings. |
68 | * @param string|array $settings |
69 | */ |
70 | public function setLaTeXMLSettings( $settings ) { |
71 | $this->LaTeXMLSettings = $settings; |
72 | } |
73 | |
74 | /** |
75 | * Calculates the HTTP POST Data for the request. Depends on the settings |
76 | * and the input string only. |
77 | * @return string HTTP POST data |
78 | */ |
79 | public function getPostData() { |
80 | $tex = $this->getTex(); |
81 | if ( $this->getMathStyle() == 'inlineDisplaystyle' ) { |
82 | // In 'inlineDisplaystyle' the old |
83 | // texvc behavior is reproduced: |
84 | // The equation is rendered in displaystyle |
85 | // (texvc used $$ $tex $$ to render) |
86 | // but the equation is not centered. |
87 | $tex = '{\displaystyle ' . $tex . '}'; |
88 | } |
89 | $texcmd = rawurlencode( $tex ); |
90 | $settings = $this->serializeSettings( $this->getLaTeXMLSettings() ); |
91 | $postData = $settings . '&tex=' . $texcmd; |
92 | |
93 | // There is an API-inconsistency between different versions of the LaTeXML daemon |
94 | // some versions require the literal prefix other don't allow it. |
95 | if ( !strpos( $this->host, '/convert' ) ) { |
96 | $postData = preg_replace( '/&tex=/', '&tex=literal:', $postData, 1 ); |
97 | } |
98 | |
99 | LoggerFactory::getInstance( 'Math' )->debug( 'Get post data: ' . $postData ); |
100 | return $postData; |
101 | } |
102 | |
103 | /** |
104 | * Does the actual web request to convert TeX to MathML. |
105 | * @return StatusValue |
106 | */ |
107 | protected function doRender(): StatusValue { |
108 | if ( trim( $this->getTex() ) === '' ) { |
109 | LoggerFactory::getInstance( 'Math' )->warning( |
110 | 'Rendering was requested, but no TeX string is specified.' ); |
111 | return StatusValue::newFatal( 'math_empty_tex' ); |
112 | } |
113 | $requestStatus = $this->makeRequest(); |
114 | if ( $requestStatus->isGood() ) { |
115 | $jsonResult = json_decode( $requestStatus->getValue() ); |
116 | if ( $jsonResult && json_last_error() === JSON_ERROR_NONE ) { |
117 | if ( $this->isValidMathML( $jsonResult->result ) ) { |
118 | $this->setMathml( $jsonResult->result ); |
119 | // Avoid PHP 7.1 warning from passing $this by reference |
120 | $renderer = $this; |
121 | ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) ) |
122 | ->onMathRenderingResultRetrieved( |
123 | $renderer, $jsonResult |
124 | ); // Enables debugging of server results |
125 | return StatusValue::newGood(); |
126 | } |
127 | |
128 | // Do not print bad mathml. It's probably too verbose and might |
129 | // mess up the browser output. |
130 | LoggerFactory::getInstance( 'Math' ) |
131 | ->warning( 'LaTeXML invalid MathML', [ |
132 | 'post' => $this->getPostData(), |
133 | 'host' => $this->host, |
134 | 'result' => $requestStatus->getValue() |
135 | ] ); |
136 | return StatusValue::newFatal( 'math_invalidxml', $this->getModeName(), $this->host ); |
137 | } |
138 | LoggerFactory::getInstance( 'Math' ) |
139 | ->warning( 'LaTeXML invalid JSON', [ |
140 | 'post' => $this->getPostData(), |
141 | 'host' => $this->host, |
142 | 'res' => $requestStatus->getValue() |
143 | ] ); |
144 | |
145 | return StatusValue::newFatal( $this->getError( 'math_invalidjson', $this->getModeName(), $this->host ) ); |
146 | } else { |
147 | return $requestStatus; |
148 | } |
149 | } |
150 | |
151 | /** |
152 | * Calculates the SVG image based on the MathML input |
153 | * No cache is used. |
154 | * @return bool |
155 | */ |
156 | public function calculateSvg() { |
157 | $renderer = new MathMathML( $this->getTex() ); |
158 | $renderer->setMathml( $this->getMathml() ); |
159 | $renderer->setMode( MathConfig::MODE_LATEXML ); |
160 | $renderer->setPurge(); |
161 | $res = $renderer->render(); |
162 | if ( $res == true ) { |
163 | $this->setSvg( $renderer->getSvg() ); |
164 | } else { |
165 | $lastError = $renderer->getLastError(); |
166 | LoggerFactory::getInstance( 'Math' )->error( |
167 | 'Failed to convert LaTeXML-MathML to SVG:' . $lastError ); |
168 | } |
169 | return $res; |
170 | } |
171 | |
172 | /** |
173 | * Gets the SVG image |
174 | * |
175 | * @param string $render if set to 'render' (default) and no SVG image exists, the function |
176 | * tries to generate it on the fly. |
177 | * Otherwise, if set to 'cached', and there is no SVG in the database |
178 | * cache, an empty string is returned. |
179 | * |
180 | * @return string XML-Document of the rendered SVG |
181 | */ |
182 | public function getSvg( $render = 'render' ) { |
183 | if ( $render == 'render' && ( $this->isPurge() || $this->svg == '' ) ) { |
184 | $this->calculateSvg(); |
185 | } |
186 | return parent::getSvg( $render ); |
187 | } |
188 | |
189 | /** |
190 | * @return string |
191 | */ |
192 | protected function getMathTableName() { |
193 | return 'mathlatexml'; |
194 | } |
195 | } |
196 | |
197 | class_alias( MathLaTeXML::class, 'MathLaTeXML' ); |