File Coverage

lib/ChordPro/lib/SVGPDF/Path.pm
Criterion Covered Total %
statement 14 137 10.2
branch 0 42 0.0
condition 0 9 0.0
subroutine 5 9 55.5
pod 0 4 0.0
total 19 201 9.4


line stmt bran cond sub pod time code
1             #! perl
2              
3 1     1   13 use v5.26;
  1         14  
4 1     1   7 use Object::Pad;
  1         4  
  1         6  
5 1     1   95 use utf8;
  1         5  
  1         5  
6 1     1   39 use Carp;
  1         4  
  1         110  
7              
8             class SVGPDF::Path :isa(SVGPDF::Element);
9              
10 1     1   622 use SVGPDF::Contrib::PathExtract qw( extract_path_info );
  1         4  
  1         2523  
11              
12 0     0 0   method process () {
  0            
  0            
13 0           my $atts = $self->atts;
14 0           my $xo = $self->xo;
15 0 0         return if $atts->{omit}; # for testing/debugging.
16              
17 0 0         if ( defined $atts->{id} ) {
18 0           $self->root->defs->{ "#" . $atts->{id} } = $self;
19             # MathJax uses curves to draw glyphs. These glyphs are filles
20             # *and* stroked with a very small stroke-width. According to
21             # the PDF specs, this should yield a 1-pixel (device pixel)
22             # stroke, which results in fat glyphs on screen.
23             # To avoid this, disable stroke when drawing MathJax glyphs.
24 0 0         if ( $atts->{id} =~ /^MJX-/ ) {
25 0           $atts->{stroke} = 'none';
26             }
27             }
28              
29 0           my ( $d, $tf ) = $self->get_params( $atts, "d:!", "transform:s" );
30              
31 0           ( my $t = $d ) =~ s/\s+/ /g;
32 0 0         $t = substr($t,0,20) . "..." if length($t) > 20;
33 0 0         $self->_dbg( $self->name, " d=\"$t\"", $tf ? " tf=\"$tf\"" : "" );
34 0 0         return unless $d;
35              
36 0           $self->_dbg( "+ xo save" );
37 0           $xo->save;
38 0           $self->set_transform($tf);
39 0           $self->set_graphics;
40              
41             # Get path info, turning relative coordinates into absolute
42             # and eliminate S and T curves.
43 0           my @d = extract_path_info( $d, { absolute => 1, no_smooth => 1 } );
44              
45 0           my $open; # path is open
46              
47 0           my $paint = $self->_paintsub;
48              
49             # Initial x,y for path, if open. See 'z'.
50 0           my ( $ix, $iy );
51              
52             # Current point. Starting point of this path.
53             # Since we're always use absolute coordinates, this is the
54             # starting point for subpaths as well.
55 0           my ( $cx, $cy ) = ( 0, 0 );
56              
57             # For debugging: collect control points.
58 0           my @cp;
59              
60 0           my $id = -1; # for diagnostics
61 0           while ( @d ) {
62 0           my $d = shift(@d);
63 0           my $op = $d->{svg_key};
64 0           $id++;
65              
66             # Reset starting point for the subpath.
67 0           my ( $x, $y ) = ( 0, 0 );
68              
69             # Remember initial point of path.
70 0 0 0       $ix = $cx, $iy = $cy unless $open++ || $op eq "Z";
71              
72 0 0 0       warn(sprintf("%s[%d] x=%.2f,y=%.2f cx=%.2f,cy=%.2f ix=%.2f,iy=%.2f\n",
73             $op, $id, $x, $y, $cx, $cy, $ix, $iy))
74             if 0 & ($x || $y || $ix || $iy);
75              
76             # MoveTo
77 0 0         if ( $op eq "M" ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
78 0           $x += $d->{point}->[0];
79 0           $y += $d->{point}->[1];
80 0           $self->_dbg( "xo move(%.2f,%.2f)", $x, $y );
81 0           $xo->move( $x, $y );
82             }
83              
84             # Horizontal LineTo.
85             elsif ( $op eq "H" ) {
86 0           $x += $d->{x};
87 0           $y = $cy;
88 0           $self->_dbg( "xo hline(%.2f)", $x );
89 0           $xo->hline($x);
90             }
91              
92             # Vertical LineTo.
93             elsif ( $op eq "V" ) {
94 0           $x = $cx;
95 0           $y += $d->{y};
96 0           $self->_dbg( "xo vline(%.2f)", $y );
97 0           $xo->vline($y);
98             }
99              
100             # Generic LineTo.
101             elsif ( $op eq "L" ) {
102 0           $x += $d->{point}->[0];
103 0           $y += $d->{point}->[1];
104 0           $self->_dbg( "xo line(%.2f,%.2f)", $x, $y );
105 0           $xo->line( $x, $y );
106             }
107              
108             # Cubic Bézier curves.
109             elsif ( $op eq "C" ) {
110             my @c = ( # control point 1
111             $x + $d->{control1}->[0],
112             $y + $d->{control1}->[1],
113             # control point 2
114             $x + $d->{control2}->[0],
115             $y + $d->{control2}->[1],
116             # end point
117             $x + $d->{end}->[0],
118 0           $y + $d->{end}->[1],
119             );
120 0           $self->_dbg( "xo curve(%.2f,%.2f %.2f,%.2f %.2f,%.2f)", @c );
121 0           $xo->curve(@c);
122 0           push( @cp, [ $cx, $cy, $c[0], $c[1] ] );
123 0           push( @cp, [ $c[4], $c[5], $c[2], $c[3] ] );
124 0           $x = $c[4]; $y = $c[5]; # end point
  0            
125             }
126              
127             # Quadratic Bézier curves.
128             elsif ( $op eq "Q" ) {
129             my @c = ( # control point 1
130             $x + $d->{control}->[0],
131             $y + $d->{control}->[1],
132             # end point
133             $x + $d->{end}->[0],
134 0           $y + $d->{end}->[1],
135             );
136 0           $self->_dbg( "xo spline(%.2f,%.2f %.2f,%.2f)", @c );
137 0           $xo->spline(@c);
138 0           push( @cp, [ $cx, $cy, $c[0], $c[1] ] );
139 0           push( @cp, [ $c[2], $c[3], $c[0], $c[1] ] );
140 0           $x = $c[2]; $y = $c[3]; # end point
  0            
141             }
142              
143             # Arcs.
144             elsif ( $op eq "A" ) {
145 0           my $rx = $d->{rx}; # radius 1
146 0           my $ry = $d->{ry}; # radius 2
147 0           my $rot = $d->{x_axis_rotation}; # rotation
148 0           my $large = $d->{large_arc_flag}; # select larger arc
149 0           my $sweep = $d->{sweep_flag}; # clockwise
150 0           my $ex = $x + $d->{x}; # end point
151 0           my $ey = $y + $d->{y};
152 0           $self->_dbg( "xo arc(%.2f,%.2f %.2f %d,%d %.2f,%.2f)",
153             $rx, $ry, $rot, $large, $sweep, $ex, $ey );
154              
155             # for circular arcs.
156 0 0         if ( $rx == $ry ) {
157 0           $self->_dbg( "circular_arc(%.2f,%.2f %.2f,%.2f %.2f ".
158             "move=%d large=%d dir=%d rot=%.2f)",
159             $cx, $cy, $ex, $ey, $rx,
160             0, $large, $sweep, $rot );
161 0           $self->circular_arc( $cx, $cy, $ex, $ey, $rx,
162             move => 0,
163             large => $large,
164             rotate => $rot,
165             dir => $sweep );
166             }
167             else {
168 0           $self->_dbg( "elliptic_arc(%.2f,%.2f %.2f,%.2f %.2f,%.2f ".
169             "move=%d large=%d dir=%d rot=%.2f)",
170             $cx, $cy, $ex, $ey, $rx, $ry,
171             0, $large, $sweep, $rot );
172 0           $self->elliptic_arc( $cx, $cy, $ex, $ey,
173             $rx, $ry,
174             move => 0,
175             large => $large,
176             rotate => $rot,
177             dir => $sweep );
178             }
179 0           ( $x, $y ) = ( $ex, $ey ); # end point
180             }
181              
182             # Close path and paint.
183             elsif ( $op eq "Z" ) {
184 0           $self->_dbg( "xo z" );
185 0 0         if ( $open ) {
186 0           $xo->close;
187 0           $open = 0;
188             # currentpoint becomes the initial point.
189 0           $x = $ix;
190 0           $y = $iy;
191             }
192 0 0 0       if ( @d && $d[0]->{svg_key} eq 'M' ) {
193             # Close is followed by a move -> do not paint yet.
194             }
195             else {
196 0           $paint->();
197             }
198             }
199              
200             # Unidenfied subpath element.
201             else {
202 0           croak("Unidenfied subpath element[$id] $op");
203             }
204              
205 0 0         ( $cx, $cy ) = ( $x, $y ) unless $op eq "Z";
206             }
207              
208 0 0         $paint->() if $open;
209 0           $self->_dbg( "- xo restore" );
210 0           $xo->restore;
211              
212             # Show collected control points.
213 0           if ( 0 && $self->root->debug && @cp ) {
214             $xo->save;
215             $xo->stroke_color('lime');
216             $xo->line_width(0.5);
217             for ( @cp ) {
218             $self->_dbg( "xo line(%.2f %.2f %.2f %.2f)", @$_ );
219             $xo->move( $_->[0], $_->[1] );
220             $xo->line( $_->[2], $_->[3] );
221             }
222             $xo->stroke;
223             $xo->restore;
224             }
225              
226 0           $self->css_pop;
227             }
228              
229 0     0 0   method curve ( @points ) {
  0            
  0            
  0            
230 0           $self->_dbg( "+ xo curve( %.2f,%.2f %.2f,%.2f %.2f,%.2f )", @points );
231 0           $self->xo->curve(@points);
232 0           $self->_dbg( "-" );
233             }
234              
235 0     0 0   method elliptic_arc( $x1,$y1, $x2,$y2, $rx,$ry, %opts) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
236 0           require SVGPDF::Contrib::Bogen;
237              
238 0           SVGPDF::Contrib::Bogen::bogen_ellip
239             ( $self, $x1,$y1, $x2,$y2, $rx,$ry, %opts );
240             }
241              
242 0     0 0   method circular_arc( $x1,$y1, $x2,$y2, $r, %opts) {
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
243 0           require SVGPDF::Contrib::Bogen;
244              
245             SVGPDF::Contrib::Bogen::bogen
246             ( $self, $x1,$y1, $x2,$y2, $r,
247 0           $opts{move}, $opts{large}, $opts{dir} );
248             }
249              
250             1;