run-llama/liteparse · error · std::io::Error
could not read pixmap
Error message
could not read pixmap
What it means
In rasterize_svg, resvg parses the SVG into a scene tree and then allocates a tiny_skia Pixmap at the SVG's intrinsic size (ceil'd width/height, minimum 1px). Pixmap::new returns None only when allocation fails or the dimensions are beyond tiny-skia's limits (e.g. area exceeding i32::MAX or extreme width/height). LiteParse surfaces that as this InvalidData io::Error rather than panicking, so the caller (prepare_image) can fail conversion of the SVG gracefully.
Solutions
- Open the SVG and check its width/height or viewBox attributes; clamp or reduce them to sane pixel dimensions (e.g. under 10000x10000) before parsing.
- If the SVG lacks sane intrinsic dimensions, add explicit width/height attributes to the root <svg> element.
- If the SVG is untrusted, sanitize/validate its dimensions before feeding it to LiteParse.
- Increase available memory if the document is legitimately large, and retry.
Example fix
// before: SVG with absurd intrinsic size <svg width="500000" height="500000" viewBox="0 0 500000 500000">...</svg> // after: clamped to a reasonable raster size <svg width="2000" height="2000" viewBox="0 0 500000 500000">...</svg>
Defensive patterns
Strategy: validation
Validate before calling
// Rust: check SVG intrinsic size before conversion
fn svg_dims_ok(svg: &[u8]) -> bool {
if let Ok(opts) = usvg::Options::parse(svg, &usvg::Options::default()) {
let s = usvg::Tree::from_xmlsvg(svg, &opts).map(|t| t.size());
matches!(s, Ok(sz) if sz.width() > 0.0 && sz.height() > 0.0
&& (sz.width() * sz.height()) < 100_000_000.0)
} else { false }
} Try / catch
match liteparse.parse("doc.pdf") {
Ok(res) => use(res),
Err(e) if e.to_string().contains("could not read pixmap") => {
// treat document as unsuitable for image conversion
}
Err(e) => return Err(e),
} Prevention
- Sanitize SVG dimensions (width/height/viewBox) before parsing untrusted documents
- Cap raster output size in your pipeline (e.g. max 10000x10000)
- Fuzz-test the conversion path with extreme-size SVGs
When it happens
Trigger: Calling parse on a PDF (or directly on an SVG) whose conversion path calls prepare_image -> rasterize_svg, where the SVG's tree.size() produces width/height whose product or individual values exceed Pixmap's allocation limits (effectively width*height > ~4 billion pixels or allocation failure).
Common situations: SVG files with gigantic viewBox dimensions (e.g. values like 100000x100000 or corrupt/malicious SVGs with huge size attributes); malformed SVG that resvg parses but for which it reports an absurd size; memory-constrained build/runtime environments where the large raster allocation fails.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
AI-assisted analysis of run-llama/liteparse@22d2dd8cd7 (2026-09-08).
Data as JSON: /api/errors/f5a873fc04f7543d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/liteparse/src/conversion.rs:649
return Some((width, height, components));
}
i += seg_len;
}
None
}
/// Rasterizes an SVG file to RGBA8 bytes + dimensions using resvg.
fn rasterize_svg(data: &[u8]) -> Result<(Vec<u8>, u32, u32), LiteParseError> {
let opt = Options::default();
let tree =
Tree::from_data(data, &opt).map_err(|e| LiteParseError::Conversion(e.to_string()))?;
let size = tree.size();
let width = size.width().ceil() as u32;
let height = size.height().ceil() as u32;
let mut pixmap = Pixmap::new(width.max(1), height.max(1))
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "could not read pixmap"))?;
resvg::render(
&tree,
resvg::tiny_skia::Transform::identity(),
&mut pixmap.as_mut(),
);
// tiny_skia's Pixmap stores premultiplied RGBA; un-premultiply so the
// PDF's separate RGB/SMask streams composite correctly.
let mut rgba = pixmap.data().to_vec();
for px in rgba.chunks_exact_mut(4) {
let a = px[3];
if a != 0 && a != 255 {
px[0] = ((px[0] as u16 * 255) / a as u16) as u8;
px[1] = ((px[1] as u16 * 255) / a as u16) as u8;
px[2] = ((px[2] as u16 * 255) / a as u16) as u8;
}
}View on GitHub (pinned to 22d2dd8cd7)