package App::PNGCrush;
use warnings;
use strict;
our $VERSION = '0.001';
use Carp;
use Proc::Reliable;
use Devel::TakeHashArgs;
use base 'Class::Data::Accessor';
my %Valid_Options = qw(
already_size -already
bit_depth -bit_depth
background -bkgd
brute_force -brute
color_type -c
color_counting -cc
output_dir -d
double_image_gamma -dou
output_extension -e
filter -f
fix_fatal -fix
output_force -force
gamma -g
itxt -itxt
level -l
method -m
maximum_idat -max
no_output -n
no_color_counting -no_cc
plte_length -plte_len
remove -rem
replace_gamma -replace_gamma
resolution -res
save_unknown -save
srgb -srgb
text -text
transparency -trns
window_size -w
strategy -z
insert_ztxt -zitxt
ztxt -ztxt
verbose -v
);
my %No_Arg_Options = map { $_ => 1 } qw(
brute_force
color_counting
double_image_gamma
fix_fatal
output_force
no_output
no_color_counting
save_unknown
verbose
);
__PACKAGE__->mk_classaccessors (
qw( proc error results ),
keys %Valid_Options
);
sub new {
my $self = bless {}, shift;
get_args_as_hash( \@_, \my %args, { maxtime => 300 } )
or croak $@;
my $proc = Proc::Reliable->new;
$proc->$_( $args{$_} ) for keys %args;
$self->proc( $proc );
return $self;
}
sub run {
my $self = shift;
my $in = shift;
get_args_as_hash( \@_, \ my %args, { in => $in }, )
or croak $@;
$self->$_(undef) for qw(error results);
my @options = exists $args{opts}
? @{ $args{opts} }
: $self->_make_options;
my $proc = $self->proc;
my %out;
@out{ qw(stdout stderr status msg) }
= $proc->run( [ 'pngcrush', @options, $in ] );
return $self->_set_error("Proc::Reliable error: $out{error}")
if defined $out{error};
return $self->_set_error("File $in does not seem to exist")
if $out{stdout} =~ /Could not find file: \Q$in/;
@out{ qw(idat size) } = $out{stdout}
=~ /\(([\d.]+)% IDAT reduction\).+?\(([\d.]+)% filesize reduction\)/s;
$out{idat} = 0
if not defined $out{idat}
and $out{stdout} =~ /\Q(no IDAT change)/;
$out{size} = 0
if not defined $out{size}
and $out{stdout} =~ /\Q(no filesize change)/;
@{ $out{cpu} }{ qw(total decoding encoding other) } = $out{stdout}
=~ /CPU \s time \s used \s = \s ([\d.]+) \s seconds \s
\(decoding \s ([\d.]+), \s+
encoding \s ([\d.]+), \s other \s ([\d.]+) \s seconds\)
/x;
( $out{total_idat_length} ) = $out{stdout}
=~ /Total length of data found in IDAT chunks\s+=\s+([\d.]+)/;
return $self->results( \%out );
}
sub set_options {
my $self = shift;
get_args_as_hash( \@_, \my %args, {}, [], [ %Valid_Options ] )
or croak $@;
$self->reset_options;
keys %args;
my %shell_args = reverse %Valid_Options;
while ( my ( $key, $value ) = each %args ) {
$key = $shell_args{$key}
unless exists $Valid_Options{$key};
$self->$key( $value );
}
return 1;
}
sub reset_options {
my $self = shift;
$self->$_(undef) for keys %Valid_Options;
return 1;
}
sub _make_options {
my $self = shift;
my @options;
for my $opt ( keys %Valid_Options ) {
my $value = $self->$opt;
next
unless defined $value;
if ( ref $value eq 'ARRAY' ) {
if ( $opt eq 'verbose' ) {
push @options, ('-v') x @$value;
next;
}
push @options, map { $Valid_Options{$opt} => $_ } @$value;
}
else {
push @options, $Valid_Options{$opt},
exists $No_Arg_Options{$opt} ? () : $value;
}
}
return @options;
}
sub _set_error {
my ( $self, $error ) = @_;
$self->error($error);
return;
}
1;
__END__
=head1 NAME
App::PNGCrush - Perl wrapper around ``pngcrush'' program
=head1 SYNOPSIS
use strict;
use warnings;
use App::PNGCrush;
my $crush = App::PNGCrush->new;
# let's use best compression and remove a few chunks
$crush->set_options(
qw( -d OUT_DIR -brute 1 ),
remove => [ qw( gAMA cHRM sRGB iCCP ) ],
);
my $out_ref = $crush->run('picture.png')
or die "Error: " . $crush->error;
print "Size reduction: $out_ref->{size}%\n"
. "IDAT reduction: $out->{idat}%\n";
=head1 DESCRIPTION
The module is a simple wrapper around ``pngcrush'' program. The program
is free open source and you can obtain it from
L on Debian systems you
can find it in the repos: C
I needed this module to utilize only little subsection of C's
functionality, if you would like some features added, I am more than open
for suggestions.
=head1 CONSTRUCTOR
=head2 C
my $crush = App::PNGCrush->new;
my $crush = App::PNGCrush->new( max_time => 300 );
Creates a new App::PNGCrush object. Arguments are optional and passed
as key/value pairs with keys being L methods and values
being the values for those methods, here you can set some options
controlling how C will be run. Generally, you'd worry only
about C (which B to C<300> seconds in C)
and set it to a higher value if you are about to process large images
with brute force.
=head1 METHODS
=head2 C
my $results_ref = $crush->run('pic.png')
or die $crush->error;
my $results_ref = $crush->run('pic.png', opts => [ qw(custom stuff) ] );
Instructs the object to run C. The first argument is mandatory
and must be a filename which will be passed to C as input file.
Takes one optional argument (so far), which is passed as key/value
pair; the key being C and value being an arrayref of custom options
you want to give to C (those will bypass shell proccessing).
Generally the custom options option is in here "just in case" and B
method (see below).>
Returns either C or an empty list (depending on the context)
if an error occured and the reason for the error will be available via
C method. On success returns a hashref with the following
keys/values:
$VAR1 = {
'total_idat_length' => '1880',
'cpu' => {
'decoding' => '0.010',
'other' => '0.050',
'total' => '0.210',
'encoding' => '0.150'
},
'stderr' => '',
'status' => '0',
'idat' => '0.80',
'stdout' => '| pngcrush 1.6.4 .. blah blah full STDOUT here',
'size' => '1.56'
};
=head3 C
{ 'size' => '1.56', }
The C key will contain percentage of filesize reduction.
=head3 C
{ 'idat' => '0.80', }
The C key will contain the percentage of IDAT size reduction.
=head3 C
{ 'total_idat_length' => '1880', }
The C key will contain total length of data found in
IDAT chunks.
=head3 C
'cpu' => {
'decoding' => '0.010',
'other' => '0.050',
'total' => '0.210',
'encoding' => '0.150'
},
The C key will contain a hashref with with four keys:
C, C, C and C with values being
number of seconds it took to process.
=head3 C
{ 'stderr' => '', }
The C key will contain any collected data from STDERR while
C was running.
=head3 C
{ 'stdout' => '| pngcrush 1.6.4 .. blah blah full STDOUT here', }
The C key will contain any collected data from STDOUT while
C was running.
=head3 C
{ 'status' => '0' }
The C key will contain the exit code of C.
=head2 C
my $ret_ref = $crush->run('some.png')
or die $crush->error;
If C failed it will return either C or an empty list depending
on the context and the reason for failure will be available via C
method. Takes no arguments, returns a human parsable error message
explaining why C failed.
=head2 C
my $results_ref = $crush->results;
Must be called after a successful call to C. Takes no arguments,
returns the exact same hashref last call to C returned.
=head2 C
$crush->set_options(
qw( -d OUT_DIR -brute 1 ),
remove => [ qw( gAMA cHRM sRGB iCCP ) ],
);
Always returns a true value. Sets the options with which to run
C. As argument takes a list of key/value pairs of
either standard C options or more verbose names this module
offers (see below). If you want to B certain option pass values
as B, thus if on a command line you'd write
C<< pngcrush -rem gAMA -rem cHRM -rem sRGB ... >> you'd use
C<< ->set_options( '-rem' => [ qw( gAMA cHRM sRGB iCPP ) ] ) >>.
B if C option does not take an argument you B
give it a value of C<1> when setting it via C method.
For C<-v> option you can set it to value C<2> to repeat twice
(aka uber verbose). B to individual option setting methods.
B call to C will call C method
(see below) before setting any of your options, thus whatever you
don't specify will not be passed to C
=head2 C
$crush->reset_options;
Always returns a true value, takes no arguments. Instructs the object
to reset all C options.
=head2 individual option methods
Module provides methods to set (almost) all C options individually
You'd probably would want to use C method (see above)
in most cases. See C method which describes how to
repeat options and how to set options which take no arguments in
C. The following is the list of methods (on the left) and
corresponding C options they set (on the right); some
options were deemed useless to the module and were not included
(this is as of C version 1.6.4):
already_size -already
bit_depth -bit_depth
background -bkgd
brute_force -brute
color_type -c
color_counting -cc
output_dir -d
double_image_gamma -dou
output_extension -e
filter -f
fix_fatal -fix
output_force -force
gamma -g
itxt -itxt
level -l
method -m
maximum_idat -max
no_output -n
no_color_counting -no_cc
plte_length -plte_len
remove -rem
replace_gamma -replace_gamma
resolution -res
save_unknown -save
srgb -srgb
text -text
transparency -trns
window_size -w
strategy -z
insert_ztxt -zitxt
ztxt -ztxt
verbose -v
See C manpage (C or C)
for descriptions of these options.
Out of those listed above the following C options do not take
arguments,
thus to set these you'd need to pass C<1> as an argument to the option
setting method (except for C which can take a value of C<2> to
indicate double verboseness (equivalent to passing C<-v -v> to
C)
brute_force
color_counting
double_image_gamma
fix_fatal
output_force
no_output
no_color_counting
save_unknown
verbose
=head2 C
my $proc_reliable_obj = $crush->proc;
$crush->proc( Proc::Reliable->new );
Returns a currently used L object used under the hood,
thus you could dynamically set arguments as
C<< $crush->proc->max_time(300) >>. When called with an argument
it must be a C object which will replace the currently
used one (and you just SOO don't wanna do this, do you?)
=head1 AUTHOR
Zoffix Znet, C<< >>
(L, L)
=head1 BUGS
Please report any bugs or feature requests to C, or through
the web interface at L. I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc App::PNGCrush
You can also look for information at:
=over 4
=item * RT: CPAN's request tracker
L
=item * AnnoCPAN: Annotated CPAN documentation
L
=item * CPAN Ratings
L
=item * Search CPAN
L
=back
=head1 COPYRIGHT & LICENSE
Copyright 2008 Zoffix Znet, all rights reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut