mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-02 22:41:47 -06:00
M2: both ends of a /Decode array have to be numbers to invert an export (#5678)
An end that is missing or isn't a number sorted below any number, so [1], or [1 7 0 R] with 7 leading nowhere, read as inverted on a JPEG or a stencil mask, and so did an end too long to hold, which parses as infinite. Such an array now leaves the default mapping, as it does on the lossless path. Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
eafe9e2c0b
commit
036811368b
@@ -236,12 +236,16 @@ fn image(doc: &Document, s: &Stream) -> Result<(&'static str, Vec<u8>), String>
|
||||
let mut alpha = if mask { None } else { soft_mask(doc, s, w, h).or_else(|| stencil_mask(doc, s, w, h)) };
|
||||
let key = if mask { None } else { colour_key(doc, s) };
|
||||
// Keep the existing JPEG and stencil-mask Decode handling separate from lossless colour images.
|
||||
let endpoint = |value: Option<&Object>| value.map(|v| doc.resolve(v)).and_then(|v| v.as_f64());
|
||||
let endpoint = |value: Option<&Object>| value.map(|v| doc.resolve(v)).and_then(|v| v.as_f64()).filter(|v| v.is_finite());
|
||||
let inverted = (jpeg || mask)
|
||||
&& s.dict
|
||||
.get(b"Decode")
|
||||
.map(|d| doc.resolve(d))
|
||||
.and_then(|d| d.as_array().map(|a| endpoint(a.first()) > endpoint(a.get(1))))
|
||||
// Both ends have to be finite numbers, as for lossless images, or the default mapping stands.
|
||||
.and_then(|d| {
|
||||
let a = d.as_array()?;
|
||||
Some(endpoint(a.first())? > endpoint(a.get(1))?)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
// In a PDF a CMYK JPEG's samples are ink amounts unless /Decode inverts them, while image
|
||||
// viewers read a CMYK JPEG file as inverted (Adobe's convention): as is, it would show as
|
||||
|
||||
@@ -2237,12 +2237,14 @@ fn a_stencil_masks_decode_array_can_hold_references() {
|
||||
for decode in ["[0 5 0 R]", "[6 0 R 1]", "[6 0 R 5 0 R]"] {
|
||||
assert_eq!(export(decode), identity, "{decode}");
|
||||
}
|
||||
// A reference that only leads back to itself holds no number. The read ends, and the file comes
|
||||
// out one way round or the other.
|
||||
for decode in ["[7 0 R 0]", "[1 7 0 R]", "[8 0 R 0]", "[1 8 0 R]", "[7 0 R 8 0 R]"] {
|
||||
let file = export(decode);
|
||||
assert!(file == inverted || file == identity, "{decode}");
|
||||
// A reference that only leads back to itself holds no number, and an end that is missing or isn't
|
||||
// a number leaves the default mapping, whichever end it is.
|
||||
for decode in ["[7 0 R 0]", "[1 7 0 R]", "[8 0 R 0]", "[1 8 0 R]", "[7 0 R 8 0 R]", "[1]", "[1 /Name]", "[/Name 0]"] {
|
||||
assert_eq!(export(decode), identity, "{decode}");
|
||||
}
|
||||
// Equal ends aren't the two ways round either, and a number too long to hold reads as infinite.
|
||||
assert_eq!(export("[1 1]"), identity);
|
||||
assert_eq!(export(&format!("[{} 0]", "9".repeat(400))), identity);
|
||||
}
|
||||
|
||||
/// A mask of other proportions than its image is scaled to it, however far apart they are: the
|
||||
|
||||
Reference in New Issue
Block a user