From: Tony Cook Date: Thu, 18 Nov 2004 05:16:04 +0000 (+0000) Subject: initial version X-Git-Tag: Imager-0.48^2~298 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/213510171033be907b3c96492fb972dca2e83203 initial version --- diff --git a/tools/imager b/tools/imager new file mode 100755 index 00000000..064010ea --- /dev/null +++ b/tools/imager @@ -0,0 +1,643 @@ +#!/usr/bin/perl -w +use strict; +use Imager; +use Getopt::Long; +use File::Spec; +use File::Basename qw(basename); +use vars qw($VERSION); + +$VERSION="0.01_00"; + +my $help; +my $verbose; +my $backup; # backup extension name +my $directory; # output directory +my $type; +my %write_opts; # options supplied to write() + +my @collection; # actions/options in order to allow us to set values as needed + +# each entry consists of: +# - ref to action/option handler function +# - help text +# - optional ref to value parser function + +my %funcs = im_functions(); +my %options = im_options(); +my %all = ( %funcs, %options ); + +my $action_func = + sub { + my ($option, $value) = @_; + if ($all{$option}[1] && ref $all{$option}[1]) { + $value = $all{$option}[1]->($option, $value); + } + push @collection, [ $option, $value ] + }; + +my $did_help; +my $help_func = + sub { + my ($option) = @_; + + $option =~ s/help-//; + help_on($option); + ++$did_help; + }; + +my @getoptions; +for my $option_name (keys %all) { + my $option = $all{$option_name}; + my @names = ( $option_name ); + my @other_names = split /\|/, $option->[2] if $option->[2]; + push @names, @other_names; + my $code = ''; + if ($option->[1]) { + $code = ref $option->[1] ? "=s" : "=".$option->[1]; + } + push @getoptions, join("|", @names) . $code => $action_func; + # this would be evil $all{$_} = $option for @other_names; + push @getoptions, join("|", map "help-$_", @names) => $help_func; +} + +GetOptions('help' => sub { $help_func->("synopsis") }, + 'verbose|v+' => \$verbose, + 'backup|i=s' => \$backup, + 'directory|d=s' => \$directory, + 'type|t=s' => \$type, # output file type + 'write-option|wo=s' => \%write_opts, + 'output|o=s' => \$output, + + @getoptions, + + 'help-color-spec' => sub { $help_func->("color specifications") }, + ) + or usage(); + +$did_help and exit; + +exists $write_opts{file} + and die "Illegal write option 'file'\n"; +exists $write_opts{type} + and die "Use the --type option to set the output format\n"; + +delete $write_opts{qw/file fd fh data callback/}; + +my @actions = grep $funcs{$_->[0]}, @collection; + +if ($help) { + if (@actions) { + print $funcs{$_}[1] for map $_->[0], @actions; + exit; + } + else { + usage(); + } +} + +if (!@actions && !@ARGV) { + usage(); +} + +unless (@ARGV) { + die "No files to process\n"; +} + +unless (@actions) { + die "Nothing to do, supply at least one action, see $0 --help\n"; +} + +my @type; +push @type, type => $type if $type; + +for my $name (@ARGV) { + my $im = Imager->new; + if ($im->read(file=>$name)) { + my %state = ( filename => $name ); + + for my $action (@collection) { + $im = $all{$action->[0]}[0]->($im, $action->[1], \%state); + last unless $im; + } + + if ($im) { + my $outname = $name; + if ($directory) { + my $file; + (undef, undef, $file) = File::Spec->split_path($outname); + $outname = File::Spec->catfile($directory, $file); + } + if ($backup) { + my $backfile = $name . $backup; + rename $name, $backfile + or die "Couldn't rename source '$name' to backup '$backfile': $!\n"; + } + + unless ($im->write(file=>$outname, @type)) { + die "Could not write result from '$name' to '$outname': ", $im->errstr,"\n"; + } + } + } +} + +sub _replace_codes { + my ($im, $state, $format) = @_; + + my %replace = + ( + f => [ 's', $state->{filename} ], + b => [ 's', basename($state->{filename}) ], + w => [ 'd', $im->getwidth ], + h => [ 'd', $im->getheight ], + c => [ 'd', $im->getchannels ], + t => [ 's', $im->type ], + n => [ 'c', ord("\n") ], # a bit of a hack + '%' => [ '%' ], + ); + my @values; + $format =~ s{%(-?(?:\d+(?:\.\d*)?|\.\d+)?)([fwhctbn%])} + { + my $which = $replace{$2}; + push @values, @$which[1..$#$which]; + "%$1$which->[0]" + }eg; + + return sprintf $format, @values; +} + +sub req_info { + my ($im, $ignored, $state) = @_; + + my $format = $state->{info_format} || <{info_format} = $value; + + $im; +} + +sub val_scale { + my ($option, $value) = @_; + + my %options; + if ($option =~ /^(\d+)\s*x\s*(\d+)$/i) { + return { xpixels=>$1, ypixels=>$2 }; + } + elsif ($option =~ /^(\d+(?:\.\d*)?|\.\d+)$/) { + return { scalefactor => $option }; + } + elsif ($option =~ /^(\d+)\s*x\s*(\d+)\s*min$/i) { + return { xpixels=>$1, ypixels=>$2, type=>'min' }; + } + elsif ($option =~ /^(\d+)\s*(?:w|wide)$/i) { + return { xpixels => $1 }; + } + elsif ($option =~ /^(\d+)\s*(?:h|high)$/i) { + return { ypixels => $1 }; + } + else { + die "Invalid parameter to --scale, try $0 --help-scale\n"; + } +} + +sub req_scale { + my ($im, $args) = @_; + + return $im->scale(%$args); +} + +sub val_rotate { + my ($option, $value) = @_; + + if ($value =~ /^[-+]?(?:\d+(?:\.\d*)|\.\d+)$/) { + return { degrees => $value }; + } + elsif ($value =~ /^([-+]?(?:\d+(?:\.\d*)|\.\d+))\s*(?:r|radians)$/i) { + return { radians => $1 }; + } + else { + die "Invalid parameter to --rotate, try $0 --help-rotate\n"; + } +} + +sub req_rotate { + my ($im, $args, $state) = @_; + + my @moreargs; + if ($state->{background}) { + push @moreargs, back => $state->{background}; + } + return $im->rotate(%$args, @moreargs); +} + +sub req_bg { + my ($im, $value, $state) = @_; + + $state->{background} = $value; + + $im; +} + +sub req_fg { + my ($im, $value, $state) = @_; + + $state->{foreground} = $value; + + $im; +} + +sub req_font { + my ($im, $value, $state) = @_; + + $state->{font} = Imager::Font->new(file=>$value) + or die "Could not create font from $value: ", Imager->errstr,"\n"; + + $im; +} + +sub val_font_size { + my ($option, $value) = @_; + + unless ($value =~ /^\d+$/ && $value > 0) { + die "$option must be a positive integer\n"; + } + + $value; +} + +sub req_font_size { + my ($im, $value, $state) = @_; + + $state->{font_size} = $value; + + $im; +} + +sub req_caption { + my ($im, $format, $state) = @_; + + my $text = _replace_codes($im, $state, $format); + + my $font = $state->{font} + or die "You must supply a --font option before the --caption command\n"; + + my $size = $state->{font_size} || 16; + + my $box = $font->bounding_box(size=>$size); + $box->total_width <= $im->getwidth + or die "Caption text '$text' is wider (", $box->total_width, + ") than the image (",$im->getwidth,")\n"; + + die "not implemented yet"; +} + +sub usage { + help_on("SYNOPSIS"); + exit 1; +} + +sub im_functions { + return + ( + info => [ \&req_info ], + scale => [ \&req_scale, \&val_scale ], + rotate => [ \&req_rotate, \&val_rotate ], + caption => [ \&req_caption ], + ); +} + +sub val_color { + my ($option, $value) = @_; + + if ($value =~ /^rgba\((\d+),(\d+),(\d+),(\d+)\)$/i) { + return Imager::Color->new($1,$2,$3,$4); + } + elsif ($value =~ /^rgb\((\d+),(\d+),(\d+)\)$/i) { + return Imager::Color->new($1,$2,$3); + } + elsif ($value =~ /^\#[\da-f]{3}([\da-f]{3})?$/) { + return Imager::Color->new(web=>$value); + } + elsif ($value =~ /^hsv\((\d+(?:\.\d*)),(\d+\.\d*|\.\d+),(\d+\.\d*|\.\d+)\)$/) { + return Imager::Color->new(hsv => [ $1, $2, $3 ]); + } + elsif ($value =~ /^hsva\((\d+(?:\.\d*)),(\d+\.\d*|\.\d+),(\d+\.\d*|\.\d+),(\d+)\)$/) { + return Imager::Color->new(hsv => [ $1, $2, $3 ], alpha=>$4); + } + else { + my $color = Imager::Color->new(name=>$value); + return $color if $color; + + die "Unrecognized color specification $value supplied to --$option\n"; + } +} + +sub im_options { + return + ( + background => [ \&req_bg, \&val_color, 'bg' ], + foreground => [ \&req_fg, \&val_color, 'fg' ], + 'info-format' => [ \&req_info_format, 's'], + font => [ \&req_font, \&val_font ], + 'font-size' => [ \&req_font_size, \&val_font_size, 'fs' ], + ); +} + +sub help_on { + my ($topic) = @_; + + open SOURCE, "< $0" or die "Cannot read source for help text: $!\n"; + my @lines; + while () { + # don't chomp it + if (/^=item --$topic\s/) { + push @lines, $_; + # read any more =items then read text until we see =item or =back + while () { + last unless /^\s*$/ or /^=item /; + push @lines, $_; + } + push @lines, $_; + # and any other until another option or =back + while () { + last if /^=(item|cut|back)/; + push @lines, $_; + } + print @lines; + return; + } + elsif (/^=head(\d) $topic\s*$/i) { + my $level = $1; + push @lines, $_; + while () { + last if /=head[1-$level]/; + push @lines, $_; + } + + print @lines; + return; + } + } + close SOURCE; + + die "No help topic $topic found\n"; +} + + +sub help_color_spec { + print <] [--fs ] [--background ] + [--bg ] [--foreground ] [--fg ] [--rotate ] [--scale ] + [--caption ] [--info] [--font fontfile] files ... + imager --help-I