NAME
SVG::Rasterize
- rasterize SVG
content to pixel graphics
VERSION
Version 0.003008
my $blockID = 0;
my @block_atoms = grep { $_->{blockID} == $blockID } @$text_atoms;
while(@block_atoms) {
$blockID++;
@block_atoms = grep { $_->{blockID} == $blockID } @$text_atoms;
}
SYNOPSIS
use SVG;
use SVG::Rasterize;
my $svg = SVG->new(width => 300, height => 200);
$svg->line(x1 => 10, y1 => 20, x2 => 220, y2 => 150,
style => {stroke => 'black', stroke-width => '2pt' });
# add more svg content
# .
# .
# .
my $rasterize = SVG::Rasterize->new();
$rasterize->rasterize(svg => $svg);
$rasterize->write(type => 'png', file_name => 'out.png');
DESCRIPTION
SVG::Rasterize
can be used to rasterize SVG objects to pixel graphics (currently png only) building on the Cairo library (by default, other underlying rasterization engines could be added). The direct rasterization of SVG
files might be implemented in the future, right now you should have a look at SVG::Parser which can generate an SVG object from an svg
file. See also SVG Input in the ADVANCED TOPICS section.
Motivation
In the past, I have used several programs to rasterize SVG
graphics including Inkscape, Konqueror, Adobe Illustrator, and rsvg. While Inkscape was my favourite none of them made me entirely happy. There were always parts of the standard that I would have liked to use, but were unsupported.
So finally, I set out to write my own rasterization engine. The ultimate goal is complete compliance with the requirements for a Conforming Static SVG Viewer
as described in the SVG
specification: http://www.w3.org/TR/SVG11/conform.html#ConformingSVGViewers. Obviously, this is a long way to go. I do not know if any support for the dynamic features of SVG
will ever be added. Anyway, the priority for SVG::Rasterize
is accuracy, not speed.
Status
The following elements are drawn at the moment:
path
all basic shapes:
rect
,circle
,ellipse
,line
,polyline
,polygon
.text/tspan in a limited (and not well tested) way:
stroke/fill colors can be set
position can be set, also for individual characters (but no rotation of individual characters, yet)
alignment via text-anchor
bidirectional text only as far as pango does that automatically, explicit settings for writing direction are ignored
font-size can be set, but no font-family, font-style etc.
The inheritance of styling properties is implemented. The following attributes are at least partly interpreted:
transform
viewBox
preserveAspectRatio
style
fill
and all associated propertiesstroke
and all associated properties
I hope that the interface described here will be largely stable. However, this is not guaranteed. Some features are documented as likely to change, but everything is subject to change at this early stage.
Here is my current view of the next part of the roadmap:
- Version 0.004
-
completion of text basics
- Version 0.005
-
support for
SVG
filesrelative units
- Version 0.006
-
clipping paths
css sections and files?
- Version 0.007
-
symbol/use
tref
and such
- Version 0.008
-
gradients and patterns
masks
INTERFACE
Constructors
new
$rasterize = SVG::Rasterize->new(%args)
Creates a new SVG::Rasterize
object and calls init(%args). If you subclass SVG::Rasterize
overload init, not new
.
init goes through the arguments given to new
. If a method of the same name exists it is called with the respective value as argument. This can be used for attribute initialization. Some of the values can be also given to rasterize to temporarily override the attribute values. The values of these overridable attributes are only validated once they are used by rasterize.
The most commonly used arguments are:
svg (optional): A
DOM
object to render.width (optional): width (in pixels) of the generated output image
height (optional): height (in pixels) of the generated output image.
Public Attributes
svg
Holds the DOM
object to render. It does not have to be an SVG object, but it has to offer certain DOM
methods (see SVG Input for details).
width
The width of the generated output in pixels.
height
The height of the generated output in pixels.
current_color
The color which is used if an SVG
element's fill
or stroke
property is set to currentColor
and the color
property has not been set directly. Setting current_color
has the same effect as if the root SVG
element's color
property was set to the same value. See http://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint for the background of this.
medium_font_size
SVG
supports keywords from xx-small
to xx-large
for the font-size
attribute. A numerical value for medium
as well as a scaling factor between neighboring values is supposed to be set by the user agent. SVG::Rasterize
uses a default value of 12pt
. This default value can be adjusted by setting this attribute. A new value has to be an absolute length larger than 0
.
font_size_scale
Read about medium_font_size
above first. font_size_scale
holds the factor between neighboring font-size
values, e.g. between large
and x-large
. The default value is 1.2
.
There are other attributes that influence unit conversions, white space handling, and the choice of the underlying rasterization engine. See ADVANCED TOPICS.
Class Attributes
%IGNORED_NODES
Defaults to
%IGNORED_NODES = (comment => 1,
title => 1,
desc => 1,
metadata => 1);
A SVG
node with a name that is a key in this hash with a true value is ignored including all its children. If you, for example set
$SVG::IGNORED_NODES{text} = 1;
then all text nodes will be ignored.
Do not unset the defaults above or you are likely to get into trouble.
Methods for Users
rasterize
$rasterize->rasterize(%args)
Traverses through the given SVG
content and renders the output. Does not return anything.
Examples:
$rasterize->rasterize(svg => $svg);
$rasterize->rasterize(svg => $svg, width => 640, height => 480);
$rasterize->rasterize(svg => $svg, engine_class => 'My::Class');
Supported parameters:
svg (optional):
DOM
object to rasterize. If not specified the value of the svg attribute is used. One of them has to be set. It does not have to be a SVG object, but it has to provide a certain set ofDOM
methods (see SVG Input.The element can be any valid
SVG
element, e.g.<svg>
,<g>
, or even just a basic shape element or so.width (optional): width of the target image in pixels, temporarily overrides the width attribute.
height (optional): height of the target image in pixels, temporarily overrides the height attribute.
current_color (optional): default color for
stroke
andfill
properties specified ascurrentColor
, temporarily overrides the current_color attribute.engine_class (optional): alternative engine class to SVG::Rasterize::Engine::PangoCairo, temporarily overrides the
engine_class
attribute. See SVG::Rasterize::Engine for details on the interface. The value has to match the regular expression p_PACKAGE_NAME.engine_args (optional): arguments for the constructor of the rasterization engine, temporarily overriding the
engine_args
attribute (NB: in the future, this behaviour might be changed such that the two hashes are merged and only the values given here override the values in the attributeengine_args
; however, at the moment, the whole hash is temporarily replaced if the parameter exists). The width and height of the output image can be set in several ways. The following values for the width are used with decreasing precedence (the same hierarchy applies to the height):normalize_attributes (optional): Influences White Space Handling, temporarily overrides the
normalize_attributes
attribute. Defaults to 1.
If width
(the same applies to height
) is 0 it is treated as not set. If you encounter any scenario where you would wish an explicit size of 0 to be treated in some other way let me know.
If width
and/or height
are not specified they have to have absolute values in the root SVG
element. If both the root SVG
element and the rasterize
method have width and/or height settings then the rasterize
parameters determine the size of the output image and the specified SVG
viewport is mapped to this image taking the viewBox
and preserveAspectRatio
attributes into account if they are present. See http://www.w3.org/TR/SVG11/coords.html#ViewportSpace for details.
The user can influence the rasterization process via hooks. See the Hooks section below.
write
$rasterize->write(%args)
Writes the rendered image to a file.
Example:
$rasterize->write(type => 'png', file_name => 'foo.png');
The supported parameters depend on the rasterization backend. The write
method hands all parameters over to the backend. See write in SVG::Rasterize::Engine::PangoCairo
for an example.
ADVANCED TOPICS
SVG
Input
In principle, SVG
input could be present as a kind of XML
tree object or as stringified XML
document. Therefore SVG::Rasterize
might eventually offer the following options:
- 1. The input data are provided in form of a SVG object tree generated by the user.
- 2. The input data are a SVG object tree generated from a file by SVG::Parser or a similar piece of software.
- 3. The input data are an object tree generated by a generic
XML
parser and offer aDOM
interface. - 4. The input data are stringified
XML
data in a file. - 5. The input data are stringified
XML
data read from a file handle. This case is different from the previous one because a file can be read multiple times in order to collect referencedSVG
fragments.
Currently, the first three options are at least partly implemented. I will not work on the other ones before a substantial subset of SVG
is supported. If the last two options will ever get implemented they will be designed to enable the rendering of files which are too large for the first options. Because it is harder to deal with cross-references in these cases, chances are that it will always be faster to use option 2. or 3. if this is possible.
Option 1. is the best tested one by far. However, option 2. should be very similar. To use option 3., the node objects have to provide at least the following DOM
methods:
getType
getNodeName
getAttributes
getData
getChildNodes
Unfortunately, option 3. cannot be treated completely in the same way as options 1. and 2. due to the peculiarity of SVG to treat CDATA
sections in a special way and not as child nodes of the element. SVG::Rasterize
tries to support both SVG object trees and generic DOM
trees, but this is neither well tested nor a main priority at the moment. Please report if you find SVG::Rasterize
not cooperating with your favourite DOM
parser.
Units
SVG
supports the absolute units px
, pt
, pc
, cm
, mm
, in
, and the relative units em
, ex
, and %
. Lengths can also be given as numbers without unit which is then interpreted as px
. See http://www.w3.org/TR/SVG11/coords.html#Units.
SVG::Rasterize
stores default values for unit conversion ratios as class variables. You can either change these values or the corresponding object variables. If you have only one SVG::Rasterize
object both approaches have the same effect.
The default values are listed below. Except px_per_in
, they are taken from the CSS
specification. See http://www.w3.org/TR/2008/REC-CSS2-20080411/syndata.html#length-units. The default for px_per_in
is arbitrarily set to 90.
Currently, the relative units listed above are not supported by SVG::Rasterize
.
Unit conversions:
px_per_in
Pixels per inch. Defaults to 90.
dpi
Alias for px_per_in. This is realized via a typeglob copy:
*dpi = \&px_per_in
in_per_cm
Inches per centimeter. Defaults to 1/2.54. This is the internationally defined value. I do not see why I should prohibit a change, but it would hardly make sense.
in_per_mm
Inches per millimeter. Defaults to 1/25.4. This is the internationally defined value. I do not see why I should prohibit a change, but it would hardly make sense.
in_per_pt
Inches per point. Defaults to 1/72. According to [1], this default was introduced by the
Postscript
language. There are other definitions. However, theCSS
specification is quite firm about it.in_per_pc
Inches per pica. Defaults to 1/6. According to the
CSS
specification, 12pc equal 1pt.map_abs_length
$number = $rasterize->map_abs_length($length) $number = $rasterize->map_abs_length($number, $unit)
This method takes a length and returns the corresponding value in
px
according to the conversion ratios above. Surrounding white space is not allowed.Examples:
$x = $rasterize->map_abs_length('5.08cm'); # returns 180 $x = $rasterize->map_abs_length(10); # returns 10 $x = $rasterize->map_abs_length(10, 'pt') # returns 12.5 $x = $rasterize->map_abs_length(' 1in '); # error $x = $rasterize->map_abs_length('50%') # error
The unit has to be absolute,
em
,ex
, and%
trigger an exception. See map_length inSVG::Rasterize::State
.There are two different interfaces. You can either pass one string or the number and unit separately. NB: In the second case, the input is not validated. This interface is meant for situations where the length string has already been parsed (namely in map_length in
SVG::Rasterize::State
) to avoid duplicate validation. The number is expected to be an A_NUMBER and the unit to be a UNIT. However, it is still checked if the unit is absolute.
The corresponding class attributes are listed below. Note that these values are not validated. Take care that you only set them to numbers.
PX_PER_IN
Defaults to 90.
DPI
Alias for
PX_PER_IN
. This is realized via a typeglob copy*DPI = \$PX_PER_IN
IN_PER_CM
Defaults to 1/2.54.
IN_PER_MM
Defaults to 1/25.4.
IN_PER_PT
Defaults to 1/72.
IN_PER_PC
Defaults to 1/6.
Hooks
The rasterize method traverses through the SVG
tree and creates an SVG::Rasterize::State object for each node (node means here element or text node if relevant, attributes are not treated as nodes). Hooks allow you to execute your own subroutines at given steps of this traversal. However, the whole hook business is experimental at the moment and likely to change. If you use any of the existing hooks or wish for other ones you may want to let me know because this will certainly influence the stability and development of this interface.
Right now, to set your own hooks you can set one of the following attributes to a code reference of your choice.
Currently, there are four hooks:
before_node_hook
Executed at encounter of a new node right before the new SVG::Rasterize::State object is created. It is called as an object method and receives the hash that would be passed to the SVG::Rasterize::State constructor. It contains the elements listed below. A custom
before_node_hook
is expected to return a hash of the same form which will then be handed over to the SVG::Rasterize::State constructor. The defaultbefore_node_hook
just returns the input hash.The argument hash always contains the following elements:
rasterize
: theSVG::Rasterize
objectnode
: the node objectnode_name
: theDOM
node namenode_attributes
: the attributes as HASH reference, already normalizedcdata
: string orundef
child_nodes
: ARRAY reference with node objects orundef
.
In addition, it may contain the following elements:
matrix
: ARRAY reference with six numbers (see multiply_matrices); this element is present when processing the root nodeparent
: the parent SVG::Rasterize::State object; this element is present when not processing the root node.
start_node_hook
Executed right after creation of the SVG::Rasterize::State object. The attributes have been parsed, properties and matrix have been set etc. The method receives the
SVG::Rasterize
object and the SVG::Rasterize::State object as parameters.end_node_hook
Executed right before a SVG::Rasterize::State object runs out of scope because the respective node is done with. The method receives the
SVG::Rasterize
object and the SVG::Rasterize::State object as parameters.in_error_hook
Executed right before
die
when the document is in error (see In error below. Receives theSVG::Rasterize
object and a newly created SVG::Rasterize::State object as parameters.
Examples:
$rasterize->start_node_hook(sub { ... })
Some hooks have non-trivial defaults. Therefore SVG::Rasterize
provides the following methods to restore the default behaviour:
restore_before_node_hook
restore_start_node_hook
restore_end_node_hook
restore_in_error_hook
restore_all_hooks
Calls all the other
restore...
methods. Takes an optional named parameterpreserve
. If this is set to atrue
value then only hooks are restored which are undefined. This is only documented for completeness, I do not see why you should need it. This option only exists such that the method can be used to initialize the hooks at construction time and preserve hooks that have been set by the user via an init parameter.
Rasterization Backend
SVG::Rasterize
does not render pixel graphics itself. By default, it uses the cairo library through its Perl bindings. However, the interface could also be implemented by other backends. In the future, it will be documented in SVG::Rasterize::Engine. Currently, the interface has to be considered unstable, though, and the documentation is sparse.
engine_class
This attribute defaults to SVG::Rasterize::Engine::PangoCairo
. It can be set as an object attribute or temporarily as a parameter to the rasterize method.
engine_args
This attribute can hold a HASH reference. The corresponding hash is given to the constructor of the rasterization engine when it is called by rasterize. engine_args
can be set as an object attribute or temporarily as a parameter to the rasterize method.
engine
$rasterize->engine
This attribute holds the interface object to the rasterization backend, by default a SVG::Rasterize::Engine::PangoCairo object. The object is created by the rasterize method.
The attribute is readonly, but, of course, you are able to manipulate the object directly via its methods. However, this is not part of the normal workflow and you do this on your own risk ;-).
White Space Handling
The XML
specification (http://www.w3.org/TR/2006/REC-xml11-20060816/#AVNormalize) states that an attribute value unless it is of the type CDATA shall be normalized such that leading and trailing white space is removed and internal white space is flattened to single space characters. XML
entities can complicate this normalization, see the specification for details.
If the SVG
tree to be rasterized by SVG::Rasterize
comes out of an parsed XML
document then the parser should have performed this normalization already. However, the tree might also be constructed directly using the SVG module. In order to prevent SVG::Rasterization
from choking on an attribute like stroke-width="2pt "
it performs by default an additional normalization run:
$value =~ s/^$WSP*//;
$value =~ s/$WSP*$//;
$value =~ s/$WSP+/ /g;
where
$WSP = qr/[\x{20}\x{9}\x{D}\x{A}]/; # space, tab, CR, LF
To prevent this normalization, you can set the normalize_attributes
attribute (as object attribute or as parameter to rasterize) to a false value.
SVG
Validation
SVG::Rasterize
is not an SVG
validator. It does check a lot of things including the validity of the element hierarchy, the required presence and absence of attributes and the values of all attributes it interpretes plus some that it does not interprete. However, it does not (and probably will never) claim to detect all errors in an SVG
document.
Attributes and Methods for Developers
state
Readonly attribute. Holds the current SVG::Rasterize::State object during tree traversal. Not internal because it is used by exception methods to retrieve the current state object (in order to store it in the exception object for debugging purposes).
init
$rasterize->init(%args)
If you overload init
, your method should also call this one.
For each given argument, init
calls the accessor with the same name to initialize the attribute. If such an accessor (or in fact, any method of that name) does not exist a warning is printed and the argument is ignored. Readonly attributes that are allowed to be set at initialization time are set separately at the beginning.
in_error
Expects an exception object or error message. Creates a fresh SVG::Rasterize::State object (without any transform etc.) and calls in_error_hook (which by default draws a translucent checkerboard across the image). After that, it dies with the given message.
Before you call in_error
directly, check out SVG::Rasterize::Exception.
absolute_font_size
$size = $rasterize->absolute_font_size('x-large')
Returns the current numerical value (in user units) corresponding to a given absolute font size keyword. The method is designed also to be used to check if a given string is an absolute font size keyword at all. Therefore it returns undef
if the input value is undef
or not an absolute font size keyword.
relative_font_size
$size = $rasterize->relative_font_size('larger')
NB: Currently, this method throws an exception if a relative font size keyword is given saying that these keywords are not supported, yet. The following describes the future behaviour.
Returns the current numerical value (in user units) corresponding to a given relative font size keyword. The method is designed also to be used to check if a given string is an relative font size keyword at all. Therefore it returns undef
if the input value is undef
or not an relative font size keyword.
Class Methods
multiply_matrices
2D affine transformation can be represented by 3 x 3 matrices of the form:
( a c e )
( b d f )
( 0 0 1 )
In this case, the concatenation of such transformations is represented by canonical matrix multiplication. This method takes two ARRAY references of the form [a, b, c, d, e, f]
whose entries correspond to the matrix entries above and returns an ARRAY reference with 6 entries representing the product matrix.
The method can be called either as subroutine or as class method or as object method:
$product = multiply_matrices($m, $n)
$product = SVG::Rasterize->multiply_matrices($m, $n)
$product = $rasterize->multiply_matrices($m, $n)
Note that multiply_matrices
does not perform any input check. It expects that you provide (at least) two ARRAY references with (at least) 6 numbers each. If you pass more parameters then the last two are used. If they contain more than 6 entries then the first 6 are used.
endpoint_to_center
@result = endpoint_to_center(@input)
@result = SVG::Rasterize->endpoint_to_center(@input)
@result = $rasterize->endpoint_to_center(@input)
Rasterization engines like SVG::Rasterize::Engine::PangoCairo might use center parameterization instead of endpoint parameterization of an elliptical arc (see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes). This method calculates the center parameters from the endpoint parameters given in a SVG
path data string. As indicated above, it can be called as a subroutine or a class method or an object method. The required parameters are:
x coordinate of the starting point
y coordinate of the starting point
radius in x direction
radius in y direction
angle by which the ellipse is rotated with respect to the positive x axis (in radiant, not degrees)
large arc flag
sweep flag
x coordinate of the end point
y coordinate of the end point.
If the reparameterization cannot be computed an empty list is returned. This can have two possible reasons. Either one of the radii is equal (with respect to machine precision) to 0 or the start and end point of the arc are equal (with respect to machine precision). The first case should have been checked before (note that no rounding problems can occur here because no arithmetics is done with the passed values) because in this case the arc should be turned into a line. In the second case, the arc should just not be drawn. Be aware that this latter case includes a full ellipse. This means that a full ellipse cannot be drawn as one arc. The SVG
specification is very clear on that point. However, an ellipse can be drawn as two arcs.
Note that the input values are not validated (e.g. if the values are numbers, if the flags are either 0 or 1 and so on). It is assumed that this has been checked before. Furthermore, it is not checked if the radii are very close to 0 or start and end point are nearly equal.
A list of the following parameters is returned (unless an empty list is returned due to the reasons mentioned above):
x coordinate of the center
y coordinate of the center
radius in x direction
This value might have been increased to make the ellipse big enough to connect start and end point. If it was negative the absolute value has been used (so the return value is always positive).
radius in y direction
This value might have been increased to make the ellipse big enough to connect start and end point. If it was negative the absolute value has been used (so the return value is always positive).
start angle in radiant
sweep angle (positive or negative) in radiant.
adjust_arc_radii
@result = adjust_arc_radii(@input)
@result = SVG::Rasterize->adjust_arc_radii(@input)
@result = $rasterize->adjust_arc_radii(@input)
The SVG
specification requires that the radii of an elliptic arc are increased automatically if the given values are too small to connect the given endpoints (see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes). This situation can arise from rounding errors, but also for example during an animation. Moreover, if a given radius is negative then the absolute value is to be used. This method takes care of these adjustments and returns the new values plus some intermediate values that might be useful for callers, namely endpoint_to_center.
In detail, it requires the following parameters:
x coordinate of the starting point
y coordinate of the starting point
radius in x direction
radius in y direction
angle phi by which the ellipse is rotated with respect to the positive x axis (in radiant)
x coordinate of the end point
y coordinate of the end point.
Note that the input values are not validated (e.g. if the values are numbers etc.). It is assumed that this has been checked before. Furthermore, it is not checked if the radii are very close to 0
or start and end point are nearly equal.
The following values are guaranteed to be returned:
adjusted absolute value of the radius in x direction
adjusted absolute value of the radius in y direction
This is all if one of the radii is equal to 0. Otherwise, the following additional values are returned:
sin(phi)
cos(phi)
x_1' (see
SVG
specification link above)y_1' (see
SVG
specification link above)1 / Lambda - 1
This value is only returned if Lambda is greater than 0 which is equivalent (assuming exact arithmetics) to the end point of the arc being different from the starting point. Lambda is the value calculated in equation (F.6.6.2) of the specification (see link above). 1 / Lambda - 1 is equal to the radicand in equation (F.6.5.2).
EXAMPLES
There are a few example scripts in the examples
directory of the tar ball. However, they rather illustrate the currently supported SVG
subset than options of SVG::Rasterize
. In order to run the example scripts, you need to have the SVG module installed which is formally only required for testing.
DIAGNOSTICS
Error processing
The SVG
documentation specifies how SVG
interpreters should react to certain incidents. The relevant section can be found here: http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
This section describes how some of these instructions are implemented by SVG::Rasterize
and how it reacts in some other situations in which the specification does not give instructions.
In error
According to the SVG
specification (see http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing, a document is "in error" if:
"the content does not conform to the
XML 1.0
specification, such as the use of incorrectXML
syntax"SVG::Rasterize
currently does not parseSVG
files and will therefore not detect such an error."an element or attribute is encountered in the document which is not part of the
SVG DTD
and which is not properly identified as being part of another namespace"Currently,
SVG::Rasterize
will also reject elements that are properly identified as being part of another namespace."an element has an attribute or property value which is not permissible according to this specification"
This is checked for those attributes and properties that are currently supported by
SVG::Rasterize
. Values that are currently ignored may or may not be checked."Other situations that are described as being in error in this specification"
In these cases, the rendering is supposed to stop before the incriminated element. Exceptions are path
, polyline
, and polygon
elements which are supposed to be partially rendered up to the point where the error occurs.
Furthermore, a "highly perceivable indication of error shall occur. For visual rendering situations, an example of an indication of error would be to render a translucent colored pattern such as a checkerboard on top of the area where the SVG
content is rendered."
In SVG::Rasterize
this is done by the in_error_hook. By default, it indeed draws a translucent (rgb(45, 45, 45)
with opacity 0.6
) checkerboard with 8 fields along the width or height (whichever is shorter). This behaviour can be changed by setting the in_error_hook. Setting the hook to undef
or sub {}
will disable the process.
SVG::Rasterize
exceptions
When SVG::Rasterize
encounters a problem it usually throws an exception. The cases where only a warning is issued a rare. This behaviour has several reasons:
If the document is in error (see section above) the
SVG
specification requires that the rendering stops.In case of failed parameter validation, Params::Validate expects the code execution to stop. One could work around this in an onerous and fragile way, but I will not do this.
Often there is no good fallback without knowing what the user intended to do. In these cases, it is better to just bail out and let the user fix the problem himself.
A too forgiving user agent deludes the user into bad behaviour. I think that if the rules are clear, a program should enforce them rather strictly.
The exceptions are thrown in form of objects. See Exception::Class for a detailed description. See below for a description of the classes used in this distribution. All error messages are described in SVG::Rasterize::Exception.
Invalid and numerically unstable values
There are situations where certain values cannot be dealt with, e.g. denominators of 0 or negative radicands. Examples are skews of 90 degrees or elliptical arcs where one radius is 0. In these situations, SVG::Rasterize
checks for these cases and acts accordingly. Great care is taken to check directly those values which are used as denominator, radicand etc. and not some mathematically equivalent expression which might evaluate to a slightly different value due to rounding errors. However, it is not checked if such an expression is very close to a critical value which might render the processing numerically unstable. I do not want to impose a certain notion of "too close" on SVG
authors. Instead it is left to them to check for these border cases. However, the underlying rasterization engine might still impose boundaries.
Exceptions
SVG::Rasterize
currently uses the following exception classes. This framework is experimental and might change considerably in future versions. See Exception::Class on how you can make use of this framework. See SVG::Rasterize::Exception for a detailed list of error messages.
SVG::Rasterize::Exception::Base
Base class for the others. Defines the state attribute which holds the current SVG::Rasterize::State object at the time the exception is thrown.
SVG::Rasterize::Exception::InError
The processing encountered an error in the
SVG
content.SVG::Rasterize::Exception::Setting
The exception was triggered by an error during the general preparation of the processing, e.g. an error during initialization of the rasterization backend.
SVG::Rasterize::Exception::Engine
The exception was triggered by the rasterization backend itself. This is specfically used when a bug in an engine implementation is encountered (e.g. a mandatory method is not overloaded). It is not restricted to these cases, though.
SVG::Rasterize::Exception::Parse
An error occured during parsing (usually of an attribute value). An exception of this class always indicates an inconsistency between validation and parsing of this value and should be reported as a bug.
SVG::Rasterize::Exception::Unsupported
The document (or user) tried to use a feature that is currently unsupported.
SVG::Rasterize::Exception::Attribute
Attribute means class attribute here, not
SVG
attribute. An example for such an exception is the attempt to change a readonly attribute.SVG::Rasterize::Exception::ParamsValidate
A method parameter did not pass a Params::Validate check.
SVG::Rasterize::Exception::Param
A method parameter passed the Params::Validate check, but is still invalid (an example is that the Params::Validate check only included that the value must be a number, but it also has to be in a certain range which is checked individually later).
SVG::Rasterize::Exception::Return
A method returned an invalid value (example: the
before_node_hook
did not return a hash).
Warnings
"Unrecognized init parameter %s."
You have given a parameter to the new method which does not have a corresponding method. The parameter is ignored in that case.
"Unable to load %s: %s. Falling back to SVG::Rasterize::Engine::PangoCairo."
The
engine_class
you were trying to use for rasterization could not be loaded.SVG::Rasterize
then tries to use its default backend SVG::Rasterize::Engine::PangoCairo. If that also fails, it gives up."Surface width is 0, nothing to do."
The width of output image evaluates to
0
. This value is rounded to an integer number of pixels, therefore this warning does not mean that you have provided an explicit number of0
(it could also have been e.g.0.005in
at a resolution of90dpi
). In this case, nothing is drawn."Surface height is 0, nothing to do."
Like above.
DEPENDENCIES
Class::Accessor, version 0.30 or higher
Cairo, version 1.061 or higher
The version of the underlying
C
library has to be at least 1.8.8. This is not automatically fulfilled by installing a sufficiently high version of the Perl module because the release cycles are completely decoupled. Regarding the functionality that is used directly, version 1.2 might actually be sufficient, but version 1.8 was the smallest I got pango (see below) compiled with.With respect to the module code, the dependency on Cairo is not strict. The code only requires Cairo in case no other rasterization engine is specified (see documentation for details). However, if you do not provide a different backend, which would probably at least require a wrapper written by you, then you cannot do anything without Cairo. Therefore I have included it as a strict dependency. You could take it out of the Makefile.PL if you know what you are doing. However, the distribution will not pass the test suite without Cairo.
Pango, version 1.220 or higher
The version of the underlying
C
library has to be at least 1.22.4. This is not automatically fulfilled by installing a sufficiently high version of the Perl module because the release cycles are completely decoupled.The rest of what has been said about
Cairo
above is also true forPango
. Both are loaded by SVG::Rasterize::Engine::PangoCairo and that is only loaded if no other backend has been specified.Params::Validate, version 0.91 or higher
Scalar::Util, version 1.19 or higher
Exception::Class, version 1.29 or higher
Additionally, testing requires the following modules:
SVG, version 2.37 or higher
Test::More, version 0.86 or higher
Test::Exception, version 0.27 or higher
Test::Warn, version 0.08 or higher.
BUGS AND LIMITATIONS
Bugs
Please report any bugs or feature requests to bug-svg-rasterize at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=SVG-Rasterize. I will be notified, and then you will automatically be notified of progress on your bug as I make changes.
Rendering of not fully opaque groups
Grouping elements are supposed to be rendered on a temporary canvas which is then composited into the background (see http://www.w3.org/TR/SVG11/render.html#Grouping). Currently,
SVG::Rasterize
renders each child element of the grouping element individually. This leads to wrong results if the group has an opacity setting below 1.Single character transformation in non-trivial character-to-glyph mapping scenarios
The specification at http://www.w3.org/TR/SVG/text.html#TSpanElement describes how single character transformations in e.g. text elements are supposed to be carried out when there is not a one to one mapping between characters and glyphs. Currently,
SVG::Rasterize
does not abide by these rules. Where values forx
,y
,dx
,dy
, orrotate
are specified on a individual character basis, the string is broken into part an rasterized piece by piece.
Limitations
Relative units
The relative units
em
,ex
, and%
are currently not supported. Neither are thefont-size
valuessmaller
andlarger
, thefont-weight
valueslighter
andbolder
, and thefont-stretch
valuesnarrower
andwider
.ICC
colorsICC
color settings for thefill
andstroke
properties are understood, but ignored. I do not know enough about color profiles to foresee how support would look like. Unless requested,ICC
color profiles will probably not be supported for a long time.XML
namesThe
XML
standard is very inclusive with respect to characters allowed inXML
Names and Nmtokens (see http://www.w3.org/TR/2006/REC-xml11-20060816/#xml-names).SVG::Rasterize
currently only allows theASCII
subset of allowed characters because I do not know how to build efficient regular expressions supporting the huge allowed character class.Most importantly, this restriction affects the
id
attribute of any element. Apart from that, it affectsxml:lang
attributes and thetarget
attribute ofa
elements.
Caveats
eval BLOCK
and$SIG{__DIE__}
Several methods in this distribution use
eval BLOCK
statements without setting a local$SIG{__DIE__}
. Therefore, a$SIG{__DIE__}
installed somewhere else can be triggered by these statements. Seedie
andeval
inperlfunc
and$^S
inperlvar
.Thread safety
I do not know much about threads and how to make a module thread safe. No specific measures have been taken to achieve thread safety of this distribution.
IMPLEMENTATION NOTES
This documentation is largely for myself. Read on if you are interested, but this section generally does not contain documentation on the usage of SVG::Rasterize
.
Deferred Rasterization
Some elements (namely text
and textPath
elements) can only be rasterized once their entire content is known (e.g. for alignment issues). In these situations, the SVG::Rasterize::State objects representing the deferred nodes are pushed to a _rasterization_queue
. The content is then only rasterized once the root element of this subtree is about to run out of scope.
INTERNALS
Regular Expressions
All reused regular expressions are located in SVG::Rasterize::Regexes. In general, they should be considered as private variables and are documented there for inspection only. Anyway, most of them are compiled into other expressions, so changing them would probably not achieve what you might expect. The expressions listed here are exceptions to this rule. They are considered part of the interface and you can change them (at your own risk ;-) ).
$package_part (internal)
qr/[a-zA-Z][a-zA-Z0-9\_]*/
$SVG::Rasterize::Regexes::RE_PACKAGE{p_PACKAGE_NAME}
qr/^$package_part(?:\:\:$package_part)*$/
Package names given to methods in this distribution, namely the
engine_class
parameters have to match this regular expression. I am not sure which package names exactly are allowed. If you know where in the Perl manpages or the Camel book this is described, please point me to it. If this pattern is too strict for your favourite package name, you can change this variable.
Internal Attributes
These attributes and the methods below are just documented for myself. You can read on to satisfy your voyeuristic desires, but be aware of that they might change or vanish without notice in a future version.
%DEFER_RASTERIZATION
Current value:
%DEFER_RASTERIZATION = (text => 1, textPath => 1);
Used by SVG::Rasterize::State to decide if rasterization needs to be deferred. See Deferred Rasterization above.
%TEXT_ROOT_ELEMENTS
Current value:
%TEXT_ROOT_ELEMENTS = (text => 1, textPath => 1);
Text content elements (like
tspan
) can inherit position information from ancestor elements. However, when they find an element of one of these types they do not have to look further up in the tree.
Internal Methods
_create_engine
Expects a HASH reference as parameter. No validation is performed. The entries
width
,height
, andengine_class
are used and expected to be valid if present._initial_viewport
Expects two HASH references. The first one contains the node attributes of the respective element. It has to be defined and a HASH reference, but the content is assumed to be unvalidated. The second is expected to be validated. The keys
width
,height
, andmatrix
are used. Does not return anything._traverse_object_tree
Called by rasterize. Expects a hash with the rasterization parameters after all substitutions and hierarchies of defaults have been applied. Handles the traversal of an SVG or generic
DOM
object tree for rasterization._process_node_object
Called by
_traverse_object_tree
. Expects a node object and a hash with the rasterization parameters. Performsextraction of the node name
This uses the
getNodeName
DOM
method. Takes whatever this method returns.extraction and normalization of attributes
This uses the
getAttributes
DOM
method. The return value is validated as being eitherundef
or a HASH reference. The result is further processed by _process_normalize_attributes. The final result is guaranteed to be a HASH reference.extraction of child nodes
This uses the
getChildNodes
DOM
method. The return value is validated as being eitherundef
or an ARRAY reference. A copy of the array is made to enable addition or removal of child nodes (by hooks) without affecting the node object.At this time, ignored nodes are filtered out of the list of child nodes.
transformation of SVG::Element character data into a SVG::Rasterize::TextNode object (see there for background information).
Returns a list of the following values. The result is not further validated than listed below.
the node name, whatever
getNodeName
on the object returneda HASH reference with the (potentially normalized) attributes
an ARRAY reference or
undef
with the list of child node objects (as returned bygetChildNodes
).
_parse_svg_file
Called by rasterize. Expects a hash with the rasterization parameters after all substitutions and hierarchies of defaults have been applied. Handles the
SAX
parsing of anSVG
file for rasterization.This method requires XML::SAX. This module is not a formal dependency of the
SVG::Rasterize
distribution because I do not want to force users to install it even if they only want to rasterize content that they have created e.g. using the SVG module. This method will raise an exception if XML::SAX cannot be loaded._process_normalize_attributes
Expects a flag (to indicate if normalization is to be performed) and a HASH reference. The second parameter can be false, but if it is
true
it is expected (without validation) to be a HASH reference. Makes a copy of the hash and returns it after removing (if the flag is true) enclosing white space from each value.Independently of the flag, it processes the
style
attribute. If this is a HASH reference it is turned into a string. This means double work, because it is split into a hash again later byState
, but it is a design decision thatState
should not see if the input data came as an object tree orXML
string. So this has to be done, and this seemed to be a good place although this method was not started for something like that (maybe it should be renamed)._flush_rasterization_queue
During Deferred Rasterization, nodes (more precisely: their corresponding SVG::Rasterize::State objects) are pushed to a queue. When the element that caused the deferred rasterization runs out of scope this method flushes the queue and calls the respective rasterization methods.
Expects nothing, returns nothing.
_generate_font_size_scale_table
Called by font_size_scale to generate the font size scale table based on medium_font_size and font_size_scale. The underlying data structure is designed to support different tables for different font families (as mentioned in the respective
CSS
specification), but the public accessor methods do not support that, yet._process_node
Is called for each node, examines what kind of node it is, and calls the more specific methods. Pushes the SVG::Rasterize::State object to the rasterization queue during deferred rasterization.
Expects a
SVG::Rasterize::State
object and optionally a hash of options. The important option isflush
. Possibly sets thequeued
option. All options are passed on to the downstream method._process_path
Expects a SVG::Rasterize::State object and optionally a hash of options. If the option
queued
is set to a true value, nothing is done.Expects that
$state->node_attributes
have been validated. Thed
attribute is handed over to _split_path_data which returns a list of instructions to render the path. This is then handed over to the rasterization backend (which has its own expectations)._angle
$angle = _angle($x1, $y1, $x2, $y2) $angle = SVG::Rasterize->_angle($x1, $y1, $x2, $y2) $angle = $rasterize->_angle($x1, $y1, $x2, $y2)
Expects two vectors and returns the angle between them in
rad
. No parameter validation is performed. If one of the vectors is0
(and only if it is exactly0
),undef
is returned._split_path_data
Expects a path data string. This is expected (without validation) to be defined. Everything else is checked within the method.
Returns a list. The first entry is either
1
or0
indicating if an error has occured (i.e. if the string is not fully valid). The rest is a list of ARRAY references containing the instructions to draw the path._process_rect
Expects a SVG::Rasterize::State object and optionally a hash of options. If the option
queued
is set to a true value, nothing is done.Expects that
$state->node_attributes
have been validated.The rest is handed over to the rasterization backend (which has its own expectations).
_process_circle
Same es _process_rect.
_process_ellipse
Same es _process_rect.
_process_line
Same es _process_rect.
_process_polyline
Same es _process_path.
_process_polygon
Same es _process_path.
_process_text
Expects a SVG::Rasterize::State object and optionally a hash of options. If the option
queued
is set to a true value, nothing is done.Expects that
$state->node_attributes
have been validated. Determinestext-anchor
and the absolute rasterization position for each text atom._process_tspan
Currently does not do anything. All text processing is done by either _process_text or _process_cdata. Might be deleted in the future.
_process_cdata
Expects a SVG::Rasterize::State object and optionally a hash of options. If the option
queued
is set to a true value, nothing is done.Expects that
$state->node_attributes
have been validated. Calls thedraw_text
method of the rasterization engine on each of its atoms in the right order.make_ro_accessor
This piece of documentation is mainly here to make the
POD
coverage test happy.SVG::Rasterize
overloadsmake_ro_accessor
to make the readonly accessors throw an exception object (of classSVG::Rasterize::Exception::Attribute
) instead of just croaking.
FOOTNOTES
[1] Yannis Haralambous: Fonts & Encodings. O'Reilly, 2007.
Tons of information about what the author calls the "digital space for writing".
SEE ALSO
ACKNOWLEDGEMENTS
This distribution builds heavily on the cairo library and its Perl bindings.
AUTHOR
Lutz Gehlen, <perl at lutzgehlen.de>
LICENSE AND COPYRIGHT
Copyright 2010-2011 Lutz Gehlen.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.