6 use File::Basename qw(basename);
13 my $backup; # backup extension name
14 my $directory; # output directory
16 my %write_opts; # options supplied to write()
18 my @collection; # actions/options in order to allow us to set values as needed
20 # each entry consists of:
21 # - ref to action/option handler function
23 # - optional ref to value parser function
25 my %funcs = im_functions();
26 my %options = im_options();
27 my %all = ( %funcs, %options );
31 my ($option, $value) = @_;
32 if ($all{$option}[1] && ref $all{$option}[1]) {
33 $value = $all{$option}[1]->($option, $value);
35 push @collection, [ $option, $value ]
49 for my $option_name (keys %all) {
50 my $option = $all{$option_name};
51 my @names = ( $option_name );
52 my @other_names = split /\|/, $option->[2] if $option->[2];
53 push @names, @other_names;
56 $code = ref $option->[1] ? "=s" : "=".$option->[1];
58 push @getoptions, join("|", @names) . $code => $action_func;
59 # this would be evil $all{$_} = $option for @other_names;
60 push @getoptions, join("|", map "help-$_", @names) => $help_func;
63 GetOptions('help' => sub { $help_func->("synopsis") },
64 'verbose|v+' => \$verbose,
65 'backup|i=s' => \$backup,
66 'directory|d=s' => \$directory,
67 'type|t=s' => \$type, # output file type
68 'write-option|wo=s' => \%write_opts,
69 'output|o=s' => \$output,
73 'help-color-spec' => sub { $help_func->("color specifications") },
79 exists $write_opts{file}
80 and die "Illegal write option 'file'\n";
81 exists $write_opts{type}
82 and die "Use the --type option to set the output format\n";
84 delete $write_opts{qw/file fd fh data callback/};
86 my @actions = grep $funcs{$_->[0]}, @collection;
90 print $funcs{$_}[1] for map $_->[0], @actions;
98 if (!@actions && !@ARGV) {
103 die "No files to process\n";
107 die "Nothing to do, supply at least one action, see $0 --help\n";
111 push @type, type => $type if $type;
113 for my $name (@ARGV) {
114 my $im = Imager->new;
115 if ($im->read(file=>$name)) {
116 my %state = ( filename => $name );
118 for my $action (@collection) {
119 $im = $all{$action->[0]}[0]->($im, $action->[1], \%state);
127 (undef, undef, $file) = File::Spec->split_path($outname);
128 $outname = File::Spec->catfile($directory, $file);
131 my $backfile = $name . $backup;
132 rename $name, $backfile
133 or die "Couldn't rename source '$name' to backup '$backfile': $!\n";
136 unless ($im->write(file=>$outname, @type)) {
137 die "Could not write result from '$name' to '$outname': ", $im->errstr,"\n";
144 my ($im, $state, $format) = @_;
148 f => [ 's', $state->{filename} ],
149 b => [ 's', basename($state->{filename}) ],
150 w => [ 'd', $im->getwidth ],
151 h => [ 'd', $im->getheight ],
152 c => [ 'd', $im->getchannels ],
153 t => [ 's', $im->type ],
154 n => [ 'c', ord("\n") ], # a bit of a hack
158 $format =~ s{%(-?(?:\d+(?:\.\d*)?|\.\d+)?)([fwhctbn%])}
160 my $which = $replace{$2};
161 push @values, @$which[1..$#$which];
165 return sprintf $format, @values;
169 my ($im, $ignored, $state) = @_;
171 my $format = $state->{info_format} || <<EOS;
173 Dimensions: %ww x %hh
178 print _replace_codes($im, $state, $format);
183 sub req_info_format {
184 my ($im, $value, $state) = @_;
186 $state->{info_format} = $value;
192 my ($option, $value) = @_;
195 if ($option =~ /^(\d+)\s*x\s*(\d+)$/i) {
196 return { xpixels=>$1, ypixels=>$2 };
198 elsif ($option =~ /^(\d+(?:\.\d*)?|\.\d+)$/) {
199 return { scalefactor => $option };
201 elsif ($option =~ /^(\d+)\s*x\s*(\d+)\s*min$/i) {
202 return { xpixels=>$1, ypixels=>$2, type=>'min' };
204 elsif ($option =~ /^(\d+)\s*(?:w|wide)$/i) {
205 return { xpixels => $1 };
207 elsif ($option =~ /^(\d+)\s*(?:h|high)$/i) {
208 return { ypixels => $1 };
211 die "Invalid parameter to --scale, try $0 --help-scale\n";
216 my ($im, $args) = @_;
218 return $im->scale(%$args);
222 my ($option, $value) = @_;
224 if ($value =~ /^[-+]?(?:\d+(?:\.\d*)|\.\d+)$/) {
225 return { degrees => $value };
227 elsif ($value =~ /^([-+]?(?:\d+(?:\.\d*)|\.\d+))\s*(?:r|radians)$/i) {
228 return { radians => $1 };
231 die "Invalid parameter to --rotate, try $0 --help-rotate\n";
236 my ($im, $args, $state) = @_;
239 if ($state->{background}) {
240 push @moreargs, back => $state->{background};
242 return $im->rotate(%$args, @moreargs);
246 my ($im, $value, $state) = @_;
248 $state->{background} = $value;
254 my ($im, $value, $state) = @_;
256 $state->{foreground} = $value;
262 my ($im, $value, $state) = @_;
264 $state->{font} = Imager::Font->new(file=>$value)
265 or die "Could not create font from $value: ", Imager->errstr,"\n";
271 my ($option, $value) = @_;
273 unless ($value =~ /^\d+$/ && $value > 0) {
274 die "$option must be a positive integer\n";
281 my ($im, $value, $state) = @_;
283 $state->{font_size} = $value;
289 my ($im, $format, $state) = @_;
291 my $text = _replace_codes($im, $state, $format);
293 my $font = $state->{font}
294 or die "You must supply a --font option before the --caption command\n";
296 my $size = $state->{font_size} || 16;
298 my $box = $font->bounding_box(size=>$size);
299 $box->total_width <= $im->getwidth
300 or die "Caption text '$text' is wider (", $box->total_width,
301 ") than the image (",$im->getwidth,")\n";
303 die "not implemented yet";
314 info => [ \&req_info ],
315 scale => [ \&req_scale, \&val_scale ],
316 rotate => [ \&req_rotate, \&val_rotate ],
317 caption => [ \&req_caption ],
322 my ($option, $value) = @_;
324 if ($value =~ /^rgba\((\d+),(\d+),(\d+),(\d+)\)$/i) {
325 return Imager::Color->new($1,$2,$3,$4);
327 elsif ($value =~ /^rgb\((\d+),(\d+),(\d+)\)$/i) {
328 return Imager::Color->new($1,$2,$3);
330 elsif ($value =~ /^\#[\da-f]{3}([\da-f]{3})?$/) {
331 return Imager::Color->new(web=>$value);
333 elsif ($value =~ /^hsv\((\d+(?:\.\d*)),(\d+\.\d*|\.\d+),(\d+\.\d*|\.\d+)\)$/) {
334 return Imager::Color->new(hsv => [ $1, $2, $3 ]);
336 elsif ($value =~ /^hsva\((\d+(?:\.\d*)),(\d+\.\d*|\.\d+),(\d+\.\d*|\.\d+),(\d+)\)$/) {
337 return Imager::Color->new(hsv => [ $1, $2, $3 ], alpha=>$4);
340 my $color = Imager::Color->new(name=>$value);
341 return $color if $color;
343 die "Unrecognized color specification $value supplied to --$option\n";
350 background => [ \&req_bg, \&val_color, 'bg' ],
351 foreground => [ \&req_fg, \&val_color, 'fg' ],
352 'info-format' => [ \&req_info_format, 's'],
353 font => [ \&req_font, \&val_font ],
354 'font-size' => [ \&req_font_size, \&val_font_size, 'fs' ],
361 open SOURCE, "< $0" or die "Cannot read source for help text: $!\n";
365 if (/^=item --$topic\s/) {
367 # read any more =items then read text until we see =item or =back
369 last unless /^\s*$/ or /^=item /;
373 # and any other until another option or =back
375 last if /^=(item|cut|back)/;
381 elsif (/^=head(\d) $topic\s*$/i) {
385 last if /=head[1-$level]/;
395 die "No help topic $topic found\n";
399 sub help_color_spec {
406 imager - Imager command-line image manipulation tool
411 imager [--font-size <size>] [--fs <size>] [--background <color>]
412 [--bg <color>] [--foreground <color>] [--fg <color]
413 [--info-format <format>] [--rotate <angle>] [--scale <scale-spec>]
414 [--caption <text>] [--info] [--font fontfile] files ...
415 imager --help-I<option>
416 imager --help-I<operation>
426 Displays the width, height, channels, type for each image, and any tags
427 Imager picks up. No options.
429 Note: Imager still converts many files into direct images when the source
430 is a paletted image, so the displayed image type may not match the
433 No output image file is produced.
435 =item --scale <scalefactor>
437 =item --scale <width>x<height>
439 =item --scale <width>x<height>min
441 =item --scale <width>w
443 =item --scale <height>h
445 Scale either by the given scaling factor, given as a floating point number,
446 or to a given dimension.
448 The scaling is always proportional, if a dimension is given then the
449 scalefactor that results in the larger image that matches either the
450 specified width or height is chosen, unless the word "min" is present".
452 --scale 0.5 # half size image
453 --scale 100x100 # aim for 100 pixel x 100 pixel image
454 --scale 100x100min # image that fits in 100 x 100 pixel box
455 --scale 100w # 100 pixel wide image
456 --scale 100h # 100 pixel high image
458 =item --rotate <degrees>
460 =item --rotate <radians>r
462 Rotate the image by the given number of degrees or radians.
466 Displays the usage message if no extra parameter is found, otherwise displays
467 more detailed help for the given function, if any.
471 Expands the image to create a caption area and draws the given text in the
474 You must set a font with --font before this.
476 imager --font arial.ttf --caption "my silly picture"
478 The text has the same replacements done as the --info command.
480 imager --font arial.ttf --caption '%b - %w x %h'
482 If the caption text is too wide for the image an error is produced.
484 Any newlines that aren't at the beginning or end of the caption cause
485 multiple lines of text to be produced.
487 The --foreground and --background options can be used to set colors
488 for this. By default black text on a white background is produced.
492 =head1 GENERAL OPTIONS
498 Display the SYNOPSIS from this POD
504 Increase the verbosity level.
506 =item --backup <extension>
510 Input files are renamed to I<filename><extension> before the output
513 =item --directory <directory>
517 If this is supplied the output files are written to this directory
520 =item --type <fileformat>
522 Specifies an output file format
524 =item --write-option name=value
526 =item --wo name=value
528 Sets the value of an option supplied to the Imager write() function.
529 The options available depend on the file format, see
530 L<Imager::Files/TYPE SPECIFIC INFORMATION> for file format specific
533 You can also supply the L<Imager::ImageTypes/Common Tags>.
535 If you're writing to a gif file you can also supply the options
536 documented as tags under L<Imager::ImageTypes/Quantization options>.
540 =head1 PROCESSING OPTIONS
542 These supply extra parameters to the actions
546 =item --background <color-spec>
548 =item --bg <color-spec>
550 Sets the background color for the --rotate and --caption actions, and
551 possibly other actions in the future.
553 See $0 --help-color-spec for possible color specifications.
558 =item --foreground <color-spec>
560 =item --fg <color-spec>
562 Sets the foreground color for the --caption action, and possibly other
563 actions in the future.
565 See $0 --help-color-spec for possible color specifications.
570 =item --font-size size
574 Set the font size used by the --caption command, in pixels.
576 --fs 16 # 16 pixels from baseline to top
577 --font-size 40 # a bit bigger
579 =item --info-format format
581 Sets the format for the output of the --info command.
583 The format can contain printf style replacement codes, each value is %
584 followed by a sprintf() field width/precision, followed by the value
587 The following values can be output:
588 w - image width in pixels
589 h - image height in pixels
590 f - full image filename
591 b - base image filename
592 c - number of channels
593 t - image type (direct vs paletted)
594 n - inserts a newline
595 % - inserts a '%' symbol
597 The default format is:
599 Image: %f%nDimensions: %ww x %hh%nChannels: %c%nType: %t%n
601 You can use field widths to produce a more table like appearance:
603 im --info-format '%4w %4h %4c %-8t %b%n' --info *.jpg
605 =item --font filename
607 Gives the name of a font file. Required by actions that render text.
614 =head1 COLOR SPECIFICATIONS
616 Possible color specifications:
617 color-name - the name of a built-in color
618 rgb(red,green,blue) - as an RGB triplet
619 #RRGGBB - as a HTML RGB hex triple
620 #RGB - as a HTML CSS RGB hex triple
621 rgba(red,green,blue,alpha) - as an RGBA quad
622 hsv(hue,sat,value) - as an HSV triplet
623 hsva(hue,sat,value,alpha) as an HSVA quad
632 If you use either of the HTML color specifications, or a specification
633 using parentheses from a Unix shell you will need to quote it, for
637 --bg 'rgb(255,0,255)'
641 Tony Cook <tony@develop-help.com>