File Coverage

blib/lib/Graphics/Primitive/CSS.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Graphics::Primitive::CSS;
2 2     2   111745 use Moose;
  0            
  0            
3              
4             our $VERSION = '0.02';
5              
6             use Carp qw(carp);
7             use Check::ISA;
8             use CSS::DOM;
9             use Graphics::Color::RGB;
10              
11             has 'styles' => (
12             is => 'ro',
13             isa => 'Str',
14             required => 1,
15             );
16              
17             has 'css_dom' => (
18             is => 'ro',
19             isa => 'CSS::DOM',
20             lazy => 1,
21             default => sub { my $self = shift; CSS::DOM::parse($self->styles) }
22             );
23              
24             sub apply {
25             my ($self, $doc) = @_;
26              
27             my @rules = $self->css_dom->cssRules;
28              
29             foreach my $rule (@rules) {
30             my $selector = $rule->selectorText;
31              
32             for(0..($rule->style->length - 1)) {
33              
34             my $prop = $rule->style->item($_);
35             my $val = $rule->style->getPropertyValue($prop);
36              
37             my $comps;
38             if($selector =~ /^\.(.*)/) {
39             # Handle classes
40             my $class = $1;
41             $comps = $doc->find(sub {
42             my ($comp, $const) = @_;
43             return 0 unless defined($comp->class);
44             return $comp->class eq $class;
45             });
46             } elsif($selector =~ /#(.*)/) {
47             # Handle names
48             my $name = $1;
49             $comps = $doc->find(sub {
50             my ($comp, $const) = @_;
51             return 0 unless defined($comp->name);
52             return $comp->name eq $name;
53             });
54             } elsif($selector =~ 'textbox') {
55             # Handle "elements"
56             $comps = $doc->find(sub {
57             my ($comp, $const) = @_;
58              
59             return obj($comp, 'Graphics::Primitive::TextBox');
60             });
61             }
62              
63             return 1 unless defined($comps) && $comps->component_count;
64              
65             if($prop eq 'background-color') {
66              
67             my $color = $self->_process_color($val);
68             next unless defined($color);
69              
70             $comps->each(sub {
71             my ($comp, $const) = @_;
72             $comp->background_color($color);
73             });
74              
75             } elsif($prop eq 'border-bottom-color') {
76              
77             my $color = $self->_process_color($val);
78             next unless defined($color);
79              
80             $comps->each(sub {
81             my ($comp, $const) = @_;
82             $comp->border->bottom->color($color);
83             });
84              
85             } elsif($prop eq 'border-bottom-width') {
86              
87             if($val =~ /(\d+)px/) {
88             $comps->each(sub {
89             my ($comp, $const) = @_; $comp->border->bottom->width($1);
90             });
91             }
92             } elsif($prop eq 'border-color') {
93              
94             my $color = $self->_process_color($val);
95             next unless defined($color);
96              
97             $comps->each(sub {
98             my ($comp, $const) = @_;
99             $comp->border->color($color);
100             });
101              
102             } elsif($prop eq 'border-left-color') {
103              
104             my $color = $self->_process_color($val);
105             next unless defined($color);
106              
107             $comps->each(sub {
108             my ($comp, $const) = @_;
109             $comp->border->left->color($color);
110             });
111              
112             } elsif($prop eq 'border-left-width') {
113              
114             if($val =~ /(\d+)px/) {
115             $comps->each(sub {
116             my ($comp, $const) = @_; $comp->border->left->width($1);
117             });
118             }
119             } elsif($prop eq 'border-right-color') {
120              
121             my $color = $self->_process_color($val);
122             next unless defined($color);
123              
124             $comps->each(sub {
125             my ($comp, $const) = @_;
126             $comp->border->right->color($color);
127             });
128              
129             } elsif($prop eq 'border-right-width') {
130              
131             if($val =~ /(\d+)px/) {
132             $comps->each(sub {
133             my ($comp, $const) = @_; $comp->border->right->width($1);
134             });
135             }
136              
137             } elsif($prop eq 'border-top-color') {
138              
139             my $color = $self->_process_color($val);
140             next unless defined($color);
141              
142             $comps->each(sub {
143             my ($comp, $const) = @_;
144             $comp->border->top->color($color);
145             });
146              
147             } elsif($prop eq 'border-top-width') {
148              
149             if($val =~ /(\d+)px/) {
150             $comps->each(sub {
151             my ($comp, $const) = @_; $comp->border->top->width($1);
152             });
153             }
154              
155             } elsif($prop eq 'border-width') {
156             my ($top, $right, $bottom, $left);
157             if($val =~ /^(\d+)px$/) {
158             $top = $1; $bottom = $1;
159             $right = $1; $left = $1;
160              
161             } elsif($val =~ /^(\d+)px (\d+)px$/) {
162             $top = $1; $bottom = $1;
163             $right = $2; $left = $2;
164              
165             } elsif($val =~ /^(\d+)px (\d+)px (\d+)px (\d+)px$/) {
166             $top = $1; $right = $2;
167             $bottom = $3; $left = $4;
168             }
169              
170             $comps->each(sub {
171             my ($comp, $const) = @_;
172             $comp->border->top->width($top);
173             $comp->border->right->width($right);
174             $comp->border->bottom->width($bottom);
175             $comp->border->left->width($left);
176             });
177              
178             } elsif($prop eq 'color') {
179              
180             my $color = $self->_process_color($val);
181             next unless defined($color);
182              
183             $comps->each(sub {
184             my ($comp, $const) = @_;
185             $comp->color($color);
186             });
187              
188             } elsif($prop eq 'font-family') {
189             $comps->each(sub {
190             my ($comp, $const) = @_; $comp->font->family($val);
191             });
192              
193             } elsif($prop eq 'font-size') {
194             if($val =~ /(\d+)pt/) {
195             $comps->each(sub {
196             my ($comp, $const) = @_; $comp->font->size($1);
197             });
198             } else {
199             carp("Unknown font-size value: '$val'");
200             }
201              
202             } elsif($prop eq 'font-weight') {
203             $comps->each(sub {
204             my ($comp, $const) = @_; $comp->font->weight($val);
205             });
206              
207              
208             } elsif($prop eq 'margin') {
209             my ($top, $right, $bottom, $left);
210             if($val =~ /^(\d+)px (\d+)px$/) {
211             $top = $1; $bottom = $1;
212             $right = $2; $left = $2;
213             } elsif($val =~ /^(\d+)px (\d+)px (\d+)px (\d+)px$/) {
214             $top = $1; $right = $2;
215             $bottom = $3; $left = $4;
216             }
217              
218             $comps->each(sub {
219             my ($comp, $const) = @_;
220             $comp->margins->top($top);
221             $comp->margins->right($right);
222             $comp->margins->bottom($bottom);
223             $comp->margins->left($left);
224             });
225             } elsif($prop eq 'margin-bottom') {
226             if($val =~ /(\d+)px/) {
227             $comps->each(sub {
228             my ($comp, $const) = @_; $comp->margins->bottom($1);
229             });
230             }
231             } elsif($prop eq 'margin-left') {
232             if($val =~ /(\d+)px/) {
233             $comps->each(sub {
234             my ($comp, $const) = @_; $comp->margins->left($1);
235             });
236             }
237             } elsif($prop eq 'margin-right') {
238             if($val =~ /(\d+)px/) {
239             $comps->each(sub {
240             my ($comp, $const) = @_; $comp->margins->right($1);
241             });
242             }
243             } elsif($prop eq 'margin-top') {
244             if($val =~ /(\d+)px/) {
245             $comps->each(sub {
246             my ($comp, $const) = @_; $comp->margins->top($1);
247             });
248             }
249              
250             } elsif($prop eq 'padding') {
251             my ($top, $right, $bottom, $left);
252             if($val =~ /^(\d+)px (\d+)px$/) {
253             $top = $1; $bottom = $1;
254             $right = $2; $left = $2;
255             } elsif($val =~ /^(\d+)px (\d+)px (\d+)px (\d+)px$/) {
256             $top = $1; $right = $2;
257             $bottom = $3; $left = $4;
258             }
259              
260             $comps->each(sub {
261             my ($comp, $const) = @_;
262             $comp->padding->top($top);
263             $comp->padding->right($right);
264             $comp->padding->bottom($bottom);
265             $comp->padding->left($left);
266             });
267             } elsif($prop eq 'padding-bottom') {
268             if($val =~ /(\d+)px/) {
269             $comps->each(sub {
270             my ($comp, $const) = @_; $comp->padding->bottom($1);
271             });
272             }
273             } elsif($prop eq 'padding-left') {
274             if($val =~ /(\d+)px/) {
275             $comps->each(sub {
276             my ($comp, $const) = @_; $comp->padding->left($1);
277             });
278             }
279             } elsif($prop eq 'padding-right') {
280             if($val =~ /(\d+)px/) {
281             $comps->each(sub {
282             my ($comp, $const) = @_; $comp->padding->right($1);
283             });
284             }
285             } elsif($prop eq 'padding-top') {
286             if($val =~ /(\d+)px/) {
287             $comps->each(sub {
288             my ($comp, $const) = @_; $comp->padding->top($1);
289             });
290             }
291              
292             } elsif($prop eq 'text-align') {
293             $comps->each(sub {
294             my ($comp, $const) = @_; $comp->horizontal_alignment($val);
295             });
296             } elsif($prop eq 'vertical-align') {
297             $comps->each(sub {
298             my ($comp, $const) = @_; $comp->vertical_alignment($val);
299             });
300             }
301             }
302             }
303              
304             return 1;
305             }
306              
307             # Attempt to find a valid color
308             sub _process_color {
309             my ($self, $val) = @_;
310              
311             my $color;
312             if($val =~ /^#(.*)/) {
313             $color = Graphics::Color::RGB->from_hex_string($val);
314              
315             # TODO:
316             # rgb(255, 0, 0)
317             # rgb(100%, 0%, 0%)
318             # rgba(255, 0, 0)
319             # hsl(0, 100%, 50%)
320             # hsla(120, 100%, 50%, 1)
321             } else {
322             # Going to try and treat it like a color name...
323             $color = Graphics::Color::RGB->from_color_library(
324             "svg:$val"
325             );
326             }
327              
328             unless(defined($color)) {
329             carp("Unable to parse color: '$val'");
330             }
331              
332             return $color;
333             }
334              
335             1;
336              
337             =head1 NAME
338              
339             Graphics::Primitive::CSS - Style Graphics::Primitive documents with CSS
340              
341             =head1 SYNOPSIS
342              
343             use Graphics::Primitive::CSS;
344              
345             my $styler = Graphics::Primitive::CSS->new(
346             style => '
347             .foo {
348             font-size: 12pt;
349             vertical-align: center;
350             }
351             '
352             );
353              
354             my $doc = Graphics::Primitive::Container->new;
355             my $textbox = Graphics::Primitive::TextBox->new( class => 'foo' );
356             $doc->add_component($textbox);
357              
358             $styler->apply($doc);
359              
360             =head1 DESCRIPTION
361              
362             Graphics::Primitive::CSS allows you to change the various attributes of a
363             Graphics::Primitive document using CSS.
364              
365             =head1 SELECTORS
366              
367             Graphics::Primitive::CSS currently supports a class (.classname), element
368             (only textbox currently), and 'id' (#name) selector. It does not support
369             nested selectors (yet).
370              
371             =head1 COLORS
372              
373             Colors can be suppled as an RBG hex triplet (#f0f0f0 and #fff) and W3C
374             spec name (aliceblue). Support is intended for rgb, rgba, hsl and hsla.
375              
376             =head1 PROPERTIES
377              
378             Graphics::Primitive::CSS supports the following properties in the following
379             ways.
380              
381             =over 4
382              
383             =item background-color, color
384              
385             Background and foreground color
386              
387             =item border-color
388              
389             Color of all borders. B<Note: Only supports a single color value currently.>
390              
391             =item border-color-top, border-color-right, border-color-bottom, border-color-left
392              
393             Set the color for various borders
394              
395             =item border-width-top, border-width-right, border-width-bottom, border-width-left
396              
397             Set the width for a border (in pixels)
398              
399             =item font-size, font-family
400              
401             Size of font as points (e.g. 7pt). Family name (does not support lists!)
402              
403             =item margin
404              
405             2 value (top, left) and 4 value (top, right, bottom, left). Only pixels
406             are supported.
407              
408             =item margin-top, margin-left, margin-bottom, margin-right
409              
410             =item padding
411              
412             2 value (top, left) and 4 value (top, right, bottom, left). Only pixels
413             are supported.
414              
415             =item padding-top, padding-left, padding-bottom, padding-right
416              
417              
418             Only pixels are supported.
419              
420              
421             =back
422              
423             =head1 AUTHOR
424              
425             Cory G Watson, C<< <gphat at cpan.org> >>
426              
427             =back
428              
429             =head1 COPYRIGHT & LICENSE
430              
431             Copyright 2009 Cold Hard Code, LLC, all rights reserved.
432              
433             This program is free software; you can redistribute it and/or modify it
434             under the same terms as Perl itself.
435              
436             =cut