--- loncom/interface/printout.pl 2006/10/30 11:40:49 1.111 +++ loncom/interface/printout.pl 2008/03/11 01:48:57 1.132 @@ -1,6 +1,7 @@ #!/usr/bin/perl # CGI-script to run LaTeX, dvips, ps2ps, ps2pdf etc. # +# $Id: printout.pl,v 1.132 2008/03/11 01:48:57 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -38,9 +39,11 @@ use Apache::loncommon(); use Apache::lonlocal; use Apache::lonmsg(); use LONCAPA::Enrollment; +use LONCAPA::Configuration; use strict; +my $busy_wait_timeout = 30; # Determine if a user is operating as a student for this course/domain. #Parameters: @@ -127,8 +130,8 @@ sub send_error_mail { foreach my $line (@$texfile) { $message .= "$line\n"; } - my (undef, %receivers) = &Apache::lonfeedback::decide_receiver(undef, 0, - 1,1,1); + my (undef, %receivers) = &Apache::lonmsg::decide_receiver(undef, 0, + 1,1,1); # print "
sending...section: $env{'request.course.sec'}"; foreach my $dest (keys %receivers) { # print "
dest is $dest"; @@ -160,9 +163,11 @@ Your cookie information is incorrect. END return; } + +my %perlvar=%{&LONCAPA::Configuration::read_conf('loncapa.conf')}; &Apache::lonlocal::get_language_handle(); &Apache::loncommon::content_type(undef,'text/html'); - +$env{'request.noversionuri'} = '/cgi-bin/printout.pl'; print(&Apache::loncommon::start_page('Creating PDF')); my $identifier = $ENV{'QUERY_STRING'}; @@ -183,13 +188,14 @@ END if ($student_names=~/_END_/) { @names_pack=split(/_ENDPERSON_/,$student_names); } - -print "Return to last resource.

"; - + if ($backref) { + print('

'.&mt("[_1]Return[_2] to editing resource.", + "","").'

