atelier/svg

PHP library for SVG manipulation, optimization, and morphing: parsing, building, styling, transforms, validation, and sanitization

Maintainers

Package info

github.com/ateliersvg/svg

pkg:composer/atelier/svg

Transparency log

Fund package maintenance!

smnandre

Statistics

Installs: 123

Dependents: 3

Suggesters: 1

Stars: 9

Open Issues: 0

v1.0.0 2026-05-20 21:15 UTC

This package is auto-updated.

Last update: 2026-08-13 17:12:12 UTC


README

Parse, build, query, optimize, sanitize and morph SVG, in pure PHP.

PHP Version Tests PHPUnit PHPStan Stable License

An SVG document as typed PHP objects rather than a string you edit with regular expressions. Load a file, query it with CSS selectors, change it, and write it back. Or build one from nothing.

echo Svg::create(120, 120)->circle(60, 60, 50)->fill('#5a8dee')->toString();

Every element is a class, every attribute is validated, and nothing depends on an extension or an external binary. Backed by an extensive test suite and PHPStan at its highest level.

Parse and build · Query · Elements · Paths · Sanitize · Optimize · Accessibility · Morph · Documentation

Installation

composer require atelier/svg

Requires PHP 8.3 or later. No extensions, no image library, no external binary.

Quick start

use Atelier\Svg\Svg;

$svg = Svg::load('logo.svg')
    ->sanitize()
    ->optimizeWeb();

$svg->save('logo.min.svg');

Loading from markup instead of a path is Svg::fromString($markup), and toString() returns the document without writing a file. See Quick start.

Parse, build and export

Svg is the fluent facade. It loads an existing document, creates an empty one, and hands back markup, a pretty-printed string, a file, or a data URI.

$svg = Svg::create(200, 100)
    ->rect(10, 10, 80, 80)
    ->circle(150, 50, 40)
    ->fill('#5a8dee');

$svg->toDataUri();   // data:image/svg+xml;... ready for a CSS background

Parsing has profiles: the default accepts what a browser accepts, the strict one refuses what is merely loadable. See Document.

Query and edit

The whole tree is objects, and CSS selectors find your way through it.

$document = Svg::load('chart.svg')->getDocument();

foreach ($document->querySelectorAll('g[id^="series-"] path') as $path) {
    $path->setAttribute('stroke-width', '2');
}

Collections are typed and chainable rather than plain arrays. See Selectors and Collections.

Every element, typed

Shapes, text, gradients, filters, clipping and masking, structure, animation: each SVG element is a class with its own attributes rather than a generic node. See Elements.

Paths

The d attribute becomes a list of typed segments, which is what makes measuring and rewriting a curve possible at all.

use Atelier\Svg\Path\Path;

$path = Path::parse('M20,80 C 80,20 220,20 280,80');

$path->getLength();
$path->getPointAtLength(120);   // a Point, for placing a marker along the curve
$path->getBoundingBox();

Building, analysis, geometry, simplification, and baking transforms into coordinates all live in Paths.

Styling and transforms

Inline styles, presentation attributes, transform matrices, and the bounding boxes that layout depends on. See Styling.

Sanitize untrusted input

Accepting an SVG upload means accepting arbitrary markup. One call strips what makes it dangerous: <script>, on* handlers, javascript: URLs, <foreignObject>, and external references.

$safe = Svg::fromString($upload)->sanitize()->toString();

Profiles range from permissive to strict, and validation is separate for when you need to know what is wrong rather than remove it. See Sanitization.

Optimize

Fifty passes, grouped into cleanup, conversion, removal, and restructuring. Four presets choose for you, and optimizeWith() takes a pipeline you assembled yourself.

Svg::load('icon.svg')->optimizeWeb()->save('icon.min.svg');

optimizeSafe() preserves ids and metadata, optimizeAggressive() goes for the smallest file. Writing your own pass is a documented interface, not a fork. See Optimization.

Accessibility

A generated SVG is invisible to a screen reader until it is told what it shows.

use Atelier\Svg\Element\Accessibility\Accessibility;

Accessibility::setTitle($document, 'Quarterly revenue');
Accessibility::setDescription($document, 'Bar chart comparing Q1 to Q4');

Titles, descriptions, ARIA roles and labels, focus order, and an audit that reports what is missing. See Accessibility.

Morph and animate

Interpolate between two shapes, whatever their segment counts, and export the result as SMIL, CSS keyframes, JavaScript, or a sprite sheet.

use Atelier\Svg\Morphing\Morph;
use Atelier\Svg\Path\PathParser;

$parser = new PathParser();
$frames = Morph::frames(
    $parser->parse('M 0 0 L 100 0 L 100 100 L 0 100 Z'),
    $parser->parse('M 50 0 L 100 50 L 50 100 L 0 50 Z'),
    60,
    'ease-in-out',
);

See Morphing.

Documentation

  • Installation: requirements and setup.
  • Quick start: load, create, and manipulate a document.
  • Document: parse, create, validate, sanitize, export.
  • Elements: every element as a typed object.
  • Paths: build, measure, simplify, transform.
  • Styling: styles, transforms, layout boxes.
  • Optimization: the passes, the presets, writing your own.
  • Morphing: interpolation and animation export.
  • Guides: sanitizing uploads, icon sprites, charts, batch processing.

The full documentation is published at ateliersvg.com/svg.

Contributing

Contributions are welcome. Visit the project on GitHub to report a bug, suggest a feature, or open a pull request.

Before submitting code, run:

composer qa   # PHP-CS-Fixer, PHPStan at level max, and PHPUnit

Changes to public behaviour need a test and a documentation update.

Support

Bug reports, security disclosures, and contribution guidelines are collected at ateliersvg.com/support.

Atelier is maintained by Simon André. Sharing the package or starring it on GitHub helps more than you would think.

License

Atelier SVG is released under the MIT License.