The position checks of the codepage guesser (letters after a character, a closing !,
the Cyrillic run around a letter) rescanned the rest of the name from every non-ASCII
character, so a hostile .atn or PSD with a 64 KiB step name of one letter kept Load
Actions busy for about three minutes. Each is now found once per name; the three
measured cases take 0.03 s together, and a regression test covers them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: Mac Roman "90°" alone has no ASCII letter, so its one windows-1251 letter made the name
"mostly Cyrillic" and the fallback showed "90Ў"; "¡Ánimo!" counted nothing for windows-1252 since
only ASCII letters were counted after ¡; and Mac Roman "20 °Celsius" tied with windows-1252
"20 ¡Celsius". A lone Cyrillic letter right after a digit never counts (a run after a digit, "5шт",
still does); the letters after a character include accented ones; and ¡ counts where it opens an
exclamation, two letters or more after it and the closing "!" later in the name, which the degree
sign before a unit never has.
Re-lands 47ee3f696, which went out under another PR's message by mistake and is reverted just
before this, with one test case less: without any hint a lone Mac Roman degree sign before an ASCII
letter is also a Big5 punctuation pair, a tie the fallback flags, so the Mac Roman cases keep their
Latin hints.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review: an empty layer gave bounds of 0 x 0, so the canvas took the drag from the Hand tool and
wrote offsets of up to ±4 layer widths from a short drag, for a gradient that paints nothing;
and the drag's one origin stayed when `selected` changed under a held press, so the next instance
moved from the previous origin and the whole travel since the press. `gradient_layer_bounds`
returns `None` for empty bounds, and `__gradientDrag` keeps the instance id, the origin and the
pointer total at that point, taken afresh when the id differs: offsets are the origin plus the
travel since.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review: "šuma" (0x9A u m a) read as Mac Roman "öuma", certain, where main tied it and read
windows-1252; and "¡Hola!" read as Mac Roman "°Hola!". The word-initial rule for windows-1252's
0x80–0x9F letters no longer asks for a capital (the file's own alphabet still decides: a file that
writes ö and never š reads the byte as Mac Roman ö), and ¡ counts as common text for windows-1252
where it opens a word of two letters or more (`opens_word`, in `score` and in the final
windows-1252/Mac Roman comparison), while Mac Roman's degree sign on the same byte, after a
number ("Rotation 90°") or before a single letter ("20 °C"), stays as it was.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review: "ŠKODA" (a capital before an ASCII capital) counted for nothing and could read as Mac
Roman "äKODA"; Mac Roman "Layer 90°é" read as windows-1251 "90ЎЋ", two Cyrillic letters right
after a number. The word-initial rule now takes an ASCII letter of either case after the capital,
and `cyrillic_word` treats a digit before the run as glue (a digit after still isn't: "Слой1").
Main's new fallback test tied Mac Roman "Ändern" with windows-1251, which this branch settles for
Mac Roman; it ties "éäé" with "ЋЉЋ" instead and checks "Ändern" is certain.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conflict in crates/engine/src/jobs.rs: main's save commit point (#5333,
OPEN/COMMITTED/CANCELLED phase) met the batch's cancel state
(CANCEL_REQUESTED/WORK_FINISHED). Both now share one atomic with a
COMMITTED bit; commit vs cancel and the batch's late-cancel guard each
stay one compare-and-swap. A Batch that refuses a repeated or late cancel
reports CancelRefused::Finishing ("finishing") in jobs.cancel's
notCancelled list, still {"cancelled": 0}. control-protocol.md documents both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A channel or merged image still in its source (an imported file's, or one
spilled to scratch) is copied to the output in 8 MiB pieces instead of being
read whole into memory first.
Resolve against main's save restructure (Services::save_file/SaveCtl, scratch-integrity checks,
ZipWriter::with_entries, exact encoded_len):
- PSD/PSB saves stream the file model through atomic_write_stream_progress; the length used for
progress and reservations is main's exact encoded_len (the PR's size_bound under-counted lazy
data); export_with/encode_with share checked_stages, so main's scratch checks (previews
included) still refuse unreadable scratch.
- Drop ZipWriter::for_entries in favour of main's with_entries.
- TIFF: take_from_psd moves layer data out of the export's own model (never the document);
body() reserves its exact length, failures are OutOfMemory (not LimitExceeded, which would
trigger the PSB retry).
- Streaming writer: AlreadyExists from the writer is a failed save, not a name collision.
- Remove dead Vec put_widened_rle; channel_data_len is test-only.
A rare windows-1252 capital starting a word counts unless the file's Unicode text writes the Mac Roman letter on that byte and never this one (état, über); Слой1 reads as a Cyrillic word. Tests.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A Cyrillic step name in a Latin-hinted set (Ёлка in a French set), a Cyrillic word beside a Latin one (Layer Слой) and a windows-1252 name starting with a capital Š or Ž (Škoda) all read as before; the Mac Roman cases of #10774 still read as Mac Roman. Tests for each.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With our fix-up 1f524c378b on top of the author's head 713aa92ba5. Resolved with main: the version check sits in main's parse_layout (it merged cleanly); doc comments combined; atn_contract no longer expects version 12 to be refused and pins that an empty version 12 set parses; one ATTRIBUTION row for all ATN fixtures; the fixture README states which files were encoded how; development.md no longer counts the fixtures.
Port the ten backend, automation and test paths from #5493 by
@jonathanwxh-cell, pinned at 43920ab780.
Keep its original CSH codec, bounded preset reader and protection tests.
The prerequisite's UI, translation and parity-document changes are excluded.
Adapt ShapePreset construction to retain the exact source identifier. Empty
identifiers remain anonymous presets; repeated identifiers resolve when their
geometry is identical and refuse resolution when it conflicts. Add one
import-to-ATN Shape Blur regression and document the source attribution.
Original contribution: https://github.com/storytold/photocraft/pull/5493
Source-only integration; compilation and combined runtime validation remain.
- #8714: a Camera Raw Filter step imports only with a process version
the PSD mapping has verified (PrVN/PrVe 5/184549376 or 6/251920384)
and a CrVe text; other steps stay kept steps that save back unchanged.
- #7642: the patternName fallback matches names only, so a pattern
whose id equals that text no longer wins. A step that names its
pattern only by patternName exports that name.
- #8327: the iso-2022-jp label is refused (its names are 7-bit escape
sequences that would pass through as ASCII).
- #10417: the Levels export gate also refuses endpoints of the wrong
shape (both ends text, a text gamma) and channels that aren't objects.
- #10459: a set paused at a step dialog keeps the rest of the set by
action name, so actions.move while the dialog is open neither plays a
foreign action nor skips one; "Played N steps" covers the whole Play
(steps before the pause and the dialog's own step); the suggested .atn
name is cut to 255 bytes at a character boundary; actions.list returns
every set, empty ones too; set_names dedups with a HashSet.
Co-authored-by: FinnNk <1424666+FinnNk@users.noreply.github.com>
Co-authored-by: Silvertree2010 <133535268+Silvertree2010@users.noreply.github.com>
Co-authored-by: regexer <3689390+regexer@users.noreply.github.com>
Co-authored-by: marshiyar <57055687+marshiyar@users.noreply.github.com>
Co-authored-by: hectorviov <1501713+hectorviov@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Stream PSD user and real masks into tile rows
* Correct streamed-mask fixture metadata and test lint issues
* Budget PSD pattern expansion and bound multichannel conversion
---------
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
The previous fix-up counted a letter glued to an ASCII letter as common
only when it was Latin. That turned ordinary windows-1251 names such as
"CMYKканал" or Ukrainian "Копiя" (Latin i) into a windows-1252 (or GBK)
reading of the whole set, with no warning, where the PR read them right.
Back to the PR's scoring for single-byte pages; the ¥ removal stays.
windows-1252 "Fläche m²" / "Preis £5" reading as windows-1251 is the
PR's own known limitation, now noted in the code. A Russian set plus
"CMYKканал" is pinned as windows-1251.
Co-authored-by: regexer <3689390+regexer@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mac Roman 0xA5 is the bullet, windows-1252 0xA5 is ¥: with ¥ counted in
the windows-1252 re-weigh, Mac "Ebene • Kopie" read as "Ebene ¥ Kopie".
Drop ¥; the other kept symbols are no letter or typographic mark in Mac
Roman.
windows-1251 has І і д where windows-1252 has ² ³ ä, so "Fläche m²" read
as "Flдche mІ" (already so in the PR head; main showed it right). In a
single-byte page, a letter glued to an ASCII letter now counts as common
only when it is a Latin letter. Real Cyrillic names ("Новый слой",
"Копия слоя 2", "Канал RGB") still read as windows-1251. "Preis £5"
(Ј between a space and a digit) still reads as windows-1251; documented.
Co-authored-by: regexer <3689390+regexer@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Bound PSD import pixel storage before decoding
* Include ZIP inflater workspace in PSD preflight
* PSD import: budget unknown-mode layers, stop after a fatal layer error, keep best-effort for non-allocation limits (#8593 review)
Conflicts with main's batch #9880:
- psd file.rs / layer.rs: #5287 (PSB by encoded size, RLE tables widened on
promotion) against #6583's lazy reader. Main's new widening and length code
read `.data`, which #6583 leaves empty while the bytes stay in the source;
it now goes through `encoded()` / `encoded_len()` (also the auto-merged
lines in `rle_widening`, `encoded_len` and `LayerRecord::write`), and the
lazy reader sets `write_as_psb: false`. New test
`a_lazy_psd_promotes_to_the_same_psb_as_an_eager_one`: a lazily read PSD
writes the same PSB bytes as an eager one; it fails with a `.data` read.
- direct_select.rs / vector_ui.rs: #9321's Type Path and #6353's hidden
Work Path both kept.
- io tests main.rs: both modules. scorecard.md regenerated (12 of 181);
ui-parity.md and gaps.md follow it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merged at the fix-up commit before its main merge (362947695d); the
conflicts with the batch are resolved here: psd_import.rs keeps both
sides' tests, tiff_layers.rs takes ImageData::new.
The lazy parse took any error from read_layer_info on a Layr/Lr16/Lr32
global block as a damaged block and dropped the layers. A failed read of
the source (PsdError::Read: a dropped network share, a file rewritten
while it opens) then let a 16/32-bit layered file open silently from its
merged image, with no damage warning, and a save replaced its layers.
A read error now fails the parse, as reading the whole file would.
The new test fails each read of a clean parse once, in turn, and checks
the parse either fails with the read error or matches from_bytes; with
the fix reverted, read 20 of the Lr16 sample drops the layers silently.
Found by CodeRabbit's review of the PR.
Co-authored-by: ptrinh <774083+ptrinh@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>