File Coverage

blib/lib/IIIF/Magick.pm
Criterion Covered Total %
statement 55 65 84.6
branch 34 44 77.2
condition 11 18 61.1
subroutine 8 10 80.0
pod 5 6 83.3
total 113 143 79.0


line stmt bran cond sub pod time code
1             package IIIF::Magick;
2 3     3   137922 use 5.014001;
  3         27  
3              
4             our $VERSION = "0.06";
5              
6 3     3   736 use parent 'Exporter';
  3         502  
  3         14  
7             our @EXPORT = qw(info available convert convert_command convert_args);
8              
9 3     3   1771 use IPC::Cmd qw(can_run);
  3         169094  
  3         166  
10 3     3   26 use List::Util qw(min);
  3         6  
  3         2740  
11              
12             sub available {
13 2   33 2 1 154 return can_run("magick") || ( can_run("identify") && can_run("convert") );
14             }
15              
16             sub info {
17 0     0 1 0 my $file = shift;
18              
19 0 0       0 -f $file or die "$file: No such file\n";
20 0         0 my $cmd = join ' ', map shell_quote($_), qw(identify -format %Wx%H), $file;
21              
22 0 0       0 ( `$cmd` =~ /^(\d+)x(\d+)$/ )
23             or die "$file: Failed to get image dimensions";
24              
25             return {
26 0         0 '@context' => 'http://iiif.io/api/image/3/context.json',
27             type => 'ImageService3',
28             protocol => 'http://iiif.io/api/image',
29             width => 1 * $1,
30             height => 1 * $2,
31             @_
32             };
33             }
34              
35             sub convert_args {
36 21     21 1 70 my $req = shift;
37 21         25 my @args;
38              
39             # apply region
40 21 100       181 if ( $req->{region} eq 'square' ) {
    100          
    100          
41              
42             # could be simpler in ImageMagick 7:
43             # push @args, qw(-gravity center -crop), "%[fx:w>h?h:w]x%[fx:w>h?h:w]+0+0";
44 1         6 push @args, qw(-set option:distort:viewport
45             %[fx:w>h?h:w]x%[fx:w>h?h:w]+%[fx:w>h?(w-h)/2:0]+%[fx:w>h?0:(h-w)/2]
46             -filter point -distort SRT 0 +repage);
47             }
48             elsif ( my $region_px = $req->{region_px} ) {
49 1         4 my ( $x, $y, $w, $h ) = @$region_px;
50 1         5 @args = ( '-crop', "${w}x$h+$x+$y" );
51             }
52             elsif ( my $region_pct = $req->{region_pct} ) {
53 3         9 my ( $x, $y, $w, $h ) = @$region_pct;
54              
55 3 100 100     14 if ( $x || $y ) {
56 2         4 my $px = $x / 100;
57 2         4 my $py = $y / 100;
58 2         18 push @args, '-set', 'page', "-%[fx:w*$px]-%[fx:h*$py]";
59             }
60              
61             # could also be simpler in ImageMagick 7
62 3         16 push @args, '-crop', "${w}x$h%+0+0";
63             }
64              
65             # apply size
66 21 100       46 if ( $req->{size_pct} ) {
    100          
67 2         8 push @args, '-resize', $req->{size_pct} . '%';
68             }
69             elsif ( $req->{size_px} ) {
70 6         7 my ( $x, $y ) = @{ $req->{size_px} };
  6         12  
71              
72 6 100 100     186 if ( $x && !$y ) {
    100 66        
    100          
73 1         4 push @args, '-resize', "${x}";
74             }
75             elsif ( !$x && $y ) {
76 1         4 push @args, '-resize', "x${y}";
77             }
78             elsif ( $req->{ratio} ) {
79 2         6 push @args, '-resize', "${x}x$y";
80             }
81             else {
82 2         8 push @args, '-resize', "${x}x$y!";
83             }
84             }
85              
86             # apply rotation
87 21 100       31 push @args, '-flop' if $req->{mirror};
88 21 100       39 if ( my $degree = $req->{degree} ) {
89 2         5 push @args, '-rotate', $degree;
90 2 100       6 if ( $degree - 90 * int( $degree / 90 ) ) {
91 1         2 push @args, '-background', 'none';
92             }
93             }
94              
95             # apply quality
96 21 100       44 if ( $req->{quality} eq 'gray' ) {
    100          
97 1         2 push @args, qw(-colorspace Gray);
98             }
99             elsif ( $req->{quality} eq 'bitonal' ) {
100 1         3 push @args, qw(-monochrome -colors 2);
101             }
102              
103 21         82 return @args;
104             }
105              
106             sub convert_command {
107 1     1 1 4 my ( $req, $in, $out ) = splice @_, 0, 3;
108              
109 1         4 my @cmd = ( 'convert', @_, convert_args($req) );
110 1 50 33     5 push @cmd, $in if defined $in and $in ne '';
111 1 50 33     5 push @cmd, $out if defined $out and $out ne '';
112 1 50       8 unshift @cmd, "magick" if can_run("magick");
113              
114 1         110973 return join ' ', map shell_quote($_), @cmd;
115             }
116              
117             sub convert {
118 0     0 1 0 my $command = convert_command(@_);
119 0         0 qx{$command};
120 0         0 return !$?;
121             }
122              
123             # adopted from
124             sub shell_quote {
125 3     3 0 76 my $arg = shift;
126              
127 3 50       11 if ( $^O eq 'MSWin32' ) {
    0          
128 3 100       22 if ( $arg !~ /\A[\w_+-]+\z/ ) {
129 1         10 $arg =~ s/\\(?=\\*(?:"|$))/\\\\/g;
130 1         4 $arg =~ s/"/\\"/g;
131 1         13 return qq("$arg");
132             }
133             }
134             elsif ( $arg !~ qr{\A[\w,_+/.-]+\z} ) {
135 0         0 $arg =~ s/'/'"'"'/g;
136 0         0 return "'$arg'";
137             }
138              
139 2         6 return $arg;
140             }
141              
142             1;
143             __END__