From 93b09379fb68515596bd3228d6efa168d14ddbae Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 22 Apr 2004 08:51:04 +0000 Subject: [PATCH] Add experimental MathML output option for math (only works with some browsers and requires use of an XML content type; try setting $wgMimeType = "application/xhtml+xml" and use Mozilla). Rearranged a couple things in prefs. Did some refactoring in Math.php to remove some duplicated code and figure out what is going on. --- includes/Math.php | 318 +++++++++++++++++++------------- includes/Metadata.php | 2 +- includes/SpecialPreferences.php | 57 +++--- index.php | 1 + languages/Language.php | 18 +- languages/LanguageEo.php | 1 + 6 files changed, 231 insertions(+), 166 deletions(-) diff --git a/includes/Math.php b/includes/Math.php index 0cd899755c..c487eb7fef 100644 --- a/includes/Math.php +++ b/includes/Math.php @@ -1,153 +1,205 @@ "; -} - -function renderMath( $tex ) -{ - global $wgUser, $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding; - global $wgTexvc; - $mf = wfMsg( "math_failure" ); - $munk = wfMsg( "math_unknown_error" ); - - $fname = "renderMath"; - - $math = $wgUser->getOption("math"); - if( $math == 3 ) { - return ('$ '.wfEscapeHTML($tex).' $'); - } - - $md5 = md5($tex); - $md5_sql = mysql_escape_string(pack("H32", $md5)); - if ($math == 0) { - $sql = "SELECT math_outputhash FROM math WHERE math_inputhash = '$md5_sql'"; - } else { - $sql = "SELECT math_outputhash,math_html_conservativeness,math_html FROM math WHERE math_inputhash = '$md5_sql'"; +# +# Takes LaTeX fragments, sends them to a helper program (texvc) for rendering +# to rasterized PNG and HTML and MathML approximations. An appropriate +# rendering form is picked and returned. +# +# by Tomasz Wegrzanowski, with additions by Brion Vibber (2003, 2004) +# + +class MathRenderer { + var $mode = MW_MATH_MODERN; + var $tex = ""; + var $inputhash = ""; + var $hash = ""; + var $html = ""; + var $mathml = ""; + var $conservativeness = 0; + + function MathRenderer( $tex ) { + $this->tex = $tex; + } + + function setOutputMode( $mode ) { + $this->mode = $mode; } - $res = wfQuery( $sql, DB_READ, $fname ); - - if( $rpage = wfFetchObject ( $res ) ) { - $outputhash = unpack( "H32md5", $rpage->math_outputhash . " " ); - $outputhash = $outputhash ['md5']; - if( file_exists( "$wgMathDirectory/$outputhash.png" ) ) { - if (($math == 0) || ($rpage->math_html == '') || (($math == 1) && ($rpage->math_html_conservativeness != 2)) || (($math == 4) && ($rpage->math_html_conservativeness == 0))) { - return linkToMathImage ( $tex, $outputhash ); - } else { - return $rpage->math_html; - } - } - } + function render() { + global $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding; + global $wgTexvc; - # Ensure that the temp and output directories are available before continuing... - if( !file_exists( $wgMathDirectory ) ) { - if( !@mkdir( $wgMathDirectory ) ) { - return "$mf (" . wfMsg( "math_bad_output" ) . ")"; + if( $this->mode == MW_MATH_SOURCE ) { + # No need to render or parse anything more! + return ('$ '.htmlspecialchars( $tex ).' $'); } - } elseif( !is_dir( $wgMathDirectory ) || !is_writable( $wgMathDirectory ) ) { - return "$mf (" . wfMsg( "math_bad_output" ) . ")"; - } - if( !file_exists( $wgTmpDirectory ) ) { - if( !@mkdir( $wgTmpDirectory ) ) { - return "$mf (" . wfMsg( "math_bad_tmpdir" ) . ")"; + + if( !$this->_recall() ) { + # Ensure that the temp and output directories are available before continuing... + if( !file_exists( $wgMathDirectory ) ) { + if( !@mkdir( $wgMathDirectory ) ) { + return $this->_error( "math_bad_output" ); + } + } elseif( !is_dir( $wgMathDirectory ) || !is_writable( $wgMathDirectory ) ) { + return $this->_error( "math_bad_output" ); + } + if( !file_exists( $wgTmpDirectory ) ) { + if( !@mkdir( $wgTmpDirectory ) ) { + return $this->_error( "math_bad_tmpdir" ); + } + } elseif( !is_dir( $wgTmpDirectory ) || !is_writable( $wgTmpDirectory ) ) { + return $this->_error( "math_bad_tmpdir" ); + } + + if( !is_executable( $wgTexvc ) ) { + return $this->_error( "math_notexvc" ); + } + $cmd = $wgTexvc." ". + escapeshellarg($wgTmpDirectory)." ". + escapeshellarg($wgMathDirectory)." ". + escapeshellarg($this->tex)." ". + escapeshellarg($wgInputEncoding); + wfDebug( "TeX: $cmd" ); + $contents = `$cmd`; + + if (strlen($contents) == 0) { + return $this->_error( "math_unknown_error" ); + } + + $retval = substr ($contents, 0, 1); + if (($retval == "C") || ($retval == "M") || ($retval == "L")) { + if ($retval == "C") + $this->conservativeness = 2; + else if ($retval == "M") + $this->conservativeness = 1; + else + $this->conservativeness = 0; + $outdata = substr ($contents, 33); + + $i = strpos($outdata, "\000"); + + $this->html = substr($outdata, 0, $i); + $this->mathml = substr($outdata, $i+1); + + $sql_html = "'".wfStrencode($this->html)."'"; + $sql_mathml = "'".wfStrencode($this->mathml)."'"; + } else if (($retval == "c") || ($retval == "m") || ($retval == "l")) { + $this->html = substr ($contents, 33); + if ($retval == "c") + $this->conservativeness = 2; + else if ($retval == "m") + $this->conservativeness = 1; + else + $this->conservativeness = 0; + $sql_html = "'".wfStrencode($this->html)."'"; + $this->mathml = ''; + $sql_mathml = 'NULL'; + } else if ($retval == "X") { + $outhtml = ''; + $this->mathml = substr ($contents, 33); + $sql_html = 'NULL'; + $sql_mathml = "'".wfStrencode($this->mathml)."'"; + $this->conservativeness = 0; + } else if ($retval == "+") { + $this->outhtml = ''; + $this->mathml = ''; + $sql_html = 'NULL'; + $sql_mathml = 'NULL'; + $this->conservativeness = 0; + } else { + $errbit = htmlspecialchars( substr($contents, 1) ); + switch( $retval ) { + case "E": return $this->_error( "math_lexing_error", $errbit ); + case "S": return $this->_error( "math_syntax_error", $errbit ); + case "F": return $this->_error( "math_unknown_function", $errbit ); + default: return $this->_error( "math_unknown_error", $errbit ); + } + } + + $this->hash = substr ($contents, 1, 32); + if (!preg_match("/^[a-f0-9]{32}$/", $this->hash)) { + return $this->_error( "math_unknown_error" ); + } + + if( !file_exists( "$wgMathDirectory/{$this->hash}.png" ) ) { + return $this->_error( "math_image_error" ); + } + + # Now save it back to the DB: + $outmd5_sql = wfStrencode(pack("H32", $this->hash)); + + $md5_sql = wfStrencode( pack("H32", $this->md5) ); # Binary packed, not hex + $sql = "REPLACE INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$this->conservativeness.", ".$sql_html.", ".$sql_mathml.")"; + + $res = wfQuery( $sql, DB_READ, "MathRenderer::render" ); + # we don't really care if it fails } - } elseif( !is_dir( $wgTmpDirectory ) || !is_writable( $wgTmpDirectory ) ) { - return "$mf (" . wfMsg( "math_bad_tmpdir" ) . ")"; + + return $this->_doRender(); } - if( !is_executable( $wgTexvc ) ) { - return "$mf (" . wfMsg( "math_notexvc" ) . ")"; - } - $cmd = $wgTexvc." ". - escapeshellarg($wgTmpDirectory)." ". - escapeshellarg($wgMathDirectory)." ". - escapeshellarg($tex)." ". - escapeshellarg($wgInputEncoding); - wfDebug( "TeX: $cmd" ); - $contents = `$cmd`; - - if (strlen($contents) == 0) { - return "".$mf." (".$munk."): ".wfEscapeHTML($tex).""; + function _error( $msg, $append = "" ) { + $mf = htmlspecialchars( wfMsg( "math_failure" ) ); + $munk = htmlspecialchars( wfMsg( "math_unknown_error" ) ); + $errmsg = htmlspecialchars( wfMsg( $msg ) ); + $source = htmlspecialchars($this->tex); + return "$mf ($errmsg$append): $source\n"; } - $retval = substr ($contents, 0, 1); - if (($retval == "C") || ($retval == "M") || ($retval == "L")) { - if ($retval == "C") - $conservativeness = 2; - else if ($retval == "M") - $conservativeness = 1; - else - $conservativeness = 0; - $outdata = substr ($contents, 33); - - $i = strpos($outdata, "\000"); - - $outhtml = substr($outdata, 0, $i); - $mathml = substr($outdata, $i+1); - - $sql_html = "'".mysql_escape_string($outhtml)."'"; - $sql_mathml = "'".mysql_escape_string($mathml)."'"; - } else if (($retval == "c") || ($retval == "m") || ($retval == "l")) { - $outhtml = substr ($contents, 33); - if ($retval == "c") - $conservativeness = 2; - else if ($retval == "m") - $conservativeness = 1; - else - $conservativeness = 0; - $sql_html = "'".mysql_escape_string($outhtml)."'"; - $mathml = ''; - $sql_mathml = 'NULL'; - } else if ($retval == "X") { - $outhtml = ''; - $mathml = substr ($contents, 33); - $sql_html = 'NULL'; - $sql_mathml = "'".mysql_escape_string($mathml)."'"; - $conservativeness = 0; - } else if ($retval == "+") { - $outhtml = ''; - $mathml = ''; - $sql_html = 'NULL'; - $sql_mathml = 'NULL'; - $conservativeness = 0; - } else { - if ($retval == "E") - $errmsg = wfMsg( "math_lexing_error" ); - else if ($retval == "S") - $errmsg = wfMsg( "math_syntax_error" ); - else if ($retval == "F") - $errmsg = wfMsg( "math_unknown_function" ); - else - $errmsg = $munk; - return "

".$mf." (".$errmsg.substr($contents, 1)."): ".wfEscapeHTML($tex)."

"; + function _recall() { + global $wgMathDirectory; + + $this->md5 = md5( $this->tex ); + + $md5_sql = wfStrencode( pack("H32", $this->md5) ); # Binary packed, not hex + $sql = "SELECT math_outputhash,math_html_conservativeness,math_html,math_mathml FROM math WHERE math_inputhash = '$md5_sql'"; + $res = wfQuery( $sql, DB_READ, "MathRenderer::_recall" ); + + if( $rpage = wfFetchObject ( $res ) ) { + # Tailing 0x20s can get dropped by the database, add it back on if necessary: + $xhash = unpack( "H32md5", $rpage->math_outputhash . " " ); + $this->hash = $xhash ['md5']; + + $this->conservativeness = $rpage->math_html_conservativeness; + $this->html = $rpage->math_html; + $this->mathml = $rpage->math_mathml; + + if( file_exists( "$wgMathDirectory/{$this->hash}.png" ) ) { + return true; + } + } + + # Missing from the database and/or the render cache + return false; } - $outmd5 = substr ($contents, 1, 32); - if (!preg_match("/^[a-f0-9]{32}$/", $outmd5)) { - return "".$mf." (".$munk."): ".wfEscapeHTML($tex).""; + # Select among PNG, HTML, or MathML output depending on + function _doRender() { + if( $this->mode == MW_MATH_MATHML && $this->mathml != '' ) { + return "{$this->mathml}"; + } + if (($this->mode == MW_MATH_PNG) || ($this->html == '') || + (($this->mode == MW_MATH_SIMPLE) && ($this->conservativeness != 2)) || + (($this->mode == MW_MATH_MODERN || $this->mode == MW_MATH_MATHML) && ($this->conservativeness == 0))) { + return $this->_linkToMathImage(); + } else { + return $this->html; + } } - if( !file_exists( "$wgMathDirectory/$outmd5.png" ) ) { - $errmsg = wfMsg( "math_image_error" ); - return "

