Bug: export colors snap to pyembroidery built-in thread table, ignoring source hex #9

Closed
opened 2026-08-27 15:28:00 +00:00 by Mark · 0 comments
Owner

Summary

Round-trip read of exported PES/JEF shows the design colors mutated from the input:

Input PES round-trip JEF round-trip
#000000 #000000 (Black) #000000 (Black)
#FD5A1E (SF Giants orange) #FE370F (pyembroidery Vermilion) #FF6600 (pyembroidery Orange)

Root cause

app.py:256-261 constructs pyembroidery.EmbThread() and calls set_color(r, g, b) without attaching the catalog thread metadata (code, name, brand). pyembroidery writers snap colors to its built-in thread table when the underlying writer (PES/JEF) requires catalog references.

Suggested fix

When the request includes a thread_palette_index (already in StitchObject.palette_index), pass the matching EmbThread from the catalog — or accept the source hex as EmbThread.description / custom so the writer preserves it.

Reproduction

Digitize a 2-color image, export to PES, read back, observe thread color deltas:

import pyembroidery
p = pyembroidery.read_pes("out.pes")
for i, t in enumerate(p.threadlist):
    r, g, b = (t.color >> 16) & 0xFF, (t.color >> 8) & 0xFF, t.color & 0xFF
    print(f"[{i}] #{r:02X}{g:02X}{b:02X} {t.description!r}")

Severity

MEDIUM — visually noticeable for brand-matched colors (SF orange, Harvard crimson, etc.). Tests should assert color stability, not just stitch count.

## Summary Round-trip read of exported PES/JEF shows the design colors mutated from the input: | Input | PES round-trip | JEF round-trip | |-------|---------------|----------------| | `#000000` | `#000000` (Black) | `#000000` (Black) | | `#FD5A1E` (SF Giants orange) | `#FE370F` (pyembroidery Vermilion) | `#FF6600` (pyembroidery Orange) | ## Root cause `app.py:256-261` constructs `pyembroidery.EmbThread()` and calls `set_color(r, g, b)` without attaching the catalog thread metadata (code, name, brand). pyembroidery writers snap colors to its built-in thread table when the underlying writer (PES/JEF) requires catalog references. ## Suggested fix When the request includes a `thread_palette_index` (already in `StitchObject.palette_index`), pass the matching `EmbThread` from the catalog — or accept the source hex as `EmbThread.description` / `custom` so the writer preserves it. ## Reproduction Digitize a 2-color image, export to PES, read back, observe thread color deltas: ```python import pyembroidery p = pyembroidery.read_pes("out.pes") for i, t in enumerate(p.threadlist): r, g, b = (t.color >> 16) & 0xFF, (t.color >> 8) & 0xFF, t.color & 0xFF print(f"[{i}] #{r:02X}{g:02X}{b:02X} {t.description!r}") ``` ## Severity MEDIUM — visually noticeable for brand-matched colors (SF orange, Harvard crimson, etc.). Tests should assert color stability, not just stitch count.
Mark 2026-08-30 00:23:15 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Mark/easystitch-service#9
No description provided.