rpdfium 7676.6.4

A faithful Rust port of Google's PDFium PDF rendering engine
Documentation
//! Integration tests for parallel rendering via `ArcDocument`.

use rpdfium::{ArcDocument, ArcLibrary, Error, OpenOptions, Rect, RenderConfig, RgbaColor};

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

/// Build a minimal valid PDF with the given number of pages.
fn build_multi_page_pdf(page_count: usize) -> Vec<u8> {
    let mut pdf = Vec::new();
    pdf.extend_from_slice(b"%PDF-1.4\n");

    // Object 1: Catalog
    let obj1_offset = pdf.len();
    pdf.extend_from_slice(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");

    // Object 2: Pages node
    let obj2_offset = pdf.len();
    let kids: Vec<String> = (0..page_count).map(|i| format!("{} 0 R", i + 3)).collect();
    let kids_str = kids.join(" ");
    let pages_dict =
        format!("2 0 obj\n<< /Type /Pages /Kids [{kids_str}] /Count {page_count} >>\nendobj\n");
    pdf.extend_from_slice(pages_dict.as_bytes());

    // Page objects (3, 4, 5, ...)
    let mut page_offsets = Vec::new();
    for i in 0..page_count {
        let offset = pdf.len();
        page_offsets.push(offset);
        let obj_num = i + 3;
        let page_obj = format!(
            "{obj_num} 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>\nendobj\n"
        );
        pdf.extend_from_slice(page_obj.as_bytes());
    }

    // Xref table
    let xref_offset = pdf.len();
    let total_objects = page_count + 3; // 0 (free) + catalog + pages + N pages
    pdf.extend_from_slice(b"xref\n");
    pdf.extend_from_slice(format!("0 {total_objects}\n").as_bytes());
    pdf.extend_from_slice(b"0000000000 65535 f \r\n");
    pdf.extend_from_slice(format!("{:010} 00000 n \r\n", obj1_offset).as_bytes());
    pdf.extend_from_slice(format!("{:010} 00000 n \r\n", obj2_offset).as_bytes());
    for offset in &page_offsets {
        pdf.extend_from_slice(format!("{:010} 00000 n \r\n", offset).as_bytes());
    }
    pdf.extend_from_slice(b"trailer\n");
    pdf.extend_from_slice(format!("<< /Size {total_objects} /Root 1 0 R >>\n").as_bytes());
    pdf.extend_from_slice(format!("startxref\n{xref_offset}\n%%EOF").as_bytes());

    pdf
}

fn render_config() -> RenderConfig {
    RenderConfig {
        width: 100,
        height: 100,
        background: RgbaColor::WHITE,
        media_box: Some(Rect::new(0.0, 0.0, 612.0, 792.0)),
        rotation: 0,
        tile_size: None,
        progress: None,
        grayscale: false,
        antialiasing: true,
        ..RenderConfig::default()
    }
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

#[test]
fn test_render_pages_parallel_basic() {
    let lib = ArcLibrary::new();
    let pdf = build_multi_page_pdf(3);
    let opts = OpenOptions::default();
    let doc = ArcDocument::open(&lib, pdf, &opts).unwrap();
    let config = render_config();

    let results = doc.render_pages_parallel(&[0, 1, 2], &config);
    assert_eq!(results.len(), 3);
    for result in &results {
        let bitmap = result.as_ref().unwrap();
        assert_eq!(bitmap.width, 100);
        assert_eq!(bitmap.height, 100);
        assert!(!bitmap.data.is_empty());
    }
}

#[test]
fn test_render_all_pages_parallel() {
    let lib = ArcLibrary::new();
    let pdf = build_multi_page_pdf(5);
    let opts = OpenOptions::default();
    let doc = ArcDocument::open(&lib, pdf, &opts).unwrap();
    let config = render_config();

    let results = doc.render_all_pages_parallel(&config);
    assert_eq!(results.len(), 5);
    for result in &results {
        assert!(result.is_ok());
    }
}

#[test]
fn test_render_parallel_with_invalid_index() {
    let lib = ArcLibrary::new();
    let pdf = build_multi_page_pdf(3);
    let opts = OpenOptions::default();
    let doc = ArcDocument::open(&lib, pdf, &opts).unwrap();
    let config = render_config();

    let results = doc.render_pages_parallel(&[0, 99, 2], &config);
    assert_eq!(results.len(), 3);

    // Page 0 should succeed
    assert!(results[0].is_ok());

    // Page 99 should fail with PageOutOfRange
    match &results[1] {
        Err(Error::PageOutOfRange { index, count }) => {
            assert_eq!(*index, 99);
            assert_eq!(*count, 3);
        }
        other => panic!("expected PageOutOfRange, got {:?}", other),
    }

    // Page 2 should succeed
    assert!(results[2].is_ok());
}

#[test]
fn test_render_parallel_empty_indices() {
    let lib = ArcLibrary::new();
    let pdf = build_multi_page_pdf(3);
    let opts = OpenOptions::default();
    let doc = ArcDocument::open(&lib, pdf, &opts).unwrap();
    let config = render_config();

    let results = doc.render_pages_parallel(&[], &config);
    assert!(results.is_empty());
}