]> git.lyx.org Git - lyx.git/blob - lib/reLyX/RelyxFigure.pm
fix typo that put too many include paths for most people
[lyx.git] / lib / reLyX / RelyxFigure.pm
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($EpsfYsize) || 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