/filter/tex/latex.php

https://github.com/itamart/moodle · PHP · 171 lines · 95 code · 25 blank · 51 comment · 13 complexity · 66e62ebb09cb599bf2f3ddbc2ed0172b MD5 · raw file

  1. <?php
  2. // latex.php
  3. // render TeX stuff using latex - this will not work on all platforms
  4. // or configurations. Only works on Linux and Mac with appropriate
  5. // software installed.
  6. // Much of this inspired/copied from Benjamin Zeiss' work
  7. class latex {
  8. var $temp_dir;
  9. var $error;
  10. /**
  11. * Constructor - create temporary directories and build paths to
  12. * external 'helper' binaries.
  13. * Other platforms could/should be added
  14. */
  15. function latex() {
  16. global $CFG;
  17. // construct directory structure
  18. $this->temp_dir = $CFG->tempdir . "/latex";
  19. make_temp_directory('latex');
  20. }
  21. /**
  22. * Accessor function for support_platform field.
  23. * @return boolean value of supported_platform
  24. */
  25. function supported() {
  26. return $this->supported_platform;
  27. }
  28. /**
  29. * Turn the bit of TeX into a valid latex document
  30. * @param string $forumula the TeX formula
  31. * @param int $fontsize the font size
  32. * @return string the latex document
  33. */
  34. function construct_latex_document( $formula, $fontsize=12 ) {
  35. global $CFG;
  36. $formula = filter_tex_sanitize_formula($formula);
  37. // $fontsize don't affects to formula's size. $density can change size
  38. $doc = "\\documentclass[{$fontsize}pt]{article}\n";
  39. $doc .= get_config('filter_tex', 'latexpreamble');
  40. $doc .= "\\pagestyle{empty}\n";
  41. $doc .= "\\begin{document}\n";
  42. //dlnsk $doc .= "$ {$formula} $\n";
  43. if (preg_match("/^[[:space:]]*\\\\begin\\{(gather|align|alignat|multline).?\\}/i",$formula)) {
  44. $doc .= "$formula\n";
  45. } else {
  46. $doc .= "$ {$formula} $\n";
  47. }
  48. $doc .= "\\end{document}\n";
  49. return $doc;
  50. }
  51. /**
  52. * execute an external command, with optional logging
  53. * @param string $command command to execute
  54. * @param file $log valid open file handle - log info will be written to this file
  55. * @return return code from execution of command
  56. */
  57. function execute( $command, $log=null ) {
  58. $output = array();
  59. exec( $command, $output, $return_code );
  60. if ($log) {
  61. fwrite( $log, "COMMAND: $command \n" );
  62. $outputs = implode( "\n", $output );
  63. fwrite( $log, "OUTPUT: $outputs \n" );
  64. fwrite( $log, "RETURN_CODE: $return_code\n " );
  65. }
  66. return $return_code;
  67. }
  68. /**
  69. * Render TeX string into gif/png
  70. * @param string $formula TeX formula
  71. * @param string $filename filename for output (including extension)
  72. * @param int $fontsize font size
  73. * @param int $density density value for .ps to .gif/.png conversion
  74. * @param string $background background color (e.g, #FFFFFF).
  75. * @param file $log valid open file handle for optional logging (debugging only)
  76. * @return bool true if successful
  77. */
  78. function render( $formula, $filename, $fontsize=12, $density=240, $background='', $log=null ) {
  79. global $CFG;
  80. // quick check - will this work?
  81. $pathlatex = get_config('filter_tex', 'pathlatex');
  82. if (empty($pathlatex)) {
  83. return false;
  84. }
  85. $doc = $this->construct_latex_document( $formula, $fontsize );
  86. // construct some file paths
  87. $convertformat = get_config('filter_tex', 'convertformat');
  88. if (!strpos($filename, ".{$convertformat}")) {
  89. $convertformat = 'png';
  90. }
  91. $filename = str_replace(".{$convertformat}", '', $filename);
  92. $tex = "{$this->temp_dir}/$filename.tex";
  93. $dvi = "{$this->temp_dir}/$filename.dvi";
  94. $ps = "{$this->temp_dir}/$filename.ps";
  95. $img = "{$this->temp_dir}/$filename.{$convertformat}";
  96. // turn the latex doc into a .tex file in the temp area
  97. $fh = fopen( $tex, 'w' );
  98. fputs( $fh, $doc );
  99. fclose( $fh );
  100. // run latex on document
  101. $command = "{$pathlatex} --interaction=nonstopmode --halt-on-error $tex";
  102. chdir( $this->temp_dir );
  103. if ($this->execute($command, $log)) { // It allways False on Windows
  104. // return false;
  105. }
  106. // run dvips (.dvi to .ps)
  107. $pathdvips = get_config('filter_tex', 'pathdvips');
  108. $command = "{$pathdvips} -E $dvi -o $ps";
  109. if ($this->execute($command, $log )) {
  110. return false;
  111. }
  112. // Run convert on document (.ps to .gif/.png) or run dvisvgm (.ps to .svg).
  113. if ($background) {
  114. $bg_opt = "-transparent \"$background\""; // Makes transparent background
  115. } else {
  116. $bg_opt = "";
  117. }
  118. if ($convertformat == 'svg') {
  119. $pathdvisvgm = get_config('filter_tex', 'pathdvisvgm');
  120. $command = "{$pathdvisvgm} -E $ps -o $img";
  121. } else {
  122. $pathconvert = get_config('filter_tex', 'pathconvert');
  123. $command = "{$pathconvert} -density $density -trim $bg_opt $ps $img";
  124. }
  125. if ($this->execute($command, $log )) {
  126. return false;
  127. }
  128. return $img;
  129. }
  130. /**
  131. * Delete files created in temporary area
  132. * Don't forget to copy the final gif/png before calling this
  133. * @param string $filename file base (no extension)
  134. */
  135. function clean_up( $filename ) {
  136. global $CFG;
  137. unlink( "{$this->temp_dir}/$filename.tex" );
  138. unlink( "{$this->temp_dir}/$filename.dvi" );
  139. unlink( "{$this->temp_dir}/$filename.ps" );
  140. $convertformat = get_config('filter_tex', 'convertformat');
  141. unlink( "{$this->temp_dir}/$filename.{$convertformat}" );
  142. unlink( "{$this->temp_dir}/$filename.aux" );
  143. unlink( "{$this->temp_dir}/$filename.log" );
  144. return;
  145. }
  146. }