lyx_mirror/lib/reLyX/RelyxFigure.pm

292 lines
9.6 KiB
Perl
Raw Normal View History

# This file is part of reLyX
# Copyright (c) 1998-9 Amir Karger karger@post.harvard.edu
# You are free to use and modify this code under the terms of
# the GNU General Public Licence version 2 or later.
package RelyxFigure;
# This is a package to read LaTeX figures and print out LyX figures
# We declare here the sub-packages found in this package.
# This allows the parser to understand "indirect object" form of subroutines
#{
#package RelyxTable::Table;
#package RelyxTable::Column;
#package RelyxTable::Row;
#}
use strict;
# Variables used by other packages
use vars qw($EpsfYsize $EpsfXsize %HW_types);
# Debugging on?
my $debug_on;
# This means "display in monochrome" and "do translations", but who cares?
# It's just here because this is the default that LyX outputs.
my $Default_Flags = 9;
# Names for width_type & height_type fields.
%HW_types = (
"def" => 0,
"cm" => 1,
"in" => 2,
"per_page" => 3, # percent of the page
"per_col" => 4, # percent of a column (illegal for height_type)
);
# Parse \epsfxsize or \epsfysize command
# Return 0 if we can't convert the length (in which case we have to type
# the \epsf[xy]size command in tex mode).
sub parse_epsfsize {
# Command & length have already been "stringified" (they're not tokens)
my ($command, $length) = (shift,shift);
if ($command eq '\\epsfxsize') {
$EpsfXsize = $length;
my @dummy = &convert_length($EpsfXsize) || return 0;
} elsif ($command eq '\\epsfysize') {
$EpsfYsize = $length;
my @dummy = &convert_length($EpsfXsize) || return 0;
}
return 1;
} # end sub RelyxFIgure::Figure::parse_epsfig
sub convert_length {
# test if it's a valid LyX width/height.
# (But assume a person won't try to set width to \textheight,
# and that they won't have negative or otherwise weird lengths)
# Then convert to width & width_type (or height/height_type)
# Return empty list on error
my $size = shift;
# A length can be (optional plus followed by) (num)(unit) where
# num is a float number (possibly with European command) and unit
# is a size unit, either in,cm,pt etc. or \textwidth etc.
my %unit_convert = ( # 1 inch = 25.4 mm
"mm" => 25.4, "pt" => 72.27, "bp" => 72, "pc" => 72.27/12,
"dd" => 72.27*1157/1238, "cc" => 72.27*1157/(1238*12),
);
my ($number, $type);
if ($size =~ /^\+?([\d.,]+)(cm|in)$/) {
($number, $type) = ($1, $2);
$number =~ s/,/./; # Allow european numbers!
# print "length is $number '$type'";
} elsif ($size =~ /^\+?([\d.,]+)(mm|pt|bp|pc|dd|cc)$/) {
($number, $type) = ($1, $2);
$number =~ s/,/./;
$number = $number / $unit_convert{$type};
$type = "in";
} elsif ($size =~ /^\+?([\d.,]*)\s*\\text(height|width)$/) {
$number = $1 || 1;
$number =~ s/,/./;
$number *= 100;
$type = "per_page";
} elsif ($size =~ /^\+?([\d.,]*)\s*\\columnwidth$/) {
$number = $1 || 1;
$number =~ s/,/./;
$number *= 100;
$type = "per_col";
} else {
print "\ncannot translate length '$size' in Figure; ",
"copying in TeX mode\n" if $debug_on;
}
if ($number) {
return ($number, $type);
} else {
return ();
}
} # end sub convert_length
sub parse_keyval {
# Parse a string containing comma-separated "key=value" pairs
# Compare the keys with a list of known keys.
# If we know all keys, return a hash containing keys/values
# Else return undef.
my ($string, @known_keys) = @_;
my @fields = split(/\s*,\s*/,$string);
my %fighash;
foreach (@fields) { # split "key=val" into fighash{key}=val
my ($key,$val) = split(/\s*=\s*/,$_);
$val = "" unless defined $val; # e.g., 'clip='
$fighash{$key} = $val;
unless (grep /^$key$/, @known_keys) {
print "\nUntranslatable key '$key' to figure;",
" copying in TeX mode\n" if $debug_on;
return undef;
}
}
return \%fighash;
} # end sub parse_keyval
{
package RelyxFigure::Figure;
# reLyX figure class
# Fields:
# file - file name
# width - width
# height - height
# width_type - is width in cm, % of pagewidth, etc.?
# height_type - is height in cm, % of pagewidth, etc.?
# angle - rotate fig through angle
# flags - various flags for LyX display. Not important
sub new {
my $class = shift;
my $thisfig;
$thisfig->{'file'} = "";
$thisfig->{'width'} = 0;
$thisfig->{'height'} = 0;
$thisfig->{'width_type'} = "def";
$thisfig->{'height_type'} = "def";
$thisfig->{'angle'} = 0;
$thisfig->{'flags'} = $Default_Flags;
# This seems like a convenient place to declare this...
$debug_on= (defined($main::opt_d) && $main::opt_d);
bless $thisfig, $class;
} # end sub RelyxFigure::Figure::new
sub parse_pscommand {
# this sub is given the various arguments to a command & adds that
# information to the figure object.
# Return 0 if it can't read the command, or if LyX can't handle it. Else 1.
#
# command is the name of the postscript command
# optargs are optional arguments (TT:Tokens). For many of the commands,
# only one optarg is allowed. And of course, they may be empty tokens.
# reqarg is usually the filename (for (e)psfig, it's more)
#
# Currently, LyX can't handle bounding box, so we always return 0 if one
# is given.
my ($self, $command, $optarg1, $optarg2, $reqarg) = @_;
my (@known_keys, $filename, $keyval_string, %fighash);
for ($command) {
if (/^\\epsf(file|box)?$/) {
# syntax: \epsffile[bounding box]{filename.eps}
# epsffile is an older version of epsfbox
return 0 if $optarg1->print; # bounding box was given. Panic!
$filename = &recursive_print($reqarg);
# fighash key shouldn't exist if no size was given
$fighash{'height'} = $RelyxFigure::EpsfYsize
if $RelyxFigure::EpsfYsize;
$fighash{'width'} = $RelyxFigure::EpsfXsize
if $RelyxFigure::EpsfXsize;
# Once you use \epsf[xy]size, they get reset
$RelyxFigure::EpsfXsize = $RelyxFigure::EpsfYsize = "";
$keyval_string = ""; # no key/value pairs for this command
} elsif (/^\\e?psfig$/) {
# "silent" key doesn't do anything
@known_keys = qw(file figure rotate angle height width silent);
$keyval_string = &recursive_print($reqarg);
my $fighashref =
&RelyxFigure::parse_keyval($keyval_string, @known_keys);
return 0 unless defined $fighashref; # found unknown key...
%fighash = %$fighashref;
$filename = $fighash{'file'} || $fighash{'figure'} || warn
"no filename found in figure argument '$keyval_string'";
} elsif (/^\\includegraphics$/) {
# 0 optargs can be either graphics or graphicx. Doesn't
# matter, matter, it's just the filename.
# 1 optarg can either be graphicx (if arg contains '=') or
# graphics (optarg is upper right; lower left is 0,0).
# 2 optargs is graphics with bounding box.
# Optional arguments are always returned as single tokens,
# not groups. So we can use the print method instead of
# recursive_print.
$keyval_string = $optarg1->print;
if ($keyval_string) {
if ($keyval_string =~ /=/) { # graphicx form
$keyval_string =~ s/\[(.*)\]/$1/; # remove '[' and ']'
@known_keys = qw(rotate angle height width);
my $fighashref = &RelyxFigure::parse_keyval(
$keyval_string, @known_keys);
return 0 unless defined $fighashref; # found unknown key
%fighash = %$fighashref;
} else { # graphics form with bounding box
print "\nLyX cannot support bounding box; ",
"copying Figure in TeX mode\n" if $debug_on;
return 0;
}
}
$filename = &recursive_print($reqarg);
}
} # end switch on command name
# Now set fields in the Figure object
$self->{'file'} = $filename;
$self->{'angle'} = $fighash{'rotate'} if exists $fighash{'rotate'};
$self->{'angle'} = $fighash{'angle'} if exists $fighash{'angle'};
if (exists $fighash{'height'}) {
my @heights = &RelyxFigure::convert_length($fighash{'height'});
return 0 unless @heights; # unconvertable length!
($self->{'height'},$self->{'height_type'}) = @heights;
}
if (exists $fighash{'width'}) {
my @widths = &RelyxFigure::convert_length($fighash{'width'});
return 0 unless @widths; # unconvertable length!
($self->{'width'},$self->{'width_type'}) = @widths;
}
return 1; # if we got here, we parsed correctly and LyX can handle it.
} # end sub RelyxFigure::Figure::parse_pscommand
sub recursive_print {
# if arg is a group, print its contents (i.e., ignore '{' and '}')
# otherwise, print arg
my $tok = shift;
my $filename = "";
my $type = ref($tok);
$type =~ s/Text::TeX::// or warn "weird group";
if ($type eq 'Group') {
foreach ($tok->contents) {$filename .= &recursive_print($_)}
} else {
$filename .= $tok->exact_print
}
return $filename;
}
sub print_info {
# LyX figure command -- return what to print; don't actually print it
my $self = shift;
my $to_print = "\n\\begin_inset Figure\n";
# (LyX fig. command has eps size here. But we don't know that, so
# we dont print it out.)
$to_print .= "file $self->{'file'}\n";
my ($size, $type) = ("","");
($size, $type) = ($self->{'width'},
$RelyxFigure::HW_types{$self->{'width_type'}});
$to_print .= "width $type $size\n" if $size;
($size, $type) = ("","");
($size, $type) = ($self->{'height'},
$RelyxFigure::HW_types{$self->{'height_type'}});
$to_print .= "height $type $size\n" if $size;
$to_print .= "angle $self->{'angle'}\n" if $self->{'angle'};
$to_print .= "flags $self->{'flags'}\n";
$to_print .= "\n\\end_inset \n\n";
} # end sub RelyxFigure::Figure::print
} # end of package RelyxFigure::Figure
1; # return true to calling package