use rpdfium_core::ParsingMode;
use rpdfium_page::{DefaultFontCache, InterpreterContext, ResourceDict, interpret};
use rpdfium_parser::ObjectStore;
use rpdfium_render::{RenderConfig, RgbaColor, render};
fn build_pdf_with_content(content: &[u8]) -> Vec<u8> {
let mut pdf = Vec::new();
pdf.extend_from_slice(b"%PDF-1.4\n");
let obj1_offset = pdf.len();
pdf.extend_from_slice(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");
let obj2_offset = pdf.len();
pdf.extend_from_slice(b"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n");
let obj3_offset = pdf.len();
pdf.extend_from_slice(
b"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents 4 0 R >>\nendobj\n",
);
let obj4_offset = pdf.len();
pdf.extend_from_slice(format!("4 0 obj\n<< /Length {} >>\nstream\n", content.len()).as_bytes());
pdf.extend_from_slice(content);
pdf.extend_from_slice(b"\nendstream\nendobj\n");
let xref_offset = pdf.len();
pdf.extend_from_slice(b"xref\n0 5\n");
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());
pdf.extend_from_slice(format!("{:010} 00000 n \r\n", obj3_offset).as_bytes());
pdf.extend_from_slice(format!("{:010} 00000 n \r\n", obj4_offset).as_bytes());
pdf.extend_from_slice(b"trailer\n<< /Size 5 /Root 1 0 R >>\n");
pdf.extend_from_slice(format!("startxref\n{xref_offset}\n%%EOF").as_bytes());
pdf
}
fn parse_and_render(content: &[u8], width: u32, height: u32) -> rpdfium_graphics::Bitmap {
let pdf = build_pdf_with_content(content);
let store = ObjectStore::open(pdf, ParsingMode::Lenient).unwrap();
let font_cache = DefaultFontCache;
let ctx = InterpreterContext {
store: &store,
font_cache: &font_cache,
mode: ParsingMode::Lenient,
oc_context: None,
};
let resources = ResourceDict::default();
let operators = rpdfium_parser::tokenize_content_stream(content).unwrap();
let tree = interpret(
&operators,
&ctx,
&resources,
rpdfium_core::fx_system::DEFAULT_MAX_OPERATORS_PER_PAGE,
)
.unwrap();
let config = RenderConfig {
width,
height,
background: RgbaColor::WHITE,
..RenderConfig::default()
};
render(&tree, &config).unwrap()
}
#[test]
fn test_render_empty_page() {
let bitmap = parse_and_render(b"", 100, 100);
assert_eq!(bitmap.width, 100);
assert_eq!(bitmap.height, 100);
assert!(
bitmap
.data
.chunks(4)
.all(|px| px[0] == 255 && px[1] == 255 && px[2] == 255)
);
}
#[test]
fn test_render_red_rectangle() {
let content = b"1 0 0 rg 50 50 100 100 re f";
let bitmap = parse_and_render(content, 200, 200);
assert_eq!(bitmap.width, 200);
let has_red = bitmap
.data
.chunks(4)
.any(|px| px[0] == 255 && px[1] == 0 && px[2] == 0);
assert!(has_red, "expected red pixels in the bitmap");
}
#[test]
fn test_render_gray_fill() {
let content = b"0.5 g 0 0 200 200 re f";
let bitmap = parse_and_render(content, 200, 200);
let has_gray = bitmap.data.chunks(4).any(|px| {
let r = px[0] as i16;
(r - 127).unsigned_abs() <= 1
});
assert!(has_gray, "expected gray pixels in the bitmap");
}
#[test]
fn test_render_blue_fill() {
let content = b"0 0 1 rg 0 0 100 100 re f";
let bitmap = parse_and_render(content, 100, 100);
let has_blue = bitmap
.data
.chunks(4)
.any(|px| px[0] == 0 && px[1] == 0 && px[2] == 255);
assert!(has_blue, "expected blue pixels in the bitmap");
}
#[test]
fn test_render_stroked_line() {
let content = b"2 w 0 0 m 100 100 l S";
let bitmap = parse_and_render(content, 100, 100);
let has_non_white = bitmap
.data
.chunks(4)
.any(|px| px[0] < 255 || px[1] < 255 || px[2] < 255);
assert!(has_non_white, "expected stroked pixels in the bitmap");
}
fn build_facade_pdf(content: &[u8]) -> Vec<u8> {
build_pdf_with_content(content)
}
#[test]
fn test_facade_render_blank_page_produces_correct_dimensions() {
let pdf_bytes = build_facade_pdf(b"");
let lib = rpdfium::Library::new();
let opts = rpdfium::OpenOptions::default();
let doc =
rpdfium::Document::open(&lib, pdf_bytes, &opts).expect("document should open successfully");
assert_eq!(doc.page_count(), 1, "expected exactly one page");
let page = doc.page(0).expect("page 0 should be accessible");
let media_box = page.media_box();
let config = rpdfium::RenderConfig {
width: 200,
height: 200,
background: rpdfium::RgbaColor::WHITE,
media_box: Some(media_box),
..rpdfium::RenderConfig::default()
};
let bitmap = page.render(&config).expect("render should succeed");
assert_eq!(bitmap.width, 200, "bitmap width should be 200");
assert_eq!(bitmap.height, 200, "bitmap height should be 200");
assert!(
!bitmap.data.is_empty(),
"bitmap pixel data must not be empty"
);
assert_eq!(
bitmap.data.len(),
200 * 200 * 4,
"bitmap should have 200×200×4 bytes for RGBA"
);
}
#[test]
fn test_facade_render_blank_page_is_all_white() {
let pdf_bytes = build_facade_pdf(b"");
let lib = rpdfium::Library::new();
let opts = rpdfium::OpenOptions::default();
let doc = rpdfium::Document::open(&lib, pdf_bytes, &opts).unwrap();
let page = doc.page(0).unwrap();
let media_box = page.media_box();
let config = rpdfium::RenderConfig {
width: 100,
height: 100,
background: rpdfium::RgbaColor::WHITE,
media_box: Some(media_box),
..rpdfium::RenderConfig::default()
};
let bitmap = page.render(&config).unwrap();
let all_white = bitmap
.data
.chunks(4)
.all(|px| px[0] == 255 && px[1] == 255 && px[2] == 255);
assert!(all_white, "blank page should render as all-white pixels");
}
#[test]
fn test_facade_render_page_with_fill_produces_non_empty_bitmap() {
let content = b"0 0 1 rg 0 0 200 200 re f";
let pdf_bytes = build_facade_pdf(content);
let lib = rpdfium::Library::new();
let opts = rpdfium::OpenOptions::default();
let doc = rpdfium::Document::open(&lib, pdf_bytes, &opts).unwrap();
let page = doc.page(0).unwrap();
let media_box = page.media_box();
let config = rpdfium::RenderConfig {
width: 200,
height: 200,
background: rpdfium::RgbaColor::WHITE,
media_box: Some(media_box),
..rpdfium::RenderConfig::default()
};
let bitmap = page.render(&config).unwrap();
assert_eq!(bitmap.width, 200);
assert_eq!(bitmap.height, 200);
assert!(!bitmap.data.is_empty());
let has_non_white = bitmap
.data
.chunks(4)
.any(|px| px[0] < 255 || px[1] < 255 || px[2] < 255);
assert!(has_non_white, "filled page must produce non-white pixels");
}