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   222474 use v5.12;
  50         140  
7 50     50   187 use warnings;
  50         64  
  50         2564  
8 50     50   220 use Graphics::Toolkit::Color::Space::Util qw/number_re/;
  50         72  
  50         119046  
9              
10             #### constructor, building attr data ###################################
11             sub new { # -, $:Basis -- ~|@~val_form, ~|@~suffix --> :_
12 542     542 0 4764 my ($pkg, $basis, $value_form, $prefix, $suffix) = @_;
13 542 100       991 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         868 my $count = $basis->axis_count;
17 541 100       1226 $value_form = number_re() unless defined $value_form;
18 541 100       1554 $value_form = [($value_form) x $count] if ref $value_form ne 'ARRAY';
19 541 50       903 return "Definition of the value format has to be an ARRAY reference" if ref $value_form ne 'ARRAY';
20 541 50 33     732 $value_form = [ map {(defined $_ and $_) ? $_ : number_re() } @$value_form]; # fill missing defs with default
  1646         4711  
21 541 100       1210 return 'Need a value form definition for every axis!' unless @$value_form == $count;
22              
23 540         879 $suffix = expand_suffix_def( $basis, $suffix ) ;
24 540 100       756 return $suffix unless ref $suffix;
25              
26             # format --> tuple
27 2307     2307   3143 my %deformats = ( hash => sub { tuple_from_hash(@_) },
28 2360 100   2360   3020 array => sub { [@{$_[1]}] if $_[0]->basis->is_value_tuple( $_[1] ) },
  213         549  
29 2261     2261   2736 named_array => sub { tuple_from_named_array(@_) },
30 2187     2187   2480 nested_array => sub { tuple_from_nested_array(@_) },
31 2214     2214   2672 named_string => sub { tuple_from_named_string(@_) },
32 2323     2323   2855 css_string => sub { tuple_from_css_string(@_) },
33 539         6507 );
34             # tuple --> format
35 49     49   55 my %formats = (list => sub { (@{$_[1]}) }, # 1, 2, 3
  49         201  
36 6     6   8 array => sub { [@{$_[1]}] }, # [ 1, 2, 3 ]
  6         29  
37 4     4   18 hash => sub { $basis->long_name_hash_from_tuple($_[1]) }, # { red => 1, green => 2, blue => 3 }
38 3     3   11 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         21  
40 0     0   0 nested_array => sub { [$basis->space_name, [@{$_[1]}]] }, # ['rgb' => [1,2,3]]
  0         0  
41 13     13   43 named_string => sub { $_[0]->named_string_from_tuple($_[1]) }, # 'rgb: 1, 2, 3'
42 42     42   131 css_string => sub { $_[0]->css_string_from_tuple($_[1]) }, # 'rgb(1,2,3)'
43 539         6999 );
44 539         4135 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 1341 my ($basis, $suffix) = @_;
52 1044         1492 my $count = $basis->axis_count;
53 1044 100       1903 $suffix = [('') x $count] unless defined $suffix;
54 1044 100       1489 $suffix = [($suffix) x $count] unless ref $suffix;
55 1044 100       1601 return 'need an ARRAY as definition of axis value suffix' unless ref $suffix eq 'ARRAY';
56 1043 100       1414 return 'definition of axis value suffix has to have same lengths as basis' unless @$suffix == $count;
57 1040         1478 return $suffix;
58             }
59             sub get_suffix {
60 2988     2988 0 3316 my ($self, $suffix) = @_;
61 2988 100       5320 return $self->{'suffix'} unless defined $suffix;
62 504         970 expand_suffix_def( $self->{'basis'}, $suffix );
63             }
64              
65             sub add_formatter {
66 17     17 0 33 my ($self, $format, $code) = @_;
67 17 50 33     154 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
68 17 50       56 return if $self->has_formatter( $format );
69 17         50 $self->{'formatter'}{ lc $format } = $code;
70             }
71             sub add_deformatter {
72 17     17 0 40 my ($self, $format, $code) = @_;
73 17 50 33     117 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
74 17 50       40 return if $self->has_deformatter( $format );
75 17         59 $self->{'deformatter'}{ lc $format } = $code;
76             }
77             sub set_value_numifier {
78 16     16 0 32 my ($self, $pre_code, $post_code) = @_;
79 16 50 33     94 return 0 if ref $pre_code ne 'CODE' or ref $post_code ne 'CODE';
80 16         42 $self->{'value_numifier'}{'into_numeric'} = $pre_code;
81 16         40 $self->{'value_numifier'}{'from_numeric'} = $post_code;
82             }
83              
84             #### public API: formatting value tuples ###############################
85 9583     9583 0 15150 sub basis { $_[0]{'basis'}}
86 169 100 100 169 0 2791 sub has_formatter { (defined $_[1] and exists $_[0]{'formatter'}{ lc $_[1] }) ? 1 : 0 }
87 19 100 66 19 0 954 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 21894 my ($self, $color_def, $suffix) = @_;
91 2360 50       3098 return undef unless defined $color_def;
92 2360         3213 $suffix = $self->get_suffix( $suffix );
93 2360 50       3145 return $suffix unless ref $suffix;
94 2360         2195 for my $format_name (sort keys %{$self->{'deformatter'}}){
  2360         7807  
95 13777         13573 my $deformatter = $self->{'deformatter'}{$format_name};
96 13777         16544 my $tuple = $deformatter->( $self, $color_def );
97 13777 100       17523 next unless ref $tuple;
98 361         672 $tuple = $self->trim_tuple( $tuple ); # remove space
99 361         746 $tuple = $self->remove_suffix( $tuple, $suffix );
100 361 100       769 next unless $self->are_tuple_numbers_well_formatted( $tuple );
101 174         397 $tuple = $self->numify_values( $tuple );
102 174 50       323 next unless $self->basis->is_number_tuple( $tuple );
103 174 100       832 return wantarray ? ($tuple, $format_name) : $tuple;
104             }
105 2186         3942 return undef;
106             }
107             sub format { # format tuple into color definition of this space
108 144     144 0 13410 my ($self, $tuple, $format, $suffix, $prefix) = @_;
109 144 50       340 return '' unless $self->basis->is_value_tuple( $tuple );
110 144 100       342 return '' unless $self->has_formatter( $format );
111 135         303 $suffix = $self->get_suffix( $suffix );
112 135 100       261 return $suffix unless ref $suffix;
113 132         286 $tuple = $self->denumify_values( $tuple );
114 132         309 $tuple = $self->add_suffix( $tuple, $suffix );
115 132         466 $self->{'formatter'}{ lc $format }->($self, $tuple);
116             }
117              
118             #### work methods ######################################################
119             sub trim_tuple {
120 361     361 0 543 my ($self, $dirty_tuple) = @_;
121 361 100       496 return unless $self->basis->is_value_tuple( $dirty_tuple );
122 360         665 my $tuple = [@$dirty_tuple];
123 360         594 $tuple->[$_] =~tr/ //d for $self->basis->axis_iterator;
124 360         621 return $tuple;
125             }
126              
127             sub remove_suffix { # and unnecessary white space and remove special number formats
128 362     362 0 542 my ($self, $tuple, $suffix) = @_;
129 362 100       478 return unless $self->basis->is_value_tuple( $tuple );
130 361         566 $suffix = $self->get_suffix( $suffix );
131 361 50       558 return $suffix unless ref $suffix;
132 361         789 $tuple = [@$tuple]; # loose ref and side effects
133 361         485 for my $axis_index ($self->basis->axis_iterator){
134 1109 100       1593 next unless $suffix->[ $axis_index ];
135 127         132 my $val_length = length $tuple->[ $axis_index ];
136 127         127 my $suf_length = length $suffix->[ $axis_index ];
137 127 100 66     378 $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         516 return $tuple;
142             }
143             sub add_suffix {
144 132     132 0 236 my ($self, $tuple, $suffix) = @_;
145 132 50       198 return unless $self->basis->is_value_tuple( $tuple );
146 132         241 $suffix = $self->get_suffix( $suffix );
147 132 50       210 return $suffix unless ref $suffix; # tuple or error message
148 132         213 $tuple = [@$tuple]; # loose ref and side effects
149 132         246 for my $axis_index ($self->basis->axis_iterator){
150 402 100       697 next unless $suffix->[ $axis_index ];
151 23         41 my $val_length = length $tuple->[ $axis_index ];
152 23         27 my $suf_length = length $suffix->[ $axis_index ];
153 23 50       65 $tuple->[$axis_index] .= $suffix->[ $axis_index ]
154             if substr( $tuple->[$axis_index], - $suf_length) ne $suffix->[ $axis_index ];
155             }
156 132         194 return $tuple;
157             }
158              
159             # works only on special value formats
160             sub numify_values {
161 174     174 0 294 my ($self, $tuple) = @_;
162 174 100       424 return $tuple unless ref $self->{'value_numifier'}{'into_numeric'};
163 9         31 $tuple = $self->{'value_numifier'}{'into_numeric'}->($tuple);
164 9 50       18 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       338 return $tuple unless ref $self->{'value_numifier'}{'from_numeric'};
169 4         10 $tuple = $self->{'value_numifier'}{'from_numeric'}->($tuple);
170 4 50       5 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 468 my ($self, $tuple) = @_;
175 361 100       615 return 0 if ref $tuple ne 'ARRAY';
176 360 50       537 return 0 if @$tuple != $self->basis->axis_count;
177 360         644 my @re = $self->get_value_regex();
178 360         526 for my $axis_index ($self->basis->axis_iterator){
179 817 100       10860 return 0 unless $tuple->[$axis_index] =~ /^$re[$axis_index]$/;
180             }
181 174         489 return 1;
182             }
183              
184             sub get_value_regex {
185 360     360 0 469 my ($self) = @_;
186 360         476 map {'\s*('.$self->{'value_form'}[$_].'(?:'.quotemeta($self->{'suffix'}[$_]).')?)\s*' } # quotemeta
  1106         2955  
187             $self->basis->axis_iterator;
188             }
189              
190             #### converter: format --> values ######################################
191             sub tuple_from_named_string {
192 2214     2214 0 2272 my ($self, $string) = @_;
193 2214 100 66     4840 return 0 unless defined $string and not ref $string;
194 612         881 $string =~ /^\s*([^ :]+):\s*(\s*[^:]+)\s*$/i;
195 612         650 my $space_name = $1;
196 612         585 my $tuple_string = $2;
197 612 100 66     806 return 0 unless $self->{'basis'}->is_name( $space_name ) and $tuple_string;
198 30         139 local $/ = ' ';
199 30         56 chomp $tuple_string;
200 30 100       300 return [split(/\s*,\s*/, $tuple_string)] if index($tuple_string, ',') > -1;
201 1         6 return [split(/\s+/, $tuple_string)];
202             }
203             sub tuple_from_css_string {
204 2323     2323 0 2595 my ($self, $string) = @_;
205 2323 100 66     5605 return 0 unless defined $string and not ref $string;
206 642         1017 $string =~ /^\s*([^()]+)\(\s*([^()]+)\s*\)\s*$/i;
207 642         734 my $space_name = $1;
208 642         626 my $tuple_string = $2;
209 642 100 66     918 return 0 unless $self->{'basis'}->is_name( $space_name ) and $tuple_string;
210 19         74 local $/ = ' ';
211 19         30 chomp $tuple_string;
212 19 100       159 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 2435 my ($self, $array) = @_;
217 2261 100 66     4735 return 0 if ref $array ne 'ARRAY' or not @$array;
218 1184 100       1486 return 0 unless $self->basis->is_name( $array->[0] );
219 52         191 $array = [@$array[1 .. $#$array]];
220 52 100 66     200 $array = $array->[0] if @$array == 1 and ref $array->[0] eq 'ARRAY';
221 52 100       110 return 0 unless @$array == $self->basis->axis_count;
222 47         94 return $array;
223             }
224             sub tuple_from_nested_array {
225 2187     2187 0 2372 my ($self, $array) = @_;
226 2187 100 100     4291 return 0 if ref $array ne 'ARRAY' or @$array != 2;
227 469 50       591 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 2404 my ($self, $hash) = @_;
234 2307 100       2841 return 0 unless $self->basis->is_hash($hash);
235 37         70 $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     45 $name //= $self->basis->space_name(undef, 'given');
247 13         127 return lc($name).': '.join(', ', @$tuple);
248             }
249             sub css_string_from_tuple {
250 42     42 0 98 my ($self, $tuple, $name) = @_;
251 42   33     186 $name //= $self->basis->space_name(undef, 'given');
252 42         470 return lc($name).'('.join(', ', @$tuple).')';
253             }
254              
255             1;