A `trailer` marker inside an unreferenced stream followed by `(` and more
than 4 KiB of text made the lexer stop at the window's end, so a later
`/Encrypt` name anywhere in the file refused a plain file. The unbounded
scan read a string there and opened it. The past-the-window fallback now
applies only when no object starts in the window or the first one opens
a dictionary (`<<`, including one split by the window's end).
Regression test with `trailer(` and `trailer[` in a stream and a later
Encrypt name.
The pre-scan window could end inside a trailer key, so a decoy like
`/Encrypted` cut right after `/Encrypt` was taken for the Encrypt key
and an unencrypted file was refused. A window that ends mid-token now
ends before that token instead, and the past-the-window fallback reads
the name whole (a real `/Encrypt` cut as `/Encryp|t` is still refused).
Tests for that boundary, for a trailer with a long /ID, and for `#`
without two hex digits staying as written in a name.
The fallback searched for the literal bytes `/Encrypt`, so a long trailer
with the key written as `/Encr#79pt` (or any other #-escaped spelling) got
past it. It now finds the last name token that decodes to `Encrypt`, with
the lexer's own name decoding, and checks whether that token ends past the
window. It is still one linear pass per file. Longer names that only start
with `Encrypt` (`/Encrypted`) no longer count.
VmHWM is per process; the other tests in the binary ran on parallel threads
and their allocations failed the 64 MiB bound intermittently.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 16384x1 output stacks every grid row onto one pixel row, so a sane warp
visits ~100x its pixels; allow each triangle about two grid cells (CodeRabbit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An empty layers list cleared the composition's markers too (CodeRabbit);
the composition-marker menu uses comp.deleteAllMarkers instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>