atelier / svg
PHP library for SVG manipulation, optimization, and morphing: parsing, building, styling, transforms, validation, and sanitization
Fund package maintenance!
Requires
- php: >=8.3
- ext-dom: *
- ext-xml: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.5
- rector/rector: ^2.3
README
Parse, build, query, optimize, sanitize and morph SVG, in pure PHP.
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.