latex.pl 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. # Copyright (C) 2003–2015 Alex Schroeder <alex@gnu.org>
  2. # Copyright (C) 2004 Haixing Hu <huhaixing@msn.com>
  3. # Copyright (C) 2004, 2005 Todd Neal <tolchz@tolchz.net>
  4. #
  5. # This program is free software: you can redistribute it and/or modify it under
  6. # the terms of the GNU General Public License as published by the Free Software
  7. # Foundation, either version 3 of the License, or (at your option) any later
  8. # version.
  9. #
  10. # This program is distributed in the hope that it will be useful, but WITHOUT
  11. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License along with
  15. # this program. If not, see <http://www.gnu.org/licenses/>.
  16. #
  17. # External programs needed
  18. # LaTeX - http://www.latex-project.org
  19. # TeX - http://www.tug.org/teTeX/
  20. #
  21. # And one of :
  22. # dvipng - http://sourceforge.net/projects/dvipng/
  23. # convert - http://www.imagemagick.org/
  24. #
  25. # CSS Styles:
  26. # span.eqCount
  27. # img.LaTeX
  28. # img.InlineMath
  29. # img.DisplayMath
  30. use strict;
  31. use v5.10;
  32. use File::Glob ':glob';
  33. our ($DataDir, @MyRules);
  34. our ($LatexDir, $LatexLinkDir, $LatexExtendPath, $LatexSingleDollars);
  35. # One of the following options must be set correctly to the full path of
  36. # either dvipng or convert. If both paths are set correctly, dvipng is used
  37. # instead of convert
  38. my $dvipngPath = "/usr/bin/dvipng";
  39. my $convertPath = "/usr/bin/convert";
  40. # Set $dispErrors to display LaTeX errors inline on the page.
  41. my $dispErrors = 1;
  42. # Set $useMD5 to 1 if you want to use MD5 hashes for filenames, set it to 0 to use
  43. # a url-encoded hash. If $useMD5 is set and the Digest::MD5 module is not available,
  44. # latex.pl falls back to urlencode
  45. my $useMD5 = 0;
  46. # PATH must be extended in order to make latex available along with
  47. # any binaries that it may need to work
  48. $LatexExtendPath = ':/usr/share/texmf/bin:/usr/bin:/usr/local/bin';
  49. # Allow single dollars signs to escape LaTeX math commands
  50. $LatexSingleDollars = 0;
  51. # Set $allowPlainTeX to 1 to allow normal LaTeX commands inside of $[ ]$
  52. # to be executed outside of the math environment. This should only be done
  53. # if your wiki is not publically editable because of the possible security risk
  54. my $allowPlainLaTeX = 0;
  55. # $LatexDir must be accessible from the outside as $LatexLinkDir. The
  56. # first directory is used to *save* the pictures, the second directory
  57. # is used to produce the *link* to the pictures.
  58. #
  59. # Example: You store the images in /org/org.emacswiki/htdocs/test/latex.
  60. # This directory is reachable from the outside as http://www.emacswiki.org/test/latex/.
  61. # /org/org.emacswiki/htdocs/test is your $DataDir.
  62. $LatexDir = "$DataDir/latex";
  63. $LatexLinkDir= "/wiki/latex";
  64. # Text used when referencing equations with EQ(equationLabel)
  65. my $eqAbbrev = "Eq. ";
  66. # You also need a template stored as $DataDir/template.latex. The
  67. # template must contain the string <math> where the LaTeX code is
  68. # supposed to go. It will be created on the first run.
  69. my $LatexDefaultTemplateName = "$LatexDir/template.latex";
  70. AddModuleDescription('latex.pl', 'LaTeX Extension');
  71. # Internal Equation counting and referencing variables
  72. my $eqCounter = 0;
  73. my %eqHash;
  74. my $LatexDefaultTemplate = << 'EOT';
  75. \documentclass[12pt]{article}
  76. \pagestyle{empty}
  77. \begin{document}
  78. <math>
  79. \end{document}
  80. EOT
  81. push(@MyRules, \&LatexRule);
  82. sub LatexRule {
  83. if (m/\G\\\[(\(.*?\))?((.*\n)*?.*?)\\\]/cg) {
  84. my $label = $1;
  85. my $latex = $2;
  86. $label =~ s#\(?\)?##g;# Remove the ()'s from the label and convert case
  87. $label =~ tr/A-Z/a-z/;
  88. $eqCounter++;
  89. $eqHash{$label} = $eqCounter;
  90. return &MakeLaTeX("\\begin{displaymath} $latex \\end{displaymath}", "display math",$label);
  91. } elsif (m/\G\$\$((.*\n)*?.*?)\$\$/cg) {
  92. return &MakeLaTeX("\$\$ $1 \$\$", $LatexSingleDollars ? "display math" : "inline math");
  93. } elsif ($LatexSingleDollars and m/\G\$((.*\n)*?.*?)\$/cg) {
  94. return &MakeLaTeX("\$ $1 \$", "inline math");
  95. } elsif ($allowPlainLaTeX && m/\G\$\[((.*\n)*?.*?)\]\$/cg) { #Pick up plain LaTeX commands
  96. return &MakeLaTeX(" $1 ", "LaTeX");
  97. } elsif (m/\GEQ\((.*?)\)/cg) { # Handle references to equations
  98. my $label = $1;
  99. $label =~ tr/A-Z/a-z/;
  100. if ($eqHash{$label}) {
  101. return $eqAbbrev . "<a href=\"#$label\">". $eqHash{$label} . "</a>";
  102. }
  103. else {
  104. return "[ Equation $label not found ]";
  105. }
  106. }
  107. return;
  108. }
  109. sub MakeLaTeX {
  110. my ($latex, $type, $label) = @_;
  111. $ENV{PATH} .= $LatexExtendPath if $LatexExtendPath and $ENV{PATH} !~ /$LatexExtendPath/;
  112. # Select which binary to use for conversion of dvi to images
  113. my $useConvert = 0;
  114. if (not IsFile($dvipngPath)) {
  115. if (not IsFile($convertPath)) {
  116. return "[Error: dvipng binary and convert binary not found at $dvipngPath or $convertPath ]";
  117. }
  118. else {
  119. $useConvert = 1; # Fall back on convert if dvipng is missing and convert exists
  120. }
  121. }
  122. $latex = UnquoteHtml($latex); # Change &lt; back to <, for example
  123. # User selects which hash to use
  124. my $hash;
  125. my $hasMD5;
  126. $hasMD5 = eval { require Digest::MD5 } if $useMD5;
  127. if ($useMD5 && $hasMD5) {
  128. $hash = Digest::MD5::md5_base64($latex);
  129. $hash =~ s/\//a/g;
  130. } else {
  131. $hash = UrlEncode($latex);
  132. $hash =~ s/%//g;
  133. }
  134. # check cache
  135. if (not IsFile("$LatexDir/$hash.png") or ZeroSize("$LatexDir/$hash.png")) {
  136. # Then create the image
  137. # read template and replace <math>
  138. CreateDir($LatexDir);
  139. if (not IsFile($LatexDefaultTemplateName)) {
  140. open (my $F, '>', encode_utf8($LatexDefaultTemplateName)) or return '[Unable to write template]';
  141. print $F $LatexDefaultTemplate;
  142. close $F;
  143. }
  144. my $template = ReadFileOrDie($LatexDefaultTemplateName);
  145. $template =~ s/<math>/$latex/gi;
  146. #setup rendering directory
  147. my $dir = "$LatexDir/$hash";
  148. if (IsDir($dir)) {
  149. Unlink((Glob("$dir/*")));
  150. } else {
  151. CreateDir($dir);
  152. }
  153. ChangeDir($dir) or return "[Unable to switch to $dir]";
  154. WriteStringToFile ("srender.tex", $template);
  155. my $errorText = qx(latex srender.tex);
  156. # We should not return until we're ready to clean up!
  157. my $error = '';
  158. if ($? && $dispErrors) {
  159. $error = "[Illegal LaTeX markup: <pre>$latex</pre>] <br/> Error: <pre>$errorText</pre>";
  160. } elsif ($?) {
  161. $error = "[Illegal LaTeX markup: <pre>$latex</pre>] <br/>";
  162. } else {
  163. my $output;
  164. # Use specified binary to convert dvi to png
  165. if ($useConvert) {
  166. $output = qx($convertPath -antialias -crop 0x0 -density 120x120 -transparent white srender.dvi srender1.png );
  167. $error = "[convert error $? ($output)]" if $?;
  168. } else {
  169. $output = qx($dvipngPath -T tight -bg Transparent srender.dvi);
  170. $error = "[dvipng error $? ($output)]" if $?;
  171. }
  172. if (not $error and IsFile('srender1.png') and not ZeroSize('srender1.png')) {
  173. my $png = ReadFileOrDie("srender1.png");
  174. WriteStringToFile ("$LatexDir/$hash.png", $png);
  175. } else {
  176. $error = "[Error retrieving image for $latex]";
  177. }
  178. }
  179. Unlink(glob('*'));
  180. ChangeDir($LatexDir);
  181. RemoveDir($dir);
  182. return $error if $error;
  183. }
  184. # Finally print the html for the image
  185. if ($type eq "inline math") { # inline math
  186. return ("<img class='InlineMath' "
  187. ."src='$LatexLinkDir/$hash.png' alt='$latex'\/>");
  188. } elsif ($type eq "display math") { # display math
  189. my $ret;
  190. if ($label) { $ret = "<a name='$label'>"; }
  191. $ret .= "<center><img class='DisplayMath' "
  192. ."src='$LatexLinkDir/$hash.png' alt='$latex'> <span class=\'eqCount\'>($eqCounter)</span><\/center>";
  193. return($ret);
  194. } else { # latex format
  195. return ("<img class='LaTeX' "
  196. ."src='$LatexLinkDir/$hash.png' alt='$latex' \/>");
  197. }
  198. }