NAME

SVGPDF - Create PDF XObject from SVG data

SYNOPSIS

    my $pdf = PDF::API2->new;
    my $svg = SVGPDF->new($pdf);
    my $xof = $svg->process("demo.svg");

    # If all goes well, $xof is an array of hashes, each representing an
    # XObject corresponding to the <svg> elements in the file.
    # Get a page and graphics context.
    my $page = $pdf->page;
    $page->bbox( 0, 0, 595, 842 );
    my $gfx = $pdf->gfx;

    # Place the objects.
    my $y = 832;
    foreach my $xo ( @$xof ) {
	my @bb = @{$xo->{vbox}};
        my $h = $bb[3];
	$gfx->object( $xo->{xo}, 10, $y-$h, 1 );
	$y -= $h;
    }

    $pdf->save("demo.pdf");

DESCRIPTION

This module processes SVG data and produces one or more PDF XObjects to be placed in a PDF document. This module is intended to be used with PDF::Builder, PDF::API2 and compatible PDF packages.

The main method is process(). It takes the SVG from an input source, see "INPUT".

COORDINATES & UNITS

SVG coordinates run from top-left to bottom-right.

Dimensions without units are pixels, at 96 pixels / inch. E.g., width="96" means 96px (pixels) and is equal to 72pt (points) or 1in (inch).

For font sizes, CSS defines em to be equal to the font size, and ex is half of the font size.

CONSTRUCTOR

In its most simple form, a new SVGPDF object can be created with a single argument, the PDF document.

$svg = SVGPDF->new($pdf);

There are a few optional arguments, these can be specified as key/value pairs.

fc

A reference to a callback routine to handle fonts. See "FONT HANDLER CALLBACK".

It may also be an array of routines which will be called in sequence until one of them succeeds (returns a 'true' result).

fontsize

The font size to be used for dimensions in 'ex' and 'em' units.

Note that CSS defines 'em' to be the font size, and 'ex' half of the font size.

pagesize

An array reference containing the maximum width and height of the resultant image.

There is no widely accepted default for this, so we use [595,842] which corresponds to an ISO A4 page.

grid

If not zero, a grid will be added to the image. This is mostly for developing and debugging.

The value determines the grid spacing.

verbose

Verbosity of informational messages. Set to zero to silence all but fatal errors.

debug

Internal use only.

For convenience, the mandatory PDF argument can also be specified with a key/value pair:

$svg = SVGPDF->new( pdf => $pdf, grid => 1, fc => \&fonthandler );

METHODS

process

$xof = $svg->process( $data, %options )

This methods gets SVG data from $data and returns an array reference with rendered images. See "OUTPUT" for details.

The input is read using File::LoadLines. See "INPUT" for details.

Recognized attributes in %options are:

fontsize

The font size to be used for dimensions in 'ex' and 'em' units.

This value overrides the value set in the constructor.

combine

An SVG can produce multiple XObjects, but sometimes these should be kept as a single image.

There are two ways to combine the image objects. This can be selected by setting $opts{combine} to either "stacked" or "bbox".

Type "stacked" (default) stacks the images on top of each other, left sides aligned. The bounding box of each object is only used to obtain the width and height.

Type "bbox" stacks the images using the bounding box details. The origins of the images are vertically aligned and images may protrude other images when the image extends below the origin.

sep

When combining images, add additional vertical space between the individual images.

INPUT

The input SVG data must be correct XML data. The data can be a single <svg> element, or a container element (e.g. <html> or <xml>) with one or more <svg> elements among its children.

The SVG data can come from several sources:

  • An SVG document on disk, specified as the name of the document.

  • A file handle, openened on a SVG document, specified as a glob reference. You can use \*DATA to append the SVG data after a __DATA__ separator at the end of the program.

  • A string containing SVG data, specified as a reference to a scalar.

The input is read using File::LoadLines. See its documentation for details.

OUTPUT

The result from calling process() is a reference to an array containing hashes that describe the XObjects. Each hash has the following keys:

vbox

