NAME
Image::SVG::Path - read the "d" attribute of an SVG path
SYNOPSIS
use Image::SVG::Path 'extract_path_info';
use Data::Dumper;
my $path_d_attribute = "M9.6,20.25c0.61,0.37,3.91,0.45,4.52,0.34";
my @path_info = extract_path_info ($path_d_attribute);
print Dumper (\@path_info);
produces output
$VAR1 = [
{
'point' => [
'9.6',
'20.25'
],
'name' => 'moveto',
'svg_key' => 'M',
'position' => 'absolute',
'type' => 'moveto'
},
{
'control2' => [
'3.91',
'0.45'
],
'svg_key' => 'c',
'name' => 'curveto',
'position' => 'relative',
'control1' => [
'0.61',
'0.37'
],
'end' => [
'4.52',
'0.34'
],
'type' => 'cubic-bezier'
}
];
(This example is included as synopsis.pl in the distribution.)
VERSION
This documents version 0.36 of Image-SVG-Path corresponding to git commit 11f37e923e736620d6a5a52623caf72ee4a5439b released on Sat May 1 18:51:04 2021 +0900.
DESCRIPTION
This module extracts information contained in the "d" attribute of an SVG <path> element and turns it into a simpler series of steps.
For example, an SVG <path> element might take the form
<path d="M9.6,20.25c0.61,0.37,3.91,0.45,4.52,0.34c2.86-0.5,14.5-2.09,21.37-2.64c0.94-0.07,2.67-0.26,3.45,0.04"/>
Using an XML parser, such as XML::Parser,
use FindBin '$Bin';
use XML::Parser;
use Image::SVG::Path 'extract_path_info';
my $file = "$Bin/Home_for_the_aged.svg";
my $p = XML::Parser->new (Handlers => {Start => \& start});
$p->parsefile ($file) or die "Error $file: ";
sub start
{
my ($expat, $element, %attr) = @_;
if ($element eq 'path') {
my $d = $attr{d};
my @r = extract_path_info ($d);
for (@r) {
if ($_->{svg_key} =~ /^[mM]$/i) {
print "MOVE TO @{$_->{point}}.\n";
}
}
}
}
produces output
MOVE TO 15 119.
MOVE TO 52 88.
MOVE TO 198 88.
MOVE TO 20 214.
MOVE TO 148 214.
(This example is included as xml-parser.pl in the distribution.)
SVG means "scalable vector graphics" and it is a standard of the W3 consortium. See "SVG standards" for details. Although SVG is a type of XML, the text in the d
attribute of SVG paths is not XML but a condensed form using single letters and numbers. This module is a parser for that condensed format.
FUNCTIONS
extract_path_info
my @path_info = extract_path_info ($path_d_attribute);
Turn the SVG path string into a series of simpler things.
For example,
use Image::SVG::Path 'extract_path_info';
my @path_info = extract_path_info ('M6.93,103.36c3.61-2.46,6.65-6.21,6.65-13.29c0-1.68-1.36-3.03-3.03-3.03s-3.03,1.36-3.03,3.03s1.36,3.03,3.03,3.03C15.17,93.1,10.4,100.18,6.93,103.36z');
my $count = 0;
for my $element (@path_info) {
$count++;
print "Element $count:\n";
for my $k (sort keys %$element) {
my $val = $element->{$k};
if (ref $val eq 'ARRAY') {
$val = "[$val->[0], $val->[1]]";
}
print " $k -> $val\n";
}
}
produces output
Element 1:
name -> moveto
point -> [6.93, 103.36]
position -> absolute
svg_key -> M
type -> moveto
Element 2:
control1 -> [3.61, -2.46]
control2 -> [6.65, -6.21]
end -> [6.65, -13.29]
name -> curveto
position -> relative
svg_key -> c
type -> cubic-bezier
Element 3:
control1 -> [0, -1.68]
control2 -> [-1.36, -3.03]
end -> [-3.03, -3.03]
name -> curveto
position -> relative
svg_key -> c
type -> cubic-bezier
Element 4:
control2 -> [-3.03, 1.36]
end -> [-3.03, 3.03]
name -> shorthand/smooth curveto
position -> relative
svg_key -> s
type -> shortcut-cubic-bezier
Element 5:
control2 -> [1.36, 3.03]
end -> [3.03, 3.03]
name -> shorthand/smooth curveto
position -> relative
svg_key -> s
type -> shortcut-cubic-bezier
Element 6:
control1 -> [15.17, 93.1]
control2 -> [10.4, 100.18]
end -> [6.93, 103.36]
name -> curveto
position -> absolute
svg_key -> C
type -> cubic-bezier
Element 7:
name -> closepath
position -> relative
svg_key -> z
type -> closepath
(This example is included as extract-path-info.pl in the distribution.)
The return value is a list of hash references. Each hash reference has at least four keys, type
, position
, name
, and svg_key
. The svg_key
field is the original key from the path. The type
and name
fields say what the element is, for example a cubic bezier curve or a line. The position
value is either "relative" or "absolute" depending on whether the coordinates of this step of the path are relative to the current point (relative) or to the drawing's origin (absolute). position
is relative if svg_key
is lower case and absolute if it is upper case, unless the user chooses the "absolute" option.
extract_path_info
replaces all implicit commands with the explicit version. For example, an input moveto followed by an implicit lineto of the form M 1 2 3 4
is converted into a moveto M 1 2
followed by an explicit lineto L 3 4
. An input sequence of elliptic arcs, one explicit and one implicit, is turned into two elliptic arcs in the output. This means that "round trips" are not possible; it is not possible to use the output of this function to reconstruct the input path string exactly, although the actual path itself can be reproduced exactly.
A second argument to extract_path_info
contains options for the extraction in the form of a hash reference. For example,
my @path_info = extract_path_info ($path, {absolute => 1});
The following options exist:
- absolute
-
If the hash element
absolute
is set to a true value, relative positions are changed to absolute. For example a "c" curve is changed to the equivalent "C" curve. In this case, theposition
value of each element's hash isabsolute
, andsvg_key
is converted to upper case. - no_smooth
-
If the hash element
no_smooth
is set to a true value then smooth cubic bezier curves, "S" curves, are changed into the equivalent "C" curves. This only works in combination with the "absolute" option, otherwise it does nothing.In versions of this module up to
0.30
,no_smooth
was erroneously namedno_shortcuts
. The nameno_shortcuts
is still accepted by this function for backward compatibility. - verbose
-
If this is set to a true value,
extract_path_info
prints out informative messages about what it is doing as it parses the path.
reverse_path
my $reverse_path = reverse_path ($path);
Make an SVG path which is the exact reverse of the input.
This only works for cubic bezier curves with absolute position, and not for smooth curves (C elements only). It doesn't fill in all the information correctly.
use Image::SVG::Path 'reverse_path';
my $path = "M26.75,73c-2.61,6.25-5.49,12.25-8.36,17.15c-0.74,1.26-1.99,1.54-3.23,1.03";
my $reverse = reverse_path ($path);
print "$reverse\n";
produces output
M15.160000,91.180000 C16.400000,91.690000 17.650000,91.410000 18.390000,90.150000 C21.260000,85.250000 24.140000,79.250000 26.750000,73.000000
(This example is included as test-reverse.pl in the distribution.)
create_path_string
my $path = create_path_string (\@info);
Given a set of information as created by "extract_path_info", turn them into an SVG string representing a path.
This only works for elements with absolute
position and not smooth curves. It does not handle quadratic bezier curves.
SVG path elements
This section documents the output elements. If the path is extracted using
my @path = extract_path_info ($d);
then the elements of @path
are each hash references which contain one of the following kinds of elements, depending on what is next on the path.
Move to elements, M
If type
is moveto
, the hash reference contains one more field, point
, which is the point to move to. This is an array reference containing the x and y coordinates as elements indexed 0 and 1 respectively.
- type
-
This is
moveto
. - svg_key
-
This is M or m.
- point
-
This is the point to move to.
Line elements, L
If type
is lineto
, the hash reference contains one more field, point
, which is the point to move to. This is an array reference containing the x and y coordinates as elements indexed 0 and 1 respectively.
- type
-
This is
lineto
. - svg_key
-
This is L or l.
- point
-
This is the end point of the line.
- end
-
This field occurs in some lines for backwards compatibility with pre-0.16 versions of the module.
Cubic bezier curve elements, C
If the type is cubic-bezier
, the hash reference contains three more fields, control1
, control2
and end
. The value of each is an array reference containing the x and y coordinates of the first and second control points and the end point of the Bezier curve respectively. (The start point of the curve is the end point of the previous part of the path.)
- type
-
This is
cubic-bezier
. - svg_key
-
This is C or c.
- control1
-
Control point 1 of the curve.
- control2
-
Control point 2 of the curve.
- end
-
The end point of the curve.
Smooth cubic bezier curve elements, S
If the type is smooth-cubic-bezier
, the hash contains two more fields, control2
and end
. control2
is the second control point, and end
is the end point. The first control point is got by reflecting the second control point of the previous curve around the end point of the previous curve (the start point of the current curve).
Use the "no_smooth" option to automatically convert these into cubic bezier curve elements.
- type
-
This is
smooth-cubic-bezier
. - svg_key
-
This is S or s.
- control2
-
This is the second control point of the curve (the first one is implicit).
- end
-
This is the end point of the curve.
Quadratic bezier curve elements, Q
If the type is quadratic-bezier
, the hash contains two more fields, control
and end
. control
is the control point, and end
is the end point.
- type
-
This is
quadratic-bezier
. - svg_key
-
This is Q or q.
- control
-
This is the control point.
- end
-
This is the end point.
Smooth quadratic Bezier curves, T
See the SVG documentation (section 8.3.7) for how to calculate the control point.
- type
-
This is
smooth-quadratic-bezier
. - svg_key
-
This is T or t.
- end
-
This is the end point.
Arc elements, A
- type
-
This is
arc
. - svg_key
-
This is
A
ora
. - rx, ry
-
X and Y radiuses
- x_axis_rotation
-
See the SVG documentation (section 8.3.8) for details.
- large_arc_flag
-
See the SVG documentation (section 8.3.8) for details.
- sweep_flag
-
See the SVG documentation (section 8.3.8) for details.
- x, y
-
These are the end points of the arc.
Arcs are omitted from "SVG Tiny".
Horizontal line elements, H
Horizontal line elements contain one additional key, x
, the x-coordinate of the end of the line. The y-coordinate is the same as the y-coordinate of the end point of the previous element.
- type
-
This is
horizontal-line-to
. - svg_key
-
This is H or h.
- x
-
This is the x coordinate of the end point. The y coordinate is implicit.
Vertical line elements, V
Vertical line elements contain one additional key, y
, the y-coordinate of the end of the line. The x-coordinate is the same as the x-coordinate of the end point of the previous element.
- type
-
This is
vertical-line-to
. - svg_key
-
This is V or v.
- y
-
This is the y coordinate of the end point. The x coordinate is implicit.
Closepath elements, Z
- type
-
This is
closepath
. - svg_key
-
This is Z or z.
Each hash reference also contains the field position
, which has either the value absolute
or relative
depending on whether svg_key
is upper case or lower case, respectively. A field name
also exists.
BUGS
- reverse_path only works for cubic bezier curves
-
See "reverse_path".
This is bug 23 on the issue tracker.
- create_path_string does not work for quadratic bezier curves
-
See "create_path_string".
This is bug 22 on the issue tracker.
EXPORTS
None of the functions is exported by default.
use Image::SVG::Path ':all';
exports all of the module's functions, "extract_path_info", "reverse_path" and "create_path_string". For backward compatibility, this does not export the regular expressions.
Regular expressions
The following SVG-parsing regular expressions which (are supposed to) exactly correspond to the SVG standard can also be exported.
- $svg_path
-
Match a complete path consisting of multiple move-to and drawing commands. Some relatively simple inputs blow up the
$svg_path
regex, causing errors of the form Complex regular subexpression recursion limit (32766) exceeded. See the file t/export-regex.t for an example (commented out).This was withdrawn from use in parsing the paths in version 0.29 of the module.
- $drawto_command
-
Match one drawing command. Note this does not match a move-to command. The command is captured as $1.
- $drawto_commands
-
Match a sequence of one or more drawing commands.
- $moveto
-
Match a move-to command, including any subsequent implicit line-tos. See "Move to elements, M".
- $closepath
- $curveto
- $elliptical_arc
- $horizontal_lineto
- $lineto
- $quadratic_bezier_curveto
- $smooth_curveto
- $smooth_quadratic_bezier_curveto
- $vertical_lineto
Of necessity, underscores (_
) have been substituted for the hyphens (-
) in the SVG standard, but otherwise these names correspond exactly to the names in the standard. In each of the drawing commands, the command itself is captured as $1 and the arguments are captured as $2.
To export all of these, use
use Image::SVG::Path ':regex';
The subexpressions used in the definitions of the above in the SVG standard (things like vertical-lineto-argument-sequence
) are not exported, and some of the more ridiculous ones (long-winded duplicates of other expressions) are not even implemented in this module's source code.
SEE ALSO
Other CPAN modules
- SVG::Rasterize
-
This contains a complete parser for SVG paths in
SVG::Rasterize::Engine::PangoCairo
. It is embedded into the module and is used to draw with Cairo. - MarpaX::Languages::SVG::Parser
-
This is a parser for SVG by Ron Savage which uses Jeffrey Kegler's Marpa::R2 system, hence the name "MarpaX" (Marpa extension).
- SVG::Estimate
-
This is an application of Image::SVG::Path which uses it to estimate the lengths of the paths of SVG images.
- Image::CairoSVG
-
This is a least-effort module by the same author as Image::SVG::Path which renders some kinds of SVGs using Cairo.
- Image::LibRSVG
-
Render SVG via a Gnome library.
- Image::SVG::Transform
-
This module reads the "transform" attribute of an SVG element.
- Image::Info::SVG
-
Part of Image::Info, you can get dimensions and other information about SVG images without the bother of parsing the file.
SVG standards
- SVG specification
-
The full specification contains all the details. The SVG path specification contains the specifications for paths. The grammar of paths is described in The grammar for path data within that section of the document.
- SVG Tiny
-
SVG Tiny is a subset of SVG. It claims to have a Perl Language Binding, but I cannot locate the source code.
- SVG basic
-
There is also another standard, SVG basic, I'm not too sure whether either this or SVG Tiny are in use.
Other things
- CairoSVG
-
CairoSVG is a Python SVG renderer using Cairo.
- Yann Armelin's SVG Path Editor
- SVG Optimizer
- SVG Cleaner
HISTORY
This module was originally begun as a way to hack the data out of the SVG-like data of a project called KanjiVG (kanji vector graphics) for the benefit of this kanji recognition system which relies on the KanjiVG data. At the time I (Ben Bullock) created this, I only had a vague idea of what SVG was. The KanjiVG data consists only of a subset of SVG, namely the initial move-tos and cubic bezier curves, which is why some parts of this module only deal with that kind of SVG path. At the time I started using it, the KanjiVG data actually contained a number of kanji strokes going in the wrong direction, and "reverse_path" was devised as a way to fix these.
ACKNOWLEDGEMENTS
Alessandro Ranellucci (http://makerblog.it/) pointed out that implicit commands and floating point numbers were not handled correctly.
Colin Kuskie (http://www.thegamecrafter.com/) fixed error messages for version 0.20, number paths for version 0.21, implicit line-tos for version 0.22, implicit arc commands for version 0.23, multiple closepaths for version 0.24, and handling plus signs in numbers in 0.25.
AUTHOR
Ben Bullock, <bkb@cpan.org>
COPYRIGHT & LICENCE
This package and associated files are copyright (C) 2011-2021 Ben Bullock.
You can use, copy, modify and redistribute this package and associated files under the Perl Artistic Licence or the GNU General Public Licence.