File Coverage

blib/lib/SVG/XML.pm
Criterion Covered Total %
statement 73 76 96.0
branch 20 28 71.4
condition 10 15 66.6
subroutine 16 16 100.0
pod 0 13 0.0
total 119 148 80.4


line stmt bran cond sub pod time code
1             package SVG::XML;
2 30     30   222 use strict;
  30         82  
  30         1239  
3 30     30   229 use warnings;
  30         64  
  30         2176  
4              
5             our $VERSION = '2.89';
6              
7             =pod
8              
9             =head1 NAME
10              
11             SVG::XML - Handle the XML generation bits for SVG.pm
12              
13             =head1 AUTHOR
14              
15             Ronan Oger, cpan@roitsystems.com
16              
17             =head1 SEE ALSO
18              
19             L<SVG>,
20             L<SVG::DOM>,
21             L<SVG::Element>,
22             L<SVG::Parser>,
23             L<SVG::Extension>
24              
25             =cut
26              
27 30     30   174 use Exporter;
  30         76  
  30         72005  
28              
29             our @ISA = ('Exporter');
30              
31             our @EXPORT = qw(
32             xmlesc
33             xmlescape
34             xmlescp
35             cssstyle
36             xmlattrib
37             xmlcomment
38             xmlpi
39             xmltag
40             xmltagopen
41             xmltagclose
42             xmltag_ln
43             xmltagopen_ln
44             xmltagclose_ln
45             processtag
46             xmldecl
47             dtddecl
48             );
49              
50             sub xmlescp {
51 14     14 0 50 my ( $self, $s ) = @_;
52              
53 14 50       48 $s = '0' unless defined $s;
54 14 50       66 $s = join( ', ', @{$s} ) if ( ref($s) eq 'ARRAY' );
  0         0  
55              
56             # Special XML entities are escaped
57 14         49 $s =~ s/&(?!#(x\w\w|\d+?);)/&amp;/g;
58 14         32 $s =~ s/>/&gt;/g;
59 14         40 $s =~ s/</&lt;/g;
60 14         46 $s =~ s/\"/&quot;/g;
61 14         40 $s =~ s/\'/&apos;/g;
62              
63             # Backtick is just a regular XML citizen
64             #$s=~s/\`/&apos;/g;
65              
66             # Invalid XML characters are removed, not just escaped: \x00-\x08\x0b\x1f
67             # Tabs (\x09) and newlines (\x0a) are valid.
68 14         68 while ( $s =~ s/([\x00-\x08\x0b\x1f])/''/e ) {
  10         33  
69 10         31 my $char = q{'\\x} . sprintf( '%02X', ord($1) ) . q{'};
70 10         27 $self->error( $char => 'This forbidden XML character was removed' );
71             }
72              
73             # Per suggestion from Adam Schneider
74 14         34 $s =~ s/([\200-\377])/'&#'.ord($1).';'/ge;
  0         0  
75              
76 14         53 return $s;
77             }
78              
79             *xmlesc = \&xmlescp;
80              
81             *xmlescape = \&xmlescp;
82              
83             sub cssstyle {
84 5     5 0 20 my %attrs = @_;
85 5         21 return ( join( '; ', map { qq($_: ) . $attrs{$_} } sort keys(%attrs) ) );
  13         111  
86             }
87              
88             # Per suggestion from Adam Schneider
89             sub xmlattrib {
90 93     93 0 252 my %attrs = @_;
91 93 100       343 return '' unless ( scalar( keys %attrs ) );
92             return (
93             ' '
94             . join( ' ',
95 69         430 map { qq($_=") . $attrs{$_} . q(") } sort keys(%attrs) )
  309         1156  
96             );
97             }
98              
99             sub xmltag {
100 39     39 0 115 my ( $name, $ns, %attrs ) = @_;
101 39 50       106 $ns = $ns ? "$ns:" : '';
102 39   100     195 my $at = xmlattrib(%attrs) || '';
103 39         221 return qq(<$ns$name$at />);
104             }
105              
106             sub xmltag_ln {
107 39     39 0 137 my ( $name, $ns, %attrs ) = @_;
108 39         161 return xmltag( $name, $ns, %attrs );
109             }
110              
111             sub xmltagopen {
112 54     54 0 205 my ( $name, $ns, %attrs ) = @_;
113 54 50       189 $ns = $ns ? "$ns:" : '';
114 54   100     205 my $at = xmlattrib(%attrs) || '';
115 54         305 return qq(<$ns$name$at>);
116             }
117              
118             sub xmltagopen_ln {
119 54     54 0 189 my ( $name, $ns, %attrs ) = @_;
120 54         234 return xmltagopen( $name, $ns, %attrs );
121             }
122              
123             sub xmlcomment {
124 27     27 0 63 my ( $self, $r_comment ) = @_;
125             my $ind = $self->{-docref}->{-elsep}
126 27         133 . $self->{-docref}->{-indent} x $self->{-docref}->{-level};
127              
128             # If the comment starts with newline character then do not prefix
129             # with space (RT #123896).
130             return (
131             $ind . join(
132             $ind,
133             map {
134 27 100       150 ( (/^\n/) ? (q(<!--)) : (q(<!-- )) ) . qq($_)
  27 100       445  
135             . ( (/\n[\t]?$/) ? (q(-->)) : (q( -->)) );
136             } @$r_comment
137             )
138             );
139             }
140              
141             sub xmlpi {
142 2     2 0 5 my ( $self, $r_pi ) = @_;
143             my $ind = $self->{-docref}->{-elsep}
144 2         10 . $self->{-docref}->{-indent} x $self->{-docref}->{-level};
145 2         5 return ( join( $ind, map {qq(<?$_?>)} @$r_pi ) );
  5         21  
146             }
147              
148             *processinginstruction = \&xmlpi;
149              
150             sub xmltagclose {
151 54     54 0 145 my ( $name, $ns ) = @_;
152 54 50       180 $ns = $ns ? "$ns:" : '';
153 54         179 return qq(</$ns$name>);
154             }
155              
156             sub xmltagclose_ln {
157 54     54 0 125 my ( $name, $ns ) = @_;
158 54         198 return xmltagclose( $name, $ns );
159             }
160              
161             sub dtddecl {
162 32     32 0 79 my $self = shift;
163 32   50     108 my $docroot = $self->{-docroot} || 'svg';
164 32         52 my $id;
165              
166 32 100       95 if ( $self->{-pubid} ) {
    50          
167 31         63 $id = 'PUBLIC "' . $self->{-pubid} . '"';
168 31 50       114 $id .= ' "' . $self->{-sysid} . '"' if ( $self->{-sysid} );
169             }
170             elsif ( $self->{-sysid} ) {
171 1         3 $id = 'SYSTEM "' . $self->{-sysid} . '"';
172             }
173             else {
174             $id
175             = 'PUBLIC "-//W3C//DTD SVG 1.0//EN"'
176             . $self->{-docref}->{-elsep}
177 0         0 . "\"$self->{-docref}->{-dtd}\"";
178             }
179              
180 32         127 my $at = join( ' ', ( $docroot, $id ) );
181              
182             #>>>TBD: add internal() method to return this
183             my $extension
184             = ( exists $self->{-internal} )
185             ? $self->{-internal}->render()
186 32 50       101 : q{};
187 32 100 66     265 if ( exists $self->{-extension} and $self->{-extension} ) {
188             $extension
189             .= $self->{-docref}{-elsep}
190             . $self->{-extension}
191 1         4 . $self->{-docref}{-elsep};
192             }
193 32 100       96 $extension = ' [' . $self->{-docref}{-elsep} . $extension . ']'
194             if $extension;
195              
196 32         139 return qq[$self->{-docref}{-elsep}<!DOCTYPE $at$extension>];
197             }
198              
199             sub xmldecl {
200 27     27 0 57 my $self = shift;
201              
202 27   50     153 my $version = $self->{-version} || '1.0';
203 27   50     81 my $encoding = $self->{-encoding} || 'UTF-8';
204 27   50     102 my $standalone = $self->{-standalone} || 'yes';
205              
206             return
207 27         128 qq{<?xml version="$version" encoding="$encoding" standalone="$standalone"?>};
208             }
209              
210             #-------------------------------------------------------------------------------
211              
212             1;