The viewBox as specified in the SVG element.

If no viewBox is specified it is set to 0 0 W H, where W and H are the width and the height.

width

The width of the XObject, as specified in the SVG element or derived from its viewBox.

height

The height of the XObject, as specified in the SVG element or derived from its viewBox.

vwidth

The desired width, as specified in the SVG element or derived from its viewBox.

vheight

The desired height, as specified in the SVG element or derived from its viewBox.

xo

The XObject itself.

FONT HANDLER CALLBACK

In SVG fonts are designated by style attributes font-family, font-style, font-weight, and font-size. How these translate to a PDF font is system dependent. SVGPDF provides a callback mechanism to handle this. As described at CONSTRUCTOR, constructor argument fc can be set to designate a user routine.

When a font is required at the PDF level, SVGPDF first checks if a @font-face CSS rule has been set up with matching properties. If a match is found, it is resolved and the font is set. If there is no appropriate CSS rule for this font, the callback is called with the following arguments:

( $self, $pdf, $style )

where $pdf is de PDF document and $style a hash reference that contains values for font-family, font-style, font-weight, and font-size. Don't touch $self, it is undocumented for a reason.

The callback function can use the contents of $style to select an appropriate font and return it.

SVGPDF will try to call the font handler callback only once for each combination of family, style and weight. If the callback function returns a 'false' result SVGPDF will try other alternatives to find a font.

Example of an (extremely simplified) callback:

    sub simple_font_handler {
        my ( $self, $pdf, $style ) = @_;

	my $family = $style->{'font-family'};

	my $font;
	if ( $family eq 'sans' ) {
	    $font = $pdf->font('Helvetica');
	}
	else {
	    $font = $pdf->font('Times-Roman');
	}

        return $font;
    }

If no callback function is set, SVGPDF will recognize the standard PDF corefonts, and aliases serif, sans and mono.

IMPORTANT: With the standard corefonts only characters of the ISO-8859-1 set (Latin-1) can be used. No greek, no chinese, no cyrillic. You have been warned.

LIMITATIONS

The following SVG elements are implemented.

  • svg, but not nested.

  • style, as a child of the outer svg.

    Many style attributes are understood, including but not limited to:

    color, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-dasharray, fill, stroke-width, stroke-linecap, stroke-linejoin, transform (translate, scale, skewX, skewY, rotate, matrix) font-family, font-style, font-weight, font-size, text-anchor.

    Partially implemented: @font-face (src url data and local file only).

  • circle, ellipse, g, image, line, path, polygon, polyline, rect (no rounded corners), text and tspan (no white-space styles).

  • defs and use,

The following SVG features are partially implemented.

  • Percentage units. For most "y", "h" or "height" attributes the result will be the percentage of the viewBox height.

    Similar for "x", "w" and "width".

    Everything else will result in a percentage of the viewBox diagonal (according to the specs).

  • Embedded SVG elements and preserveAspectRatio.

  • Standalone T-path elements.

The following SVG features are not (yet) implemented.

  • title, desc elements

The following SVG features are not planned to be implemented.

  • Shades, gradients, patterns and animations.

  • Shape rendering attributes.

  • Transparency.

  • Text paths.

  • Clipping and masking.

What is supported, then? Most SVG files generated by any of the following tools seem to produce good if not perfect results:

  • abc2svg (ABC music notation tool)

  • MathJax, inline and display without tag

  • GNUplot

  • QRcode and barcode generating tools

  • XTerm SVG screen dumps

AUTHOR

Johan Vromans < jvromans at squirrel dot nl >

Code for circular and elliptic arcs donated by Phil Perry.

SUPPORT

SVGPDF development is hosted on GitHub, repository https://github.com/sciurius/perl-SVGPDF.

Please report any bugs or feature requests to the GitHub issue tracker, https://github.com/sciurius/perl-SVGPDF/issues.

LICENSE

Copyright (C) 2022.2023 Johan Vromans,

Redistribution and use in source and binary forms, with or without modification, are permitted provided under the terms of the Simplified BSD License.