Seven readers were compared on synthetic files. Most draw the entries a
short table holds and black past them, and none draws an image whose
lookup is missing.
- Indexed::new keeps how many entries the table holds in full. Indices
at or past that are set to black after conversion, so DeviceCMYK,
ICCBased, Separation, DeviceN and Lab come out black too (zero bytes
gave white, or blue in Lab). Fill and stroke colours follow the same
rule. An incomplete last entry counts as missing.
- A lookup that is missing, null, a number, or a stream that does not
decode (or decodes to nothing although it holds data) is no table.
ColorSpace::new_allowing_tableless builds such a space marked
tableless (for the resource cache and ImageXObject::new only), and
the image is skipped with MalformedIndexedImage. A cs/CS colour in it
keeps the upstream DeviceGray fallback with ColorFallback. Every other
caller still gets None, as upstream.
- hival clamping and truncation are unchanged, and so is case C.
- The warning now reads "an image with a malformed Indexed colour table
may be drawn wrongly or not at all".
- vendor/README.md: the patch (60) row is one line again. 3275f853c
split it.
Tests: indexed_lookup.rs now has 25 tests, including one per base space
(DeviceGray, DeviceCMYK, Lab, Separation, DeviceN, and ICCBased N=4
drawn through its alternate). Against 6179cf795, 13 fail, including
every base space except DeviceGray, where zero is black. Against
b6f93a3ae, 19 fail.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
Mutation pass on patch (60):
- keying the resource colour-space cache with one key for every space
survived the earlier tests, which used one Indexed resource a page:
raster::tests::indexed_lookup_cache::two_indexed_resources_are_cached_apart
kills it;
- the ColorFallback report for a recovered space set with CS had no
test (only cs was tested): a stroke case in
a_fill_or_stroke_colour_in_a_short_table_is_padded_and_reported fails
with that report removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
Patch (60) skipped such an image. A reviewer's side-by-side tests on #4393
(seven cases against main and Chrome's viewer) showed the skip dropped
colours other readers draw, and regressed a real hival that main draws.
Indexed::new now recovers instead:
- a lookup table shorter than m x (hival + 1) bytes, missing, or not a
string or decodable stream is padded with zero bytes;
- a hival past 255 is taken as 255;
- a real hival is truncated toward zero (as upstream read it).
The space records that it was recovered. ImageXObject::new raises
MalformedIndexedImage from the space it built; a cs/CS colour in such a
space reports ColorFallback, as the upstream DeviceGray fallback did. A
hival below zero or not a number has no reading: the space falls back as
upstream, and the image reports the warning. Index values past hival
stay clamped (8.6.6.3).
The separate is_malformed_indexed check is gone, so no lookup table is
read twice: inline images naming an Indexed resource read its table once
through the resource cache (CodeRabbit, interpret/mod.rs:945). The read
count is exposed as InterpreterCache::indexed_lookup_reads.
Tests: indexed_lookup.rs rewritten as draw tests (17, cases A-G and
bounds); raster::tests::indexed_lookup_cache. On b6f93a3ae logic 14 of
the 17 fail (the three that pass are guards: full table, case C, device
name) and the cache test reads [21, 41] instead of [1, 1].
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
- render_warning_text (crates/automation/src/pages.rs, on main since this
branch was cut) matched RenderWarning exhaustively and did not build
with the new variant. It now names it, in the CLI's words, so
page_render and render_preview report the skip. A workspace search
finds no other match on RenderWarning outside pdfcraft-render.
- An Indexed lookup is a string or a stream (8.6.6.3). Indexed::new and
is_malformed_indexed tried it as a stream first; a failed stream read
of a reference consumed the reference, so a string lookup given by
reference was never found and the image was skipped. The lookup is now
read once and dispatched by type, in both places.
- hival is judged before the base: an out-of-range hival rules the space
out even when the base cannot be read (the table size still needs the
base, so a usable hival keeps today's handling there).
The size check is unchanged: hival is range-checked to 0..=255 before
(hival + 1) * m is formed with checked_add/checked_mul.
Tests (each seen failing with a fault injected): bounds at DeviceCMYK,
DeviceGray and hival 255 (exact and one short); hivals past u32/u64/any
integer, negative and fractional (with unchecked arithmetic and the range
widened the test panics with "attempt to multiply with overflow"); a
lookup string by reference (fails with the old stream-then-string read);
an unreadable base with hival 300; an undecodable lookup stream; and
page_render reporting the skip (without the new arm the crate does not
build, E0004).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv
Includes #4325#1952#4180#2529#4320#4805, preserving their original histories.
Keep paragraph alignment and hard/soft wrap metadata together; retain CCITT/downsampling/RGB ownership guards with bounded JBIG2 export and exact-copy symbol encoding.
Combined interaction regressions added. Validation pending.