line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#! perl |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
13
|
use v5.26; |
|
1
|
|
|
|
|
5
|
|
4
|
1
|
|
|
1
|
|
6
|
use Object::Pad; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
7
|
|
5
|
1
|
|
|
1
|
|
98
|
use utf8; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
6
|
|
|
|
|
|
|
class SVGPDF::Element; |
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
228
|
use Carp; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
9168
|
|
9
|
|
|
|
|
|
|
|
10
|
0
|
|
|
0
|
0
|
|
field $xo :mutator; |
|
0
|
|
|
|
|
|
|
11
|
0
|
0
|
|
0
|
0
|
|
field $style :accessor; |
|
0
|
|
|
|
|
|
|
12
|
0
|
0
|
|
0
|
0
|
|
field $name :param :accessor; |
|
0
|
|
|
|
|
|
|
13
|
0
|
0
|
|
0
|
0
|
|
field $atts :param :accessor; |
|
0
|
|
|
|
|
|
|
14
|
0
|
0
|
|
0
|
0
|
|
field $css :accessor; |
|
0
|
|
|
|
|
|
|
15
|
0
|
0
|
|
0
|
0
|
|
field $content :param :accessor; # array of children |
|
0
|
|
|
|
|
|
|
16
|
0
|
0
|
|
0
|
0
|
|
field $root :param :accessor; # top module |
|
0
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
BUILD { |
19
|
|
|
|
|
|
|
$css = $root->css; |
20
|
|
|
|
|
|
|
$xo = $root->xoforms->[-1]->{xo}; |
21
|
|
|
|
|
|
|
}; |
22
|
|
|
|
|
|
|
|
23
|
0
|
|
|
0
|
|
|
method _dbg (@args) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
24
|
0
|
|
|
|
|
|
$root->_dbg(@args); |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
0
|
|
|
0
|
0
|
|
method css_push ( $updated_atts = undef ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
28
|
0
|
|
0
|
|
|
|
$style = $css->push( element => $name, %{$updated_atts // $atts} ); |
|
0
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
0
|
|
|
0
|
0
|
|
method css_pop () { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
32
|
0
|
|
|
|
|
|
$css->pop; |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
0
|
|
|
0
|
0
|
|
method set_transform ( $tf ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
36
|
0
|
0
|
|
|
|
|
return unless $tf; |
37
|
|
|
|
|
|
|
|
38
|
0
|
|
|
|
|
|
my $nooptimize = 1; |
39
|
0
|
|
|
|
|
|
$tf =~ s/\s+/ /g; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# The parts of the transform need to be executed in order. |
42
|
0
|
|
|
|
|
|
while ( $tf =~ /\S/ ) { |
43
|
0
|
0
|
|
|
|
|
if ( $tf =~ /^\s*translate\s*\((.*?)\)(.*)/ ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
44
|
0
|
|
|
|
|
|
$tf = $2; |
45
|
0
|
|
|
|
|
|
my ( $x, $y ) = $self->getargs($1); |
46
|
0
|
|
0
|
|
|
|
$y ||= 0; |
47
|
0
|
0
|
0
|
|
|
|
if ( $nooptimize || $x || $y ) { |
|
|
|
0
|
|
|
|
|
48
|
0
|
|
|
|
|
|
$xo->transform( translate => [ $x, $y ] ); |
49
|
0
|
|
|
|
|
|
$self->_dbg( "transform translate(%.2f,%.2f)", $x, $y ); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
elsif ( $tf =~ /^\s*rotate\s*\((.*?)\)(.*)/ ) { |
53
|
0
|
|
|
|
|
|
$tf = $2; |
54
|
0
|
|
|
|
|
|
my ( $r, $x, $y ) = $self->getargs($1); |
55
|
0
|
0
|
0
|
|
|
|
if ( $nooptimize || $r ) { |
56
|
0
|
0
|
0
|
|
|
|
if ( $x || $y ) { |
57
|
0
|
|
|
|
|
|
$xo->transform( translate => [ $x, $y ] ); |
58
|
0
|
|
|
|
|
|
$self->_dbg( "transform translate(%.2f,%.2f)", $x, $y ); |
59
|
|
|
|
|
|
|
} |
60
|
0
|
|
|
|
|
|
$self->_dbg( "transform rotate(%.2f)", $r ); |
61
|
0
|
|
|
|
|
|
$xo->transform( rotate => $r ); |
62
|
0
|
0
|
0
|
|
|
|
if ( $x || $y ) { |
63
|
0
|
|
|
|
|
|
$xo->transform( translate => [ -$x, -$y ] ); |
64
|
0
|
|
|
|
|
|
$self->_dbg( "transform translate(%.2f,%.2f)", -$x, -$y ); |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
elsif ( $tf =~ /^\s*scale\s*\((.*?)\)(.*)/ ) { |
69
|
0
|
|
|
|
|
|
$tf = $2; |
70
|
0
|
|
|
|
|
|
my ( $x, $y ) = $self->getargs($1); |
71
|
0
|
|
0
|
|
|
|
$y ||= $x; |
72
|
0
|
0
|
0
|
|
|
|
if ( $nooptimize || $x != 1 && $y != 1 ) { |
|
|
|
0
|
|
|
|
|
73
|
0
|
|
|
|
|
|
$self->_dbg( "transform scale(%.2f,%.2f)", $x, $y ); |
74
|
0
|
|
|
|
|
|
$xo->transform( scale => [ $x, $y ] ); |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
elsif ( $tf =~ /^\s*matrix\s*\((.*?)\)(.*)/ ) { |
78
|
0
|
|
|
|
|
|
$tf = $2; |
79
|
0
|
|
|
|
|
|
my ( @m ) = $self->getargs($1); |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# 1 0 0 1 dx dy translate |
82
|
|
|
|
|
|
|
# sx 0 0 sy 0 0 scale |
83
|
|
|
|
|
|
|
# c s -s c 0 0 rotate (s = sin, c = cos) |
84
|
|
|
|
|
|
|
# 1 a b 1 0 0 skew (a = tan a, b = tan b) |
85
|
|
|
|
|
|
|
|
86
|
0
|
|
|
|
|
|
$self->_dbg( "transform matrix(%.2f,%.2f %.2f,%.2f %.2f,%.2f)", @m); |
87
|
0
|
|
|
|
|
|
$xo->matrix(@m); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
elsif ( $tf =~ /^\s*skew([XY])\s*\((.*?)\)(.*)/i ) { |
90
|
0
|
|
|
|
|
|
$tf = $3; |
91
|
0
|
|
|
|
|
|
my ( $x ) = $self->getargs($2); |
92
|
0
|
|
|
|
|
|
my $y = 0; |
93
|
0
|
0
|
|
|
|
|
if ( $1 eq "X" ) { |
94
|
0
|
|
|
|
|
|
$y = $x; |
95
|
0
|
|
|
|
|
|
$x = 0; |
96
|
|
|
|
|
|
|
} |
97
|
0
|
|
|
|
|
|
$self->_dbg( "transform skew(%.2f %.2f)", $x, $y ); |
98
|
0
|
|
|
|
|
|
$xo->transform( skew => [ $x, $y ] ); |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
else { |
101
|
0
|
0
|
|
|
|
|
warn("Ignoring transform: $tf") |
102
|
|
|
|
|
|
|
if $self->root->verbose; |
103
|
0
|
|
|
|
|
|
$self->_dbg("Ignoring transform: \"$tf\""); |
104
|
0
|
|
|
|
|
|
$tf = ""; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
# %rel = ( relative => 1 ); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
0
|
|
|
0
|
0
|
|
method set_graphics () { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
|
my $msg = $name; |
113
|
|
|
|
|
|
|
|
114
|
0
|
0
|
|
|
|
|
if ( defined( my $lw = $style->{'stroke-width'} ) ) { |
115
|
|
|
|
|
|
|
my $w = $self->u( $lw, |
116
|
|
|
|
|
|
|
fontsize => $style->{'font-size'}, |
117
|
0
|
|
|
|
|
|
width => $self->root->xoforms->[-1]->{diag}); |
118
|
0
|
|
|
|
|
|
$msg .= " stroke-width=$w"; |
119
|
0
|
0
|
|
|
|
|
if ( $lw =~ /e[mx]/ ) { |
120
|
|
|
|
|
|
|
$msg .= "($lw@" . |
121
|
0
|
|
0
|
|
|
|
( $style->{'font-size'}|| $self->root->fontsize) . ")"; |
122
|
|
|
|
|
|
|
} |
123
|
0
|
0
|
|
|
|
|
if ( $lw =~ /\%/ ) { |
124
|
|
|
|
|
|
|
$msg .= "($lw@" . |
125
|
0
|
|
|
|
|
|
( $self->root->xoforms->[-1]->{diag}) . ")"; |
126
|
|
|
|
|
|
|
} |
127
|
0
|
|
|
|
|
|
$xo->line_width($w); |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
0
|
0
|
|
|
|
|
if ( defined( my $linecap = $style->{'stroke-linecap'} ) ) { |
131
|
0
|
|
|
|
|
|
$linecap = lc($linecap); |
132
|
0
|
0
|
|
|
|
|
if ( $linecap eq "round" ) { $linecap = 1 } |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
133
|
0
|
|
|
|
|
|
elsif ( $linecap eq "r" ) { $linecap = 1 } |
134
|
0
|
|
|
|
|
|
elsif ( $linecap eq "square" ) { $linecap = 2 } |
135
|
0
|
|
|
|
|
|
elsif ( $linecap eq "s" ) { $linecap = 2 } |
136
|
0
|
|
|
|
|
|
else { $linecap = 0 } # b butt |
137
|
0
|
|
|
|
|
|
$msg .= " linecap=$linecap"; |
138
|
0
|
|
|
|
|
|
$xo->line_cap($linecap); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
0
|
0
|
|
|
|
|
if ( defined( my $linejoin = $style->{'stroke-linejoin'} ) ) { |
142
|
0
|
|
|
|
|
|
$linejoin = lc($linejoin); |
143
|
0
|
0
|
|
|
|
|
if ( $linejoin eq "round" ) { $linejoin = 1 } |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
144
|
0
|
|
|
|
|
|
elsif ( $linejoin eq "r" ) { $linejoin = 1 } |
145
|
0
|
|
|
|
|
|
elsif ( $linejoin eq "bevel" ) { $linejoin = 2 } |
146
|
0
|
|
|
|
|
|
elsif ( $linejoin eq "b" ) { $linejoin = 2 } |
147
|
0
|
|
|
|
|
|
else { $linejoin = 0 } # m miter |
148
|
0
|
|
|
|
|
|
$msg .= " linejoin=$linejoin"; |
149
|
0
|
|
|
|
|
|
$xo->line_join($linejoin); |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
0
|
|
|
|
|
|
my $color = $style->{color}; |
153
|
0
|
|
|
|
|
|
my $stroke = $style->{stroke}; |
154
|
0
|
0
|
|
|
|
|
if ( lc($stroke) eq "currentcolor" ) { |
155
|
|
|
|
|
|
|
# Nothing. Use current. |
156
|
0
|
|
|
|
|
|
$msg .= " stroke=(current)"; |
157
|
0
|
|
|
|
|
|
$stroke = $color; |
158
|
|
|
|
|
|
|
} |
159
|
0
|
0
|
|
|
|
|
if ( $stroke ne "none" ) { |
160
|
0
|
|
|
|
|
|
$stroke =~ s/\s+//g; |
161
|
0
|
0
|
|
|
|
|
if ( $stroke =~ /rgb\(([\d.]+)%,([\d.]+)%,([\d.]+)%\)/ ) { |
|
|
0
|
|
|
|
|
|
162
|
|
|
|
|
|
|
$stroke = sprintf("#%02X%02X%02X", |
163
|
0
|
|
|
|
|
|
map { $_*2.55 } $1, $2, $3); |
|
0
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
elsif ( $stroke =~ /rgb\(([\d.]+),([\d.]+),([\d.]+)\)/ ) { |
166
|
0
|
|
|
|
|
|
$stroke = sprintf("#%02X%02X%02X", $1, $2, $3); |
167
|
|
|
|
|
|
|
} |
168
|
0
|
|
|
|
|
|
$xo->stroke_color($stroke); |
169
|
0
|
|
|
|
|
|
$msg .= " stroke=$stroke"; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
else { |
172
|
0
|
|
|
|
|
|
$msg .= " stroke=none"; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
|
my $fill = $style->{fill}; |
176
|
0
|
0
|
|
|
|
|
if ( lc($fill) eq "currentcolor" ) { |
177
|
|
|
|
|
|
|
# Nothing. Use current. |
178
|
0
|
|
|
|
|
|
$msg .= " fill=(current)"; |
179
|
0
|
|
|
|
|
|
$fill = $color; |
180
|
|
|
|
|
|
|
} |
181
|
0
|
0
|
0
|
|
|
|
if ( lc($fill) ne "none" && $fill ne "transparent" ) { |
182
|
0
|
|
|
|
|
|
$fill =~ s/\s+//g; |
183
|
0
|
0
|
|
|
|
|
if ( $fill =~ /rgb\(([\d.]+)%,([\d.]+)%,([\d.]+)%\)/ ) { |
|
|
0
|
|
|
|
|
|
184
|
|
|
|
|
|
|
$fill = sprintf("#%02X%02X%02X", |
185
|
0
|
|
|
|
|
|
map { $_*2.55 } $1, $2, $3); |
|
0
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
elsif ( $fill =~ /rgb\(([\d.]+),([\d.]+),([\d.]+)\)/ ) { |
188
|
0
|
|
|
|
|
|
$fill = sprintf("#%02X%02X%02X", $1, $2, $3); |
189
|
|
|
|
|
|
|
} |
190
|
0
|
|
|
|
|
|
$xo->fill_color($fill); |
191
|
0
|
|
|
|
|
|
$msg .= " fill=$fill"; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
else { |
194
|
0
|
|
|
|
|
|
$msg .= " fill=none"; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
0
|
0
|
|
|
|
|
if ( my $sda = $style->{'stroke-dasharray'} ) { |
198
|
0
|
|
|
|
|
|
my @sda; |
199
|
0
|
0
|
0
|
|
|
|
if ( $sda && $sda ne "none" ) { |
200
|
0
|
|
|
|
|
|
$sda =~ s/,/ /g; |
201
|
0
|
|
|
|
|
|
@sda = split( ' ', $sda ); |
202
|
|
|
|
|
|
|
} |
203
|
0
|
|
|
|
|
|
$msg .= " sda=@sda"; |
204
|
0
|
|
|
|
|
|
$xo->line_dash_pattern(@sda); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
$self->_dbg( "%s", $msg ); |
208
|
0
|
|
|
|
|
|
return $style; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# Return a stroke/fill/paint sub depending on the fill stroke styles. |
212
|
0
|
|
|
0
|
|
|
method _paintsub () { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
213
|
0
|
0
|
0
|
|
|
|
if ( $style->{stroke} |
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
214
|
|
|
|
|
|
|
&& $style->{stroke} ne 'none' |
215
|
|
|
|
|
|
|
&& $style->{stroke} ne 'transparent' |
216
|
|
|
|
|
|
|
# Hmm. Saw a note somewhere that it defaults to 0 but other notes |
217
|
|
|
|
|
|
|
# say that it should be 1px... |
218
|
|
|
|
|
|
|
&& $style->{'stroke-width'}//1 != 0 |
219
|
|
|
|
|
|
|
) { |
220
|
0
|
0
|
0
|
|
|
|
if ( $style->{fill} |
|
|
|
0
|
|
|
|
|
221
|
|
|
|
|
|
|
&& $style->{fill} ne 'none' |
222
|
|
|
|
|
|
|
&& $style->{fill} ne 'transparent' |
223
|
|
|
|
|
|
|
) { |
224
|
|
|
|
|
|
|
return sub { |
225
|
|
|
|
|
|
|
$self->_dbg("xo paint (", |
226
|
0
|
|
|
0
|
|
|
join(" ", $style->{stroke}, $style->{fill} ), ")"); |
227
|
0
|
|
|
|
|
|
$xo->paint; |
228
|
0
|
|
|
|
|
|
}; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
else { |
231
|
|
|
|
|
|
|
return sub { |
232
|
0
|
|
|
0
|
|
|
$self->_dbg("xo stroke (", $style->{stroke}, ")"); |
233
|
0
|
|
|
|
|
|
$xo->stroke; |
234
|
0
|
|
|
|
|
|
}; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
elsif ( $style->{fill} |
238
|
|
|
|
|
|
|
&& $style->{fill} ne 'none' |
239
|
|
|
|
|
|
|
&& $style->{fill} ne 'transparent' |
240
|
|
|
|
|
|
|
) { |
241
|
|
|
|
|
|
|
return sub { |
242
|
0
|
|
|
0
|
|
|
$self->_dbg("xo fill (", $style->{stroke}, ")"); |
243
|
0
|
|
|
|
|
|
$xo->fill; |
244
|
0
|
|
|
|
|
|
}; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
else { |
247
|
0
|
|
|
0
|
|
|
return sub {}; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
0
|
|
|
0
|
0
|
|
method process () { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# Unless overridden in a subclass there's not much we can do. |
253
|
0
|
|
|
|
|
|
state $warned = { desc => 1, title => 1, metadata => 1 }; |
254
|
|
|
|
|
|
|
warn("SVG: Skipping element \"$name\" (not implemented)\n") |
255
|
0
|
0
|
0
|
|
|
|
unless $warned->{$name}++ || !$self->root->verbose; |
256
|
0
|
|
|
|
|
|
$self->_dbg("skipping $name (not implemented)"); |
257
|
|
|
|
|
|
|
# $self->traverse; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
0
|
|
|
0
|
0
|
|
method get_children () { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# Note: This is the only place where these objects are created. |
263
|
|
|
|
|
|
|
|
264
|
0
|
|
|
|
|
|
my @res; |
265
|
0
|
|
|
|
|
|
for my $e ( @{$self->content} ) { |
|
0
|
|
|
|
|
|
|
266
|
0
|
0
|
|
|
|
|
if ( $e->{type} eq 'e' ) { |
|
|
0
|
|
|
|
|
|
267
|
0
|
|
|
|
|
|
my $pkg = "SVGPDF::" . ucfirst(lc $e->{name}); |
268
|
0
|
0
|
|
|
|
|
$pkg = "SVGPDF::Element" unless $pkg->can("process"); |
269
|
|
|
|
|
|
|
push( @res, $pkg->new |
270
|
|
|
|
|
|
|
( name => $e->{name}, |
271
|
0
|
|
|
|
|
|
atts => { map { lc($_) => $e->{attrib}->{$_} } keys %{$e->{attrib}} }, |
|
0
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
content => $e->{content}, |
273
|
0
|
|
|
|
|
|
root => $self->root, |
274
|
|
|
|
|
|
|
) ); |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
elsif ( $e->{type} eq 't' ) { |
277
|
|
|
|
|
|
|
push( @res, SVGPDF::TextElement->new |
278
|
|
|
|
|
|
|
( content => $e->{content}, |
279
|
0
|
|
|
|
|
|
) ); |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
else { |
282
|
|
|
|
|
|
|
# Basically a 'cannot happen', |
283
|
0
|
|
|
|
|
|
croak("Unhandled node type ", $e->{type}); |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
} |
286
|
0
|
|
|
|
|
|
return @res; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
0
|
|
|
0
|
0
|
|
method traverse () { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
290
|
0
|
|
|
|
|
|
for my $c ( $self->get_children ) { |
291
|
0
|
0
|
|
|
|
|
next if ref($c) eq "SVGPDF::TextElement"; |
292
|
0
|
|
|
|
|
|
$self->_dbg("+ start handling ", $c->name, " (", ref($c), ")"); |
293
|
0
|
|
|
|
|
|
$c->process; |
294
|
0
|
|
|
|
|
|
$self->_dbg("- end handling ", $c->name); |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
0
|
|
|
0
|
0
|
|
method u ( $a, %args ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
299
|
0
|
0
|
|
|
|
|
confess("Undef in units") unless defined $a; |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# Pixels per point. Usually 96/72. |
302
|
0
|
|
|
|
|
|
my $pxpt = $self->root->pxpi / $self->root->ptpi; |
303
|
|
|
|
|
|
|
|
304
|
0
|
0
|
|
|
|
|
return undef unless $a =~ /^([-+]?[\d.]+)(.*)$/; |
305
|
0
|
0
|
|
|
|
|
return $1*$pxpt if $2 eq "pt"; |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# default is px |
308
|
0
|
0
|
0
|
|
|
|
return $1 if $2 eq "" || $2 eq "px"; |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
# 1 inch = pxpi px. |
311
|
0
|
0
|
|
|
|
|
return $1/2.54 * $self->root->pxpi if $2 eq "cm"; |
312
|
0
|
0
|
|
|
|
|
return $1/25.4 * $self->root->pxpi if $2 eq "mm"; |
313
|
0
|
0
|
|
|
|
|
return $1 * $self->root->pxpi if $2 eq "in"; |
314
|
|
|
|
|
|
|
|
315
|
0
|
0
|
|
|
|
|
if ( $2 eq '%' ) { |
316
|
0
|
|
0
|
|
|
|
my $w = $args{width} || $self->root->xoforms->[-1]->{diag}; |
317
|
0
|
|
|
|
|
|
return $1/100 * $w * $pxpt; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
# Font dependent. |
320
|
|
|
|
|
|
|
# CSS defines em to be the font size. |
321
|
0
|
0
|
|
|
|
|
if ( $2 eq "em" ) { |
322
|
|
|
|
|
|
|
return $1 * ( $args{fontsize} |
323
|
0
|
|
0
|
|
|
|
|| $style->{'font-size'} |
324
|
|
|
|
|
|
|
|| $self->root->fontsize ); |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
# CSS defines ex to be half the font size. |
327
|
0
|
0
|
|
|
|
|
if ( $2 eq "ex" ) { |
328
|
|
|
|
|
|
|
return $1 * 0.5 * ( $args{fontsize} |
329
|
0
|
|
0
|
|
|
|
|| $style->{'font-size'} |
330
|
|
|
|
|
|
|
|| $self->root->fontsize ); |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
0
|
|
|
|
|
|
confess("Unhandled units in \"$a\""); |
334
|
0
|
|
|
|
|
|
return $a; # will hopefully crash somewhere... |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
0
|
|
|
0
|
0
|
|
method getargs ( $a ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
338
|
0
|
0
|
|
|
|
|
confess("Null attr?") unless defined $a; |
339
|
0
|
|
|
|
|
|
$a =~ s/^\s+//; |
340
|
0
|
|
|
|
|
|
$a =~ s/\s+$//; |
341
|
0
|
|
|
|
|
|
map { $self->u($_) } split( /\s*[,\s]\s*/, $a ); |
|
0
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# Initial fiddling with entity attributes. |
345
|
0
|
|
|
0
|
0
|
|
method get_params ( @desc ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
346
|
0
|
0
|
|
|
|
|
my $atts = shift(@desc) if ref($desc[0]) eq 'HASH'; |
347
|
0
|
|
|
|
|
|
my @res; |
348
|
0
|
|
0
|
|
|
|
my %atts = %{ $atts // $self->atts }; # copy |
|
0
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# xlink:href is obsoleted in favour of href. |
351
|
0
|
0
|
0
|
|
|
|
$atts{href} //= delete $atts{"xlink:href"} if exists $atts{"xlink:href"}; |
352
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
|
my @todo; |
354
|
0
|
|
|
|
|
|
for my $param ( @desc ) { |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# Attribute may be followed by ':' and flags. |
357
|
|
|
|
|
|
|
# 0 undef -> 0 |
358
|
|
|
|
|
|
|
# h process units, % is viewBox height |
359
|
|
|
|
|
|
|
# s undef -> "" |
360
|
|
|
|
|
|
|
# u process units |
361
|
|
|
|
|
|
|
# v process units, % is viewBox width |
362
|
|
|
|
|
|
|
# U undef -> 0, process units |
363
|
|
|
|
|
|
|
# ! barf if undef |
364
|
0
|
|
|
|
|
|
my $flags = ""; |
365
|
0
|
0
|
|
|
|
|
( $param, $flags ) = ( $1, $2 ) |
366
|
|
|
|
|
|
|
if $param =~ /^(.*):(.*)$/; |
367
|
0
|
|
|
|
|
|
$param = lc($param); |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# Get and remove the attribute. |
370
|
0
|
|
|
|
|
|
my $p = delete( $atts{$param} ); |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
# Queue. |
373
|
0
|
|
|
|
|
|
push( @todo, [ $param, $flags, $p ] ); |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# CSS push with updated attributes. |
377
|
0
|
|
|
|
|
|
$self->css_push( \%atts ); |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# Now we can process the values. |
380
|
0
|
|
|
|
|
|
for ( @todo ) { |
381
|
0
|
|
|
|
|
|
my ( $param, $flags, $p ) = @$_; |
382
|
|
|
|
|
|
|
|
383
|
0
|
0
|
|
|
|
|
unless ( defined $p ) { |
384
|
0
|
0
|
|
|
|
|
if ( $flags =~ /s/ ) { $p = ""; } |
|
0
|
0
|
|
|
|
|
|
385
|
0
|
|
|
|
|
|
elsif ( $flags =~ /[0HUV]/ ) { $p = 0; } |
386
|
|
|
|
|
|
|
else { |
387
|
0
|
0
|
|
|
|
|
croak("Undefined mandatory attribute: $param") |
388
|
|
|
|
|
|
|
if $flags =~ /\!/; |
389
|
0
|
|
|
|
|
|
push( @res, $p ); |
390
|
0
|
|
|
|
|
|
next; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
0
|
|
|
|
|
|
$flags = lc($flags); |
395
|
|
|
|
|
|
|
# Convert units if 'u' flag. |
396
|
0
|
0
|
|
|
|
|
if ( $flags =~ /([huv])/ ) { |
397
|
0
|
|
|
|
|
|
my $flag = $1; |
398
|
0
|
0
|
|
|
|
|
if ( $p =~ /^([\d.]+)\%$/ ) { |
399
|
0
|
|
|
|
|
|
$p = $1/100; |
400
|
0
|
0
|
0
|
|
|
|
if ( $flags eq "w" || $param =~ /^(?:w(?:idth)|x)?$/i ) { |
|
|
0
|
0
|
|
|
|
|
401
|
|
|
|
|
|
|
# Percentage of viewBox width. |
402
|
0
|
|
|
|
|
|
$p *= $root->xoforms->[-1]->{width}; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
elsif ( $flag eq "h" || $param =~ /^(?:h(?:eight)?|y)$/i ) { |
405
|
|
|
|
|
|
|
# Percentage of viewBox height. |
406
|
0
|
|
|
|
|
|
$p *= $root->xoforms->[-1]->{height}; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
else { |
409
|
|
|
|
|
|
|
# Percentage of viewBox diagonal. |
410
|
0
|
|
|
|
|
|
$p *= $root->xoforms->[-1]->{diag}; |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
else { |
414
|
0
|
|
|
|
|
|
$p = $self->u($p); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
0
|
|
|
|
|
|
push( @res, $p ); |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# Return param values. |
422
|
0
|
|
|
|
|
|
return @res; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
0
|
|
|
0
|
0
|
|
method get_cdata () { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
426
|
0
|
|
|
|
|
|
my $res = ""; |
427
|
0
|
|
|
|
|
|
for ( $self->get_children ) { |
428
|
0
|
0
|
|
|
|
|
$res .= "\n" . $_->content if ref($_) eq "SVGPDF::TextElement"; |
429
|
|
|
|
|
|
|
} |
430
|
0
|
|
|
|
|
|
$res; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
0
|
|
|
0
|
0
|
|
method nfi ( $tag ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
434
|
0
|
|
|
|
|
|
state $aw = {}; |
435
|
|
|
|
|
|
|
warn("SVG: $tag - not fully implemented, expect strange results.\n") |
436
|
0
|
0
|
0
|
|
|
|
unless !$self->root->verbose || $aw->{$tag}++; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
0
|
|
|
0
|
0
|
|
method set_font ( $xo, $style ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
440
|
0
|
|
|
|
|
|
my $msg =""; |
441
|
0
|
|
|
|
|
|
my $ret; |
442
|
|
|
|
|
|
|
{ |
443
|
0
|
|
|
0
|
|
|
local $SIG{__WARN__} = sub { $msg .= "@_" }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
444
|
0
|
|
|
|
|
|
$ret = $self->root->fontmanager->set_font( $xo, $style ); |
445
|
|
|
|
|
|
|
} |
446
|
0
|
0
|
0
|
|
|
|
if ( $msg && $self->root->verbose ) { |
447
|
0
|
|
|
|
|
|
warn($msg); |
448
|
|
|
|
|
|
|
} |
449
|
0
|
|
|
|
|
|
$ret; |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
################ Bounding Box ################ |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
# method bb ( $x, $y, $t = 0 ) { |
455
|
|
|
|
|
|
|
# my $bb = $self->root->xoforms->[-1]->{bb}; |
456
|
|
|
|
|
|
|
# |
457
|
|
|
|
|
|
|
# $t = $self->u($t) unless $t =~ /^[-+]?\d*(?:\.\d*)$/; |
458
|
|
|
|
|
|
|
# $t /= 2; |
459
|
|
|
|
|
|
|
# $bb->[0] = $x-$t if $bb->[0] > $x-$t; |
460
|
|
|
|
|
|
|
# $bb->[1] = $y-$t if $bb->[1] > $y-$t; |
461
|
|
|
|
|
|
|
# $bb->[2] = $x+$t if $bb->[2] < $x+$t; |
462
|
|
|
|
|
|
|
# $bb->[3] = $y+$t if $bb->[3] < $y+$t; |
463
|
|
|
|
|
|
|
# |
464
|
|
|
|
|
|
|
# return $bb; |
465
|
|
|
|
|
|
|
# } |
466
|
|
|
|
|
|
|
# |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
################ TextElement ################ |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
class SVGPDF::TextElement; |
471
|
|
|
|
|
|
|
|
472
|
0
|
0
|
|
0
|
|
|
field $content :param :accessor; |
|
0
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# Actually, we should take style->{white-space} into account... |
475
|
|
|
|
|
|
|
BUILD { |
476
|
|
|
|
|
|
|
# Reduce whitespace. |
477
|
|
|
|
|
|
|
$content =~ s/\s+/ /g; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
0
|
|
|
0
|
|
|
method process () { |
|
0
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# Nothing to process. |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
1; |