--- loncom/xml/lonplot.pm 2002/01/10 19:45:51 1.38 +++ loncom/xml/lonplot.pm 2012/07/09 11:11:47 1.159 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Dynamic plot # -# $Id: lonplot.pm,v 1.38 2002/01/10 19:45:51 matthew Exp $ +# $Id: lonplot.pm,v 1.159 2012/07/09 11:11:47 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,29 +25,43 @@ # # http://www.lon-capa.org/ # -# 12/15/01 Matthew -# 12/17 12/18 12/19 12/20 12/21 12/27 12/28 12/30 12/31 Matthew -# 01/01/02 Matthew -# 01/02 01/03 01/04 01/07 01/08 01/09 Matthew - -# Current issues -# 1. Gnuplot is unable to vary the color or linestyle of plots. -# The key does not know this so it is misleading for the user. -# Multiple s can be plotted with varying line styles and -# colors. -# + + + + package Apache::lonplot; use strict; +use warnings FATAL=>'all'; +no warnings 'uninitialized'; use Apache::File; use Apache::response; use Apache::lonxml; use Apache::edit; +use Apache::lonnet; +use LONCAPA; + + +use vars qw/$weboutputformat $version/; + + BEGIN { - &Apache::lonxml::register('Apache::lonplot',('plot')); + &Apache::lonxml::register('Apache::lonplot',('gnuplot')); + # + # Determine the version of GNUPLOT + $weboutputformat = 'gif'; + my $versionstring = `gnuplot --version 2>/dev/null`; + ($version) = ($versionstring =~ /^gnuplot ([\d.]+)/); + if ($version >= 4) { + $weboutputformat = 'png'; + } + } + +=pod + ## ## Description of data structures: ## @@ -79,9 +93,22 @@ BEGIN { ## ## ################################################################### +=cut + my $max_str_len = 50; # if a label, title, xlabel, or ylabel text # is longer than this, it will be truncated. +my %linetypes = # For png use these linetypes. + ( + solid => 1, + dashed => 0 + ); +my %ps_linetypes = # For ps the line types are different! + ( + solid => 0, + dashed => 7 + ); + my %linestyles = ( lines => 2, # Maybe this will be used in the future @@ -96,92 +123,239 @@ my %linestyles = yerrorbars => [3,4], xyerrorbars => [4,6], boxes => 3, -# boxerrorbars => [3,4,5], -# boxxyerrorbars => [4,6,7], -# financebars => 5, -# candlesticks => 5, + filledcurves => 2, vector => 4 ); my $int_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^\d+$/}; my $real_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^[+-]?\d*\.?\d*([eE][+-]\d+)?$/}; -my $color_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^x[\da-f]{6}$/}; +my $pos_real_test = + sub {$_[0]=~s/\s+//g;$_[0]=~/^[+]?\d*\.?\d*([eE][+-]\d+)?$/}; +my $color_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^x[\da-fA-F]{6}$/}; my $onoff_test = sub {$_[0]=~/^(on|off)$/}; my $key_pos_test = sub {$_[0]=~/^(top|bottom|right|left|outside|below| )+$/}; -my $sml_test = sub {$_[0]=~/^(small|medium|large)$/}; +my $sml_test = sub {$_[0]=~/^(\d+|small|medium|large)$/}; my $linestyle_test = sub {exists($linestyles{$_[0]})}; -my $words_test = sub {$_[0]=~s/\s+/ /g;$_[0]=~/^([\w\(\)]+ ?)+$/}; +my $words_test = sub {$_[0]=~s/\s+/ /g;$_[0]=~/^([\w~!\@\#\$\%^&\*\(\)-=_\+\[\]\{\}:\;\'<>,\.\/\?\\]+ ?)+$/}; ################################################################### ## ## ## Attribute metadata ## ## ## ################################################################### -my @plot_edit_order = - qw/bgcolor fgcolor height width font transparent grid border align/; -my %plot_defaults = +my @gnuplot_edit_order = + qw/alttag bgcolor fgcolor height width texwidth fontface font texfont + transparent grid samples + border align plotcolor plottype gridtype lmargin rmargin + tmargin bmargin major_ticscale minor_ticscale boxwidth gridlayer fillstyle + pattern solid/; + +my $margin_choices = ['default',0..20]; + +my %gnuplot_defaults = ( + alttag => { + default => 'dynamically generated plot', + test => $words_test, + description => 'Brief description of the plot', + edit_type => 'entry', + size => '40' + }, height => { - default => 200, + default => 300, test => $int_test, - description => 'height of image (pixels)', + description => 'Height of image (pixels)', edit_type => 'entry', size => '10' }, width => { - default => 200, + default => 400, test => $int_test, - description => 'width of image (pixels)', + description => 'Width of image (pixels)', edit_type => 'entry', size => '10' }, bgcolor => { default => 'xffffff', test => $color_test, - description => 'background color of image (xffffff)', + description => 'Background color of image (xffffff)', edit_type => 'entry', - size => '10' + size => '10', + class => 'colorchooser' }, fgcolor => { default => 'x000000', test => $color_test, - description => 'foreground color of image (x000000)', + description => 'Foreground color of image (x000000)', edit_type => 'entry', - size => '10' + size => '10', + class => 'colorchooser' }, transparent => { default => 'off', test => $onoff_test, description => 'Transparent image', - edit_type => 'on_off' + edit_type => 'onoff' }, grid => { - default => 'off', + default => 'on', test => $onoff_test, description => 'Display grid', - edit_type => 'on_off' + edit_type => 'onoff' + }, + gridlayer => { + default => 'off', + test => $onoff_test, + description => 'Display grid front layer over filled boxes or filled curves', + edit_type => 'onoff' + }, + box_border => { + default => 'noborder', + test => sub {$_[0]=~/^(noborder|border)$/}, + description => 'Draw border for boxes', + edit_type => 'choice', + choices => ['border','noborder'] }, border => { default => 'on', test => $onoff_test, description => 'Draw border around plot', - edit_type => 'on_off' + edit_type => 'onoff' }, font => { - default => 'medium', + default => '9', test => $sml_test, - description => 'Size of font to use', + description => 'Font size to use in web output (pts)', edit_type => 'choice', - choices => ['small','medium','large'] + choices => [['5','5 (small)'],'6','7','8',['9','9 (medium)'],'10',['11','11 (large)'],'12','15'] + }, + fontface => { + default => 'sans-serif', + test => sub {$_[0]=~/^(sans-serif|serif|classic)$/}, + description => 'Type of font to use', + edit_type => 'choice', + choices => ['sans-serif','serif', 'classic'] + }, + samples => { + default => '100', + test => $int_test, + description => 'Number of samples for non-data plots', + edit_type => 'choice', + choices => ['100','200','500','1000','2000','5000'] }, align => { - default => 'left', - test => sub {$_[0]=~/^(left|right|center)$/}, - description => 'alignment for image in html', + default => 'middle', + test => sub {$_[0]=~/^(left|right|middle|center)$/}, + description => 'Alignment for image in HTML', edit_type => 'choice', - choices => ['left','right','center'] - } + choices => ['left','right','middle'] + }, + texwidth => { + default => '93', + test => $int_test, + description => 'Width of plot when printed (mm)', + edit_type => 'entry', + size => '5' + }, + texfont => { + default => '22', + test => $int_test, + description => 'Font size to use in TeX output (pts):', + edit_type => 'choice', + choices => [qw/8 10 12 14 16 18 20 22 24 26 28 30 32 34 36/], + }, + plotcolor => { + default => 'monochrome', + test => sub {$_[0]=~/^(monochrome|color|colour)$/}, + description => 'Color setting for printing:', + edit_type => 'choice', + choices => [qw/monochrome color colour/], + }, + pattern => { + default => '', + test => $int_test, + description => 'Pattern value for boxes:', + edit_type => 'choice', + choices => [0,1,2,3,4,5,6] + }, + solid => { + default => 0, + test => $real_test, + description => 'The density of fill style for boxes', + edit_type => 'entry', + size => '5' + }, + fillstyle => { + default => 'empty', + test => sub {$_[0]=~/^(empty|solid|pattern)$/}, + description => 'Filled style for boxes:', + edit_type => 'choice', + choices => ['empty','solid','pattern'] + }, + plottype => { + default => 'Cartesian', + test => sub {$_[0]=~/^(Polar|Cartesian)$/}, + description => 'Plot type:', + edit_type => 'choice', + choices => ['Cartesian','Polar'] + }, + gridtype => { + default => 'Cartesian', + test => sub {$_[0]=~/^(Polar|Cartesian|Linear-Log|Log-Linear|Log-Log)$/}, + description => 'Grid type:', + edit_type => 'choice', + choices => ['Cartesian','Polar','Linear-Log','Log-Linear','Log-Log'] + }, + lmargin => { + default => 'default', + test => sub {$_[0]=~/^(default|\d+)$/}, + description => 'Left margin width (pts):', + edit_type => 'choice', + choices => $margin_choices, + }, + rmargin => { + default => 'default', + test => sub {$_[0]=~/^(default|\d+)$/}, + description => 'Right margin width (pts):', + edit_type => 'choice', + choices => $margin_choices, + }, + tmargin => { + default => 'default', + test => sub {$_[0]=~/^(default|\d+)$/}, + description => 'Top margin width (pts):', + edit_type => 'choice', + choices => $margin_choices, + }, + bmargin => { + default => 'default', + test => sub {$_[0]=~/^(default|\d+)$/}, + description => 'Bottom margin width (pts):', + edit_type => 'choice', + choices => $margin_choices, + }, + boxwidth => { + default => '', + test => $real_test, + description => 'Width of boxes, default is auto', + edit_type => 'entry', + size => '5' + }, + major_ticscale => { + default => '1', + test => $real_test, + description => 'Size of major tic marks (plot coordinates)', + edit_type => 'entry', + size => '5' + }, + minor_ticscale => { + default => '0.5', + test => $real_test, + description => 'Size of minor tic mark (plot coordinates)', + edit_type => 'entry', + size => '5' + }, ); my %key_defaults = @@ -197,12 +371,12 @@ my %key_defaults = default => 'off', test => $onoff_test, description => 'Draw a box around the key?', - edit_type => 'on_off' + edit_type => 'onoff' }, pos => { default => 'top right', test => $key_pos_test, - description => 'position of the key on the plot', + description => 'Position of the key on the plot', edit_type => 'choice', choices => ['top left','top right','bottom left','bottom right', 'outside','below'] @@ -214,14 +388,14 @@ my %label_defaults = xpos => { default => 0, test => $real_test, - description => 'x position of label (graph coordinates)', + description => 'X position of label (graph coordinates)', edit_type => 'entry', size => '10' }, ypos => { default => 0, test => $real_test, - description => 'y position of label (graph coordinates)', + description => 'Y position of label (graph coordinates)', edit_type => 'entry', size => '10' }, @@ -231,71 +405,195 @@ my %label_defaults = description => 'justification of the label text on the plot', edit_type => 'choice', choices => ['left','right','center'] + }, + rotate => { + default => 0, + test => $real_test, + description => 'Rotation of label (degrees)', + edit_type => 'entry', + size => '10', } ); +my @tic_edit_order = ('location','mirror','start','increment','end', + 'minorfreq'); +my %tic_defaults = + ( + location => { + default => 'border', + test => sub {$_[0]=~/^(border|axis)$/}, + description => 'Location of major tic marks', + edit_type => 'choice', + choices => ['border','axis'] + }, + mirror => { + default => 'on', + test => $onoff_test, + description => 'Mirror tics on opposite axis?', + edit_type => 'onoff' + }, + start => { + default => '-10.0', + test => $real_test, + description => 'Start major tics at', + edit_type => 'entry', + size => '10' + }, + increment => { + default => '1.0', + test => $real_test, + description => 'Place a major tic every', + edit_type => 'entry', + size => '10' + }, + end => { + default => ' 10.0', + test => $real_test, + description => 'Stop major tics at ', + edit_type => 'entry', + size => '10' + }, + minorfreq => { + default => '0', + test => $int_test, + description => 'Number of minor tics per major tic mark', + edit_type => 'entry', + size => '10' + }, + ); + +my @axis_edit_order = ('color','xmin','xmax','ymin','ymax','xformat', 'yformat', 'xzero', 'yzero'); my %axis_defaults = ( color => { default => 'x000000', test => $color_test, - description => 'color of axes (x000000)', + description => 'Color of grid lines (x000000)', edit_type => 'entry', - size => '10' + size => '10', + class => 'colorchooser' }, xmin => { default => '-10.0', test => $real_test, - description => 'minimum x-value shown in plot', + description => 'Minimum x-value shown in plot', edit_type => 'entry', size => '10' }, xmax => { default => ' 10.0', test => $real_test, - description => 'maximum x-value shown in plot', + description => 'Maximum x-value shown in plot', edit_type => 'entry', size => '10' }, ymin => { default => '-10.0', test => $real_test, - description => 'minimum y-value shown in plot', + description => 'Minimum y-value shown in plot', edit_type => 'entry', size => '10' }, ymax => { default => ' 10.0', test => $real_test, - description => 'maximum y-value shown in plot', + description => 'Maximum y-value shown in plot', edit_type => 'entry', size => '10' - } + }, + xformat => { + default => 'on', + test => sub {$_[0]=~/^(on|off|\d+(f|F|e|E))$/}, + description => 'X-axis number formatting', + edit_type => 'choice', + choices => ['on', 'off', '2e', '2f'], + }, + yformat => { + default => 'on', + test => sub {$_[0]=~/^(on|off|\d+(f|F|e|E))$/}, + description => 'Y-axis number formatting', + edit_type => 'choice', + choices => ['on', 'off', '2e', '2f'], + }, + + xzero => { + default => 'off', + test => sub {$_[0]=~/^(off|line|thick-line|dotted)$/}, + description => 'Show x-zero (y=0) axis', + edit_type => 'choice', + choices => ['off', 'line', 'thick-line', 'dotted'], + }, + + yzero => { + default => 'off', + test => sub {$_[0]=~/^(off|line|thick-line|dotted)$/}, + description => 'Show y-zero (x=0) axis', + edit_type => 'choice', + choices => ['off', 'line', 'thick-line', 'dotted'], + }, ); +my @curve_edit_order = ('color','name','linestyle','linewidth','linetype','pointtype','pointsize','limit'); + my %curve_defaults = ( color => { default => 'x000000', test => $color_test, - description => 'color of curve (x000000)', + description => 'Color of curve (x000000)', edit_type => 'entry', - size => '10' + size => '10', + class => 'colorchooser' }, name => { default => '', test => $words_test, - description => 'name of curve to appear in key', + description => 'Name of curve to appear in key', edit_type => 'entry', size => '20' }, linestyle => { default => 'lines', test => $linestyle_test, - description => 'Line style', + description => 'Plot with:', edit_type => 'choice', choices => [keys(%linestyles)] - } + }, + linewidth => { + default => 1, + test => $int_test, + description => 'Line width (may not apply to all plot styles)', + edit_type => 'choice', + choices => [1,2,3,4,5,6,7,8,9,10] + }, + linetype => { + default => 'solid', + test => sub {$_[0]=~/^(solid|dashed)$/}, + description => 'Line type (may not apply to all plot styles)', + edit_type => 'choice', + choices => ['solid', 'dashed'] + }, + pointsize => { + default => 1, + test => $pos_real_test, + description => 'Point size (may not apply to all plot styles)', + edit_type => 'entry', + size => '5' + }, + pointtype => { + default => 1, + test => $int_test, + description => 'Point type (may not apply to all plot styles)', + edit_type => 'choice', + choices => [0,1,2,3,4,5,6] + }, + limit => { + default => 'closed', + test => sub {$_[0]=~/^(above|below|closed|x1|x2|y1|y2)$/}, + description => 'Point to fill -- for filledcurves', + edit_type => 'choice', + choices => ['above', 'below', 'closed','x1','x2','y1','y2'] + }, ); ################################################################### @@ -303,76 +601,216 @@ my %curve_defaults = ## parsing and edit rendering ## ## ## ################################################################### -my (%plot,%key,%axis,$title,$xlabel,$ylabel,@labels,@curves); -sub start_plot { - %plot = (); %key = (); %axis = (); - $title = undef; $xlabel = undef; $ylabel = undef; - $#labels = -1; $#curves = -1; +undef %Apache::lonplot::plot; +my (%key,%axis,$title,$xlabel,$ylabel,@labels,@curves,%xtics,%ytics); + +sub start_gnuplot { + undef(%Apache::lonplot::plot); undef(%key); undef(%axis); + undef($title); undef($xlabel); undef($ylabel); + undef(@labels); undef(@curves); + undef(%xtics); undef(%ytics); # my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; &Apache::lonxml::register('Apache::lonplot', - ('title','xlabel','ylabel','key','axis','label','curve')); + ('title','xlabel','ylabel','key','axis','label','curve', + 'xtics','ytics')); push (@Apache::lonxml::namespace,'lonplot'); - if ($target eq 'web') { - my $inside = &Apache::lonxml::get_all_text("/plot",$$parser[-1]); - $inside=&Apache::run::evaluate($inside,$safeeval,$$parstack[-1]); - &Apache::lonxml::newparser($parser,\$inside); - &get_attributes(\%plot,\%plot_defaults,$parstack,$safeeval, + if ($target eq 'web' || $target eq 'tex') { + &get_attributes(\%Apache::lonplot::plot,\%gnuplot_defaults,$parstack,$safeeval, $tagstack->[-1]); } elsif ($target eq 'edit') { - $result .= &Apache::edit::tag_start($target,$token,'Plot'); - $result .= &edit_attributes($target,$token,\%plot_defaults, - \@plot_edit_order); + $result .= &Apache::edit::tag_start($target,$token,'GnuPlot'); + $result .= &edit_attributes($target,$token,\%gnuplot_defaults, + \@gnuplot_edit_order) + .&Apache::edit::end_row() + .&Apache::edit::start_spanning_row(); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args - ($token,$parstack,$safeeval,keys(%plot_defaults)); + ($token,$parstack,$safeeval,keys(%gnuplot_defaults)); + if ($constructtag) { + # + # The color chooser does not prepent x to the color values + # Do that here: + # + foreach my $attribute ('bgcolor', 'fgcolor') { + my $value = $token->[2]{$attribute}; + if (defined $value && !($value =~ /^x/)) { + $token->[2]{$attribute} = 'x' . $value; + } + } $result = &Apache::edit::rebuild_tag($token); -# $result.= &Apache::edit::handle_insert(); } } return $result; } -sub end_plot { +sub end_gnuplot { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; - pop @Apache::lonxml::namespace; &Apache::lonxml::deregister('Apache::lonplot', ('title','xlabel','ylabel','key','axis','label','curve')); my $result = ''; - if ($target eq 'web') { + my $randnumber; + my $tmpdir =LONCAPA::tempdir(); # Where temporary files live: + + # need to call rand everytime start_script would evaluate, as the + # safe space rand number generator and the global rand generator + # are not separate + if ($target eq 'web' || $target eq 'tex' || $target eq 'grade' || + $target eq 'answer') { + $randnumber=int(rand(1000)); + } + if ($target eq 'web' || $target eq 'tex') { &check_inputs(); # Make sure we have all the data we need ## ## Determine filename - my $tmpdir = '/home/httpd/perl/tmp/'; - my $filename = $ENV{'user.name'}.'_'.$ENV{'user.domain'}. - '_'.time.'_'.$$.int(rand(1000)).'_plot.data'; + my $filename = $env{'user.name'}.'_'.$env{'user.domain'}. + '_'.time.'_'.$$.$randnumber.'_plot'; ## Write the plot description to the file - my $fh=Apache::File->new(">$tmpdir$filename"); - print $fh &write_gnuplot_file(); - close($fh); + &write_gnuplot_file($tmpdir,$filename,$target); + $filename = &escape($filename); ## return image tag for the plot - $result .= <<"ENDIMAGE"; -/cgi-bin/plot.gif?$filename + if ($target eq 'web') { + $result .= <<"ENDIMAGE"; +$Apache::lonplot::plot{'alttag'} ENDIMAGE + } elsif ($target eq 'tex') { + &Apache::lonxml::debug(" gnuplot wid = $Apache::lonplot::plot{'width'}"); + &Apache::lonxml::debug(" gnuplot ht = $Apache::lonplot::plot{'height'}"); + #might be inside the safe space, register the URL for later + &Apache::lonxml::register_ssi("/cgi-bin/plot.gif?file=$filename.data&output=eps"); + $result = "%DYNAMICIMAGE:$Apache::lonplot::plot{'width'}:$Apache::lonplot::plot{'height'}:$Apache::lonplot::plot{'texwidth'}\n"; + $result .= '\graphicspath{{'.$tmpdir.'}}'."\n"; + $result .= '\includegraphics[width='.$Apache::lonplot::plot{'texwidth'}.' mm]{'.&unescape($filename).'.eps}'; + } + } elsif ($target eq 'edit') { + $result.=&Apache::edit::tag_end($target,$token); + } + return $result; +} + + +##--------------------------------------------------------------- xtics +sub start_xtics { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + my $result=''; + if ($target eq 'web' || $target eq 'tex') { + &get_attributes(\%xtics,\%tic_defaults,$parstack,$safeeval, + $tagstack->[-1]); + } elsif ($target eq 'edit') { + $result .= &Apache::edit::tag_start($target,$token,'xtics'); + $result .= &edit_attributes($target,$token,\%tic_defaults, + \@tic_edit_order); + } elsif ($target eq 'modified') { + my $constructtag=&Apache::edit::get_new_args + ($token,$parstack,$safeeval,keys(%tic_defaults)); + if ($constructtag) { + $result = &Apache::edit::rebuild_tag($token); + } + } + return $result; +} + +sub end_xtics { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + my $result = ''; + if ($target eq 'web' || $target eq 'tex') { + } elsif ($target eq 'edit') { + $result.=&Apache::edit::tag_end($target,$token); + } + return $result; +} + +##--------------------------------------------------------------- ytics +sub start_ytics { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + my $result=''; + if ($target eq 'web' || $target eq 'tex') { + &get_attributes(\%ytics,\%tic_defaults,$parstack,$safeeval, + $tagstack->[-1]); + } elsif ($target eq 'edit') { + $result .= &Apache::edit::tag_start($target,$token,'ytics'); + $result .= &edit_attributes($target,$token,\%tic_defaults, + \@tic_edit_order); + } elsif ($target eq 'modified') { + my $constructtag=&Apache::edit::get_new_args + ($token,$parstack,$safeeval,keys(%tic_defaults)); + if ($constructtag) { + $result = &Apache::edit::rebuild_tag($token); + } + } + return $result; +} + +sub end_ytics { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + my $result = ''; + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } +##-----------------------------------------------------------------font +my %font_properties = + ( + 'classic' => { + face => 'classic', + file => 'DejaVuSansMono-Bold', + printname => 'Helvetica', + tex_no_file => 1, + }, + 'sans-serif' => { + face => 'sans-serif', + file => 'DejaVuSans', + printname => 'DejaVuSans', + }, + 'serif' => { + face => 'serif', + file => 'DejaVuSerif', + printname => 'DejaVuSerif', + }, + ); + +sub get_font { + my ($target) = @_; + my ($size, $selected_font); + + if ( $Apache::lonplot::plot{'font'} =~ /^(small|medium|large)/) { + $selected_font = $font_properties{'classic'}; + if ( $Apache::lonplot::plot{'font'} eq 'small') { + $size = '5'; + } elsif ( $Apache::lonplot::plot{'font'} eq 'medium') { + $size = '9'; + } elsif ( $Apache::lonplot::plot{'font'} eq 'large') { + $size = '11'; + } else { + $size = '9'; + } + } else { + $size = $Apache::lonplot::plot{'font'}; + $selected_font = $font_properties{$Apache::lonplot::plot{'fontface'}}; + } + if ($target eq 'tex' && defined($Apache::lonplot::plot{'texfont'})) { +# $selected_font = $font_properties{'classic'}; + $size = $Apache::lonplot::plot{'texfont'}; + } + return ($size, $selected_font); +} + ##----------------------------------------------------------------- key sub start_key { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { &get_attributes(\%key,\%key_defaults,$parstack,$safeeval, $tagstack->[-1]); } elsif ($target eq 'edit') { @@ -383,7 +821,6 @@ sub start_key { ($token,$parstack,$safeeval,keys(%key_defaults)); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); - $result.= &Apache::edit::handle_insert(); } } return $result; @@ -392,30 +829,407 @@ sub start_key { sub end_key { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } return $result; } +sub parse_label { + my ($target,$text) = @_; + my $parser=HTML::LCParser->new(\$text); + my $result; + while (my $token=$parser->get_token) { + if ($token->[0] eq 'S') { + if ($token->[1] eq 'sub') { + $result .= '_{'; + } elsif ($token->[1] eq 'sup') { + $result .= '^{'; + } else { + $result .= $token->[4]; + } + } elsif ($token->[0] eq 'E') { + if ($token->[1] eq 'sub' + || $token->[1] eq 'sup') { + $result .= '}'; + } else { + $result .= $token->[2]; + } + } elsif ($token->[0] eq 'T') { + $result .= &replace_entities($target,$token->[1]); + } + } + return $result; +} + +# +# Note that there are severe restrictions on font selection in the +# ps driver now. later in life Gnuplot is supposed to support +# utf-8 fonts in the posts script driver. When this happens, +# the tex entries with comments that include the word +# should be changed to print the correct glyphs rather than some +# approximation or fallback of what is intended. + +my %lookup = + ( # Greek alphabet: + + '(Alpha|#913)' => {'tex' => '{/Symbol A}', 'web' => "\x{391}"}, + '(Beta|#914)' => {'tex' => '{/Symbol B}', 'web' => "\x{392}"}, + '(Chi|#935)' => {'tex' => '{/Symbol C}', 'web' => "\x{3A7}"}, + '(Delta|#916)' => {'tex' => '{/Symbol D}', 'web' => "\x{394}"}, + '(Epsilon|#917)' => {'tex' => '{/Symbol E}', 'web' => "\x{395}"}, + '(Phi|#934)' => {'tex' => '{/Symbol F}', 'web' => "\x{3A6}"}, + '(Gamma|#915)' => {'tex' => '{/Symbol G}', 'web' => "\x{393}"}, + '(Eta|#919)' => {'tex' => '{/Symbol H}', 'web' => "\x{397}"}, + '(Iota|#921)' => {'tex' => '{/Symbol I}', 'web' => "\x{399}"}, + '(Kappa|#922)' => {'tex' => '{/Symbol K}', 'web' => "\x{39A}"}, + '(Lambda|#923)' => {'tex' => '{/Symbol L}', 'web' => "\x{39B}"}, + '(Mu|#924)' => {'tex' => '{/Symbol M}', 'web' => "\x{39C}"}, + '(Nu|#925)' => {'tex' => '{/Symbol N}', 'web' => "\x{39D}"}, + '(Omicron|#927)' => {'tex' => '{/Symbol O}', 'web' => "\x{39F}"}, + '(Pi|#928)' => {'tex' => '{/Symbol P}', 'web' => "\x{3A0}"}, + '(Theta|#920)' => {'tex' => '{/Symbol Q}', 'web' => "\x{398}"}, + '(Rho|#929)' => {'tex' => '{/Symbol R}', 'web' => "\x{3A1}"}, + '(Sigma|#931)' => {'tex' => '{/Symbol S}', 'web' => "\x{3A3}"}, + '(Tau|#932)' => {'tex' => '{/Symbol T}', 'web' => "\x{3A4}"}, + '(Upsilon|#933)' => {'tex' => '{/Symbol U}', 'web' => "\x{3A5}"}, + '(Omega|#937)' => {'tex' => '{/Symbol W}', 'web' => "\x{3A9}"}, + '(Xi|#926)' => {'tex' => '{/Symbol X}', 'web' => "\x{39E}"}, + '(Psi|#936)' => {'tex' => '{/Symbol Y}', 'web' => "\x{3A8}"}, + '(Zeta|#918)' => {'tex' => '{/Symbol Z}', 'web' => "\x{396}"}, + '(alpha|#945)' => {'tex' => '{/Symbol a}', 'web' => "\x{3B1}"}, + '(beta|#946)' => {'tex' => '{/Symbol b}', 'web' => "\x{3B2}"}, + '(chi|#967)' => {'tex' => '{/Symbol c}', 'web' => "\x{3C7}"}, + '(delta|#948)' => {'tex' => '{/Symbol d}', 'web' => "\x{3B4}"}, + '(epsilon|#949)' => {'tex' => '{/Symbol e}', 'web' => "\x{3B5}"}, + '(phi|#966)' => {'tex' => '{/Symbol f}', 'web' => "\x{3C6}"}, + '(gamma|#947)' => {'tex' => '{/Symbol g}', 'web' => "\x{3B3}"}, + '(eta|#951)' => {'tex' => '{/Symbol h}', 'web' => "\x{3B7}"}, + '(iota|#953)' => {'tex' => '{/Symbol i}', 'web' => "\x{3B9}"}, + '(kappa|#954)' => {'tex' => '{/Symbol k}', 'web' => "\x{3BA}"}, + '(lambda|#955)' => {'tex' => '{/Symbol k}', 'web' => "\x{3BB}"}, + '(mu|#956)' => {'tex' => '{/Symbol m}', 'web' => "\x{3BC}"}, + '(nu|#957)' => {'tex' => '{/Symbol n}', 'web' => "\x{3BD}"}, + '(omicron|#959)' => {'tex' => '{/Symbol o}', 'web' => "\x{3BF}"}, + '(pi|#960)' => {'tex' => '{/Symbol p}', 'web' => "\x{3C0}"}, + '(theta|#952)' => {'tex' => '{/Symbol q}', 'web' => "\x{3B8}"}, + '(rho|#961)' => {'tex' => '{/Symbol r}', 'web' => "\x{3C1}"}, + '(sigma|#963)' => {'tex' => '{/Symbol s}', 'web' => "\x{3C3}"}, + '(tau|#964)' => {'tex' => '{/Symbol t}', 'web' => "\x{3C4}"}, + '(upsilon|#965)' => {'tex' => '{/Symbol u}', 'web' => "\x{3C5}"}, + '(omega|#969)' => {'tex' => '{/Symbol w}', 'web' => "\x{3C9}"}, + '(xi|#958)' => {'tex' => '{/Symbol x}', 'web' => "\x{3BE}"}, + '(psi|#968)' => {'tex' => '{/Symbol y}', 'web' => "\x{3C8}"}, + '(zeta|#950)' => {'tex' => '{/Symbol z}', 'web' => "\x{3B6}"}, + '(thetasym|#977)' => {'tex' => '{/Symbol \165}', 'web' => "\x{3d1}"}, + '(upsih|#978)' => {'tex' => '{/Symbol \241}', 'web' => "\x{3d2}"}, + '(piv|#982)' => {'tex' => '{/Symbol \166}', 'web' => "\x{3d6}"}, + + + # Punctuation: + + '(quot|#034)' => {'tex' => '\42', 'web' => '\42'}, + '(amp|#038)' => {'tex' => '\46', 'web' => '\46'}, + '(lt|#060)' => {'tex' => '\74', 'web' => '\74'}, + '(gt|#062)' => {'tex' => '\76', 'web' => '\76'}, + '#131' => {'tex' => '{/Symbol \246}', 'web' => "\x{192}"}, + '#132' => {'tex' => '{/Text \271}', 'web' => "\x{201e}"}, + '#133' => {'tex' => '{/Symbol \274}', 'web'=> "\x{2026}"}, + '#134' => {'tex' => '{/Text \262}', 'web' => "\x{2020}"}, + '#135' => {'tex' => '{/Text \263}', 'web' => "\x{2021}"}, + '#136' => {'tex' => '\\\\^', 'web' => '\\\\^'}, + '#137' => {'tex' => '%o', 'web' => "\x{2030}"}, # Per Mille + '#138' => {'tex' => 'S', 'web' => "\x{160}"}, # S-Caron + '#139' => {'tex' => '<', 'web' => '<'}, + '#140' => {'tex' => 'AE', 'web' => "\x{152}"}, # AE ligature + '#145' => {'tex' => '\140', 'web' => "\x{2018}"}, + '#146' => {'tex' => '\47', 'web' => "\x{2019}"}, + '#147' => {'tex' => '\140\140', 'web' => "\x{201c}"}, # Left " + '#148' => {'tex' => '\47\47', 'web' => '\\"'}, # Right " + '#149' => {'tex' => '{/Symbol \267}', 'web' => "\x{2022}"}, + '#150' => {'tex' => '{/Text \55}', 'web' => "\x{2013}"}, # en dash + '#151' => {'tex' => '{/Symbol \55}', 'web' => "\x{2014}"}, # em dash + '#152' => {'tex' => '\\\\~', 'web' => '\\\\~'}, + '#153' => {'tex' => '{/Symbol \324}', 'web' => "\x{2122}"}, # trademark + + # Accented letters, and other furreign language glyphs. + + '#154' => {'tex' => 's', 'web' => "\x{161}"}, # small s-caron no ps. + '#155' => {'tex' => '>', 'web' => '\76'}, # > + '#156' => {'tex' => '{/Text \366}', 'web' => "\x{153}"}, # oe ligature. + '#159', => {'tex' => 'Y', 'web' => "\x{178}"}, # Y-umlaut - can't print + '(nbsp|#160)' => {'tex' => ' ', 'web' => ' '}, # non breaking space. + '(iexcl|#161)' => {'tex' => '{/Text \241}', 'web' => "\x{a1}"}, # inverted ! + '(cent|#162)' => {'tex' => '{/Text \242}', 'web' => "\x{a2}"}, # Cent currency. + '(pound|#163)' => {'tex' => '{/Text \243}', 'web' => "\x{a3}"}, # GB Pound currency. + '(curren|#164)' => {'tex' => '{/ZapfDingbats \161}','web' => "\x{a4}"}, # Generic currency symb. + '(yen|#165)' => {'tex' => '{/Text \245}', 'web' => "\x{a5}"}, # Yen currency. + '(brvbar|#166)' => {'tex' => '{/Symbol \174}', 'web' => "\x{a6}"}, # Broken vert bar no print. + '(sect|#167)' => {'tex' => '{\247}', 'web' => "\x{a7}"}, # Section symbol. + '(uml|#168)' => {'tex' => '{\250}', 'web' => "\x{a8}"}, # 'naked' umlaut. + '(copy|#169)' => {'tex' => '{/Symbol \343}', 'web' => "\x{a9}"}, # Copyright symbol. + '(ordf|#170)' => {'tex' => '{/Text \343}', 'web' => "\x{aa}"}, # Feminine ordinal. + '(laquo|#171)' => {'tex' => '{/Text \253}', 'web' => "\x{ab}"}, # << quotes. + '(not|#172)' => {'tex' => '\254', 'web' => "\x{ac}"}, # Logical not. + '(shy|#173)' => {'tex' => '\255', 'web' => "\x{ad}"}, # soft hyphen. + '(reg|#174)' => {'tex' => '{/Symbol \342}', 'web' => "\x{ae}"}, # Registered tm. + '(macr|#175)' => {'tex' => '^{\255}', 'web' => "\x{af}"}, # 'naked' macron (overbar). + '(deg|#176)' => {'tex' => '{/Text \260}', 'web' => "\x{b0}"}, # Degree symbo..` + '(plusmn|#177)' => {'tex' => '{/Symbol \261}', 'web' => "\x{b1}"}, # +/- symbol. + '(sup2|#178)' => {'tex' => '^2', 'web' => "\x{b2}"}, # Superscript 2. + '(sup3|#179)' => {'tex' => '^3', 'web' => "\x{b3}"}, # Superscript 3. + '(acute|#180)' => {'tex' => '{/Text \222}', 'web' => "\x{b4}"}, # 'naked' acute accent. + '(micro|#181)' => {'tex' => '{/Symbol \155}', 'web' => "\x{b5}"}, # Micro (small mu). + '(para|#182)' => {'tex' => '{/Text \266}', 'web' => "\x{b6}"}, # Paragraph symbol. + '(middot|#183)' => {'tex' => '\267', 'web' => "\x{b7}"}, # middle dot + '(cedil|#184)' => {'tex' => '\233', 'web' => "\x{b8}"}, # 'naked' cedilla. + '(sup1|#185)' => {'tex' => '^1', 'web' => "\x{b9}"}, # superscript 1. + '(ordm|#186)' => {'tex' => '{\260}', 'web' => "\x{ba}"}, # masculine ordinal. + '(raquo|#187)', => {'tex' => '\273', 'web' => "\x{bb}"}, # Right angle quotes. + '(frac14|#188)' => {'tex' => '\274', 'web' => "\x{bc}"}, # 1/4. + '(frac12|#189)' => {'tex' => '\275', 'web' => "\x{bd}"}, # 1/2. + '(frac34|#190)' => {'tex' => '\276', 'web' => "\x{be}"}, # 3/4 + '(iquest|#191)' => {'tex' => '{/Text \277}', 'web' => "\x{bf}"}, # Inverted ? + '(Agrave|#192)' => {'tex' => '\300', 'web' => "\x{c0}"}, # A Grave. + '(Aacute|#193)' => {'tex' => '\301', 'web' => "\x{c1}"}, # A Acute. + '(Acirc|#194)' => {'tex' => '\302', 'web' => "\x{c2}"}, # A Circumflex. + '(Atilde|#195)' => {'tex' => '\303', 'web' => "\x{c3}"}, # A tilde. + '(Auml|#196)' => {'tex' => '\304', 'web' => "\x{c4}"}, # A umlaut. + '(Aring|#197)' => {'tex' => '\305', 'web' => "\x{c5}"}, # A ring. + '(AElig|#198)' => {'tex' => '\306', 'web' => "\x{c6}"}, # AE ligature. + '(Ccedil|#199)' => {'tex' => '\307', 'web' => "\x{c7}"}, # C cedilla + '(Egrave|#200)' => {'tex' => '\310', 'web' => "\x{c8}"}, # E Accent grave. + '(Eacute|#201)' => {'tex' => '\311', 'web' => "\x{c9}"}, # E acute accent. + '(Ecirc|#202)' => {'tex' => '\312', 'web' => "\x{ca}"}, # E Circumflex. + '(Euml|#203)' => {'tex' => '\313', 'web' => "\x{cb}"}, # E umlaut. + '(Igrave|#204)' => {'tex' => '\314', 'web' => "\x{cc}"}, # I grave accent. + '(Iacute|#205)' => {'tex' => '\315', 'web' => "\x{cd}"}, # I acute accent. + '(Icirc|#206)' => {'tex' => '\316', 'web' => "\x{ce}"}, # I circumflex. + '(Iuml|#207)' => {'tex' => '\317', 'web' => "\x{cf}"}, # I umlaut. + '(ETH|#208)' => {'tex' => '\320', 'web' => "\x{d0}"}, # Icelandic Cap eth. + '(Ntilde|#209)' => {'tex' => '\321', 'web' => "\x{d1}"}, # Ntilde (enyan). + '(Ograve|#210)' => {'tex' => '\322', 'web' => "\x{d2}"}, # O accent grave. + '(Oacute|#211)' => {'tex' => '\323', 'web' => "\x{d3}"}, # O accent acute. + '(Ocirc|#212)' => {'tex' => '\324', 'web' => "\x{d4}"}, # O circumflex. + '(Otilde|#213)' => {'tex' => '\325', 'web' => "\x{d5}"}, # O tilde. + '(Ouml|#214)' => {'tex' => '\326', 'web' => "\x{d6}"}, # O umlaut. + '(times|#215)' => {'tex' => '\327', 'web' => "\x{d7}"}, # Times symbol. + '(Oslash|#216)' => {'tex' => '\330', 'web' => "\x{d8}"}, # O slash. + '(Ugrave|#217)' => {'tex' => '\331', 'web' => "\x{d9}"}, # U accent grave. + '(Uacute|#218)' => {'tex' => '\332', 'web' => "\x{da}"}, # U accent acute. + '(Ucirc|#219)' => {'tex' => '\333', 'web' => "\x{db}"}, # U circumflex. + '(Uuml|#220)' => {'tex' => '\334', 'web' => "\x{dc}"}, # U umlaut. + '(Yacute|#221)' => {'tex' => '\335', 'web' => "\x{dd}"}, # Y accent acute. + '(THORN|#222)' => {'tex' => '\336', 'web' => "\x{de}"}, # Icelandic thorn. + '(szlig|#223)' => {'tex' => '\337', 'web' => "\x{df}"}, # German sharfes s. + '(agrave|#224)' => {'tex' => '\340', 'web' => "\x{e0}"}, # a accent grave. + '(aacute|#225)' => {'tex' => '\341', 'web' => "\x{e1}"}, # a grave. + '(acirc|#226)' => {'tex' => '\342', 'web' => "\x{e2}"}, # a circumflex. + '(atilde|#227)' => {'tex' => '\343', 'web' => "\x{e3}"}, # a tilde. + '(auml|#228)' => {'tex' => '\344', 'web' => "\x{e4}"}, # a umlaut + '(aring|#229)' => {'tex' => '\345', 'web' => "\x{e5}"}, # a ring on top. + '(aelig|#230)' => {'tex' => '\346', 'web' => "\x{e6}"}, # ae ligature. + '(ccedil|#231)' => {'tex' => '\347', 'web' => "\x{e7}"}, # C cedilla + '(egrave|#232)' => {'tex' => '\350', 'web' => "\x{e8}"}, # e accent grave. + '(eacute|#233)' => {'tex' => '\351', 'web' => "\x{e9}"}, # e accent acute. + '(ecirc|#234)' => {'tex' => '\352', 'web' => "\x{ea}" }, # e circumflex. + '(euml|#235)' => {'tex' => '\353', 'web' => "\x{eb}"}, # e umlaut. + '(igrave|#236)' => {'tex' => '\354', 'web' => "\x{ec}"}, # i grave. + '(iacute|#237)' => {'tex' => '\355', 'web' => "\x{ed}"}, # i acute. + '(icirc|#238)' => {'tex' => '\356', 'web' => "\x{ee}"}, # i circumflex. + '(iuml|#239)' => {'tex' => '\357', 'web' => "\x{ef}"}, # i umlaut. + '(eth|#240)' => {'tex' => '\360', 'web' => "\x{f0}"}, # Icelandic eth. + '(ntilde|#241)' => {'tex' => '\361', 'web' => "\x{f1}"}, # n tilde. + '(ograve|#242)' => {'tex' => '\362', 'web' => "\x{f2}"}, # o grave. + '(oacute|#243)' => {'tex' => '\363', 'web' => "\x{f3}"}, # o acute. + '(ocirc|#244)' => {'tex' => '\364', 'web' => "\x{f4}"}, # o circumflex. + '(otilde|#245)' => {'tex' => '\365', 'web' => "\x{f5}"}, # o tilde. + '(ouml|#246)' => {'tex' => '\366', 'web' => "\x{f6}"}, # o umlaut. + '(divide|#247)' => {'tex' => '\367', 'web' => "\x{f7}"}, # division symbol + '(oslash|#248)' => {'tex' => '\370', 'web' => "\x{f8}"}, # o slashed. + '(ugrave|#249)' => {'tex' => '\371', 'web' => "\x{f9}"}, # u accent grave. + '(uacute|#250)' => {'tex' => '\372', 'web' => "\x{fa}"}, # u acute. + '(ucirc|#251)' => {'tex' => '\373', 'web' => "\x{fb}"}, # u circumflex. + '(uuml|#252)' => {'tex' => '\374', 'web' => "\x{fc}"}, # u umlaut. + '(yacute|#253)' => {'tex' => '\375', 'web' => "\x{fd}"}, # y acute accent. + '(thorn|#254)' => {'tex' => '\376', 'web' => "\x{fe}"}, # small thorn (icelandic). + '(yuml|#255)' => {'tex' => '\377', 'web' => "\x{ff}"}, # y umlaut. + + # Latin extended A entities: + + '(OElig|#338)' => {'tex' => '{/Text \326}', 'web' => "\x{152}"}, # OE ligature. + '(oelig|#339)' => {'tex' => '{/Text \366}', 'web' => "\x{153}"}, # oe ligature. + '(Scaron|#352)' => {'tex' => 'S', 'web' => "\x{160}"}, # S caron no printable. + '(scaron|#353)' => {'tex' => 's', 'web' => "\x{161}"}, # s caron no printable. + '(Yuml|#376)' => {'tex' => 'Y', 'web' => "\x{178}"}, # Y umlaut - no printable. + + # Latin extended B. + + '(fnof|#402)' => {'tex' =>'{/Symbol \246}', 'web' => "\x{192}"}, # f with little hook. + + # Standalone accents: + + '(circ|#710)' => {'tex' => '^', 'web' => '^'}, # circumflex. + '(tilde|#732)' => {'tex' => '~', 'web' => '~'}, # tilde. + + # General punctuation. We're not able to make a distinction between + # the various length spacings in the print version. (e.g. en/em/thin). + # the various joiners will be empty strings in the print version too. + + + '(ensp|#8194)' => {'tex' => ' ', 'web' => "\x{2002}"}, # en space. + '(emsp|#8195)' => {'tex' => ' ', 'web' => "\x{2003}"}, # em space. + '(thinsp|#8201)' => {'tex' => ' ', 'web' => "\x{2009}"}, # thin space. + '(zwnj|#8204)' => {'tex' => ' ', 'web' => "\x{200c}"}, # Zero width non joiner. + '(zwj|#8205)' => {'tex' => ' ', 'web' => "\x{200d}"}, # Zero width joiner. + '(lrm|#8206)' => {'tex' => ' ', 'web' => "\x{200e}"}, # Left to right mark + '(rlm|#8207)' => {'tex' => ' ', 'web' => "\x{200f}"}, # right to left mark. + '(ndash|#8211)' => {'tex' => '{/Text \55}', 'web' => "\x{2013}"}, # en dash. + '(mdash|#8212)' => {'tex' => '{/Symbol \55}', 'web' => "\x{2014}"}, # em dash. + '(lsquo|#8216)' => {'tex' => '{/Text \140}', 'web' => "\x{2018}"}, # Left single quote. + '(rsquo|#8217)' => {'tex' => '\47', 'web' => "\x{2019}"}, # Right single quote. + '(sbquo|#8218)' => {'tex' => '\54', 'web' => "\x{201a}"}, # Single low-9 quote. + '(ldquo|#8220)' => {'tex' => '\42', 'web' => "\x{201c}"}, # Left double quote. + '(rdquo|#8221)' => {'tex' => '\42', 'web' => "\x{201d}"}, # Right double quote. + '(bdquo|#8222)' => {'tex' => ',', 'web' => "\x{201e}"}, # Double low-9 quote. + '(dagger|#8224)' => {'tex' => '+', 'web' => "\x{2020}"}, # Is this a dagger I see before me now? + '(Dagger|#8225)' => {'tex' => '\261', 'web' => "\x{2021}"}, # it's handle pointing towards my heart? + '(bull|#8226)' => {'tex' => '\267', 'web' => "\x{2022}"}, # Bullet. + '(hellep|#8230)' => {'tex' => '{/Symbol \274}', 'web' => "\x{2026}"}, # Ellipses. + '(permil|#8240)' => {'tex' => '%_o', 'web' => "\x{2031}"}, # Per mille. + '(prime|#8242)' => {'tex' => '\264', 'web' => "\x{2032}"}, # Prime. + '(Prime|#8243)' => {'tex' => '{/Symbol \262}', 'web' => "\x{2033}"}, # double prime. + '(lsaquo|#8249)' => {'tex' => '<', 'web' => "\x{2039}"}, # < quote. + '(rsaquo|#8250)' => {'tex' => '\74', 'web' => "\x{203a}"}, # > quote. + '(oline|#8254)' => {'tex' => '{/Symbol \140}', 'web' => "\x{203e}"}, # Overline. + '(frasl|#8260)' => {'tex' => '/', 'web' => "\x{2044}"}, # Fraction slash. + '(euro|#8364)' => {'tex' => '{/Symbol \240}', 'web' => "\x{20ac}"}, # Euro currency. + + # Letter like symbols. + + '(weierp|#8472)' => {'tex' => '{/Symbol \303}', 'web' => "\x{2118}"}, # Power set symbol + '(image|#8465)' => {'tex' => '{/Symbol \301}', 'web' => "\x{2111}"}, # Imaginary part + '(real|#8476)' => {'tex' => '{/Symbol \302}', 'web' => "\x{211c}"}, # Real part. + '(trade|#8482)' => {'tex' => '{/Symbol \344}', 'web' => "\x{2122}"}, # trademark symbol. + '(alefsym|#8501)' => {'tex' => '{/Symbol \300}', 'web' => "\x{2135}"}, # Hebrew alef. + + # Arrows of various types and directions. + '(larr|#8592)' => {'tex' => '{/Symbol \254}', 'web' => "\x{2190}"}, # <-- + '(uarr|#8593)' => {'tex' => '{/Symbol \255}', 'web' => "\x{2191}"}, # up arrow. + '(rarr|#8594)' => {'tex' => '{/Symbol \256}', 'web' => "\x{2192}"}, # --> + '(darr|#8595)' => {'tex' => '{/Symbol \257}', 'web' => "\x{2193}"}, # down arrow. + '(harr|#8596)' => {'tex' => '{/Symbol \253}', 'web' => "\x{2194}"}, # <--> + '(crarr|#8629)' => {'tex' => '{/Symbol \277}', 'web' => "\x{21b5}"}, # corner arrow down and right. + '(lArr|#8656)' => {'tex' => '{/Symbol \334}', 'web' => "\x{21d0}"}, # <== + '(uArr|#8657)' => {'tex' => '{/Symbol \335}', 'web' => "\x{21d1}"}, # Up double arrow. + '(rArr|#8658)' => {'tex' => '{/Symbol \336}', 'web' => "\x{21d2}"}, # ==> + '(dArr|#8659)' => {'tex' => '{/Symbol \337}', 'web' => "\x{21d3}"}, # Down double arrow. + '(hArr|#8660)' => {'tex' => '{/Symbol \333}', 'web' => "\x{21d4}"}, # <==> + + # Mathematical operators. For some of these we do the best we can in printing. + + '(forall|#8704)' => {'tex' => '{/Symbol \42}', 'web' => "\x{2200}"}, # For all. + '(part|#8706)' => {'tex' => '{/Symbol d}', 'web' => "\x{2202}"}, # partial derivative + '(exist|#8707)' => {'tex' => '{/Symbol \44}', 'web' => "\x{2203}"}, # There exists. + '(empty|#8709)' => {'tex' => '{/Symbol \306}', 'web' => "\x{2205}"}, # Null set. + '(nabla|#8711)' => {'tex' => '{/Symbol \321}', 'web' => "\x{2207}"}, # Gradient e.g. + '(isin|#8712)' => {'tex' => '{/Symbol \316}', 'web' => "\x{2208}"}, # Element of the set. + '(notin|#8713)' => {'tex' => '{/Symbol \317}', 'web' => "\x{2209}"}, # Not an element of + '(ni|#8715)' => {'tex' => '{/Symbol \47}', 'web' => "\x{220b}"}, # Contains as a member + '(prod|#8719)' => {'tex' => '{/Symbol \325}', 'web' => "\x{220f}"}, # Product + '(sum|#8721)' => {'tex' => '{/Symbol \345}', 'web' => "\x{2211}"}, # Sum of. + '(minus|#8722)' => {'tex' => '{/Symbol \55}', 'web' => "\x{2212}"}, # - sign. + '(lowast|#8727)' => {'tex' => '*', 'web' => "\x{2217}"}, # * + '(radic|#8730)' => {'tex' => '{/Symbol \326}', 'web' => "\x{221a}"}, # Square root. + '(prop|#8733)' => {'tex' => '{/Symbol \265}', 'web' => "\x{221d}"}, # Proportional to. + '(infin|#8734)' => {'tex' => '{/Symbol \245}', 'web' => "\x{221e}"}, # Infinity. + '(ang|#8736)' => {'tex' => '{/Symbol \320}', 'web' => "\x{2220}"}, # Angle . + '(and|#8743)' => {'tex' => '{/Symbol \331}', 'web' => "\x{2227}"}, # Logical and. + '(or|#8744)' => {'tex' => '{/Symbol \332}', 'web' => "\x{2228}"}, # Logical or. + '(cap|#8745)' => {'tex' => '{/Symbol \307}', 'web' => "\x{2229}"}, # Set intersection. + '(cup|#8746)' => {'tex' => '{/Symbol \310}', 'web' => "\x{222a}"}, # Set union. + '(int|8747)' => {'tex' => '{/Symbol \362}', 'web' => "\x{222b}"}, # Integral. + + # Some gnuplot guru will have to explain to me why the next three + # require the extra slashes... else they print very funkily. + + '(there4|#8756)' => {'tex' => '{/Symbol \\\134}', 'web' => "\x{2234}"}, # Therefore triple dots. + '(sim|#8764)' => {'tex' => '\\\176', 'web' => "\x{223c}"}, # Simlar to. + '(cong|#8773)' => {'tex' => '{/Symbol \\\100}','web' => "\x{2245}"}, # Congruent to/with. + + '(asymp|#8776)' => {'tex' => '{/Symbol \273}', 'web' => "\x{2248}"}, # Asymptotic to. + '(ne|#8800)' => {'tex' => '{/Symbol \271}', 'web' => "\x{2260}"}, # not equal to. + '(equiv|#8801)' => {'tex' => '{/Symbol \272}', 'web' => "\x{2261}"}, # Equivalent to. + '(le|8804)' => {'tex' => '{/Symbol \243}', 'web' => "\x{2264}"}, # Less than or equal to. + '(ge|8805)' => {'tex' => '{/Symbol \263}', 'web' => "\x{2265}"}, # Greater than or equal to + '(sub|8834)' => {'tex' => '{/Symbol \314}', 'web' => "\x{2282}"}, # Subset of. + '(sup|8835)' => {'tex' => '{/Symbol \311}', 'web' => "\x{2283}"}, # Super set of. + '(nsub|8836)' => {'tex' => '{/Symbol \313}', 'web' => "\x{2284}"}, # not subset of. + '(sube|8838)' => {'tex' => '{/Symbol \315}', 'web' => "\x{2286}"}, # Subset or equal. + '(supe|8839)' => {'tex' => '{/Symbol \312}', 'web' => "\x{2287}"}, # Superset or equal + '(oplus|8853)' => {'tex' => '{/Symbol \305}', 'web' => "\x{2295}"}, # O with plus inside + '(otimes|8855)' => {'tex' => '{/Symbol \304}', 'web' => "\x{2297}"}, # O with times. + '(perp|8869)' => {'tex' => '{/Symbol \136}', 'web' => "\x{22a5}"}, # Perpendicular. + '(sdot|8901)' => {'tex' => '{/Symbol \227}', 'web' => "\x{22c5}"}, # Dot operator. + + # Misc. technical symbols: + + '(lceil|8698)' => {'tex' => '{/Symbol \351}', 'web' => "\x{2308}"}, # Left ceiling. + '(rceil|8969)' => {'tex' => '{/Symbol \371}', 'web' => "\x{2309}"}, # Right ceiling. + '(lfloor|8970)' => {'tex' => '{/Symbol \353}', 'web' => "\x{230a}"}, # Left floor. + '(rfloor|8971)' => {'tex' => '{/Symbol \373}', 'web' => "\x{230b}"}, # Right floor. + + # The gnuplot png font evidently does not have the big angle brackets at + # positions 0x2329, 0x232a so use ordinary brackets. + + '(lang|9001)' => {'tex' => '{/Symbol \341}', 'web' => '<'}, # Left angle bracket. + '(rang|9002)' => {'tex' => '{/Symbol \361}', 'web' => '>'}, # Right angle bracket. + + # Gemoetric shapes. + + '(loz|9674)' => {'tex' => '{/Symbol \340}', 'web' => "\x{25ca}"}, # Lozenge. + + # Misc. symbols + + '(spades|9824)' => {'tex' => '{/Symbol \252}', 'web' => "\x{2660}"}, + '(clubs|9827)' => {'tex' => '{/Symbol \247}', 'web' => "\x{2663}"}, + '(hearts|9829)' => {'tex' => '{/Symbol \251}', 'web' => "\x{2665}"}, + '(diams|9830)' => {'tex' => '{/Symbol \250}', 'web' => "\x{2666}"} + + ); + + +sub replace_entities { + my ($target,$text) = @_; + $text =~ s{([_^~\{\}]|\\\\)}{\\\\$1}g; + while (my ($re, $replace) = each(%lookup)) { + my $repl = $replace->{$target}; + $text =~ s/&$re;/$replace->{$target}/g; + } + $text =~ s{(&)}{\\\\$1}g; + return $text; +} + ##------------------------------------------------------------------- title sub start_title { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - if ($target eq 'web') { - $title = &Apache::lonxml::get_all_text("/title",$$parser[-1]); + if ($target eq 'web' || $target eq 'tex') { + $title = &Apache::lonxml::get_all_text("/title",$parser,$style); + $title=&Apache::run::evaluate($title,$safeeval,$$parstack[-1]); + $title =~ s/\n/ /g; if (length($title) > $max_str_len) { $title = substr($title,0,$max_str_len); } + $title = &parse_label($target,$title); } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_start($target,$token,'Plot Title'); - my $text=&Apache::lonxml::get_all_text("/title",$$parser[-1]); - $result.=''. - &Apache::edit::editfield('',$text,'',60,1); + my $text=&Apache::lonxml::get_all_text("/title",$parser,$style); + $result.=&Apache::edit::editline('',$text,'',60); } elsif ($target eq 'modified') { - my $text=$$parser[-1]->get_text("/title"); - $result.=&Apache::edit::modifiedfield($token); + $result.=&Apache::edit::rebuild_tag($token); + $result.=&Apache::edit::modifiedfield("/title",$parser); } return $result; } @@ -423,7 +1237,7 @@ sub start_title { sub end_title { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } @@ -433,19 +1247,21 @@ sub end_title { sub start_xlabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - if ($target eq 'web') { - $xlabel = &Apache::lonxml::get_all_text("/xlabel",$$parser[-1]); + if ($target eq 'web' || $target eq 'tex') { + $xlabel = &Apache::lonxml::get_all_text("/xlabel",$parser,$style); + $xlabel=&Apache::run::evaluate($xlabel,$safeeval,$$parstack[-1]); + $xlabel =~ s/\n/ /g; if (length($xlabel) > $max_str_len) { $xlabel = substr($xlabel,0,$max_str_len); } + $xlabel = &parse_label($target,$xlabel); } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_start($target,$token,'Plot Xlabel'); - my $text=&Apache::lonxml::get_all_text("/xlabel",$$parser[-1]); - $result.=''. - &Apache::edit::editfield('',$text,'',60,1); + my $text=&Apache::lonxml::get_all_text("/xlabel",$parser,$style); + $result.=&Apache::edit::editline('',$text,'',60); } elsif ($target eq 'modified') { - my $text=$$parser[-1]->get_text("/xlabel"); - $result.=&Apache::edit::modifiedfield($token); + $result.=&Apache::edit::rebuild_tag($token); + $result.=&Apache::edit::modifiedfield("/xlabel",$parser); } return $result; } @@ -453,7 +1269,7 @@ sub start_xlabel { sub end_xlabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } @@ -464,19 +1280,21 @@ sub end_xlabel { sub start_ylabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - if ($target eq 'web') { - $ylabel = &Apache::lonxml::get_all_text("/ylabel",$$parser[-1]); + if ($target eq 'web' || $target eq 'tex') { + $ylabel = &Apache::lonxml::get_all_text("/ylabel",$parser,$style); + $ylabel = &Apache::run::evaluate($ylabel,$safeeval,$$parstack[-1]); + $ylabel =~ s/\n/ /g; if (length($ylabel) > $max_str_len) { $ylabel = substr($ylabel,0,$max_str_len); } + $ylabel = &parse_label($target,$ylabel); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token,'Plot Ylabel'); - my $text = &Apache::lonxml::get_all_text("/ylabel",$$parser[-1]); - $result .= ''. - &Apache::edit::editfield('',$text,'',60,1); + my $text = &Apache::lonxml::get_all_text("/ylabel",$parser,$style); + $result .= &Apache::edit::editline('',$text,'',60); } elsif ($target eq 'modified') { - my $text=$$parser[-1]->get_text("/ylabel"); - $result.=&Apache::edit::modifiedfield($token); + $result.=&Apache::edit::rebuild_tag($token); + $result.=&Apache::edit::modifiedfield("/ylabel",$parser); } return $result; } @@ -484,7 +1302,7 @@ sub start_ylabel { sub end_ylabel { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } @@ -495,29 +1313,28 @@ sub end_ylabel { sub start_label { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { my %label; &get_attributes(\%label,\%label_defaults,$parstack,$safeeval, $tagstack->[-1]); - my $text = &Apache::lonxml::get_all_text("/label",$$parser[-1]); + my $text = &Apache::lonxml::get_all_text("/label",$parser,$style); + $text = &Apache::run::evaluate($text,$safeeval,$$parstack[-1]); + $text =~ s/\n/ /g; $text = substr($text,0,$max_str_len) if (length($text) > $max_str_len); - $label{'text'} = $text; + $label{'text'} = &parse_label($target,$text); push(@labels,\%label); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token,'Plot Label'); $result .= &edit_attributes($target,$token,\%label_defaults); - my $text = &Apache::lonxml::get_all_text("/label",$$parser[-1]); - $result .= ''. - &Apache::edit::editfield('',$text,'',60,1); + my $text = &Apache::lonxml::get_all_text("/label",$parser,$style); + $result .= &Apache::edit::end_row(). + &Apache::edit::start_spanning_row(). + &Apache::edit::editline('',$text,'',60); } elsif ($target eq 'modified') { - my $constructtag=&Apache::edit::get_new_args + &Apache::edit::get_new_args ($token,$parstack,$safeeval,keys(%label_defaults)); - if ($constructtag) { - $result = &Apache::edit::rebuild_tag($token); - $result.= &Apache::edit::handle_insert(); - } - my $text=$$parser[-1]->get_text("/label"); - $result.=&Apache::edit::modifiedfield($token); + $result.=&Apache::edit::rebuild_tag($token); + $result.=&Apache::edit::modifiedfield("/label",$parser); } return $result; } @@ -525,7 +1342,7 @@ sub start_label { sub end_label { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } @@ -538,20 +1355,30 @@ sub start_curve { my $result=''; &Apache::lonxml::register('Apache::lonplot',('function','data')); push (@Apache::lonxml::namespace,'curve'); - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { my %curve; &get_attributes(\%curve,\%curve_defaults,$parstack,$safeeval, $tagstack->[-1]); push (@curves,\%curve); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token,'Curve'); - $result .= &edit_attributes($target,$token,\%curve_defaults); + $result .= &edit_attributes($target,$token,\%curve_defaults, + \@curve_edit_order) + .&Apache::edit::end_row() + .&Apache::edit::start_spanning_row(); + } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args ($token,$parstack,$safeeval,keys(%curve_defaults)); if ($constructtag) { + # + # Fix up the color attribute as jcolor does not prepend an x + # + my $value = $token->[2]{'color'}; + if (defined $value && !($value =~ /^x/)) { + $token->[2]{'color'} = 'x' . $value; + } $result = &Apache::edit::rebuild_tag($token); - $result.= &Apache::edit::handle_insert(); } } return $result; @@ -562,7 +1389,7 @@ sub end_curve { my $result = ''; pop @Apache::lonxml::namespace; &Apache::lonxml::deregister('Apache::lonplot',('function','data')); - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } @@ -573,22 +1400,27 @@ sub end_curve { sub start_function { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { if (exists($curves[-1]->{'data'})) { - &Apache::lonxml::warning('Use of precludes use of . The will be omitted in favor of the declaration.'); + &Apache::lonxml::warning + ('Use of the curve function tag precludes use of '. + ' the curve data tag. '. + 'The curve data tag will be omitted in favor of the '. + 'curve function declaration.'); delete $curves[-1]->{'data'} ; } - $curves[-1]->{'function'} = - &Apache::lonxml::get_all_text("/function",$$parser[-1]); + my $function = &Apache::lonxml::get_all_text("/function",$parser, + $style); + $function = &Apache::run::evaluate($function,$safeeval,$$parstack[-1]); + $function=~s/\^/\*\*/gs; + $curves[-1]->{'function'} = $function; } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token,'Gnuplot compatible curve function'); - my $text = &Apache::lonxml::get_all_text("/function",$$parser[-1]); - $result .= ''. - &Apache::edit::editfield('',$text,'',60,1); - } elsif ($target eq 'modified') { - # Why do I do this? - my $text=$$parser[-1]->get_text("/function"); - $result.=&Apache::edit::modifiedfield($token); + my $text = &Apache::lonxml::get_all_text("/function",$parser,$style); + $result .= &Apache::edit::editline('',$text,'',60); + } elsif ($target eq 'modified') { + $result.=&Apache::edit::rebuild_tag($token); + $result.=&Apache::edit::modifiedfield("/function",$parser); } return $result; } @@ -596,7 +1428,7 @@ sub start_function { sub end_function { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result .= &Apache::edit::end_table(); } @@ -607,43 +1439,51 @@ sub end_function { sub start_data { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { if (exists($curves[-1]->{'function'})) { - &Apache::lonxml::warning('Use of precludes use of .'. - '. The will be omitted in favor of '. - 'the declaration.'); + &Apache::lonxml::warning + ('Use of the curve function tag precludes use of '. + ' the curve data tag. '. + 'The curve function tag will be omitted in favor of the '. + 'curve data declaration.'); delete($curves[-1]->{'function'}); } - my $datatext = &Apache::lonxml::get_all_text("/data",$$parser[-1]); - $datatext =~ s/\s+/ /g; + my $datatext = &Apache::lonxml::get_all_text("/data",$parser,$style); + $datatext=&Apache::run::evaluate($datatext,$safeeval,$$parstack[-1]); + # Deal with cases where we're given an array... + if ($datatext =~ /^\@/) { + $datatext = &Apache::run::run('return "'.$datatext.'"', + $safeeval,1); + } + $datatext =~ s/\s+/ /g; # Need to do some error checking on the @data array - # make sure it's all numbers and make sure each array # is of the same length. my @data; if ($datatext =~ /,/) { # comma deliminated @data = split /,/,$datatext; - } else { # Assume it's space seperated. + } else { # Assume it's space separated. @data = split / /,$datatext; } for (my $i=0;$i<=$#data;$i++) { # Check that it's non-empty if (! defined($data[$i])) { &Apache::lonxml::warning( - 'undefined value. Replacing with '. + 'undefined curve data value. Replacing with '. ' pi/e = 1.15572734979092'); $data[$i] = 1.15572734979092; } # Check that it's a number if (! &$real_test($data[$i]) & ! &$int_test($data[$i])) { &Apache::lonxml::warning( - 'Bad value of '.$data[$i].' Replacing with '. + 'Bad curve data value of '.$data[$i].' Replacing with '. ' pi/e = 1.15572734979092'); $data[$i] = 1.15572734979092; } } # complain if the number of data points is not the same as # in previous sets of data. - if (($curves[-1]->{'data'}) && ($#data != $#{@{$curves[-1]->{'data'}->[0]}})){ + if (($curves[-1]->{'data'}) && ($#data != $#{$curves[-1]->{'data'}->[0]})){ &Apache::lonxml::warning ('Number of data points is not consistent with previous '. 'number of data points'); @@ -651,12 +1491,11 @@ sub start_data { push @{$curves[-1]->{'data'}},\@data; } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token,'Comma or space deliminated curve data'); - my $text = &Apache::lonxml::get_all_text("/data",$$parser[-1]); - $result .= ''. - &Apache::edit::editfield('',$text,'',60,1); + my $text = &Apache::lonxml::get_all_text("/data",$parser,$style); + $result .= &Apache::edit::editline('',$text,'',60); } elsif ($target eq 'modified') { - my $text=$$parser[-1]->get_text("/data"); - $result.=&Apache::edit::modifiedfield($token); + $result.=&Apache::edit::rebuild_tag($token); + $result.=&Apache::edit::modifiedfield("/data",$parser); } return $result; } @@ -664,7 +1503,7 @@ sub start_data { sub end_data { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result .= &Apache::edit::end_table(); } @@ -675,18 +1514,28 @@ sub end_data { sub start_axis { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { &get_attributes(\%axis,\%axis_defaults,$parstack,$safeeval, $tagstack->[-1]); } elsif ($target eq 'edit') { $result .= &Apache::edit::tag_start($target,$token,'Plot Axes'); - $result .= &edit_attributes($target,$token,\%axis_defaults); + $result .= &edit_attributes($target,$token,\%axis_defaults, + \@axis_edit_order); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args ($token,$parstack,$safeeval,keys(%axis_defaults)); + if ($constructtag) { + # + # Fix up the color attribute since jchooser does not + # prepend an x to the color: + # + my $value = $token->[2]{'color'}; + if (defined $value && !($value =~ /^x/)) { + $token->[2]{'color'} = 'x' . $value; + } + $result = &Apache::edit::rebuild_tag($token); - $result.= &Apache::edit::handle_insert(); } } return $result; @@ -695,7 +1544,7 @@ sub start_axis { sub end_axis { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ''; - if ($target eq 'web') { + if ($target eq 'web' || $target eq 'tex') { } elsif ($target eq 'edit') { $result.=&Apache::edit::tag_end($target,$token); } elsif ($target eq 'modified') { @@ -722,8 +1571,13 @@ sub set_defaults { sub get_attributes{ my ($values,$defaults,$parstack,$safeeval,$tag) = @_; foreach my $attr (keys(%{$defaults})) { - $values->{$attr} = - &Apache::lonxml::get_param($attr,$parstack,$safeeval); + if ($attr eq 'texwidth' || $attr eq 'texfont') { + $values->{$attr} = + &Apache::lonxml::get_param($attr,$parstack,$safeeval,undef,1); + } else { + $values->{$attr} = + &Apache::lonxml::get_param($attr,$parstack,$safeeval); + } if ($values->{$attr} eq '' | !defined($values->{$attr})) { $values->{$attr} = $defaults->{$attr}->{'default'}; next; @@ -738,39 +1592,224 @@ sub get_attributes{ } return ; } + ##------------------------------------------------------- write_gnuplot_file sub write_gnuplot_file { + my ($tmpdir,$filename,$target)= @_; + my ($fontsize, $font_properties) = &get_font($target); my $gnuplot_input = ''; my $curve; + # + # Check to be sure we do not have any empty curves + my @curvescopy; + foreach my $curve (@curves) { + if (exists($curve->{'function'})) { + if ($curve->{'function'} !~ /^\s*$/) { + push(@curvescopy,$curve); + } + } elsif (exists($curve->{'data'})) { + foreach my $data (@{$curve->{'data'}}) { + if (scalar(@$data) > 0) { + push(@curvescopy,$curve); + last; + } + } + } + } + @curves = @curvescopy; # Collect all the colors my @Colors; - push @Colors, $plot{'bgcolor'}; - push @Colors, $plot{'fgcolor'}; - push @Colors, (defined($axis{'color'})?$axis{'color'}:$plot{'fgcolor'}); + push @Colors, $Apache::lonplot::plot{'bgcolor'}; + push @Colors, $Apache::lonplot::plot{'fgcolor'}; + push @Colors, (defined($axis{'color'})?$axis{'color'}:$Apache::lonplot::plot{'fgcolor'}); foreach $curve (@curves) { push @Colors, ($curve->{'color'} ne '' ? $curve->{'color'} : - $plot{'fgcolor'} ); + $Apache::lonplot::plot{'fgcolor'} ); } + # set term - $gnuplot_input .= 'set term gif '; - $gnuplot_input .= 'transparent ' if ($plot{'transparent'} eq 'on'); - $gnuplot_input .= $plot{'font'} . ' '; - $gnuplot_input .= 'size '.$plot{'width'}.','.$plot{'height'}.' '; - $gnuplot_input .= "@Colors\n"; + if ($target eq 'web') { + $gnuplot_input .= 'set terminal png enhanced nocrop '; + $gnuplot_input .= 'transparent ' if ($Apache::lonplot::plot{'transparent'} eq 'on'); + $gnuplot_input .= 'font "'.$Apache::lonnet::perlvar{'lonFontsDir'}. + '/'.$font_properties->{'file'}.'.ttf" '; + $gnuplot_input .= $fontsize; + $gnuplot_input .= ' size '.$Apache::lonplot::plot{'width'}.','.$Apache::lonplot::plot{'height'}.' '; + $gnuplot_input .= "@Colors\n"; + # set output + $gnuplot_input .= "set output\n"; + } elsif ($target eq 'tex') { + $gnuplot_input .= "set term postscript eps enhanced $Apache::lonplot::plot{'plotcolor'} dash "; + if (!$font_properties->{'tex_no_file'}) { + $gnuplot_input .= + 'fontfile "'.$Apache::lonnet::perlvar{'lonFontsDir'}. + '/'.$font_properties->{'file'}.'.pfb" '; + } + $gnuplot_input .= ' "'.$font_properties->{'printname'}.'" '; + $gnuplot_input .= $fontsize; + $gnuplot_input .= "\nset output \"".$tmpdir. + &unescape($filename).".eps\"\n"; + $gnuplot_input .= "set encoding iso_8859_1\n"; # Get access to extended font. + + } + $gnuplot_input .= "set encoding utf8\n"; + # cartesian or polar plot? + if (lc($Apache::lonplot::plot{'plottype'}) eq 'polar') { + $gnuplot_input .= 'set polar'.$/; + } else { + # Assume Cartesian + } + # cartesian or polar grid? + if (lc($Apache::lonplot::plot{'gridtype'}) eq 'polar') { + $gnuplot_input .= 'set grid polar'.$/; + } elsif (lc($Apache::lonplot::plot{'gridtype'}) eq 'linear-log') { + $gnuplot_input .= 'set logscale x'.$/; + } elsif (lc($Apache::lonplot::plot{'gridtype'}) eq 'log-linear') { + $gnuplot_input .= 'set logscale y'.$/; + } elsif (lc($Apache::lonplot::plot{'gridtype'}) eq 'log-log') { + $gnuplot_input .= 'set logscale x'.$/; + $gnuplot_input .= 'set logscale y'.$/; + } else { + # Assume Cartesian + } + # solid or pattern for boxes? + if (lc($Apache::lonplot::plot{'fillstyle'}) eq 'solid') { + $gnuplot_input .= 'set style fill solid '. + $Apache::lonplot::plot{'solid'}.$Apache::lonplot::plot{'box_border'}.$/; + } elsif (lc($Apache::lonplot::plot{'fillstyle'}) eq 'pattern') { + $gnuplot_input .= 'set style fill pattern '.$Apache::lonplot::plot{'pattern'}.$Apache::lonplot::plot{'box_border'}.$/; + } elsif (lc($Apache::lonplot::plot{'fillstyle'}) eq 'empty') { + } + # margin + if (lc($Apache::lonplot::plot{'lmargin'}) ne 'default') { + $gnuplot_input .= 'set lmargin '.$Apache::lonplot::plot{'lmargin'}.$/; + } + if (lc($Apache::lonplot::plot{'rmargin'}) ne 'default') { + $gnuplot_input .= 'set rmargin '.$Apache::lonplot::plot{'rmargin'}.$/; + } + if (lc($Apache::lonplot::plot{'tmargin'}) ne 'default') { + $gnuplot_input .= 'set tmargin '.$Apache::lonplot::plot{'tmargin'}.$/; + } + if (lc($Apache::lonplot::plot{'bmargin'}) ne 'default') { + $gnuplot_input .= 'set bmargin '.$Apache::lonplot::plot{'bmargin'}.$/; + } + + # tic scales + if ($version > 4) { + $gnuplot_input .= 'set tics scale '. + $Apache::lonplot::plot{'major_ticscale'}.', '.$Apache::lonplot::plot{'minor_ticscale'}.$/; + } else { + $gnuplot_input .= 'set ticscale '. + $Apache::lonplot::plot{'major_ticscale'}.' '.$Apache::lonplot::plot{'minor_ticscale'}.$/; + } + #boxwidth + if (lc($Apache::lonplot::plot{'boxwidth'}) ne '') { + $gnuplot_input .= 'set boxwidth '.$Apache::lonplot::plot{'boxwidth'}.$/; + } + # gridlayer + $gnuplot_input .= 'set grid noxtics noytics front '.$/ + if ($Apache::lonplot::plot{'gridlayer'} eq 'on'); + # grid - $gnuplot_input .= 'set grid'.$/ if ($plot{'grid'} eq 'on'); + $gnuplot_input .= 'set grid'.$/ if ($Apache::lonplot::plot{'grid'} eq 'on'); # border - $gnuplot_input .= ($plot{'border'} eq 'on'? + $gnuplot_input .= ($Apache::lonplot::plot{'border'} eq 'on'? 'set border'.$/ : - 'set noborder'.$/ ); # title, xlabel, ylabel - $gnuplot_input .= "set output\n"; - $gnuplot_input .= "set title \"$title\"\n" if (defined($title)) ; - $gnuplot_input .= "set xlabel \"$xlabel\"\n" if (defined($xlabel)); - $gnuplot_input .= "set ylabel \"$ylabel\"\n" if (defined($ylabel)); + 'set noborder'.$/ ); + # sampling rate for non-data curves + $gnuplot_input .= "set samples $Apache::lonplot::plot{'samples'}\n"; + # title, xlabel, ylabel + # titles + my $extra_space_x = ($xtics{'location'} eq 'axis') ? ' 0, -0.5 ' : ''; + my $extra_space_y = ($ytics{'location'} eq 'axis') ? ' -0.5, 0 ' : ''; + + if ($target eq 'tex') { + $gnuplot_input .= "set title \"$title\" font \"".$font_properties->{'printname'}.",".$fontsize."pt\"\n" if (defined($title)) ; + $gnuplot_input .= "set xlabel \"$xlabel\" $extra_space_x font \"".$font_properties->{'printname'}.",".$fontsize."pt\"\n" if (defined($xlabel)); + $gnuplot_input .= "set ylabel \"$ylabel\" $extra_space_y font \"".$font_properties->{'printname'}.",".$fontsize."pt\"\n" if (defined($ylabel)); + } else { + $gnuplot_input .= "set title \"$title\" \n" if (defined($title)) ; + $gnuplot_input .= "set xlabel \"$xlabel\" $extra_space_x \n" if (defined($xlabel)); + $gnuplot_input .= "set ylabel \"$ylabel\" $extra_space_y \n" if (defined($ylabel)); + } + # tics + if (%xtics) { + $gnuplot_input .= "set xtics $xtics{'location'} "; + $gnuplot_input .= ( $xtics{'mirror'} eq 'on'?"mirror ":"nomirror "); + $gnuplot_input .= "$xtics{'start'}, "; + $gnuplot_input .= "$xtics{'increment'}, "; + $gnuplot_input .= "$xtics{'end'} "; + if ($target eq 'tex') { + $gnuplot_input .= 'font "Helvetica,22"'; # Needed in iso 8859-1 enc. + } + $gnuplot_input .= "\n"; + if ($xtics{'minorfreq'} != 0) { + $gnuplot_input .= "set mxtics ".$xtics{'minorfreq'}."\n"; + } + } else { + if ($target eq 'tex') { + $gnuplot_input .= 'set xtics font "Helvetica,22"'."\n"; # needed in iso 8859-1 enc + } + } + if (%ytics) { + $gnuplot_input .= "set ytics $ytics{'location'} "; + $gnuplot_input .= ( $ytics{'mirror'} eq 'on'?"mirror ":"nomirror "); + $gnuplot_input .= "$ytics{'start'}, "; + $gnuplot_input .= "$ytics{'increment'}, "; + $gnuplot_input .= "$ytics{'end'} "; + if ($target eq 'tex') { + $gnuplot_input .= 'font "Helvetica,22"'; # Needed in iso-8859-1 encoding. + } + $gnuplot_input .= "\n"; + if ($ytics{'minorfreq'} != 0) { + $gnuplot_input .= "set mytics ".$ytics{'minorfreq'}."\n"; + } + } else { + if ($target eq 'tex') { + $gnuplot_input .= 'set ytics font "Helvetica,22"'."\n"; # Needed for iso 8859-1 enc. + } + } + # axis if (%axis) { + if ($axis{'xformat'} ne 'on') { + $gnuplot_input .= "set format x "; + if ($axis{'xformat'} eq 'off') { + $gnuplot_input .= "\"\"\n"; + } else { + $gnuplot_input .= "\"\%.".$axis{'xformat'}."\"\n"; + } + } + if ($axis{'yformat'} ne 'on') { + $gnuplot_input .= "set format y "; + if ($axis{'yformat'} eq 'off') { + $gnuplot_input .= "\"\"\n"; + } else { + $gnuplot_input .= "\"\%.".$axis{'yformat'}."\"\n"; + } + } $gnuplot_input .= "set xrange \[$axis{'xmin'}:$axis{'xmax'}\]\n"; $gnuplot_input .= "set yrange \[$axis{'ymin'}:$axis{'ymax'}\]\n"; + if ($axis{'xzero'} ne 'off') { + $gnuplot_input .= "set xzeroaxis "; + if ($axis{'xzero'} eq 'line' || $axis{'xzero'} eq 'thick-line') { + $gnuplot_input .= "lt -1 "; + if ($axis{'xzero'} eq 'thick-line') { + $gnuplot_input .= "lw 3 "; + } + } + $gnuplot_input .= "\n"; + } + if ($axis{'yzero'} ne 'off') { + $gnuplot_input .= "set yzeroaxis "; + if ($axis{'yzero'} eq 'line' || $axis{'yzero'} eq 'thick-line') { + $gnuplot_input .= "lt -1 "; + if ($axis{'yzero'} eq 'thick-line') { + $gnuplot_input .= "lw 3 "; + } + } + $gnuplot_input .= "\n"; + } } # Key if (%key) { @@ -786,23 +1825,53 @@ sub write_gnuplot_file { my $label; foreach $label (@labels) { $gnuplot_input .= 'set label "'.$label->{'text'}.'" at '. - $label->{'xpos'}.','.$label->{'ypos'}.' '.$label->{'justify'}.$/ ; + $label->{'xpos'}.','.$label->{'ypos'}; + if ($label->{'rotate'} ne '') { + $gnuplot_input .= ' rotate by '.$label->{'rotate'}; + } + $gnuplot_input .= ' '.$label->{'justify'}; + + if ($target eq 'tex') { + $gnuplot_input .=' font "'.$font_properties->{'printname'}.','.$fontsize.'pt"' ; + } + $gnuplot_input .= $/; + } + if ($target eq 'tex') { + $gnuplot_input .="set size 1,".$Apache::lonplot::plot{'height'}/$Apache::lonplot::plot{'width'}*1.38; + $gnuplot_input .="\n"; } # curves - $gnuplot_input .= 'plot '; - my $datatext = ''; + # + # Each curve will have its very own linestyle. + # (This should work just fine in web rendition I think). + # The line_xxx variables will hold the elements of the line style. + # type (solid/dashed), color, width + # + my $linestyle_index = 50; + my $line_width = ''; + + my $plot_command; + my $plot_type; + for (my $i = 0;$i<=$#curves;$i++) { $curve = $curves[$i]; - $gnuplot_input.= ', ' if ($i > 0); + $plot_command.= ', ' if ($i > 0); + if ($target eq 'tex') { + $curve->{'linewidth'} *= 2; + } + $line_width = $curve->{'linewidth'}; if (exists($curve->{'function'})) { - $gnuplot_input.= + $plot_type = $curve->{'function'}.' title "'. $curve->{'name'}.'" with '. - $curve->{'linestyle'}; + $curve->{'linestyle'}; } elsif (exists($curve->{'data'})) { - $gnuplot_input.= '\'-\' title "'. - $curve->{'name'}.'" with '. - $curve->{'linestyle'}; + # Store data values in $datatext + my $datatext = ''; + # get new filename + my $datafilename = "$tmpdir/$filename.data.$i"; + my $fh=Apache::File->new(">$datafilename"); + # Compile data my @Data = @{$curve->{'data'}}; my @Data0 = @{$Data[0]}; for (my $i =0; $i<=$#Data0; $i++) { @@ -812,18 +1881,67 @@ sub write_gnuplot_file { } $datatext .= $/; } - $datatext .=$/; + # write file + print $fh $datatext; + close($fh); + # generate gnuplot text + $plot_type = '"'.$datafilename.'" title "'. + $curve->{'name'}.'" with '. + $curve->{'linestyle'}; } - } - $gnuplot_input .= $/.$datatext; - return $gnuplot_input; + my $pointtype = ''; + my $pointsize = ''; + + if (($curve->{'linestyle'} eq 'points') || + ($curve->{'linestyle'} eq 'linespoints') || + ($curve->{'linestyle'} eq 'errorbars') || + ($curve->{'linestyle'} eq 'xerrorbars') || + ($curve->{'linestyle'} eq 'yerrorbars') || + ($curve->{'linestyle'} eq 'xyerrorbars')) { + + $pointtype =' pointtype '.$curve->{'pointtype'}; + $pointsize =' pointsize '.$curve->{'pointsize'}; + } elsif ($curve->{'linestyle'} eq 'filledcurves') { + $plot_command.= ' '.$curve->{'limit'}; + } + + + # Figure out the linestyle: + + my $lt = $curve->{'linetype'} ne '' ? $curve->{'linetype'} + : 'solid'; # Line type defaults to solid. + # The mapping of lt -> the actual gnuplot line type depends on the target: + + if ($target eq 'tex') { + $lt = $ps_linetypes{$lt}; + } else { + $lt = $linetypes{$lt} + } + + my $color = $curve->{'color'}; + $color =~ s/^x/#/; # Convert xhex color -> #hex color. + + my $style_command = "set style line $linestyle_index $pointtype $pointsize linetype $lt linewidth $line_width lc rgb '$color'\n"; + $gnuplot_input .= $style_command; + + $plot_command.= " ls $linestyle_index"; + $gnuplot_input .= 'plot ' . $plot_type . ' ' . $plot_command . "\n"; + $linestyle_index++; # Each curve get a unique linestyle. + } + # Write the output to a file. + open (my $fh,">$tmpdir$filename.data"); + # binmode($fh, ":utf8"); + print $fh $gnuplot_input; + close($fh); + # That's all folks. + return ; } #---------------------------------------------- check_inputs sub check_inputs { ## Note: no inputs, no outputs - this acts only on global variables. ## Make sure we have all the input we need: - if (! %plot) { &set_defaults(\%plot,\%plot_defaults); } + if (! %Apache::lonplot::plot) { &set_defaults(\%Apache::lonplot::plot,\%gnuplot_defaults); } if (! %key ) {} # No key for this plot, thats okay # if (! %axis) { &set_defaults(\%axis,\%axis_defaults); } if (! defined($title )) {} # No title for this plot, thats okay @@ -837,7 +1955,7 @@ sub check_inputs { my $curve; foreach $curve (@curves) { if (!defined($curve->{'function'})&&!defined($curve->{'data'})){ - &Apache::lonxml::warning("One of the curves specified did not contain any or declarations\n"); + &Apache::lonxml::warning("One of the curves specified did not contain any curve data or curve function declarations\n"); return ''; } } @@ -859,12 +1977,13 @@ sub edit_attributes { if ($defaults->{$attr}->{'edit_type'} eq 'entry') { $result .= &Apache::edit::text_arg ($description,$attr,$token, - $defaults->{$attr}->{'size'}); + $defaults->{$attr}->{'size'}, + $defaults->{$attr}->{'class'}); } elsif ($defaults->{$attr}->{'edit_type'} eq 'choice') { - $result .= &Apache::edit::select_arg + $result .= &Apache::edit::select_or_text_arg ($description,$attr,$defaults->{$attr}->{'choices'},$token); - } elsif ($defaults->{$attr}->{'edit_type'} eq 'on_off') { - $result .= &Apache::edit::select_arg + } elsif ($defaults->{$attr}->{'edit_type'} eq 'onoff') { + $result .= &Apache::edit::select_or_text_arg ($description,$attr,['on','off'],$token); } $result .= '
'; @@ -879,81 +1998,107 @@ sub edit_attributes { ## ## ################################################################### -#------------------------------------------------ insert_xxxxxxx -sub insert_plot { +sub insert_gnuplot { my $result = ''; # plot attributes - $result .= "{'default'}\"\n"; - } - $result .= ">\n"; - # Add the components - $result .= &insert_key(); - $result .= &insert_axis(); - $result .= &insert_title(); - $result .= &insert_xlabel(); - $result .= &insert_ylabel(); + $result .= "\n{'default'}\""; + } + $result .= ">"; + # Add the components (most are commented out for simplicity) + # $result .= &insert_key(); + # $result .= &insert_axis(); + # $result .= &insert_title(); + # $result .= &insert_xlabel(); + # $result .= &insert_ylabel(); $result .= &insert_curve(); - # close up the - $result .= "\n"; + # close up the + $result .= "\n"; + return $result; +} + +sub insert_tics { + my $result; + $result .= &insert_xtics() . &insert_ytics; + return $result; +} + +sub insert_xtics { + my $result; + $result .= "\n {'default'}\" "; + } + $result .= "/>"; + return $result; +} + +sub insert_ytics { + my $result; + $result .= "\n {'default'}\" "; + } + $result .= "/>"; return $result; } sub insert_key { my $result; - $result .= " {'default'}\"\n"; + $result .= "\n $attr=\"$key_defaults{$attr}->{'default'}\""; } - $result .= " />\n"; + $result .= " />"; return $result; } sub insert_axis{ my $result; - $result .= ' {'default'}\"\n"; + $result .= "\n $attr=\"$axis_defaults{$attr}->{'default'}\""; } - $result .= " />\n"; + $result .= " />"; return $result; } -sub insert_title { return " \n"; } -sub insert_xlabel { return " \n"; } -sub insert_ylabel { return " \n"; } +sub insert_title { return "\n "; } +sub insert_xlabel { return "\n "; } +sub insert_ylabel { return "\n "; } sub insert_label { my $result; - $result .= ' \n"; + $result .= ">"; return $result; } sub insert_curve { my $result; - $result .= ' {'default'}."\""; } - $result .= " >\n"; + $result .= " >"; + $result .= &insert_data().&insert_data()."\n "; } sub insert_function { my $result; - $result .= "\n"; + $result .= "\n "; return $result; } sub insert_data { my $result; - $result .= " \n"; + $result .= "\n "; return $result; } @@ -962,3 +2107,118 @@ sub insert_data { __END__ +=head1 NAME + +Apache::lonplot.pm + +=head1 SYNOPSIS + +XML-based plotter of graphs + +This is part of the LearningOnline Network with CAPA project +described at http://www.lon-capa.org. + + +=head1 SUBROUTINES (parsing and edit rendering) + +=over + +=item start_gnuplot() + +=item end_gnuplot() + +=item start_xtics() + +=item end_xtics() + +=item start_ytics() + +=item end_ytics() + +=item get_font() + +=item start_key() + +=item end_key() + +=item parse_label() + +=item replace_entities() + +=item start_title() + +=item end_title() + +=item start_xlabel() + +=item end_xlabel() + +=item start_ylabel() + +=item end_label() + +=item start_curve() + +=item end_curve() + +=item start_function() + +=item end_function() + +=item start_data() + +=item end_data() + +=item start_axis() + +=item end_axis + +=back + +=head1 SUBROUTINES (Utility) + +=over + +=item set_defaults() + +=item get_attributes() + +=item write_gnuplot_file() + +=item check_inputs() + +=item edit_attributes() + +=back + +=head1 SUBROUTINES (Insertion functions for editing plots) + +=over + +=item insert_gnuplot() + +=item insert_tics() + +=item insert_xtics() + +=item insert_key() + +=item insert_axis() + +=item insert_title() + +=item insert_xlabel() + +=item insert_ylabel() + +=item insert_label() + +=item insert_curve() + +=item insert_function() + +=item insert_data() + +=back + +=cut 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.