- Python 100%
| .idea | ||
| test | ||
| .gitignore | ||
| .gitlab-ci.yml | ||
| isvg.py | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
Inline SVG Extension for Markdown
A Python-Markdown extension that enables inline embedding of local SVG
files directly into rendered HTML. Unlike standard Markdown image
syntax—which outputs <img> tags. This extension inserts the actual SVG
markup, enabling:
- full CSS styling of embedded SVGs
- consistent scaling behaviour
figure+figcaptionsupport- improved control over accessibility and semantics
Features
Inline SVG injection
Embed SVG files using a syntax similar to normal images:

or without caption:
!(path/to/image.svg)
Automatic <figure> + <figcaption> wrapping
If a caption is provided, the output becomes:
<figure>
<svg> ... </svg>
<figcaption>Your caption</figcaption>
</figure>
Without a caption, only the SVG element is inserted.
Safe Placeholder Handling
The extension first inserts placeholder tokens, preventing Python-Markdown from escaping the SVG. Placeholders are later replaced with the final SVG markup in the postprocessor phase.
Internal Caching
SVG files are parsed once and stored in a global cache for the lifetime of the process, improving performance.
Installation
pip install markdown isvg
Use the extension:
import markdown
from isvg import InlineSVGExtension
md = markdown.Markdown(extensions=[InlineSVGExtension(root="assets/svg")])
html = md.convert("")
Usage
Basic Inline SVG
!(diagram.svg)
Inline SVG with Caption

Example HTML Output
<figure>
<svg xmlns="http://www.w3.org/2000/svg" ...> ... </svg>
<figcaption>Data Flow</figcaption>
</figure>
Configuration
root
Defines the base directory from which SVG paths are resolved. Defaults to
"", the current working directory.
InlineSVGExtension(root="/var/www/assets/svg")
A Markdown reference such as:

resolves to:
/var/www/assets/svg/ui/menu.svg
remove_prefix
If your Markdown is generated from a web source that prefixes URLs with /,
you can remove that prefix:
InlineSVGExtension(remove_prefix="/")

Becomes reference to:
images/logo.svg
remove_prefex defaults to "", which means no prefix is removed.
How it Works
- Reference detection
A lightweight custom parser matches
and!(file.svg). - Structured extraction
captionandhrefsections are parsed using a bracket-aware tokenizer, not regex grouping. This ensures correct handling of nested brackets. - Path rewriting
remove_prefixandrootare applied. - File validation
- Must exist
- Must end in
.svg - Must not be remote (
http/https///)
- Caching & XML parsing
SVGs are loaded with
xml.etree.ElementTreeand stored in a module-level cache for reuse. - Placeholder emission
A unique stable placeholder token (
\x02path\x03) prevents Markdown from interfering with raw XML. - Postprocessing Placeholders are replaced with the final SVG markup after Markdown rendering is complete.
Error Handling
The extension silently ignores:
- Non-SVG files
- Remote URLs (
http://,https://,//) - Missing paths
- Invalid or malformed SVGs
In such cases, the original Markdown text is left unchanged.
Security Considerations
Inlining SVGs introduces risks if files are not trusted. SVGs can contain:
- JavaScript
- External resource references
- Embedded HTML
- CSS injections
If processing user-provided content, sanitize SVGs beforehand using tools such as: external SVG sanitizers or whitelist-based filtering.
Testing
This project includes pytest-based unit tests located in the test/ directory.
Run the full suite with:
pytest test
The tests cover:
- Correct placeholder insertion
- Proper caption extraction
figure+figcaptiongeneration- SVG loading and caching behavior
- Error-handling paths (invalid SVG, missing files, remote URLs)
- Interactions with other Python-Markdown extensions
If you extend or modify the extension, adding tests in this folder is highly recommended.
Known Limitations
- A global cache is used, which persists for the lifetime of the process.
- Relative links inside SVGs are not rewritten.
- Interactions with other Markdown extensions may affect final output order.
License
This project is licensed under the GNU General Public License v3.0 (GPLv3). You may redistribute and/or modify it under the terms of the GPLv3.