mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 12:24:54 +00:00
Merge pull request #6795 from dotCooCoo/fix/pdfx-check-skips-strings
PDF/X: names inside a string or a comment no longer fail the standard check
This commit is contained in:
+35
-12
@@ -211,23 +211,46 @@ pub(crate) fn verify(pdf: &[u8], standard: Standard) -> Result<(), PdfError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `pdf` without its streams' data (dictionaries and the rest of the syntax), so that what is
|
||||
/// looked for there isn't found in image or font bytes.
|
||||
/// `pdf` without its streams' data, the text of its literal strings and its comments (dictionaries
|
||||
/// and the rest of the syntax), so that what is looked for there isn't found in image or font
|
||||
/// bytes, or in a string such as the file identifier, whose base64 hash can hold `/BM`.
|
||||
fn without_streams(pdf: &[u8]) -> Vec<u8> {
|
||||
let mut out = Vec::with_capacity(pdf.len().min(1 << 20));
|
||||
let mut at = 0;
|
||||
while let Some(s) = find(pdf, b"stream", at) {
|
||||
let data = s + b"stream".len();
|
||||
// `endstream` itself, or a stream keyword that isn't one (inside a name or string).
|
||||
if (s >= 3 && pdf.get(s - 3..s) == Some(b"end")) || !pdf.get(data).is_some_and(|b| matches!(b, b'\r' | b'\n')) {
|
||||
out.extend_from_slice(pdf.get(at..data).unwrap_or_default());
|
||||
at = data;
|
||||
continue;
|
||||
while let Some(&b) = pdf.get(at) {
|
||||
match b {
|
||||
// A literal string: parentheses nest, and a backslash escapes the next byte.
|
||||
b'(' => {
|
||||
out.extend_from_slice(b"()");
|
||||
let mut depth = 0usize;
|
||||
at += 1;
|
||||
while let Some(&c) = pdf.get(at) {
|
||||
at += 1;
|
||||
match c {
|
||||
b'\\' => at += 1,
|
||||
b'(' => depth += 1,
|
||||
b')' if depth == 0 => break,
|
||||
b')' => depth -= 1,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// A comment, to the end of its line.
|
||||
b'%' => at += pdf.get(at..).unwrap_or_default().iter().take_while(|c| !matches!(c, b'\r' | b'\n')).count(),
|
||||
// A stream keyword (not `endstream`, and followed by its end of line): its data is left out.
|
||||
b's' if pdf.get(at..at + 6) == Some(b"stream")
|
||||
&& !(at >= 3 && pdf.get(at - 3..at) == Some(b"end"))
|
||||
&& pdf.get(at + 6).is_some_and(|c| matches!(c, b'\r' | b'\n')) =>
|
||||
{
|
||||
out.extend_from_slice(b"stream");
|
||||
at = find(pdf, b"endstream", at + 6).unwrap_or(pdf.len());
|
||||
}
|
||||
_ => {
|
||||
out.push(b);
|
||||
at += 1;
|
||||
}
|
||||
}
|
||||
out.extend_from_slice(pdf.get(at..data).unwrap_or_default());
|
||||
at = find(pdf, b"endstream", data).unwrap_or(pdf.len());
|
||||
}
|
||||
out.extend_from_slice(pdf.get(at..).unwrap_or_default());
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
@@ -224,3 +224,23 @@ fn verify_finds_what_each_standard_forbids() {
|
||||
assert!(matches!(verify(&file(font), Standard::PdfX4), Err(PdfError::Unsupported(m)) if m.contains("fonts")));
|
||||
assert!(verify(&file("<</Type/FontDescriptor/FontFile2 4 0 R>>"), Standard::PdfX4).is_ok());
|
||||
}
|
||||
|
||||
/// Names inside a string or a comment are text, not syntax: a file identifier, whose base64 hash
|
||||
/// can hold `/BM` or `/Lab`, does not make a file break its standard.
|
||||
#[test]
|
||||
fn verify_skips_names_in_strings_and_comments() {
|
||||
use crate::pdfx::verify;
|
||||
let file = |body: &str| format!("%PDF-1.3\n%/BM/Multiply\n1 0 obj\n{body}\nendobj\n").into_bytes();
|
||||
for ok in [
|
||||
"<</Size 2/ID[(q/BM+Zt1kR0x/Lab=) (q/BM+Zt1kR0x/Lab=)]>>",
|
||||
"<</Title(a\\)/SMask 5 0 R)/Subject(a(b)/ca 0.5)>>",
|
||||
"<</Title(stream\n/BM/Multiply\nendstream)>>",
|
||||
] {
|
||||
for standard in [Standard::PdfX1a, Standard::PdfX3] {
|
||||
assert!(verify(&file(ok), standard).is_ok(), "{standard:?}: {ok}");
|
||||
}
|
||||
}
|
||||
// A name after a string is still read.
|
||||
let after = "<</Title(a(b)c)/BM/Multiply>>";
|
||||
assert!(matches!(verify(&file(after), Standard::PdfX3), Err(PdfError::Unsupported(m)) if m.contains("blend modes")));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user