6 use File::Basename qw(basename);
13 my $backup; # backup extension name
14 my $directory; # output directory
17 my %write_opts; # options supplied to write()
20 my @collection; # actions/options in order to allow us to set values as needed
22 # each entry consists of:
23 # - ref to action/option handler function
25 # - optional ref to value parser function
27 my %funcs = im_functions();
28 my %options = im_options();
29 my %all = ( %funcs, %options );
33 my ($option, $value) = @_;
34 if ($all{$option}[1] && ref $all{$option}[1]) {
35 $value = $all{$option}[1]->($option, $value);
37 push @collection, [ $option, $value ]
51 for my $option_name (keys %all) {
52 my $option = $all{$option_name};
53 my @names = ( $option_name );
54 my @other_names = split /\|/, $option->[2] if $option->[2];
55 push @names, @other_names;
58 $code = ref $option->[1] ? "=s" : "=".$option->[1];
60 push @getoptions, join("|", @names) . $code => $action_func;
61 # this would be evil $all{$_} = $option for @other_names;
62 push @getoptions, join("|", map "help-$_", @names) => $help_func;
65 GetOptions('help' => sub { $help_func->("synopsis") },
66 'verbose|v+' => \$verbose,
67 'backup|i=s' => \$backup,
68 'directory|d=s' => \$directory,
69 'type|t=s' => \$type, # output file type
70 'write-option|wo=s' => \%write_opts,
71 'output|o=s' => \$output,
72 'understand' => \$understand,
76 'help-color-spec' => sub { $help_func->("color specifications") },
77 'help-actions' => sub { $help_func->("actions") },
78 'help-options' => sub { $help_func->("processing options") },
79 'help-general' => sub { $help_func->("general options") },
85 unless ($understand) {
87 This tool is under-tested and will probably destroy your data.
89 If you understand and agree with this use the --understand option to
92 In fact, only the --info and --tags actions have been used at all.
96 exists $write_opts{file}
97 and die "Illegal write option 'file'\n";
98 exists $write_opts{type}
99 and die "Use the --type option to set the output format\n";
101 delete $write_opts{qw/file fd fh data callback/};
103 my @actions = grep $funcs{$_->[0]}, @collection;
107 print $funcs{$_}[1] for map $_->[0], @actions;
115 if (!@actions && !@ARGV) {
120 die "No files to process\n";
124 die "Nothing to do, supply at least one action, see $0 --help\n";
128 push @type, type => $type if $type;
130 for my $name (@ARGV) {
131 my $im = Imager->new;
132 if ($im->read(file=>$name)) {
133 my %state = ( filename => $name );
135 for my $action (@collection) {
136 $im = $all{$action->[0]}[0]->($im, $action->[1], \%state);
144 (undef, undef, $file) = File::Spec->split_path($outname);
145 $outname = File::Spec->catfile($directory, $file);
148 my $backfile = $name . $backup;
149 rename $name, $backfile
150 or die "Couldn't rename source '$name' to backup '$backfile': $!\n";
153 unless ($im->write(file=>$outname, @type)) {
154 die "Could not write result from '$name' to '$outname': ", $im->errstr,"\n";
159 print STDERR "Failed reading $name: ",$im->errstr,"\n";
164 my ($im, $state, $format) = @_;
168 f => [ 's', $state->{filename} ],
169 b => [ 's', basename($state->{filename}) ],
170 w => [ 'd', $im->getwidth ],
171 h => [ 'd', $im->getheight ],
172 c => [ 'd', $im->getchannels ],
173 t => [ 's', $im->type ],
174 n => [ 'c', ord("\n") ], # a bit of a hack
178 $format =~ s{%(-?(?:\d+(?:\.\d*)?|\.\d+)?)([fwhctbn%])}
180 my $which = $replace{$2};
181 push @values, @$which[1..$#$which];
185 return sprintf $format, @values;
189 my ($im, $ignored, $state) = @_;
191 my $format = $state->{info_format} || <<EOS;
193 Dimensions: %ww x %hh
198 print _replace_codes($im, $state, $format);
203 sub req_info_format {
204 my ($im, $value, $state) = @_;
206 $state->{info_format} = $value;
212 my ($im, $ignored, $state) = @_;
214 print $state->{filename},"\n";
215 my @tags = $im->tags;
216 for my $tag (sort { $a->[0] cmp $b->[0] } @tags) {
217 my $name = shift @$tag;
218 print " $name: @$tag\n";
225 my ($im, $ignored, $state) = @_;
227 print $state->{filename},"\n";
228 if ($im->type eq 'direct') {
229 print " No palette - this is a direct color image\n";
232 my @colors = $im->getcolors;
233 for my $index (0..$#colors) {
234 printf "%3d: (%3d, %3d, %3d)\n", $index, ($colors[$index]->rgba)[0..2];
242 my ($option, $value) = @_;
245 if ($option =~ /^(\d+)\s*x\s*(\d+)$/i) {
246 return { xpixels=>$1, ypixels=>$2 };
248 elsif ($option =~ /^(\d+(?:\.\d*)?|\.\d+)$/) {
249 return { scalefactor => $option };
251 elsif ($option =~ /^(\d+)\s*x\s*(\d+)\s*min$/i) {
252 return { xpixels=>$1, ypixels=>$2, type=>'min' };
254 elsif ($option =~ /^(\d+)\s*(?:w|wide)$/i) {
255 return { xpixels => $1 };
257 elsif ($option =~ /^(\d+)\s*(?:h|high)$/i) {
258 return { ypixels => $1 };
261 die "Invalid parameter to --scale, try $0 --help-scale\n";
266 my ($im, $args) = @_;
268 return $im->scale(%$args);
272 my ($option, $value) = @_;
274 if ($value =~ /^[-+]?(?:\d+(?:\.\d*)|\.\d+)$/) {
275 return { degrees => $value };
277 elsif ($value =~ /^([-+]?(?:\d+(?:\.\d*)|\.\d+))\s*(?:r|radians)$/i) {
278 return { radians => $1 };
281 die "Invalid parameter to --rotate, try $0 --help-rotate\n";
286 my ($im, $args, $state) = @_;
289 if ($state->{background}) {
290 push @moreargs, back => $state->{background};
292 return $im->rotate(%$args, @moreargs);
296 my ($im, $value, $state) = @_;
298 $state->{background} = $value;
304 my ($im, $value, $state) = @_;
306 $state->{foreground} = $value;
312 my ($im, $value, $state) = @_;
314 $state->{font} = Imager::Font->new(file=>$value)
315 or die "Could not create font from $value: ", Imager->errstr,"\n";
321 my ($option, $value) = @_;
323 unless ($value =~ /^\d+$/ && $value > 0) {
324 die "$option must be a positive integer\n";
331 my ($im, $value, $state) = @_;
333 $state->{font_size} = $value;
339 my ($im, $format, $state) = @_;
341 my $text = _replace_codes($im, $state, $format);
343 my $font = $state->{font}
344 or die "You must supply a --font option before the --caption command\n";
346 my $size = $state->{font_size} || 16;
348 my $box = $font->bounding_box(size=>$size);
349 $box->total_width <= $im->getwidth
350 or die "Caption text '$text' is wider (", $box->total_width,
351 ") than the image (",$im->getwidth,")\n";
353 die "not implemented yet";
364 info => [ \&req_info ],
365 tags => [ \&req_tags ],
366 palette => [ \&req_palette ],
367 scale => [ \&req_scale, \&val_scale ],
368 rotate => [ \&req_rotate, \&val_rotate ],
369 # caption => [ \&req_caption ], # not done yet
374 my ($option, $value) = @_;
376 if ($value =~ /^rgba\((\d+),(\d+),(\d+),(\d+)\)$/i) {
377 return Imager::Color->new($1,$2,$3,$4);
379 elsif ($value =~ /^rgb\((\d+),(\d+),(\d+)\)$/i) {
380 return Imager::Color->new($1,$2,$3);
382 elsif ($value =~ /^\#[\da-f]{3}([\da-f]{3})?$/) {
383 return Imager::Color->new(web=>$value);
385 elsif ($value =~ /^hsv\((\d+(?:\.\d*)),(\d+\.\d*|\.\d+),(\d+\.\d*|\.\d+)\)$/) {
386 return Imager::Color->new(hsv => [ $1, $2, $3 ]);
388 elsif ($value =~ /^hsva\((\d+(?:\.\d*)),(\d+\.\d*|\.\d+),(\d+\.\d*|\.\d+),(\d+)\)$/) {
389 return Imager::Color->new(hsv => [ $1, $2, $3 ], alpha=>$4);
392 my $color = Imager::Color->new(name=>$value);
393 return $color if $color;
395 die "Unrecognized color specification $value supplied to --$option\n";
402 background => [ \&req_bg, \&val_color, 'bg' ],
403 foreground => [ \&req_fg, \&val_color, 'fg' ],
404 'info-format' => [ \&req_info_format, 's'],
405 font => [ \&req_font, \&val_font ],
406 'font-size' => [ \&req_font_size, \&val_font_size, 'fs' ],
413 open SOURCE, "< $0" or die "Cannot read source for help text: $!\n";
417 if (/^=item --$topic\s/) {
419 # read any more =items then read text until we see =item or =back
421 last unless /^\s*$/ or /^=item /;
425 # and any other until another option or =back
427 last if /^=(item|cut|back)/;
433 elsif (/^=head(\d) $topic\s*$/i) {
437 last if /=head[1-$level]/;
447 die "No help topic $topic found\n";
451 sub help_color_spec {
458 imager - Imager command-line image manipulation tool
463 imager [--font-size <size>] [--fs <size>] [--background <color>]
464 [--bg <color>] [--foreground <color>] [--fg <color]
465 [--info-format <format>] [--rotate <angle>] [--scale <scale-spec>]
466 [--caption <text>] [--info] [--tags] [--font fontfile] files ...
467 imager --help-I<option>
468 imager --help-I<operation>
469 imager --help-options
470 imager --help-actions
471 imager --help-general
472 imager --help-colorspec
482 Displays the width, height, channels, type for each image, and any tags
483 Imager picks up. No options.
485 Note: Imager still converts many files into direct images when the source
486 is a paletted image, so the displayed image type may not match the
489 No output image file is produced.
493 Displays all the tags the Imager reader for that format sets for each
496 See L<Imager::Files> for file format specific tags and
497 L<Imager::ImageTypes> for common tags.
501 Dumps the palette of the given file, if it is an indexed image.
503 =item --scale <scalefactor>
505 =item --scale <width>x<height>
507 =item --scale <width>x<height>min
509 =item --scale <width>w
511 =item --scale <height>h
513 Scale either by the given scaling factor, given as a floating point number,
514 or to a given dimension.
516 The scaling is always proportional, if a dimension is given then the
517 scalefactor that results in the larger image that matches either the
518 specified width or height is chosen, unless the word "min" is present".
520 --scale 0.5 # half size image
521 --scale 100x100 # aim for 100 pixel x 100 pixel image
522 --scale 100x100min # image that fits in 100 x 100 pixel box
523 --scale 100w # 100 pixel wide image
524 --scale 100h # 100 pixel high image
526 =item --rotate <degrees>
528 =item --rotate <radians>r
530 Rotate the image by the given number of degrees or radians.
534 Displays the usage message if no extra parameter is found, otherwise displays
535 more detailed help for the given function, if any.
541 Expands the image to create a caption area and draws the given text in the
544 You must set a font with --font before this.
546 imager --font arial.ttf --caption "my silly picture"
548 The text has the same replacements done as the --info command.
550 imager --font arial.ttf --caption '%b - %w x %h'
552 If the caption text is too wide for the image an error is produced.
554 Any newlines that aren't at the beginning or end of the caption cause
555 multiple lines of text to be produced.
557 The --foreground and --background options can be used to set colors
558 for this. By default black text on a white background is produced.
562 =head1 GENERAL OPTIONS
568 Display the SYNOPSIS from this POD
574 Increase the verbosity level.
576 =item --backup <extension>
580 Input files are renamed to I<filename><extension> before the output
583 =item --directory <directory>
587 If this is supplied the output files are written to this directory
590 =item --type <fileformat>
592 Specifies an output file format
594 =item --write-option name=value
596 =item --wo name=value
598 Sets the value of an option supplied to the Imager write() function.
599 The options available depend on the file format, see
600 L<Imager::Files/TYPE SPECIFIC INFORMATION> for file format specific
603 You can also supply the L<Imager::ImageTypes/Common Tags>.
605 If you're writing to a gif file you can also supply the options
606 documented as tags under L<Imager::ImageTypes/Quantization options>.
610 =head1 PROCESSING OPTIONS
612 These supply extra parameters to the actions
616 =item --background <color-spec>
618 =item --bg <color-spec>
620 Sets the background color for the --rotate and --caption actions, and
621 possibly other actions in the future.
623 See $0 --help-color-spec for possible color specifications.
628 =item --foreground <color-spec>
630 =item --fg <color-spec>
632 Sets the foreground color for the --caption action, and possibly other
633 actions in the future.
635 See $0 --help-color-spec for possible color specifications.
640 =item --font-size size
644 Set the font size used by the --caption command, in pixels.
646 --fs 16 # 16 pixels from baseline to top
647 --font-size 40 # a bit bigger
649 =item --info-format format
651 Sets the format for the output of the --info command.
653 The format can contain printf style replacement codes, each value is %
654 followed by a sprintf() field width/precision, followed by the value
657 The following values can be output:
658 w - image width in pixels
659 h - image height in pixels
660 f - full image filename
661 b - base image filename
662 c - number of channels
663 t - image type (direct vs paletted)
664 n - inserts a newline
665 % - inserts a '%' symbol
667 The default format is:
669 Image: %f%nDimensions: %ww x %hh%nChannels: %c%nType: %t%n
671 You can use field widths to produce a more table like appearance:
673 im --info-format '%4w %4h %4c %-8t %b%n' --info *.jpg
675 =item --font filename
677 Gives the name of a font file. Required by actions that render text.
684 =head1 COLOR SPECIFICATIONS
686 Possible color specifications:
687 color-name - the name of a built-in color
688 rgb(red,green,blue) - as an RGB triplet
689 #RRGGBB - as a HTML RGB hex triple
690 #RGB - as a HTML CSS RGB hex triple
691 rgba(red,green,blue,alpha) - as an RGBA quad
692 hsv(hue,sat,value) - as an HSV triplet
693 hsva(hue,sat,value,alpha) as an HSVA quad
702 If you use either of the HTML color specifications, or a specification
703 using parentheses from a Unix shell you will need to quote it, for
707 --bg 'rgb(255,0,255)'
711 Tony Cook <tony@develop-help.com>