API Reference

This section documents the public API of psd2svg.

Main API

convert()

psd2svg.convert(input_path, output_path, image_prefix=None, enable_text=True, enable_live_shapes=True, enable_title=False, enable_class=False, image_format='webp', text_letter_spacing_offset=0.0, text_wrapping_mode=0, font_mapping=None, embed_fonts=False, font_format='woff2', resource_limits=None)[source]

Convenience method to convert a PSD file to an SVG file.

Parameters:
  • input_path (str) – Path to the input PSD file.

  • output_path (str) – Path to the output SVG file.

  • image_prefix (str | None) – Optional path prefix to save extracted images. If None, images will be embedded.

  • enable_text (bool) – Enable text layer conversion. If False, text layers are rasterized as images. Default is True.

  • enable_live_shapes (bool) – Enable live shape conversion when possible. Disabling live shapes results in <path> elements instead of shape primitives like <rect> or <circle>. This may be more accurate, but less editable. Default is True.

  • enable_title (bool) – Enable insertion of <title> elements with layer names. When False (default), title elements are omitted to reduce file size. Set to True to include <title> elements containing the Photoshop layer name for accessibility and debugging.

  • enable_class (bool) – Enable insertion of class attributes on SVG elements for debugging purposes. When False (default), elements will not have class attributes, producing cleaner SVG output. Set to True to add class attributes for layer types, effects, and semantic roles (e.g., “shape-layer”, “drop-shadow-effect”, “fill”) for debugging or styling.

  • image_format (str) – Image format to use when embedding or saving images. Supported formats: ‘webp’, ‘png’, ‘jpeg’. Default is ‘webp’.

  • text_letter_spacing_offset (float) – Global offset (in pixels) to add to all letter-spacing values. This can be used to compensate for differences between Photoshop’s text rendering and SVG’s text rendering. Typical values range from -0.02 to 0.02. Default is 0.0 (no offset).

  • text_wrapping_mode (int) – Text wrapping mode for bounding box text. Use 0 for no wrapping (default, native SVG <text>), or 1 for <foreignObject> with XHTML wrapping. Import TextWrappingMode from psd2svg.core.text for enum values. Only affects bounding box text (ShapeType=1); point text always uses native SVG <text> elements.

  • font_mapping (dict[str, dict[str, float | str]] | None) – Optional custom font mapping dictionary for resolving PostScript font names to font families without fontconfig. Useful on Windows or when fonts are not installed. Format: {“PostScriptName”: {“family”: str, “style”: str, “weight”: float}}. When not provided, uses built-in mapping for common fonts.

  • embed_fonts (bool) – Enable font embedding in SVG. When True, fonts used in text layers are embedded as base64-encoded data URIs in @font-face rules. Default is False. Requires fontconfig on Linux/macOS for font file discovery.

  • font_format (str) – Font format for embedding. Supported formats: ‘woff2’ (best compression, default), ‘woff’, ‘ttf’, ‘otf’. Only used when embed_fonts=True. WOFF2 provides 90%+ size reduction through automatic font subsetting.

  • resource_limits (ResourceLimits | None) – Optional resource limits for DoS prevention. If None, uses ResourceLimits.default() which enables limits (2GB file size, 3 minute timeout, 100 layer depth, 16K image dimension). Use ResourceLimits.unlimited() to disable all limits for trusted input.

Raises:
  • ValueError – If file size, layer depth, or image dimensions exceed limits.

  • TimeoutError – If conversion exceeds timeout limit.

Return type:

None

SVGDocument

class psd2svg.SVGDocument(svg, images=<factory>)[source]

Bases: object

SVG document and resources.

Example usage:

from psd_tools import PSDImage
from psd2svg import SVGDocument

# Create from PSDImage.
psdimage = PSDImage.open("input.psd")
document = SVGDocument.from_psd(psdimage)

# Save to file or get as string.
document.save("output.svg")  # Images embedded by default
svg_string = document.tostring()

# Rasterize to PIL Image.
rasterized = document.rasterize()

# Export and load back.
exported = document.export()
document = SVGDocument.load(exported["svg"], exported["images"])
svg: Element
images: dict[str, Image]
static from_psd(psdimage, enable_live_shapes=True, enable_text=True, enable_title=False, enable_class=False, text_letter_spacing_offset=0.0, text_wrapping_mode=0, font_mapping=None, resource_limits=None)[source]

Create a new SVGDocument from a PSDImage.

Parameters:
  • psdimage (PSDImage) – PSDImage object to convert.

  • enable_live_shapes (bool) – Enable live shape conversion when possible. Disabling live shapes results in <path> elements instead of shape primitives like <rect> or <circle>. This may be more accurate, but less editable.

  • enable_text (bool) – Enable text layer conversion. If False, text layers are rasterized as images.

  • enable_title (bool) – Enable insertion of <title> elements with layer names. When False (default), title elements are omitted to reduce file size. Set to True to include <title> elements containing the Photoshop layer name for accessibility and debugging.

  • enable_class (bool) – Enable insertion of class attributes on SVG elements for debugging purposes. When False (default), elements will not have class attributes, producing cleaner SVG output. Set to True to add class attributes for layer types, effects, and semantic roles (e.g., “shape-layer”, “drop-shadow-effect”, “fill”) for debugging or styling.

  • text_letter_spacing_offset (float) – Global offset (in pixels) to add to all letter-spacing values. This can be used to compensate for differences between Photoshop’s text rendering and SVG’s text rendering. Typical values range from -0.02 to 0.02. Default is 0.0 (no offset).

  • text_wrapping_mode (int) – Text wrapping mode for bounding box text. Use 0 for no wrapping (default, native SVG <text>), or 1 for <foreignObject> with XHTML wrapping. Import TextWrappingMode from psd2svg.core.text for enum values. Only affects bounding box text (ShapeType=1); point text always uses native SVG <text> elements.

  • font_mapping (dict[str, dict[str, float | str]] | None) – Optional custom font mapping dictionary for resolving PostScript font names to font families. Takes priority over built-in static mapping. Useful for providing custom fonts or overriding default mappings. Format: {“PostScriptName”: {“family”: str, “style”: str, “weight”: float}}. Example: {“ArialMT”: {“family”: “Arial”, “style”: “Regular”, “weight”: 80.0}}. When not provided, uses built-in mapping for ~4,950 fonts (539 default + 370 Hiragino + 4,042 Morisawa), with automatic fallback to system font resolution (fontconfig/Windows registry) if needed.

  • resource_limits (ResourceLimits | None) – Optional resource limits for DoS prevention. If None, uses ResourceLimits.default() which enables limits (2GB file size, 3 minute timeout, 100 layer depth, 16K image dimension). Use ResourceLimits.unlimited() to disable all limits for trusted input.

Return type:

SVGDocument

Returns:

SVGDocument object containing the converted SVG and images.

Raises:
  • ValueError – If layer depth or image dimensions exceed limits.

  • TimeoutError – If conversion exceeds timeout limit.

append_css(css)[source]

Append custom CSS rules to the SVG <style> element.

This method allows you to inject custom CSS rules into the SVG document. The CSS is appended to an existing <style> element if present, or a new <style> element is created as the first child of the root SVG element.

Parameters:

css (str) – CSS rules to append. Can be any valid CSS including selectors, media queries, keyframes, etc.

Return type:

None

Example

>>> from psd_tools import PSDImage
>>> from psd2svg import SVGDocument
>>>
>>> psdimage = PSDImage.open("input.psd")
>>> svg_doc = SVGDocument.from_psd(psdimage)
>>>
>>> # Add custom CSS for Japanese text
>>> svg_doc.append_css(
...     "text { font-variant-east-asian: proportional-width; }"
... )
>>>
>>> # Add more custom CSS
>>> svg_doc.append_css("@media print { .no-print { display: none; } }")
>>>
>>> svg_doc.save("output.svg")

Note

This method is idempotent - if the same CSS is appended multiple times, it will only appear once in the output (duplicate detection).

tostring(embed_images=True, embed_fonts=False, subset_fonts=True, font_format='woff2', image_prefix=None, image_format='webp', indent='  ', optimize=True)[source]

Convert SVG document to string.

Parameters:
  • embed_images (bool) – If True, embed images as base64 data URIs. Default is True since string output has no file system context for external images.

  • embed_fonts (bool) – If True, embed fonts as @font-face rules in <style> element. WARNING: Font embedding may be subject to licensing restrictions. Ensure you have appropriate rights before distributing SVG files with embedded fonts.

  • subset_fonts (bool) – If True, subset fonts to only include glyphs used in the SVG. Requires embed_fonts=True. This significantly reduces file size (typically 90%+ reduction). Default is True.

  • font_format (str) – Font format for embedding: “woff2” (default), “woff”, “ttf”, or “otf”. WOFF2 provides best compression and is recommended for web use.

  • image_prefix (str | None) – If provided, save images to files with this prefix. When specified, embed_images is ignored.

  • image_format (str) – Image format to use when embedding or saving images.

  • indent (str) – Indentation string for pretty-printing the SVG.

  • optimize (bool) – If True, apply SVG optimizations (consolidate defs, etc.). Default is True.

Return type:

str

save(filepath, embed_images=True, embed_fonts=False, subset_fonts=True, font_format='woff2', image_prefix=None, image_format='webp', indent='  ', optimize=True)[source]

Save the SVG to a file.

