#[cfg(test)]
mod form_filling_tests {
use crate::document::Document;
use crate::page::Page;
use crate::text::Font;
use crate::writer::{PdfWriter, WriterConfig};
use std::fs;
use std::io::BufWriter;
use std::process::Command;
use tempfile::TempDir;
fn extract_pdf_text(pdf_path: &std::path::Path) -> String {
let output = Command::new("pdftotext")
.arg(pdf_path)
.arg("-")
.output()
.expect("pdftotext must be installed for tests");
String::from_utf8_lossy(&output.stdout).to_string()
}
#[test]
#[ignore] fn test_form_filling_preserves_template_and_adds_data() {
let temp_dir = TempDir::new().unwrap();
let base_path = temp_dir.path().join("template.pdf");
let filled_path = temp_dir.path().join("filled.pdf");
let mut base_doc = Document::new();
let mut template_page = Page::a4();
template_page
.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 700.0)
.write("Name: _______________")
.unwrap();
template_page
.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 670.0)
.write("Email: ______________")
.unwrap();
base_doc.add_page(template_page);
base_doc.save(&base_path).unwrap();
let mut filled_doc = Document::new();
let mut filled_page = Page::a4();
filled_page
.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 700.0)
.write("Name: _______________")
.unwrap();
filled_page
.text()
.set_font(Font::Helvetica, 12.0)
.at(50.0, 670.0)
.write("Email: ______________")
.unwrap();
filled_page
.text()
.set_font(Font::Helvetica, 12.0)
.at(110.0, 700.0)
.write("Jane Doe")
.unwrap();
filled_page
.text()
.set_font(Font::Helvetica, 12.0)
.at(115.0, 670.0)
.write("jane@example.com")
.unwrap();
filled_doc.add_page(filled_page);
let file = fs::File::create(&filled_path).unwrap();
let writer = BufWriter::new(file);
let mut pdf_writer = PdfWriter::with_config(writer, WriterConfig::incremental());
pdf_writer
.write_incremental_with_page_replacement(&base_path, &mut filled_doc)
.unwrap();
let extracted_text = extract_pdf_text(&filled_path);
assert!(
extracted_text.contains("Name:"),
"Template 'Name:' field missing. Extracted: {}",
extracted_text
);
assert!(
extracted_text.contains("Email:"),
"Template 'Email:' field missing. Extracted: {}",
extracted_text
);
let name_found = extracted_text.contains("Jane Doe")
|| extracted_text.contains("J a n e D o e")
|| extracted_text.contains("J_a_n_e_D_o_e")
|| extracted_text
.replace("_", "")
.replace(" ", "")
.contains("JaneDoe");
assert!(
name_found,
"Filled name 'Jane Doe' missing (checked with flexible spacing). Extracted: {}",
extracted_text
);
let email_found = extracted_text.contains("jane@example.com")
|| extracted_text.contains("jane@ex am ple.com")
|| extracted_text.contains("ja_n_e_@__e_x_a_m_ple.com")
|| extracted_text
.replace("_", "")
.replace(" ", "")
.contains("jane@example.com");
assert!(
email_found,
"Filled email missing (checked with flexible spacing). Extracted: {}",
extracted_text
);
}
#[test]
#[ignore] fn test_page_replacement_keeps_correct_page_count() {
let temp_dir = TempDir::new().unwrap();
let base_path = temp_dir.path().join("base.pdf");
let updated_path = temp_dir.path().join("updated.pdf");
let mut base_doc = Document::new();
base_doc.add_page(Page::a4());
base_doc.save(&base_path).unwrap();
let mut updated_doc = Document::new();
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 12.0)
.at(100.0, 700.0)
.write("Replaced content")
.unwrap();
updated_doc.add_page(page);
let file = fs::File::create(&updated_path).unwrap();
let writer = BufWriter::new(file);
let mut pdf_writer = PdfWriter::with_config(writer, WriterConfig::incremental());
pdf_writer
.write_incremental_with_page_replacement(&base_path, &mut updated_doc)
.unwrap();
let output = Command::new("pdfinfo")
.arg(&updated_path)
.output()
.expect("pdfinfo must be installed");
let info = String::from_utf8_lossy(&output.stdout);
let page_count_line = info.lines().find(|line| line.contains("Pages:")).unwrap();
assert!(
page_count_line.contains("Pages: 1"),
"Expected 1 page, got: {}",
page_count_line
);
}
#[test]
#[ignore] fn test_multiple_page_replacement_preserves_unmodified_pages() {
let temp_dir = TempDir::new().unwrap();
let base_path = temp_dir.path().join("base.pdf");
let updated_path = temp_dir.path().join("updated.pdf");
let mut base_doc = Document::new();
for i in 1..=3 {
let mut page = Page::a4();
page.text()
.set_font(Font::Helvetica, 12.0)
.at(100.0, 700.0)
.write(&format!("Original Page {}", i))
.unwrap();
base_doc.add_page(page);
}
base_doc.save(&base_path).unwrap();
let mut updated_doc = Document::new();
let mut page1 = Page::a4();
page1
.text()
.set_font(Font::Helvetica, 12.0)
.at(100.0, 700.0)
.write("MODIFIED Page 1")
.unwrap();
updated_doc.add_page(page1);
let file = fs::File::create(&updated_path).unwrap();
let writer = BufWriter::new(file);
let mut pdf_writer = PdfWriter::with_config(writer, WriterConfig::incremental());
pdf_writer
.write_incremental_with_page_replacement(&base_path, &mut updated_doc)
.unwrap();
let output = Command::new("pdfinfo")
.arg(&updated_path)
.output()
.expect("pdfinfo required");
let info = String::from_utf8_lossy(&output.stdout);
assert!(info.contains("Pages: 3"));
let page1_text = Command::new("pdftotext")
.args(&["-f", "1", "-l", "1"])
.arg(&updated_path)
.arg("-")
.output()
.unwrap();
let page1_content = String::from_utf8_lossy(&page1_text.stdout);
assert!(page1_content.contains("MODIFIED Page 1"));
let page2_text = Command::new("pdftotext")
.args(&["-f", "2", "-l", "2"])
.arg(&updated_path)
.arg("-")
.output()
.unwrap();
let page2_content = String::from_utf8_lossy(&page2_text.stdout);
assert!(page2_content.contains("Original Page 2"));
let page3_text = Command::new("pdftotext")
.args(&["-f", "3", "-l", "3"])
.arg(&updated_path)
.arg("-")
.output()
.unwrap();
let page3_content = String::from_utf8_lossy(&page3_text.stdout);
assert!(page3_content.contains("Original Page 3"));
}
#[test]
fn test_incremental_update_structure_compliance() {
let temp_dir = TempDir::new().unwrap();
let base_path = temp_dir.path().join("base.pdf");
let updated_path = temp_dir.path().join("updated.pdf");
let mut base_doc = Document::new();
base_doc.add_page(Page::a4());
base_doc.save(&base_path).unwrap();
let base_size = fs::metadata(&base_path).unwrap().len();
let mut updated_doc = Document::new();
updated_doc.add_page(Page::a4());
let file = fs::File::create(&updated_path).unwrap();
let writer = BufWriter::new(file);
let mut pdf_writer = PdfWriter::with_config(writer, WriterConfig::incremental());
pdf_writer
.write_incremental_with_page_replacement(&base_path, &mut updated_doc)
.unwrap();
let updated_size = fs::metadata(&updated_path).unwrap().len();
assert!(updated_size > base_size);
let base_bytes = fs::read(&base_path).unwrap();
let updated_bytes = fs::read(&updated_path).unwrap();
assert_eq!(
&updated_bytes[0..base_size as usize],
&base_bytes[..],
"Base PDF content must be preserved byte-for-byte"
);
let updated_str = String::from_utf8_lossy(&updated_bytes);
assert!(
updated_str.contains("/Prev"),
"/Prev pointer required for ISO 32000-1 ยง7.5.6 compliance"
);
}
}