File Coverage

lib/Graphics/Toolkit/Color/Space/Format.pm
Criterion Covered Total %
statement 172 180 95.5
branch 97 118 82.2
condition 30 60 50.0
subroutine 41 43 95.3
pod 0 26 0.0
total 340 427 79.6


line stmt bran cond sub pod time code
1              
2             # bidirectional conversion of value tuples (ARRAY) into different string and other formats
3             # values can have color space dependant extra shape, suffixes, etc.
4              
5             package Graphics::Toolkit::Color::Space::Format;
6 50     50   188374 use v5.12;
  50         147  
7 50     50   205 use warnings;
  50         71  
  50         2670  
8 50     50   226 use Graphics::Toolkit::Color::Space::Util qw/number_re/;
  50         76  
  50         113281  
9              
10             #### constructor, building attr data ###################################
11             sub new { # -, $:Basis -- ~|@~val_form, ~|@~suffix --> :_
12 542     542 0 5005 my ($pkg, $basis, $value_form, $prefix, $suffix) = @_;
13 542 100       1044 return 'First argument has to be an GT::Color::Space::Basis reference !'
14             unless ref $basis eq 'Graphics::Toolkit::Color::Space::Basis';
15              
16 541         821 my $count = $basis->axis_count;
17 541 100       1319 $value_form = number_re() unless defined $value_form;
18 541 100       1557 $value_form = [($value_form) x $count] if ref $value_form ne 'ARRAY';
19 541 50       930 return "Definition of the value format has to be an ARRAY reference" if ref $value_form ne 'ARRAY';
20 541 50 33     775 $value_form = [ map {(defined $_ and $_) ? $_ : number_re() } @$value_form]; # fill missing defs with default
  1646         4641  
21 541 100       1184 return 'Need a value form definition for every axis!' unless @$value_form == $count;
22              
23 540         834 $suffix = expand_suffix_def( $basis, $suffix ) ;
24 540 100       743 return $suffix unless ref $suffix;
25              
26             # format --> tuple
27 2307     2307   2731 my %deformats = ( hash => sub { tuple_from_hash(@_) },
28 2360 100   2360   3030 array => sub { [@{$_[1]}] if $_[0]->basis->is_value_tuple( $_[1] ) },
  213         481  
29 2261     2261   2699 named_array => sub { tuple_from_named_array(@_) },
30 2187     2187   2526 nested_array => sub { tuple_from_nested_array(@_) },
31 2214     2214   2622 named_string => sub { tuple_from_named_string(@_) },
32 2323     2323   2900 css_string => sub { tuple_from_css_string(@_) },
33 539         6581 );
34             # tuple --> format
35 49     49   51 my %formats = (list => sub { (@{$_[1]}) }, # 1, 2, 3
  49         235  
36 6     6   8 array => sub { [@{$_[1]}] }, # [ 1, 2, 3 ]
  6         26  
37 4     4   18 hash => sub { $basis->long_name_hash_from_tuple($_[1]) }, # { red => 1, green => 2, blue => 3 }
38 3     3   14 char_hash => sub { $basis->short_name_hash_from_tuple($_[1]) }, # { r =>1, g => 2, b => 3 }
39 5     5   21 named_array => sub { [$basis->space_name, @{$_[1]}] }, # ['rgb',1,2,3]
  5         26  
40 0     0   0 nested_array => sub { [$basis->space_name, [@{$_[1]}]] }, # ['rgb' => [1,2,3]]
  0         0  
41 13     13   49 named_string => sub { $_[0]->named_string_from_tuple($_[1]) }, # 'rgb: 1, 2, 3'
42 42     42   148 css_string => sub { $_[0]->css_string_from_tuple($_[1]) }, # 'rgb(1,2,3)'
43 539         7005 );
44 539         4168 bless { basis => $basis, deformatter => \%deformats, formatter => \%formats,
45             value_form => $value_form, prefix => $prefix, suffix => $suffix,
46             value_numifier => { into_numeric => '', from_numeric => '' },
47             }
48             }
49              
50             sub expand_suffix_def {
51 1044     1044 0 1411 my ($basis, $suffix) = @_;
52 1044         1506 my $count = $basis->axis_count;
53 1044 100       1919 $suffix = [('') x $count] unless defined $suffix;
54 1044 100       1507 $suffix = [($suffix) x $count] unless ref $suffix;
55 1044 100       1592 return 'need an ARRAY as definition of axis value suffix' unless ref $suffix eq 'ARRAY';
56 1043 100       1502 return 'definition of axis value suffix has to have same lengths as basis' unless @$suffix == $count;
57 1040         1437 return $suffix;
58             }
59             sub get_suffix {
60 2988     2988 0 3324 my ($self, $suffix) = @_;
61 2988 100       5068 return $self->{'suffix'} unless defined $suffix;
62 504         953 expand_suffix_def( $self->{'basis'}, $suffix );
63             }
64              
65             sub add_formatter {
66 17     17 0 32 my ($self, $format, $code) = @_;
67 17 50 33     156 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
68 17 50       43 return if $self->has_formatter( $format );
69 17         42 $self->{'formatter'}{ lc $format } = $code;
70             }
71             sub add_deformatter {
72 17     17 0 24 my ($self, $format, $code) = @_;
73 17 50 33     121 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
74 17 50       41 return if $self->has_deformatter( $format );
75 17         55 $self->{'deformatter'}{ lc $format } = $code;
76             }
77             sub set_value_numifier {
78 16     16 0 43 my ($self, $pre_code, $post_code) = @_;
79 16 50 33     101 return 0 if ref $pre_code ne 'CODE' or ref $post_code ne 'CODE';
80 16         38 $self->{'value_numifier'}{'into_numeric'} = $pre_code;
81 16         39 $self->{'value_numifier'}{'from_numeric'} = $post_code;
82             }
83              
84             #### public API: formatting value tuples ###############################
85 9583     9583 0 14611 sub basis { $_[0]{'basis'}}
86 169 100 100 169 0 2828 sub has_formatter { (defined $_[1] and exists $_[0]{'formatter'}{ lc $_[1] }) ? 1 : 0 }
87 19 100 66 19 0 926 sub has_deformatter { (defined $_[1] and exists $_[0]{'deformatter'}{ lc $_[1] }) ? 1 : 0 }
88              
89             sub deformat { # check if color definition can be rad by any available formats of this space
90 2360     2360 0 22702 my ($self, $color_def, $suffix) = @_;
91 2360 50       2854 return undef unless defined $color_def;
92 2360         3203 $suffix = $self->get_suffix( $suffix );
93 2360 50       2912 return $suffix unless ref $suffix;
94 2360         2410 for my $format_name (sort keys %{$self->{'deformatter'}}){
  2360         7881  
95 13777         13514 my $deformatter = $self->{'deformatter'}{$format_name};
96 13777         15947 my $tuple = $deformatter->( $self, $color_def );
97 13777 100       17416 next unless ref $tuple;
98 361         630 $tuple = $self->trim_tuple( $tuple ); # remove space
99 361         719 $tuple = $self->remove_suffix( $tuple, $suffix );
100 361 100       739 next unless $self->are_tuple_numbers_well_formatted( $tuple );
101 174         373 $tuple = $self->numify_values( $tuple );
102 174 50       286 next unless $self->basis->is_number_tuple( $tuple );
103 174 100       833 return wantarray ? ($tuple, $format_name) : $tuple;
104             }
105 2186         4739 return undef;
106             }
107             sub format { # format tuple into color definition of this space
108 144     144 0 13809 my ($self, $tuple, $format, $suffix, $prefix) = @_;
109 144 50       315 return '' unless $self->basis->is_value_tuple( $tuple );
110 144 100       300 return '' unless $self->has_formatter( $format );
111 135         313 $suffix = $self->get_suffix( $suffix );
112 135 100       270 return $suffix unless ref $suffix;
113 132         286 $tuple = $self->denumify_values( $tuple );
114 132         333 $tuple = $self->add_suffix( $tuple, $suffix );
115 132         425 $self->{'formatter'}{ lc $format }->($self, $tuple);
116             }
117              
118             #### work methods ######################################################
119             sub trim_tuple {
120 361     361 0 449 my ($self, $dirty_tuple) = @_;
121 361 100       533 return unless $self->basis->is_value_tuple( $dirty_tuple );
122 360         572 my $tuple = [@$dirty_tuple];
123 360         862 $tuple->[$_] =~tr/ //d for $self->basis->axis_iterator;
124 360         590 return $tuple;
125             }
126              
127             sub remove_suffix { # and unnecessary white space and remove special number formats
128 362     362 0 528 my ($self, $tuple, $suffix) = @_;
129 362 100       485 return unless $self->basis->is_value_tuple( $tuple );
130 361         578 $suffix = $self->get_suffix( $suffix );
131 361 50       605 return $suffix unless ref $suffix;
132 361         707 $tuple = [@$tuple]; # loose ref and side effects
133 361         592 for my $axis_index ($self->basis->axis_iterator){
134 1109 100       1694 next unless $suffix->[ $axis_index ];
135 127         136 my $val_length = length $tuple->[ $axis_index ];
136 127         110 my $suf_length = length $suffix->[ $axis_index ];
137 127 100 66     373 $tuple->[$axis_index] = substr($tuple->[$axis_index], 0, $val_length - $suf_length)
138             if substr( $tuple->[$axis_index], - $suf_length) eq $suffix->[ $axis_index ]
139             and substr( $tuple->[$axis_index], - ($suf_length+1),1) ne ' ';
140             }
141 361         599 return $tuple;
142             }
143             sub add_suffix {
144 132     132 0 207 my ($self, $tuple, $suffix) = @_;
145 132 50       189 return unless $self->basis->is_value_tuple( $tuple );
146 132         225 $suffix = $self->get_suffix( $suffix );
147 132 50       207 return $suffix unless ref $suffix; # tuple or error message
148 132         234 $tuple = [@$tuple]; # loose ref and side effects
149 132         244 for my $axis_index ($self->basis->axis_iterator){
150 402 100       718 next unless $suffix->[ $axis_index ];
151 23         72 my $val_length = length $tuple->[ $axis_index ];
152 23         34 my $suf_length = length $suffix->[ $axis_index ];
153 23 50       75 $tuple->[$axis_index] .= $suffix->[ $axis_index ]
154             if substr( $tuple->[$axis_index], - $suf_length) ne $suffix->[ $axis_index ];
155             }
156 132         193 return $tuple;
157             }
158              
159             # works only on special value formats
160             sub numify_values {
161 174     174 0 258 my ($self, $tuple) = @_;
162 174 100       472 return $tuple unless ref $self->{'value_numifier'}{'into_numeric'};
163 9         31 $tuple = $self->{'value_numifier'}{'into_numeric'}->($tuple);
164 9 50       20 return $tuple if $self->basis->is_value_tuple( $tuple );
165             }
166             sub denumify_values {
167 132     132 0 214 my ($self, $tuple) = @_;
168 132 100       335 return $tuple unless ref $self->{'value_numifier'}{'from_numeric'};
169 4         16 $tuple = $self->{'value_numifier'}{'from_numeric'}->($tuple);
170 4 50       9 return $tuple if $self->basis->is_value_tuple( $tuple );
171             }
172              
173             sub are_tuple_numbers_well_formatted { # custom or normal
174 361     361 0 459 my ($self, $tuple) = @_;
175 361 100       666 return 0 if ref $tuple ne 'ARRAY';
176 360 50       541 return 0 if @$tuple != $self->basis->axis_count;
177 360         589 my @re = $self->get_value_regex();
178 360         514 for my $axis_index ($self->basis->axis_iterator){
179 817 100       10362 return 0 unless $tuple->[$axis_index] =~ /^$re[$axis_index]$/;
180             }
181 174         540 return 1;
182             }
183              
184             sub get_value_regex {
185 360     360 0 496 my ($self) = @_;
186 360         437 map {'\s*('.$self->{'value_form'}[$_].'(?:'.quotemeta($self->{'suffix'}[$_]).')?)\s*' } # quotemeta
  1106         2883  
187             $self->basis->axis_iterator;
188             }
189              
190             #### converter: format --> values ######################################
191             sub tuple_from_named_string {
192 2214     2214 0 2352 my ($self, $string) = @_;
193 2214 100 66     4980 return 0 unless defined $string and not ref $string;
194 612         933 $string =~ /^\s*([^ :]+):\s*(\s*[^:]+)\s*$/i;
195 612         774 my $space_name = $1;
196 612         634 my $tuple_string = $2;
197 612 100 66     890 return 0 unless $self->{'basis'}->is_name( $space_name ) and $tuple_string;
198 30         138 local $/ = ' ';
199 30         67 chomp $tuple_string;
200 30 100       329 return [split(/\s*,\s*/, $tuple_string)] if index($tuple_string, ',') > -1;
201 1         5 return [split(/\s+/, $tuple_string)];
202             }
203             sub tuple_from_css_string {
204 2323     2323 0 2552 my ($self, $string) = @_;
205 2323 100 66     5503 return 0 unless defined $string and not ref $string;
206 642         980 $string =~ /^\s*([^()]+)\(\s*([^()]+)\s*\)\s*$/i;
207 642         800 my $space_name = $1;
208 642         690 my $tuple_string = $2;
209 642 100 66     1001 return 0 unless $self->{'basis'}->is_name( $space_name ) and $tuple_string;
210 19         76 local $/ = ' ';
211 19         31 chomp $tuple_string;
212 19 100       162 return [split(/\s*,\s*/, $tuple_string)] if index($tuple_string, ',') > -1;
213 2         10 return [split(/\s+/, $tuple_string)];
214             }
215             sub tuple_from_named_array {
216 2261     2261 0 2480 my ($self, $array) = @_;
217 2261 100 66     4675 return 0 if ref $array ne 'ARRAY' or not @$array;
218 1184 100       1425 return 0 unless $self->basis->is_name( $array->[0] );
219 52         184 $array = [@$array[1 .. $#$array]];
220 52 100 66     180 $array = $array->[0] if @$array == 1 and ref $array->[0] eq 'ARRAY';
221 52 100       105 return 0 unless @$array == $self->basis->axis_count;
222 47         84 return $array;
223             }
224             sub tuple_from_nested_array {
225 2187     2187 0 2601 my ($self, $array) = @_;
226 2187 100 100     4341 return 0 if ref $array ne 'ARRAY' or @$array != 2;
227 469 50       540 return 0 unless $self->basis->is_name( $array->[0] );
228 0         0 $array = $array->[1];
229 0 0 0     0 return 0 if ref $array ne 'ARRAY' or @$array != $self->basis->axis_count;
230 0         0 return [@$array];
231             }
232             sub tuple_from_hash {
233 2307     2307 0 2501 my ($self, $hash) = @_;
234 2307 100       2880 return 0 unless $self->basis->is_hash($hash);
235 37         66 $self->basis->tuple_from_hash( $hash );
236             }
237              
238             #### converter: values --> format ######################################
239             sub named_array_from_tuple {
240 0     0 0 0 my ($self, $tuple, $name) = @_;
241 0   0     0 $name //= $self->basis->space_name(undef, 'given');
242 0         0 return [$name, @$tuple];
243             }
244             sub named_string_from_tuple {
245 13     13 0 30 my ($self, $tuple, $name) = @_;
246 13   33     77 $name //= $self->basis->space_name(undef, 'given');
247 13         146 return lc($name).': '.join(', ', @$tuple);
248             }
249             sub css_string_from_tuple {
250 42     42 0 98 my ($self, $tuple, $name) = @_;
251 42   33     164 $name //= $self->basis->space_name(undef, 'given');
252 42         485 return lc($name).'('.join(', ', @$tuple).')';
253             }
254              
255             1;