Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 111
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
JsonToMathML
0.00% covered (danger)
0.00%
0 / 108
0.00% covered (danger)
0.00%
0 / 7
930
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 readJsonAndGenerateMML
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
156
 formatInput
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 1
156
 getJSON
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 writeToFile
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 fetchMathML
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @ingroup Maintenance
19 */
20
21use MediaWiki\Extension\Math\MathRenderer;
22use MediaWiki\MediaWikiServices;
23
24require_once __DIR__ . '/../../../maintenance/Maintenance.php';
25
26/**
27 * From a specified json file with (La)TeX formula as input,
28 * create a json file with the Tex and corresponding MathML.
29 * This is mostly used for generating Test-Content for the MathML features of WikiTexVC.
30 *
31 * The script fetches:
32 * - Mathoid MathML (mode: 'mathml')
33 * - LaTeXML MathML (mode: 'latexml')
34 *
35 * @author Johannes Stegmüller
36 */
37class JsonToMathML extends Maintenance {
38    /** @var string */
39    private $inputPath = "";
40
41    /** @var string */
42    private $outputPath = "";
43    /** @var int */
44    private $inputFormat = 0;
45
46    public function __construct() {
47        parent::__construct();
48        $this->addDescription( 'From a JSON file containing (La)TeX math inputs, create a JSON file with LaTeX and ' .
49            'corresponding MathML.' );
50        $this->addArg( 'input-path', "Path (with filename) of the json file read by this script.",
51            true );
52        $this->addArg( 'output-path', "Path (with filename) of the output json file created by this script.",
53            true );
54        $this->addOption( 'inputformat', 'Custom parsing how to format the input-json ( see formatInput function)',
55            false, true, 'i' );
56        $this->addOption( 'chem-fallback', 'If the json read does not define input-type (tex or chem), check ' .
57            'expressions as Tex and then as chem', false, true, 'c' );
58        $this->requireExtension( 'Math' );
59    }
60
61    public function execute() {
62        $this->inputFormat = $this->getOption( "inputformat", 0 );
63        $this->inputPath = $this->getArg( 0 );
64        $this->outputPath = $this->getArg( 1 );
65        $this->readJsonAndGenerateMML();
66    }
67
68    public function readJsonAndGenerateMML() {
69        $inputTex = $this->getJSON( $this->inputPath );
70        if ( $inputTex == null ) {
71            throw new InvalidArgumentException( "Provide a json file as input which has content" );
72        }
73        $inputTexF = $this->formatInput( $inputTex );
74        $allEntries = [];
75
76        foreach ( $inputTexF as $entry ) {
77            try {
78                $mmlMathoid = $this->fetchMathML( $entry['tex'], $entry['type'], 'mathml' );
79                if ( ( $this->getOption( "chem-fallback", 0 ) && !$mmlMathoid ) || $mmlMathoid == "" ) {
80                    $mmlMathoid = $this->fetchMathML( $entry['tex'], "chem", 'mathml' );
81                    if ( $mmlMathoid && $mmlMathoid != "" ) {
82                        $entry['type'] = "chem";
83                    }
84                }
85                $mmlLaTeXML = $this->fetchMathML( $entry['tex'], $entry['type'], 'latexml' );
86                $entryToAdd = [
87                    "tex" => $entry['tex'],
88                    "type" => $entry['type'],
89                    "mmlMathoid" => $mmlMathoid,
90                    "mmlLaTeXML" => $mmlLaTeXML
91                ];
92
93                if ( array_key_exists( "texNew", $entry ) ) {
94                    $entryToAdd["texNew"] = $entry["texNew"];
95                }
96                if ( array_key_exists( "typeC", $entry ) ) {
97                    $entryToAdd["typeC"] = $entry["typeC"];
98                }
99                if ( array_key_exists( "description", $entry ) ) {
100                    $entryToAdd["description"] = $entry["description"];
101                }
102
103                $allEntries[] = $entryToAdd;
104            } catch ( Exception $e ) {
105                $allEntries[] = [
106                    "tex" => $entry['tex'],
107                    "type" => $entry['type'],
108                    "mmlMathoid" => "skipped (Exception)",
109                    "mmlLaTeXML" => "skipped (Exception)",
110                    "skipped" => true,
111                ];
112                $this->output( "Exception occurred during rendering:" . $entry['tex'] . " render:" . $e . "\n" );
113            }
114        }
115
116        $this->writeToFile( $this->outputPath, $allEntries );
117    }
118
119    /**
120     * Creates a uniform array of data from files for more convenient parsing.
121     * @param array $fileData input read from json, format can differ
122     * @return array uniform array
123     */
124    private function formatInput( $fileData ) {
125        $inputF = [];
126        switch ( $this->inputFormat ) {
127            case 0:
128                // Example file ParserTest135.json
129                foreach ( $fileData as $entry ) {
130                    $inputF[] = [
131                        "tex" => $entry['input'],
132                        "type" => "tex",
133                    ];
134                }
135                break;
136            case 1:
137                // Example file TexUtilMMLLookup.json
138                foreach ( $fileData as $tex => $mml ) {
139                    $inputF[] = [
140                        "tex" => $tex,
141                        "type" => "tex",
142                    ];
143                }
144                break;
145            case 2:
146                // Example file ExportedTexUtilKeys.json
147                foreach ( $fileData as $tex => $type ) {
148                    $inputF[] = [
149                        "tex" => $tex,
150                        "type" => $type,
151                    ];
152                }
153                break;
154            case 3:
155                // Example file Mhchemv4tex.json
156                foreach ( $fileData as $group => $cases ) {
157                    foreach ( $cases as $case ) {
158                        $inputF[] = [
159                            "description" => $group,
160                            "tex" => $case["tex"],
161                            "texNew" => $case["texNew"],
162                            "type" => "chem",
163                            "typeC" => $case["type"]
164                        ];
165                    }
166                }
167                break;
168            case 4:
169                // Example file ExamplesNewCommandsMhchem.json
170                foreach ( $fileData as $entry ) {
171                    $inputF[] = [
172                        "description" => $entry["description"],
173                        "tex" => $entry['tex'],
174                        "type" => $entry['type'],
175                    ];
176                }
177                break;
178        }
179        return $inputF;
180    }
181
182    /**
183     * Reads the json file to an object
184     * @param string $filePath filepath to the json-file
185     * @return array
186     */
187    private function getJSON( string $filePath ) {
188        $file = file_get_contents( $filePath );
189        $json = json_decode( $file, true );
190        return $json;
191    }
192
193    public function writeToFile( string $fullPath, array $allEntries ): void {
194        $jsonData = json_encode( $allEntries, JSON_PRETTY_PRINT );
195        file_put_contents( $fullPath, $jsonData );
196    }
197
198    /**
199     * Creates a renderer and fetches the generated MathML
200     * @param string $tex input tex
201     * @param string $type input type ('tex' or 'chem')
202     * @param string $renderingMode mode for rendering (latexml, mathml)
203     * @return string MathML as string
204     */
205    public function fetchMathML( string $tex, string $type, string $renderingMode ): string {
206        $params = [ "type" => $type ];
207        if ( $type == "chem" ) {
208            $params["chem"] = true;
209            $tex = "\\ce{ " . $tex . " }";
210        }
211        /** @var MathRenderer $renderer */
212        $renderer = MediaWikiServices::getInstance()->get( 'Math.RendererFactory' )
213            ->getRenderer( $tex, $params, $renderingMode );
214        $renderer->render();
215        $mml = $renderer->getMathml();
216        return $mml;
217    }
218}
219
220$maintClass = JsonToMathML::class;
221require_once RUN_MAINTENANCE_IF_MAIN;