Development Guide
This guide covers setting up a development environment and contributing to psd2svg.
Development Setup
Prerequisites
Python 3.10-3.14
uv package manager
Git
Clone the Repository
git clone https://github.com/kyamagu/psd2svg.git
cd psd2svg
Install Dependencies
The project uses uv for dependency management:
# Install dependencies
uv sync
# Optional dependencies
uv sync --group docs # Documentation tools
uv sync --extra browser # Playwright rasterizer
uv run playwright install chromium # Install Chromium browser for Playwright
Quick Reference
For a quick command reference, git workflow, and development philosophy,
see the CLAUDE.md file in the repository root. This file provides
essential guidance for AI-assisted development with Claude Code.
Development Commands
Testing
Run the test suite with pytest:
uv run pytest
# Run with coverage
uv run pytest --cov=psd2svg
# Run specific test file
uv run pytest tests/test_svg_document.py
Type Checking
The project uses mypy for static type checking:
uv run mypy src/ tests/
# Check specific module
uv run mypy src/psd2svg/svg_document.py
Linting
Ruff is used for fast linting:
uv run ruff check src/ tests/
# Auto-fix issues
uv run ruff check --fix src/ tests/
Code Formatting
Format code with ruff:
uv run ruff format src/ tests/
# Check formatting without changes
uv run ruff format --check src/ tests/
Building
Build distribution packages:
uv build
This creates wheel and sdist packages in the dist/ directory.
Documentation
Build the documentation:
uv run sphinx-build -b html docs docs/_build/html
# Watch for changes and rebuild
uv run sphinx-autobuild docs docs/_build/html
View the built documentation by opening docs/_build/html/index.html in a browser.
Architecture
The package follows a modular converter architecture with multiple inheritance.
Public API Layer
Location: src/psd2svg/
The public API consists of:
SVGDocument- Main class for working with SVG documentsconvert()- Convenience function for simple conversionssvg_utils- SVG manipulation utilitiesimage_utils- Image encoding/decoding utilitieseval.py- Quality evaluation utilities for testing
Core Converter Layer
Location: src/psd2svg/core/
The core converter uses multiple inheritance with specialized mixins:
class Converter(
AdjustmentConverter,
LayerConverter,
PaintConverter,
ShapeConverter,
TextConverter,
EffectConverter,
):
"""Main converter class combining all converter mixins."""
Converter Mixins:
AdjustmentConverter(adjustment.py) - Handles adjustment layersEffectConverter(effects.py) - Processes layer effectsLayerConverter(layer.py) - Core layer conversion logicShapeConverter(shape.py) - Converts vector shapesPaintConverter(paint.py) - Handles fill and stroke patternsTextConverter(text.py) - Processes text layers
Supporting Modules:
base.py- Base converter classcolor_utils.py- Color conversion utilitiesconstants.py- Constants and enumsconverter.py- Main converter classcounter.py- ID generation for SVG elementsfont_mapping.py- Font mapping functionalityfont_utils.py- Font utilitiesgradient.py- Gradient conversion logictypesetting.py- Text typesettingwindows_fonts.py- Windows font resolution_font_mapping_data.py- Font mapping data
Rasterizer Layer
Location: src/psd2svg/rasterizer/
Provides SVG to raster image conversion:
base_rasterizer.py- Abstract base class defining the interfaceresvg_rasterizer.py- Default, fast production rasterizer (resvg-py)playwright_rasterizer.py- Optional browser-based renderer (better SVG 2.0 support)
Code Quality Standards
Type Hints
The project requires full type annotation coverage:
from PIL import Image
from typing import Optional
def process_image(
image: Image.Image,
quality: int = 95,
format: str = "png",
) -> bytes:
"""Process image and return bytes."""
...
All public APIs and internal functions must have type hints. Use mypy to verify:
uv run mypy src/
Docstrings
Use Google-style docstrings for all public functions and classes:
def convert(
input_path: str,
output_path: str,
embed_images: bool = True,
) -> None:
"""Convert PSD file to SVG.
Args:
input_path: Path to input PSD file.
output_path: Path to output SVG file.
embed_images: Whether to embed images as data URIs.
Returns:
None
Raises:
FileNotFoundError: If input file doesn't exist.
ValueError: If file format is invalid.
"""
...
Code Style
Follow PEP 8 guidelines
Use ruff for linting and formatting
Maximum line length: 88 characters (Black-compatible)
Use meaningful variable names
Keep functions focused and small
Testing
Write tests for all new features
Maintain or improve code coverage
Test edge cases and error conditions
Use pytest fixtures for common setup
import pytest
from psd2svg import SVGDocument
def test_svg_document_creation():
"""Test SVGDocument creation."""
# Arrange
svg_string = '<svg>...</svg>'
# Act
document = SVGDocument.load(svg_string, {})
# Assert
assert document is not None
Contributing
Workflow
Fork the repository on GitHub
Clone your fork locally
Create a feature branch:
git checkout -b feature/my-featureMake your changes with tests and documentation
Run quality checks:
uv run ruff format src/ tests/ uv run ruff check src/ tests/ uv run mypy src/ tests/ uv run pytest
Commit your changes:
git commit -m "Add my feature"Push to your fork:
git push origin feature/my-featureOpen a Pull Request on GitHub
Pull Request Guidelines
Provide a clear description of the changes
Reference related issues
Include tests for new functionality
Update documentation as needed
Ensure all CI checks pass
Keep PRs focused on a single feature/fix
Commit Messages
Use clear, descriptive commit messages:
Add support for gradient patterns
- Implement linear gradient conversion
- Add radial gradient support
- Add tests for gradient patterns
- Update documentation
Code Review
All contributions go through code review:
Address reviewer feedback promptly
Be open to suggestions and improvements
Discuss significant changes before implementation
Adding New Features
Adding a Converter Feature
To add support for a new Photoshop feature:
Identify the appropriate converter mixin (Layer, Shape, Effect, etc.)
Add conversion logic to the mixin
Add helper methods if needed
Write tests for the new feature
Update documentation
Example structure:
# In src/psd2svg/core/effects.py
class EffectConverter(BaseConverter):
def convert_new_effect(
self,
layer: Layer,
effect_data: dict,
) -> str:
"""Convert new effect to SVG.
Args:
layer: The layer with the effect.
effect_data: Effect parameters.
Returns:
SVG filter element string.
"""
# Implementation here
...
Working with Rasterizers
The package provides two rasterization backends:
ResvgRasterizer (default):
from psd2svg.rasterizer import ResvgRasterizer
# Create rasterizer instance
rasterizer = ResvgRasterizer(dpi=96)
# Rasterize from string
svg_string = '<svg>...</svg>'
image = rasterizer.from_string(svg_string)
image.save('output.png')
# Rasterize from file
image = rasterizer.from_file('input.svg')
image.save('output.png')
PlaywrightRasterizer (optional, requires browser optional dependency):
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')
Debugging
Common Issues
Import errors:
# Ensure dependencies are installed
uv sync
Type checking errors:
# Run mypy with verbose output
uv run mypy --verbose src/
Test failures:
# Run specific test with output
uv run pytest -vv tests/test_name.py::test_function
Development Tips
Use the Python REPL for interactive testing:
uv run python
Test with sample PSD files in
tests/directoryCheck SVG output in a browser to verify rendering
Use debugger for complex issues:
import pdb; pdb.set_trace()
Resources
Release Process
For maintainers, this project follows a pull request workflow for all changes to the main branch:
# 1. Create release branch
git checkout -b release/v0.10.0
# 2. Update version and changelog
# - Edit version in pyproject.toml
# - Update CHANGELOG.md with release notes
# 3. Sync dependencies to update lock file
uv sync
# 4. Commit and push
git add pyproject.toml CHANGELOG.md uv.lock
git commit -m "Prepare release v0.10.0"
git push -u origin release/v0.10.0
# 5. Create PR for release
gh pr create --title "Release v0.10.0" --body "Release notes..."
# 6. After PR is merged to main, create and push tag
git checkout main
git pull origin main
git tag v0.10.0
git push origin v0.10.0
Note: GitHub Actions automatically builds and publishes to PyPI via OIDC when a version tag is pushed (.github/workflows/release.yml).
Manual publishing is not necessary as the release workflow handles this automatically.
Getting Help
Issues: Report bugs and ask questions on GitHub Issues
Documentation: Refer to psd2svg.readthedocs.io
Security: Report vulnerabilities privately following SECURITY.md