$mf ($errmsg): " . wfEscapeHTML($tex) . "

"; + function _linkToMathImage() { + global $wgMathPath; + $url = htmlspecialchars( "$wgMathPath/{$this->hash}.png" ); + $alt = htmlspecialchars( $this->tex ); + return "\"$alt\""; } - $outmd5_sql = mysql_escape_string(pack("H32", $outmd5)); - - $sql = "REPLACE INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$conservativeness.", ".$sql_html.", ".$sql_mathml.")"; - - $res = wfQuery( $sql, DB_READ, $fname ); - # we don't really care if it fails +} - if (($math == 0) || ($rpage->math_html == '') || (($math == 1) && ($conservativeness != 2)) || (($math == 4) && ($conservativeness == 0))) - return linkToMathImage($tex, $outmd5); - else - return $outhtml; +function renderMath( $tex ) { + global $wgUser; + $math = new MathRenderer( $tex ); + $math->setOutputMode( $wgUser->getOption("math")); + return $math->render(); } ?> diff --git a/includes/Metadata.php b/includes/Metadata.php index c8253c4727..f9df5393aa 100644 --- a/includes/Metadata.php +++ b/includes/Metadata.php @@ -18,7 +18,7 @@ */ define("RDF_TYPE_PREFS", "application/rdf+xml,text/xml;q=0.7,application/xml;q=0.5,text/rdf;q=0.1"); - + function wfDublinCoreRdf($article) { $url = dcReallyFullUrl($article->mTitle); diff --git a/includes/SpecialPreferences.php b/includes/SpecialPreferences.php index 46bd003209..dd17a05345 100644 --- a/includes/SpecialPreferences.php +++ b/includes/SpecialPreferences.php @@ -321,17 +321,24 @@ class PreferencesForm { $wgOut->addHTML( "
" ); - # Quickbar setting + # First section: identity + # Email, etc. # - $wgOut->addHtml( "
\n$qb:\n" ); - for ( $i = 0; $i < count( $qbs ); ++$i ) { - if ( $i == $this->mQuickbar ) { $checked = ' checked="checked"'; } - else { $checked = ""; } - $wgOut->addHTML( "
\n" ); - } - $wgOut->addHtml( "
\n\n" ); + $this->mUserEmail = wfEscapeHTML( $this->mUserEmail ); + $this->mRealName = wfEscapeHTML( $this->mRealName ); + $this->mNick = wfEscapeHTML( $this->mNick ); + if ( $this->mEmailFlag ) { $emfc = 'checked="checked"'; } + else { $emfc = ""; } + + $ps = $this->namespacesCheckboxes(); + $wgOut->addHTML( "
+ Idento +
+
+
+
\n" ); + # Fields for changing password # $this->mOldpass = wfEscapeHTML( $this->mOldpass ); @@ -344,7 +351,19 @@ class PreferencesForm {
" . $this->getToggle( "rememberpassword" ) . " -
\n\n" ); + \n\n" ); + + + # Quickbar setting + # + $wgOut->addHtml( "
\n$qb:\n" ); + for ( $i = 0; $i < count( $qbs ); ++$i ) { + if ( $i == $this->mQuickbar ) { $checked = ' checked="checked"'; } + else { $checked = ""; } + $wgOut->addHTML( "
\n" ); + } + $wgOut->addHtml( "
\n\n" ); # Skin setting # @@ -414,23 +433,7 @@ class PreferencesForm {
\n\n" ); - # Email, etc. - # - $this->mUserEmail = wfEscapeHTML( $this->mUserEmail ); - $this->mRealName = wfEscapeHTML( $this->mRealName ); - $this->mNick = wfEscapeHTML( $this->mNick ); - if ( $this->mEmailFlag ) { $emfc = 'checked="checked"'; } - else { $emfc = ""; } - - $ps = $this->namespacesCheckboxes(); - - $wgOut->addHTML( "
-
-
-
-
-
- + $wgOut->addHTML( "
" . $this->getToggle( "hideminor" ) . diff --git a/index.php b/index.php index 6a811df6a5..74f065e5db 100644 --- a/index.php +++ b/index.php @@ -1,4 +1,5 @@ "MonoBook" ); +define( "MW_MATH_PNG", 0 ); +define( "MW_MATH_SIMPLE", 1 ); +define( "MW_MATH_HTML", 2 ); +define( "MW_MATH_SOURCE", 3 ); +define( "MW_MATH_MODERN", 4 ); +define( "MW_MATH_MATHML", 5 ); + /* private */ $wgMathNamesEn = array( - "Always render PNG", - "HTML if very simple or else PNG", - "HTML if possible or else PNG", - "Leave it as TeX (for text browsers)", - "Recommended for modern browsers" + MW_MATH_PNG => "Always render PNG", + MW_MATH_SIMPLE => "HTML if very simple or else PNG", + MW_MATH_HTML => "HTML if possible or else PNG", + MW_MATH_SOURCE => "Leave it as TeX (for text browsers)", + MW_MATH_MODERN => "Recommended for modern browsers", + MW_MATH_MATHML => "MathML if possible (experimental)", ); /* private */ $wgDateFormatsEn = array( diff --git a/languages/LanguageEo.php b/languages/LanguageEo.php index d8497853a0..67b4e829d4 100644 --- a/languages/LanguageEo.php +++ b/languages/LanguageEo.php @@ -49,6 +49,7 @@ $wgEditEncoding = "x"; "HTMLigu se eble, aÅ­ PNG", "Lasu TeX-fonton (por tekstfoliumiloj)", "Rekomendita por modernaj foliumiloj", + "MathML seeble (provizora)", ); /* private */ $wgUserTogglesEo = array( -- 2.20.1