psdparse 0.8.0__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {psdparse-0.8.0 → psdparse-0.9.0}/PKG-INFO +1 -1
- {psdparse-0.8.0 → psdparse-0.9.0}/docs/PYTHON_API.md +3 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/docs/ROADMAP.md +16 -2
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psddata.h +626 -608
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdengine.cpp +274 -7
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdengine.h +49 -0
- psdparse-0.9.0/psdparse/psdfile.cpp +821 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdfile.h +101 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdimage.cpp +1478 -1477
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdparse.cpp +690 -649
- {psdparse-0.8.0 → psdparse-0.9.0}/pyproject.toml +1 -1
- {psdparse-0.8.0 → psdparse-0.9.0}/python/psdparse_module.cpp +18 -54
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_edit.py +45 -0
- psdparse-0.8.0/psdparse/psdfile.cpp +0 -373
- {psdparse-0.8.0 → psdparse-0.9.0}/.gitignore +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/CMakeLists.txt +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/CMakePresets.json +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/LICENSE +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/Makefile +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/README.md +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/docs/ARCHITECTURE.md +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/docs/SUPPORT.md +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/examples/README.md +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/examples/composite.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/examples/extract_layers.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/examples/variations.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/CMakeLists.txt +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/bmp.cpp +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psd_cli.cpp +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdbase.h +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psddesc.cpp +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psddesc.h +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdlayer.cpp +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdlayer.h +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdparse.h +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdresource.cpp +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdresource.h +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdwrite.cpp +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/psdparse/psdwrite.h +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/python/CMakeLists.txt +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/conftest.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_colormodes.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_comps.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_create.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_descriptors.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_edit_effects.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_edit_mask.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_edit_mask_pixels.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_edit_name.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_edit_pixels.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_edit_run_style.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_edit_text.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_header.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_images.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_layers.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_mask_extras.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_merged.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_metadata.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_resources.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_save.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tests/test_text.py +0 -0
- {psdparse-0.8.0 → psdparse-0.9.0}/tools/psd_export.py +0 -0
|
@@ -331,6 +331,9 @@ new_i = p.copy_layer_from(src, j, dest_index=-1) # copy layer j from another PS
|
|
|
331
331
|
```
|
|
332
332
|
|
|
333
333
|
Notes:
|
|
334
|
+
- `duplicate_layer` / `copy_layer_from` assign the copy a **fresh `layer_id`**
|
|
335
|
+
(max existing lyid + 1, like Photoshop) so layer IDs stay unique within the
|
|
336
|
+
document; the `lyid` additional-info block is rewritten on save.
|
|
334
337
|
- `delete_layer` / `duplicate_layer` on a single layer are exact. Deleting **one
|
|
335
338
|
half of a group's FOLDER/HIDDEN divider pair unbalances the group** — delete
|
|
336
339
|
whole groups (both dividers + contents) for clean nesting. (Unbalanced results
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
For a feature-by-feature account of what is and isn't supported today, see
|
|
4
4
|
[SUPPORT.md](SUPPORT.md). This file tracks planned work.
|
|
5
5
|
|
|
6
|
-
## Current state (2026-08-
|
|
6
|
+
## Current state (2026-08-12, v0.8.1)
|
|
7
7
|
|
|
8
8
|
- ✅ Pure C++17 parser (no Boost)
|
|
9
9
|
- ✅ mmap + StreamReader / Source abstraction
|
|
10
10
|
- ✅ Python bindings (pybind11)
|
|
11
|
-
- ✅ pytest regression suite (
|
|
11
|
+
- ✅ pytest regression suite (140 tests)
|
|
12
12
|
- ✅ Python composite recipes (`examples/`) + `set_merged_image`; layer comps exposed (`layer.comp_states`)
|
|
13
13
|
- ✅ Round-trip PSD save (byte-identical)
|
|
14
14
|
- ✅ Edit & save: structure / pixels / mask / parameters / effects / text / new-from-scratch (E1–E6, byte-exact re-serialization)
|
|
@@ -91,6 +91,20 @@ Editing text content/style means writing the `TySh` block back: re-serializing t
|
|
|
91
91
|
|
|
92
92
|
## Other future work
|
|
93
93
|
|
|
94
|
+
- ✅ **`duplicate_layer` assigns a fresh `lyid`.** *Done 2026-08-12.* The
|
|
95
|
+
duplicated layer used to copy the source's `lyid` (layer ID) verbatim, so the
|
|
96
|
+
output PSD contained duplicate layer IDs — Photoshop itself assigns a *new*
|
|
97
|
+
ID when duplicating. Downstream tools that use `lyid` as a persistent layer
|
|
98
|
+
identity (e.g. elements_console's uitool reference resolution) then saw
|
|
99
|
+
ambiguous matches. Fix (`psdfile.cpp`): `assignFreshLayerId` sets
|
|
100
|
+
`layerId = max existing lyid + 1` and replaces (or appends) the `lyid`
|
|
101
|
+
additional-info entry on the copy, flipping `useRawBytes=false` so save
|
|
102
|
+
re-serializes it; applied in both `duplicateLayer` and `copyLayerFrom` (the
|
|
103
|
+
latter picks an ID unique within the *destination* document). `add_layer`
|
|
104
|
+
already picked max+1 — verified. Tests (`tests/test_edit.py`): duplicate →
|
|
105
|
+
all `layer.layer_id` distinct → save → reload → still distinct; double
|
|
106
|
+
duplicate cross-checked via psd-tools tagged blocks; `copy_layer_from`
|
|
107
|
+
uniqueness in the destination.
|
|
94
108
|
- ✅ **Text layer content extraction (`TySh` type-tool additional info).** *Done 2026-07-27.* `lay.text` returns `{"text", "orientation", "justification", "transform", "runs":[{"length","font","size_px","color","tracking","kerning","auto_kerning"}]}` (or `None` for non-text layers). Implementation: `psddesc` now reads `tdta` raw data (length-prefixed), `loadLayerTypeTool` (`psdlayer.cpp`) parses the `TySh` header + text descriptor, and `psdengine.cpp` parses the embedded Adobe *EngineData* mini-language (FontSet / StyleRun / ParagraphRun) into per-run styling. Validated against `tests/data/fontsample.psd` (multi-font/size/color, **vertical**, emoji, tracking) — see `tests/test_text.py`.
|
|
95
109
|
|
|
96
110
|
**Deferred (need targeted sample PSDs, next turn):**
|