'); + } my $figfile = $texfile; - $figfile =~ s/^([^\.]+printout)[^t]+\.tex/$1\.dat/; + $figfile =~ s/^(.*_printout)_\d+_\d+_\d+\.tex/$1\.dat/; my $duefile = $texfile; - $duefile =~ s/^([^\.]+printout)[^t]+\.tex/$1\.due/; + $duefile =~ s/^(.*_printout)_\d+_\d+_\d+\.tex/$1\.due/; #do we have figures? # print "Figure file: $figfile\n"; if (-e $figfile) { @@ -200,12 +206,12 @@ print "Return'); foreach my $not_eps (@content_of_file) { chomp($not_eps); if ($not_eps ne '') { - # print "Converting $not_eps"; # Debugging. + # print "Converting $not_eps"; # Debugging. my $status_statement='EPS picture for '.$not_eps; # print "$status_statement\n"; $not_eps=~s|\/\.\/|\/|g; @@ -213,13 +219,13 @@ print "ReturnReturnReturn/dev/null 2>/dev/null", "for $status_statement now LaTeXing file", - \%prog_state,$dvi_file); + \%prog_state,$dvi_file, $busy_wait_timeout); if ($tableofcontents eq 'yes') { &busy_wait_command("latex $name_file 1>/dev/null 2>/dev/null", "for $status_statement First LaTeX of file for table of contents", - \%prog_state,$dvi_file); + \%prog_state,$dvi_file, $busy_wait_timeout); &busy_wait_command("latex $name_file 1>/dev/null 2>/dev/null", "for $status_statement Second LaTeX of file for table of contents", - \%prog_state,$dvi_file); + \%prog_state,$dvi_file,$busy_wait_timeout); } #to create table of contents my $idxname=$name_file; $idxname=~s/\.tex$/\.idx/; @@ -387,7 +397,7 @@ foreach $texfile (@texfile) { \%prog_state,$idxname); &busy_wait_command("latex $name_file 1>/dev/null 2>/dev/null", "for $status_statement now LaTeXing file for index section", - \%prog_state,$dvi_file); + \%prog_state,$dvi_file, $busy_wait_timeout); } #to create index #Do we have a latex error in the log file? my $logfilename = $texfile; $logfilename =~ s/\.tex$/\.log/; @@ -436,10 +446,10 @@ foreach $texfile (@texfile) { print "\n"; # print "
Advanced role
"; print "The link to "; - $logfilename=~s/\/home\/httpd//; + $logfilename=~s{^\Q$perlvar{'lonPrtDir'}\E}{/prtspool}; print "
Your log file "; print "\n"; - #link tooriginal LaTeX file (included according Michael Hamlin desire) + #link to original LaTeX file my $tex_temporary_file=IO::File->new($texfile) || die "Couldn't open tex file $texfile for reading: $!\n"; my @tex_content_of_file = <$tex_temporary_file>; close $tex_temporary_file; @@ -449,7 +459,7 @@ foreach $texfile (@texfile) { print $tex_temporary_file 'LOGFILE
'.$body_tex_file.'
'."\n"; print "

"; print "The link to "; - $texfile=~s/\/home\/httpd//; + $texfile=~s{^\Q$perlvar{'lonPrtDir'}\E}{/prtspool}; print "Your original LaTeX file "; print "\n"; my $help_text = &Apache::loncommon::help_open_topic("Print_Resource", "Help on printing"); @@ -503,29 +513,64 @@ foreach $texfile (@texfile) { # &busy_wait_command("latex $latex_file 1>/dev/null 2>/dev/null", "for $status_statement first latex to repaginate", - \%prog_state, $name_file); + \%prog_state, $name_file,$busy_wait_timeout); if ($tableofcontents eq 'yes') { &busy_wait_command("latex $latex_file 1>/dev/null 2>/dev/null", "for $status_statement second latex to repaginate", - \%prog_state, $name_file); + \%prog_state, $name_file,$busy_wait_timeout); &busy_wait_command("latex $latex_file 1>/dev/null 2>/dev/null", "for $status_statement third latex to repaginate", - \%prog_state, $name_file); + \%prog_state, $name_file,$busy_wait_timeout); } if ($tableofindex eq 'yes') { my $idxname = $latex_file; $idxname =~ s/\.tex$/\.idx/; - &busy_wait_command("makindex $idxname", + &busy_wait_command("makeindex $idxname", "Re-creating index file", \%prog_state, $idxname); &busy_wait_command("latex $latex_file 1>/dev/null 2>/dev/null", "for $status_statement now Recreting index (latex)", - \%prog_state, $dvi_file); + \%prog_state, $dvi_file,$busy_wait_timeout); } - &busy_wait_command("$comma $name_file 1>dev/null 2>/dev/null", + &busy_wait_command("$comma $name_file 1>/dev/null 2>/dev/null", "for $status_statement dvips to repaginate", \%prog_state, $new_name_file); + # + # One last little hinky kinky thing. + # It's just possible that some fonts could not be maded + # at the resolution of the pdf print driver. + # In that case a file called missfont.log will have been + # created that will contain the commands that were attempted + # to create the missing fonts. If we basically + # take all the 8000 strings in that file, and + # replace them with 600 (the ljfour resolution) + # run the commands in that file and redvips, + # we'll be able to print the missing glyphs at 600dpi. + # + # Supposedly it is possible to tune TeX/Metafont to do this + # right but I failed to get that to work when following the + # docs at the tug site, hence this rather kludgey fix. + # + # We make the (I think) reasonable assumption that + # missing glyphs won't change the pagination and I think + # this is true because TeX/dvips will leave a space + # instead of these glyphs based on the font metrics + # (fancy way to say there will be a blank the size of the missing + # glyphs). + # + my $print_directory = dirname($name_file); + my $missfonts_file = $print_directory."/missfont.log"; + #print("
Missing fonts file is: $missfonts_file"); + if (-e $missfonts_file) { + #print("
Missing fonts file exists\n"); + &create_missing_fonts($missfonts_file,\%prog_state); + &busy_wait_command("$comma $name_file 1>/dev/null 2>/dev/null", + "for $status_statement dvips generated missing fonts", + \%prog_state, $new_name_file); + } + + # print "\n

PDF output file (see link below)

\n"; $new_name_file =~ m/^(.*)\./; my $ps_file = my $tempo_file = $1.'temporar.ps'; @@ -564,16 +609,18 @@ foreach $texfile (@texfile) { $texps =~ s/\.tex/\.ps/; my @garb = ($texaux,$texdvi,$texps); # unlink @garb; - unlink $duefile; + unlink($duefile); print "Your PDF document"; + unlink($missfonts_file); + } if ($advanced_role) { print "

"; print "The link to "; - $logfilename=~s/\/home\/httpd//; + $logfilename=~s{^\Q$perlvar{'lonPrtDir'}\E}{/prtspool}; print "Your log file "; print "\n"; - #link tooriginal LaTeX file (included according Michael Hamlin desire) + #link to original LaTeX file my $tex_temporary_file=IO::File->new($texfile) || die "Couldn't open tex file $texfile for reading: $!\n"; my @tex_content_of_file = <$tex_temporary_file>; close $tex_temporary_file; @@ -583,10 +630,11 @@ foreach $texfile (@texfile) { print $tex_temporary_file 'LOGFILE
'.$body_tex_file.'
'."\n"; print "

"; print "The link to "; - $texfile=~s/\/home\/httpd//; + $texfile=~s{^\Q$perlvar{'lonPrtDir'}\E}{/prtspool}; print "Your original LaTeX file "; print "\n"; } + } else { #LaTeX successfully parsed tex file $name_file =~ s/\.tex/\.dvi/; @@ -599,20 +647,53 @@ foreach $texfile (@texfile) { &busy_wait_command("$comma $name_file 1>/dev/null 2>/dev/null", "for $status_statement now Converting to PS", \%prog_state,$new_name_file); + # + # One last little hinky kinky thing. + # It's just possible that some fonts could not be maded + # at the resolution of the pdf print driver. + # In that case a file called missfont.log will have been + # created that will contain the commands that were attempted + # to create the missing fonts. If we basically + # take all the 8000 strings in that file, and + # replace them with 600 (the ljfour resolution) + # run the commands in that file and redvips, + # we'll be able to print the missing glyphs at 600dpi. + # + # Supposedly it is possible to tune TeX/Metafont to do this + # right but I failed to get that to work when following the + # docs at the tug site, hence this rather kludgey fix. + # + # We make the (I think) reasonable assumption that + # missing glyphs won't change the pagination and I think + # this is true because TeX/dvips will leave a space + # instead of these glyphs based on the font metrics + # (fancy way to say there will be a blank the size of the missing + # glyphs). + # + my $print_directory = dirname($name_file); + my $missfonts_file = $print_directory."/missfont.log"; + #print("
Missing fonts file is: $missfonts_file"); + if (-e $missfonts_file) { + #print("
Missing fonts file exists\n"); + &create_missing_fonts($missfonts_file,\%prog_state); + &busy_wait_command("$comma $name_file 1>/dev/null 2>/dev/null", + "for $status_statement dvips generated missing fonts", + \%prog_state, $new_name_file); + } if (-e $new_name_file) { my $latex_file = $name_file; $latex_file =~ s/\.dvi/\.tex/; &repaginate($new_name_file, $latex_file, $numberofcolumns); &busy_wait_command("latex $latex_file 1>/dev/null 2>/dev/null", "for $status_statement first latex to repaginate", - \%prog_state, $name_file); + \%prog_state, $name_file, $busy_wait_timeout); if ($tableofcontents eq 'yes') { &busy_wait_command("latex $latex_file 1>/dev/null 2>/dev/null", "for $status_statement second latex to repaginate", - \%prog_state, $name_file); + \%prog_state, $name_file, $busy_wait_timeout); &busy_wait_command("latex $latex_file 1>/dev/null 2>/dev/null", "for $status_statement third latex to repaginate", - \%prog_state, $name_file); + \%prog_state, $name_file, $busy_wait_timeout); } if ($tableofindex eq 'yes') { my $idxname = $latex_file; @@ -622,11 +703,12 @@ foreach $texfile (@texfile) { \%prog_state, $idxname); &busy_wait_command("latex $latex_file 1>/dev/null 2>/dev/null", "for $status_statement now Recreting index (latex)", - \%prog_state, $dvi_file); + \%prog_state, $dvi_file, $busy_wait_timeout); } &busy_wait_command("$comma $name_file 1>/dev/null 2>/dev/null", "for $status_statement dvips to repaginate", \%prog_state, $new_name_file); + print "
"; $new_name_file =~ m/^(.*)\./; my $ps_file = my $tempo_file = $1.'temporar.ps'; @@ -680,10 +762,12 @@ foreach $texfile (@texfile) { $texps =~ s/\.tex/\.ps/; my @garb = ($texlog,$texaux,$texdvi,$texps); # unlink @garb; - unlink $duefile; + unlink($duefile); print "$link_text - click here to download pdf"; print "\n"; } + unlink($missfonts_file); + } } else { print "LaTeX file $texfile was not created successfully"; @@ -701,7 +785,7 @@ if ($number_of_files>1) { print("
Zip Output:\n");
     system($statement);
     print("
"); - $zipfile=~s/\/home\/httpd//; + $zipfile=~s{^\Q$perlvar{'lonPrtDir'}\E}{/prtspool}; print "
A ZIP file of all the PDFs."; } if ($advanced_role) { &Apache::lonhtmlcommon::Close_PrgWin('',\%prog_state); } @@ -711,9 +795,17 @@ my $done; sub REAPER { $done=1; } - +# +# Execute a command updating the status window as the command's +# output file builds up (at intervals of a second). +# +# If the timeout argument defined, then if that many seconds +# elapses without an increase in the size of the output file, +# the command will be killed (this deals with the case when +# latex crawls into an infinite loop). +# sub busy_wait_command { - my ($command,$message,$progress_win,$output_file)=@_; + my ($command,$message,$progress_win,$output_file, $timeout)=@_; $SIG{CHLD} = \&REAPER; $done=0; @@ -721,12 +813,30 @@ sub busy_wait_command { if ($advanced_role) { &Apache::lonhtmlcommon::Update_PrgWin('',$progress_win,$message); } + my $last_size = 0; + my $unchanged_time = 0; while(!$done) { sleep 1; my $extra_msg; if ($output_file) { my $size=(stat($output_file))[7]; $extra_msg=", $size bytes generated"; + if ($size == $last_size) { + $unchanged_time++; + if ($timeout && ($unchanged_time > $timeout)) { + print "

Operation timed out!

\n"; + print "

Executing $command, the output file $output_file did not grow\n"; + print "after $timeout seconds. This may indicate $command\n"; + print "is in an infinite loop.\n"; + print "See if printing fewer copies helps. Please contact LON-CAPA\n"; + print "support about this in any event."; + print "

"; + kill(9, $pid); # Reaper will do the rest...I hope there's errors in the log. + } + } else { + $last_size = $size; + $unchanged_time = 0; + } } if ($advanced_role) { &Apache::lonhtmlcommon::Update_PrgWin('',$progress_win, @@ -786,8 +896,8 @@ sub repaginate { # bottom of the page, m the page number within the document. # - if ($line =~ /^%%Page:/) { - my @pageinfo = split(/ /, $line); + if ($line =~ /^%%Page:\s+\d+\s+\d+/) { + my @pageinfo = split(/\s+/, $line); if ($page_number < $pageinfo[1]) { $page_number = $pageinfo[1]; } elsif ($pageinfo[2] ne 1) { @@ -869,3 +979,57 @@ sub repaginate { } +# +# Create missing fonts given a latex missfonts.log file. +# This file will have lines like: +# +# mktexpk --mfmode ljfour --bdpi 8000 --mag 1+0/8000 --dpi 8000 tcrm0500 +# +# We want to execute those lines with the 8000's changed to 600's +# in order to match the resolution of the ljfour printer. +# Of course if some wiseguy has changed the default printer from ljfour +# in the dvips's config.ps file that will break so we'll also +# ensure that --mfmode is ljfour. +# +sub create_missing_fonts { + my ($fontfile, $state) = @_; + + # Open and read in the font file..we'll read it into the array + # font_commands. + # + open(my $font_handle, $fontfile); + my @font_commands = <$font_handle>; + + # make the list contain each command only once + my %uniq; + @font_commands = map { $uniq{$_}++ == 0 ? $_ : () } @font_commands; + + # Now process each command replacing the appropriate 8000's with + # 600's ensuring that font names with 8000's in them are not corrupted. + # and if the --mfmode is not ljfour we turn it into ljfour. + # Then we execute the command. + # + + foreach my $command (@font_commands) { + #print("
Raw command: $command"); + $command =~ s/ 8000/ 600/g; # dpi directives. + $command =~ s/\/8000/\/600/g; # mag directives. + #print("
After dpi replacements: $command"); + + my @cmdarray = split(/ /,$command); + for (my $i =0; $i < scalar(@cmdarray); $i++) { + if ($cmdarray[$i] eq '--mfmode') { + $cmdarray[$i+1] = "ljfour"; + } + } + #print("
before reassembly : (@cmdarray)"); + $command = join(" ", (@cmdarray)); + + #print("
Creating fonts via command: $command"); + &busy_wait_command("$command 1>/dev/null 2>/dev/null", + "Creating missing font", + $state); + + } + +}