File Coverage

lib/Color/Swatch/ASE/Reader.pm
Criterion Covered Total %
statement 101 117 86.3
branch 27 46 58.7
condition 1 3 33.3
subroutine 22 26 84.6
pod 3 3 100.0
total 154 195 78.9


line stmt bran cond sub pod time code
1 2     2   32680 use 5.010; # unpack >
  2         7  
  2         73  
2 2     2   11 use strict;
  2         5  
  2         58  
3 2     2   40 use warnings;
  2         4  
  2         66  
4 2     2   2681 use utf8;
  2         24  
  2         11  
5              
6             package Color::Swatch::ASE::Reader;
7              
8             our $VERSION = '0.001003';
9              
10             # ABSTRACT: Low-Level ASE (Adobe Swatch Exchange) File decoder
11              
12             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
13              
14 2     2   2689 use Encode qw( decode );
  2         24423  
  2         3307  
15              
16             ## no critic (ValuesAndExpressions::ProhibitEscapedCharacters)
17             my $BLOCK_GROUP_START = "\x{c0}\x{01}";
18             my $BLOCK_GROUP_END = "\x{c0}\x{02}";
19             my $BLOCK_COLOR = "\x{00}\x{01}";
20             my $UTF16NULL = "\x{00}\x{00}";
21             ## use critic
22              
23              
24              
25              
26              
27              
28              
29             sub read_file {
30 1     1 1 69 my ( $class, $file ) = @_;
31 1         12 require Path::Tiny;
32 1         4 return $class->read_string( Path::Tiny::path($file)->slurp_raw );
33             }
34              
35              
36              
37              
38              
39              
40              
41             sub read_filehandle {
42 0     0 1 0 my ( $class, $filehandle ) = @_;
43 0         0 return $class->read_string( scalar <$filehandle> );
44             }
45              
46              
47              
48              
49              
50              
51              
52             sub read_string {
53 1     1 1 266 my ( $class, $string ) = @_;
54 1         3 my $clone = "$string";
55              
56 1         5 my $signature = $class->_read_signature( \$clone );
57 1         5 my $version = $class->_read_version( \$clone );
58 1         5 my $numblocks = $class->_read_numblocks( \$clone );
59              
60 1         2 my @blocks;
61              
62 1         3 for my $id ( 1 .. $numblocks ) {
63 7         19 push @blocks, $class->_read_block( \$clone, $id, );
64             }
65              
66 1 50       4 if ( length $clone ) {
67 0         0 warn +( ( length $clone ) . ' bytes of unhandled data' );
68             }
69              
70 1         7 return { signature => $signature, version => $version, blocks => \@blocks, };
71              
72             }
73              
74             sub _read_bytes {
75 44     44   63 my ( undef, $string, $num, $decode ) = @_;
76 44 50       37 return if ( length ${$string} ) < $num;
  44         87  
77 44         42 my $chars = substr ${$string}, 0, $num, q[];
  44         59  
78 44 100       118 return unpack $decode, $chars if $decode;
79 19         33 return $chars;
80             }
81              
82             sub _read_signature {
83 1     1   1 my ( $class, $string ) = @_;
84 1         4 my $signature = $class->_read_bytes( $string, 4 );
85 1 50 33     12 die 'No ASEF signature' if not defined $signature or q[ASEF] ne $signature;
86 1         3 return $signature;
87             }
88              
89             sub _read_version {
90 1     1   2 my ( $class, $string ) = @_;
91 1         4 my (@version) = $class->_read_bytes( $string, 4, q[nn] );
92 1 50       4 die 'No VERSION header' if @version != 2;
93 1         2 return \@version;
94             }
95              
96             sub _read_numblocks {
97 1     1   1 my ( $class, $string ) = @_;
98 1         3 my $blocks = $class->_read_bytes( $string, 4, q[N] );
99 1 50       4 die 'No NUM BLOCKS header' if not defined $blocks;
100 1         2 return $blocks;
101             }
102              
103             sub _read_block_group {
104 6     6   7 my ( $class, $string ) = @_;
105 6         14 return $class->_read_bytes( $string, 2, q[n] );
106             }
107              
108             sub _read_group_end {
109 1     1   2 my ( undef, $group, $label ) = @_;
110             return {
111 1 50       7 type => 'group_end',
    50          
112             ( $group ? ( group => $group ) : () ),
113             ( $label ? ( label => $label ) : () ),
114             };
115             }
116              
117             sub _read_group_start {
118 1     1   3 my ( undef, $group, $label ) = @_;
119             return {
120 1 50       11 type => 'group_start',
    50          
121             ( $group ? ( group => $group ) : () ),
122             ( $label ? ( label => $label ) : () ),
123             };
124             }
125              
126             sub _read_rgb {
127 5     5   5 my ( $class, $block_body ) = @_;
128 5         10 return $class->_read_bytes( $block_body, 12, 'f>f>f>' );
129             }
130              
131             sub _read_lab {
132 0     0   0 my ( $class, $block_body ) = @_;
133 0         0 return $class->_read_bytes( $block_body, 12, 'f>f>f>' );
134             }
135              
136             sub _read_cmyk {
137 0     0   0 my ( $class, $block_body ) = @_;
138 0         0 return $class->_read_bytes( $block_body, 16, 'f>f>f>f>' );
139             }
140              
141             sub _read_gray {
142 0     0   0 my ( $class, $block_body ) = @_;
143 0         0 return $class->_read_bytes( $block_body, 4, 'f>' );
144             }
145              
146             my $color_table = {
147             q[RGB ] => '_read_rgb',
148             q[LAB ] => '_read_lab',
149             q[CMYK] => '_read_cymk',
150             q[Gray] => '_read_gray',
151             };
152              
153             sub _read_color_model {
154 5     5   5 my ( $class, $id, $block_body ) = @_;
155 5         10 my $model = $class->_read_bytes( $block_body, 4 );
156 5 50       11 if ( not defined $model ) {
157 0         0 die "No COLOR MODEL for block $id";
158             }
159 5 50       9 if ( not exists $color_table->{$model} ) {
160 0         0 die "Unsupported model $model";
161             }
162 5         10 return $model;
163             }
164              
165             sub _read_color_type {
166 5     5   6 my ( $class, $block_body ) = @_;
167 5         8 my $type = $class->_read_bytes( $block_body, 2, q[n] );
168 5         7 return $type;
169             }
170              
171             sub _read_color {
172 5     5   8 my ( $class, $id, $group, $label, $block_body ) = @_;
173              
174 5         11 my $model = $class->_read_color_model( $id, $block_body );
175              
176 5         5 my @values;
177              
178 5         25 my $method = $class->can( $color_table->{$model} );
179 5         11 @values = $class->$method($block_body);
180              
181 5         13 my $type = $class->_read_color_type($block_body);
182             return {
183 5 50       42 type => 'color',
    50          
    50          
184             ( $group ? ( group => $group ) : () ),
185             ( $label ? ( label => $label ) : () ),
186             ( $model ? ( model => $model ) : () ),
187             values => \@values,
188             color_type => $type,
189             };
190              
191             }
192              
193             sub _read_block_label {
194 6     6   8 my ( undef, $string ) = @_;
195 6         6 my ( $label, $rest ) = ( ${$string} =~ /\A(.*?)${UTF16NULL}(.*\z)/msx );
  6         75  
196 6 50       14 if ( defined $rest ) {
197 6         6 ${$string} = "$rest";
  6         8  
198             }
199             else {
200 0         0 ${$string} = q[];
  0         0  
201             }
202 6         25 return decode( 'UTF-16BE', $label, Encode::FB_CROAK );
203             }
204              
205             sub _read_block_type {
206 7     7   8 my ( $class, $string, $id ) = @_;
207 7         15 my $type = $class->_read_bytes( $string, 2 );
208 7 50       13 die "No BLOCK TYPE for block $id" if not defined $type;
209 7         47 return $type;
210             }
211              
212             sub _read_block_length {
213 7     7   11 my ( $class, $string, $id ) = @_;
214 7         13 my $length = $class->_read_bytes( $string, 4, q[N] );
215 7 50       14 die "No BLOCK LENGTH for block $id" if not defined $length;
216 7 50       20 if ( ( length ${$string} ) < $length ) {
  7         13  
217 0         0 warn "Possibly corrupt file, EOF before length $length in block $id";
218             }
219 7         10 return $length;
220             }
221              
222             sub _read_block {
223 7     7   7 my ( $class, $string, $id, ) = @_;
224 7         15 my $type = $class->_read_block_type($string);
225 7         15 my $length = $class->_read_block_length($string);
226 7         8 my $block_body;
227             my $group;
228 0         0 my $label;
229 7 100       11 if ( $length > 0 ) {
230 6         13 $block_body = $class->_read_bytes( $string, $length );
231 6         16 $group = $class->_read_block_group( \$block_body );
232 6         15 $label = $class->_read_block_label( \$block_body );
233             }
234              
235 7 100       4117 if ( $BLOCK_GROUP_END eq $type ) {
236 1         4 return $class->_read_group_end( $group, $label, );
237             }
238 6 100       12 if ( $BLOCK_GROUP_START eq $type ) {
239 1         9 return $class->_read_group_start( $group, $label, );
240             }
241 5 50       9 if ( $BLOCK_COLOR eq $type ) {
242 5         14 return $class->_read_color( $id, $group, $label, \$block_body, );
243             }
244 0           die "Unknown type $type";
245              
246             }
247              
248             1;
249              
250             __END__