tags using * different technologies. These static methods create a new instance of the * extending classes and render the math tags based on the mode setting of the user. * Furthermore this class handles the caching of the rendered output and provides * debug information, * if run in mathdebug mode. * * @author Tomasz Wegrzanowski * @author Brion Vibber * @author Moritz Schubotz */ abstract class MathRenderer { /** * The following variables should made private, as soon it can be verified * that they are not being directly accessed by other extensions. */ var $mode = MW_MATH_PNG; var $tex = ''; /** * is calculated by texvc. * @var string */ var $hash = ''; var $html = ''; var $mathml = ''; var $conservativeness = 0; var $params = ''; var $changed = false; protected $recall; /** * Constructs a base MathRenderer * * @param string $tex (optional) LaTeX markup * @param array $params (optional) HTML attributes */ public function __construct( $tex='', $params = array() ) { $this->tex = $tex; $this->params = $params; } /** * Static method for rendering math tag * * @param string $tex LaTeX markup * @param array $params HTML attributes * @param int $mode constant indicating rendering mode * @return string HTML for math tag */ public static function renderMath( $tex, $params = array(), $mode = MW_MATH_PNG ) { $renderer = self::getRenderer( $tex, $params, $mode ); return $renderer->render(); } /** * Static factory method for getting a renderer based on mode * * @param string $tex LaTeX markup * @param array $params HTML attributes * @param int $mode constant indicating rendering mode * @return MathRenderer appropriate renderer for mode */ public static function getRenderer( $tex, $params = array(), $mode = MW_MATH_PNG ) { global $wgDefaultUserOptions; $validModes = array( MW_MATH_PNG, MW_MATH_SOURCE, MW_MATH_MATHJAX ); if ( !in_array( $mode, $validModes ) ) $mode = $wgDefaultUserOptions['math']; switch ( $mode ) { case MW_MATH_SOURCE: $renderer = new MathSource( $tex, $params ); break; case MW_MATH_MATHJAX: $renderer = new MathMathJax( $tex, $params ); break; case MW_MATH_PNG: default: $renderer = new MathTexvc( $tex, $params ); } wfDebugLog ( "Math", 'start rendering $' . $renderer->tex . '$' ); return $renderer; } /** * Performs the rendering and returns the rendered element that needs to be embedded. * * @return string of rendered HTML */ abstract public function render(); /** * texvc error messages * TODO: update to MathML * Returns an internationalized HTML error string * * @param string $msg message key for specific error * @param string $append string to append after error * @return string HTML error string */ protected function getError( $msg, $append = '' ) { $mf = wfMessage( 'math_failure' )->inContentLanguage()->escaped(); $errmsg = wfMessage( $msg )->inContentLanguage()->escaped(); $source = htmlspecialchars( str_replace( "\n", ' ', $this->tex ) ); return "$mf ($errmsg$append): $source\n"; } /** * Return hash of input * * @return string hash */ public function getInputHash() { // TODO: What happens if $tex is empty? $dbr = wfGetDB( DB_SLAVE ); return $dbr->encodeBlob( pack( "H32", md5( $this->tex ) ) ); # Binary packed, not hex } /** * Reads rendering data from database * * @return boolean true if read successfully, false otherwise */ public function readFromDatabase() { $dbr = wfGetDB( DB_SLAVE ); $rpage = $dbr->selectRow( 'math', array( 'math_outputhash', 'math_html_conservativeness', 'math_html', 'math_mathml' ), array( 'math_inputhash' => $this->getInputHash() ), __METHOD__ ); if ( $rpage !== false ) { # Trailing 0x20s can get dropped by the database, add it back on if necessary: $xhash = unpack( 'H32md5', $dbr->decodeBlob( $rpage->math_outputhash ) . " " ); $this->hash = $xhash['md5']; $this->conservativeness = $rpage->math_html_conservativeness; $this->html = $rpage->math_html; $this->mathml = $rpage->math_mathml; $this->recall = true; return true; } # Missing from the database and/or the render cache $this->recall = false; return false; } /** * Writes rendering entry to database. * * WARNING: Use writeCache() instead of this method to be sure that all * renderer specific (such as squid caching) are taken into account. * This function stores the values that are currently present in the class to the database even if they are empty. * * This function can be seen as protected function. */ public function writeToDatabase() { # Now save it back to the DB: if ( !wfReadOnly() ) { $dbw = wfGetDB( DB_MASTER ); if ( $this->hash !== '' ) { $outmd5_sql = $dbw->encodeBlob( pack( 'H32', $this->hash ) ); } else { $outmd5_sql = ''; } wfDebugLog( "Math", 'store entry for $' . $this->tex . '$ in database (hash:' . $this->hash . ')\n' ); $dbw->replace( 'math', array( 'math_inputhash' ), array( 'math_inputhash' => $this->getInputHash(), 'math_outputhash' => $outmd5_sql , 'math_html_conservativeness' => $this->conservativeness, 'math_html' => $this->html, 'math_mathml' => $this->mathml, ), __METHOD__ ); } } /** * Returns sanitized attributes * * @param string $tag element name * @param array $defaults default attributes * @param array $overrides attributes to override defaults * @return array HTML attributes */ protected function getAttributes( $tag, $defaults = array(), $overrides = array() ) { $attribs = Sanitizer::validateTagAttributes( $this->params, $tag ); $attribs = Sanitizer::mergeAttributes( $defaults, $attribs ); $attribs = Sanitizer::mergeAttributes( $attribs, $overrides ); return $attribs; } /** * Writes cache. Does nothing by default */ public function writeCache() { } /** * Determines if this is a cached/recalled render * * @return boolean true if recalled, false otherwise */ public function isRecall() { return $this->recall; } /** * Gets TeX markup * * @return string TeX markup */ public function getTex() { return $this->tex; } /** * gets the rendering mode MW_MATH_* * * @return int */ public function getMode() { return $this->mode; } /** * Sets the TeX code * * @param string $tex */ public function setTex( $tex ) { $this->changed = true; $this->tex = $tex; } /** * Get the hash calculated by texvc * * @return string hash */ public function getHash() { return $this->hash; } /** * @param string $hash */ public function setHash( $hash ) { $this->changed = true; $this->hash = $hash; } /** * Returns the html-representation of the mathematical formula. * @return string */ public function getHtml() { return $this->html; } /** * @param string $html */ public function setHtml( $html ) { $this->changed = true; $this->html = $html; } /** * Gets the MathML XML element * @return string in UTF-8 encoding */ public function getMathml() { return $this->mathml; } /** * @param string $mathml use UTF-8 encoding */ public function setMathml( $mathml ) { $this->changed = true; $this->mathml = $mathml; } /** * Gets the so called 'conservativeness' calculated by texvc * * @return int */ public function getConservativeness() { return $this->conservativeness; } /** * @param int $conservativeness */ public function setConservativeness( $conservativeness ) { $this->changed = true; $this->conservativeness = $conservativeness; } /** * Get the attributes of the math tag * * @return array() */ public function getParams() { return $this->params; } /** * @param array() $params */ public function setParams( $params ) { //$changed is not set to true here, because the attributes do not affect //the rendering in the current implementation. //If this behavior will change in the future $this->tex is no longer a //primary key and the input hash cannot be calculate form $this->tex //only. See the discussion 'Tag extensions in Block mode' on wikitech-l. $this->params = $params; } /** * Checks if the instance was modified i.e., because math was rendered * * @return boolean true if something was changed false otherwise */ public function isChanged() { return $this->changed; } }