Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialMathShowImage
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 5
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setHeaders
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 execute
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
240
 printSvgError
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\Math;
4
5use InvalidArgumentException;
6use MediaWiki\Extension\Math\Render\RendererFactory;
7use MediaWiki\MainConfigNames;
8use MediaWiki\SpecialPage\UnlistedSpecialPage;
9
10/**
11 * Description of SpecialMathShowSVG
12 *
13 * @author Moritz Schubotz (Physikerwelt)
14 */
15class SpecialMathShowImage extends UnlistedSpecialPage {
16    /** @var bool */
17    private $noRender = false;
18    /** @var MathRenderer|null */
19    private $renderer = null;
20    /** @var string */
21    private $mode = MathConfig::MODE_MATHML;
22
23    public function __construct(
24        private readonly MathConfig $mathConfig,
25        private readonly RendererFactory $rendererFactory,
26    ) {
27        parent::__construct( 'MathShowImage' );
28    }
29
30    /**
31     * Sets headers - this should be called from the execute() method of all derived classes!
32     * @param bool $success
33     */
34    public function setHeaders( $success = true ) {
35        $out = $this->getOutput();
36        $request = $this->getRequest();
37        $out->setArticleBodyOnly( true );
38        $out->setArticleRelated( false );
39        $out->setRobotPolicy( "noindex,nofollow" );
40        $out->disable();
41        $request->response()->header( "Content-type: image/svg+xml; charset=utf-8" );
42        if ( $success && !( $this->noRender ) ) {
43            $request->response()->header(
44                'Cache-Control: public, s-maxage=604800, max-age=3600'
45            ); // 1 week (server) 1 hour (client)
46            $request->response()->header( 'Vary: User-Agent' );
47        }
48    }
49
50    /** @inheritDoc */
51    public function execute( $par ) {
52        $request = $this->getRequest();
53        $hash = $request->getText( 'hash', '' );
54        $tex = $request->getText( 'tex', '' );
55        if ( $this->getConfig()->get( 'MathEnableExperimentalInputFormats' ) ) {
56            $asciimath = $request->getText( 'asciimath', '' );
57        } else {
58            $asciimath = '';
59        }
60        $this->mode = MathConfig::normalizeRenderingMode( $request->getText( 'mode' ) );
61        if ( !$this->mathConfig->isValidRenderingMode( $this->mode ) ) {
62            // Fallback to the default if an invalid mode was specified
63            $this->mode = MathConfig::MODE_MATHML;
64        }
65        if ( $hash === '' && $tex === '' && $asciimath === '' ) {
66            $this->setHeaders( false );
67            echo $this->printSvgError( 'No Inputhash specified' );
68            return;
69        }
70
71        if ( $tex === '' && $asciimath === '' ) {
72            try {
73                $this->renderer = $this->rendererFactory->getFromHash( $hash );
74            } catch ( InvalidArgumentException $exception ) {
75                $this->setHeaders( false );
76                echo $this->printSvgError( $exception->getMessage() );
77                return;
78            }
79            $this->noRender = $request->getBool( 'noRender', false );
80            $isInDatabase = $this->renderer->readFromCache();
81            if ( $isInDatabase || $this->noRender ) {
82                $success = $isInDatabase;
83            } else {
84                $success = $this->renderer->render();
85            }
86        } elseif ( $asciimath === '' ) {
87            $this->renderer = $this->rendererFactory->getRenderer( $tex, [], $this->mode );
88            $success = $this->renderer->render();
89        } else {
90            $this->renderer = $this->rendererFactory->getRenderer(
91                $asciimath, [ 'type' => 'ascii' ], $this->mode
92            );
93            $success = $this->renderer->render();
94        }
95        if ( $success ) {
96            $output = $this->renderer->getSvg();
97        } else {
98            $output = $this->printSvgError( $this->renderer->getLastError() );
99        }
100        if ( $output == "" ) {
101            $output = $this->printSvgError( 'No Output produced' );
102            $success = false;
103        }
104        $this->setHeaders( $success );
105        echo $output;
106        if ( $success ) {
107            $this->renderer->writeCache();
108        }
109    }
110
111    /**
112     * Prints the specified error message as svg.
113     * @param string $msg error message, HTML escaped
114     * @return string xml svg image with the error message
115     */
116    private function printSvgError( $msg ) {
117        $result = <<<SVG
118<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 4" preserveAspectRatio="xMidYMid meet" >
119<text text-anchor="start" fill="red" y="2">
120$msg
121</text>
122</svg>
123SVG;
124        if ( $this->getConfig()->get( MainConfigNames::DebugComments ) ) {
125            $result .= '<!--' . var_export( $this->renderer, true ) . '-->';
126        }
127        return $result;
128    }
129
130    protected function getGroupName(): string {
131        return 'other';
132    }
133}