[system] / trunk / webwork-modperl / lib / WeBWorK / PG / ImageGenerator.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK/PG/ImageGenerator.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1235 - (download) (as text) (annotate)
Fri Jun 20 16:38:52 2003 UTC (9 years, 11 months ago) by sh002i
File size: 9942 byte(s)
fixed logic error for setting refresh flag

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9