home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / lyx21041.zip / XFree86 / lib / X11 / lyx / reLyX / RelyxFigure.pm < prev    next >
Text File  |  1999-04-08  |  10KB  |  292 lines

  1. # This file is part of reLyX
  2. # Copyright (c) 1998-9 Amir Karger karger@post.harvard.edu
  3. # You are free to use and modify this code under the terms of
  4. # the GNU General Public Licence version 2 or later.
  5.  
  6. package RelyxFigure;
  7.  
  8. # This is a package to read LaTeX figures and print out LyX figures
  9.  
  10.  
  11. # We declare here the sub-packages found in this package.
  12. # This allows the parser to understand "indirect object" form of subroutines
  13. #{
  14. #package RelyxTable::Table;
  15. #package RelyxTable::Column;
  16. #package RelyxTable::Row;
  17. #}
  18.  
  19. use strict;
  20.  
  21. # Variables used by other packages
  22. use vars qw($EpsfYsize $EpsfXsize %HW_types);
  23.  
  24. # Debugging on?
  25. my $debug_on;
  26.  
  27. # This means "display in monochrome" and "do translations", but who cares?
  28. # It's just here because this is the default that LyX outputs.
  29. my $Default_Flags = 9;
  30.  
  31. # Names for width_type & height_type fields.
  32. %HW_types = (
  33.     "def"     => 0,
  34.     "cm"     => 1,
  35.     "in"     => 2,
  36.     "per_page" => 3,  # percent of the page
  37.     "per_col"  => 4,  # percent of a column (illegal for height_type)
  38. );
  39.  
  40. # Parse \epsfxsize or \epsfysize command
  41. # Return 0 if we can't convert the length (in which case we have to type
  42. # the \epsf[xy]size command in tex mode).
  43. sub parse_epsfsize {
  44.     # Command & length have already been "stringified" (they're not tokens)
  45.     my ($command, $length) = (shift,shift);
  46.     if ($command eq '\\epsfxsize') {
  47.         $EpsfXsize = $length;
  48.     my @dummy = &convert_length($EpsfXsize) || return 0;
  49.     } elsif ($command eq '\\epsfysize') {
  50.         $EpsfYsize = $length;
  51.     my @dummy = &convert_length($EpsfXsize) || return 0;
  52.     }
  53.  
  54.     return 1;
  55. } # end sub RelyxFIgure::Figure::parse_epsfig
  56.  
  57. sub convert_length {
  58.     # test if it's a valid LyX width/height.
  59.     # (But assume a person won't try to set width to \textheight,
  60.     # and that they won't have negative or otherwise weird lengths)
  61.     # Then convert to width & width_type (or height/height_type)
  62.     # Return empty list on error
  63.     my $size = shift;
  64.  
  65.     # A length can be (optional plus followed by) (num)(unit) where 
  66.     # num is a float number (possibly with European command) and unit
  67.     # is a size unit, either in,cm,pt etc. or \textwidth etc.
  68.     my %unit_convert = (  # 1 inch = 25.4 mm
  69.         "mm" => 25.4, "pt" => 72.27, "bp" => 72, "pc" => 72.27/12,
  70.     "dd" => 72.27*1157/1238, "cc" => 72.27*1157/(1238*12),
  71.     );
  72.     my ($number, $type);
  73.     if ($size =~ /^\+?([\d.,]+)(cm|in)$/) {
  74.         ($number, $type) = ($1, $2);
  75.     $number =~ s/,/./; # Allow european numbers!
  76.     # print "length is $number '$type'";
  77.     } elsif ($size =~ /^\+?([\d.,]+)(mm|pt|bp|pc|dd|cc)$/) {
  78.         ($number, $type) = ($1, $2);
  79.     $number =~ s/,/./;
  80.     $number = $number / $unit_convert{$type};
  81.     $type = "in";
  82.     } elsif ($size =~ /^\+?([\d.,]*)\s*\\text(height|width)$/) {
  83.         $number = $1 || 1;
  84.     $number =~ s/,/./;
  85.     $number *= 100;
  86.     $type = "per_page";
  87.     } elsif ($size =~ /^\+?([\d.,]*)\s*\\columnwidth$/) {
  88.         $number = $1 || 1;
  89.     $number =~ s/,/./;
  90.     $number *= 100;
  91.     $type = "per_col";
  92.     } else {
  93.     print "\ncannot translate length '$size' in Figure; ",
  94.           "copying in TeX mode\n" if $debug_on;
  95.     }
  96.  
  97.     if ($number) {
  98.         return ($number, $type);
  99.     } else {
  100.     return ();
  101.     }
  102. } # end sub convert_length
  103.  
  104. sub parse_keyval {
  105. # Parse a string containing comma-separated "key=value" pairs
  106. # Compare the keys with a list of known keys.
  107. # If we know all keys, return a hash containing keys/values
  108. # Else return undef.
  109.     my ($string, @known_keys) = @_;
  110.     my @fields = split(/\s*,\s*/,$string);
  111.     my %fighash;
  112.     foreach (@fields) { # split "key=val" into fighash{key}=val
  113.     my ($key,$val) = split(/\s*=\s*/,$_);
  114.     $val = "" unless defined $val; # e.g., 'clip='
  115.     $fighash{$key} = $val;
  116.  
  117.     unless (grep /^$key$/, @known_keys)  {
  118.         print "\nUntranslatable key '$key' to figure;",
  119.           " copying in TeX mode\n" if $debug_on;
  120.         return undef;
  121.     }
  122.     }
  123.  
  124.     return \%fighash;
  125. } # end sub parse_keyval
  126.  
  127.  
  128.  
  129. {
  130.     package RelyxFigure::Figure;
  131.     # reLyX figure class
  132.     # Fields:
  133.     #    file        - file name
  134.     #    width       - width
  135.     #    height      - height
  136.     #    width_type  - is width in cm, % of pagewidth, etc.?
  137.     #    height_type - is height in cm, % of pagewidth, etc.?
  138.     #    angle       - rotate fig through angle
  139.     #    flags       - various flags for LyX display. Not important
  140.  
  141.     sub new {
  142.         my $class = shift;
  143.     my $thisfig;
  144.     $thisfig->{'file'} = "";
  145.     $thisfig->{'width'} = 0;
  146.     $thisfig->{'height'} = 0;
  147.     $thisfig->{'width_type'} = "def";
  148.     $thisfig->{'height_type'} = "def";
  149.     $thisfig->{'angle'} = 0;
  150.     $thisfig->{'flags'} = $Default_Flags;
  151.         # This seems like a convenient place to declare this...
  152.         $debug_on= (defined($main::opt_d) && $main::opt_d);
  153.  
  154.     bless $thisfig, $class;
  155.     } # end sub RelyxFigure::Figure::new
  156.  
  157.  
  158.     sub parse_pscommand {
  159.     # this sub is given the various arguments to a command & adds that
  160.     # information to the figure object.
  161.     # Return 0 if it can't read the command, or if LyX can't handle it. Else 1.
  162.     #
  163.     # command is the name of the postscript command
  164.     # optargs are optional arguments (TT:Tokens). For many of the commands,
  165.     #    only one optarg is allowed. And of course, they may be empty tokens.
  166.     # reqarg is usually the filename (for (e)psfig, it's more)
  167.     #
  168.     # Currently, LyX can't handle bounding box, so we always return 0 if one
  169.     # is given.
  170.         my ($self, $command, $optarg1, $optarg2, $reqarg) = @_;
  171.     my (@known_keys, $filename, $keyval_string, %fighash);
  172.  
  173.     for ($command) {
  174.             if (/^\\epsf(file|box)?$/) {
  175.         # syntax: \epsffile[bounding box]{filename.eps}
  176.         # epsffile is an older version of epsfbox
  177.         return 0 if $optarg1->print; # bounding box was given. Panic!
  178.         $filename = &recursive_print($reqarg);
  179.         # fighash key shouldn't exist if no size was given
  180.         $fighash{'height'} = $RelyxFigure::EpsfYsize 
  181.             if $RelyxFigure::EpsfYsize;
  182.         $fighash{'width'} = $RelyxFigure::EpsfXsize
  183.             if $RelyxFigure::EpsfXsize;
  184.         # Once you use \epsf[xy]size, they get reset
  185.         $RelyxFigure::EpsfXsize = $RelyxFigure::EpsfYsize = "";
  186.         $keyval_string = ""; # no key/value pairs for this command
  187.  
  188.         } elsif (/^\\e?psfig$/) {
  189.         # "silent" key doesn't do anything
  190.         @known_keys = qw(file figure rotate angle height width silent);
  191.         $keyval_string = &recursive_print($reqarg);
  192.         my $fighashref = 
  193.                 &RelyxFigure::parse_keyval($keyval_string, @known_keys);
  194.         return 0 unless defined $fighashref; # found unknown key...
  195.         %fighash = %$fighashref;
  196.  
  197.         $filename = $fighash{'file'} || $fighash{'figure'} || warn
  198.             "no filename found in figure argument '$keyval_string'";
  199.  
  200.         } elsif (/^\\includegraphics$/) {
  201.         # 0 optargs can be either graphics or graphicx. Doesn't
  202.         # matter, matter, it's just the filename.
  203.         #    1 optarg can either be graphicx (if arg contains '=') or
  204.         # graphics (optarg is upper right; lower left is 0,0).
  205.         #    2 optargs is graphics with bounding box.
  206.  
  207.         # Optional arguments are always returned as single tokens,
  208.         # not groups. So we can use the print method instead of
  209.         # recursive_print.
  210.         $keyval_string = $optarg1->print;
  211.         if ($keyval_string) {
  212.             if ($keyval_string =~ /=/) { # graphicx form
  213.             $keyval_string =~ s/\[(.*)\]/$1/; # remove '[' and ']'
  214.             @known_keys = qw(rotate angle height width);
  215.             my $fighashref = &RelyxFigure::parse_keyval(
  216.                             $keyval_string, @known_keys);
  217.             return 0 unless defined $fighashref; # found unknown key
  218.             %fighash = %$fighashref;
  219.  
  220.             } else { # graphics form with bounding box
  221.             print "\nLyX cannot support bounding box; ",
  222.                   "copying Figure in TeX mode\n" if $debug_on;
  223.             return 0;
  224.             }
  225.         }
  226.  
  227.         $filename = &recursive_print($reqarg);
  228.  
  229.         }
  230.     } # end switch on command name
  231.  
  232.     # Now set fields in the Figure object
  233.     $self->{'file'} = $filename;
  234.     $self->{'angle'} = $fighash{'rotate'} if exists $fighash{'rotate'};
  235.     $self->{'angle'} = $fighash{'angle'} if exists $fighash{'angle'};
  236.     if (exists $fighash{'height'}) {
  237.         my @heights = &RelyxFigure::convert_length($fighash{'height'});
  238.         return 0 unless @heights; # unconvertable length!
  239.         ($self->{'height'},$self->{'height_type'}) = @heights;
  240.     }
  241.     if (exists $fighash{'width'}) {
  242.         my @widths = &RelyxFigure::convert_length($fighash{'width'});
  243.         return 0 unless @widths; # unconvertable length!
  244.         ($self->{'width'},$self->{'width_type'}) = @widths;
  245.     }
  246.  
  247.     return 1; # if we got here, we parsed correctly and LyX can handle it.
  248.     } # end sub RelyxFigure::Figure::parse_pscommand
  249.  
  250.     sub recursive_print {
  251.     # if arg is a group, print its contents (i.e., ignore '{' and '}')
  252.     # otherwise, print arg
  253.     my $tok = shift;
  254.     my $filename = "";
  255.     my $type = ref($tok);
  256.     $type =~ s/Text::TeX::// or warn "weird group";
  257.     if ($type eq 'Group') {
  258.         foreach ($tok->contents) {$filename .= &recursive_print($_)}
  259.     } else {
  260.         $filename .= $tok->exact_print
  261.     }
  262.     return $filename;
  263.     }
  264.  
  265.     sub print_info {
  266.         # LyX figure command -- return what to print; don't actually print it
  267.         my $self = shift;
  268.  
  269.     my $to_print = "\n\\begin_inset Figure\n";
  270.     # (LyX fig. command has eps size here. But we don't know that, so
  271.     # we dont print it out.)
  272.  
  273.     $to_print .= "file $self->{'file'}\n";
  274.     my ($size, $type) = ("","");
  275.     ($size, $type) = ($self->{'width'}, 
  276.                       $RelyxFigure::HW_types{$self->{'width_type'}});
  277.     $to_print .= "width $type $size\n" if $size;
  278.     ($size, $type) = ("","");
  279.     ($size, $type) = ($self->{'height'}, 
  280.                       $RelyxFigure::HW_types{$self->{'height_type'}});
  281.     $to_print .= "height $type $size\n" if $size;
  282.     $to_print .= "angle $self->{'angle'}\n" if $self->{'angle'};
  283.     $to_print .= "flags $self->{'flags'}\n";
  284.  
  285.     $to_print .= "\n\\end_inset \n\n";
  286.         
  287.     } # end sub RelyxFigure::Figure::print
  288.  
  289. } # end of package RelyxFigure::Figure
  290.  
  291. 1; # return true to calling package
  292.