renfordt/php-svg

Read, edit, write, and render SVG files with PHP

Maintainers

Package info

codeberg.org/renfordt/php-svg

Issues

pkg:composer/renfordt/php-svg

Transparency log

Statistics

Installs: 12

Dependents: 0

Suggesters: 0

v0.1.0 2026-07-30 12:47 UTC

This package is auto-updated.

Last update: 2026-07-30 11:11:12 UTC


README

The zero-dependency vector graphics library for PHP applications

Badge Packagist Version Packagist PHP Version status-badge Quality Gate Status Coverage

[!NOTE] PHP-SVG is a fork of meyfa/php-svg. The aim of this fork is to keep the package updated, typesafe, and modern.

[!WARNING] PHP-SVG is still in its alpha stage and may not be suitable for complex applications, yet. Especially the rasterization functionality (GD & Imagick) is still in its early stages and may not be suitable for production use.

Features

PHP-SVG can help with the following set of tasks:

  • Generating vector graphics programmatically with PHP code. The resulting SVG string can be written to a file or sent as part of the HTTP response.
  • Loading and parsing SVG images into document trees that are easy to modify and append.
  • Converting vector graphics into raster image formats, such as PNG or JPEG (rasterization).

Please note that PHP-SVG is still in its alpha stage and may not be suitable for complex applications, yet. Contributions are very welcome. Find out how to contribute.

Installation

Requirements

  • PHP: >=8.4
  • renfordt/colors - used for color manipulation
  • ext-simplexml - (optional) for loading SVG files or strings containing SVG code
  • ext-gd - (optional) for rasterization
  • ext-imagick - (optional) for rasterization, with RSVG

The original PHP-SVG from mayfa is free of dependencies. To reduce the complexity of this library, I decided to remove the color manipulation from this package and add one dependency renfordt/colors to support color manipulation.

While most extensions are almost always available on typical PHP installations, ext-imagick is a special case here and usually not available by default on most PHP installations. Still, you might want to make sure that your hosting provider actually has them. If SimpleXML, GD or Imagick are missing, PHP-SVG can still perform all tasks except those that require the missing extension. For example, programmatically generating SVG images and outputting them as XML will always work, even without any extension.

Composer (recommended)

This package is available on Packagist and can be installed with Composer:

composer require renfordt/php-svg

This adds a dependency to your composer.json file. If you haven't already, setup autoloading for Composer dependencies by adding the following require statement at the top of your PHP scripts:

<?php
require_once __DIR__ . '/vendor/autoload.php';

You may need to adjust the path if your script is located in another folder.

Manual installation

Download the latest release or the current development version of this repository, and extract the source code somewhere in your project. Then you can use our own autoloader to make available the SVG namespace:

<?php
require_once __DIR__ . '/<path_to_php_svg>/autoloader.php';

The rest works exactly the same as with Composer, just without the benefits of a package manager.

Getting started

This section contains a few simple examples to get you started with PHP-SVG.

Creating SVGs programmatically

The following code generates an SVG containing a blue square. It then sets the Content-Type header and sends the SVG as text to the client:

<?php
require __DIR__ . '/vendor/autoload.php';

use SVG\SVG;
use SVG\Nodes\Shapes\SVGRect;

// image with dimensions 100x100
$image = new SVG(100, 100);
$doc = $image->getDocument();

// blue 40x40 square at the origin
$square = new SVGRect(0, 0, 40, 40);
$square->setStyle('fill', '#0000FF');
$doc->addChild($square);

header('Content-Type: image/svg+xml');
echo $image;

Converting SVGs to strings

The above example uses echo, which implicitly converts the SVG to a string containing its XML representation. This conversion can also be made explicitly:

// This will include the leading <?xml ... ?> tag needed for standalone SVG files:
$xmlString = $image->toXMLString();
file_put_contents('my-image.svg', $xmlString);

// This will omit the <?xml ... ?> tag for outputting directly into HTML:
$xmlString = $image->toXMLString(false);
echo '<div class="svg-container">' . $xmlString . '</div>';

Loading SVGs from strings

