mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-15 15:22:54 +00:00
fix(xfa): resolve clippy lints on main (#7354)
* fix(xfa): resolve clippy needless_update and field_reassign_with_default * fix(create,xfdf): resolve remaining clippy lints * fix(export): remove needless borrow in pptx and as_docx * fix(engine): resolve clippy lints in signature_image * fix(automation): resolve clippy lints in tests * fix(create): avoid clippy::unreachable in tests * chore(deny): allow IJG license for jpeg-encoder via craft-image-codecs
This commit is contained in:
1 parent
2524889a6b
commit
7d44e65412
17 files changed
+27
-71
No files matched your search
@@ -947,7 +947,7 @@ fn open_inspect_render_and_find() {
|
||||
let Content::Png { data, width, height } = &png[0] else { panic!("expected an image") };
|
||||
assert_eq!((*width, *height), (200, 300));
|
||||
assert_eq!(&data[..8], b"\x89PNG\r\n\x1a\n");
|
||||
let decoded = craft_image_codecs::decode(&data).unwrap();
|
||||
let decoded = craft_image_codecs::decode(data).unwrap();
|
||||
assert_eq!(decoded.dimensions(), (200, 300));
|
||||
}
|
||||
|
||||
@@ -7790,7 +7790,7 @@ fn typed_signatures_stay_upright_on_rotated_pages() {
|
||||
let Content::Png { data, .. } = &output[0] else { panic!() };
|
||||
let (width, height, pixels) = decode_rgba(data);
|
||||
assert_eq!((width, height), if page % 2 == 1 { (200, 300) } else { (300, 200) }, "page {page}");
|
||||
let dark: Vec<bool> = pixels.chunks_exact(4).map(|p| p[..3].iter().all(|v| *v < 128)).collect();
|
||||
let dark: Vec<bool> = pixels.as_chunks::<4>().0.iter().map(|p| p[..3].iter().all(|v| *v < 128)).collect();
|
||||
for y in [60, 140] {
|
||||
let mut b = [u32::MAX, u32::MAX, 0, 0];
|
||||
for py in 0..height {
|
||||
@@ -7847,7 +7847,7 @@ fn fill_sign_marks_stay_upright_on_rotated_pages() {
|
||||
let output = a.call("page_render", &json!({ "doc": doc, "page": page, "dpi": 144 })).unwrap();
|
||||
let Content::Png { data, .. } = &output[0] else { panic!() };
|
||||
let (width, height, pixels) = decode_rgba(data);
|
||||
let dark: Vec<bool> = pixels.chunks_exact(4).map(|p| p[..3].iter().all(|v| *v < 128)).collect();
|
||||
let dark: Vec<bool> = pixels.as_chunks::<4>().0.iter().map(|p| p[..3].iter().all(|v| *v < 128)).collect();
|
||||
// The line, 36 × 4 pt displayed, runs across.
|
||||
let mut b = [u32::MAX, u32::MAX, 0, 0];
|
||||
for y in 0..height {
|
||||
@@ -8389,7 +8389,7 @@ fn named_layer_verdicts_are_reported_and_rendered_through_tools() {
|
||||
let rendered = a.call("page_render", &json!({"doc": doc, "page": 1, "dpi": 72})).unwrap();
|
||||
let Content::Png { data, .. } = &rendered[0] else { panic!("expected a PNG") };
|
||||
let (_, _, pixels) = decode_rgba(data);
|
||||
let red = pixels.chunks_exact(4).filter(|p| p[..3].iter().zip([0xD7u8, 0x37, 0x3F]).all(|(a, b)| a.abs_diff(b) < 30)).count();
|
||||
let red = pixels.as_chunks::<4>().0.iter().filter(|p| p[..3].iter().zip([0xD7u8, 0x37, 0x3F]).all(|(a, b)| a.abs_diff(b) < 30)).count();
|
||||
assert!(red > 100, "the invalid state is drawn: {red} red pixels");
|
||||
ok(&mut a, "doc_save", json!({"doc": doc, "path": "saved.pdf", "full": false}));
|
||||
assert_eq!(std::fs::read(dir.join("saved.pdf")).unwrap(), original, "the drawn state never enters saves");
|
||||
@@ -8923,7 +8923,7 @@ fn a_one_bit_scan_stays_one_bit_and_exports() {
|
||||
assert_eq!(files.len(), 1, "{r}");
|
||||
let exported = std::fs::read(files[0]["path"].as_str().unwrap()).unwrap();
|
||||
let (_, _, rgba) = decode_rgba(&exported);
|
||||
let white: Vec<bool> = rgba.chunks_exact(4).map(|p| p[..3] == [255, 255, 255]).collect();
|
||||
let white: Vec<bool> = rgba.as_chunks::<4>().0.iter().map(|p| p[..3] == [255, 255, 255]).collect();
|
||||
let expected: Vec<bool> =
|
||||
(0..h as usize).flat_map(|y| (0..w as usize).map(move |x| (y, x))).map(|(y, x)| bits[y * stride + x / 8] & (0x80 >> (x % 8)) != 0).collect();
|
||||
assert_eq!(white, expected);
|
||||
|
||||
@@ -431,7 +431,6 @@ fn png_image(name: &str, bytes: &[u8]) -> Result<Embedded, CreateError> {
|
||||
let opts = craft_image_codecs::DecodeOptions {
|
||||
keep_orientation: true,
|
||||
limits: craft_image_codecs::Limits { max_alloc: MAX_DECODED_BYTES as u64, ..Default::default() },
|
||||
..Default::default()
|
||||
};
|
||||
let img = craft_image_codecs::decode_as_with(craft_image_codecs::Format::Png, bytes, &opts).map_err(|e| match e {
|
||||
craft_image_codecs::CodecError::LimitExceeded(m) => bad(format!("image too large: {m}")),
|
||||
@@ -695,7 +694,6 @@ fn tiff_pages_within(name: &str, bytes: &[u8], wanted: usize, budget: usize) ->
|
||||
let opts = craft_image_codecs::DecodeOptions {
|
||||
keep_orientation: true,
|
||||
limits: craft_image_codecs::Limits { max_alloc: MAX_DECODED_BYTES as u64, ..Default::default() },
|
||||
..Default::default()
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
let mut kept = 0usize;
|
||||
|
||||
@@ -1687,7 +1687,7 @@ fn png_of(w: u32, h: u32, color: TestPngColor, depth: TestPngDepth, data: &[u8],
|
||||
(TestPngColor::Grayscale | TestPngColor::Indexed, TestPngDepth::One) => (w as usize).div_ceil(8),
|
||||
(TestPngColor::Grayscale | TestPngColor::Indexed, TestPngDepth::Eight) => w as usize,
|
||||
(TestPngColor::Rgb, TestPngDepth::Eight) => w as usize * 3,
|
||||
(TestPngColor::Rgb, TestPngDepth::One) => unreachable!(),
|
||||
(TestPngColor::Rgb, TestPngDepth::One) => return Vec::new(),
|
||||
};
|
||||
let mut raw = Vec::with_capacity((1 + row_bytes) * h as usize);
|
||||
for row in data.chunks_exact(row_bytes) {
|
||||
|
||||
@@ -81,7 +81,6 @@ fn turned(bytes: &[u8]) -> Option<Vec<u8>> {
|
||||
let opts = craft_image_codecs::DecodeOptions {
|
||||
keep_orientation: false,
|
||||
limits: craft_image_codecs::Limits { max_pixels: MAX_PIXELS, ..Default::default() },
|
||||
..Default::default()
|
||||
};
|
||||
let mut img = craft_image_codecs::decode_as_with(craft_image_codecs::Format::Jpeg, bytes, &opts).ok()?;
|
||||
if let Some((mut dx, mut dy)) = jfif_density_dpi(bytes) {
|
||||
|
||||
@@ -143,7 +143,6 @@ pub(crate) fn decode_source(bytes: &[u8]) -> Result<DynamicImage, SignatureImage
|
||||
max_pixels: MAX_SOURCE_PIXELS,
|
||||
max_alloc: MAX_SOURCE_PIXELS * 4,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let decoded = craft_image_codecs::decode_as_with(format, bytes, &opts).map_err(|e| match e {
|
||||
craft_image_codecs::CodecError::LimitExceeded(_) => SignatureImageError::Size,
|
||||
@@ -218,7 +217,7 @@ impl SignatureImage {
|
||||
let mut raw = self.rgba.as_ref().clone();
|
||||
let high = f32::from(cutoff);
|
||||
let span = f32::from(feather.max(1));
|
||||
for px in raw.chunks_exact_mut(4) {
|
||||
for px in raw.as_chunks_mut::<4>().0 {
|
||||
let whiteness = f32::from(px[0].min(px[1]).min(px[2]));
|
||||
let keep = ((high - whiteness) / span).clamp(0.0, 1.0);
|
||||
px[3] = (f32::from(px[3]) * keep).round() as u8;
|
||||
|
||||
@@ -387,7 +387,7 @@ xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawin
|
||||
target: format!("media/{name}"),
|
||||
external: false,
|
||||
});
|
||||
pkg.binary(&format!("word/media/{name}"), image_mime(&im.ext), im.bytes.to_vec());
|
||||
pkg.binary(&format!("word/media/{name}"), image_mime(im.ext), im.bytes.to_vec());
|
||||
}
|
||||
if page_colour.is_some() {
|
||||
doc_rels.items.push(RelOut { id: "rIdSettings".into(), ty: format!("{RT_BASE}settings"), target: "settings.xml".into(), external: false });
|
||||
|
||||
@@ -368,7 +368,7 @@ xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" type=\"blank\"
|
||||
pkg.rels(&path, s_rels);
|
||||
}
|
||||
for (name, im) in &global_media {
|
||||
pkg.binary(&format!("ppt/media/{name}"), image_mime(&im.ext), im.bytes.to_vec());
|
||||
pkg.binary(&format!("ppt/media/{name}"), image_mime(im.ext), im.bytes.to_vec());
|
||||
}
|
||||
pkg.finish().unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ pub fn parse_data_file(text: &str, expect_root: Option<&str>) -> Result<Vec<Impo
|
||||
if crate::xml_nesting_depth(text) > MAX_IMPORT_DEPTH {
|
||||
return Err(XfaError::TooLarge(format!("the data file nests more than {MAX_IMPORT_DEPTH} levels deep")));
|
||||
}
|
||||
let opts = crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES, ..Default::default() };
|
||||
let opts = crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES };
|
||||
let parsed = crate::xml::Document::parse_with_options(text, opts).map_err(|e| XfaError::Malformed(format!("the data file is not XML: {e}")))?;
|
||||
let root = parsed.root_element();
|
||||
let local = root.tag_name().name();
|
||||
@@ -304,7 +304,7 @@ pub fn export_data(doc: &Document, xdp: bool, pdf_name: &str) -> Result<String,
|
||||
/// data element), each with the namespaces it uses declared on it. `Ok(None)`: no datasets
|
||||
/// element; `Err`: the text can't be read.
|
||||
fn datasets_markup(text: &str) -> Result<Option<(String, String)>, ()> {
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES, ..Default::default() };
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES };
|
||||
if crate::xml_nesting_depth(text) > crate::MAX_XML_DEPTH {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ fn merge(text: &str, within: Within, op: Op, warnings: &mut Vec<String>) -> Merg
|
||||
warnings.push("The XFA data nests XML elements deeper than the reader accepts; the field values were not written to it".into());
|
||||
return Merged::Failed;
|
||||
}
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES, ..Default::default() };
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES };
|
||||
// A packet of an array may use the prefixes the XDP envelope declares: read it inside
|
||||
// an element declaring the usual ones.
|
||||
let wrapped;
|
||||
@@ -465,7 +465,7 @@ pub(super) fn readable(text: &str, within: Within, warnings: &mut Vec<String>) -
|
||||
warnings.push(format!("The XFA data would be larger than {} MB; it was not written", MAX_PACKET_WRITE >> 20));
|
||||
return false;
|
||||
}
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES, ..Default::default() };
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES };
|
||||
let ok = crate::xml::Document::parse_with_options(text, opts()).is_ok()
|
||||
|| (within == Within::Packet && crate::xml::Document::parse_with_options(&format!("{WRAP_OPEN}{text}{WRAP_CLOSE}"), opts()).is_ok());
|
||||
if !ok {
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn parse_datasets(xdp: &str) -> Option<DataNode> {
|
||||
if crate::xml_nesting_depth(xdp) > crate::MAX_XML_DEPTH {
|
||||
return None;
|
||||
}
|
||||
let opts = crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES, ..Default::default() };
|
||||
let opts = crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES };
|
||||
let doc = crate::xml::Document::parse_with_options(xdp, opts).ok()?;
|
||||
let datasets = doc.descendants().find(|n| n.is_element() && n.tag_name().name() == "datasets")?;
|
||||
let data = datasets.children().find(|n| n.is_element() && n.tag_name().name() == "data")?;
|
||||
@@ -33,7 +33,7 @@ pub(super) fn parse_datasets_packet(text: &str) -> Option<DataNode> {
|
||||
if crate::xml_nesting_depth(text) > crate::MAX_XML_DEPTH {
|
||||
return None;
|
||||
}
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES, ..Default::default() };
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES };
|
||||
let wrapped;
|
||||
let doc = match crate::xml::Document::parse_with_options(text, opts()) {
|
||||
Ok(d) => d,
|
||||
|
||||
@@ -169,7 +169,7 @@ pub fn decode(data: &[u8]) -> Result<Decoded, String> {
|
||||
}
|
||||
match kind(data) {
|
||||
Some(Kind::Png) => {
|
||||
let opts = craft_image_codecs::DecodeOptions { keep_orientation: true, limits: decode_limits(), ..Default::default() };
|
||||
let opts = craft_image_codecs::DecodeOptions { keep_orientation: true, limits: decode_limits() };
|
||||
let img = craft_image_codecs::decode_as_with(craft_image_codecs::Format::Png, data, &opts).map_err(|e| match e {
|
||||
craft_image_codecs::CodecError::LimitExceeded(_) => too_big(),
|
||||
other => bad(other.to_string()),
|
||||
@@ -212,7 +212,7 @@ pub fn decode(data: &[u8]) -> Result<Decoded, String> {
|
||||
_ => craft_image_codecs::Format::Tiff,
|
||||
};
|
||||
let data = if k == Kind::Bmp { bmp_with_pixel_offset(data) } else { std::borrow::Cow::Borrowed(data) };
|
||||
let opts = craft_image_codecs::DecodeOptions { keep_orientation: false, limits: decode_limits(), ..Default::default() };
|
||||
let opts = craft_image_codecs::DecodeOptions { keep_orientation: false, limits: decode_limits() };
|
||||
let img = craft_image_codecs::decode_as_with(format, &data, &opts).map_err(|e| match e {
|
||||
craft_image_codecs::CodecError::LimitExceeded(_) => too_big(),
|
||||
other => bad(other.to_string()),
|
||||
|
||||
@@ -1266,7 +1266,7 @@ pub fn parse(xml: &str) -> Result<(Template, Vec<String>), XfaError> {
|
||||
crate::MAX_XML_DEPTH
|
||||
)));
|
||||
}
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES, ..Default::default() };
|
||||
let opts = || crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: MAX_NODES };
|
||||
let doc = crate::xml::Document::parse_with_options(xml, opts()).map_err(|e| XfaError::Malformed(format!("template XML: {e}")))?;
|
||||
let root = doc.root_element();
|
||||
// The XDP's config packet has a <template> element too: the one we want is in the
|
||||
|
||||
@@ -151,7 +151,7 @@ pub fn strip(doc: &mut Document) -> Result<Stripped, XfaError> {
|
||||
}
|
||||
// One XDP: the packets it holds are the envelope's children.
|
||||
let text = s.decoded_within(64 << 20).ok().and_then(|b| crate::packets::decode(&b)).map(|(t, _)| t).unwrap_or_default();
|
||||
let opts = crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: 1_000_000, ..Default::default() };
|
||||
let opts = crate::xml::ParsingOptions { allow_dtd: false, nodes_limit: 1_000_000 };
|
||||
if crate::xml_nesting_depth(&text) <= crate::MAX_XML_DEPTH
|
||||
&& let Ok(parsed) = crate::xml::Document::parse_with_options(&text, opts)
|
||||
{
|
||||
|
||||
@@ -165,9 +165,11 @@ impl Document {
|
||||
}
|
||||
|
||||
pub(crate) fn parse_with_options(text: &str, options: ParsingOptions) -> Result<Self, String> {
|
||||
let mut opts = craft_xml::Options::default();
|
||||
opts.strict = true;
|
||||
opts.doctype = if options.allow_dtd { craft_xml::Doctype::Ignore } else { craft_xml::Doctype::Reject };
|
||||
let mut opts = craft_xml::Options {
|
||||
strict: true,
|
||||
doctype: if options.allow_dtd { craft_xml::Doctype::Ignore } else { craft_xml::Doctype::Reject },
|
||||
..Default::default()
|
||||
};
|
||||
opts.limits.max_nodes = options.nodes_limit as usize;
|
||||
opts.limits.max_depth = crate::MAX_XML_DEPTH;
|
||||
|
||||
|
||||
@@ -86,9 +86,7 @@ pub(crate) fn check_form_xml_depth(text: &str) -> Result<(), DataError> {
|
||||
}
|
||||
|
||||
pub(crate) fn parse_form_xml(text: &str) -> Result<craft_xml::El, craft_xml::Error> {
|
||||
let mut opts = craft_xml::Options::default();
|
||||
opts.strict = true;
|
||||
opts.doctype = craft_xml::Doctype::Reject;
|
||||
let mut opts = craft_xml::Options { strict: true, doctype: craft_xml::Doctype::Reject, ..Default::default() };
|
||||
opts.limits.max_nodes = MAX_NODES as usize;
|
||||
opts.limits.max_depth = MAX_XML_DEPTH.max(1024);
|
||||
opts.parse(text.as_bytes())
|
||||
|
||||
+1
-42
@@ -316,9 +316,7 @@ fn walk_rich_plain(node: &craft_xml::El, out: &mut String) {
|
||||
for n in &node.children {
|
||||
match n {
|
||||
craft_xml::Node::El(el) => {
|
||||
if el.local() == "br" {
|
||||
out.push('\r');
|
||||
} else if matches!(el.local(), "p" | "div" | "li") && !out.is_empty() && !out.ends_with('\r') {
|
||||
if el.local() == "br" || (matches!(el.local(), "p" | "div" | "li") && !out.is_empty() && !out.ends_with('\r')) {
|
||||
out.push('\r');
|
||||
}
|
||||
walk_rich_plain(el, out);
|
||||
@@ -1023,45 +1021,6 @@ fn read_rich_text(rich: &craft_xml::El, ns: &[(String, String)], has_contents: b
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether start tag `tag` (from `<` up to its closing `>`) declares attribute `name` (white
|
||||
/// space may come before the `=`). Attribute values are skipped: `title=" xmlns:b='x'"` declares
|
||||
/// nothing.
|
||||
fn declares(tag: &str, name: &str) -> bool {
|
||||
let mut quote = None;
|
||||
let mut after_space = false;
|
||||
for (i, c) in tag.char_indices() {
|
||||
if let Some(q) = quote {
|
||||
if c == q {
|
||||
quote = None;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if c == '"' || c == '\'' {
|
||||
quote = Some(c);
|
||||
} else if after_space && tag.get(i..).and_then(|rest| rest.strip_prefix(name)).is_some_and(|a| a.trim_start().starts_with('=')) {
|
||||
return true;
|
||||
}
|
||||
after_space = c.is_whitespace();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Where the start tag at the beginning of `markup` ends: its first `>` outside an attribute
|
||||
/// value (the end of `markup` when there is none).
|
||||
fn start_tag_end(markup: &str) -> usize {
|
||||
let mut quote = None;
|
||||
for (i, c) in markup.char_indices() {
|
||||
match quote {
|
||||
Some(q) if c == q => quote = None,
|
||||
Some(_) => {}
|
||||
None if c == '"' || c == '\'' => quote = Some(c),
|
||||
None if c == '>' => return i,
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
markup.len()
|
||||
}
|
||||
|
||||
/// The one element `markup` (cut out of `rich`, a `<contents-richtext>`) as an XML
|
||||
/// document of its own: the namespaces it inherits from the XFDF around it are declared on it,
|
||||
/// the default one (XHTML declared on `<contents-richtext>`, say; not XFDF's own, which a
|
||||
|
||||
Reference in new issue
Block a user