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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1514 Revision 1515
16 16
17=cut 17=cut
18 18
19use strict; 19use strict;
20use warnings; 20use warnings;
21use WeBWorK::PG::EquationCache;
21use WeBWorK::Utils qw(readDirectory makeTempDirectory removeTempDirectory); 22use WeBWorK::Utils qw(readDirectory makeTempDirectory removeTempDirectory);
22 23
23use constant PREAMBLE => <<'EOF'; 24use constant PREAMBLE => <<'EOF';
24\documentclass[12pt]{article} 25\documentclass[12pt]{article}
25\nonstopmode 26\nonstopmode
44 45
45=over 46=over
46 47
47=item new 48=item new
48 49
49Returns a new ImageGenerator object. <C%options> must contain the following 50Returns a new ImageGenerator object. C<%options> must contain the following
50entries: 51entries:
51 52
52 tempDir => directory in which to create temporary processing directory 53 tempDir => directory in which to create temporary processing directory
53 dir => directory for resulting files 54 dir => directory for resulting files
54 url => url to directory for resulting files 55 url => url to directory for resulting files
55 basename => base name for image files 56 basename => base name for image files
56 latex => path to latex binary 57 latex => path to latex binary
57 dvipng => path to dvipng binary 58 dvipng => path to dvipng binary
58 59
60C<%options> may also contain the following entries:
61
62 useCache => boolean, whether to use global image cache
63 cacheDir => directory for resulting files
64 cacheURL => url to imageCacheDir
65 cacheDB => path to cache database file
66
67If C<useCache> is true, then C<basename> is ignored, and C<cacheDir>
68and C<cacheURL> override C<dir> and C<url>, respectively.
69
59=cut 70=cut
60 71
61sub new { 72sub new {
62 my ($invocant, %options) = @_; 73 my ($invocant, %options) = @_;
63 my $class = ref $invocant || $invocant; 74 my $class = ref $invocant || $invocant;
64 my $self = { 75 my $self = {
76 names => [],
65 strings => [], 77 strings => [],
66 %options, 78 %options,
67 }; 79 };
68 80
81 if ($self->{useCache}) {
82 $self->{dir} = $self->{cacheDir};
83 $self->{url} = $self->{cacheURL};
84 $self->{basename} = "";
85 $self->{equationCache} = WeBWorK::PG::EquationCache->new(cacheDB => $self->{cacheDB});
86 }
87
69 bless $self, $class; 88 bless $self, $class;
70} 89}
71 90
72=item add($string, $mode) 91=item add($string, $mode)
73 92
78=cut 97=cut
79 98
80sub add { 99sub add {
81 my ($self, $string, $mode) = @_; 100 my ($self, $string, $mode) = @_;
82 101
102 my $names = $self->{names};
83 my $strings = $self->{strings}; 103 my $strings = $self->{strings};
84 my $dir = $self->{dir}; 104 my $dir = $self->{dir};
85 my $url = $self->{url}; 105 my $url = $self->{url};
86 my $basename = $self->{basename}; 106 my $basename = $self->{basename};
107 my $useCache = $self->{useCache};
87 108
88 # if the string came in with delimiters, chop them off and set the mode 109 # 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 110 # based on whether they were \[ .. \] or \( ... \). this means that if
90 # the string has delimiters, the mode *argument* is ignored. 111 # the string has delimiters, the mode *argument* is ignored.
91 if ($string =~ s/^\\\[(.*)\\\]$/$1/s) { 112 if ($string =~ s/^\\\[(.*)\\\]$/$1/s) {
96 # otherwise, leave the string and the mode alone. 117 # otherwise, leave the string and the mode alone.
97 118
98 # assume that a bare string with no mode specified is inline 119 # assume that a bare string with no mode specified is inline
99 $mode ||= "inline"; 120 $mode ||= "inline";
100 121
101 my $imageNum = @$strings + 1; 122 # now that we know what mode we're dealing with, we can generate a "real"
123 # string to pass to latex
124 my $realString = ($mode eq "display")
125 ? '\(\displaystyle{' . $string . '}\)'
126 : '\(' . $string . '\)';
127
128 # determine what the image's "number" is
129 my $imageNum = ($useCache)
130 ? $self->{equationCache}->lookup($realString)
131 : @$strings + 1;
132
133 # get the full file name of the image
134 my $imageName = ($basename)
135 ? "$basename.$imageNum.png"
136 : "$imageNum.png";
137
138 # store the full file name of the image, and the "real" tex string to the object
139 push @$names, $imageName;
140 push @$strings, $realString;
141 #warn "ImageGenerator: added string $realString with name $imageName\n";
142
143 # ... and the full URL.
102 my $imageURL = "$url/$basename.$imageNum.png"; 144 my $imageURL = "$url/$imageName";
145
146 my $imageTag = ($mode eq "display")
147 ? " <div align=\"center\"><img src=\"$imageURL\" align=\"middle\" alt=\"$string\"></div> "
103 my $imageTag = "<img src=\"$imageURL\" align=\"middle\" alt=\"$string\">"; 148 : " <img src=\"$imageURL\" align=\"middle\" alt=\"$string\"> ";
104 149
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 "; 150 return $imageTag;
111 }
112} 151}
113 152
114=item render(%options) 153=item render(%options)
115 154
116Uses LaTeX and dvipng to render the equations stored in the object. If the key 155Uses LaTeX and dvipng to render the equations stored in the object. If the key
129 my $tempDir = $self->{tempDir}; 168 my $tempDir = $self->{tempDir};
130 my $dir = $self->{dir}; 169 my $dir = $self->{dir};
131 my $basename = $self->{basename}; 170 my $basename = $self->{basename};
132 my $latex = $self->{latex}; 171 my $latex = $self->{latex};
133 my $dvipng = $self->{dvipng}; 172 my $dvipng = $self->{dvipng};
173 my $names = $self->{names};
134 my $strings = $self->{strings}; 174 my $strings = $self->{strings};
135 175
136 my $mtime = $options{mtime}; 176 my $mtime = $options{mtime};
137 my $refresh = $options{refresh} || ! defined $mtime; 177 my $refresh = $options{refresh} || ! defined $mtime;
138 # must refresh if no mtime is given 178 # must refresh if no mtime is given
139 179
140 return unless @$strings; # Don't run latex if there are no images to generate
141
142 unless ($refresh) { 180 #unless ($refresh) {
143 my $firstImage = "$dir/$basename.1.png"; 181 # #my $firstImage = "$dir/$basename.1.png";
182 # my $firstImage = "$dir/" . $names->[0];
144 if (-e $firstImage) { 183 # if (-e $firstImage) {
145 # return if first image newer than $mtime 184 # # return if first image newer than $mtime
146 return if (stat $firstImage)[9] >= $mtime; 185 # return if (stat $firstImage)[9] >= $mtime;
186 # }
187 #}
188
189 # determine which images need to be generated
190 my (@newStrings, @newNames);
191 for (my $i = 0; $i < @$strings; $i++) {
192 my $string = $strings->[$i];
193 my $name = $names->[$i];
194 if (-e "$dir/$name") {
195 #warn "ImageGenerator: found a file named $name, skipping string $string\n";
196 } else {
197 #warn "ImageGenerator: didn't find a file named $name, including string $string\n";
198 push @newStrings, $string;
199 push @newNames, $name;
147 } 200 }
148 } 201 }
202
203 return unless @newStrings; # Don't run latex if there are no images to generate
149 204
150 # create temporary directory in which to do TeX processing 205 # create temporary directory in which to do TeX processing
151 my $wd = makeTempDirectory($tempDir, "ImageGenerator"); 206 my $wd = makeTempDirectory($tempDir, "ImageGenerator");
152 207
153 # store equations in a tex file 208 # store equations in a tex file
154 my $texFile = "$wd/equation.tex"; 209 my $texFile = "$wd/equation.tex";
155 open my $tex, ">", $texFile 210 open my $tex, ">", $texFile
156 or die "failed to open file $texFile for writing: $!"; 211 or die "failed to open file $texFile for writing: $!";
157 print $tex PREAMBLE; 212 print $tex PREAMBLE;
158 print $tex "$_\n" foreach @$strings; 213 print $tex "$_\n" foreach @newStrings;
159 print $tex POSTAMBLE; 214 print $tex POSTAMBLE;
160 close $tex; 215 close $tex;
161 216
162 # call LaTeX 217 # call LaTeX
163 my $latexCommand = "cd $wd && $latex equation > latex.out 2> latex.err"; 218 my $latexCommand = "cd $wd && $latex equation > latex.out 2> latex.err";
179 next unless $image =~ m/^equation(\d+)\.png$/; 234 next unless $image =~ m/^equation(\d+)\.png$/;
180 235
181 # get image number from above match 236 # get image number from above match
182 my $imageNum = $1; 237 my $imageNum = $1;
183 238
239 #warn "ImageGenerator: found generated image $imageNum with name $newNames[$imageNum-1]\n";
240
184 # move/rename image 241 # move/rename image
185 my $mvCommand = "cd $wd && /bin/mv $wd/$image $dir/$basename.$imageNum.png"; 242 #my $mvCommand = "cd $wd && /bin/mv $wd/$image $dir/$basename.$imageNum.png";
243 my $mvCommand = "cd $wd && /bin/mv $wd/$image $dir/" . $newNames[$imageNum-1];
186 my $mvStatus = system $mvCommand; 244 my $mvStatus = system $mvCommand;
187 warn "$mvCommand returned non-zero status $mvStatus: $!" 245 warn "$mvCommand returned non-zero status $mvStatus: $!"
188 if $mvStatus; 246 if $mvStatus;
189 } 247 }
190 248
191 # remove temporary directory (and its contents) 249 # remove temporary directory (and its contents)
192 removeTempDirectory($wd); 250 removeTempDirectory($wd);
193} 251}
194 252
1951; 2531;
196
197__END__
198
199################################################################################
200# OLD VERSIONS OF SUBROUTINES
201################################################################################
202
203
204sub getCount {
205 my $self = shift;
206 return $self->{count};
207}
208
209sub tmpurl {
210 my $self = shift;
211 return "$self->{tmpURLstart}/$self->{filenamestart}";
212}
213
214sub 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
234sub 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
259sub 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}
304EOT
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
3431;

Legend:
Removed from v.1514  
changed lines
  Added in v.1515

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9