Parameters:
  • filepath (str) – Path to the output SVG file.

  • embed_images (bool) – If True, embed images as base64 data URIs. Default is True. Set to False and provide image_prefix to save images as external files.

  • embed_fonts (bool) – If True, embed fonts as @font-face rules in <style> element. WARNING: Font embedding may be subject to licensing restrictions. Ensure you have appropriate rights before distributing SVG files with embedded fonts.

  • subset_fonts (bool) – If True, subset fonts to only include glyphs used in the SVG. Requires embed_fonts=True. This significantly reduces file size (typically 90%+ reduction). Default is True.

  • font_format (str) – Font format for embedding: “woff2” (default), “woff”, “ttf”, or “otf”. WOFF2 provides best compression and is recommended for web use.

  • image_prefix (str | None) – If provided, save images to files with this prefix relative to the output SVG file’s directory.

  • image_format (str) – Image format to use when embedding or saving images.

  • indent (str) – Indentation string for pretty-printing the SVG.

  • optimize (bool) – If True, apply SVG optimizations (consolidate defs, etc.). Default is True.

Return type:

None

rasterize(dpi=0, rasterizer=None)[source]

Rasterize the SVG document to PIL Image.

Parameters:
  • dpi (int) – Dots per inch for rendering. If 0 (default), uses the rasterizer’s default (96 DPI for ResvgRasterizer). Higher values produce larger, higher resolution images (e.g., 300 DPI for print quality). Only used if rasterizer is None.

  • rasterizer (BaseRasterizer | None) – Optional custom rasterizer instance. If None, uses ResvgRasterizer with the specified dpi. Use this to specify alternative rasterizers like PlaywrightRasterizer for better SVG 2.0 feature support.

Return type:

Image

Returns:

PIL Image object in RGBA mode containing the rasterized SVG.

Note

When using PlaywrightRasterizer, fonts are automatically embedded using local file:// URLs for optimal performance (60-80% faster than data URIs, 99% smaller SVG strings).

Example

>>> # Default resvg rasterization
>>> image = document.rasterize()
>>> # High DPI rasterization
>>> image = document.rasterize(dpi=300)
>>> # Browser-based rasterization (fonts auto-embedded with file URLs)
>>> from psd2svg.rasterizer import PlaywrightRasterizer
>>> browser_rasterizer = PlaywrightRasterizer(dpi=96)
>>> image = document.rasterize(rasterizer=browser_rasterizer)
export(image_format='webp', indent='  ')[source]

Export the SVG document in a serializable format.

Note: Font information is now embedded in SVG font-family attributes, so no separate fonts list is exported.

Return type:

dict[str, str | dict[str, bytes]]

classmethod load(svg, images)[source]

Load an SVGDocument from SVG content and image bytes.

Parameters:
  • svg (str) – SVG content as a string.

  • images (dict[str, bytes]) – Dictionary mapping image IDs to image bytes.

Return type:

SVGDocument

Note

Font information is stored in SVG font-family attributes, not as a separate parameter.

__init__(svg, images=<factory>)

Rasterizers

Base Rasterizer

class psd2svg.rasterizer.BaseRasterizer[source]

Bases: ABC

Base class for SVG rasterizer implementations.

This abstract base class defines the interface for converting SVG documents to raster images (PIL Image objects). Subclasses must implement the from_file method to provide the actual rasterization logic.

from_string(svg_content)[source]

Rasterize SVG content from a string or bytes to a PIL Image.

This is a convenience method that writes the SVG content to a temporary file and calls from_file. Subclasses may override this for more efficient implementations.

Parameters:

svg_content (Union[str, bytes]) – SVG content as string or bytes.

Return type:

Image

Returns:

PIL Image object containing the rasterized SVG.

abstractmethod from_file(filepath)[source]

Rasterize an SVG file to a PIL Image.

This is the primary method that subclasses must implement to provide the actual rasterization logic.

Parameters:

filepath (str) – Path to the SVG file to rasterize.

Return type:

Image

Returns:

PIL Image object containing the rasterized SVG.

Resvg Rasterizer

class psd2svg.rasterizer.ResvgRasterizer(dpi=0)[source]

Bases: BaseRasterizer

High-performance SVG rasterizer using resvg.

This rasterizer uses the resvg library (via resvg-py) to convert SVG documents to raster images. Resvg is a fast, accurate SVG renderer written in Rust that provides excellent quality with minimal dependencies.

Note

