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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1235 - (view) (download) (as text)

1 : gage 1022 ################################################################################
2 : sh002i 1131 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
3 :     # $Id$
4 : gage 1022 ################################################################################
5 :    
6 : sh002i 1131 package WeBWorK::PG::ImageGenerator;
7 : gage 1022
8 :     =head1 NAME
9 :    
10 : sh002i 1154 WeBWorK::PG::ImageGenerator - create an object for holding bits of math for
11 :     LaTeX, and then to process them all at once.
12 : gage 1022
13 :     =head1 SYNPOSIS
14 :    
15 : sh002i 1154 FIXME: add this
16 : gage 1022
17 :     =cut
18 :    
19 : sh002i 1131 use strict;
20 :     use warnings;
21 : sh002i 1154 use WeBWorK::Utils qw(readDirectory makeTempDirectory removeTempDirectory);
22 : gage 1022
23 : sh002i 1154 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 : sh002i 1131 =head1 METHODS
44 : gage 1022
45 : sh002i 1131 =over
46 : gage 1022
47 : sh002i 1131 =item new
48 :    
49 : sh002i 1163 Returns a new ImageGenerator object. <C%options> must contain the following
50 :     entries:
51 : gage 1022
52 : sh002i 1163 tempDir => directory in which to create temporary processing directory
53 : sh002i 1154 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 : sh002i 1131
59 : gage 1022 =cut
60 :    
61 :     sub new {
62 : sh002i 1154 my ($invocant, %options) = @_;
63 :     my $class = ref $invocant || $invocant;
64 : sh002i 1131 my $self = {
65 : sh002i 1154 strings => [],
66 :     %options,
67 : sh002i 1131 };
68 :    
69 :     bless $self, $class;
70 : gage 1022 }
71 :    
72 : sh002i 1154 =item add($string, $mode)
73 :    
74 : sh002i 1163 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 : sh002i 1154
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 : sh002i 1159 # 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 : sh002i 1169 if ($string =~ s/^\\\[(.*)\\\]$/$1/s) {
92 : sh002i 1159 $mode = "display";
93 : sh002i 1169 } elsif ($string =~ s/^\\\((.*)\\\)$/$1/s) {
94 : sh002i 1159 $mode = "inline";
95 :     }
96 :     # otherwise, leave the string and the mode alone.
97 :    
98 : sh002i 1169 # assume that a bare string with no mode specified is inline
99 :     $mode ||= "inline";
100 :    
101 : sh002i 1154 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 : sh002i 1163 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 : sh002i 1154
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 : sh002i 1235 my $refresh = $options{refresh} || ! defined $mtime;
138 : sh002i 1154 # 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 : gage 1022 sub initialize {
215 : sh002i 1154 my ($self, $envir) = @_;
216 : sh002i 1131
217 : sh002i 1154 my $problemnum = $envir->{'probNum'};
218 :     my $studname = $envir->{'studentLogin'};
219 :     my $psvn = $envir->{'psvn'};
220 :     my $setname = $envir->{'setNumber'};
221 : sh002i 1131 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 : gage 1022 }
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 : sh002i 1131
255 : gage 1022 push @{$self->{latexlines}}, $newstr;
256 :     return $tag;
257 :     }
258 :    
259 :     sub render {
260 :     my $self = shift;
261 :     my %opts = @_;
262 : sh002i 1131
263 :     # Don't run latex if there are no images
264 :     if($self->{count}==0) {
265 :     return;
266 :     }
267 :    
268 : gage 1022 my $refreshMe = 0;
269 : sh002i 1131 if (defined($opts{refresh}) and (($opts{refresh} eq "yes") or ($opts{refresh} == 1))) {
270 : gage 1022 $refreshMe = 1;
271 :     }
272 :    
273 : sh002i 1131 #$refreshMe = 1; # Uncomment for testing
274 :     my $latexfilenamebase = $self->{tmppath} . $self->{filenamestart};
275 : gage 1022
276 :     my $sourcePath = $self->{sourceFile};
277 : sh002i 1131 my $tempFile = "${latexfilenamebase}" . $self->{count} . ".png"; # last image
278 : gage 1022
279 : sh002i 1131 if ($refreshMe or not -e $tempFile or (stat $sourcePath)[9] > (stat $tempFile)[9]) {
280 : gage 1022 # image file doesn't exist, or source file is newer then image file
281 :     # or we just want new images produced
282 :    
283 : sh002i 1131 #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 : gage 1022
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 : sh002i 1131 #chdir($old_cdir);
340 : gage 1022 }
341 :     }
342 :    
343 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9