In addition to generating images programmatically from scratch, they can be parsed from strings or loaded from files. The following code parses an SVG string, mutates part of the image, and echoes the result to the client:

<?php
require __DIR__ . '/vendor/autoload.php';

use SVG\SVG;

$svg  = '<svg width="100" height="100">';
$svg .= '<rect width="40" height="40" fill="#00F" id="my-rect" />';
$svg .= '</svg>';

$image = SVG::fromString($svg);
$rect = $image->getDocument()->getElementById('my-rect');
$rect->setX(25)->setY(25);

header('Content-Type: image/svg+xml');
echo $image;

Loading SVGs from files

For loading from a file instead of a string, call SVG::fromFile($file). That function supports paths on the local file system, as well as remote URLs. For example:

// load from the local file system:
$image = SVG::fromFile('path/to/file.svg');

// or from the web (worse performance due to HTTP request):
$image = SVG::fromFile('https://upload.wikimedia.org/wikipedia/commons/8/8c/Even-odd_and_non-zero_winding_fill_rules.svg');

Rasterizing

[!Warning] This feature in particular is very much work-in-progress. Many things will look wrong and rendering large images may be very slow.

The toRasterImage($width, $height [, $background [, $engine]]) method is used to render an SVG to a raster image. It returns an instance of SVG\Rasterization\Engine\RenderingResult, a small readonly wrapper around the image produced by the rendering engine:

$result = $image->toRasterImage(200, 200);

$result->image;  // GdImage|Imagick — the rendered image
$result->engine; // RenderingEngineEnum::GD or RenderingEngineEnum::IMAGICK
$result->width;  // 200
$result->height; // 200

$result->isGd();      // true if $result->image is a GdImage
$result->isImagick(); // true if $result->image is an Imagick instance

Choosing the rendering engine

The engine is selected via the fourth parameter, using SVG\Enums\RenderingEngineEnum:

use SVG\Enums\RenderingEngineEnum;

// AUTO (the default): use Imagick if ext-imagick is loaded, otherwise GD
$result = $image->toRasterImage(200, 200, null, RenderingEngineEnum::AUTO);

// force a specific engine
$result = $image->toRasterImage(200, 200, null, RenderingEngineEnum::GD);
$result = $image->toRasterImage(200, 200, null, RenderingEngineEnum::IMAGICK);

Because the default is AUTO, $result->image is an Imagick instance whenever ext-imagick is available, and a GdImage otherwise. Either check $result->isGd() before handing the image to GD functions, or request RenderingEngineEnum::GD explicitly. A MissingExtensionException is thrown if the requested engine is unavailable.

Encoding the result

For a GD result, GD provides methods for encoding the image to a number of formats:

<?php
require __DIR__ . '/vendor/autoload.php';

use SVG\SVG;
use SVG\Enums\RenderingEngineEnum;
use SVG\Nodes\Shapes\SVGCircle;

$image = new SVG(100, 100);
$doc = $image->getDocument();

// circle with radius 20 and green border, center at (50, 50)
$doc->addChild(
    (new SVGCircle(50, 50, 20))
        ->setStyle('fill', 'none')
        ->setStyle('stroke', '#0F0')
        ->setStyle('stroke-width', '2px')
);

// rasterize to a 200x200 image, i.e. the original SVG size scaled by 2.
// the background will be transparent by default.
$result = $image->toRasterImage(200, 200, null, RenderingEngineEnum::GD);

header('Content-Type: image/png');
imagepng($result->image);

For an Imagick result, use Imagick's own encoding methods instead:

$result = $image->toRasterImage(200, 200, null, RenderingEngineEnum::IMAGICK);

$result->image->setImageFormat('png');

header('Content-Type: image/png');
echo $result->image->getImageBlob();

The raster image will default to preserving any transparency present in the SVG. For cases where an opaque image is desired instead, it is possible to specify a background color. This may be mandatory when outputting to some formats, such as JPEG, that cannot encode alpha channel information. For example:

