File Coverage

blib/lib/Color/Swatch/ASE/Reader.pm
Criterion Covered Total %
statement 97 113 85.8
branch 27 46 58.7
condition 1 3 33.3
subroutine 21 25 84.0
pod 3 3 100.0
total 149 190 78.4


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