File Coverage

blib/lib/Image/Base/SVG.pm
Criterion Covered Total %
statement 106 118 89.8
branch 36 46 78.2
condition 18 23 78.2
subroutine 21 21 100.0
pod 9 10 90.0
total 190 218 87.1


line stmt bran cond sub pod time code
1             # Copyright 2011, 2012, 2019 Kevin Ryde
2              
3             # This file is part of Image-Base-SVG.
4             #
5             # Image-Base-SVG is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Image-Base-SVG is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Image-Base-SVG. If not, see .
17              
18              
19             package Image::Base::SVG;
20 3     3   68012 use 5.006; # SVG is 5.6 for weakening
  3         20  
21 3     3   17 use strict;
  3         6  
  3         80  
22 3     3   15 use Carp;
  3         5  
  3         211  
23 3     3   1212 use SVG; # version 2.50 needs an import() to create methods
  3         32212  
  3         25  
24              
25 3     3   3024 use vars '$VERSION', '@ISA';
  3         7  
  3         209  
26             $VERSION = 5;
27              
28 3     3   1628 use Image::Base;
  3         5704  
  3         4064  
29             @ISA = ('Image::Base');
30              
31             # uncomment this to run the ### lines
32             # use Smart::Comments '###';
33              
34              
35             sub new {
36 25     25 1 38952 my ($class, %params) = @_;
37             ### Image-Base-SVG new(): %params
38              
39             # $obj->new(...) means make a copy, with some extra settings
40 25 50       75 if (ref $class) {
41 0         0 my $self = $class;
42 0         0 $class = ref $self;
43 0         0 croak "Cannot clone $class yet ..."
44              
45             # if (! defined $params{'-svg_object'}) {
46             # $params{'-svg_object'} = $self->{'-svg_object'}->cloneNode;
47             # }
48             # # inherit everything else
49             # %params = (%$self, %params);
50             # ### copy params: \%params
51             }
52              
53 25         41 my $svg = delete $params{'-svg_object'};
54 25 100       59 if (! $svg) {
55             $svg = SVG->new ((exists $params{'-width'}
56             ? (width => delete $params{'-width'})
57             : ()),
58             (exists $params{'-height'} ?
59 24 100       129 (height => delete $params{'-height'})
    100          
60             : ()));
61             }
62              
63 25         6462 my $self = bless { -svg_object => $svg }, $class;
64             ### %params
65 25         87 $self->set (%params);
66 25         84 return $self;
67             }
68              
69             # these two not documented yet
70             my %key_to_cdata = ('-title' => 'title',
71             '-description' => 'desc');
72              
73             my %key_to_attribute = ('-width' => 'width',
74             '-height' => 'height');
75             sub _get {
76 31     31   7995 my ($self, $key) = @_;
77             ### _get(): $key
78              
79 31 100       123 if (my $tagname = $key_to_cdata{$key}) {
    100          
80 8         15 my $elem;
81 8   33     17 return (($elem = _get_tag($self,$tagname))
82             && $elem->getCDATA);
83              
84             } elsif (my $aname = $key_to_attribute{$key}) {
85 4         24 return _svg_element($self)->getAttribute ($aname);
86              
87             } else {
88 19         70 return $self->{$key};
89             }
90             }
91              
92             sub set {
93 29     29 1 48 my $self = shift;
94 29         110 while (@_) {
95 7         15 my $key = shift;
96 7 50       14 @_ or croak "Odd number of arguments to set()";
97 7         11 my $value = shift;
98              
99 7 100       22 if (my $tagname = $key_to_cdata{$key}) {
    50          
100 4         10 my $elem = _get_or_create_tag($self,$tagname);
101 4         179 $elem->cdata ($value);
102              
103             } elsif (my $aname = $key_to_attribute{$key}) {
104             ### $aname
105             ### $value
106 0         0 _svg_element($self)->setAttribute ($aname, $value);
107              
108             } else {
109 3         11 $self->{$key} = $value;
110             }
111             }
112             }
113             sub _get_tag {
114 8     8   14 my ($self,$tagname) = @_;
115 8         14 my $svg = $self->{'-svg_object'};
116 8         20 return ($svg->getElements($tagname))[0];
117             }
118             sub _get_or_create_tag {
119 4     4   10 my ($self,$tagname) = @_;
120 4         6 my $svg = $self->{'-svg_object'};
121 4         12 my @elems = $svg->getElements($tagname);
122 4 50       50 if (@elems) {
123 0         0 return $elems[0];
124             } else {
125 4         11 return $svg->tag($tagname);
126             }
127             }
128             sub _svg_element {
129 4     4   13 my ($self) = @_;
130 4         6 my $svg = $self->{'-svg_object'};
131             ### docroot: $svg->{'-docroot'}
132             ### elems: join(',',$svg->getElements())
133              
134 4   50     23 return ($svg->getElements($svg->{'-docroot'}))[0]
135             || die "Oops, -docroot element not found";
136             }
137              
138             sub xy {
139 4     4 1 88 my ($self, $x, $y, $colour) = @_;
140             ### Image-Base-SVG xy(): @_[1 .. $#_]
141              
142 4         8 my $svg = $self->{'-svg_object'};
143 4 100       11 if (@_ == 3) {
144 2         12 return undef; # no pixel fetching available
145             } else {
146 2         8 $svg->rectangle (x => $x, y => $y,
147             width => 1, height => 1,
148             fill => $colour);
149             }
150             }
151              
152             sub rectangle {
153 4     4 1 399 my ($self, $x1,$y1, $x2,$y2, $colour, $fill) = @_;
154             ### Image-Base-SVG rectangle(): @_[1 .. $#_]
155              
156 4   66     33 $fill ||= ($x1 == $x2 || $y1 == $y2); # 1xN or Nx1 done filled
      100        
157 4 100       11 if (! $fill) {
158 2         7 $x1 += .5; # for stroke width 1
159 2         4 $y1 += .5;
160 2         5 $x2 -= .5;
161 2         5 $y2 -= .5;
162             }
163 4 100       32 $self->{'-svg_object'}->rectangle (x => $x1,
164             y => $y1,
165             width => $x2-$x1+1,
166             height => $y2-$y1+1,
167             ($fill?'fill':'stroke') => $colour);
168             }
169              
170             sub ellipse {
171 4     4 1 28 my ($self, $x1,$y1, $x2,$y2, $colour, $fill) = @_;
172             ### Image-Base-SVG ellipse(): @_[1 .. $#_]
173              
174 4   100     26 $fill ||= ($x1 == $x2 || $y1 == $y2);
      100        
175 4         13 my $rx = ($x2-$x1+1) / 2;
176 4         8 my $ry = ($y2-$y1+1) / 2;
177 4 100       42 if (! $fill) {
178 1         3 $rx -= .5; # for stroke width 1
179 1         3 $ry -= .5;
180             }
181 4 100       33 $self->{'-svg_object'}->ellipse (cx => ($x1+$x2+1) / 2,
182             cy => ($y1+$y2+1) / 2,
183             rx => $rx,
184             ry => $ry,
185             ($fill?'fill':'stroke') => $colour);
186             }
187              
188             sub line {
189 1     1 1 9 my ($self, $x1,$y1, $x2,$y2, $colour, $fill) = @_;
190             ### Image-Base-SVG rectangle(): @_[1 .. $#_]
191              
192 1         15 $self->{'-svg_object'}->line (x1 => $x1+.5,
193             y1 => $y1+.5,
194             x2 => $x2+.5,
195             y2 => $y2+.5,
196             stroke => $colour,
197             'stroke-linecap' => "square");
198             }
199              
200             sub diamond {
201 3     3 1 22 my ($self, $x1,$y1, $x2,$y2, $colour, $fill) = @_;
202             ### Image-Base-SVG diamond(): @_[1 .. $#_]
203              
204 3   66     21 $fill ||= ($x1 == $x2 || $y1 == $y2); # 1xN or Nx1 done filled
      100        
205 3 100       8 if ($fill) {
206 2         4 $x2++;
207 2         4 $y2++;
208             } else {
209 1         4 $x1 += .5; # for stroke width 1
210 1         3 $y1 += .5;
211 1         3 $x2 += .5;
212 1         3 $y2 += .5;
213             }
214 3         9 my $xm = ($x1+$x2)/2;
215 3         5 my $ym = ($y1+$y2)/2;
216 3 100       46 $self->{'-svg_object'}->polygon (points => "$xm,$y1 $x1,$ym $xm,$y2 $x2,$ym",
217             ($fill?'fill':'stroke') => $colour);
218             }
219              
220             sub load {
221 1     1 1 8 my ($self, $filename) = @_;
222 1 50       4 if (@_ > 1) {
223 1         5 $self->set('-file', $filename);
224             } else {
225 0         0 $filename = $self->get('-file');
226             }
227             ### $filename
228              
229             # stringize any oopery to stop SVG::Parser being clever ... maybe
230 1         4 $filename = "$filename";
231              
232             # use SVG::Parser qw(SVG::Parser::SAX=XML::LibXML::SAX::Parser);
233             # use SVG::Parser qw(SVG::Parser::SAX=XML::LibXML::SAX::Parser);
234              
235 1 50   1   85 eval 'use SVG::Parser; 1' or die;
  1         12  
  1         2  
  1         9  
236 1         13 my $parser = SVG::Parser->new (
237             # -debug => 1,
238             );
239 1         787 my $svg = $parser->parse_file ($filename);
240 1         9058 $self->{'-svg_object'} = $svg;
241             }
242              
243             sub save {
244 1     1 1 84 my ($self, $filename) = @_;
245             ### Image-Base-SVG save(): @_
246 1 50       5 if (@_ > 1) {
247 1         4 $self->set('-file', $filename);
248             } else {
249 0         0 $filename = $self->get('-file');
250             }
251             ### $filename
252              
253 1 50       180 open my $fh, '>', $filename,
254             or croak "Cannot create $filename: $!";
255              
256 1 50       8 if (! $self->save_fh ($fh)) {
257 0         0 my $err = "Error writing $filename: $!";
258 0         0 { local $!; close $fh; }
  0         0  
  0         0  
259 0         0 croak $err;
260             }
261 1 50       886 close $fh
262             or croak "Error closing $filename: $!";
263             }
264              
265             # not yet documented ...
266             sub save_fh {
267 1     1 0 4 my ($self, $fh) = @_;
268             ### save_fh() ...
269             ### elements: $self->{'-elements'}
270             ### height: $self->{'-height'}
271              
272 1         2 my $svg = $self->{'-svg_object'};
273             # $svg->comment ("\n\tGenerated using ".ref($self)." version ".$self->VERSION."\n");
274 1         7 return print $fh $svg->xmlify;
275             }
276              
277             # sub _add_comment {
278             # my ($self) = @_;
279             # my $svg_element = _svg_element($self);
280             # my $generated
281             # = "\n\tGenerated using ".ref($self)." version ".$self->VERSION."\n";
282             # foreach my $comment ($svg_element->getElements('comment')) {
283             # if ($comment->cdata eq $generated) {
284             # return;
285             # }
286             # }
287             # $self->{'-svg_object'}->comment ($generated);
288             # }
289              
290             1;
291             __END__