#![cfg(target_arch = "wasm32")]
use wasm_bindgen::JsValue;
use wasm_bindgen_test::*;
use pdf_oxide::api::{Pdf, PdfBuilder};
use pdf_oxide::geometry::Rect;
use pdf_oxide::wasm::{WasmPdf, WasmPdfDocument};
use pdf_oxide::writer::{CheckboxWidget, ComboBoxWidget, PdfWriter, TextFieldWidget};
wasm_bindgen_test_configure!(run_in_browser);
fn make_text_pdf(text: &str) -> Vec<u8> {
Pdf::from_text(text).unwrap().into_bytes()
}
fn doc_from_text(text: &str) -> WasmPdfDocument {
WasmPdfDocument::new(&make_text_pdf(text)).unwrap()
}
#[wasm_bindgen_test]
fn test_new_invalid_bytes() {
let result = WasmPdfDocument::new(b"not a pdf");
assert!(result.is_err());
}
#[wasm_bindgen_test]
fn test_new_empty_bytes() {
let result = WasmPdfDocument::new(b"");
assert!(result.is_err());
}
#[wasm_bindgen_test]
fn test_extract_chars_returns_array() {
let mut doc = doc_from_text("ABC");
let result = doc.extract_chars(0).unwrap();
assert!(js_sys::Array::is_array(&result), "extract_chars should return an array");
let arr = js_sys::Array::from(&result);
assert!(arr.length() > 0, "should have at least one char");
let first = arr.get(0);
let char_val = js_sys::Reflect::get(&first, &JsValue::from_str("char")).unwrap();
assert!(char_val.is_string(), "char field should be a string");
}
#[wasm_bindgen_test]
fn test_extract_chars_has_bbox() {
let mut doc = doc_from_text("X");
let result = doc.extract_chars(0).unwrap();
let arr = js_sys::Array::from(&result);
let first = arr.get(0);
let bbox = js_sys::Reflect::get(&first, &JsValue::from_str("bbox")).unwrap();
assert!(!bbox.is_undefined(), "char should have a bbox field");
}
#[wasm_bindgen_test]
fn test_extract_chars_has_font_name() {
let mut doc = doc_from_text("A");
let result = doc.extract_chars(0).unwrap();
let arr = js_sys::Array::from(&result);
let first = arr.get(0);
let font = js_sys::Reflect::get(&first, &JsValue::from_str("font_name")).unwrap();
assert!(!font.is_undefined(), "char should have a font_name field");
}
#[wasm_bindgen_test]
fn test_extract_chars_invalid_page() {
let mut doc = doc_from_text("ABC");
let result = doc.extract_chars(999);
assert!(result.is_err());
}
#[wasm_bindgen_test]
fn test_extract_spans_returns_array() {
let mut doc = doc_from_text("Hello spans test");
let result = doc.extract_spans(0).unwrap();
assert!(js_sys::Array::is_array(&result), "extract_spans should return an array");
let arr = js_sys::Array::from(&result);
assert!(arr.length() > 0, "should have at least one span");
let first = arr.get(0);
let text = js_sys::Reflect::get(&first, &JsValue::from_str("text")).unwrap();
assert!(text.is_string(), "span should have a text field");
}
#[wasm_bindgen_test]
fn test_extract_spans_has_font_size() {
let mut doc = doc_from_text("Hello");
let result = doc.extract_spans(0).unwrap();
let arr = js_sys::Array::from(&result);
let first = arr.get(0);
let font_size = js_sys::Reflect::get(&first, &JsValue::from_str("font_size")).unwrap();
assert!(!font_size.is_undefined(), "span should have font_size");
}
#[wasm_bindgen_test]
fn test_search_returns_array() {
let mut doc = doc_from_text("Hello world search test");
let result = doc.search("Hello", None, Some(true), None, None).unwrap();
assert!(js_sys::Array::is_array(&result), "search should return an array");
}
#[wasm_bindgen_test]
fn test_search_result_has_fields() {
let mut doc = doc_from_text("Hello world");
let result = doc.search("Hello", None, Some(true), None, None).unwrap();
let arr = js_sys::Array::from(&result);
if arr.length() > 0 {
let first = arr.get(0);
let page = js_sys::Reflect::get(&first, &JsValue::from_str("page")).unwrap();
assert!(!page.is_undefined(), "search result should have page field");
let text = js_sys::Reflect::get(&first, &JsValue::from_str("text")).unwrap();
assert!(!text.is_undefined(), "search result should have text field");
}
}
#[wasm_bindgen_test]
fn test_search_not_found_empty_array() {
let mut doc = doc_from_text("Hello world");
let result = doc
.search("ZZZZZ_NONEXISTENT", None, Some(true), None, None)
.unwrap();
let arr = js_sys::Array::from(&result);
assert_eq!(arr.length(), 0, "search for nonexistent text should return empty array");
}
#[wasm_bindgen_test]
fn test_search_page() {
let mut doc = doc_from_text("Hello page search");
let result = doc
.search_page(0, "Hello", None, Some(true), None, None)
.unwrap();
assert!(js_sys::Array::is_array(&result));
}
#[wasm_bindgen_test]
fn test_search_case_insensitive() {
let mut doc = doc_from_text("Hello World");
let result = doc
.search("hello", Some(true), Some(true), None, None)
.unwrap();
let arr = js_sys::Array::from(&result);
assert!(arr.length() > 0, "case-insensitive search should find 'hello' in 'Hello World'");
}
#[wasm_bindgen_test]
fn test_extract_images_returns_array() {
let mut doc = doc_from_text("No images");
let result = doc.extract_images(0).unwrap();
assert!(js_sys::Array::is_array(&result));
let arr = js_sys::Array::from(&result);
assert_eq!(arr.length(), 0, "text-only PDF should have no images");
}
#[wasm_bindgen_test]
fn test_extract_images_invalid_page() {
let mut doc = doc_from_text("Hello");
let result = doc.extract_images(999);
assert!(result.is_err());
}
#[wasm_bindgen_test]
fn test_page_crop_box_null_when_unset() {
let mut doc = doc_from_text("Hello");
let result = doc.page_crop_box(0).unwrap();
if result.is_null() {
} else {
assert!(js_sys::Array::is_array(&result));
}
}
#[wasm_bindgen_test]
fn test_page_rotation_invalid_page() {
let mut doc = doc_from_text("Hello");
let result = doc.page_rotation(999);
assert!(result.is_err());
}
#[wasm_bindgen_test]
fn test_erase_regions_invalid_length() {
let mut doc = doc_from_text("Hello");
let rects = [0.0, 0.0, 100.0]; let result = doc.erase_regions(0, &rects);
assert!(result.is_err());
}
#[wasm_bindgen_test]
fn test_page_images_returns_array() {
let mut doc = doc_from_text("Hello");
let result = doc.page_images(0).unwrap();
assert!(js_sys::Array::is_array(&result));
}
#[wasm_bindgen_test]
fn test_extract_text_invalid_page() {
let mut doc = doc_from_text("Hello");
let result = doc.extract_text(999);
assert!(result.is_err());
}
#[wasm_bindgen_test]
fn test_full_roundtrip() {
let mut doc = doc_from_text("Roundtrip WASM test");
doc.set_title("WASM Title").unwrap();
doc.set_author("WASM Author").unwrap();
doc.set_page_rotation(0, 90).unwrap();
let bytes = doc.save_to_bytes().unwrap();
assert!(bytes.starts_with(b"%PDF"));
let mut doc2 = WasmPdfDocument::new(&bytes).unwrap();
assert_eq!(doc2.page_count().unwrap(), 1);
let text = doc2.extract_text(0).unwrap();
assert!(text.contains("Roundtrip"), "text should survive roundtrip");
let rotation = doc2.page_rotation(0).unwrap();
assert_eq!(rotation, 90, "rotation should survive roundtrip");
}
#[wasm_bindgen_test]
fn test_encrypted_roundtrip() {
let mut doc = doc_from_text("Encrypted content");
let bytes = doc
.save_encrypted_to_bytes("mypass", None, None, None, None, None)
.unwrap();
assert!(bytes.starts_with(b"%PDF"));
let mut doc2 = WasmPdfDocument::new(&bytes).unwrap();
let auth = doc2.authenticate("mypass").unwrap();
assert!(auth, "should authenticate with correct password");
}
#[wasm_bindgen_test]
fn test_wasm_pdf_from_markdown_roundtrip() {
let pdf = WasmPdf::from_markdown("# Hello\n\nWorld content", None, None).unwrap();
let mut doc = WasmPdfDocument::new(&pdf.to_bytes()).unwrap();
let text = doc.extract_all_text().unwrap();
assert!(!text.is_empty());
}
#[wasm_bindgen_test]
fn test_wasm_pdf_from_html_roundtrip() {
let pdf = WasmPdf::from_html("<p>HTML content here</p>", None, None).unwrap();
let mut doc = WasmPdfDocument::new(&pdf.to_bytes()).unwrap();
let text = doc.extract_all_text().unwrap();
assert!(!text.is_empty());
}
#[wasm_bindgen_test]
fn test_wasm_pdf_from_text_roundtrip() {
let pdf = WasmPdf::from_text("Plain text content", None, None).unwrap();
let mut doc = WasmPdfDocument::new(&pdf.to_bytes()).unwrap();
let text = doc.extract_all_text().unwrap();
assert!(text.contains("Plain"), "should contain source text");
}
#[wasm_bindgen_test]
fn test_get_outline_returns_null_or_array() {
let mut doc = doc_from_text("No outline here");
let result = doc.get_outline().unwrap();
assert!(
result.is_null() || js_sys::Array::is_array(&result),
"getOutline should return null or an array"
);
}
#[wasm_bindgen_test]
fn test_get_annotations_returns_array() {
let mut doc = doc_from_text("No annotations");
let result = doc.get_annotations(0).unwrap();
assert!(js_sys::Array::is_array(&result), "getAnnotations should return an array");
let arr = js_sys::Array::from(&result);
assert_eq!(arr.length(), 0, "text-only PDF should have no annotations");
}
#[wasm_bindgen_test]
fn test_get_annotations_invalid_page() {
let mut doc = doc_from_text("Hello");
let result = doc.get_annotations(999);
assert!(result.is_err(), "invalid page should return error");
}
#[wasm_bindgen_test]
fn test_extract_paths_returns_array() {
let mut doc = doc_from_text("No paths");
let result = doc.extract_paths(0).unwrap();
assert!(js_sys::Array::is_array(&result), "extractPaths should return an array");
}
#[wasm_bindgen_test]
fn test_extract_paths_invalid_page() {
let mut doc = doc_from_text("Hello");
let result = doc.extract_paths(999);
assert!(result.is_err(), "invalid page should return error");
}
#[wasm_bindgen_test]
fn test_wasm_pdf_metadata() {
let pdf = WasmPdf::from_text(
"With metadata",
Some("My Title".to_string()),
Some("My Author".to_string()),
)
.unwrap();
assert!(pdf.size() > 0);
let bytes = pdf.to_bytes();
assert!(bytes.starts_with(b"%PDF"));
}
fn make_form_pdf() -> Vec<u8> {
let mut writer = PdfWriter::new();
{
let mut page = writer.add_page(612.0, 792.0);
page.add_text_field(
TextFieldWidget::new("name", Rect::new(72.0, 700.0, 200.0, 20.0)).with_value("Alice"),
);
page.add_checkbox(
CheckboxWidget::new("agree", Rect::new(72.0, 650.0, 15.0, 15.0)).checked(),
);
page.add_combo_box(
ComboBoxWidget::new("color", Rect::new(72.0, 600.0, 150.0, 20.0))
.with_options(vec!["Red", "Blue", "Green"])
.with_value("Blue"),
);
}
writer.finish().expect("Failed to create form PDF")
}
#[wasm_bindgen_test]
fn test_get_form_fields_returns_array() {
let bytes = make_form_pdf();
let mut doc = WasmPdfDocument::new(&bytes).unwrap();
let result = doc.get_form_fields().unwrap();
assert!(js_sys::Array::is_array(&result), "getFormFields should return an array");
let arr = js_sys::Array::from(&result);
assert!(arr.length() >= 3, "Should have at least 3 form fields, got {}", arr.length());
}
#[wasm_bindgen_test]
fn test_get_form_fields_has_name_and_type() {
let bytes = make_form_pdf();
let mut doc = WasmPdfDocument::new(&bytes).unwrap();
let result = doc.get_form_fields().unwrap();
let arr = js_sys::Array::from(&result);
let first = arr.get(0);
let name = js_sys::Reflect::get(&first, &JsValue::from_str("name")).unwrap();
assert!(name.is_string(), "field should have a string 'name'");
let ft = js_sys::Reflect::get(&first, &JsValue::from_str("field_type")).unwrap();
assert!(ft.is_string(), "field should have a string 'field_type'");
}
#[wasm_bindgen_test]
fn test_get_form_fields_has_value() {
let bytes = make_form_pdf();
let mut doc = WasmPdfDocument::new(&bytes).unwrap();
let result = doc.get_form_fields().unwrap();
let arr = js_sys::Array::from(&result);
for i in 0..arr.length() {
let field = arr.get(i);
let ft = js_sys::Reflect::get(&field, &JsValue::from_str("field_type")).unwrap();
if ft.as_string().as_deref() == Some("text") {
let value = js_sys::Reflect::get(&field, &JsValue::from_str("value")).unwrap();
assert!(value.is_string(), "text field should have a string value");
return;
}
}
}
#[wasm_bindgen_test]
fn test_get_form_fields_empty_on_plain_pdf() {
let mut doc = doc_from_text("No forms here");
let result = doc.get_form_fields().unwrap();
let arr = js_sys::Array::from(&result);
assert_eq!(arr.length(), 0, "Plain text PDF should have no form fields");
}
#[wasm_bindgen_test]
fn test_has_xfa_false_on_plain_pdf() {
let mut doc = doc_from_text("No XFA");
let result = doc.has_xfa().unwrap();
assert!(!result, "Plain text PDF should not have XFA");
}
#[wasm_bindgen_test]
fn test_has_xfa_false_on_acroform_pdf() {
let bytes = make_form_pdf();
let mut doc = WasmPdfDocument::new(&bytes).unwrap();
let result = doc.has_xfa().unwrap();
assert!(!result, "PdfWriter-created form should not have XFA");
}
#[wasm_bindgen_test]
fn test_set_and_get_form_field_value() {
let bytes = make_form_pdf();
let mut doc = WasmPdfDocument::new(&bytes).unwrap();
doc.set_form_field_value("name", JsValue::from_str("Bob"))
.unwrap();
let result = doc.get_form_field_value("name").unwrap();
assert!(result.is_string(), "text field value should be a string");
assert_eq!(result.as_string().unwrap(), "Bob");
}
#[wasm_bindgen_test]
fn test_set_checkbox_form_field() {
let bytes = make_form_pdf();
let mut doc = WasmPdfDocument::new(&bytes).unwrap();
doc.set_form_field_value("agree", JsValue::from(true))
.unwrap();
let result = doc.get_form_field_value("agree").unwrap();
assert_eq!(result.as_bool(), Some(true), "checkbox should be true");
}
#[wasm_bindgen_test]
fn test_get_form_field_value_not_found() {
let bytes = make_form_pdf();
let mut doc = WasmPdfDocument::new(&bytes).unwrap();
let result = doc.get_form_field_value("nonexistent_field").unwrap();
assert!(result.is_null(), "non-existent field should return null");
}
#[wasm_bindgen_test]
fn test_extract_image_bytes_empty() {
let mut doc = doc_from_text("No images");
let result = doc.extract_image_bytes(0).unwrap();
assert!(js_sys::Array::is_array(&result), "should return an array");
let arr = js_sys::Array::from(&result);
assert_eq!(arr.length(), 0, "text-only PDF should have no images");
}
fn create_minimal_image() -> Vec<u8> {
vec![
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x08, 0x06, 0x06, 0x07, 0x06,
0x05, 0x08, 0x07, 0x07, 0x07, 0x09, 0x09, 0x08, 0x0A, 0x0C, 0x14, 0x0D, 0x0C, 0x0B, 0x0B,
0x0C, 0x19, 0x12, 0x13, 0x0F, 0x14, 0x1D, 0x1A, 0x1F, 0x1E, 0x1D, 0x1A, 0x1C, 0x1C, 0x20,
0x24, 0x2E, 0x27, 0x20, 0x22, 0x2C, 0x23, 0x1C, 0x1C, 0x28, 0x37, 0x29, 0x2C, 0x30, 0x31,
0x34, 0x34, 0x34, 0x1F, 0x27, 0x39, 0x3D, 0x38, 0x32, 0x3C, 0x2E, 0x33, 0x34, 0x32, 0xFF,
0xC0, 0x00, 0x0B, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xC4, 0x00,
0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05,
0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08,
0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A,
0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93,
0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6,
0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3F, 0x00, 0xFB, 0xD5,
0xDB, 0x20, 0xA8, 0xF9, 0xFF, 0xD9,
]
}
#[wasm_bindgen_test]
fn test_pdf_from_image_bytes() {
let png = create_minimal_image();
let result = WasmPdf::from_image_bytes(&png);
assert!(result.is_ok(), "fromImageBytes should succeed with valid PNG");
let pdf = result.unwrap();
assert!(pdf.size() > 0, "PDF should have content");
let mut doc = WasmPdfDocument::new(&pdf.to_bytes()).unwrap();
assert_eq!(doc.page_count().unwrap(), 1, "should have 1 page");
}
#[wasm_bindgen_test]
fn test_pdf_from_multiple_image_bytes() {
let png1 = create_minimal_image();
let png2 = create_minimal_image();
let arr = js_sys::Array::new();
arr.push(&js_sys::Uint8Array::from(png1.as_slice()));
arr.push(&js_sys::Uint8Array::from(png2.as_slice()));
let result = WasmPdf::from_multiple_image_bytes(arr.into());
assert!(result.is_ok(), "fromMultipleImageBytes should succeed");
let pdf = result.unwrap();
let mut doc = WasmPdfDocument::new(&pdf.to_bytes()).unwrap();
assert_eq!(doc.page_count().unwrap(), 2, "should have 2 pages");
}
#[wasm_bindgen_test]
fn test_flatten_forms() {
let bytes = make_form_pdf();
let mut doc = WasmPdfDocument::new(&bytes).unwrap();
let fields_before = doc.get_form_fields().unwrap();
let arr_before = js_sys::Array::from(&fields_before);
assert!(arr_before.length() >= 3, "should have fields before flatten");
doc.flatten_forms().unwrap();
let saved = doc.save_to_bytes().unwrap();
let mut doc2 = WasmPdfDocument::new(&saved).unwrap();
let fields_after = doc2.get_form_fields().unwrap();
let arr_after = js_sys::Array::from(&fields_after);
assert_eq!(arr_after.length(), 0, "should have no fields after flatten");
}
#[wasm_bindgen_test]
fn test_merge_from() {
let bytes1 = make_text_pdf("Document 1");
let bytes2 = make_text_pdf("Document 2");
let mut doc = WasmPdfDocument::new(&bytes1).unwrap();
let count = doc.merge_from(&bytes2).unwrap();
assert_eq!(count, 1, "should merge 1 page");
}
#[wasm_bindgen_test]
fn test_embed_file() {
let mut doc = doc_from_text("Hello");
doc.embed_file("test.txt", b"Hello embedded").unwrap();
let bytes = doc.save_to_bytes().unwrap();
assert!(bytes.starts_with(b"%PDF"));
}
#[wasm_bindgen_test]
fn test_page_labels_empty() {
let mut doc = doc_from_text("Hello");
let result = doc.page_labels().unwrap();
assert!(js_sys::Array::is_array(&result), "should return an array");
}
#[wasm_bindgen_test]
fn test_xmp_metadata_null_or_object() {
let mut doc = doc_from_text("Hello");
let result = doc.xmp_metadata().unwrap();
assert!(
result.is_null() || result.is_object(),
"xmpMetadata should return null or an object"
);
}