use easyofd_core::OfdPage;
pub type ExtractorFilterFn = dyn Fn(&str) -> Option<String>;
#[derive(Default)]
pub struct ContentExtractor {
filter: Option<Box<ExtractorFilterFn>>,
}
pub type ReceiverFn = dyn FnMut(usize, &[String]);
impl ContentExtractor {
#[must_use]
pub fn new() -> Self {
Self { filter: None }
}
#[must_use]
pub fn with_filter(filter: Box<ExtractorFilterFn>) -> Self {
Self {
filter: Some(filter),
}
}
#[must_use]
pub fn get_page_content(&self, pages: &[OfdPage], page_num: usize) -> Vec<String> {
if page_num == 0 || page_num > pages.len() {
return Vec::new();
}
let page = &pages[page_num - 1];
self.extract_page_texts(page)
}
#[must_use]
pub fn extract_all(&self, pages: &[OfdPage]) -> Vec<String> {
let mut all_texts = Vec::new();
for page in pages {
let page_texts = self.extract_page_texts(page);
all_texts.extend(page_texts);
}
all_texts
}
pub fn traverse(&self, pages: &[OfdPage], mut receiver: impl FnMut(usize, &[String])) {
for (i, page) in pages.iter().enumerate() {
let texts = self.extract_page_texts(page);
if !texts.is_empty() {
receiver(i + 1, &texts);
}
}
}
fn extract_page_texts(&self, page: &OfdPage) -> Vec<String> {
use easyofd_core::ContentObject;
let mut texts = Vec::new();
for obj in &page.content {
if let ContentObject::Text(t) = obj {
let text = &t.text;
if let Some(ref filter) = self.filter {
if let Some(allowed) = filter(text) {
if !allowed.is_empty() {
texts.push(allowed);
}
}
} else if !text.is_empty() {
texts.push(text.clone());
}
}
}
texts
}
}
#[cfg(test)]
mod tests {
use super::*;
use easyofd_core::TextObject;
fn make_page_with_texts(texts: &[&str]) -> OfdPage {
let mut page = OfdPage::new(210.0, 297.0);
for text in texts {
page.add_text(TextObject::new(0.0, 0.0, *text));
}
page
}
#[test]
fn test_extract_single_page() {
let pages = vec![make_page_with_texts(&["Hello", "World"])];
let extractor = ContentExtractor::new();
let result = extractor.get_page_content(&pages, 1);
assert_eq!(result, vec!["Hello", "World"]);
}
#[test]
fn test_extract_page_out_of_range() {
let pages = vec![make_page_with_texts(&["Hello"])];
let extractor = ContentExtractor::new();
assert!(extractor.get_page_content(&pages, 0).is_empty());
assert!(extractor.get_page_content(&pages, 2).is_empty());
}
#[test]
fn test_extract_all() {
let pages = vec![
make_page_with_texts(&["Page1"]),
make_page_with_texts(&["Page2"]),
];
let extractor = ContentExtractor::new();
let result = extractor.extract_all(&pages);
assert_eq!(result, vec!["Page1", "Page2"]);
}
#[test]
fn test_with_filter() {
let pages = vec![make_page_with_texts(&["Hello", "SECRET", "World"])];
let extractor = ContentExtractor::with_filter(Box::new(|text| {
if text.contains("SECRET") {
None
} else {
Some(text.to_string())
}
}));
let result = extractor.get_page_content(&pages, 1);
assert_eq!(result, vec!["Hello", "World"]);
}
#[test]
fn test_traverse() {
let pages = vec![make_page_with_texts(&["A"]), make_page_with_texts(&["B"])];
let extractor = ContentExtractor::new();
let mut visited = Vec::new();
extractor.traverse(&pages, |page_num, texts| {
visited.push((page_num, texts.to_vec()));
});
assert_eq!(visited.len(), 2);
assert_eq!(visited[0].0, 1);
assert_eq!(visited[1].0, 2);
}
}