File Coverage

lib/Graphics/Toolkit/Color/Space/Format.pm
Criterion Covered Total %
statement 166 169 98.2
branch 93 110 84.5
condition 27 54 50.0
subroutine 38 39 97.4
pod 0 25 0.0
total 324 397 81.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 46     46   307693 use v5.12;
  46         174  
7 46     46   256 use warnings;
  46         79  
  46         2951  
8 46     46   291 use Graphics::Toolkit::Color::Space::Util qw/number_re/;
  46         104  
  46         155889  
9              
10             #### constructor, building attr data ###################################
11             sub new { # -, $:Basis -- ~|@~val_form, ~|@~suffix --> :_
12 464     464 0 7526 my ($pkg, $basis, $value_form, $prefix, $suffix) = @_;
13 464 100       1165 return 'First argument has to be an GT::Color::Space::Basis reference !'
14             unless ref $basis eq 'Graphics::Toolkit::Color::Space::Basis';
15              
16 463         1082 my $count = $basis->axis_count;
17 463 100       1586 $value_form = number_re() unless defined $value_form;
18 463 100       1976 $value_form = [($value_form) x $count] if ref $value_form ne 'ARRAY';
19 463 50       1196 return "Definition of the value format has to be an ARRAY reference" if ref $value_form ne 'ARRAY';
20 463 50 33     941 $value_form = [ map {(defined $_ and $_) ? $_ : number_re() } @$value_form]; # fill missing defs with default
  1411         5851  
21 463 100       1408 return 'Need a value form definition for every axis!' unless @$value_form == $count;
22              
23 462         1065 $suffix = expand_suffix_def( $basis, $suffix ) ;
24 462 100       960 return $suffix unless ref $suffix;
25              
26             # format --> tuple
27 1981     1981   3019 my %deformats = ( hash => sub { tuple_from_hash(@_) },
28 1938     1938   2918 named_array => sub { tuple_from_named_array(@_) },
29 1897     1897   2758 named_string => sub { tuple_from_named_string(@_) },
30 1997     1997   3074 css_string => sub { tuple_from_css_string(@_) },
31 461         5557 );
32             # tuple --> format
33 43     43   77 my %formats = (list => sub { @{$_[1]} }, # 1, 2, 3
  43         373  
34 6     6   12 array => sub { [@{$_[1]}] }, # [ 1, 2, 3 ]
  6         38  
35 4     4   22 hash => sub { $basis->long_name_hash_from_tuple($_[1]) }, # { red => 1, green => 2, blue => 3 }
36 3     3   45 char_hash => sub { $basis->short_name_hash_from_tuple($_[1]) }, # { r =>1, g => 2, b => 3 }
37 5     5   23 named_array => sub { [$basis->space_name, @{$_[1]}] }, # ['rgb',1,2,3]
  5         30  
38 13     13   56 named_string => sub { $_[0]->named_string_from_tuple($_[1]) }, # 'rgb: 1, 2, 3'
39 36     36   157 css_string => sub { $_[0]->css_string_from_tuple($_[1]) }, # 'rgb(1,2,3)'
40 461         7700 );
41 461         4969 bless { basis => $basis, deformatter => \%deformats, formatter => \%formats,
42             value_form => $value_form, prefix => $prefix, suffix => $suffix,
43             value_numifier => { into_numeric => '', from_numeric => '' },
44             }
45             }
46              
47             sub expand_suffix_def {
48 764     764 0 1464 my ($basis, $suffix) = @_;
49 764         1641 my $count = $basis->axis_count;
50 764 100       2110 $suffix = [('') x $count] unless defined $suffix;
51 764 100       1608 $suffix = [($suffix) x $count] unless ref $suffix;
52 764 100       1763 return 'need an ARRAY as definition of axis value suffix' unless ref $suffix eq 'ARRAY';
53 763 100       1553 return 'definition of axis value suffix has to have same lengths as basis' unless @$suffix == $count;
54 760         1600 return $suffix;
55             }
56             sub get_suffix {
57 2432     2432 0 3530 my ($self, $suffix) = @_;
58 2432 100       7035 return $self->{'suffix'} unless defined $suffix;
59 302         840 expand_suffix_def( $self->{'basis'}, $suffix );
60             }
61              
62             sub add_formatter {
63 31     31 0 74 my ($self, $format, $code) = @_;
64 31 50 33     221 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
65 31 100       81 return if $self->has_formatter( $format );
66 16         62 $self->{'formatter'}{ lc $format } = $code;
67             }
68             sub add_deformatter {
69 31     31 0 72 my ($self, $format, $code) = @_;
70 31 50 33     186 return if not defined $format or ref $format or ref $code ne 'CODE';
      33        
71 31 50       74 return if $self->has_deformatter( $format );
72 31         126 $self->{'deformatter'}{ lc $format } = $code;
73             }
74             sub set_value_numifier {
75 15     15 0 49 my ($self, $pre_code, $post_code) = @_;
76 15 50 33     111 return 0 if ref $pre_code ne 'CODE' or ref $post_code ne 'CODE';
77 15         44 $self->{'value_numifier'}{'into_numeric'} = $pre_code;
78 15         66 $self->{'value_numifier'}{'from_numeric'} = $post_code;
79             }
80              
81             #### public API: formatting value tuples ###############################
82 4839     4839 0 11068 sub basis { $_[0]{'basis'}}
83 171 100 100 171 0 4574 sub has_formatter { (defined $_[1] and exists $_[0]{'formatter'}{ lc $_[1] }) ? 1 : 0 }
84 33 100 66 33 0 1932 sub has_deformatter { (defined $_[1] and exists $_[0]{'deformatter'}{ lc $_[1] }) ? 1 : 0 }
85              
86             sub deformat { # check if color definition can be rad by any available formats of this space
87 2018     2018 0 44183 my ($self, $color_def, $suffix) = @_;
88 2018 50       3289 return undef unless defined $color_def;
89 2018         3182 $suffix = $self->get_suffix( $suffix );
90 2018 50       3400 return $suffix unless ref $suffix;
91 2018         2356 for my $format_name (sort keys %{$self->{'deformatter'}}){
  2018         7353  
92 8075         10320 my $deformatter = $self->{'deformatter'}{$format_name};
93 8075         12299 my $tuple = $deformatter->( $self, $color_def );
94 8075 100       13577 next unless ref $tuple;
95 240         681 $tuple = $self->trim_tuple( $tuple ); # remove space
96 240         767 $tuple = $self->remove_suffix( $tuple, $suffix );
97 240 100       782 next unless $self->are_tuple_numbers_well_formatted( $tuple );
98 155         474 $tuple = $self->numify_values( $tuple );
99 155 50       330 next unless $self->basis->is_number_tuple( $tuple );
100 155 100       1004 return wantarray ? ($tuple, $format_name) : $tuple;
101             }
102 1863         4313 return undef;
103             }
104             sub format { # format tuple into color definition of this space
105 132     132 0 25585 my ($self, $tuple, $format, $suffix, $prefix) = @_;
106 132 50       373 return '' unless $self->basis->is_value_tuple( $tuple );
107 132 100       324 return '' unless $self->has_formatter( $format );
108 123         311 $suffix = $self->get_suffix( $suffix );
109 123 100       288 return $suffix unless ref $suffix;
110 120         400 $tuple = $self->denumify_values( $tuple );
111 120         314 $tuple = $self->add_suffix( $tuple, $suffix );
112 120         500 $self->{'formatter'}{ lc $format }->($self, $tuple);
113             }
114              
115             #### work methods ######################################################
116             sub trim_tuple {
117 240     240 0 511 my ($self, $dirty_tuple) = @_;
118 240 100       565 return unless $self->basis->is_value_tuple( $dirty_tuple );
119 170         535 my $tuple = [@$dirty_tuple];
120 170         439 $tuple->[$_] =~tr/ //d for $self->basis->axis_iterator;
121             #~ for my $axis_index ($self->basis->axis_iterator){
122             #~ chomp $tuple->[$axis_index];
123             #~ $tuple->[$axis_index] = substr($tuple->[$axis_index], 1) while $tuple->[$axis_index]
124             #~ and substr($tuple->[$axis_index],0,1) eq ' ';
125             #~ }
126 170         445 return $tuple;
127             }
128              
129             sub remove_suffix { # and unnecessary white space and remove special number formats
130 241     241 0 497 my ($self, $tuple, $suffix) = @_;
131 241 100       516 return unless $self->basis->is_value_tuple( $tuple );
132 171         433 $suffix = $self->get_suffix( $suffix );
133 171 50       428 return $suffix unless ref $suffix;
134 171         555 $tuple = [@$tuple]; # loose ref and side effects
135 171         381 for my $axis_index ($self->basis->axis_iterator){
136 519 100       1152 next unless $suffix->[ $axis_index ];
137 97         160 my $val_length = length $tuple->[ $axis_index ];
138 97         135 my $suf_length = length $suffix->[ $axis_index ];
139 97 100 66     520 $tuple->[$axis_index] = substr($tuple->[$axis_index], 0, $val_length - $suf_length)
140             if substr( $tuple->[$axis_index], - $suf_length) eq $suffix->[ $axis_index ]
141             and substr( $tuple->[$axis_index], - ($suf_length+1),1) ne ' ';
142             }
143 171         393 return $tuple;
144             }
145             sub add_suffix {
146 120     120 0 236 my ($self, $tuple, $suffix) = @_;
147 120 50       307 return unless $self->basis->is_value_tuple( $tuple );
148 120         302 $suffix = $self->get_suffix( $suffix );
149 120 50       245 return $suffix unless ref $suffix; # tuple or error message
150 120         262 $tuple = [@$tuple]; # loose ref and side effects
151 120         315 for my $axis_index ($self->basis->axis_iterator){
152 366 100       785 next unless $suffix->[ $axis_index ];
153 23         54 my $val_length = length $tuple->[ $axis_index ];
154 23         51 my $suf_length = length $suffix->[ $axis_index ];
155 23 50       85 $tuple->[$axis_index] .= $suffix->[ $axis_index ]
156             if substr( $tuple->[$axis_index], - $suf_length) ne $suffix->[ $axis_index ];
157             }
158 120         276 return $tuple;
159             }
160              
161             # works only on special value formats
162             sub numify_values {
163 155     155 0 324 my ($self, $tuple) = @_;
164 155 100       527 return $tuple unless ref $self->{'value_numifier'}{'into_numeric'};
165 9         62 $tuple = $self->{'value_numifier'}{'into_numeric'}->($tuple);
166 9 50       19 return $tuple if $self->basis->is_value_tuple( $tuple );
167             }
168             sub denumify_values {
169 120     120 0 266 my ($self, $tuple) = @_;
170 120 100       540 return $tuple unless ref $self->{'value_numifier'}{'from_numeric'};
171 4         18 $tuple = $self->{'value_numifier'}{'from_numeric'}->($tuple);
172 4 50       9 return $tuple if $self->basis->is_value_tuple( $tuple );
173             }
174              
175             sub are_tuple_numbers_well_formatted { # custom or normal
176 240     240 0 426 my ($self, $tuple) = @_;
177 240 100       762 return 0 if ref $tuple ne 'ARRAY';
178 170 50       415 return 0 if @$tuple != $self->basis->axis_count;
179 170         552 my @re = $self->get_value_regex();
180 170         383 for my $axis_index ($self->basis->axis_iterator){
181 495 100       10273 return 0 unless $tuple->[$axis_index] =~ /^$re[$axis_index]$/;
182             }
183 155         715 return 1;
184             }
185              
186             sub get_value_regex {
187 170     170 0 323 my ($self) = @_;
188 170         392 map {'\s*('.$self->{'value_form'}[$_].'(?:'.quotemeta($self->{'suffix'}[$_]).')?)\s*' } # quotemeta
  516         2220  
189             $self->basis->axis_iterator;
190             }
191              
192             #### converter: format --> values ######################################
193             sub tuple_from_named_string {
194 1897     1897 0 2335 my ($self, $string) = @_;
195 1897 100 66     5534 return 0 unless defined $string and not ref $string;
196 618         1313 $string =~ /^\s*([^ :]+):\s*(\s*[^:]+)\s*$/i;
197 618         1002 my $space_name = $1;
198 618         778 my $tuple_string = $2;
199 618 100 66     1232 return 0 unless $self->{'basis'}->is_name( $space_name ) and $tuple_string;
200 36         243 local $/ = ' ';
201 36         75 chomp $tuple_string;
202 36 100       504 return [split(/\s*,\s*/, $tuple_string)] if index($tuple_string, ',') > -1;
203 1         8 return [split(/\s+/, $tuple_string)];
204             }
205             sub tuple_from_css_string {
206 1997     1997 0 2809 my ($self, $string) = @_;
207 1997 100 66     6334 return 0 unless defined $string and not ref $string;
208 646         1225 $string =~ /^\s*([^()]+)\(\s*([^()]+)\s*\)\s*$/i;
209 646         977 my $space_name = $1;
210 646         852 my $tuple_string = $2;
211 646 100 66     1482 return 0 unless $self->{'basis'}->is_name( $space_name ) and $tuple_string;
212 19         103 local $/ = ' ';
213 19         35 chomp $tuple_string;
214 19 100       233 return [split(/\s*,\s*/, $tuple_string)] if index($tuple_string, ',') > -1;
215 2         15 return [split(/\s+/, $tuple_string)];
216             }
217             sub tuple_from_named_array {
218 1938     1938 0 2479 my ($self, $array) = @_;
219 1938 100 66     5018 return 0 if ref $array ne 'ARRAY' or not @$array;
220 855 100       1437 return 0 unless $self->basis->is_name( $array->[0] );
221 46         204 $array = [@$array[1 .. $#$array]];
222 46 100 66     199 $array = $array->[0] if @$array == 1 and ref $array->[0] eq 'ARRAY';
223 46 100       123 return 0 unless @$array == $self->basis->axis_count;
224 41         143 return $array;
225             }
226             sub tuple_from_hash {
227 1981     1981 0 4466 my ($self, $hash) = @_;
228 1981 100       3199 return 0 unless $self->basis->is_hash($hash);
229 36         86 $self->basis->tuple_from_hash( $hash );
230             }
231              
232             #### converter: values --> format ######################################
233             sub named_array_from_tuple {
234 0     0 0 0 my ($self, $tuple, $name) = @_;
235 0   0     0 $name //= $self->basis->space_name(undef, 'given');
236 0         0 return [$name, @$tuple];
237             }
238             sub named_string_from_tuple {
239 13     13 0 245 my ($self, $tuple, $name) = @_;
240 13   33     68 $name //= $self->basis->space_name(undef, 'given');
241 13         180 return lc($name).': '.join(', ', @$tuple);
242             }
243             sub css_string_from_tuple {
244 36     36 0 89 my ($self, $tuple, $name) = @_;
245 36   33     189 $name //= $self->basis->space_name(undef, 'given');
246 36         507 return lc($name).'('.join(', ', @$tuple).')';
247             }
248              
249             1;