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   298022 use v5.12;
  34         132  
7 34     34   256 use warnings;
  34         72  
  34         103505  
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 9666 my ($pkg, $basis, $value_form, $prefix, $suffix) = @_;
14 284 100       782 return 'First argument has to be an Color::Space::Basis reference !'
15             unless ref $basis eq 'Graphics::Toolkit::Color::Space::Basis';
16              
17 283         708 my $count = $basis->axis_count;
18 283 100       705 $value_form = $number_form unless defined $value_form;
19 283 100       1234 $value_form = [($value_form) x $count] unless ref $value_form;
20 283 50       1052 return "Definition of the value format has to be as ARRAY reference" if ref $value_form ne 'ARRAY';
21 283 50 33     607 $value_form = [ map {(defined $_ and $_) ? $_ : $number_form } @$value_form]; # fill missing defs with default
  871         3579  
22 283 100       897 return 'Need a value form definition for every axis!' unless @$value_form == $count;
23              
24 282         662 $suffix = expand_suffix_def( $basis, $suffix ) ;
25 282 100       668 return $suffix unless ref $suffix;
26              
27             # format --> tuple
28 2116     2116   4402 my %deformats = ( hash => sub { tuple_from_hash(@_) },
29 2076     2076   4547 named_array => sub { tuple_from_named_array(@_) },
30 2039     2039   4235 named_string => sub { tuple_from_named_string(@_) },
31 2130     2130   4581 css_string => sub { tuple_from_css_string(@_) },
32 281         7180 );
33             # tuple --> format
34 35     35   71 my %formats = (list => sub { @{$_[1]} }, # 1, 2, 3
  35         464  
35 4     4   28 hash => sub { $basis->long_name_hash_from_tuple($_[1]) }, # { red => 1, green => 2, blue => 3 }
36 3     3   19 char_hash => sub { $basis->short_name_hash_from_tuple($_[1]) }, # { r =>1, g => 2, b => 3 }
37 5     5   26 named_array => sub { [$basis->space_name, @{$_[1]}] }, # ['rgb',1,2,3]
  5         47  
38 12     12   52 named_string => sub { $_[0]->named_string_from_tuple($_[1]) }, # 'rgb: 1, 2, 3'
39 38     38   197 css_string => sub { $_[0]->css_string_from_tuple($_[1]) }, # 'rgb(1,2,3)'
40 281         4073 );
41 281         2808 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 521     521 0 1078 my ($basis, $suffix) = @_;
49 521         1329 my $count = $basis->axis_count;
50 521 100       1410 $suffix = [('') x $count] unless defined $suffix;
51 521 100       1291 $suffix = [($suffix) x $count] unless ref $suffix;
52 521 100       1272 return 'need an ARRAY as definition of axis value suffix' unless ref $suffix eq 'ARRAY';
53 520 100       1213 return 'definition of axis value suffix has to have same lengths as basis' unless @$suffix == $count;
54 517         1269 return $suffix;
55             }
56             sub get_suffix {
57 2480     2480 0 4708 my ($self, $suffix) = @_;
58 2480 100       8821 return $self->{'suffix'} unless defined $suffix;
59 239         868 expand_suffix_def( $self->{'basis'}, $suffix );
60             }
61              
62             sub add_formatter {
63 31     31 0 87 my ($self, $format, $code) = @_;
64 31 50 33     243 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
65 31 50       95 return if $self->has_formatter( $format );
66 31         120 $self->{'formatter'}{ $format } = $code;
67             }
68             sub add_deformatter {
69 31     31 0 64 my ($self, $format, $code) = @_;
70 31 50 33     242 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
71 31 50       113 return if $self->has_deformatter( $format );
72 31         141 $self->{'deformatter'}{ lc $format } = $code;
73             }
74             sub set_value_numifier {
75 15     15 0 123 my ($self, $pre_code, $post_code) = @_;
76 15 50 33     227 return 0 if ref $pre_code ne 'CODE' or ref $post_code ne 'CODE';
77 15         53 $self->{'value_numifier'}{'into_numric'} = $pre_code;
78 15         82 $self->{'value_numifier'}{'from_numeric'} = $post_code;
79             }
80              
81             #### public API: formatting value tuples ###############################
82 7304     7304 0 21533 sub basis { $_[0]{'basis'}}
83 164 100 100 164 0 6639 sub has_formatter { (defined $_[1] and exists $_[0]{'formatter'}{ lc $_[1] }) ? 1 : 0 }
84 33 100 66 33 0 1147 sub has_deformatter { (defined $_[1] and exists $_[0]{'deformatter'}{ lc $_[1] }) ? 1 : 0 }
85              
86             sub deformat {
87 2140     2140 0 115654 my ($self, $color, $suffix) = @_;
88 2140 50       4462 return undef unless defined $color;
89 2140         4770 $suffix = $self->get_suffix( $suffix );
90 2140 50       4756 return $suffix unless ref $suffix;
91 2140         3041 for my $format_name (sort keys %{$self->{'deformatter'}}){
  2140         10130  
92 8520         14674 my $deformatter = $self->{'deformatter'}{$format_name};
93 8520         17884 my $values = $deformatter->( $self, $color );
94 8520 100       20058 next unless ref $values;
95 177         582 $values = $self->check_raw_value_format( $values );
96 177 100       556 next unless ref $values;
97 116         452 $values = $self->remove_suffix($values, $suffix);
98 116 50       383 next unless ref $values;
99 116 100       867 return wantarray ? ($values, $format_name) : $values;
100             }
101 2024         7054 return undef;
102             }
103             sub format {
104 125     125 0 25579 my ($self, $values, $format, $suffix, $prefix) = @_;
105 125 50       556 return '' unless $self->basis->is_value_tuple( $values );
106 125 100       412 return '' unless $self->has_formatter( $format );
107 113         394 $suffix = $self->get_suffix( $suffix );
108 113 100       367 return $suffix unless ref $suffix;
109 110         407 $values = $self->add_suffix( $values, $suffix );
110 110         517 $self->{'formatter'}{ lc $format }->($self, $values);
111             }
112              
113             #### work methods ######################################################
114             sub remove_suffix { # and unnecessary white space
115 117     117 0 312 my ($self, $values, $suffix) = @_;
116 117 50       322 return unless $self->basis->is_value_tuple( $values );
117 117         395 $suffix = $self->get_suffix( $suffix );
118 117 50       344 return $suffix unless ref $suffix;
119 117         447 $values = [@$values]; # loose ref and side effects
120 117 100       517 if (ref $self->{'value_numifier'}{'into_numric'}){
121 9         41 $values = $self->{'value_numifier'}{'into_numric'}->($values);
122 9 50       31 return unless $self->basis->is_value_tuple( $values );
123             }
124 117         868 local $/ = ' ';
125 117         456 chomp $values->[$_] for $self->basis->axis_iterator;
126 117         398 for my $axis_index ($self->basis->axis_iterator){
127 357 100       863 next unless $suffix->[ $axis_index ];
128 76         210 my $val_length = length $values->[ $axis_index ];
129 76         123 my $suf_length = length $suffix->[ $axis_index ];
130 76 100 66     442 $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 117         526 return $values;
135             }
136             sub add_suffix {
137 110     110 0 271 my ($self, $values, $suffix) = @_;
138 110 50       327 return unless $self->basis->is_value_tuple( $values );
139 110         262 $suffix = $self->get_suffix( $suffix );
140 110 50       317 return $suffix unless ref $suffix; # has to be array or error message
141 110         370 $values = [@$values]; # loose ref and side effects
142 110 100       398 if (ref $self->{'value_numifier'}{'from_numeric'}){
143 4         16 $values = $self->{'value_numifier'}{'from_numeric'}->($values);
144 4 50       12 return unless $self->basis->is_value_tuple( $values );
145             }
146 110         792 local $/ = ' ';
147 110         315 chomp $values->[$_] for $self->basis->axis_iterator;
148 110         325 for my $axis_index ($self->basis->axis_iterator){
149 336 100       821 next unless $suffix->[ $axis_index ];
150 25         43 my $val_length = length $values->[ $axis_index ];
151 25         42 my $suf_length = length $suffix->[ $axis_index ];
152 25 50       97 $values->[$axis_index] .= $suffix->[ $axis_index ]
153             if substr( $values->[$axis_index], - $suf_length) ne $suffix->[ $axis_index ];
154             }
155 110         534 return $values;
156             }
157              
158             sub check_raw_value_format {
159 177     177 0 435 my ($self, $values) = @_;
160 177 100       641 return 0 if ref $values ne 'ARRAY';
161 159 100       476 return 0 if @$values != $self->basis->axis_count;
162 138         499 my @re = $self->get_value_regex();
163 138         467 for my $axis_index ($self->basis->axis_iterator){
164 389 100       8966 return 0 unless $values->[$axis_index] =~ /^$re[$axis_index]$/;
165             }
166 116         553 return $values;
167             }
168              
169             sub get_value_regex {
170 138     138 0 287 my ($self) = @_;
171 138         402 map {'\s*('.$self->{'value_form'}[$_].'(?:'.quotemeta($self->{'suffix'}[$_]).')?)\s*' } # quotemeta
  420         2913  
172             $self->basis->axis_iterator;
173             }
174              
175             #### converter: format --> values ######################################
176             sub tuple_from_named_string {
177 2039     2039 0 3614 my ($self, $string) = @_;
178 2039 100 66     8462 return 0 unless defined $string and not ref $string;
179 663         1270 my $name = $self->basis->space_name;
180 663         23925 $string =~ /^\s*$name:\s*(\s*[^:]+)\s*$/i;
181 663         2572 my $match = $1;
182 663 100       1575 unless ($match){
183 646         1497 my $name = $self->basis->alias_name;
184 646 100       1738 return 0 unless $name;
185 324         9540 $string =~ /^\s*$name:\s*(\s*[^:]+)\s*$/i;
186 324         1101 $match = $1;
187             }
188 341 100       1170 return 0 unless $match;
189 18         92 local $/ = ' ';
190 18         41 chomp $match;
191 18 100       255 return [split(/\s*,\s*/, $match)] if index($match, ',') > -1;
192 1         10 return [split(/\s+/, $match)];
193             }
194             sub tuple_from_css_string {
195 2130     2130 0 3912 my ($self, $string) = @_;
196 2130 100 66     8872 return 0 unless defined $string and not ref $string;
197 689         1493 my $name = $self->basis->space_name;
198 689         25457 $string =~ /^\s*$name\(\s*([^)]+)\s*\)\s*$/i;
199 689         2465 my $match = $1;
200 689 100       1715 unless ($match){
201 674         1461 my $name = $self->basis->alias_name;
202 674 100       1941 return 0 unless $name;
203 332         9722 $string =~ /^\s*$name\(\s*([^)]+)\s*\)\s*$/i;
204 332         1148 $match = $1;
205             }
206 347 100       1129 return 0 unless $match;
207 17         91 local $/ = ' ';
208 17         40 chomp $match;
209 17 100       222 return [split(/\s*,\s*/, $match)] if index($match, ',') > -1;
210 1         9 return [split(/\s+/, $match)];
211             }
212             sub tuple_from_named_array {
213 2076     2076 0 3614 my ($self, $array) = @_;
214 2076 100       5318 return 0 unless ref $array eq 'ARRAY';
215 900 100       1999 return 0 unless @$array == $self->basis->axis_count+1;
216 274 100       552 return 0 unless $self->basis->is_name( $array->[0] );
217 37         137 return [@{$array}[1 .. $#$array]];
  37         186  
218             }
219             sub tuple_from_hash {
220 2116     2116 0 3870 my ($self, $hash) = @_;
221 2116 100       4727 return 0 unless $self->basis->is_hash($hash);
222 38         119 $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 35 my ($self, $values, $name) = @_;
233 12   33     81 $name //= $self->basis->space_name;
234 12         247 return lc( $name).': '.join(', ', @$values);
235             }
236             sub css_string_from_tuple {
237 38     38 0 161 my ($self, $values, $name) = @_;
238 38   33     246 $name //= $self->basis->space_name;
239 38         478 return lc( $name).'('.join(', ', @$values).')';
240             }
241              
242             1;