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

  1. 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.
  2. If the SVG lacks sane intrinsic dimensions, add explicit width/height attributes to the root <svg> element.
  3. If the SVG is untrusted, sanitize/validate its dimensions before feeding it to LiteParse.
  4. 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

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)