test: add extract_pdf_text unit tests (DOC-FULL-023)
- pdf_with_text_stream helper: build minimal PDFs with BT/ET text - single/multiple text blocks, no BT/ET→empty, empty PDF→empty - MAX_SCAN_BYTES limit respected, empty text filtered - page_count from pages dict, default to 1 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
1c93308e8b
commit
7570717ec8
@ -344,6 +344,104 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pdf_with_text_stream(text_content: &str) -> Vec<u8> {
|
||||||
|
// Build a minimal PDF with a stream object containing BT/ET text
|
||||||
|
let stream_text = format!("BT\n({})\nET", text_content);
|
||||||
|
let obj = format!(
|
||||||
|
"1 0 obj\n<< /Length {} >>\nstream\n{}\nendstream\nendobj\n",
|
||||||
|
stream_text.len(),
|
||||||
|
stream_text
|
||||||
|
);
|
||||||
|
let obj_bytes = obj.as_bytes();
|
||||||
|
let header = b"%PDF-1.4\n%\x80\x80\x80\x80\n";
|
||||||
|
let header_len = header.len() as u64;
|
||||||
|
let obj_len = obj_bytes.len() as u64;
|
||||||
|
let trailer = format!(
|
||||||
|
"xref\n0 2\n0000000000 65535 f \n{:010} 00000 n \ntrailer\n<< /Size 2 /Root 1 0 R >>\nstartxref\n{}\n%%EOF\n",
|
||||||
|
header_len,
|
||||||
|
header_len + obj_len
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut pdf = Vec::new();
|
||||||
|
pdf.extend_from_slice(header);
|
||||||
|
pdf.extend_from_slice(obj_bytes);
|
||||||
|
pdf.extend_from_slice(trailer.as_bytes());
|
||||||
|
pdf
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_pdf_text_single_bt_et_block() {
|
||||||
|
let pdf = pdf_with_text_stream("Hello World");
|
||||||
|
let path = write_temp(&pdf, "text_single.pdf");
|
||||||
|
let pages = extract_pdf_text(&path).unwrap();
|
||||||
|
assert_eq!(pages.len(), 1);
|
||||||
|
assert_eq!(pages[0].text, "Hello World");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_pdf_text_multiple_blocks() {
|
||||||
|
let stream = "BT\n(First line)\nET\nBT\n(Second line)\nET";
|
||||||
|
let obj = format!("1 0 obj\n<< /Length {} >>\nstream\n{}\nendstream\nendobj\n", stream.len(), stream);
|
||||||
|
let header = b"%PDF-1.4\n";
|
||||||
|
let pdf: Vec<u8> = [header.as_slice(), obj.as_bytes()].concat();
|
||||||
|
// This is a slightly malformed PDF but we're testing the BT/ET scanner
|
||||||
|
let path = write_temp(&pdf, "text_multi.pdf");
|
||||||
|
let pages = extract_pdf_text(&path).unwrap();
|
||||||
|
// The scanner finds bt\n...\net blocks in sequence
|
||||||
|
assert!(pages.len() >= 1, "Should find at least one text block");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_pdf_text_no_bt_et_returns_empty() {
|
||||||
|
let pdf = b"%PDF-1.4\nNo text blocks here\n%%EOF".to_vec();
|
||||||
|
let path = write_temp(&pdf, "no_text.pdf");
|
||||||
|
let pages = extract_pdf_text(&path).unwrap();
|
||||||
|
assert!(pages.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_pdf_text_empty_pdf_returns_empty() {
|
||||||
|
let pdf = b"%PDF-1.4\n".to_vec();
|
||||||
|
let path = write_temp(&pdf, "empty.pdf");
|
||||||
|
let pages = extract_pdf_text(&path).unwrap();
|
||||||
|
assert!(pages.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_pdf_text_max_scan_limit_respected() {
|
||||||
|
// Create a PDF larger than MAX_SCAN_BYTES (8MB) by padding zeros
|
||||||
|
let mut pdf = pdf_with_text_stream("Hello");
|
||||||
|
pdf.resize(MAX_SCAN_BYTES + 1024, 0);
|
||||||
|
let path = write_temp(&pdf, "large.pdf");
|
||||||
|
let result = extract_pdf_text(&path);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_pdf_text_empty_text_filtered() {
|
||||||
|
let pdf = pdf_with_text_stream("");
|
||||||
|
let path = write_temp(&pdf, "empty_text.pdf");
|
||||||
|
let pages = extract_pdf_text(&path).unwrap();
|
||||||
|
// Empty text blocks are filtered out
|
||||||
|
assert!(pages.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_pdf_text_page_count_from_pages_dict() {
|
||||||
|
let data = minimal_pdf(3);
|
||||||
|
let path = write_temp(&data, "count_pages.pdf");
|
||||||
|
let meta = read_pdf_metadata(&path).unwrap();
|
||||||
|
assert_eq!(meta.page_count, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_pdf_text_default_to_one_page() {
|
||||||
|
let pdf = b"%PDF-1.4\n".to_vec();
|
||||||
|
let path = write_temp(&pdf, "default_page.pdf");
|
||||||
|
let meta = read_pdf_metadata(&path).unwrap();
|
||||||
|
assert_eq!(meta.page_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fixture_scanned_pdf() {
|
fn test_fixture_scanned_pdf() {
|
||||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user