File Coverage

blib/lib/SVG/Parser/Base.pm
Criterion Covered Total %
statement 102 177 57.6
branch 22 66 33.3
condition 4 32 12.5
subroutine 23 33 69.7
pod 0 19 0.0
total 151 327 46.1


line stmt bran cond sub pod time code
1             package SVG::Parser::Base;
2 6     6   32 use strict;
  6         12  
  6         230  
3              
4 6     6   31 use vars qw($VERSION);
  6         11  
  6         312  
5             $VERSION="1.03";
6              
7             #-------------------------------------------------------------------------------
8              
9             # XML declaration defaults
10 6     6   29 use constant SVG_DEFAULT_DECL_VERSION => "1.0";
  6         9  
  6         417  
11 6     6   29 use constant SVG_DEFAULT_DECL_ENCODING => "UTF-8";
  6         11  
  6         272  
12 6     6   36 use constant SVG_DEFAULT_DECL_STANDALONE => "yes";
  6         10  
  6         292  
13              
14             # Document type definition defaults
15 6     6   27 use constant SVG_DEFAULT_DOCTYPE_NAME => "svg";
  6         18  
  6         271  
16 6     6   27 use constant SVG_DEFAULT_DOCTYPE_SYSID => "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd";
  6         16  
  6         248  
17 6     6   35 use constant SVG_DEFAULT_DOCTYPE_PUBID => "-//W3C//DTD SVG 1.0//EN";
  6         27  
  6         1333  
18              
19             #-------------------------------------------------------------------------------
20              
21             # debug: simple debug method
22             sub debug ($$;@) {
23 208     208 0 490 my ($self,$what,@msgs)=@_;
24 208 50       1458 return unless $self->{-debug};
25              
26 0         0 my $first=1;
27 0   0     0 my $name||=ref($self);
28 0   0     0 my $length||=length($name);
29 0 0       0 if (@msgs) {
30 0         0 foreach (@msgs) {
31 0 0       0 next unless defined $_;
32 0 0       0 printf STDERR "+++%s %-8s: %s\n",
    0          
33             ($first?$name:" "x$length),
34             ($first?$what:" "),$_;
35 0         0 $first=0;
36             }
37             } else {
38 0         0 printf STDERR "+++%s %s\n",$name,$what;
39             }
40             }
41              
42             #-------------------------------------------------------------------------------
43              
44 6     6   32 use constant ARG_IS_STRING => "string";
  6         10  
  6         303  
45 6     6   29 use constant ARG_IS_HANDLE => "handle";
  6         11  
  6         247  
46 6     6   41 use constant ARG_IS_HASHRF => "hash";
  6         10  
  6         303  
47 6     6   30 use constant ARG_IS_INVALID => "nonsuch";
  6         8  
  6         1025  
