easypdf_markdown/processors/
reading_order.rs1use easypdf_core::PdfInput;
4use easypdf_core::Result;
5use easypdf_core::{PdfBlock, PdfDocumentModel, PdfPageModel};
6
7use crate::{MarkdownProcessorCapabilities, MarkdownWarning, PdfMarkdownProcessor};
8
9#[derive(Clone, Copy, Debug, Default)]
28pub struct ReadingOrderProcessor;
29
30impl PdfMarkdownProcessor for ReadingOrderProcessor {
31 fn capabilities(&self) -> MarkdownProcessorCapabilities {
32 MarkdownProcessorCapabilities::new().with_reading_order()
33 }
34
35 fn process(
36 &self,
37 _input: &PdfInput,
38 document: PdfDocumentModel,
39 ) -> Result<(PdfDocumentModel, Vec<MarkdownWarning>)> {
40 let mut new_pages = Vec::with_capacity(document.page_count());
43 for page in document.pages() {
44 let sorted_blocks = Self::sort_blocks_by_reading_order(page);
45 let new_page = rebuild_page(page, sorted_blocks);
46 new_pages.push(new_page);
47 }
48 Ok((
49 PdfDocumentModel::new(document.metadata().clone(), new_pages),
50 Vec::new(),
51 ))
52 }
53}
54
55impl ReadingOrderProcessor {
56 fn sort_blocks_by_reading_order(page: &PdfPageModel) -> Vec<&PdfBlock> {
60 page.blocks().iter().collect()
61 }
62}
63
64fn rebuild_page(page: &PdfPageModel, blocks: Vec<&PdfBlock>) -> PdfPageModel {
66 let mut new_page = PdfPageModel::new(page.index());
67 if let (Some(w), Some(h)) = (page.width_pt(), page.height_pt()) {
68 new_page = new_page.with_dimensions(w, h);
69 }
70 new_page = new_page.with_rotation(page.rotation());
71 for block in blocks {
72 new_page = new_page.with_block(block.clone());
73 }
74 new_page
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use easypdf_core::SourceLocation;
81 use easypdf_core::{PageIndex, PdfMetadata};
82
83 #[test]
84 fn capabilities_include_reading_order() {
85 let proc = ReadingOrderProcessor;
86 assert!(proc.capabilities().reading_order());
87 }
88
89 #[test]
90 fn preserves_block_count() {
91 let proc = ReadingOrderProcessor;
92 let loc = SourceLocation::new(PageIndex::new(0), 1.0);
93 let page = PdfPageModel::new(PageIndex::new(0))
94 .with_block(PdfBlock::heading(1, "Title", loc))
95 .with_block(PdfBlock::paragraph("Body", loc));
96 let doc = PdfDocumentModel::new(PdfMetadata::default(), vec![page]);
97 let (result, warnings) = proc.process(&PdfInput::from_bytes(vec![]), doc).unwrap();
98 assert!(warnings.is_empty());
99 assert_eq!(result.total_blocks(), 2);
100 }
101}