Resvg does not support CSS @font-face rules with embedded fonts (data URIs). This implementation automatically extracts font file paths from @font-face src: url(”file://…”) declarations and passes them to resvg’s native font loading API. The @font-face CSS rules themselves are ignored by resvg. Data URIs (data:font/…) are not supported and will be silently ignored.

Example

>>> rasterizer = ResvgRasterizer(dpi=96)
>>> image = rasterizer.from_file('input.svg')
>>> image.save('output.png')
>>> svg_content = '<svg>...</svg>'
>>> image = rasterizer.from_string(svg_content)
>>> image.save('output.png')
__init__(dpi=0)[source]

Initialize the resvg rasterizer.

Parameters:

dpi (int) – Dots per inch for rendering. If 0 (default), uses resvg’s default of 96 DPI. Higher values produce larger, higher resolution images (e.g., 300 DPI for print quality).

from_file(filepath, font_files=None)[source]

Rasterize an SVG file to a PIL Image.

Parameters:
  • filepath (str) – Path to the SVG file to rasterize.

  • font_files (list[str] | None) – Optional list of font file paths to use for rendering.

Return type:

Image

Returns:

PIL Image object in RGBA mode containing the rasterized SVG.

Raises:

ValueError – If the SVG file does not exist or the content is invalid.

from_string(svg_content, font_files=None)[source]

Rasterize SVG content from a string to a PIL Image.

This method provides an optimized implementation that directly rasterizes the SVG content without creating a temporary file.

If font_files is not provided, this method automatically extracts font file paths from @font-face CSS rules in the SVG content (file:// URLs).

Parameters:
  • svg_content (Union[str, bytes]) – SVG content as string or bytes.

  • font_files (list[str] | None) – Optional list of font file paths to use for rendering. If None, font paths are extracted from the SVG content.

Return type:

Image

Returns:

PIL Image object in RGBA mode containing the rasterized SVG.

Raises:

ValueError – If the SVG content is invalid.

Fast, production-ready SVG rasterizer using the resvg rendering engine.

Example:

from psd2svg.rasterizer import ResvgRasterizer

# Create rasterizer instance
rasterizer = ResvgRasterizer(dpi=96)

# Rasterize from string
image = rasterizer.from_string(svg_string)

# Rasterize from file
image = rasterizer.from_file('input.svg')

Playwright Rasterizer

class psd2svg.rasterizer.PlaywrightRasterizer(dpi=96, browser_type='chromium')[source]

Bases: BaseRasterizer

Browser-based SVG rasterizer using Playwright.

This rasterizer uses Playwright’s headless Chromium to render SVG documents, providing accurate support for advanced SVG features including vertical text, text-orientation, dominant-baseline, and other SVG 2.0 features that may not be supported by native rasterizers.

Note

Requires Playwright to be installed: uv sync –group browser After installation, run: uv run playwright install chromium

Advantages:
  • Full SVG 2.0 feature support

  • Accurate vertical text rendering

  • Matches browser rendering exactly

Disadvantages:
  • Slower than native rasterizers (browser startup overhead)

  • Requires Chromium binary (~300MB)

  • More resource intensive

Example

>>> rasterizer = PlaywrightRasterizer(dpi=96)
>>> image = rasterizer.from_file('input.svg')
>>> image.save('output.png')
>>> # Use as context manager for automatic cleanup
>>> with PlaywrightRasterizer(dpi=96) as rasterizer:
...     image = rasterizer.from_string('<svg>...</svg>')
...     image.save('output.png')
classmethod is_available()[source]

Check if Playwright is available.

Return type:

bool

Returns:

True if Playwright is installed and can be used, False otherwise.

Example

>>> if PlaywrightRasterizer.is_available():
...     rasterizer = PlaywrightRasterizer()
... else:
...     print("Playwright not available")
__init__(dpi=96, browser_type='chromium')[source]

Initialize the Playwright rasterizer.

Parameters:
  • dpi (int) – Dots per inch for rendering. Higher values produce larger, higher resolution images (e.g., 300 DPI for print quality). Default is 96 DPI (standard screen resolution).

  • browser_type (Literal['chromium', 'firefox', 'webkit']) – Browser engine to use. Options are: - “chromium”: Best SVG support (recommended) - “firefox”: Good compatibility - “webkit”: Safari engine Default is “chromium”.

from_file(filepath)[source]

Rasterize an SVG file to a PIL Image.

Parameters:

filepath (str) – Path to the SVG file to rasterize.

Return type:

Image

Returns:

PIL Image object in RGBA mode containing the rasterized SVG.

Raises:
from_string(svg_content)[source]

Rasterize SVG content from a string to a PIL Image.

This method renders the SVG by loading it into a headless browser page and taking a screenshot.

Parameters:

svg_content (Union[str, bytes]) – SVG content as string or bytes.

Return type:

Image

Returns:

PIL Image object in RGBA mode containing the rasterized SVG.

Raises:
close()[source]

Close the browser and cleanup resources.

This method should be called when the rasterizer is no longer needed to free browser resources. Alternatively, use the context manager interface (with statement) for automatic cleanup.

Return type:

None

__enter__()[source]

Enter context manager.

Return type:

PlaywrightRasterizer

__exit__(*args)[source]

Exit context manager and cleanup resources.

Return type:

None

__del__()[source]

Destructor to ensure cleanup.

Return type:

None

Browser-based SVG rasterizer with full SVG 2.0 support using Playwright/Chromium.

Installation:

pip install psd2svg[browser]
playwright install chromium

Example:

from psd2svg.rasterizer import PlaywrightRasterizer

# Use as context manager (automatically cleans up browser)
with PlaywrightRasterizer(dpi=96) as rasterizer:
    image = rasterizer.from_file('input.svg')
    image.save('output.png')

# Or with SVGDocument
from psd2svg import SVGDocument
from psd_tools import PSDImage

psdimage = PSDImage.open('input.psd')
document = SVGDocument.from_psd(psdimage)

with PlaywrightRasterizer(dpi=96) as rasterizer:
    image = document.rasterize(rasterizer=rasterizer)
    image.save('output.png')

When to use:

  • Testing SVG 2.0 features (vertical text, text-orientation, dominant-baseline)

  • Quality assurance against browser rendering

  • Better support for advanced SVG features not supported by resvg

Utility Modules

SVG Utilities

psd2svg.svg_utils.safe_utf8(text)[source]

Remove illegal and problematic XML characters from text.

This function filters out characters that are illegal or problematic in XML documents by replacing them with spaces. The filtering is intentionally conservative, removing more characters than strictly required by the XML 1.0 specification.

Characters filtered (replaced with space):
  • C0 controls (0x00-0x08, 0x0B-0x1F): NULL, control characters Exception: TAB (0x09), LF (0x0A) are preserved

  • C1 controls (0x7F-0x9F): DEL and C1 control block including NEL (0x85)

  • UTF-16 surrogates (0xD800-0xDFFF): Illegal in UTF-8/XML

  • Non-characters (0xFDD0-0xFDDF, 0xFFFE-0xFFFF): Illegal in XML

Characters preserved:
  • TAB (0x09): Horizontal spacing

  • LF (0x0A): Line breaks

  • Normal printable characters (0x20 and above, excluding filtered ranges)

Note on CR (0x0D):

CR (Carriage Return) is filtered despite being technically legal in XML 1.0. This is an intentional conservative design choice because: 1. XML processors normalize CR to LF per XML 1.0 spec (section 2.11) 2. Filtering CR prevents line-ending confusion attacks 3. TAB and LF are sufficient for all whitespace needs in SVG 4. Historical issues with CR in PSD text conversion (see commit d81e4b5)

Args:

text: Input string that may contain illegal XML characters.

Returns:

Sanitized string with illegal characters replaced by spaces.

Example:
>>> safe_utf8("Hello\x00World\x85Test")
'Hello World Test'
>>> safe_utf8("Tab\there\nNewline")
'Tab    here

Newline’

Return type:

str

psd2svg.svg_utils.num2str(num, digit=2)[source]

Convert a number to a string, using the specified format for floats.

Return type:

str

psd2svg.svg_utils.num2str_with_unit(num, unit='px', digit=2)[source]

Convert a number to a string with a CSS unit appended.

Parameters:
  • num (int | float) – The numeric value to format

  • unit (str) – The CSS unit to append (default: “px”)

  • digit (int) – Number of decimal digits for floats (default: 2)

Return type:

str

Returns:

Formatted string with unit (e.g., “10px”, “1.5em”, “100%”)

Examples

>>> num2str_with_unit(10.0, "px")
'10px'
>>> num2str_with_unit(1.5, "em")
'1.5em'
>>> num2str_with_unit(100, "%")
'100%'
psd2svg.svg_utils.seq2str(seq, sep=',', digit=2)[source]

Convert a sequence of numbers to a string.

Uses the specified format for floats.

Return type:

str

psd2svg.svg_utils.create_node(tag, parent=None, class_='', title='', text='', desc='', xml_space=None, **kwargs)[source]

Create an XML node with attributes.

Parameters:

xml_space (Optional[str]) – Set xml:space attribute with proper XML namespace. Use “preserve” to preserve whitespace, “default” for normal behavior.

Return type:

Element

psd2svg.svg_utils.create_xhtml_node(tag, parent=None, text='', xml_space=None, **kwargs)[source]

Create an XHTML node with proper namespace.

This helper creates elements in the XHTML namespace for use within SVG <foreignObject> elements. The namespace is required for proper rendering in modern browsers (Chrome, Firefox, Safari, Edge). Note: resvg/resvg-py does not support foreignObject rendering.

Parameters:
  • tag (str) – HTML tag name (e.g., ‘div’, ‘p’, ‘span’).

  • parent (Optional[Element]) – Optional parent element to append this node to.

  • text (str) – Optional text content.

  • xml_space (Optional[str]) – Set xml:space attribute with proper XML namespace. Use “preserve” to preserve whitespace.

  • **kwargs (Any) – Additional attributes. Underscores in keys are converted to hyphens (e.g., ‘font_size’ becomes ‘font-size’).

Return type:

Element

Returns:

XHTML element with namespace prefix.

Example

>>> foreign_obj = create_node("foreignObject", x=0, y=0, width=100, height=50)
>>> div = create_xhtml_node("div", parent=foreign_obj)
>>> p = create_xhtml_node("p", parent=div, text="Hello")
>>> span = create_xhtml_node("span", parent=p, text="World", style="color: red")
psd2svg.svg_utils.styles_to_string(styles)[source]

Convert a dictionary of CSS styles to a CSS string.

Parameters:

styles (dict[str, str]) – Dictionary of CSS property names to values.

Return type:

str

Returns:

CSS string suitable for use in a ‘style’ attribute.

Example

>>> styles = {"color": "red", "font-size": "12px"}
>>> styles_to_string(styles)
'color: red; font-size: 12px'
psd2svg.svg_utils.fromstring(data)[source]

Parse an XML string to an Element.

Return type:

Element

psd2svg.svg_utils.tostring(node, indent='  ')[source]

Convert an XML node to a string.

Return type:

str

psd2svg.svg_utils.parse(file)[source]

Parse an XML file to an Element.

Return type:

Element

psd2svg.svg_utils.write(node, file, indent='  ')[source]

Write an XML node to a file.

Return type:

None

psd2svg.svg_utils.add_style(node, key, value)[source]

Add a CSS property to an XML node.

Return type:

None

psd2svg.svg_utils.add_class(node, class_name)[source]

Add a class to an XML node.

Return type:

None

psd2svg.svg_utils.set_attribute(node, key, value)[source]

Add an attribute to an XML node.

Return type:

None

psd2svg.svg_utils.append_attribute(node, key, value, separator=' ')[source]

Append a value to an existing attribute of an XML node.

Return type:

None

psd2svg.svg_utils.set_transform_with_origin(element, transform_attr, transforms, origin=None)[source]

Set transform attribute with optional transform-origin.

This function sets transform operations on gradient, pattern, or other SVG elements using the modern SVG 2.0 transform-origin attribute instead of translate wrappers.

Parameters:
  • element (Element) – SVG element to modify (e.g., linearGradient, pattern)

  • transform_attr (str) – Name of the transform attribute (e.g., “gradientTransform”, “patternTransform”)

  • transforms (list[str]) – List of transform functions (e.g., [‘rotate(45)’, ‘scale(2)’])

  • origin (tuple[float, float] | None) – Transform origin point (x, y). If provided, sets transform-origin attribute. Default SVG transform-origin is (0, 0).

Return type:

None

Example

Instead of: gradientTransform=”translate(50,50) rotate(45)

translate(-50,-50)”

Produces: gradientTransform=”rotate(45)” transform-origin=”50 50”

Note

SVG 2.0 feature. Supported by modern browsers and resvg-py. When origin is provided but no transforms, uses translate() instead. When the attribute already exists (from append_attribute), appends to it instead.

psd2svg.svg_utils.get_uri(node)[source]

Get an uri string for the given node.

Return type:

str

psd2svg.svg_utils.get_funciri(node)[source]

Get a funciri string for the given node.

Return type:

str

psd2svg.svg_utils.wrap_element(node, parent, wrapper)[source]

Wrap the given existing node in the wrapper element.

Usage::

wrapper = svg_utils.create_node(“g”) wrapped_node = svg_utils.wrap_element(node, parent, wrapper)

Parameters:
  • node (Element) – The XML node to be wrapped.

  • parent (Element) – The parent XML node containing the node to be wrapped.

  • wrapper (Element) – The wrapper XML node.

Return type:

Element

psd2svg.svg_utils.merge_attribute_less_children(element)[source]

Recursively merge children without attributes into their parent nodes.

This utility removes redundant wrapper elements that have no attributes, moving their text content directly into the parent element. This helps produce cleaner, more compact SVG output.

The function preserves document order by properly handling both element.text and child.tail to ensure text appears in the correct sequence.

Parameters:

element (Element) – The XML element to process recursively.

Return type:

None

Example

Before: <text><tspan x=”10”><tspan>Hello</tspan></tspan></text> After: <text><tspan x=”10”>Hello</tspan></text>

Before: <text><tspan font-weight=”700”>Bold</tspan><tspan> text</tspan></text> After: <text><tspan font-weight=”700”>Bold</tspan> text</text>

psd2svg.svg_utils.merge_common_child_attributes(element, excludes=None)[source]

Recursively merge common child attributes to their parent node.

This utility hoists attributes that are common to ALL children (with the same value) to the parent element. This helps produce cleaner, more compact SVG output by reducing redundant attribute declarations.

Parameters:
  • element (Element) – The XML element to process recursively.

  • excludes (set[str] | None) – Set of attribute names that should not be hoisted to parent. Common excludes for text elements: {“x”, “y”, “dx”, “dy”, “transform”}

Return type:

None

Example

Before: <text><tspan fill=”red”>A</tspan><tspan fill=”red”>B</tspan>

</text>

After: <text fill=”red”><tspan>A</tspan><tspan>B</tspan></text>

Before (with excludes={“x”}):
<text><tspan x=”10” fill=”red”>A</tspan><tspan x=”20”

fill=”red”>B</tspan></text>

After: <text fill=”red”><tspan x=”10”>A</tspan><tspan x=”20”>B

</tspan></text>

psd2svg.svg_utils.merge_consecutive_siblings(element)[source]

Recursively merge consecutive sibling elements with identical attributes.

This utility consolidates sequences of consecutive child elements that have the same tag and identical attributes by merging their text content. This is particularly useful for optimizing SVG <tspan> elements.

Parameters:

element (Element) – The XML element to process recursively.

Return type:

None

Example

Before: <text>

<tspan font-size=”18” letter-spacing=”0.72”>す</tspan> <tspan font-size=”18” letter-spacing=”0.72”>だ</tspan> <tspan font-size=”18” letter-spacing=”0.72”>け</tspan>

</text>

After: <text>

<tspan font-size=”18” letter-spacing=”0.72”>すだけ </tspan>

</text>

Before: <text>
<tspan font-size=”18” baseline-shift=”-0.36”

letter-spacing=”0.72”>さ</tspan>

<tspan font-size=”18” letter-spacing=”0.72”>す</tspan> <tspan font-size=”18” letter-spacing=”0.72”>だ</tspan>

</text>

After: <text>
<tspan font-size=”18” baseline-shift=”-0.36”

letter-spacing=”0.72”>さ</tspan>

<tspan font-size=”18” letter-spacing=”0.72”>すだ</tspan>

</text>

Note

  • Only merges elements with the same tag name

  • Only merges elements with identical attribute sets

  • Preserves document order

  • Does not merge elements with child elements

  • Empty elements (no text or tail) are removed

psd2svg.svg_utils.merge_singleton_children(element)[source]

Recursively merge singleton child nodes into their parent nodes.

This utility removes redundant wrapper elements when a parent has exactly one child and there are no conflicting attributes. The child’s attributes and text content are moved to the parent element.

Parameters:

element (Element) – The XML element to process recursively.

Return type:

None

Example

Before: <text><tspan>Hello</tspan></text> After: <text>Hello</text>

Before: <text x=”10”><tspan font-weight=”700”>Bold</tspan></text> After: <text x=”10” font-weight=”700”>Bold</text>

Not merged (conflicting attributes): Before: <text x=”10”><tspan x=”20”>Text</tspan></text> After: <text x=”10”><tspan x=”20”>Text</tspan></text> (unchanged)

Not merged (child has children): Before: <text><tspan><tspan>A</tspan><tspan>B</tspan></tspan></text> After: <text><tspan><tspan>A</tspan><tspan>B</tspan></tspan></text>

(unchanged)

psd2svg.svg_utils.consolidate_defs(svg)[source]

Consolidate all <defs> and definition elements into a global <defs>.

This optimization improves SVG structure by: 1. Creating a single global <defs> element at the beginning of the SVG 2. Moving all definition elements (filters, gradients, patterns, etc.) into it 3. Removing now-empty inline <defs> elements

Definition elements that are moved: - <defs> (contents merged into global defs) - <filter> - <linearGradient> - <radialGradient> - <pattern> - <clipPath> - <marker> - <symbol>

Parameters:

svg (Element) – The root SVG element to optimize (modified in-place).

Return type:

None

Note

This function preserves all id references and element ordering within defs.

Mask Element Exclusion:

This function does NOT move <mask> elements for the following technical reasons:

  1. Coordinate Systems: Masks use maskContentUnits="userSpaceOnUse" by default, meaning mask content coordinates are evaluated at reference time (where mask="url(#id)" is applied), not at definition time. Moving masks to a different DOM position could affect coordinate system evaluation.

  2. Property Inheritance: Masks inherit CSS properties (fill, stroke, opacity, etc.) from their DOM ancestors. Moving a mask to <defs> changes its inheritance chain, which can alter rendering for masks with styled content.

  3. Transform Handling: psd2svg uses special transform workarounds for masked elements that rely on specific mask positioning in the DOM tree.

  4. Renderer Compatibility: Known compatibility issues exist with some SVG renderers regarding mask positioning and interactions with other elements (e.g., clipPath-mask interactions).

Nested Definitions Are Consolidated:

While <mask> elements themselves are not moved, any nested <defs> and definition elements (filters, gradients, etc.) WITHIN masks ARE moved to the global defs. This provides optimization benefits while preserving mask rendering fidelity. See test_consolidate_complex_real_world_example in tests/test_optimize.py for demonstration.

Example

Before:
<svg>

<rect fill=”url(#g1)”/> <linearGradient id=”g1”>…</linearGradient> <defs><filter id=”f1”>…</filter></defs>

</svg>

After:
<svg>
<defs>

<linearGradient id=”g1”>…</linearGradient> <filter id=”f1”>…</filter>

</defs> <rect fill=”url(#g1)”/>

</svg>

psd2svg.svg_utils.deduplicate_definitions(svg)[source]

Deduplicate identical definition elements in <defs>.

Identifies structurally identical definition elements (filter, gradients, patterns, clipPath, marker, symbol) and merges duplicates by keeping the first occurrence and updating all url(#id) references throughout the SVG tree.

This function should be called AFTER consolidate_defs() to ensure all definition elements are in a single global <defs>.

Priority order (based on PSD per-layer structure): 1. filter, linearGradient, radialGradient, pattern (most common duplicates) 2. clipPath, marker, symbol (less common but still beneficial)

Parameters:

svg (Element) – The root SVG element (modified in-place).

Return type:

None

Note

Elements are considered identical if they have: - Same tag - Same attributes (except ‘id’) - Same child structure and content

Example

Before:
<defs>

<linearGradient id=”g1”><stop offset=”0%”/></linearGradient> <linearGradient id=”g2”><stop offset=”0%”/></linearGradient>

</defs> <rect fill=”url(#g1)”/> <circle fill=”url(#g2)”/>

After:
<defs>

<linearGradient id=”g1”><stop offset=”0%”/></linearGradient>

</defs> <rect fill=”url(#g1)”/> <circle fill=”url(#g1)”/>

psd2svg.svg_utils.unwrap_groups(svg)[source]

Unwrap <g> elements that have no meaningful attributes.

This optimization removes redundant <g> wrapper elements that don’t affect rendering, moving their children directly to the parent level. This reduces SVG nesting depth and file size.

Groups are unwrapped if they have NO attributes (or only empty class), AND no <title> child elements. Groups with rendering attributes (opacity, style, filter, mask, clip-path, transform) or identity attributes (id) are preserved.

Empty groups (no children) are removed entirely.

Parameters:

svg (Element) – The root SVG element to optimize (modified in-place).

Return type:

None

Example

Before:
<svg>
<g>

<rect x=”0” y=”0” width=”100” height=”100”/>

</g>

</svg>

After:
<svg>

<rect x=”0” y=”0” width=”100” height=”100”/>

</svg>

Preserved (has opacity):
<svg>
<g opacity=”0.5”>

<rect x=”0” y=”0” width=”100” height=”100”/>

</g>

</svg>

Note

This function is automatically called when optimize=True in save() and tostring(). It processes the tree recursively, unwrapping all eligible groups in a single pass.

psd2svg.svg_utils.extract_font_families(svg)[source]

Extract all unique font families from font-family attributes in SVG tree.

Scans the SVG element tree for all font-family attributes (both as direct attributes and within style attributes) and extracts all font families from comma-separated lists.

Parameters:

svg (Element) – The SVG element tree to scan.

Return type:

set[str]

Returns:

Set of unique font family names found in the SVG.

Note

  • Extracts ALL fonts from comma-separated font-family lists

  • Strips quotes from font family names

  • Searches both font-family attributes and CSS style attributes

Example

>>> svg = fromstring(
...     '<svg><text font-family="Arial, sans-serif">Hi</text></svg>'
... )
>>> families = extract_font_families(svg)
>>> "Arial" in families
True
>>> "sans-serif" in families
True
psd2svg.svg_utils.find_elements_with_font_family(svg, font_family, include_inherited=True)[source]

Find all text/tspan and XHTML text elements that use the given font family.

This function searches for text, tspan, and XHTML text elements (p, span from foreignObject) that have the specified font-family applied, either directly via attributes or through CSS inheritance from parent elements.

Parameters:
  • svg (Element) – SVG element tree to search.

  • font_family (str) – Font family name to search for (case-insensitive).

  • include_inherited (bool) – If True, include elements that inherit the font from parents. If False, only include elements with direct font-family declarations. Default is True for backward compatibility.

Return type:

list[Element]

Returns:

List of text/tspan/p/span elements that use the specified font family.

Note

  • Searches both font-family attributes and style attributes

  • Supports CSS inheritance (walks up parent chain) when include_inherited=True

  • Case-insensitive font family matching

  • Only returns text and tspan elements (not their parents)

Example

>>> svg = svg_utils.fromstring(
...     '<svg><text font-family="Arial">Hi</text></svg>'
... )
>>> elements = find_elements_with_font_family(svg, "Arial")
>>> len(elements)
1
>>> elements = find_elements_with_font_family(
...     svg, "Arial", include_inherited=False
... )
>>> len(elements)
1
psd2svg.svg_utils.extract_text_characters(element)[source]

Extract text characters from an element for font subsetting.

This function extracts both element.text and element.tail with HTML entity decoding. The tail is included because it’s rendered using the element’s font-family (not the parent’s), which is important for accurate font subsetting.

Control characters, format characters, and combining marks (like variation selectors) are filtered out as they are not rendered in SVG text elements and should not be included in charset matching for font resolution.

Parameters:

element (Element) – XML element to extract text from (typically text or tspan).

Return type:

str

Returns:

Text content (text + tail) with HTML entities decoded and non-renderable characters removed.

Note

  • Extracts element.text (content before first child element)

  • ALSO extracts tail (content after element’s closing tag)

  • Does NOT include text from child elements

  • Decodes HTML/XML entities (e.g., &lt; → <, &#x4E00; → 一)

  • Filters out: * Control characters (Cc, Cf): newlines, tabs, format controls * Surrogate characters (Cs): invalid in UTF-8 * Unassigned characters (Cn): not valid Unicode * Combining marks (Mn, Mc, Me): variation selectors, diacritics

  • Tail is included because SVG inherits font-family: the tail is rendered using the element’s font, not the parent’s font

Example

>>> elem = ET.fromstring('<text>Hello &amp; world</text>')
>>> extract_text_characters(elem)
'Hello & world'
>>> root = ET.fromstring('<text><tspan>A</tspan>B</text>')
>>> tspan = root[0]
>>> extract_text_characters(tspan)  # Returns 'AB' (text +
...                                  # tail)
'AB'
>>> elem = ET.fromstring('<text>Hello\nWorld</text>')
>>> extract_text_characters(elem)  # Newline filtered
'HelloWorld'
>>> elem = ET.fromstring('<text>©\uFE0E</text>')
>>> # Copyright + variation selector
>>> extract_text_characters(elem)  # Variation selector filtered
'©'
psd2svg.svg_utils.replace_font_family(element, old_font_family, new_font_family)[source]

Replace a font family with another in the given element.

This function modifies an SVG text/tspan element by replacing occurrences of old_font_family with new_font_family in font-family specifications. It handles both font-family attributes and style attributes.

Parameters:
  • element (Element) – Element to update (typically text or tspan element).

  • old_font_family (str) – Font family name to replace.

  • new_font_family (str) – Font family name to use as replacement.

Return type:

None

Example

Before: <text font-family=”ArialMT”>Hello</text> After: <text font-family=”Arial”>Hello</text>

Before: <text font-family=”ArialMT, Helvetica”>Hello</text> After: <text font-family=”Arial, Helvetica”>Hello</text>

Before: <text style=”font-family: ArialMT”>Hello</text> After: <text style=”font-family: ‘Arial’”>Hello</text>

Note

  • Replaces first occurrence of old_font_family in comma-separated list

  • If old_font_family not found, does nothing

  • For font-family attributes: no quotes (attribute delimiter is sufficient)

  • For style attributes: quotes font names for CSS compliance

  • Updates font-family attribute with higher priority than style

psd2svg.svg_utils.add_font_family(element, font_family)[source]

Add font family to font-family specification in the given element.

This function modifies an SVG text/tspan element by appending a font family to its existing font-family specification if not already present. It handles both font-family attributes and font-family declarations within style attributes.

Behavior:
  • If element has font-family attribute: append font to it

  • Else if element has font-family in style: append font to style

  • Else: create new font-family attribute with the font

  • If both attribute and style exist: append to attribute (higher priority)

Parameters:
  • element (Element) – Element to update (typically text or tspan element).

  • font_family (str) – Font family name to add.

Return type:

None

Example

Before: <text font-family=”Arial”>Hello</text> After: <text font-family=”Arial, Helvetica”>Hello</text>

Before: <text style=”font-family: Arial”>Hello</text> After: <text style=”font-family: ‘Arial’, ‘Helvetica’”>Hello</text>

Before: <text>Hello</text> After: <text font-family=”Helvetica”>Hello</text>

Note

  • Updates font-family attribute with higher priority than style

  • For font-family attributes: no quotes (attribute delimiter is sufficient)

  • For style attributes: quotes font names for CSS compliance

  • Idempotent: does not add font if already present in the chain

  • Ignores CSS ‘font’ shorthand property, only handles ‘font-family’

psd2svg.svg_utils.insert_or_update_style_element(svg, css_content)[source]

Insert or update a <style> element in the SVG root.

Parameters:
  • svg (Element) – SVG root element to modify.

  • css_content (str) – CSS content to insert or append.

Return type:

None

Note

  • If a <style> element exists as first child, appends to it

  • Otherwise creates a new <style> element as first child

  • Idempotent: skips if CSS content already present

Image Utilities

psd2svg.image_utils.encode_image(image, format='WEBP')[source]

Encode a PIL image to bytes in the specified format.

For JPEG format, RGBA images are automatically converted to RGB with a white background.

Return type:

bytes

psd2svg.image_utils.encode_data_uri(image, format='WEBP')[source]

Encode a PIL image as a base64 data URI.

For JPEG format, RGBA images are automatically converted to RGB with a white background.

Return type:

str

psd2svg.image_utils.decode_image(data, mode=None)[source]

Decode image data from bytes to a PIL image.

Return type:

Image

psd2svg.image_utils.decode_data_uri(data_uri, mode=None)[source]

Decode a base64 data URI to a PIL image.

Return type:

Image

psd2svg.image_utils.save_image(image, filepath, image_format)[source]

Save a PIL Image to file, with JPEG conversion if needed.

Parameters:
  • image (Image) – PIL Image to save.

  • filepath (str) – Output file path.

  • image_format (str) – Image format (e.g., ‘JPEG’, ‘PNG’, ‘WEBP’).

Return type:

None

Note

JPEG doesn’t support alpha channel, so RGBA images are converted to RGB with a white background.

Font Subsetting

Font subsetting utilities for reducing embedded font file sizes.

psd2svg.font_subsetting.extract_used_unicode(svg_tree)[source]

Extract Unicode characters per font-family from SVG text elements.

This function analyzes all <text>, <tspan>, and XHTML text elements (p, span from foreignObject) in the SVG tree to determine which Unicode characters are used by each font family.

Parameters:

svg_tree (Element) – Root SVG element to analyze.

Return type:

dict[str, set[str]]

Returns:

Dictionary mapping font-family names to sets of Unicode characters. Example: {“Arial”: {“A”, “B”, “C”}, “Noto Sans JP”: {“あ”, “い”}}

Note

  • Handles nested <tspan> elements and XHTML elements

  • Decodes XML entities (e.g., &lt;, &#x4E00;)

  • Extracts font-family from style attributes

  • Returns empty dict if no text elements found

psd2svg.font_subsetting.subset_font(input_path, output_format, unicode_codepoints)[source]

Subset a font file to include only specified Unicode codepoints.

This function uses fontTools (pyftsubset) to create a minimal font file containing only the glyphs needed for the specified codepoints.

Parameters:
  • input_path (str) – Path to input font file (TTF/OTF).

  • output_format (str) – Output format - “ttf”, “otf”, or “woff2”.

  • unicode_codepoints (set[int]) – Set of Unicode codepoints (integers) to include in the subset.

Return type:

bytes

Returns:

Subset font file as bytes.

Raises:
  • ImportError – If fonttools package is not installed.

  • Exception – If subsetting fails (invalid font, I/O error, etc.).

Example

>>> codepoints = {0x41, 0x42, 0x43, 0x3042}  # A, B, C, あ
>>> font_bytes = subset_font("/usr/share/fonts/arial.ttf", "woff2", codepoints)
>>> len(font_bytes)  # Much smaller than original
8432
psd2svg.font_subsetting.get_font_usage_from_svg(svg_tree)[source]

Get font usage information from SVG for subsetting.

This is a convenience wrapper around extract_used_unicode() that logs appropriate messages about font usage.

Parameters:

svg_tree (Element) – Root SVG element to analyze.

Return type:

dict[str, set[str]]

Returns:

Dictionary mapping font-family names to sets of Unicode characters.

Font subsetting reduces embedded font file sizes by 90%+ by including only the glyphs actually used in the SVG.

Note: Font subsetting is enabled by default when embedding fonts. The required fonttools package is automatically installed with psd2svg.

Usage Example:

from psd2svg.font_subsetting import extract_used_unicode, subset_font
import xml.etree.ElementTree as ET

# Parse SVG
svg_tree = ET.parse("output.svg").getroot()

# Extract Unicode characters per font
font_usage = extract_used_unicode(svg_tree)
# => {"Arial": {"H", "e", "l", "o"}, "Times": {"W", "r", "d"}}

# Subset a font file
font_bytes = subset_font(
    input_path="/usr/share/fonts/arial.ttf",
    output_format="woff2",
    unicode_chars={"H", "e", "l", "o"}
)
# => b'wOF2...' (WOFF2 font bytes)

Note: This module is typically used internally by SVGDocument.save() and tostring() methods. Direct usage is only needed for advanced use cases.

Quality Evaluation

psd2svg.eval.compute_conversion_quality(psdimage, metric)[source]

Test conversion quality in the raster format.

Return type:

float

psd2svg.eval.create_diff_image(psdimage, amplify=1.0)[source]

Create a diff image between the original and rasterized images.

For debugging purposes.

Parameters:
  • psdimage (PSDImage) – The PSD image to compare.

  • amplify (float) – Multiplier to amplify differences for better visibility. Default is 1.0.

Return type:

Image

Returns:

A PIL Image showing the differences between original and rasterized images.

psd2svg.eval.compare_raster_images(input1, input2, metric='MSE')[source]

Compare two raster images in numpy array format.

Return type:

float

Utilities for evaluating the quality of PSD to SVG conversion by comparing rasterized outputs.

Usage Example:

from psd2svg.eval import compute_conversion_quality, create_diff_image
from psd_tools import PSDImage

# Load PSD
psdimage = PSDImage.open("input.psd")

# Compute quality score (0.0 to 1.0, higher is better)
score = compute_conversion_quality(psdimage, metric="mse")
print(f"Quality score: {score:.4f}")

# Create visual diff image for debugging
diff_image = create_diff_image(psdimage, amplify=5.0)
diff_image.save("diff.png")

Supported metrics:

  • mse - Mean Squared Error (default)

  • rmse - Root Mean Squared Error

  • psnr - Peak Signal-to-Noise Ratio

  • ssim - Structural Similarity Index

Note: This module is primarily intended for testing and quality assurance purposes.

Internal Modules

The following modules are internal implementation details and may change without notice. They are documented here for reference but should not be used directly.

Core Converter

class psd2svg.core.converter.Converter(psdimage, enable_live_shapes=True, enable_text=True, enable_title=False, enable_class=False, text_letter_spacing_offset=0.0, text_wrapping_mode=0, font_mapping=None, resource_limits=None)[source]

Bases: AdjustmentConverter, LayerConverter, PaintConverter, ShapeConverter, TextConverter, EffectConverter

Converter main class.

Example usage:

from psd2svg.core.converter import Converter

Converter.convert(“example.psd”, “output.svg”)

Example usage:

from psd_tools import PSDImage from psd2svg.core.conveter import Converter

psd = PSDImage.open(“example.psd”) converter = Converter(psd) document = converter.build() document.embed_images() # or document.export_images(“output/image_%02d”) svg_string = document.export()

Parameters:
  • psdimage (PSDImage) – Source PSDImage to convert.

  • enable_live_shapes (bool) – Enable live shape conversion when possible.

  • enable_text (bool) – Enable text layer conversion when possible.

  • enable_title (bool) – Enable insertion of <title> elements with layer names. When True (default), each layer in the SVG will have a <title> element containing the Photoshop layer name for accessibility and debugging. Set to False to omit title elements and reduce file size.

  • enable_class (bool) – Enable insertion of class attributes on SVG elements for debugging purposes. When False (default), elements will not have class attributes, producing cleaner SVG output. Set to True to add class attributes for layer types, effects, and semantic roles (e.g., “shape-layer”, “drop-shadow-effect”, “fill”) for debugging or styling.

  • text_letter_spacing_offset (float) – Global offset (in pixels) to add to all letter-spacing values. This can be used to compensate for differences between Photoshop’s text rendering and SVG’s text rendering. Typical values range from -0.02 to 0.02. Default is 0.0 (no offset).

  • text_wrapping_mode (int) – Text wrapping mode for bounding box text. Use 0 for no wrapping (default, native SVG <text>), or 1 for <foreignObject> with XHTML wrapping. Import TextWrappingMode from psd2svg.core.text for enum values. Only affects bounding box text (ShapeType=1); point text always uses native SVG <text> elements.

  • font_mapping (dict[str, dict[str, float | str]] | None) – Optional custom font mapping dictionary. Takes priority over built-in static mapping. Format: {“PostScriptName”: {“family”: str, “style”: str, “weight”: float}}. When not provided, uses built-in mapping for ~4,950 fonts (539 default + 370 Hiragino + 4,042 Morisawa) with automatic fallback to system font resolution (fontconfig/Windows registry) if needed.

_id_counter: AutoCounter | None = None
__init__(psdimage, enable_live_shapes=True, enable_text=True, enable_title=False, enable_class=False, text_letter_spacing_offset=0.0, text_wrapping_mode=0, font_mapping=None, resource_limits=None)[source]

Initialize the converter internal state.

build()[source]

Build the SVG structure and internally save the result.

Return type:

None

auto_id(prefix='')[source]

Generate a unique ID for SVG elements.

Return type:

str

create_node(tag, parent=None, class_='', title='', text='', desc='', **kwargs)[source]

Create an SVG node with the current element as default parent.

This is a convenience wrapper around svg_utils.create_node that automatically uses self.current as the parent if no parent is specified.

Parameters:
  • tag (str) – The XML tag name.

  • parent (Element | None) – Optional parent element. Defaults to self.current.

  • class – Optional class attribute.

  • title (str) – Optional title element.

  • text (str) – Optional text content.

  • desc (str) – Optional description element.

  • **kwargs (Any) – Additional attributes to pass to svg_utils.create_node.

Return type:

Element

Returns:

The created XML element.

set_current(node)[source]

Set the current node for the converter.

Return type:

Iterator[None]

_abc_impl = <_abc._abc_data object>
_is_protocol = False

Layer Converter

class psd2svg.core.layer.LayerConverter(*args, **kwargs)[source]

Bases: ConverterProtocol

Main layer converter mixin.

add_layer(layer, depth=0, **attrib)[source]

Add a layer to the svg document.

Parameters:
  • layer (Layer) – The PSD layer to add.

  • depth (int) – Current nesting depth (for resource limit checking).

  • attrib (str) – Additional attributes to set on the created node.

Return type:

Element | None

add_artboard(layer, depth=0, **attrib)[source]

Add an artboard layer to the svg document.

Return type:

Element | None

add_group(layer, depth=0, **attrib)[source]

Add a group layer to the svg document.

Return type:

Element | None

add_children(group, depth=0)[source]

Add child layers to the current node.

Parameters:
  • group (Group | Artboard | PSDImage) – Group/Artboard/PSDImage to process.

  • depth (int) – Current nesting depth (for resource limit checking).

Raises:

ValueError – If depth exceeds resource_limits.max_layer_depth.

Return type:

None

add_pixel(layer, depth=0, **attrib)[source]

Add a general pixel-based layer to the svg document.

Return type:

Element | None

add_shape(layer, depth=0, **attrib)[source]

Add a shape layer to the svg document.

Return type:

Element | None

add_text(layer, depth=0, **attrib)[source]

Add a text layer to the svg document.

Return type:

Element | None

add_clipping_target(layer)[source]

Context manager to handle clipping target.

Return type:

Iterator[dict]

add_clip_path(layer)[source]

Add a clipping path and associated elements.

Usage:

with self.add_clip_path(layer) as clip_attrib:
    # Create elements inside the clipping mask.
    for clip_layer in layer.clip_layers:
        self.add_layer(clip_layer, ..., **clip_attrib)
Parameters:

layer (ShapeLayer) – The shape layer to use as a clipping path.

Yields:

Dictionary with clip-path attribute to apply to clipped elements.

Return type:

Iterator[dict]

NOTE: Due to the bad interactions between clip-path and masks in SVG,

we recommend using add_clip_mask instead of this method.

add_clip_mask(layer)[source]

Add a clipping mask and associated elements.

Usage:

with self.add_clip_mask(layer) as clip_attrib:
    # Create elements inside the clipping mask.
    for clip_layer in layer.clip_layers:
        self.add_layer(clip_layer, ..., **clip_attrib)
Parameters:

layer (Layer | Group) – The layer to use as a clipping mask.

Yields:

Dictionary with mask attribute to apply to clipped elements.

Return type:

Iterator[dict]

add_fill(layer, **attrib)[source]

Add fill node to the given element.

Return type:

Element | None

apply_raster_fill(layer, node)[source]

Add a raster main fill to the svg document.

Return type:

Element

set_layer_attributes(layer, node)[source]

Set common layer attributes to a layer node.

Return type:

None

set_opacity(opacity, node)[source]

Set opacity style to the node.

Return type:

None

set_blend_mode(psd_mode, node)[source]

Set blend mode style to the node.

Parameters:
  • psd_mode (bytes | BlendMode) – The Photoshop blend mode to convert.

  • node (Element) – The XML element to apply the blend mode to.

Raises:

ValueError – If the blend mode is not supported.

Return type:

None

set_isolation(layer, node)[source]

Add isolation to a group.

Note

  1. The default blending mode of a PSD group is passthrough, which corresponds to SVG isolation: auto (default)

  2. When the group has blending mode normal, it corresponds to SVG isolation: isolate.

  3. Other blending modes also isolate the group, and in SVG setting mix-blend-mode on a <g> to a value other than normal isolates the group by default.

Return type:

None

apply_mask(layer, target)[source]

Add a layer mask to the target node.

Return type:

Element

Shape Converter

Mixin module for geometric methods related to shape layers.

Handles conversion of Photoshop vector shapes to SVG path elements:

  • Basic shapes (ellipse, rectangle, rounded rectangle, line, polygon)

  • Custom paths with Bezier curves

  • Multi-path shapes with boolean operations

  • Path operations (combine, subtract, intersect, exclude)

The module processes vector mask data from shape layers and converts Photoshop’s knot points and control points to SVG path syntax.

class psd2svg.core.shape.ShapeConverter(*args, **kwargs)[source]

Bases: ConverterProtocol

Mixin for shape layers.

create_shape(layer, **attrib)[source]

Create a shape element from the layer’s vector mask or origination data.

Return type:

Element

create_composite_shape(layer, **attrib)[source]

Create a composite shape element from multiple paths with operations.

If the layer has a mask, it will be applied to the composite mask node, creating a mask chain: layer_mask -> composite_mask -> paths.

Return type:

Element

apply_union_operation(layer, path_group, current, rule)[source]

Apply Union (OR) operation to combine shapes.

Return type:

Element

apply_subtract_operation(layer, path_group, current, rule)[source]

Apply Subtract (NOT OR) operation to create holes.

Return type:

Element

apply_intersect_operation(layer, path_group, current, previous, rule)[source]

Apply Intersect (AND) operation to find overlapping regions.

Return type:

Element

apply_xor_operation(layer, path_group, current, previous, rule)[source]

Apply XOR (exclusive-or) operation using (A OR B) AND NOT (A AND B).

Return type:

Element

create_single_shape(layer, path, **attrib)[source]

Create a single shape element from the layer’s vector mask or origination data.

Return type:

Element

create_origination_rectangle(origination, reference, **attrib)[source]

Create a rectangle shape from origination data.

Return type:

Element

create_origination_rounded_rectangle(origination, reference, **attrib)[source]

Create a rounded rectangle shape from origination data.

Return type:

Element

create_origination_ellipse(origination, reference, **attrib)[source]

Create an ellipse shape from origination data.

Return type:

Element

apply_origination_transform(layer, origination, node)[source]

Apply transform attribute from origination data.

Return type:

Element

create_path(path, **attrib)[source]

Create a path element.

Return type:

Element

create_composite_path(paths, rule='fill-rule', **attrib)[source]

Create a composite path element.

Return type:

Element

psd2svg.core.shape.generate_path(path, width, height, command='C')[source]

Sequence generator for SVG path constructor.

Return type:

Iterator[str]

psd2svg.core.shape.get_origin_bbox(origination, reference)[source]

Get the origin bounding box for origination data.

Return type:

tuple[float, float, float, float]

psd2svg.core.shape.get_origin_scale(origination)[source]

Get the origin scale for origination data.

Return type:

tuple[float, float]

Text Converter

SVG text conversion logic for PSD text layers.

This module contains the TextConverter mixin class that converts Photoshop text layers (TypeLayer) to SVG text elements. It supports two rendering modes:

  1. Native SVG <text> elements (default) - Uses SVG text/tspan for accurate text rendering

  2. Foreign object mode - Uses <foreignObject> with XHTML for text wrapping support

The TextConverter works with TypeSetting and related data structures from the typesetting module to extract PSD text data and generate corresponding SVG markup.

Key features: - Point text and bounding box text - Paragraph alignment and justification - Text styling (font, color, size, decoration, etc.) - Vertical and horizontal text direction - Letter spacing, tracking, and kerning - Font effects (superscript, subscript, small caps)

Note: This module re-exports TypeSetting and TextWrappingMode for backward

compatibility. New code should import these directly from psd2svg.core.typesetting.

class psd2svg.core.text.TextConverter(*args, **kwargs)[source]

Bases: ConverterProtocol

Mixin for text layers.

create_text_node(layer)[source]

Create SVG text node from a TypeLayer.

Return type:

Element

class psd2svg.core.text.TextWrappingMode(*values)[source]

Bases: IntEnum

Text wrapping mode values.

NONE = 0
FOREIGN_OBJECT = 1
class psd2svg.core.text.TypeSetting(setting)[source]

Bases: object

Type tool object setting wrapper.

Example:

setting = TypeSetting(type_tool_object_setting)
for span in setting:
    print(span.start, span.end, span.text, span.paragraph, span.style)
__init__(setting)[source]
__iter__()[source]

Iterate over paragraph and style runs.

Return type:

Iterator[Paragraph]

property bottom: float

Bottom offset likely in normalized coordinate space.

property bounding_box: Rectangle

Text bounding box rectangle.

This is a bounding box around the text content.

property bounds: Rectangle

Text bounds rectangle.

This is a user-defined bounds that may differ from the bounding box. Use bounds for positioning the text content by justification settings.

property box_bounds: Rectangle

Box bounds from shape structure in engine data.

property document_resources: DictElement

Document resource dictionary from engine data.

property engine_data: EngineData

Engine data dictionary.

property engine_dict: DictElement

Engine dictionary from engine data.

property font_set: list[dict]

List of font info dictionaries from resources.

get_font_info(font_index, font_mapping=None)[source]

Get the font family name for the given font index.

Parameters:
  • font_index (int) – Index into the font set.

  • font_mapping (dict[str, dict[str, float | str]] | None) – Optional custom font mapping dictionary.

Return type:

FontInfo | None

Returns:

FontInfo object with font metadata, or None if font not found.

get_paragraph_sheet(sheet)[source]

Get the merged paragraph sheet.

Return type:

ParagraphSheet

get_postscript_name(font_index)[source]

Get the PostScript name for the given font index.

Parameters:

font_index (int) – Index into the font set.

Return type:

str | None

Returns:

PostScript name string, or None if not found.

get_style_sheet(sheet)[source]

Get the merged style sheet.

Return type:

StyleSheet

get_warp_path()[source]

Generate SVG path data for the warp effect.

Return type:

str

has_warp()[source]

Check if the text has a warp effect applied.

Return type:

bool

property left: float

Left offset likely in normalized coordinate space.

property paragraph_sheets: list[ParagraphSheet]

List of paragraph sheets from resources.

property point_base: tuple[float, float]

Point base from shape structure in engine data.

property resources: DictElement

Resource dictionary from engine data.

property right: float

Right offset likely in normalized coordinate space.

property shape_type: ShapeType

Shape type from engine data.

property small_cap_size: float

Small cap size from document resources.

property style_sheets: list[StyleSheet]

List of style sheets from resources.

property subscript_position: float

Subscript position from document resources.

property subscript_size: float

Subscript size from document resources.

property superscript_position: float

Superscript position from document resources.

property superscript_size: float

Superscript size from document resources.

property text: str

Text content from engine data.

property top: float

Top offset likely in normalized coordinate space.

property transform: Transform

Affine transform matrix.

property warp_perspective: float

Warp perspective from warp descriptor.

property warp_perspective_other: float

Warp perspective other from warp descriptor.

property warp_rotate: Literal['Hrzn', 'Vrtc'] | None

Warp rotate from warp descriptor.

property warp_style: str | None

Warp style.

Example warp content:

warp=DescriptorBlock(b'warp'){
    'warpStyle': (b'warpStyle', b'warpArc'),
    'warpValue': -100.0,
    'warpPerspective': 0.0,
    'warpPerspectiveOther': 0.0,
    'warpRotate': (b'Ornt', b'Hrzn')
}
property warp_value: float

Warp value from warp descriptor.

property warp_version: int

Warp version.

property writing_direction: WritingDirection

Writing direction from engine data.

Values are: - 0: Horizontal Top to Bottom - 2: Vertical Right to Left

NOTE: There could be other values, need to verify.

Effects Converter

class psd2svg.core.effects.EffectConverter(*args, **kwargs)[source]

Bases: ConverterProtocol

Effect converter mixin.

apply_background_effects(layer, target, insert_before_target=True)[source]

Apply background effects to the target element.

Return type:

None

apply_overlay_effects(layer, target)[source]

Apply overlay effects to the target element.

Return type:

None

apply_color_overlay_effect(layer, target)[source]

Apply color overlay effect to the target element.

Return type:

None

add_raster_color_overlay_effect(effect, target)[source]

Add a color overlay filter to the SVG document.

SVG does not allow coloring a raster image directly, so we create a filter.

Return type:

Element

add_vector_color_overlay_effect(effect, target)[source]

Add a color overlay effect to the current element using vector path.

Return type:

Element

apply_stroke_effect(layer, target)[source]

Apply stroke effects to the target element.

Return type:

None

add_raster_stroke_effect(layer, effect, target)[source]

Add a stroke filter to the SVG document.

SVG does not allow stroking a raster image directly, so we create a filter.

Return type:

Element

add_vector_stroke_effect(layer, effect, target)[source]

Add a stroke effect to the current element using vector path.

Return type:

Element

apply_drop_shadow_effect(layer, target, insert_before_target=False)[source]

Apply drop shadow effect to the current element.

Return type:

None

add_raster_drop_shadow_effect(effect, target)[source]

Add a drop shadow filter to the SVG document.

Return type:

Element

apply_outer_glow_effect(layer, target, insert_before_target=False)[source]

Apply outer glow effect to the current element.

Return type:

None

add_raster_outer_glow_effect(effect, target)[source]

Add an outer glow filter to the SVG document.

Return type:

Element

apply_gradient_overlay_effect(layer, target)[source]
Return type:

None

add_raster_gradient_overlay_effect(gradient, target, effect)[source]
Return type:

Element

add_vector_gradient_overlay_effect(gradient, target)[source]
Return type:

Element

set_gradient_transform(layer, gradient, effect)[source]

Set gradient transformations based on the effect properties.

Return type:

None

apply_pattern_overlay_effect(layer, target)[source]
Return type:

None

add_raster_pattern_overlay_effect(pattern, target)[source]
Return type:

Element

add_vector_pattern_overlay_effect(pattern, target)[source]
Return type:

Element

set_pattern_effect_transform(pattern, effect, reference)[source]

Set pattern transformations based on the effect properties.

Note: For patterns, reference and phase are simple translates, not pivot points.

Return type:

None

apply_inner_shadow_effect(layer, target)[source]
Return type:

None

add_raster_inner_shadow_effect(effect, target)[source]

Add an inner shadow filter to the SVG document.

Return type:

Element

apply_inner_glow_effect(layer, target)[source]
Return type:

None

add_raster_inner_glow_effect(effect, target)[source]

Add an inner glow filter to the SVG document.

Return type:

Element

apply_satin_effect(layer, target)[source]
Return type:

None

apply_bevel_emboss_effect(layer, target)[source]
Return type:

None

psd2svg.core.effects.polar_to_cartesian(angle, distance)[source]

Convert the polar coordinate to dx and dy.

Return type:

tuple[float, float]

Adjustment Converter

Mixin for adjustment layers conversion.

Due to the limited support of BackgroundImage in SVG filters, our approach wraps the backdrop elements into a symbol, and use two <use> elements to apply the filter.

When we have a layer structure like this:

<layer 1 />
<layer 2 />
<adjustment />

We convert it into the following SVG structure:

<symbol id="backdrop">
    <image id="layer1" ... />
    <image id="layer2" ... />
</symbol>
<filter id="adjustment"></filter>
<use href="#backdrop" />
<use href="#backdrop" filter="url(#adjustment)" />

This approach may have limitations when the backdrop has transparency, as the stacked use elements may not produce the intended visual result.

class psd2svg.core.adjustment.AdjustmentConverter(*args, **kwargs)[source]

Bases: ConverterProtocol

Mixin for adjustment layers.

add_invert_adjustment(layer, **attrib)[source]

Add an invert adjustment layer to the svg document.

Return type:

Element | None

add_posterize_adjustment(layer, **attrib)[source]

Add a posterize adjustment layer to the svg document.

Return type:

Element | None

add_threshold_adjustment(layer, **attrib)[source]

Add a threshold adjustment layer to the svg document.

Threshold converts the image to high-contrast black and white by comparing each pixel’s luminance against a threshold value. Pixels below the threshold become black (0), and pixels at or above become white (255).

Parameters:
  • layer (Threshold) – The Threshold adjustment layer to convert.

  • attrib (str) – Additional attributes for the SVG element.

Return type:

Element | None

Returns:

The SVG use element with the filter applied, or None if invalid.

add_hue_saturation_adjustment(layer, **attrib)[source]

Add a hue/saturation adjustment layer to the svg document.

Supports two modes: - Normal mode: Adjusts hue, saturation, and lightness using master values - Colorize mode: Desaturates then applies colorization tint

Note: Per-range color adjustments (layer.data) are not yet supported.

Return type:

Element | None

add_exposure_adjustment(layer, **attrib)[source]

Add an exposure adjustment layer to the svg document.

Applies exposure, offset, and gamma correction to simulate Photoshop’s Exposure adjustment layer. Operations are applied in linear RGB space to match Photoshop’s behavior.

The three parameters are applied in sequence: 1. Exposure: output = input × 2^exposure 2. Offset: output = input + offset 3. Gamma: output = input^(1/gamma)

Parameters:
  • layer (Exposure) – The Exposure adjustment layer to convert.

  • attrib (str) – Additional attributes for the SVG element.

Return type:

Element | None

Returns:

The SVG use element with the filter applied, or None if no-op.

add_brightness_contrast_adjustment(layer, **attrib)[source]

Add a brightness/contrast adjustment layer to the svg document.

Applies brightness and contrast adjustments using SVG feComponentTransfer filters with linear transfer functions. Operations are applied sequentially: brightness first, then contrast.

Brightness adds a constant value to all RGB channels:

output = input + (brightness / 255)

Contrast scales values around the midpoint (0.5):

output = (input - 0.5) * factor + 0.5 where factor = (259 * (contrast + 255)) / (255 * (259 - contrast))

Note: Photoshop’s modern (non-legacy) brightness/contrast uses a complex curves-based algorithm. Our linear approximation works well for most cases but may have higher error for extreme negative brightness values.

Parameters:
  • layer (BrightnessContrast) – The BrightnessContrast adjustment layer to convert.

  • attrib (str) – Additional attributes for the SVG element.

Return type:

Element | None

Returns:

The SVG use element with the filter applied, or None if no-op.

add_color_balance_adjustment(layer, **attrib)[source]

Add a color balance adjustment layer to the svg document.

Applies color balance adjustments to shadows, midtones, and highlights using SVG feComponentTransfer filters with lookup tables.

Note: Uses grayscale approximation for luminance due to SVG’s independent channel processing. Accuracy ~95% for typical adjustments.

Parameters:
  • layer (ColorBalance) – The ColorBalance adjustment layer to convert.

  • attrib (str) – Additional attributes for the SVG element.

Return type:

Element | None

Returns:

The SVG use element with the filter applied, or None if no-op.

add_black_and_white_adjustment(layer, **attrib)[source]

Add a black and white adjustment layer to the svg document.

Note: This adjustment layer type is not yet implemented.

Return type:

Element | None

add_channel_mixer_adjustment(layer, **attrib)[source]

Add a channel mixer adjustment layer to the svg document.

Note: This adjustment layer type is not yet implemented.

Return type:

Element | None

add_color_lookup_adjustment(layer, **attrib)[source]

Add a color lookup adjustment layer to the svg document.

Note: This adjustment layer type is not yet implemented.

Return type:

Element | None

add_curves_adjustment(layer, **attrib)[source]

Add a curves adjustment layer to the svg document.

Applies tonal curve adjustments using SVG feComponentTransfer filters with lookup tables generated from control points. Supports both composite (RGB) curves and per-channel (R/G/B) curves, matching Photoshop’s curve application order.

Parameters:
  • layer (Curves) – The Curves adjustment layer to convert.

  • attrib (str) – Additional attributes for the SVG element.

Return type:

Element | None

Returns:

The SVG use element with the filter applied, or None if identity.

add_gradient_map_adjustment(layer, **attrib)[source]

Add a gradient map adjustment layer to the svg document.

Note: This adjustment layer type is not yet implemented.

Return type:

Element | None

add_levels_adjustment(layer, **attrib)[source]

Add a levels adjustment layer to the svg document.

Applies tonal adjustments using input/output ranges and gamma correction. Supports both composite (RGB) and per-channel (R/G/B) adjustments, matching Photoshop’s application order.

Parameters:
  • layer (Levels) – The Levels adjustment layer to convert.

  • attrib (str) – Additional attributes for the SVG element.

Return type:

Element | None

Returns:

The SVG use element with the filter applied, or None if identity.

add_photo_filter_adjustment(layer, **attrib)[source]

Add a photo filter adjustment layer to the svg document.

Note: This adjustment layer type is not yet implemented.

Return type:

Element | None

add_selective_color_adjustment(layer, **attrib)[source]

Add a selective color adjustment layer to the svg document.

Note: This adjustment layer type is not yet implemented.

Return type:

Element | None

add_vibrance_adjustment(layer, **attrib)[source]

Add a vibrance adjustment layer to the svg document.

Note: This adjustment layer type is not yet implemented.

Return type:

Element | None

Paint Converter

Mixin module for paint methods, such as fill and stroke.

Photoshop supports the following paint types:

  • Solid color

  • Gradient (linear and radial)

  • Pattern

This module handles paint application for: - Shape layer fills (VECTOR_STROKE_CONTENT_DATA) - Shape layer strokes - Fill adjustment layers

(SOLID_COLOR_SHEET_SETTING, GRADIENT_FILL_SETTING, PATTERN_FILL_SETTING)

Note layer effects also support similar paint types, but the data structures and descriptors are different.

class psd2svg.core.paint.PaintConverter(*args, **kwargs)[source]

Bases: ConverterProtocol

Mixin for paint methods.

apply_vector_fill(layer, target)[source]

Apply fill effects to the target element.

Return type:

None

apply_vector_stroke(layer, target)[source]

Apply stroke effects to the target element.

Return type:

None

set_fill(layer, node)[source]

Set fill attribute to the given element.

Return type:

None

set_fill_stroke_content(layer, node)[source]

Set fill or stroke content from VECTOR_STROKE_CONTENT_DATA.

Return type:

None

set_fill_setting(layer, node)[source]

Set fill attribute from fill settings tagged blocks.

Return type:

None

set_fill_opacity(layer, node)[source]

Set fill opacity to the given element.

Return type:

None

set_stroke(layer, node)[source]

Add stroke style to the path node.

Return type:

None

add_gradient_definition(layer, descriptor)[source]

Add gradient definition to the SVG document.

Return type:

Element | None

add_linear_gradient(gradient)[source]

Add linear gradient definition to the SVG document.

Return type:

Element

add_radial_gradient(gradient)[source]

Add radial gradient definition to the SVG document.

Return type:

Element

set_gradient_stops(gradient, node)[source]

Set gradient stops to the given gradient element.

Return type:

Element

set_gradient_attributes(layer, setting, gradient)[source]

Set gradient settings such as angle to the gradient element.

Return type:

None

add_pattern(psdimage, descriptor)[source]

Add pattern definition to the SVG document.

Return type:

Element

set_pattern_transform(layer, setting, pattern)[source]

Set pattern transform to the pattern element.

The order is likely the following in Photoshop:

  1. Reference point translation (prepended)

  2. Scale

  3. Rotation

Note: For patterns, the reference point is a simple translate,

not a pivot point.

Return type:

None