Skip to main content

superbook_pdf/margin/
types.rs

1//! Margin module core types
2//!
3//! Contains basic data structures for margin detection and processing.
4
5use std::path::{Path, PathBuf};
6use thiserror::Error;
7
8// ============================================================
9// Error Types
10// ============================================================
11
12/// Margin error types
13#[derive(Debug, Error)]
14pub enum MarginError {
15    #[error("Image not found: {0}")]
16    ImageNotFound(PathBuf),
17
18    #[error("Invalid image: {0}")]
19    InvalidImage(String),
20
21    #[error("No content detected in image")]
22    NoContentDetected,
23
24    #[error("IO error: {0}")]
25    IoError(#[from] std::io::Error),
26}
27
28pub type Result<T> = std::result::Result<T, MarginError>;
29
30// ============================================================
31// Core Data Structures
32// ============================================================
33
34/// Margin information in pixels
35#[derive(Debug, Clone, Copy, Default)]
36pub struct Margins {
37    pub top: u32,
38    pub bottom: u32,
39    pub left: u32,
40    pub right: u32,
41}
42
43impl Margins {
44    /// Create uniform margins
45    pub fn uniform(value: u32) -> Self {
46        Self {
47            top: value,
48            bottom: value,
49            left: value,
50            right: value,
51        }
52    }
53
54    /// Total horizontal margin
55    pub fn total_horizontal(&self) -> u32 {
56        self.left + self.right
57    }
58
59    /// Total vertical margin
60    pub fn total_vertical(&self) -> u32 {
61        self.top + self.bottom
62    }
63}
64
65/// Content rectangle
66#[derive(Debug, Clone, Copy)]
67pub struct ContentRect {
68    pub x: u32,
69    pub y: u32,
70    pub width: u32,
71    pub height: u32,
72}
73
74/// Margin detection result
75#[derive(Debug, Clone)]
76pub struct MarginDetection {
77    /// Detected margins
78    pub margins: Margins,
79    /// Image size
80    pub image_size: (u32, u32),
81    /// Content rectangle
82    pub content_rect: ContentRect,
83    /// Detection confidence
84    pub confidence: f64,
85}
86
87/// Unified margins result
88#[derive(Debug, Clone)]
89pub struct UnifiedMargins {
90    /// Common margins for all pages
91    pub margins: Margins,
92    /// Per-page detection results
93    pub page_detections: Vec<MarginDetection>,
94    /// Unified size after trimming
95    pub unified_size: (u32, u32),
96}
97
98/// Trim operation result
99#[derive(Debug)]
100pub struct TrimResult {
101    pub input_path: PathBuf,
102    pub output_path: PathBuf,
103    pub original_size: (u32, u32),
104    pub trimmed_size: (u32, u32),
105    pub margins_applied: Margins,
106}
107
108// ============================================================
109// Detector Trait
110// ============================================================
111
112use super::MarginOptions;
113
114/// Margin detector trait
115pub trait MarginDetector {
116    /// Detect margins in a single image
117    fn detect(image_path: &Path, options: &MarginOptions) -> Result<MarginDetection>;
118
119    /// Detect unified margins for multiple images
120    fn detect_unified(images: &[PathBuf], options: &MarginOptions) -> Result<UnifiedMargins>;
121
122    /// Trim image using specified margins
123    fn trim(input_path: &Path, output_path: &Path, margins: &Margins) -> Result<TrimResult>;
124
125    /// Pad image to target size
126    fn pad_to_size(
127        input_path: &Path,
128        output_path: &Path,
129        target_size: (u32, u32),
130        background: [u8; 3],
131    ) -> Result<TrimResult>;
132
133    /// Process batch with unified margins
134    fn process_batch(
135        images: &[(PathBuf, PathBuf)],
136        options: &MarginOptions,
137    ) -> Result<Vec<TrimResult>>;
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_margins_uniform() {
146        let margins = Margins::uniform(10);
147        assert_eq!(margins.top, 10);
148        assert_eq!(margins.bottom, 10);
149        assert_eq!(margins.left, 10);
150        assert_eq!(margins.right, 10);
151    }
152
153    #[test]
154    fn test_margins_total() {
155        let margins = Margins {
156            top: 10,
157            bottom: 20,
158            left: 15,
159            right: 25,
160        };
161        assert_eq!(margins.total_horizontal(), 40);
162        assert_eq!(margins.total_vertical(), 30);
163    }
164
165    #[test]
166    fn test_margins_default() {
167        let margins = Margins::default();
168        assert_eq!(margins.top, 0);
169        assert_eq!(margins.bottom, 0);
170        assert_eq!(margins.left, 0);
171        assert_eq!(margins.right, 0);
172    }
173}