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   177789 use v5.12;
  50         139  
7 50     50   196 use warnings;
  50         69  
  50         2705  
8 50     50   217 use Graphics::Toolkit::Color::Space::Util qw/number_re/;
  50         77  
  50         117970  
9              
10             #### constructor, building attr data ###################################
11             sub new { # -, $:Basis -- ~|@~val_form, ~|@~suffix --> :_
12 542     542 0 4730 my ($pkg, $basis, $value_form, $prefix, $suffix) = @_;
13 542 100       1029 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         823 my $count = $basis->axis_count;
17 541 100       1242 $value_form = number_re() unless defined $value_form;
18 541 100       1546 $value_form = [($value_form) x $count] if ref $value_form ne 'ARRAY';
19 541 50       934 return "Definition of the value format has to be an ARRAY reference" if ref $value_form ne 'ARRAY';
20 541 50 33     793 $value_form = [ map {(defined $_ and $_) ? $_ : number_re() } @$value_form]; # fill missing defs with default
  1646         4352  
21 541 100       1170 return 'Need a value form definition for every axis!' unless @$value_form == $count;
22              
23 540         827 $suffix = expand_suffix_def( $basis, $suffix ) ;
24 540 100       755 return $suffix unless ref $suffix;
25              
26             # format --> tuple
27 2307     2307   3013 my %deformats = ( hash => sub { tuple_from_hash(@_) },
28 2360 100   2360   3168 array => sub { [@{$_[1]}] if $_[0]->basis->is_value_tuple( $_[1] ) },
  213         472  
29 2261     2261   2954 named_array => sub { tuple_from_named_array(@_) },
30 2187     2187   2871 nested_array => sub { tuple_from_nested_array(@_) },
31 2214     2214   2782 named_string => sub { tuple_from_named_string(@_) },
32 2323     2323   3146 css_string => sub { tuple_from_css_string(@_) },
33 539         6306 );
34             # tuple --> format
35 49     49   49 my %formats = (list => sub { (@{$_[1]}) }, # 1, 2, 3
  49         211  
36 6     6   12 array => sub { [@{$_[1]}] }, # [ 1, 2, 3 ]
  6         35  
37 4     4   17 hash => sub { $basis->long_name_hash_from_tuple($_[1]) }, # { red => 1, green => 2, blue => 3 }
38 3     3   13 char_hash => sub { $basis->short_name_hash_from_tuple($_[1]) }, # { r =>1, g => 2, b => 3 }
39 5     5   16 named_array => sub { [$basis->space_name, @{$_[1]}] }, # ['rgb',1,2,3]
  5         23  
40 0     0   0 nested_array => sub { [$basis->space_name, [@{$_[1]}]] }, # ['rgb' => [1,2,3]]
  0         0  
41 13     13   44 named_string => sub { $_[0]->named_string_from_tuple($_[1]) }, # 'rgb: 1, 2, 3'
42 42     42   113 css_string => sub { $_[0]->css_string_from_tuple($_[1]) }, # 'rgb(1,2,3)'
43 539         6881 );
44 539         4008 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 1402 my ($basis, $suffix) = @_;
52 1044         1403 my $count = $basis->axis_count;
53 1044 100       1901 $suffix = [('') x $count] unless defined $suffix;
54 1044 100       1477 $suffix = [($suffix) x $count] unless ref $suffix;
55 1044 100       1588 return 'need an ARRAY as definition of axis value suffix' unless ref $suffix eq 'ARRAY';
56 1043 100       1481 return 'definition of axis value suffix has to have same lengths as basis' unless @$suffix == $count;
57 1040         1479 return $suffix;
58             }
59             sub get_suffix {
60 2988     2988 0 3435 my ($self, $suffix) = @_;
61 2988 100       5280 return $self->{'suffix'} unless defined $suffix;
62 504         872 expand_suffix_def( $self->{'basis'}, $suffix );
63             }
64              
65             sub add_formatter {
66 17     17 0 49 my ($self, $format, $code) = @_;
67 17 50 33     163 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
68 17 50       47 return if $self->has_formatter( $format );
69 17         46 $self->{'formatter'}{ lc $format } = $code;
70             }
71             sub add_deformatter {
72 17     17 0 29 my ($self, $format, $code) = @_;
73 17 50 33     140 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
74 17 50       49 return if $self->has_deformatter( $format );
75 17         53 $self->{'deformatter'}{ lc $format } = $code;
76             }
77             sub set_value_numifier {
78 16     16 0 35 my ($self, $pre_code, $post_code) = @_;
79 16 50 33     100 return 0 if ref $pre_code ne 'CODE' or ref $post_code ne 'CODE';
80 16         36 $self->{'value_numifier'}{'into_numeric'} = $pre_code;
81 16         53 $self->{'value_numifier'}{'from_numeric'} = $post_code;
82             }
83              
84             #### public API: formatting value tuples ###############################
85 9583     9583 0 15527 sub basis { $_[0]{'basis'}}
86 169 100 100 169 0 2887 sub has_formatter { (defined $_[1] and exists $_[0]{'formatter'}{ lc $_[1] }) ? 1 : 0 }
87 19 100 66 19 0 919 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 21638 my ($self, $color_def, $suffix) = @_;
91 2360 50       3250 return undef unless defined $color_def;
92 2360         3289 $suffix = $self->get_suffix( $suffix );
93 2360 50       3176 return $suffix unless ref $suffix;
94 2360         2185 for my $format_name (sort keys %{$self->{'deformatter'}}){
  2360         8135  
95 13777         14346 my $deformatter = $self->{'deformatter'}{$format_name};
96 13777         17415 my $tuple = $deformatter->( $self, $color_def );
97 13777 100       18775 next unless ref $tuple;
98 361         657 $tuple = $self->trim_tuple( $tuple ); # remove space
99 361         747 $tuple = $self->remove_suffix( $tuple, $suffix );
100 361 100       773 next unless $self->are_tuple_numbers_well_formatted( $tuple );
101 174         426 $tuple = $self->numify_values( $tuple );
102 174 50       279 next unless $self->basis->is_number_tuple( $tuple );
103 174 100       846 return wantarray ? ($tuple, $format_name) : $tuple;
104             }
105 2186         4121 return undef;
106             }
107             sub format { # format tuple into color definition of this space
108 144     144 0 13053 my ($self, $tuple, $format, $suffix, $prefix) = @_;
109 144 50       373 return '' unless $self->basis->is_value_tuple( $tuple );
110 144 100       262 return '' unless $self->has_formatter( $format );
111 135         295 $suffix = $self->get_suffix( $suffix );
112 135 100       251 return $suffix unless ref $suffix;
113 132         352 $tuple = $self->denumify_values( $tuple );
114 132         268 $tuple = $self->add_suffix( $tuple, $suffix );
115 132         431 $self->{'formatter'}{ lc $format }->($self, $tuple);
116             }
117              
118             #### work methods ######################################################
119             sub trim_tuple {
120 361     361 0 472 my ($self, $dirty_tuple) = @_;
121 361 100       631 return unless $self->basis->is_value_tuple( $dirty_tuple );
122 360         575 my $tuple = [@$dirty_tuple];
123 360         519 $tuple->[$_] =~tr/ //d for $self->basis->axis_iterator;
124 360         617 return $tuple;
125             }
126              
127             sub remove_suffix { # and unnecessary white space and remove special number formats
128 362     362 0 539 my ($self, $tuple, $suffix) = @_;
129 362 100       513 return unless $self->basis->is_value_tuple( $tuple );
130 361         559 $suffix = $self->get_suffix( $suffix );
131 361 50       570 return $suffix unless ref $suffix;
132 361         710 $tuple = [@$tuple]; # loose ref and side effects
133 361         542 for my $axis_index ($self->basis->axis_iterator){
134 1109 100       1765 next unless $suffix->[ $axis_index ];
135 127         155 my $val_length = length $tuple->[ $axis_index ];
136 127         131 my $suf_length = length $suffix->[ $axis_index ];
137 127 100 66     392 $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         539 return $tuple;
142             }
143             sub add_suffix {
144 132     132 0 192 my ($self, $tuple, $suffix) = @_;
145 132 50       203 return unless $self->basis->is_value_tuple( $tuple );
146 132         244 $suffix = $self->get_suffix( $suffix );
147 132 50       243 return $suffix unless ref $suffix; # tuple or error message
148 132         208 $tuple = [@$tuple]; # loose ref and side effects
149 132         219 for my $axis_index ($self->basis->axis_iterator){
150 402 100       655 next unless $suffix->[ $axis_index ];
151 23         37 my $val_length = length $tuple->[ $axis_index ];
152 23         21 my $suf_length = length $suffix->[ $axis_index ];
153 23 50       85 $tuple->[$axis_index] .= $suffix->[ $axis_index ]
154             if substr( $tuple->[$axis_index], - $suf_length) ne $suffix->[ $axis_index ];
155             }
156 132         186 return $tuple;
157             }
158              
159             # works only on special value formats
160             sub numify_values {
161 174     174 0 273 my ($self, $tuple) = @_;
162 174 100       475 return $tuple unless ref $self->{'value_numifier'}{'into_numeric'};
163 9         31 $tuple = $self->{'value_numifier'}{'into_numeric'}->($tuple);
164 9 50       16 return $tuple if $self->basis->is_value_tuple( $tuple );
165             }
166             sub denumify_values {
167 132     132 0 194 my ($self, $tuple) = @_;
168 132 100       344 return $tuple unless ref $self->{'value_numifier'}{'from_numeric'};
169 4         12 $tuple = $self->{'value_numifier'}{'from_numeric'}->($tuple);
170 4 50       6 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 475 my ($self, $tuple) = @_;
175 361 100       588 return 0 if ref $tuple ne 'ARRAY';
176 360 50       539 return 0 if @$tuple != $self->basis->axis_count;
177 360         621 my @re = $self->get_value_regex();
178 360         622 for my $axis_index ($self->basis->axis_iterator){
179 817 100       11106 return 0 unless $tuple->[$axis_index] =~ /^$re[$axis_index]$/;
180             }
181 174         491 return 1;
182             }
183              
184             sub get_value_regex {
185 360     360 0 539 my ($self) = @_;
186 360         500 map {'\s*('.$self->{'value_form'}[$_].'(?:'.quotemeta($self->{'suffix'}[$_]).')?)\s*' } # quotemeta
  1106         2996  
187             $self->basis->axis_iterator;
188             }
189              
190             #### converter: format --> values ######################################
191             sub tuple_from_named_string {
192 2214     2214 0 2389 my ($self, $string) = @_;
193 2214 100 66     4957 return 0 unless defined $string and not ref $string;
194 612         993 $string =~ /^\s*([^ :]+):\s*(\s*[^:]+)\s*$/i;
195 612         693 my $space_name = $1;
196 612         622 my $tuple_string = $2;
197 612 100 66     845 return 0 unless $self->{'basis'}->is_name( $space_name ) and $tuple_string;
198 30         114 local $/ = ' ';
199 30         51 chomp $tuple_string;
200 30 100       286 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 2690 my ($self, $string) = @_;
205 2323 100 66     5875 return 0 unless defined $string and not ref $string;
206 642         1039 $string =~ /^\s*([^()]+)\(\s*([^()]+)\s*\)\s*$/i;
207 642         829 my $space_name = $1;
208 642         658 my $tuple_string = $2;
209 642 100 66     1030 return 0 unless $self->{'basis'}->is_name( $space_name ) and $tuple_string;
210 19         76 local $/ = ' ';
211 19         33 chomp $tuple_string;
212 19 100       172 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 2490 my ($self, $array) = @_;
217 2261 100 66     4784 return 0 if ref $array ne 'ARRAY' or not @$array;
218 1184 100       1475 return 0 unless $self->basis->is_name( $array->[0] );
219 52         187 $array = [@$array[1 .. $#$array]];
220 52 100 66     180 $array = $array->[0] if @$array == 1 and ref $array->[0] eq 'ARRAY';
221 52 100       98 return 0 unless @$array == $self->basis->axis_count;
222 47         80 return $array;
223             }
224             sub tuple_from_nested_array {
225 2187     2187 0 2292 my ($self, $array) = @_;
226 2187 100 100     4402 return 0 if ref $array ne 'ARRAY' or @$array != 2;
227 469 50       559 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 2650 my ($self, $hash) = @_;
234 2307 100       2903 return 0 unless $self->basis->is_hash($hash);
235 37         71 $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 43 my ($self, $tuple, $name) = @_;
246 13   33     66 $name //= $self->basis->space_name(undef, 'given');
247 13         124 return lc($name).': '.join(', ', @$tuple);
248             }
249             sub css_string_from_tuple {
250 42     42 0 78 my ($self, $tuple, $name) = @_;
251 42   33     170 $name //= $self->basis->space_name(undef, 'given');
252 42         373 return lc($name).'('.join(', ', @$tuple).')';
253             }
254              
255             1;