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.
8 # This is a package to read LaTeX figures and print out LyX figures
11 # We declare here the sub-packages found in this package.
12 # This allows the parser to understand "indirect object" form of subroutines
14 #package RelyxTable::Table;
15 #package RelyxTable::Column;
16 #package RelyxTable::Row;
21 # Variables used by other packages
22 use vars qw($EpsfYsize $EpsfXsize %HW_types);
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;
31 # Names for width_type & height_type fields.
36 "per_page" => 3, # percent of the page
37 "per_col" => 4, # percent of a column (illegal for height_type)
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).
44 # Command & length have already been "stringified" (they're not tokens)
45 my ($command, $length) = (shift,shift);
46 if ($command eq '\\epsfxsize') {
48 my @dummy = &convert_length($EpsfXsize) || return 0;
49 } elsif ($command eq '\\epsfysize') {
51 my @dummy = &convert_length($EpsfYsize) || return 0;
55 } # end sub RelyxFIgure::Figure::parse_epsfig
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
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),
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);
80 $number = $number / $unit_convert{$type};
82 } elsif ($size =~ /^\+?([\d.,]*)\s*\\text(height|width)$/) {
87 } elsif ($size =~ /^\+?([\d.,]*)\s*\\columnwidth$/) {
93 print "\ncannot translate length '$size' in Figure; ",
94 "copying in TeX mode\n" if $debug_on;
98 return ($number, $type);
102 } # end sub convert_length
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
109 my ($string, @known_keys) = @_;
110 my @fields = split(/\s*,\s*/,$string);
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;
117 unless (grep /^$key$/, @known_keys) {
118 print "\nUntranslatable key '$key' to figure;",
119 " copying in TeX mode\n" if $debug_on;
125 } # end sub parse_keyval
130 package RelyxFigure::Figure;
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
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);
154 bless $thisfig, $class;
155 } # end sub RelyxFigure::Figure::new
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.
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)
168 # Currently, LyX can't handle bounding box, so we always return 0 if one
170 my ($self, $command, $optarg1, $optarg2, $reqarg) = @_;
171 my (@known_keys, $filename, $keyval_string, %fighash);
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
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);
193 &RelyxFigure::parse_keyval($keyval_string, @known_keys);
194 return 0 unless defined $fighashref; # found unknown key...
195 %fighash = %$fighashref;
197 $filename = $fighash{'file'} || $fighash{'figure'} || warn
198 "no filename found in figure argument '$keyval_string'";
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.
207 # Optional arguments are always returned as single tokens,
208 # not groups. So we can use the print method instead of
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;
220 } else { # graphics form with bounding box
221 print "\nLyX cannot support bounding box; ",
222 "copying Figure in TeX mode\n" if $debug_on;
227 $filename = &recursive_print($reqarg);
230 } # end switch on command name
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;
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;
247 return 1; # if we got here, we parsed correctly and LyX can handle it.
248 } # end sub RelyxFigure::Figure::parse_pscommand
250 sub recursive_print {
251 # if arg is a group, print its contents (i.e., ignore '{' and '}')
252 # otherwise, print arg
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($_)}
260 $filename .= $tok->exact_print
266 # LyX figure command -- return what to print; don't actually print it
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.)
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";
285 $to_print .= "\n\\end_inset \n\n";
287 } # end sub RelyxFigure::Figure::print
289 } # end of package RelyxFigure::Figure
291 1; # return true to calling package