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   350084 use v5.12;
  34         126  
7 34     34   216 use warnings;
  34         65  
  34         108971  
8              
9             my $number_form = '-?(?:\d+|\d+\.\d+|\.\d+)';
10              
11             #### constructor and name space API ####################################
12             sub new { # -, $:Basis -- ~|@~val_form, , ~|@~suffix --> :_
13 284     284 0 6615 my ($pkg, $basis, $value_form, $prefix, $suffix) = @_;
14 284 100       871 return 'First argument has to be an Color::Space::Basis reference !'
15             unless ref $basis eq 'Graphics::Toolkit::Color::Space::Basis';
16              
17 283         722 my $count = $basis->axis_count;
18 283 100       719 $value_form = $number_form unless defined $value_form;
19 283 100       1078 $value_form = [($value_form) x $count] unless ref $value_form;
20 283 50       744 return "Definition of the value format has to be as ARRAY reference" if ref $value_form ne 'ARRAY';
21 283 50 33     702 $value_form = [ map {(defined $_ and $_) ? $_ : $number_form } @$value_form]; # fill missing defs with default
  871         3583  
22 283 100       998 return 'Need a value form definition for every axis!' unless @$value_form == $count;
23              
24 282         3382 $suffix = create_suffix_list( $basis, $suffix ) ;
25 282 100       655 return $suffix unless ref $suffix;
26              
27             # format --> tuple
28 2092     2092   3811 my %deformats = ( hash => sub { tuple_from_hash(@_) },
29 2052     2052   3838 named_array => sub { tuple_from_named_array(@_) },
30 2015     2015   3711 named_string => sub { tuple_from_named_string(@_) },
31 2106     2106   4013 css_string => sub { tuple_from_css_string(@_) },
32 281         3696 );
33             # tuple --> format
34 34     34   39 my %formats = (list => sub { @{$_[1]} }, # 1, 2, 3
  34         255  
35 4     4   21 hash => sub { $basis->long_name_hash_from_tuple($_[1]) }, # { red => 1, green => 2, blue => 3 }
36 3     3   32 char_hash => sub { $basis->short_name_hash_from_tuple($_[1]) }, # { r =>1, g => 2, b => 3 }
37 5     5   24 named_array => sub { [$basis->space_name, @{$_[1]}] }, # ['rgb',1,2,3]
  5         42  
38 12     12   73 named_string => sub { $_[0]->named_string_from_tuple($_[1]) }, # 'rgb: 1, 2, 3'
39 38     38   248 css_string => sub { $_[0]->css_string_from_tuple($_[1]) }, # 'rgb(1,2,3)'
40 281         4592 );
41 281         6731 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 create_suffix_list {
48 520     520 0 990 my ($basis, $suffix) = @_;
49 520         5055 my $count = $basis->axis_count;
50 520 100       1937 $suffix = [('') x $count] unless defined $suffix;
51 520 100       1106 $suffix = [($suffix) x $count] unless ref $suffix;
52 520 100       1259 return 'need an ARRAY as definition of axis value suffix' unless ref $suffix eq 'ARRAY';
53 519 100       1111 return 'definition of axis value suffix has to have same lengths as basis' unless @$suffix == $count;
54 516         1098 return $suffix;
55             }
56              
57             sub add_formatter {
58 31     31 0 73 my ($self, $format, $code) = @_;
59 31 50 33     265 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
60 31 50       89 return if $self->has_formatter( $format );
61 31         115 $self->{'formatter'}{ $format } = $code;
62             }
63             sub add_deformatter {
64 31     31 0 69 my ($self, $format, $code) = @_;
65 31 50 33     202 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
66 31 50       76 return if $self->has_deformatter( $format );
67 31         156 $self->{'deformatter'}{ lc $format } = $code;
68             }
69             sub set_value_numifier {
70 15     15 0 48 my ($self, $pre_code, $post_code) = @_;
71 15 50 33     143 return 0 if ref $pre_code ne 'CODE' or ref $post_code ne 'CODE';
72 15         119 $self->{'value_numifier'}{'into_numric'} = $pre_code;
73 15         172 $self->{'value_numifier'}{'from_numeric'} = $post_code;
74             }
75              
76             #### public API: formatting value tuples ###############################
77 7251     7251 0 19244 sub basis { $_[0]{'basis'}}
78 163 100 100 163 0 5506 sub has_formatter { (defined $_[1] and exists $_[0]{'formatter'}{ lc $_[1] }) ? 1 : 0 }
79 33 100 66 33 0 1136 sub has_deformatter { (defined $_[1] and exists $_[0]{'deformatter'}{ lc $_[1] }) ? 1 : 0 }
80             sub get_suffix {
81 2454     2454 0 4032 my ($self, $suffix) = @_;
82 2454 100       9858 return $self->{'suffix'} unless defined $suffix;
83 238         788 create_suffix_list( $self->{'basis'}, $suffix );
84             }
85              
86             sub deformat {
87 2116     2116 0 86548 my ($self, $color, $suffix) = @_;
88 2116 50       3893 return undef unless defined $color;
89 2116         3949 $suffix = $self->get_suffix( $suffix );
90 2116 50       3946 return $suffix unless ref $suffix;
91 2116         2786 for my $format_name (sort keys %{$self->{'deformatter'}}){
  2116         8877  
92 8422         12576 my $deformatter = $self->{'deformatter'}{$format_name};
93 8422         15207 my $values = $deformatter->( $self, $color );
94 8422 100       17303 next unless ref $values;
95 176         655 $values = $self->check_raw_value_format( $values );
96 176 100       465 next unless ref $values;
97 116         517 $values = $self->remove_suffix($values, $suffix);
98 116 50       434 next unless ref $values;
99 116 100       760 return wantarray ? ($values, $format_name) : $values;
100             }
101 2000         5651 return undef;
102             }
103             sub format {
104 124     124 0 18824 my ($self, $values, $format, $suffix, $prefix) = @_;
105 124 50       350 return '' unless $self->basis->is_value_tuple( $values );
106 124 100       328 return '' unless $self->has_formatter( $format );
107 112         288 $suffix = $self->get_suffix( $suffix );
108 112 100       304 return $suffix unless ref $suffix;
109 109         395 $values = $self->add_suffix( $values, $suffix );
110 109         445 $self->{'formatter'}{ lc $format }->($self, $values);
111             }
112              
113             #### work methods ######################################################
114             sub remove_suffix { # and unnecessary white space
115 117     117 0 350 my ($self, $values, $suffix) = @_;
116 117 50       377 return unless $self->basis->is_value_tuple( $values );
117 117         298 $suffix = $self->get_suffix( $suffix );
118 117 50       334 return $suffix unless ref $suffix;
119 117         434 $values = [@$values]; # loose ref and side effects
120 117 100       773 if (ref $self->{'value_numifier'}{'into_numric'}){
121 9         40 $values = $self->{'value_numifier'}{'into_numric'}->($values);
122 9 50       27 return unless $self->basis->is_value_tuple( $values );
123             }
124 117         639 local $/ = ' ';
125 117         333 chomp $values->[$_] for $self->basis->axis_iterator;
126 117         450 for my $axis_index ($self->basis->axis_iterator){
127 357 100       919 next unless $suffix->[ $axis_index ];
128 76         137 my $val_length = length $values->[ $axis_index ];
129 76         115 my $suf_length = length $suffix->[ $axis_index ];
130 76 100 66     448 $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         523 return $values;
135             }
136             sub add_suffix {
137 109     109 0 220 my ($self, $values, $suffix) = @_;
138 109 50       199 return unless $self->basis->is_value_tuple( $values );
139 109         313 $suffix = $self->get_suffix( $suffix );
140 109 50       246 return $suffix unless ref $suffix; # has to be array or error message
141 109         264 $values = [@$values]; # loose ref and side effects
142 109 100       458 if (ref $self->{'value_numifier'}{'from_numeric'}){
143 4         16 $values = $self->{'value_numifier'}{'from_numeric'}->($values);
144 4 50       9 return unless $self->basis->is_value_tuple( $values );
145             }
146 109         671 local $/ = ' ';
147 109         286 chomp $values->[$_] for $self->basis->axis_iterator;
148 109         371 for my $axis_index ($self->basis->axis_iterator){
149 333 100       739 next unless $suffix->[ $axis_index ];
150 25         54 my $val_length = length $values->[ $axis_index ];
151 25         80 my $suf_length = length $suffix->[ $axis_index ];
152 25 50       108 $values->[$axis_index] .= $suffix->[ $axis_index ]
153             if substr( $values->[$axis_index], - $suf_length) ne $suffix->[ $axis_index ];
154             }
155 109         369 return $values;
156             }
157              
158             sub check_raw_value_format {
159 176     176 0 400 my ($self, $values) = @_;
160 176 100       679 return 0 if ref $values ne 'ARRAY';
161 158 100       387 return 0 if @$values != $self->basis->axis_count;
162 138         452 my @re = $self->get_value_regex();
163 138         419 for my $axis_index ($self->basis->axis_iterator){
164 389 100       8433 return 0 unless $values->[$axis_index] =~ /^$re[$axis_index]$/;
165             }
166 116         415 return $values;
167             }
168              
169             sub get_value_regex {
170 138     138 0 296 my ($self) = @_;
171 138         427 map {'\s*('.$self->{'value_form'}[$_].'(?:'.quotemeta($self->{'suffix'}[$_]).')?)\s*' } # quotemeta
  420         1604  
172             $self->basis->axis_iterator;
173             }
174              
175             #### converter: format --> values ######################################
176             sub tuple_from_named_string {
177 2015     2015 0 3177 my ($self, $string) = @_;
178 2015 100 66     7110 return 0 unless defined $string and not ref $string;
179 663         1237 my $name = $self->basis->space_name;
180 663         43625 $string =~ /^\s*$name:\s*(\s*[^:]+)\s*$/i;
181 663         1956 my $match = $1;
182 663 100       15774 unless ($match){
183 646         1417 my $name = $self->basis->alias_name;
184 646 100       1566 return 0 unless $name;
185 324         8629 $string =~ /^\s*$name:\s*(\s*[^:]+)\s*$/i;
186 324         891 $match = $1;
187             }
188 341 100       901 return 0 unless $match;
189 18         93 local $/ = ' ';
190 18         37 chomp $match;
191 18 100       253 return [split(/\s*,\s*/, $match)] if index($match, ',') > -1;
192 1         9 return [split(/\s+/, $match)];
193             }
194             sub tuple_from_css_string {
195 2106     2106 0 3280 my ($self, $string) = @_;
196 2106 100 66     8810 return 0 unless defined $string and not ref $string;
197 689         1302 my $name = $self->basis->space_name;
198 689         24905 $string =~ /^\s*$name\(\s*([^)]+)\s*\)\s*$/i;
199 689         2094 my $match = $1;
200 689 100       1414 unless ($match){
201 674         1324 my $name = $self->basis->alias_name;
202 674 100       1643 return 0 unless $name;
203 332         7684 $string =~ /^\s*$name\(\s*([^)]+)\s*\)\s*$/i;
204 332         839 $match = $1;
205             }
206 347 100       1043 return 0 unless $match;
207 17         85 local $/ = ' ';
208 17         34 chomp $match;
209 17 100       225 return [split(/\s*,\s*/, $match)] if index($match, ',') > -1;
210 1         6 return [split(/\s+/, $match)];
211             }
212             sub tuple_from_named_array {
213 2052     2052 0 3313 my ($self, $array) = @_;
214 2052 100       4381 return 0 unless ref $array eq 'ARRAY';
215 876 100       1605 return 0 unless @$array == $self->basis->axis_count+1;
216 274 100       564 return 0 unless $self->basis->is_name( $array->[0] );
217 37         172 return [@{$array}[1 .. $#$array]];
  37         161  
218             }
219             sub tuple_from_hash {
220 2092     2092 0 3512 my ($self, $hash) = @_;
221 2092 100       3926 return 0 unless $self->basis->is_hash($hash);
222 38         106 $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 34 my ($self, $values, $name) = @_;
233 12   33     85 $name //= $self->basis->space_name;
234 12         165 return lc( $name).': '.join(', ', @$values);
235             }
236             sub css_string_from_tuple {
237 38     38 0 98 my ($self, $values, $name) = @_;
238 38   33     175 $name //= $self->basis->space_name;
239 38         469 return lc( $name).'('.join(', ', @$values).')';
240             }
241              
242             1;