// white background
$result = $image->toRasterImage(200, 200, '#FFFFFF', RenderingEngineEnum::GD);
imagejpeg($result->image, 'path/to/output.jpg');

Text rendering (loading fonts)

PHP-SVG implements support for TrueType fonts (.ttf files) using a handcrafted TTF parser. Since PHP doesn't come with any built-in font files, you will need to provide your own. The following example shows how to load a set of font files. PHP-SVG will try to pick the best matching font for a given text element, based on algorithms from the CSS spec.

<?php
require __DIR__ . '/vendor/autoload.php';

use SVG\SVG;
use SVG\Enums\RenderingEngineEnum;

// load a set of fonts from the "fonts" directory relative to the script directory
SVG::addFont(__DIR__ . '/fonts/Ubuntu-Regular.ttf');
SVG::addFont(__DIR__ . '/fonts/Ubuntu-Bold.ttf');
SVG::addFont(__DIR__ . '/fonts/Ubuntu-Italic.ttf');
SVG::addFont(__DIR__ . '/fonts/Ubuntu-BoldItalic.ttf');

$image = SVG::fromString('
<svg width="220" height="220">
  <rect x="0" y="0" width="100%" height="100%" fill="lightgray"/>
  <g font-size="15">
    <text y="20">hello world</text>
    <text y="100" font-weight="bold">in bold!</text>
    <text y="120" font-style="italic">and italic!</text>
    <text y="140" font-weight="bold" font-style="italic">bold and italic</text>
  </g>
</svg>
');

header('Content-Type: image/png');
imagepng($image->toRasterImage(220, 220, null, RenderingEngineEnum::GD)->image);

Note that PHP often behaves unexpectedly when using relative paths, especially with fonts. Hence, it is recommended to use absolute paths, or use the __DIR__ constant to prepend the directory of the current script.

Document model

This section explains a bit more about the object structure and how to query or mutate parts of documents.

SVG root node

An instance of the SVG class represents the image abstractly. It does not store DOM information itself. That is the responsibility of the document root node, which is the object corresponding to the <svg> XML tag. For example:

<?php
require __DIR__ . '/vendor/autoload.php';

use SVG\SVG;

$svg  = '<svg width="100" height="100">';
$svg .= '<rect width="40" height="40" fill="#00F" id="my-rect" />';
$svg .= '</svg>';

$image = SVG::fromString($svg);

// obtain the <svg> node (an instance of \SVG\Nodes\Structures\SVGDocumentFragment):
$doc = $image->getDocument();

Child nodes

Child nodes of any element can be obtained as follows:

// Returns the number of children.
$numberOfChildren = $element->countChildren();

// Returns a child element by its index.
$firstChild = $element->getChild(0);

// Returns an array of matching child elements.
$childrenThatAreRects = $element->getElementsByTagName('rect');

// Returns an array of matching child elements.
$childrenWithClass = $element->getElementsByClassName('my-class-name');

The root node has an additional function for obtaining a unique element by its id attribute:

// Returns an element or null.
$element = $doc->getElementById('my-rect');

Child nodes can be added, removed, and replaced:

// Append a child at the end.
$document->addChild(new \SVG\Nodes\Shapes\SVGLine(0, 0, 10, 10));

// Insert a new child between the children at positions 0 and 1.
$group = new \SVG\Nodes\Structures\SVGGroup();
$document->addChild($group, 1);

// Remove the second child. (equivalent to $document->removeChild($group);)
$document->removeChild(1);

// replace the first child with $group
$document->setChild(0, $group);

Several children can be added in one call. Without indexes, the nodes are appended in order; with indexes, each node is inserted at the position given at the same offset in the second array:

// Append both nodes at the end.
$document->addChildren([
    new \SVG\Nodes\Shapes\SVGLine(0, 0, 10, 10),
    new \SVG\Nodes\Shapes\SVGCircle(5, 5, 5),
]);

// Insert the line at index 0 and the circle at index 2.
$document->addChildren([$line, $circle], [0, 2]);

Note that each index is applied as its node is inserted, so it refers to the child list as it exists at that moment, not to the list before the call.

Attributes

Every attribute is accessible via $element->getAttribute($name). Some often-used attributes have additional shortcut methods, but they are only available on the classes for which they are valid, so make sure the node you are accessing is of the correct type to prevent runtime errors. Some examples:

$document->getWidth();
// equivalent to: $doc->getAttribute('width')

$document->setWidth('200px');
// equivalent to: $doc->setAttribute('width', '200px');

$rect->setRX('10%');
// equivalent to: $rect->setAttribute('rx', '10%');

Attributes can also be set in bulk, and removed again. Passing null as a value unsets the attribute, which makes setAttribute($name, null) equivalent to removeAttribute($name). Note that the empty string is a valid value and does not unset anything:

// Set several attributes at once (values may be int, string, or null).
$rect->setAttributes([
    'x' => 10,
    'y' => 10,
    'width' => '50%',
]);

// Remove an attribute.
$rect->removeAttribute('rx');

All attribute getters return string|null, since attributes are stored in their string form. One exception is the viewBox attribute, which has a parsed accessor:

// Returns [x, y, width, height] as floats, or null if there is no
// viewBox attribute or it cannot be parsed as four numbers.
[$x, $y, $width, $height] = $document->getViewBox();

Node values

Nodes that carry text content (such as <text> or <title>) expose it via getValue and setValue. getValue always returns a string — the empty string if no value is set — and passing null to setValue clears the value:

$text->setValue('hello world');
$text->getValue(); // 'hello world'

$text->setValue(null);
$text->getValue(); // ''

Styles

Some presentation attributes are considered style properties by the SVG spec. These will be treated specially by PHP-SVG and made available via getStyle and setStyle instead of getAttribute and setAttribute. Consider the following node:


<circle cx="10" cy="10" r="5" style="fill: red" stroke="blue" stroke-width="4"/>
  • cx, cy and r are attributes.
  • fill, stroke and stroke-width are styles.

Styles work much like attributes. setStyle($name, null) unsets the style, as does removeStyle($name):

$circle->setStyle('fill', '#F00');
$circle->getStyle('fill'); // '#F00'

$circle->removeStyle('fill');
$circle->getStyle('fill'); // null

getStyle only reports what is set on the node itself. To resolve the style that is actually in effect, use getComputedStyle, which takes <style> rules on ancestor containers, inheritance from parent nodes, and the inherit keyword into account:

$image = SVG::fromString('
<svg width="100" height="100">
  <g fill="#00F">
    <rect width="40" height="40" id="my-rect" />
  </g>
</svg>
');

$rect = $image->getDocument()->getElementById('my-rect');

$rect->getStyle('fill');         // null — not set on the rect itself
$rect->getComputedStyle('fill'); // '#00F' — inherited from the <g>

Note that getComputedStyle returns null rather than 'inherit' when nothing resolves to a concrete value.

Debugging

If you aren't getting any output but only a blank page, try temporarily enabling PHP's error reporting to find the cause of the problem.

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// ... rest of the script ...

Make sure you disable this again when you're done, as it may leak sensitive information about your server setup. Additionally, ensure you're not setting the Content-Type header to an image format in this mode, as your browser will try to render the error message as an image, which won't work.

Alternatively, you may attempt to find your server's error log file. Its location depends on how you're running PHP.

Contributing

This project is available to the community for free, and there is still a lot of work to do. Every bit of help is welcome! You can contribute in the following ways:

  • By starring PHP-SVG on Codeberg to spread the word. The more it's used, the better it will become!
  • By reporting any bugs or missing features you encounter. Please quickly search through the issues tab to see whether someone has already reported the same problem. Maybe you can add something to the discussion?
  • By contributing code. You can take a look at the currently open issues to see what needs to be worked on. If you encounter a bug that you know how to resolve, it should be pretty safe to submit the fix as a pull request directly. For new features, it is recommended to first create an issue for your proposal, to gather feedback and discuss implementation strategies.

Please read:

By contributing material or code to PHP-SVG via a pull request or commit, you confirm that you own the necessary rights to that material or code, and agree to make available that material or code under the terms of the MIT license.