Convert images to hatched SVG files for Cricut pen plotters
Project description
hatchsvg
Turn raster images into pen-plotted hatched SVGs. Optimized for Cricut, Axidraw, and any pen plotter that reads SVG.
What's new in 2.0.0
- Package renamed to
hatchsvg(waspng2svg). Matches the repo name. See the migration guide in CHANGELOG.md —pip install hatchsvg,import hatchsvg,hatchsvg photo.jpg out.svg. - All v1.2.0 features still ship:
--preview,--split-layers,--optimize-travel,--hatch-angles, plus 5 inline examples in--help. - Pure Python — no C compiler required. Works on every platform pip supports.
What it does
hatchsvg converts raster images (PNG, JPG, WebP, BMP, GIF, TIFF) into
hatched SVG vector files. Each color in the image becomes a separate SVG
layer, and each layer is filled with parallel hatch lines sized to match a
physical marker set. The output is optimized for Cricut pen plotters and
similar pen-plotting machines — it includes continuous serpentine paths, arc
smoothing at row ends, and connected-component chaining to minimize pen
lifts and ringing artifacts.
Quickstart
pip install hatchsvg
hatchsvg photo.jpg drawing.svg --preset portrait
That's it. Open drawing.svg in Inkscape, Cricut Design Space, or any
vector editor. The output uses Inkscape-compatible layer names so each color
is selectable individually.
No SVG file? Try it on the included sample:
hatchsvg examples/scene.png /tmp/scene.svg --preset fast
Why use hatchsvg?
| Problem with naive "image → SVG" converters | How hatchsvg solves it |
|---|---|
| Tracer output is dense, jittery, and pen plotter hates it. | Uses scanline hatching at a configurable line step — predictable pen motion. |
| Pen lifts between every line segment cause visible artifacts on Cricut. | Serpentine continuous paths chain rows together (zigzag motion). |
| Sharp 180° U-turns vibrate the pen carriage. | Arc smoothing at row-end reversals (configurable radius). |
| Color matching to physical markers is hard. | Built-in Crayola and Jot marker palettes; bring your own JSON. |
| Re-rendering with slightly different settings breaks reproducibility. | --save-session writes a JSON snapshot; --use-session replays it bit-for-bit. |
Comparison with similar tools
| Tool | Input | Output | Pen-plotter aware | Marker palettes |
|---|---|---|---|---|
| hatchsvg | Raster | Hatched SVG (per-color layers) | Yes — serpentine + arc smoothing + chain components | Yes — Crayola, Jot, custom JSON |
potrace |
Bitmap | Smooth traced paths | No — fills entire region | No |
vtracer |
Bitmap | Smooth traced paths | Partial | No |
inkscape --trace-bitmap |
Bitmap | Smooth traced paths | No | No |
stipplegen, penkit, etc. |
Raster | Various pen-plotter art | Yes (each is its own niche) | No |
hatchsvg is the right choice when you want stylized, low-pen-lift,
marker-accurate output rather than a photo-faithful trace.
Presets
Presets are named combinations of parameters tuned for common use cases.
Apply one with --preset NAME. You can still override any individual flag
on top of the preset.
| Preset | Use case |
|---|---|
portrait |
Photographic portraits, faces, soft gradients. Tight hatch + arc smoothing. |
logo |
Brand marks, flat colors, clean edges. Separate outline for crisp boundaries. |
line-art |
Pencil sketches, ink drawings, fine line illustrations. Low min-pixels preserves thin strokes. |
photo |
Detailed photos, landscapes, complex gradients. High palette + fine hatch. |
sketch |
Hand-drawn rough aesthetic. Wider hatch + visible U-turns. |
fast |
Quick preview. Few colors, wide hatch, no optimization. Renders in seconds. |
hatchsvg drawing.png out.svg --preset portrait
hatchsvg drawing.png out.svg --preset logo --max-palette 4 # override one flag
See hatchsvg --help for full preset descriptions.
Installation
pip install hatchsvg
Pure Python: hatchsvg has zero C extensions. No compiler needed, no
potracer/pypotracebuild failures. Works on Windows, macOS, Linux, ARM, anywhere Python 3.11+ runs.
Optional extras:
pip install hatchsvg[plot] # progress bars (rich) + scipy for component chaining
For development:
git clone https://github.com/Veedubin/hatchsvg
cd hatchsvg
python -m venv .venv
.venv/bin/pip install -e ".[dev,plot]"
CLI Reference
hatchsvg [OPTIONS] INPUT OUTPUT_SVG
| Flag | Default | Description |
|---|---|---|
--preset NAME |
(none) | Apply a named preset (see table above). |
--max-palette N |
12 | Maximum number of colors in the quantized output. |
--line-step N |
4 | Spacing between hatch lines (pixels). Lower = denser. |
--alpha-threshold N |
10 | Pixels with alpha below this are treated as transparent. |
--min-pixels N |
200 | Minimum pixel count for a layer to be rendered. |
--stroke-width N |
auto | Stroke width for hatch lines (uses marker tip_width_mm if a palette is set). |
--outline-width N |
auto | Stroke width for outline paths. |
--no-skip-background |
(off) | Include the background color as a layer. |
--white-medium |
(off) | Use white as a medium color instead of skipping near-white. |
--paper-white-soft N |
20 | Soft threshold for "paper white" detection. |
--scale N |
1.0 | Scale factor for the output SVG. |
--separate-outline |
(off) | Generate outline paths as a separate SVG group. |
--palette-file FILE |
(none) | Path to a JSON marker palette. |
--naming-mode {inkscape,flat} |
inkscape | Layer naming convention. |
--continuous-paths |
(off) | Generate serpentine paths (huge pen-lift reduction). |
--arc-radius N |
0.0 | Arc radius at U-turns (0 = disabled; try 3-5). |
--save-session |
(off) | Write a *.session.json for reproducibility. |
--use-session FILE |
(none) | Replay a previous session (overrides all flags). |
--preview |
(off) | Open output SVG in default viewer after render. |
--split-layers |
(off) | Write one SVG file per color layer. |
--optimize-travel |
(off) | Reorder layers to minimize pen travel distance. |
--hatch-angles ANGLES |
(none) | Comma-separated hatch angles per layer (degrees). |
--progress |
(off) | Show Rich progress bars (requires pip install hatchsvg[plot]). |
--stats |
(off) | Show processing statistics on completion. |
--version |
Print the version and exit. |
Cookbook
Basic conversion
hatchsvg photo.jpg drawing.svg
Recommended for Cricut pen plotters
The two flags that matter most:
hatchsvg photo.jpg drawing.svg --continuous-paths --arc-radius 5
--continuous-pathschains rows into a single zigzag, eliminating the thousands of micro-pen-lifts that cause "ringing" artifacts.--arc-radius 5smooths the 180° U-turns with arc commands so the pen carriage doesn't slam to a stop.
Use a bundled marker palette
hatchsvg photo.jpg drawing.svg --palette-file color_palettes/crayola_10ct_fine_line_classic.json
Bundled palettes: crayola_10ct_fine_line_classic.json and
jot_20ct_washable_fineline.json.
Reproducible output
# First run: save the exact config used
hatchsvg photo.jpg drawing.svg --preset portrait --save-session
# Later: replay the exact same config
hatchsvg photo.jpg drawing2.svg --use-session drawing.svg.session.json
Multi-format input
hatchsvg scan.webp out.svg # WebP
hatchsvg photo.bmp out.svg # BMP
hatchsvg photo.tiff out.svg # TIFF
hatchsvg anim.gif out.svg # GIF (uses first frame)
Show processing stats
hatchsvg photo.jpg out.svg --progress --stats
Outputs a Rich table with image dimensions, color count, layer count, pen-lift reduction percentage, and estimated plot time savings.
Project Structure
hatchsvg/
├── src/
│ └── hatchsvg/
│ ├── __init__.py # __version__
│ ├── __main__.py # python -m hatchsvg
│ ├── core.py # Algorithm (hatching, color matching, SVG emission)
│ ├── cli.py # CLI + friendly error handling
│ └── presets.py # Named presets (portrait, logo, line-art, photo, sketch, fast)
├── tests/
│ ├── unit/ # 51 unit tests (segments, color, palette, session, hatch, presets)
│ ├── integration/ # 9 integration tests (golden file + CLI subprocess)
│ ├── fixtures/ # Golden SVG for regression
│ └── conftest.py
├── examples/ # Sample input/output pairs (4 presets × 1 image)
├── color_palettes/ # Bundled Crayola + Jot palettes
├── .github/workflows/ci.yml # CI: ruff + pytest on Linux+macOS × 3.11/3.12/3.13
├── pyproject.toml # Build + project config
├── LICENSE # MIT
├── CHANGELOG.md # Version history
├── REVIEW.md # Audit + release plan
├── PLAN.md # v1.0.0 implementation plan
└── README.md # This file
Tech Stack
- Python 3.11+ (uses PEP 604 unions, tomllib, modern type hints)
- NumPy — image array processing and color quantization
- Pillow — image decoding (PNG, JPG, WebP, BMP, GIF, TIFF)
- dataclasses —
RenderParams,LayerResult,StrokeStyle - Rich (optional) — progress bars and statistics table
- scipy (optional) — connected component detection for pen-lift reduction
Development
# Run all tests with coverage
.venv/bin/pytest
# Lint
.venv/bin/ruff check src tests
.venv/bin/ruff format --check src tests
# Build a wheel for local testing
.venv/bin/python -m build
See PLAN.md for the v1.0.0 implementation plan and REVIEW.md for the comprehensive audit, feature roadmap, and 30-day release timeline.
Compatibility
- Python: 3.11, 3.12, 3.13 (CI matrix covers all three on Linux + macOS)
- Input formats: PNG, JPG, JPEG, WebP, BMP, GIF, TIFF, TIF
- Output: SVG 1.1 with Inkscape-compatible layer names
- Consumers: Inkscape, Cricut Design Space, Illustrator, browser preview
License
MIT — see LICENSE.
Credits
Algorithm extracted from the original hatchsvg.py script. Bundled marker
palettes (Crayola, Jot) are trademarks of their respective owners and are
included only as practical examples.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hatchsvg-2.0.0.tar.gz.
File metadata
- Download URL: hatchsvg-2.0.0.tar.gz
- Upload date:
- Size: 2.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae753b498e159deebea6d9ce6c3e3aceacae3a9fcfa144013a277ed0e781f5bc
|
|
| MD5 |
a33e973b1469958e258702ad11ce0dda
|
|
| BLAKE2b-256 |
3229ec887615567053a18b9113f726752b28f30d939670702bfd7c7fa00e059c
|
Provenance
The following attestation bundles were made for hatchsvg-2.0.0.tar.gz:
Publisher:
workflow.yml on Veedubin/hatchsvg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hatchsvg-2.0.0.tar.gz -
Subject digest:
ae753b498e159deebea6d9ce6c3e3aceacae3a9fcfa144013a277ed0e781f5bc - Sigstore transparency entry: 1860700791
- Sigstore integration time:
-
Permalink:
Veedubin/hatchsvg@2ea761fc7c5f6a420dcfaf4fd92fe43addcf8a81 -
Branch / Tag:
refs/heads/v1.0.0-release - Owner: https://github.com/Veedubin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@2ea761fc7c5f6a420dcfaf4fd92fe43addcf8a81 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file hatchsvg-2.0.0-py3-none-any.whl.
File metadata
- Download URL: hatchsvg-2.0.0-py3-none-any.whl
- Upload date:
- Size: 30.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
871f3f0f7092d04e3f10bc6f4ae12d585678b377e4b6faaab5e4563ec6984d36
|
|
| MD5 |
f1555e435ebdd2a1962f75502e12f00e
|
|
| BLAKE2b-256 |
ba83348fc17deccdbf24f4bdbbe988e6dd6e289c393084444165c359f04dc9ba
|
Provenance
The following attestation bundles were made for hatchsvg-2.0.0-py3-none-any.whl:
Publisher:
workflow.yml on Veedubin/hatchsvg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hatchsvg-2.0.0-py3-none-any.whl -
Subject digest:
871f3f0f7092d04e3f10bc6f4ae12d585678b377e4b6faaab5e4563ec6984d36 - Sigstore transparency entry: 1860701140
- Sigstore integration time:
-
Permalink:
Veedubin/hatchsvg@2ea761fc7c5f6a420dcfaf4fd92fe43addcf8a81 -
Branch / Tag:
refs/heads/v1.0.0-release - Owner: https://github.com/Veedubin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@2ea761fc7c5f6a420dcfaf4fd92fe43addcf8a81 -
Trigger Event:
workflow_dispatch
-
Statement type: