[system] / trunk / webwork2 / lib / WeBWorK / PG / ImageGenerator.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/PG/ImageGenerator.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1159 - (download) (as text) (annotate)
Fri Jun 13 17:26:40 2003 UTC (9 years, 11 months ago) by sh002i
File size: 9796 byte(s)
removed unneeded code from WeBWorK::PG, WeBWorK::PG::ImageGenerator::add
now supports both add($in_delim) and add($in, $mode).
-sam

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK::PG::ImageGenerator;
    7 
    8 =head1 NAME
    9 
   10 WeBWorK::PG::ImageGenerator - create an object for holding bits of math for
   11 LaTeX, and then to process them all at once.
   12 
   13 =head1 SYNPOSIS
   14 
   15 FIXME: add this
   16 
   17 =cut
   18 
   19 use strict;
   20 use warnings;
   21 use WeBWorK::Utils qw(readDirectory makeTempDirectory removeTempDirectory);
   22 
   23 use constant PREAMBLE => <<'EOF';
   24 \documentclass[12pt]{article}
   25 \nonstopmode
   26 \usepackage{amsmath,amsfonts,amssymb}
   27 \def\gt{>}
   28 \def\lt{<}
   29 \usepackage[active,textmath,displaymath]{preview}
   30 \begin{document}
   31 EOF
   32 use constant POSTAMBLE => <<'EOF';
   33 \end{document}
   34 EOF
   35 use constant DVIPNG_ARGS => join " ", qw(
   36 -x4000.5
   37 -bgTransparent
   38 -Q6
   39 -mode toshiba
   40 -D180
   41 );
   42 
   43 =head1 METHODS
   44 
   45 =over
   46 
   47 =item new
   48 
   49 Returns a new ImageGenerator object. <C%options> must contain the following entries:
   50 
   51  tempDir  => directory for storing temporary files (non-web accessible)
   52  dir    => directory for resulting files
   53  url    => url to directory for resulting files
   54  basename => base name for image files
   55  latex    => path to latex binary
   56  dvipng   => path to dvipng binary
   57 
   58 =cut
   59 
   60 sub new {
   61   my ($invocant, %options) = @_;
   62   my $class = ref $invocant || $invocant;
   63   my $self = {
   64     strings => [],
   65     %options,
   66   };
   67 
   68   bless $self, $class;
   69 }
   70 
   71 =item add($string, $mode)
   72 
   73 Adds the equation in C<$string> to the object. C<$mode> can be "display" or "inline". If
   74 not specified, "inline" is assumed. Returns the proper HTML tag for displaying the image.
   75 
   76 =cut
   77 
   78 sub add {
   79   my ($self, $string, $mode) = @_;
   80 
   81   my $strings  = $self->{strings};
   82   my $dir      = $self->{dir};
   83   my $url      = $self->{url};
   84   my $basename = $self->{basename};
   85 
   86   # if the string came in with delimiters, chop them off and set the mode
   87   # based on whether they were \[ .. \] or \( ... \). this means that if
   88   # the string has delimiters, the mode *argument* is ignored.
   89   if ($string =~ s/^\\\[(.*)\\\]$/$1/) {
   90     $mode = "display";
   91   } elsif ($string =~ s/^\\\((.*)\\\)$/$1/) {
   92     $mode = "inline";
   93   }
   94   # otherwise, leave the string and the mode alone.
   95 
   96   my $imageNum  = @$strings + 1;
   97   my $imageURL  = "$url/$basename.$imageNum.png";
   98   my $imageTag  = "<img src=\"$imageURL\" align=\"middle\" alt=\"$string\">";
   99 
  100   if ($mode eq "display") {
  101     push @$strings, '\(\displaystyle{' . $string . '}\)';
  102     return " <div align=\"center\">$imageTag</div> ";
  103   } else {
  104     push @$strings, '\(' . $string . '\)';
  105     return " $imageTag ";
  106   }
  107 }
  108 
  109 =item render(%options)
  110 
  111 Uses LaTeX and dvipng to render the equations stored in the object. If the key "mtime" in
  112 C<%options> is given, it will be interpreted as a unix date and compared with the
  113 modification date on any existing copy of the first image to be generated. It is
  114 recommended that the modification time of the source file from which the equations
  115 originate be used for this value. If the key "refresh" in C<%options> is true, images
  116 will be regenerated regardless of when they were last modified.
  117 
  118 =cut
  119 
  120 sub render {
  121   my ($self, %options) = @_;
  122 
  123   my $tempDir  = $self->{tempDir};
  124   my $dir      = $self->{dir};
  125   my $basename = $self->{basename};
  126   my $latex    = $self->{latex};
  127   my $dvipng   = $self->{dvipng};
  128   my $strings  = $self->{strings};
  129 
  130   my $mtime   = $options{mtime};
  131   my $refresh = not defined $mtime || $options{refresh};
  132     # must refresh if no mtime is given
  133 
  134   return unless @$strings; # Don't run latex if there are no images to generate
  135 
  136   unless ($refresh) {
  137     my $firstImage = "$dir/$basename.1.png";
  138     if (-e $firstImage) {
  139       # return if first image newer than $mtime
  140       return if (stat $firstImage)[9] >= $mtime;
  141     }
  142   }
  143 
  144   # create temporary directory in which to do TeX processing
  145   my $wd = makeTempDirectory($tempDir, "ImageGenerator");
  146 
  147   # store equations in a tex file
  148   my $texFile = "$wd/equation.tex";
  149   open my $tex, ">", $texFile
  150     or die "failed to open file $texFile for writing: $!";
  151   print $tex PREAMBLE;
  152   print $tex "$_\n" foreach @$strings;
  153   print $tex POSTAMBLE;
  154   close $tex;
  155 
  156   # call LaTeX
  157   my $latexCommand  = "cd $wd && $latex equation > latex.out 2> latex.err";
  158   my $latexStatus = system $latexCommand;
  159   warn "$latexCommand returned non-zero status $latexStatus: $!"
  160     if $latexStatus;
  161   warn "$latexCommand failed to generate a DVI file"
  162     unless -e "$wd/equation.dvi";
  163 
  164   # call dvipng
  165   my $dvipngCommand = "cd $wd && $dvipng " . DVIPNG_ARGS . " equation > dvipng.out 2> dvipng.err";
  166   my $dvipngStatus = system $dvipngCommand;
  167   #warn "$dvipngCommand returned non-zero status $dvipngStatus: $!"
  168   # if $dvipngStatus;
  169 
  170   # move/rename images
  171   foreach my $image (readDirectory($wd)) {
  172     # only work on equation#.png files
  173     next unless $image =~ m/^equation(\d+)\.png$/;
  174 
  175     # get image number from above match
  176     my $imageNum = $1;
  177 
  178     # move/rename image
  179     my $mvCommand = "cd $wd && /bin/mv $wd/$image $dir/$basename.$imageNum.png";
  180     my $mvStatus = system $mvCommand;
  181     warn "$mvCommand returned non-zero status $mvStatus: $!"
  182       if $mvStatus;
  183   }
  184 
  185   # remove temporary directory (and its contents)
  186   removeTempDirectory($wd);
  187 }
  188 
  189 1;
  190 
  191 __END__
  192 
  193 ################################################################################
  194 # OLD VERSIONS OF SUBROUTINES
  195 ################################################################################
  196 
  197 
  198 sub getCount {
  199   my $self = shift;
  200   return $self->{count};
  201 }
  202 
  203 sub tmpurl {
  204   my $self = shift;
  205   return "$self->{tmpURLstart}/$self->{filenamestart}";
  206 }
  207 
  208 sub initialize {
  209   my ($self, $envir) = @_;
  210 
  211   my $problemnum  = $envir->{'probNum'};
  212   my $studname    = $envir->{'studentLogin'};
  213   my $psvn        = $envir->{'psvn'};
  214   my $setname     = $envir->{'setNumber'};
  215   my $tmpURLstart = $envir->{'tempURL'};
  216 
  217   my $path=main::surePathToTmpFile(main::convertPath("png/$setname/$psvn/foo"));
  218   $path =~ s/foo$//; # remove final foo
  219 
  220   $self->{sourceFile} = $envir->{templateDirectory} . "/" . $envir->{fileName};
  221   $self->{tmpURLstart} = $tmpURLstart."png/$setname/$psvn";
  222   $self->{tmppath}=$path;
  223   $self->{filenamestart}="$studname-prob${problemnum}image";
  224 }
  225 
  226 # Add another string to list to be LaTeX'ed
  227 # return the tag
  228 sub add {
  229   my $self = shift;
  230   my $newstr = shift;
  231   my $tag = $newstr;
  232   $self->{count}++;
  233   my $tempURL= $self->tmpurl()."$self->{count}.png";
  234 
  235   if ($tag =~ /^\\\(/) {
  236     $tag =~ s|^\\\( *||;
  237     $tag =~ s|\\\)$||;
  238     $tag = qq!<img src="$tempURL" align="middle" alt="$tag">!;
  239   } else {
  240     # Displayed math comes in with \[ stuff \].  To get a good
  241     # bounding box through preview, we change that to \( \displaystyle{
  242     # stuff } \), and then center the resulting image
  243     $tag =~ s|^\\\[ *||;
  244     $tag =~ s|\\\]$||;
  245     $newstr = '\(\displaystyle{'.$tag.'}\)';
  246     $tag = qq!<div align="center"><img src="$tempURL" align="middle" alt="$tag"></div>!;
  247   }
  248 
  249   push @{$self->{latexlines}}, $newstr;
  250   return $tag;
  251 }
  252 
  253 sub render {
  254   my $self = shift;
  255   my %opts = @_;
  256 
  257   # Don't run latex if there are no images
  258   if($self->{count}==0) {
  259     return;
  260   }
  261 
  262   my $refreshMe = 0;
  263   if (defined($opts{refresh}) and (($opts{refresh} eq "yes") or ($opts{refresh} == 1))) {
  264     $refreshMe = 1;
  265   }
  266 
  267   #$refreshMe = 1;  # Uncomment for testing
  268   my $latexfilenamebase = $self->{tmppath} . $self->{filenamestart};
  269 
  270   my $sourcePath = $self->{sourceFile};
  271   my $tempFile = "${latexfilenamebase}" . $self->{count} . ".png"; # last image
  272 
  273   if ($refreshMe or not -e $tempFile or (stat $sourcePath)[9] > (stat $tempFile)[9]) {
  274     # image file doesn't exist, or source file is newer then image file
  275     # or we just want new images produced
  276 
  277     #my $old_cdir = `pwd`; # cd for running latex
  278     #chomp($old_cdir);
  279     chdir($self->{tmppath})
  280       || warn "Could not move into temporary directory $self->{tmppath}";
  281 
  282     if (-e "$latexfilenamebase.tex") {
  283       unlink("$latexfilenamebase.tex") ||
  284         warn "Could not delete old LaTeX file";
  285     }
  286 
  287     local *LATEXME;
  288     open(LATEXME,">$latexfilenamebase.tex") || warn "Cannot create temporary tex file";
  289     print LATEXME <<'EOT';
  290 \documentclass[12pt]{article}
  291 \nonstopmode
  292 \usepackage{amsmath,amsfonts,amssymb}
  293 \def\gt{>}
  294 \def\lt{<}
  295 
  296 \usepackage[active,textmath,displaymath]{preview}
  297 \begin{document}
  298 EOT
  299 
  300     my $j;
  301     for $j (@{$self->{latexlines}}) {
  302       print LATEXME "\n$j\n";
  303     }
  304 
  305     print LATEXME '\end{document}'."\n";
  306     close(LATEXME);
  307 
  308     chmod(0666, "$latexfilenamebase.tex") || warn "Could not change permissions on $latexfilenamebase.tex";
  309 
  310     my $error_log = '/dev/null';    ## by default do not log error messages
  311     $error_log = &Global::getErrorLog if $Global::imageDebugMode;
  312 
  313     $ENV{PATH} .= "$Global::extendedPath";
  314     my $dvipng_res = int($Global::dvipngScaling * 1000+0.5);
  315     my $cmdout="";
  316 
  317     # remove any old files using this name
  318     unlink("$self->{filenamestart}.dvi","$self->{filenamestart}.log",
  319            "$self->{filenamestart}.aux","missfont.log");
  320 
  321     $cmdout=system("$Global::externalLatexPath $self->{filenamestart}.tex >>$error_log 2>>$error_log");
  322     warn "$Global::externalLatexPath $self->{filenamestart}.tex >>$error_log 2>>$error_log -- FAILED in ImageGenerator returned $cmdout" if $cmdout;
  323 
  324     $cmdout=system("$Global::externalDvipngPath -x$dvipng_res -bgTransparent -Q$Global::dvipngShrinkFactor -mode $Global::dvipngMode -D$Global::dvipngDPI $self->{filenamestart}.dvi >>$error_log 2>>$error_log");
  325     warn "$Global::externalDvipngPath -x$dvipng_res -bgTransparent -Q$Global::dvipngShrinkFactor -mode $Global::dvipngMode -D$Global::dvipngDPI $self->{filenamestart}.dvi >>$error_log 2>>$error_log -- FAILED in ImageGenerator.pm returned $cmdout" if $cmdout !=256;
  326 
  327     unless ($Global::imageDebugMode) {
  328       unlink("$self->{filenamestart}.dvi","$self->{filenamestart}.log",
  329            "$self->{filenamestart}.tex",
  330            "$self->{filenamestart}.aux"
  331       );
  332     }
  333     #chdir($old_cdir);
  334   }
  335 }
  336 
  337 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9