App-ImageMagickUtils

 view release on metacpan or  search on metacpan

lib/App/ImageMagickUtils.pm  view on Meta::CPAN

package App::ImageMagickUtils;

use 5.010001;
use strict;
use warnings;
use Log::ger;

use Perinci::Exporter;

our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
our $DATE = '2024-12-21'; # DATE
our $DIST = 'App-ImageMagickUtils'; # DIST
our $VERSION = '0.024'; # VERSION

our %SPEC;

$SPEC{':package'} = {
    v => 1.1,
    summary => 'Utilities related to ImageMagick',
};

our %argspec0plus_files = (
    files => {
        'x.name.is_plural' => 1,
        'x.name.singular' => 'file',
        schema => ['array*' => of => 'filename*'],
        req => 1,
        pos => 0,
        slurpy => 1,
    },
);

our %argspec0plus_files__comp_png = (
    files => {
        'x.name.is_plural' => 1,
        'x.name.singular' => 'file',
        schema => ['array*' => of => 'filename*'],
        'x.completion' => [filename => {file_ext_filter=>'png'}],
        req => 1,
        pos => 0,
        slurpy => 1,
    },
);

our %argspec0plus_files__comp_jpg = (
    files => {
        'x.name.is_plural' => 1,
        'x.name.singular' => 'file',
        schema => ['array*' => of => 'filename*'],
        'x.completion' => [filename => {file_ext_filter=>qr/^(jpe?g)$/i}],
        req => 1,
        pos => 0,
        slurpy => 1,
    },
);

our %argspec0plus_files__comp_pdf = (
    files => {
        'x.name.is_plural' => 1,
        'x.name.singular' => 'file',
        schema => ['array*' => of => 'filename*'],
        'x.completion' => [filename => {file_ext_filter=>'pdf'}],
        req => 1,
        pos => 0,
        slurpy => 1,
    },
);

our %argspecs_delete = (
    delete_original => {
        summary => 'Delete (unlink) the original file after downsizing',
        schema => 'bool*',
        description => <<'MARKDOWN',

See also the `trash_original` option.

MARKDOWN
        cmdline_aliases => {D=>{}},
    },
    trash_original => {
        summary => 'Trash the original file after downsizing',
        schema => 'bool*',
        description => <<'MARKDOWN',

This option uses the <pm:File::Trash::FreeDesktop> module to do the trashing.
Compared to deletion, with this option you can still restore the trashed
original files from the Trash directory.

See also the `delete_original` option.

MARKDOWN
        cmdline_aliases => {T=>{}},
    },
);

our %argspecopt_quality = (
    quality => {
        summary => 'Quality setting (for JPEG/PNG), 1 (best compression, worst quality) to 100 (least compression, best quality)',

lib/App/ImageMagickUtils.pm  view on Meta::CPAN

        my $ext = $downsized ? ".$downsize_to-q$q.jpg" : ".q$q.jpg";
        $output_file =~ s/(\.\w{3,4})?\z/($1 eq ".jpg" ? "" : $1) . $ext/e;

        push @convert_args, (
            $output_file,
        );

        if ($args{-dry_run}) {
            log_info "[DRY-RUN] Running $convert_path with args %s ...", \@convert_args;
            $num_success++;
            next FILE;
        }

        log_info "Running $convert_path with args %s ...", \@convert_args;
        IPC::System::Options::system(
            $convert_path, @convert_args,
        );
        if ($?) {
            my ($exit_code, $signal, $core_dump) = ($? < 0 ? $? : $? >> 8, $? & 127, $? & 128);
            log_error "convert for $file failed: exit_code=$exit_code, signal=$signal, core_dump=$core_dump";
        } else {
            if ($args{trash_original}) {
                require File::Trash::FreeDesktop;
                $trash //= File::Trash::FreeDesktop->new;
                log_info "Trashing original file %s ...", $file;
                # will die upon failure, currently we don't trap
                $trash->trash($file);
            } elsif ($args{delete_original}) {
                # currently we ignore the results
                log_info "Deleting original file %s ...", $file;
                unlink $file;
            }
            $num_success++;
        }
    }

    $num_success == 0 ? [500, "All files failed"] : [200];
}

