NAME

Image::SVG::Path - read the "d" attribute of an SVG path

SYNOPSIS

use Image::SVG::Path 'extract_path_info';
my $path_d_attribute = "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";
my @path_info = extract_path_info ($path_d_attribute);

VERSION

This documents version 0.24 of Image::SVG::Path corresponding to git commit 9cc7411d91bd2873f926bdb2747ea7e52fd8006f released on Mon Sep 12 11:48:06 2016 +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.

An option "no_shortcuts" automatically replaces shortcut cubic bezier curves with the normal kind, by calculating the first control point.

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 may be chosen by adding them to the hash reference:

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, the position value of each element's hash is absolute, and svg_key is converted to upper case.

no_shortcuts

If the hash element no_shortcuts is set to a true value then shortcut 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. It also does not work with shortcut quadratic bezier curves.

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 no shortcuts (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 cubic bezier curves and the initial moveto element for absolute position and no shortcuts (C elements only).

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.

Shortcut cubic bezier curve elements, S

If the type is shortcut-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_shortcuts" option to automatically convert these into cubic bezier curve elements.

type

This is shortcut-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.

Shortcut quadratic Bezier curves, T

See the SVG documentation (section 8.3.7) for how to calculate the control point.

type

This is shortcut-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 or a.

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

Does not use the grammar

There is a grammar for the paths in the W3 specification. See https://www.w3.org/TR/SVG/paths.html#PathDataBNF. However, this module does not use that grammar. Instead it hacks up the path using regexes.

"no_shortcuts" does not work with quadratic bezier curves

Currently the "no_shortcuts" option does not work with quadratic bezier curves.

EXPORTS

None of the functions is exported by default.

use Image::SVG::Path ':all';

exports all of "extract_path_info", "reverse_path" and "create_path_string".

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 parser uses the SVG grammar mentioned in "BUGS".

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.

SVG standards

SVG specification

The full specification contains all the details. The SVG path specification contains the specifications for paths.

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.

ACKNOWLEDGEMENTS

Colin Kuskie (http://www.perldreamer.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, and multiple closepaths for version 0.24.

AUTHOR

Ben Bullock, <bkb@cpan.org>

Request

If you'd like to see this module continued, let me know that you're using it. For example, send an email, write a bug report, star the project's github repository, add a patch, add a ++ on Metacpan.org, or write a rating at CPAN ratings. It really does make a difference. Thanks.

COPYRIGHT & LICENCE

This package and associated files are copyright (C) 2011-2016 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.