gamut-png polices a text chunk's keyword against §11.3.3.1 and reports every deviation
through MetadataNotice. The iCCP profile name has, word for word, the same rules — and
gets none of that machinery. §11.3.2.3:
Profile name — 1-79 bytes (character string) … Profile names shall contain only printable
Latin-1 characters and spaces (only code points 0x20-7E and 0xA1-FF are allowed). Leading,
trailing, and consecutive spaces are not permitted.
Compare §11.3.3.1's keyword clause, which keyword_verdict in crates/gamut-png/src/ancillary.rs
implements: same repertoire, same 1–79 bytes, same spacing prohibition. The writer, by contrast,
is three lines in Ancillary::write_pre_plte:
let mut data = name.clone().into_bytes();
data.push(0); // null separator
String::into_bytes is UTF-8, and nothing checks length, emptiness or an embedded null.
Executed
Encoding a 2×2 RGB image with PngEncoder::with_icc_profile(name, profile) and reading the
output back with gamut_png::metadata, on fix/483-png-metadata-preservation:
| Profile name |
Read back as |
Notices |
œrofile (U+0153) |
Å\u{93}rofile — mojibake, the UTF-8 pair C5 93 read as two Latin-1 bytes |
none |
"" (empty) |
profile gone |
none |
"p" × 80 |
profile gone |
none |
pro\0file |
profile gone |
none |
" profile" (leading space) |
" profile" — written verbatim, §11.3.2.3 forbids it |
none |
profile |
profile |
none |
The three "profile gone" rows are this crate's own reader rejecting what this crate's own writer
produced: split_keyword refuses a name field outside 1–79 bytes, and an embedded null makes the
byte after the split the compression method, which is then not 0. An ICC profile — the payload
whose loss silently changes what a viewer paints — disappears between a write and a read with
nothing said.
What would close this
The notice channel already exists and already has the vocabulary. Route the profile name through
the same verdict the keyword takes, with the same outcomes:
- outside Latin-1, or outside 1–79 bytes, or holding a null → drop the
iCCP chunk and report it
(the profile cannot be carried, and dropping it loudly beats dropping it silently);
- outside the printable repertoire, or with a leading/trailing/consecutive space → write verbatim
and report that the datastream is non-conforming per §15.3.1, exactly as TextKeywordRepertoire
and TextKeywordSpacing do.
MetadataNotice is #[non_exhaustive] with permanent append-only discriminants, so the new
variants are additive.
Why it was not fixed in #550
It is pre-existing (the iCCP writer predates that branch), it is unreachable through the
metadata carry that PR is about — a name read out of a file has already passed split_keyword,
so it is Latin-1 and 1–79 bytes by construction — and it is outside that PR's manifest. It is
filed here because it is the one other field the specification defines identically to the
keyword, and because the notice channel it needs now exists.
gamut-pngpolices a text chunk's keyword against §11.3.3.1 and reports every deviationthrough
MetadataNotice. TheiCCPprofile name has, word for word, the same rules — andgets none of that machinery. §11.3.2.3:
Compare §11.3.3.1's keyword clause, which
keyword_verdictincrates/gamut-png/src/ancillary.rsimplements: same repertoire, same 1–79 bytes, same spacing prohibition. The writer, by contrast,
is three lines in
Ancillary::write_pre_plte:String::into_bytesis UTF-8, and nothing checks length, emptiness or an embedded null.Executed
Encoding a 2×2 RGB image with
PngEncoder::with_icc_profile(name, profile)and reading theoutput back with
gamut_png::metadata, onfix/483-png-metadata-preservation:œrofile(U+0153)Å\u{93}rofile— mojibake, the UTF-8 pairC5 93read as two Latin-1 bytes""(empty)"p"× 80pro\0file" profile"(leading space)" profile"— written verbatim, §11.3.2.3 forbids itprofileprofileThe three "profile gone" rows are this crate's own reader rejecting what this crate's own writer
produced:
split_keywordrefuses a name field outside 1–79 bytes, and an embedded null makes thebyte after the split the compression method, which is then not 0. An ICC profile — the payload
whose loss silently changes what a viewer paints — disappears between a write and a read with
nothing said.
What would close this
The notice channel already exists and already has the vocabulary. Route the profile name through
the same verdict the keyword takes, with the same outcomes:
iCCPchunk and report it(the profile cannot be carried, and dropping it loudly beats dropping it silently);
and report that the datastream is non-conforming per §15.3.1, exactly as
TextKeywordRepertoireand
TextKeywordSpacingdo.MetadataNoticeis#[non_exhaustive]with permanent append-only discriminants, so the newvariants are additive.
Why it was not fixed in #550
It is pre-existing (the
iCCPwriter predates that branch), it is unreachable through themetadata carry that PR is about — a name read out of a file has already passed
split_keyword,so it is Latin-1 and 1–79 bytes by construction — and it is outside that PR's manifest. It is
filed here because it is the one other field the specification defines identically to the
keyword, and because the notice channel it needs now exists.