$SPEC{convert_image_to} = {
    v => 1.1,
    summary => 'Convert images using ImageMagick\'s \'convert\' utility, with multiple file support and automatic output naming',
    description => <<'MARKDOWN',

This is a simple wrapper to ImageMagick's `convert` utility to let you process
multiple files using a single command:

    % convert-image-to --to pdf *.jpg

is basically equivalent to:

    % for f in *.jpg; do convert "$f" "$f.pdf"; done

MARKDOWN
    args => {
        %argspec0plus_files,
        %argspecopt_quality,
        to => {
            schema => ['str*', match=>qr/\A\w+\z/],
            req => 1,
            examples => [qw/pdf jpg png/], # for tab completion
        },
        %argspecs_delete,
    },
    #features => {
    #    dry_run => 1,
    #},
    deps => {
        prog => 'convert',
    },
    examples => [
    ],
};
sub convert_image_to {
    require IPC::System::Options;
    require Perinci::Object;
    require Process::Status;

    my %args = @_;

    my $to = $args{to} or return [400, "Please specify target format in `to`"];

    my $envres = Perinci::Object::envresmulti();
    my $trash;
    for my $file (@{$args{files}}) {
        log_info "Processing file %s ...", $file;
        IPC::System::Options::system(
            {log=>1},
            "convert", ($args{quality} ? ("-quality", $args{quality}) : ()), $file, "$file.$to",
        );
        my $ps = Process::Status->new;

        if ($ps->is_success) {
            $envres->add_result(200, "OK", {item_id=>$file});
            if ($args{trash_original}) {
                require File::Trash::FreeDesktop;
                $trash //= File::Trash::FreeDesktop->new;
                log_info "Trashing original file %s ...", $file;
                # will die upon failure, currently we don't trap
                $trash->trash($file);
            } elsif ($args{delete_original}) {
                # currently we ignore the result of deletion
                log_info "Deleting original file %s ...", $file;
                unlink $file;
            }
        } else {
            $envres->add_result(500, "Failed (exit code ".$ps->exitstatus.")", {item_id=>$file});
        }
    }
    $envres->as_struct;
}

$SPEC{convert_image_to_pdf} = {
    v => 1.1,
    summary => 'Convert images to PDF using ImageMagick\'s \'convert\' utility',
    description => <<'MARKDOWN',

This is a wrapper to `convert-image-to`, with `--to` set to `pdf`:

    % convert-image-to-pdf *.jpg

is equivalent to:

    % convert-image-to --to pdf *.jpg

which in turn is equivalent to:

    % for f in *.jpg; do convert "$f" "$f.pdf"; done

MARKDOWN
    args => {
        %argspec0plus_files,
        %argspecs_delete,
    },
    #features => {
    #    dry_run => 1,
    #},
    deps => {
        prog => 'convert',
    },
    examples => [
    ],
};
sub convert_image_to_pdf {
    my %args = @_;
    convert_image_to(%args, to=>'pdf');
}

$SPEC{convert_image_to_jpg} = {
    v => 1.1,
    summary => 'Convert images to JPG using ImageMagick\'s \'convert\' utility',
    description => <<'MARKDOWN',

This is a wrapper to `convert-image-to`, with `--to` set to `jpg`:

    % convert-image-to-pdf *.png

is equivalent to:

    % convert-image-to --to jpg *.png

which in turn is equivalent to:

    % for f in *.png; do convert "$f" "$f.jpg"; done

MARKDOWN
    args => {
        %argspec0plus_files,
        %argspecopt_quality,
        %argspecs_delete,
    },
    #features => {
    #    dry_run => 1,
    #},
    deps => {
        prog => 'convert',
    },
    examples => [
    ],
};
sub convert_image_to_jpg {
    my %args = @_;
    convert_image_to(%args, to=>'jpg');
}

$SPEC{convert_image_to_png} = {
    v => 1.1,
    summary => 'Convert images to JPG using ImageMagick\'s \'convert\' utility',
    description => <<'MARKDOWN',

This is a wrapper to `convert-image-to`, with `--to` set to `png`:

    % convert-image-to-png *.jpg

is equivalent to:

    % convert-image-to --to png *.jpg

which in turn is equivalent to:

    % for f in *.jpg; do convert "$f" "$f.png"; done

MARKDOWN
    args => {
        %argspec0plus_files,
        %argspecopt_quality,
        %argspecs_delete,
    },
    #features => {
    #    dry_run => 1,
    #},
    deps => {
        prog => 'convert',
    },
    examples => [
    ],
};
sub convert_image_to_png {
    my %args = @_;
    convert_image_to(%args, to=>'png');
}

1;
# ABSTRACT: Utilities related to ImageMagick

__END__

=pod

=encoding UTF-8

=head1 NAME

App::ImageMagickUtils - Utilities related to ImageMagick

=head1 VERSION

This document describes version 0.024 of App::ImageMagickUtils (from Perl distribution App-ImageMagickUtils), released on 2024-12-21.

=head1 DESCRIPTION

This distribution includes the following CLI utilities related to ImageMagick:

=over

=item 1. L<calc-image-resized-size>

=item 2. L<convert-image-to>

=item 3. L<convert-image-to-jpg>

=item 4. L<convert-image-to-pdf>

=item 5. L<convert-image-to-png>

=item 6. L<downsize-image>

=item 7. L<image-resize-notation-to-human>

=item 8. L<img2jpg>

=item 9. L<img2pdf>

=item 10. L<img2png>

=item 11. L<jpg2png>

=item 12. L<png2jpg>

=back

=head1 FUNCTIONS


=head2 convert_image_to

Usage:

 convert_image_to(%args) -> [$status_code, $reason, $payload, \%result_meta]

Convert images using ImageMagick's 'convert' utility, with multiple file support and automatic output naming.

This is a simple wrapper to ImageMagick's C<convert> utility to let you process
multiple files using a single command:

 % convert-image-to --to pdf *.jpg