48              
49             #-------------------------------------------------------------------------------
50              
51             # is it a bird...is it a plane?
52             sub identify ($$) {
53 6     6 0 16 my ($self,$source)=@_;
54              
55 6 50       26 return ARG_IS_INVALID unless $source;
56              
57             # assume a string unless we determine differently
58 6         14 my $type=ARG_IS_STRING;
59              
60             # check for various filehandle cases
61 6 100       25 if (ref $source) {
62 2         6 my $class = ref($source);
63              
64 2 50       21 if (UNIVERSAL::isa($source,'IO::Handle')) {
    50          
65             # it's a new-style filehandle
66 0         0 $type=ARG_IS_HANDLE;
67             } elsif (tied($source)) {
68             # it's a tied filehandle?
69 6     6   38 no strict 'refs';
  6         18  
  6         439  
70 0 0       0 $type=ARG_IS_HANDLE if defined &{"${class}::TIEHANDLE"};
  0         0  
71             }
72             } else {
73             # it's an old-style filehandle?
74 6     6   34 no strict 'refs';
  6         10  
  6         10453  
75 4 100       8 $type=ARG_IS_HANDLE if eval { *{$source}{IO} };
  4         13  
  4         169  
76             }
77              
78             # possibly a hash argument is called via parse_file (SAX)
79 6 100 66     41 $type=ARG_IS_HASHRF if ref($source) and $type eq ARG_IS_STRING;
80              
81 6         29 return $type;
82             }
83              
84             #-------------------------------------------------------------------------------
85             # Additional SVG.pm processing
86              
87             sub process_attrs ($$) {
88 160     160 0 204 my ($parser,$attrs)=@_;
89              
90 160 100       479 if (exists $attrs->{style}) {
91 96         3127 my %styles=split /\s*[:;]\s*/,$attrs->{style};
92 96         397 $attrs->{style}=\%styles;
93             }
94             }
95              
96             #---------------------------------------------------------------------------------------
97             # Shared Expat/SAX Handlers
98              
99             # create and set SVG document object as root element
100             sub StartDocument {
101 4     4 0 16 my $parser=shift;
102              
103             # gather SVG constuctor attributes
104 4         9 my %svg_attr;
105 4 50       21 %svg_attr=%{delete $parser->{__svg_attr}} if exists $parser->{__svg_attr};
  0         0  
106 4         19 $svg_attr{-nostub}=1;
107             # instantiate SVG document object
108 4         41 $parser->{__svg}=new SVG(%svg_attr);
109             # empty element list
110 4         583 $parser->{__elements}=[];
111             # empty unassigned attlist list (for internal DTD subset handling)
112 4         12 $parser->{__unassigned_attlists}=[];
113             # cdata count
114 4         13 $parser->{__in_cdata}=0;
115              
116 4         68 $parser->debug("Start",$parser."/".$parser->{__svg});
117             }
118              
119             # handle start of element - extend chain by one
120             sub StartTag {
121 160     160 0 791 my ($parser,$type,%attrs)=@_;
122 160         287 my $elements=$parser->{__elements};
123 160         237 my $svg=$parser->{__svg};
124              
125             # some attributes need extra processing
126 160         488 $parser->process_attrs(\%attrs);
127              
128 160 100       376 if (@$elements) {
129 156         227 my $parent=$elements->[-1];
130 156         931 push @$elements, $parent->element($type,%attrs);
131             } else {
132 4 50       31 $svg->{-inline}=1 if $type ne "svg"; #inlined
133 4         61 my $el=$svg->element($type,%attrs);
134 4         489 $svg->{-document} = $el;
135 4         15 push @$elements, $el;
136             }
137              
138 160         13333 $parser->debug("Element",$type);
139             }
140              
141             # handle end of element - shorten chain by one
142             sub EndTag {
143 160     160 0 211 my ($parser,$type)=@_;
144 160         230 my $elements=$parser->{__elements};
145 160         463 pop @$elements;
146             }
147              
148             # handle cannonical data (text)
149             sub Text {
150 260     260 0 362 my ($parser,$text)=@_;
151 260         365 my $elements=$parser->{__elements};
152              
153 260 100       1476 return if $text=~/^\s*$/s; #ignore redundant whitespace
154 32         45 my $parent=$elements->[-1];
155              
156             # are we in a CDATA section? (see CdataStart/End below)
157 32 50       73 if ($parser->{__in_cdata}) {
158 0   0     0 my $current=$parent->{-CDATA} || '';
159 0         0 $parent->CDATA($current.$parser->{__svg}{-elsep}.$text);
160             } else {
161 32   100     128 my $current=$parent->{-cdata} || '';
162 32         167 $parent->cdata($current.$parser->{__svg}{-elsep}.$text);
163             }
164              
165 32         302 $parser->debug("CDATA","\"$text\"");
166             }
167              
168             # handle cannonical data (CDATA sections)
169             sub CdataStart {
170 0     0 0 0 my $parser=shift;
171 0         0 $parser->{__in_cdata}++;
172              
173 0         0 $parser->debug("CDATA","start->");
174             }
175              
176             sub CdataEnd {
177 0     0 0 0 my $parser=shift;
178 0         0 my $elements=$parser->{__elements};
179 0         0 my $parent=$elements->[-1];
180              
181 0   0     0 my $current=$parent->{-CDATA} || '';
182 0         0 $parent->CDATA($current.$parser->{__svg}{-elsep});
183 0         0 $parser->{__in_cdata}--;
184              
185 0         0 $parser->debug("CDATA","<-end");
186             }
187              
188             # handle processing instructions
189             sub PI {
190 0     0 0 0 my ($parser,$target,$data)=@_;
191 0         0 my $elements=$parser->{__elements};
192              
193 0 0       0 if (my $parent=$elements->[-1]) {
194 0         0 /^<\?(.*)\?>/;
195 0         0 $parent->pi($1);
196             };
197              
198 0         0 $parser->debug("PI",$_);
199             }
200              
201             # handle XML Comments
202             sub Comment {
203 8     8 0 16 my ($parser,$data)=@_;
204              
205 8         25 my $elements=$parser->{__elements};
206 8 100       145 if (my $parent=$elements->[-1]) {
207             # SVG.pm doesn't handle comment prior to document start
208 4         25 $parent->comment($data);
209             }
210              
211 8         223 $parser->debug("Comment",$data);
212             }
213              
214             # return root SVG document object as result of parse()
215             sub FinishDocument {
216 4     4 0 10 my $parser=shift;
217 4         11 my $svg=$parser->{__svg};
218              
219             # add any attlists that were seen before their element
220 4 50       18 if (my $attlists=$parser->{__unassigned_attlists}) {
221 4         13 foreach my $unassigned_attlist (@$attlists) {
222             # if the element is still missing this will complain (if the parser didn't)
223 0         0 $svg->attlist_decl(@$unassigned_attlist);
224             }
225             }
226              
227 4         22 $parser->debug("Done");
228              
229 4         234 return $parser->{__svg};
230             }
231              
232             #---------------------------------------------------------------------------------------
233              
234             # handle XML declaration, if present
235             sub XMLDecl {
236 0     0 0   my ($parser,$version,$encoding,$standalone)=@_;
237 0           my $svg=$parser->{__svg};
238              
239 0   0       $svg->{-version}=$version || $parser->SVG_DEFAULT_DECL_VERSION;
240 0   0       $svg->{-encoding}=$encoding || $parser->SVG_DEFAULT_DECL_ENCODING;
241 0 0         $svg->{-standalone}=$standalone?"yes":"no";
242              
243 0           $parser->debug("XMLDecl","-version=\"$svg->{-version}\"",
244             "-encoding=\"$svg->{-encoding}\"","-standalone=\"$svg->{-standalone}\"");
245             }
246              
247             # handle Doctype declaration, if present
248             sub Doctype {
249 0     0 0   my ($parser,$name,$sysid,$pubid,$internal)=@_;
250 0           my $svg=$parser->{__svg};
251              
252 0   0       $svg->{-docroot}=$name || $parser->SVG_DEFAULT_DOCTYPE_NAME;
253 0   0       $svg->{-sysid}=$sysid || $parser->SVG_DEFAULT_DOCTYPE_SYSID;
254 0   0       $svg->{-pubid}=$pubid || $parser->SVG_DEFAULT_DOCTYPE_PUBID;
255              
256 0           $parser->debug("Doctype",
257             "-docroot=\"$svg->{-docroot}\"",
258             "-sysid=\"$svg->{-sysid}\"",
259             "-pubid=\"$svg->{-pubid}\"",
260             );
261             }
262              
263             #---------------------------------------------------------------------------------------
264              
265             # Unparsed (Expat, Entity, Base, Sysid, Pubid, Notation)
266             sub Unparsed {
267 0     0 0   my ($parser,$name,$base,$sysid,$pubid,$notation)=@_;
268 0           my $svg=$parser->{__svg};
269              
270 0           my %entity=(name=>$name, sysid=>$sysid, notation=>$notation);
271 0 0         $entity{base}=$base if defined $base;
272 0 0         $entity{sysid}=$sysid if defined $sysid;
273              
274 0           $svg->entity_decl(%entity);
275             }
276              
277             # Notation (Expat, Notation, Base, Sysid, Pubid)
278             sub Notation {
279 0     0 0   my ($parser,$name,$base,$sysid,$pubid)=@_;
280 0           my $svg=$parser->{__svg};
281              
282 0           my %notation=(name=>$name);
283 0 0         $notation{base}=$base if defined $base;
284 0 0         $notation{sysid}=$sysid if defined $sysid;
285 0 0         $notation{pubid}=$pubid if defined $pubid;
286              
287 0           $svg->notation_decl(%notation);
288             }
289              
290             # Entity (Expat, Name, Val, Sysid, Pubid, Ndata, IsParam)
291             sub Entity {
292 0     0 0   my ($parser,$name,$val,$sysid,$pubid,$data,$isp)=@_;
293 0           my $svg=$parser->{__svg};
294 0   0       $isp||=0;
295              
296 0 0         if (defined $val) {
    0          
297 0           $svg->entity_decl(name=>$name, value=>$val, isp=>$isp);
298             } elsif (defined $pubid) {
299 0           $svg->entity_decl(name=>$name, sysid=>$sysid, pubid=>$pubid, ndata=>$data, isp=>$isp);
300             } else {
301 0           $svg->entity_decl(name=>$name, sysid=>$sysid, ndata=>$data, isp=>$isp);
302             }
303             }
304              
305             # Element (Expat, Name, Model)
306             sub Element {
307 0     0 0   my ($parser,$name,$model)=@_;
308 0           my $svg=$parser->{__svg};
309              
310 0 0         if (defined $model) {
311             # convert model to string context
312 0           $svg->element_decl(name=>$name, model=>qq{$model});
313             } else {
314 0           $svg->element_decl(name=>$name);
315             }
316             }
317              
318             # Attlist (Expat, Elname, Attname, Type, Default, Fixed)
319             sub Attlist {
320 0     0 0   my ($parser,$name,$attr,$type,$default,$fixed)=@_;
321 0           my $svg=$parser->{__svg};
322              
323 0 0         if ($svg->getElementDeclByName($name)) {
324 0 0         $svg->attlist_decl(
325             name=>$name, attr=>$attr, type=>$type, default=>$default, fixed=>($fixed?1:0)
326             );
327             } else {
328 0 0         push @{$parser->{__unassigned_attlists}}, [
  0            
329             name=>$name, attr=>$attr, type=>$type, default=>$default, fixed=>($fixed?1:0)
330             ];
331             }
332             }
333              
334             #---------------------------------------------------------------------------------------
335              
336             1;