File Coverage

lib/Graphics/Toolkit/Color/Space/Format.pm
Criterion Covered Total %
statement 165 168 98.2
branch 96 114 84.2
condition 19 42 45.2
subroutine 33 34 97.0
pod 0 22 0.0
total 313 380 82.3


line stmt bran cond sub pod time code
1              
2             # bidirectional conversion of value tuples (ARRAY) into different string and other formats
3             # values themself can have space dependant extra shape, suffixes, etc.
4              
5             package Graphics::Toolkit::Color::Space::Format;
6 34     34   224216 use v5.12;
  34         99  
7 34     34   142 use warnings;
  34         51  
  34         78350  
8              
9             my $number_form = '-?(?:\d+|\d+\.\d+|\.\d+)';
10              
11             #### constructor, building attr data ###################################
12             sub new { # -, $:Basis -- ~|@~val_form, , ~|@~suffix --> :_
13 284     284 0 4152 my ($pkg, $basis, $value_form, $prefix, $suffix) = @_;
14 284 100       603 return 'First argument has to be an Color::Space::Basis reference !'
15             unless ref $basis eq 'Graphics::Toolkit::Color::Space::Basis';
16              
17 283         528 my $count = $basis->axis_count;
18 283 100       511 $value_form = $number_form unless defined $value_form;
19 283 100       737 $value_form = [($value_form) x $count] unless ref $value_form;
20 283 50       541 return "Definition of the value format has to be as ARRAY reference" if ref $value_form ne 'ARRAY';
21 283 50 33     421 $value_form = [ map {(defined $_ and $_) ? $_ : $number_form } @$value_form]; # fill missing defs with default
  871         2713  
22 283 100       682 return 'Need a value form definition for every axis!' unless @$value_form == $count;
23              
24 282         510 $suffix = expand_suffix_def( $basis, $suffix ) ;
25 282 100       501 return $suffix unless ref $suffix;
26              
27             # format --> tuple
28 2243     2243   4226 my %deformats = ( hash => sub { tuple_from_hash(@_) },
29 2203     2203   3754 named_array => sub { tuple_from_named_array(@_) },
30 2166     2166   3693 named_string => sub { tuple_from_named_string(@_) },
31 2257     2257   3949 css_string => sub { tuple_from_css_string(@_) },
32 281         2678 );
33             # tuple --> format
34 38     38   51 my %formats = (list => sub { @{$_[1]} }, # 1, 2, 3
  38         285  
35 4     4   21 hash => sub { $basis->long_name_hash_from_tuple($_[1]) }, # { red => 1, green => 2, blue => 3 }
36 3     3   15 char_hash => sub { $basis->short_name_hash_from_tuple($_[1]) }, # { r =>1, g => 2, b => 3 }
37 5     5   22 named_array => sub { [$basis->space_name, @{$_[1]}] }, # ['rgb',1,2,3]
  5         36  
38 12     12   45 named_string => sub { $_[0]->named_string_from_tuple($_[1]) }, # 'rgb: 1, 2, 3'
39 38     38   137 css_string => sub { $_[0]->css_string_from_tuple($_[1]) }, # 'rgb(1,2,3)'
40 281         2878 );
41 281         2349 bless { basis => $basis, deformatter => \%deformats, formatter => \%formats,
42             value_form => $value_form, prefix => $prefix, suffix => $suffix,
43             value_numifier => { into_numric => '', from_numeric => '' },
44             }
45             }
46              
47             sub expand_suffix_def {
48 530     530 0 879 my ($basis, $suffix) = @_;
49 530         936 my $count = $basis->axis_count;
50 530 100       1174 $suffix = [('') x $count] unless defined $suffix;
51 530 100       904 $suffix = [($suffix) x $count] unless ref $suffix;
52 530 100       1043 return 'need an ARRAY as definition of axis value suffix' unless ref $suffix eq 'ARRAY';
53 529 100       4420 return 'definition of axis value suffix has to have same lengths as basis' unless @$suffix == $count;
54 526         897 return $suffix;
55             }
56             sub get_suffix {
57 2621     2621 0 3947 my ($self, $suffix) = @_;
58 2621 100       6444 return $self->{'suffix'} unless defined $suffix;
59 248         688 expand_suffix_def( $self->{'basis'}, $suffix );
60             }
61              
62             sub add_formatter {
63 31     31 0 56 my ($self, $format, $code) = @_;
64 31 50 33     163 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
65 31 50       73 return if $self->has_formatter( $format );
66 31         68 $self->{'formatter'}{ $format } = $code;
67             }
68             sub add_deformatter {
69 31     31 0 46 my ($self, $format, $code) = @_;
70 31 50 33     183 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
71 31 50       79 return if $self->has_deformatter( $format );
72 31         102 $self->{'deformatter'}{ lc $format } = $code;
73             }
74             sub set_value_numifier {
75 15     15 0 80 my ($self, $pre_code, $post_code) = @_;
76 15 50 33     159 return 0 if ref $pre_code ne 'CODE' or ref $post_code ne 'CODE';
77 15         87 $self->{'value_numifier'}{'into_numric'} = $pre_code;
78 15         39 $self->{'value_numifier'}{'from_numeric'} = $post_code;
79             }
80              
81             #### public API: formatting value tuples ###############################
82 7782     7782 0 17131 sub basis { $_[0]{'basis'}}
83 167 100 100 167 0 5329 sub has_formatter { (defined $_[1] and exists $_[0]{'formatter'}{ lc $_[1] }) ? 1 : 0 }
84 33 100 66 33 0 953 sub has_deformatter { (defined $_[1] and exists $_[0]{'deformatter'}{ lc $_[1] }) ? 1 : 0 }
85              
86             sub deformat {
87 2269     2269 0 49473 my ($self, $color, $suffix) = @_;
88 2269 50       4124 return undef unless defined $color;
89 2269         3962 $suffix = $self->get_suffix( $suffix );
90 2269 50       4144 return $suffix unless ref $suffix;
91 2269         2846 for my $format_name (sort keys %{$self->{'deformatter'}}){
  2269         8550  
92 9136         13455 my $deformatter = $self->{'deformatter'}{$format_name};
93 9136         16310 my $values = $deformatter->( $self, $color );
94 9136 100       17171 next unless ref $values;
95 220         646 $values = $self->check_raw_value_format( $values );
96 220 100       543 next unless ref $values;
97 122         433 $values = $self->remove_suffix($values, $suffix);
98 122 50       468 next unless ref $values;
99 122 100       771 return wantarray ? ($values, $format_name) : $values;
100             }
101 2147         6012 return undef;
102             }
103             sub format {
104 128     128 0 16558 my ($self, $values, $format, $suffix, $prefix) = @_;
105 128 50       437 return '' unless $self->basis->is_value_tuple( $values );
106 128 100       342 return '' unless $self->has_formatter( $format );
107 116         409 $suffix = $self->get_suffix( $suffix );
108 116 100       270 return $suffix unless ref $suffix;
109 113         321 $values = $self->add_suffix( $values, $suffix );
110 113         482 $self->{'formatter'}{ lc $format }->($self, $values);
111             }
112              
113             #### work methods ######################################################
114             sub remove_suffix { # and unnecessary white space
115 123     123 0 303 my ($self, $values, $suffix) = @_;
116 123 50       291 return unless $self->basis->is_value_tuple( $values );
117 123         300 $suffix = $self->get_suffix( $suffix );
118 123 50       310 return $suffix unless ref $suffix;
119 123         423 $values = [@$values]; # loose ref and side effects
120 123 100       388 if (ref $self->{'value_numifier'}{'into_numric'}){
121 9         40 $values = $self->{'value_numifier'}{'into_numric'}->($values);
122 9 50       24 return unless $self->basis->is_value_tuple( $values );
123             }
124 123         703 local $/ = ' ';
125 123         305 chomp $values->[$_] for $self->basis->axis_iterator;
126 123         347 for my $axis_index ($self->basis->axis_iterator){
127 375 100       843 next unless $suffix->[ $axis_index ];
128 76         111 my $val_length = length $values->[ $axis_index ];
129 76         115 my $suf_length = length $suffix->[ $axis_index ];
130 76 100 66     414 $values->[$axis_index] = substr($values->[$axis_index], 0, $val_length - $suf_length)
131             if substr( $values->[$axis_index], - $suf_length) eq $suffix->[ $axis_index ]
132             and substr( $values->[$axis_index], - ($suf_length+1),1) ne ' ';
133             }
134 123         465 return $values;
135             }
136             sub add_suffix {
137 113     113 0 256 my ($self, $values, $suffix) = @_;
138 113 50       191 return unless $self->basis->is_value_tuple( $values );
139 113         191 $suffix = $self->get_suffix( $suffix );
140 113 50       219 return $suffix unless ref $suffix; # has to be array or error message
141 113         251 $values = [@$values]; # loose ref and side effects
142 113 100       290 if (ref $self->{'value_numifier'}{'from_numeric'}){
143 4         10 $values = $self->{'value_numifier'}{'from_numeric'}->($values);
144 4 50       5 return unless $self->basis->is_value_tuple( $values );
145             }
146 113         580 local $/ = ' ';
147 113         229 chomp $values->[$_] for $self->basis->axis_iterator;
148 113         261 for my $axis_index ($self->basis->axis_iterator){
149 345 100       598 next unless $suffix->[ $axis_index ];
150 25         33 my $val_length = length $values->[ $axis_index ];
151 25         31 my $suf_length = length $suffix->[ $axis_index ];
152 25 50       81 $values->[$axis_index] .= $suffix->[ $axis_index ]
153             if substr( $values->[$axis_index], - $suf_length) ne $suffix->[ $axis_index ];
154             }
155 113         324 return $values;
156             }
157              
158             sub check_raw_value_format {
159 220     220 0 426 my ($self, $values) = @_;
160 220 100       660 return 0 if ref $values ne 'ARRAY';
161 188 100       447 return 0 if @$values != $self->basis->axis_count;
162 149         442 my @re = $self->get_value_regex();
163 149         429 for my $axis_index ($self->basis->axis_iterator){
164 415 100       7942 return 0 unless $values->[$axis_index] =~ /^$re[$axis_index]$/;
165             }
166 122         396 return $values;
167             }
168              
169             sub get_value_regex {
170 149     149 0 291 my ($self) = @_;
171 149         391 map {'\s*('.$self->{'value_form'}[$_].'(?:'.quotemeta($self->{'suffix'}[$_]).')?)\s*' } # quotemeta
  453         1576  
172             $self->basis->axis_iterator;
173             }
174              
175             #### converter: format --> values ######################################
176             sub tuple_from_named_string {
177 2166     2166 0 3153 my ($self, $string) = @_;
178 2166 100 66     7183 return 0 unless defined $string and not ref $string;
179 715         1059 my $name = $self->basis->space_name;
180 715         16360 $string =~ /^\s*$name:\s*(\s*[^:]+)\s*$/i;
181 715         1624 my $match = $1;
182 715 100       1215 unless ($match){
183 694         1035 my $name = $self->basis->alias_name;
184 694 100       1297 return 0 unless $name;
185 377         7266 $string =~ /^\s*$name:\s*(\s*[^:]+)\s*$/i;
186 377         802 $match = $1;
187             }
188 398 100       1831 return 0 unless $match;
189 22         96 local $/ = ' ';
190 22         45 chomp $match;
191 22 100       216 return [split(/\s*,\s*/, $match)] if index($match, ',') > -1;
192 1         6 return [split(/\s+/, $match)];
193             }
194             sub tuple_from_css_string {
195 2257     2257 0 3285 my ($self, $string) = @_;
196 2257 100 66     7565 return 0 unless defined $string and not ref $string;
197 741         1155 my $name = $self->basis->space_name;
198 741         17066 $string =~ /^\s*$name\(\s*([^)]+)\s*\)\s*$/i;
199 741         1645 my $match = $1;
200 741 100       1298 unless ($match){
201 726         1158 my $name = $self->basis->alias_name;
202 726 100       1368 return 0 unless $name;
203 399         7212 $string =~ /^\s*$name\(\s*([^)]+)\s*\)\s*$/i;
204 399         792 $match = $1;
205             }
206 414 100       935 return 0 unless $match;
207 17         77 local $/ = ' ';
208 17         30 chomp $match;
209 17 100       195 return [split(/\s*,\s*/, $match)] if index($match, ',') > -1;
210 1         5 return [split(/\s+/, $match)];
211             }
212             sub tuple_from_named_array {
213 2203     2203 0 3494 my ($self, $array) = @_;
214 2203 100       4810 return 0 unless ref $array eq 'ARRAY';
215 961 100       1873 return 0 unless @$array == $self->basis->axis_count+1;
216 279 100       561 return 0 unless $self->basis->is_name( $array->[0] );
217 37         153 return [@{$array}[1 .. $#$array]];
  37         174  
218             }
219             sub tuple_from_hash {
220 2243     2243 0 3290 my ($self, $hash) = @_;
221 2243 100       3934 return 0 unless $self->basis->is_hash($hash);
222 38         135 $self->basis->tuple_from_hash( $hash );
223             }
224              
225             #### converter: values --> format ######################################
226             sub named_array_from_tuple {
227 0     0 0 0 my ($self, $values, $name) = @_;
228 0   0     0 $name //= $self->basis->space_name;
229 0         0 return [$name, @$values];
230             }
231             sub named_string_from_tuple {
232 12     12 0 24 my ($self, $values, $name) = @_;
233 12   33     57 $name //= $self->basis->space_name;
234 12         116 return lc( $name).': '.join(', ', @$values);
235             }
236             sub css_string_from_tuple {
237 38     38 0 82 my ($self, $values, $name) = @_;
238 38   33     150 $name //= $self->basis->space_name;
239 38         360 return lc( $name).'('.join(', ', @$values).')';
240             }
241              
242             1;