Skip to main content

edgeparse_core/api/
config.rs

1//! Processing configuration for EdgeParse.
2
3use serde::{Deserialize, Serialize};
4
5/// Output format selection.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub enum OutputFormat {
8    /// JSON structured output
9    Json,
10    /// Plain text
11    Text,
12    /// HTML5
13    Html,
14    /// Annotated PDF with bounding boxes
15    Pdf,
16    /// Markdown
17    Markdown,
18    /// Markdown with HTML tables
19    MarkdownWithHtml,
20    /// Markdown with embedded images
21    MarkdownWithImages,
22}
23
24/// Table detection method.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26pub enum TableMethod {
27    /// Border-based detection only
28    Default,
29    /// Border + cluster detection
30    Cluster,
31}
32
33/// Reading order algorithm.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35pub enum ReadingOrder {
36    /// No reading order sorting
37    Off,
38    /// XY-Cut++ algorithm
39    XyCut,
40}
41
42/// Image output mode.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44pub enum ImageOutput {
45    /// No image extraction
46    Off,
47    /// Base64 data URIs embedded in output
48    Embedded,
49    /// External file references
50    External,
51}
52
53/// Image format for extracted images.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
55pub enum ImageFormat {
56    /// PNG format
57    Png,
58    /// JPEG format
59    Jpeg,
60}
61
62/// Hybrid backend selection.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
64pub enum HybridBackend {
65    /// No hybrid processing
66    Off,
67    /// Docling Fast server
68    DoclingFast,
69}
70
71/// Hybrid triage mode.
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73pub enum HybridMode {
74    /// Dynamic triage — route pages based on complexity
75    Auto,
76    /// Skip triage — send all pages to backend
77    Full,
78}
79
80/// Main processing configuration (24 options from CLI).
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct ProcessingConfig {
83    /// Output directory (default: input file directory)
84    pub output_dir: Option<String>,
85    /// Password for encrypted PDFs
86    pub password: Option<String>,
87    /// Output formats
88    pub formats: Vec<OutputFormat>,
89    /// Suppress console logging
90    pub quiet: bool,
91    /// Content safety filter configuration
92    pub filter_config: super::filter::FilterConfig,
93    /// Enable PII sanitization
94    pub sanitize: bool,
95    /// Preserve original line breaks
96    pub keep_line_breaks: bool,
97    /// Replacement character for invalid characters
98    pub replace_invalid_chars: String,
99    /// Use PDF structure tree for tagged PDFs
100    pub use_struct_tree: bool,
101    /// Table detection method
102    pub table_method: TableMethod,
103    /// Reading order algorithm
104    pub reading_order: ReadingOrder,
105    /// Page separator for Markdown output
106    pub markdown_page_separator: Option<String>,
107    /// Page separator for text output
108    pub text_page_separator: Option<String>,
109    /// Page separator for HTML output
110    pub html_page_separator: Option<String>,
111    /// Image output mode
112    pub image_output: ImageOutput,
113    /// Image format
114    pub image_format: ImageFormat,
115    /// Directory for extracted images
116    pub image_dir: Option<String>,
117    /// Enable raster table OCR recovery on image-based tables
118    pub raster_table_ocr: bool,
119    /// Soft wall-clock budget for raster OCR recovery (milliseconds).
120    /// `None` = unlimited. When exhausted, remaining images are skipped.
121    pub ocr_budget_ms: Option<u64>,
122    /// Pages to extract (e.g., "1,3,5-7")
123    pub pages: Option<String>,
124    /// Include headers/footers in output
125    pub include_header_footer: bool,
126    /// Hybrid backend
127    pub hybrid: HybridBackend,
128    /// Hybrid triage mode
129    pub hybrid_mode: HybridMode,
130    /// Hybrid backend URL
131    pub hybrid_url: Option<String>,
132    /// Hybrid timeout in milliseconds
133    pub hybrid_timeout: u64,
134    /// Enable local fallback on hybrid error
135    pub hybrid_fallback: bool,
136}
137
138impl Default for ProcessingConfig {
139    fn default() -> Self {
140        Self {
141            output_dir: None,
142            password: None,
143            formats: vec![OutputFormat::Json],
144            quiet: false,
145            filter_config: super::filter::FilterConfig::default(),
146            sanitize: false,
147            keep_line_breaks: false,
148            replace_invalid_chars: " ".to_string(),
149            use_struct_tree: false,
150            table_method: TableMethod::Default,
151            reading_order: ReadingOrder::XyCut,
152            markdown_page_separator: None,
153            text_page_separator: None,
154            html_page_separator: None,
155            image_output: ImageOutput::External,
156            image_format: ImageFormat::Png,
157            image_dir: None,
158            raster_table_ocr: true,
159            ocr_budget_ms: None,
160            pages: None,
161            include_header_footer: false,
162            hybrid: HybridBackend::Off,
163            hybrid_mode: HybridMode::Auto,
164            hybrid_url: None,
165            hybrid_timeout: 30000,
166            hybrid_fallback: false,
167        }
168    }
169}
170
171impl ProcessingConfig {
172    /// Returns true when a hybrid backend is configured.
173    pub fn hybrid_enabled(&self) -> bool {
174        !matches!(self.hybrid, HybridBackend::Off)
175    }
176
177    /// Returns true when local OCR recovery should run.
178    ///
179    /// Classical raster OCR (Tesseract / RapidOCR) recovers tables that exist
180    /// only as page images — required for local TEDS on figure-embedded grids.
181    /// Independent of Docling hybrid; disabled only via `raster_table_ocr`.
182    pub fn raster_table_ocr_enabled(&self) -> bool {
183        self.raster_table_ocr
184    }
185
186    /// OCR wall-clock budget for this document.
187    pub fn ocr_budget(&self) -> crate::pdf::image_region::OcrBudget {
188        crate::pdf::image_region::OcrBudget::from_millis(self.ocr_budget_ms)
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn test_default_config() {
198        let config = ProcessingConfig::default();
199        assert_eq!(config.formats, vec![OutputFormat::Json]);
200        assert!(!config.quiet);
201        assert!(!config.sanitize);
202        assert_eq!(config.reading_order, ReadingOrder::XyCut);
203        assert_eq!(config.table_method, TableMethod::Default);
204        assert_eq!(config.image_output, ImageOutput::External);
205        assert_eq!(config.image_format, ImageFormat::Png);
206        assert!(config.raster_table_ocr);
207        assert_eq!(config.hybrid, HybridBackend::Off);
208        assert_eq!(config.hybrid_timeout, 30000);
209    }
210
211    #[test]
212    fn test_raster_table_ocr_independent_of_hybrid() {
213        let mut config = ProcessingConfig::default();
214        assert!(!config.hybrid_enabled());
215        assert!(config.raster_table_ocr_enabled());
216
217        config.hybrid = HybridBackend::DoclingFast;
218        assert!(config.hybrid_enabled());
219        assert!(config.raster_table_ocr_enabled());
220
221        config.raster_table_ocr = false;
222        assert!(!config.raster_table_ocr_enabled());
223    }
224}