is basically equivalent to:

 % for f in *.jpg; do convert "$f" "$f.pdf"; done

This function is not exported.

Arguments ('*' denotes required arguments):

=over 4

=item * B<delete_original> => I<bool>

Delete (unlink) the original file after downsizing.

See also the C<trash_original> option.

=item * B<files>* => I<array[filename]>

(No description)

=item * B<quality> => I<int> (default: 92)

Quality setting (for JPEGE<sol>PNG), 1 (best compression, worst quality) to 100 (least compression, best quality).

=item * B<to>* => I<str>

(No description)

=item * B<trash_original> => I<bool>

Trash the original file after downsizing.

This option uses the L<File::Trash::FreeDesktop> module to do the trashing.
Compared to deletion, with this option you can still restore the trashed
original files from the Trash directory.

See also the C<delete_original> option.


=back

Returns an enveloped result (an array).

First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.

Return value:  (any)



=head2 convert_image_to_jpg

Usage:

 convert_image_to_jpg(%args) -> [$status_code, $reason, $payload, \%result_meta]

Convert images to JPG using ImageMagick's 'convert' utility.

This is a wrapper to C<convert-image-to>, with C<--to> set to C<jpg>:

 % convert-image-to-pdf *.png

is equivalent to:

 % convert-image-to --to jpg *.png

which in turn is equivalent to:

 % for f in *.png; do convert "$f" "$f.jpg"; done

This function is not exported.

Arguments ('*' denotes required arguments):

=over 4

=item * B<delete_original> => I<bool>

Delete (unlink) the original file after downsizing.

See also the C<trash_original> option.

=item * B<files>* => I<array[filename]>

(No description)

=item * B<quality> => I<int> (default: 92)

Quality setting (for JPEGE<sol>PNG), 1 (best compression, worst quality) to 100 (least compression, best quality).

=item * B<trash_original> => I<bool>

Trash the original file after downsizing.

This option uses the L<File::Trash::FreeDesktop> module to do the trashing.
Compared to deletion, with this option you can still restore the trashed
original files from the Trash directory.

See also the C<delete_original> option.


=back

Returns an enveloped result (an array).

First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.

Return value:  (any)



=head2 convert_image_to_pdf

Usage:

 convert_image_to_pdf(%args) -> [$status_code, $reason, $payload, \%result_meta]

Convert images to PDF using ImageMagick's 'convert' utility.

This is a wrapper to C<convert-image-to>, with C<--to> set to C<pdf>:

 % convert-image-to-pdf *.jpg

is equivalent to:

 % convert-image-to --to pdf *.jpg

which in turn is equivalent to:

 % for f in *.jpg; do convert "$f" "$f.pdf"; done

This function is not exported.

Arguments ('*' denotes required arguments):

=over 4

=item * B<delete_original> => I<bool>

Delete (unlink) the original file after downsizing.

See also the C<trash_original> option.

=item * B<files>* => I<array[filename]>

(No description)

=item * B<trash_original> => I<bool>

Trash the original file after downsizing.

This option uses the L<File::Trash::FreeDesktop> module to do the trashing.
Compared to deletion, with this option you can still restore the trashed
original files from the Trash directory.

See also the C<delete_original> option.


=back

Returns an enveloped result (an array).

First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.

Return value:  (any)



=head2 convert_image_to_png

Usage:

 convert_image_to_png(%args) -> [$status_code, $reason, $payload, \%result_meta]

Convert images to JPG using ImageMagick's 'convert' utility.

This is a wrapper to C<convert-image-to>, with C<--to> set to C<png>:

 % convert-image-to-png *.jpg

is equivalent to:

 % convert-image-to --to png *.jpg

which in turn is equivalent to:

 % for f in *.jpg; do convert "$f" "$f.png"; done

This function is not exported.

Arguments ('*' denotes required arguments):

=over 4

=item * B<delete_original> => I<bool>

Delete (unlink) the original file after downsizing.

See also the C<trash_original> option.

=item * B<files>* => I<array[filename]>

(No description)

=item * B<quality> => I<int> (default: 92)

Quality setting (for JPEGE<sol>PNG), 1 (best compression, worst quality) to 100 (least compression, best quality).

=item * B<trash_original> => I<bool>

Trash the original file after downsizing.

This option uses the L<File::Trash::FreeDesktop> module to do the trashing.
Compared to deletion, with this option you can still restore the trashed
original files from the Trash directory.

See also the C<delete_original> option.


=back

Returns an enveloped result (an array).

First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.

Return value:  (any)



=head2 downsize_image

Usage:

 downsize_image(%args) -> [$status_code, $reason, $payload, \%result_meta]

Reduce image size, by default via compressing to JPEG quality 40 and downsizing to 1024p.

This utility uses L<convert> utility to compress an image into JPEG with
default quality of 40 and downsized to 1024p (shortest side to 1024px).

Output filenames are:



( run in 2.286 seconds using v1.01-cache-2.11-cpan-df04353d9ac )