![]() ![]() | ![]() |
As usual forgot to remove the debug logging
1: # The LearningOnline Network with CAPA 2: # TeX Conversion Module 3: # 4: # $Id: lontexconvert.pm,v 1.86 2008/01/29 11:18:06 foxr Exp $ 5: # 6: # Copyright Michigan State University Board of Trustees 7: # 8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA). 9: # 10: # LON-CAPA is free software; you can redistribute it and/or modify 11: # it under the terms of the GNU General Public License as published by 12: # the Free Software Foundation; either version 2 of the License, or 13: # (at your option) any later version. 14: # 15: # LON-CAPA is distributed in the hope that it will be useful, 16: # but WITHOUT ANY WARRANTY; without even the implied warranty of 17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18: # GNU General Public License for more details. 19: # 20: # You should have received a copy of the GNU General Public License 21: # along with LON-CAPA; if not, write to the Free Software 22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23: # 24: # /home/httpd/html/adm/gpl.txt 25: # 26: # http://www.lon-capa.org/ 27: # 28: # Copyright for TtHfunc and TtMfunc by Ian Hutchinson. 29: # TtHfunc and TtMfunc (the "Code") may be compiled and linked into 30: # binary executable programs or libraries distributed by the 31: # Michigan State University (the "Licensee"), but any binaries so 32: # distributed are hereby licensed only for use in the context 33: # of a program or computational system for which the Licensee is the 34: # primary author or distributor, and which performs substantial 35: # additional tasks beyond the translation of (La)TeX into HTML. 36: # The C source of the Code may not be distributed by the Licensee 37: # to any other parties under any circumstances. 38: # 39: 40: package Apache::lontexconvert; 41: 42: use strict; 43: use tth(); 44: use vars qw($errorstring); 45: #use Apache::lonxml(); 46: use Apache::lonlocal; 47: use Apache::lonnet; 48: use lib '/home/httpd/lib/perl/'; 49: use LONCAPA; 50: 51: 52: # ====================================================================== Header 53: 54: sub init_tth { 55: my $options=$env{'course.'.$env{'request.course.id'}.'.tthoptions'}; 56: if ($options =~ /\S/) { 57: $options = ' '.$options; 58: } else { 59: undef($options); 60: } 61: if ($env{'browser.mathml'}) { 62: &tth::ttminit(); 63: if ($env{'browser.unicode'}) { 64: &tth::ttmoptions('-L -u1'.$options); 65: } else { 66: &tth::ttmoptions('-L -u0'.$options); 67: } 68: } else { 69: &tth::tthinit(); 70: if ($env{'browser.unicode'}) { 71: &tth::tthoptions('-L -u1'.$options); 72: } else { 73: &tth::tthoptions('-L -u0'.$options); 74: } 75: } 76: } 77: 78: # ================================================================== Conversion 79: 80: $Apache::lontexconvert::messedup=0; 81: 82: # we need this routine because &converted can get called from inside 83: # of the safespace (through &xmlparse('<m>stuff</m>') which doesn't 84: # allow the opcode for alarm, so we need to compile this before we get 85: # into the safe space since opcode checks only occur at compile time 86: sub convert_real { 87: my ($texstring)=@_; 88: my ($xmlstring,$errorstring); 89: local $SIG{SEGV}=sub { $Apache::lontexconvert::messedup=1; die; }; 90: local $SIG{ALRM}=sub { 91: &Apache::lonnet::logthis("ALRM"); 92: $xmlstring='['.&mt("TeX unconverted due to errors").']'; 93: $Apache::lontexconvert::messedup=1; 94: die &mt("TeX unconverted due to errors"); 95: }; 96: &Apache::lonxml::start_alarm(); 97: if ($env{'browser.mathml'}) { 98: $xmlstring=&tth::ttm($$texstring); 99: $xmlstring=~s/\<math\>/\<math xmlns=\"\&mathns\;\"\>/g; 100: $xmlstring=~s/\<br\>/\<br\/\>/g; 101: $xmlstring=~s/\<p\>/\<p\>\<\/p\>/g; 102: $errorstring.=&tth::ttmerror(); 103: } else { 104: $xmlstring=&tth::tth($$texstring); 105: $errorstring.=&tth::ttherror(); 106: $xmlstring=~s-</font(\s*)>-</font>-g; 107: } 108: $xmlstring=~s/^\s*\<br clear\=\"all\"/\<br/s; 109: $xmlstring=~s/^\s*//; 110: $xmlstring=~s/\s*$//; 111: # 112: # \rightleftharpoons is not converted by tth but maps 113: # reasonably well to ⇔. If we get many more of these, 114: # we're going to need to have a translation sub. 115: # 116: $xmlstring=~s/\\rightleftharpoons/\&\#8660;/g; 117: 118: &Apache::lonxml::end_alarm(); 119: return ($xmlstring,$errorstring); 120: } 121: 122: sub tth_converted { 123: my $texstring=shift; 124: my $xmlstring='['.&mt('UNDISPLAYABLE').']'; 125: if ($Apache::lontexconvert::messedup) { 126: return '['.&mt('TeX unconverted due to previous errors').']'; 127: } 128: $$texstring ='\\documentstyle{article}'.$$texstring; 129: 130: eval(<<'ENDCONV'); 131: ($xmlstring,$errorstring)=&convert_real($texstring) 132: ENDCONV 133: if ($@) { 134: $errorstring.=&mt("Evaluation Error: ").$@; 135: $Apache::lontexconvert::messedup=1; 136: } 137: if ($Apache::lontexconvert::messedup || &tth::tthmessedup() || 138: $errorstring) { 139: &Apache::lonnet::logthis("Trying to kill myself"); 140: $Apache::lontexconvert::messedup=1; 141: if (ref($Apache::lonxml::request)) { 142: $Apache::lonxml::request->child_terminate(); 143: } else { 144: my $request; 145: eval { $request=Apache->request; }; 146: if (!$request) { 147: eval { $request=Apache2::RequestUtil->request; }; 148: } 149: if ($request) { 150: $request->child_terminate(); 151: } else { 152: &Apache::lonnet::logthis("Unable to find a request to do child_terminate on"); 153: } 154: } 155: } 156: return $xmlstring; 157: } 158: 159: sub clean_out_math_mode { 160: my ($texstring)=@_; 161: $$texstring=~s/(?<!\\)\$//g; 162: $$texstring=~s/\\[\)\(\]\[]//g; 163: $$texstring=~s/\\ensuremath//g; 164: return ''; 165: } 166: 167: sub displaystyle { 168: my ($texstring)=@_; 169: #has a $$ or \[ or \displaystyle in it, guessinng it's display mode 170: if ($$texstring=~/[^\\]\$\$/ || 171: $$texstring=~/\\\[/ || 172: $$texstring=~/\\displaystyle/) { return 1; } 173: return 0; 174: } 175: 176: sub jsMath_converted { 177: my $texstring=shift; 178: my $tag='span'; 179: if (&displaystyle($texstring)) { $tag='div'; } 180: &clean_out_math_mode($texstring); 181: return &jsMath_header(). 182: '<'.$tag.' class="math">'.$$texstring.'</'.$tag.'>'; 183: } 184: 185: { 186: my @jsMath_sent_header; 187: sub jsMath_reset { 188: undef(@jsMath_sent_header); 189: } 190: sub jsMath_push { 191: push(@jsMath_sent_header,0); 192: } 193: sub jsMath_header { 194: if (!@jsMath_sent_header) { 195: &Apache::lonnet::logthis("mismatched calls of jsMath_header and jsMath_process"); 196: return ''; 197: } 198: return '' if $jsMath_sent_header[-1]; 199: $jsMath_sent_header[-1]=1; 200: return 201: '<script type="text/javascript"> 202: function NoFontMessage () {} 203: jsMath = {Parser: {prototype: {environments: {\'eqnarray*\' :[\'Array\',null,null,\'rcl\',[5/18,5/18],3,\'D\']}}}}; 204: </script>'."\n". 205: '<script type="text/javascript" src="/adm/jsMath/jsMath.js"></script>'."\n"; 206: } 207: sub jsMath_process { 208: my $state = pop(@jsMath_sent_header); 209: return '' if !$state; 210: return "\n". 211: '<script type="text/javascript">jsMath.Process()</script>'."\n"; 212: } 213: sub jsMath_state { 214: my ($level) = @_; 215: return $jsMath_sent_header[$level]; 216: } 217: } 218: 219: sub tex_engine { 220: if (exists($env{'form.texengine'})) { 221: return $env{'form.texengine'}; 222: } 223: if ($env{'request.course.id'} 224: && exists($env{'course.'.$env{'request.course.id'}.'.texengine'})) { 225: return $env{'course.'.$env{'request.course.id'}.'.texengine'}; 226: } 227: if (exists($env{'environment.texengine'})) { 228: return $env{'environment.texengine'}; 229: } 230: return 'tth'; 231: } 232: 233: sub init_math_support { 234: my ($inherit_jsmath) = @_; 235: &init_tth(); 236: &Apache::lontexconvert::jsMath_push(); 237: if (lc(&tex_engine()) eq 'jsmath' || 238: ($inherit_jsmath && &jsMath_state(-2))) { 239: return &Apache::lontexconvert::jsMath_header(); 240: } 241: return; 242: } 243: 244: sub mimetex_converted { 245: my $texstring=shift; 246: my $displaystyle=&displaystyle($texstring); 247: 248: &clean_out_math_mode($texstring); 249: 250: if ($displaystyle) { 251: $$texstring='\\displaystyle \\Large '.$$texstring; 252: } 253: my $result='<img src="/cgi-bin/mimetex.cgi?'.&escape($$texstring).'" />'; 254: if ($displaystyle) { 255: $result='<center>'.$result.'</center>'; 256: } 257: return $result; 258: } 259: 260: sub converted { 261: my ($string,$mode)=@_; 262: if ($mode eq '') { $mode = &tex_engine(); } 263: if ($mode =~ /tth/i) { 264: return &tth_converted($string); 265: } elsif ($mode =~ /jsmath/i) { 266: return &jsMath_converted($string); 267: } elsif ($mode =~ /mimetex/i) { 268: return &mimetex_converted($string); 269: } 270: return &tth_converted($string); 271: } 272: 273: # ------------------------------------------------------------ Message display 274: 275: sub to_convert { 276: my ($string) = @_; 277: $string=~s/\<br\s*\/?\>/ /gs; 278: # $string=~s/\s/ /gs; 279: $string=&HTML::Entities::decode($string); 280: return &converted(\$string); 281: } 282: 283: sub smiley { 284: my $expression=shift; 285: if ($env{'browser.imagesuppress'} eq 'on') { return $expression; } 286: my %smileys=('\:\-\)' => 'smiley', 287: '8\-\)' => 'coolsmile', 288: '8\-(I|\|)' => 'coolindiff', 289: ':\-(I|\|)' => 'neutral', 290: '\:\-(o|O|\(\))' => 'shocked', 291: ':\-\(' => 'frowny', 292: '\;\-\)' => 'wink', 293: '\:\-P' => 'baeh', 294: '\:\-(\\\|\\/)' => 'hrrm', 295: '\:\-D' => 'bigsmile', 296: '\:\-C' => 'angry', 297: '\:(\'|\`)\-\(' => 'cry', 298: '\:\-(X|\#)' => 'lipsrsealed', 299: '\:\-S' => 'huh'); 300: my $iconpath=$Apache::lonnet::perlvar{'lonIconsURL'}; 301: foreach my $smiley (keys(%smileys)) { 302: $expression=~s/$smiley/\<img src="$iconpath\/$smileys{$smiley}.gif" \/\>/gs; 303: } 304: return $expression; 305: } 306: 307: sub msgtexconverted { 308: my ($message,$email) = @_; 309: $errorstring=''; 310: &init_tth(); 311: my $outmessage=''; 312: my $tex=0; 313: foreach my $fragment (split(/(?:\<\;|\<)\/*m\s*(?:\>\;|\>)/i,$message)) { 314: if ($tex) { 315: if ($email) { 316: $outmessage.='</pre><tt>'.&to_convert($fragment).'</tt><pre>'; 317: $tex=0; 318: } else { 319: $outmessage.=&to_convert($fragment); 320: $tex=0; 321: } 322: } else { 323: $outmessage.=&smiley($fragment); 324: $tex=1; 325: } 326: } 327: $message=$outmessage; $outmessage=''; $tex=0; 328: foreach my $fragment (split(/(?:\<\;|\<)\/*algebra\s*(?:\>\;|\>)/i, 329: $message)) { 330: if ($tex) { 331: if ($email) { 332: $outmessage.='</pre><tt>'.&algebra($fragment,'web').'</tt><pre>'; 333: $tex=0; 334: } else { 335: $outmessage.=&algebra($fragment,'web'); 336: $tex=0; 337: } 338: } else { 339: $outmessage.=$fragment; 340: $tex=1; 341: } 342: } 343: if (wantarray) { 344: return ($outmessage,$errorstring); 345: } else { 346: return $outmessage.$errorstring; 347: } 348: } 349: 350: sub algebra { 351: use AlgParser; 352: 353: my ($string,$target,$style)=@_; 354: my $parser = new AlgParserWithImplicitExpand; 355: $string=&prepare_algebra($string); 356: my $ret = $parser->parse($string); 357: my $result='['.&mt('Algebra unconverted due to previous errors').']'; 358: if ( ref($ret) ) { 359: #$parser->tostring(); 360: $parser->normalize(); 361: my $latex=$parser->tolatex(); 362: $latex=&postprocess_algebra($latex); 363: if ($style eq 'display') { 364: $latex='$$'.$latex.'$$x'; 365: } else { 366: # style is 'inline' 367: $latex='\\ensuremath{'.$latex.'}'; 368: } 369: if ($target eq 'web' || $target eq 'analyze') { 370: $result = &converted(\$latex); 371: } else { 372: $result = $latex; 373: } 374: } else { 375: &Apache::lonxml::error($parser->{'htmlerror'}); 376: } 377: } 378: 379: sub prepare_algebra { 380: my ($string)=@_; 381: 382: # makes the decision about what is a minus sign easier supposedly 383: $string =~ s/(\<\>|\<\=|\>\=[\=\>\<] *)-/$1 zeroplace -/g; 384: 385: return $string; 386: } 387: 388: sub postprocess_algebra { 389: my ($string)=@_; 390: 391: # moodle had these and I don't know why, ignoring them for now 392: # $string =~s/\\fun/ /g; 393: 394: # sqrt(3,4) means the 4 root of 3 395: $string =~s/\\sqrt{([^,]+),([^\}]+)}/\\sqrt[$2]{$1}/gs; 396: 397: # log(3,4) means the log base 4 of 3 398: $string =~s/\\log\\left\((.+?),(.+?)\\right\)/\\log_{$2}\\left($1\\right)/gs; 399: 400: # log(3,4) means the log base 4 of 3 401: $string =~s/\\((?:sin|cos|tan|sec|csc|cot)(?:h)?)\\left\((.+?),(.+?)\\right\)/\\$1^{$3}\\left($2\\right)/gs; 402: 403: # int(3,a,b) integral from a to b of 3 404: $string =~s/\\int\\left\((.+?),(.+?),(.+?)\\right\)/\\int_{$2}^{$3}\\left($1\\right)/gs; 405: 406: # int( ... dx) -> ... 407: $string =~s/\\int\\left\((.+?)d[a-z]\\right\)/$1/gs; 408: 409: # 410: $string =~s/\\lim\\left\((.+?),(.+?),(.+?)\\right\)/\\lim_{$2\\to $3}$1/gs; 411: return $string; 412: } 413: 1; 414: __END__ 415: 416: 417: 418: 419: 420: 421: