tesseract_ocr_static/
layout.rs1use core::marker::PhantomData;
2use core::ops::Deref;
3use core::ops::DerefMut;
4use core::ptr::NonNull;
5
6use crate::BlockType;
7use crate::Image;
8use crate::LayoutLevel;
9use crate::Line;
10use crate::Orientation;
11use crate::OrientationParams;
12use crate::ParagraphInfo;
13use crate::ParagraphJustification;
14use crate::Rectangle;
15use crate::Tesseract;
16use crate::TextlineOrder;
17use crate::WritingDirection;
18
19pub struct LayoutAnalyzer {
24 base: Tesseract,
25}
26
27impl LayoutAnalyzer {
28 pub fn new() -> Self {
30 let ptr = unsafe { c::TessBaseAPICreate() };
31 let ptr = NonNull::new(ptr).expect("TessBaseAPICreate returned NULL");
32 unsafe { c::TessBaseAPIInitForAnalysePage(ptr.as_ptr()) };
33 let base = Tesseract { ptr };
34 Self { base }
35 }
36
37 pub fn analyze(&mut self, image: &Image) -> LayoutIter<'_> {
39 unsafe { c::TessBaseAPISetImage2(self.as_ptr(), image.ptr.as_ptr()) };
40 let ptr = unsafe { c::TessBaseAPIAnalyseLayout(self.as_ptr()) };
41 let ptr = NonNull::new(ptr).expect("TessBaseAPIAnalyseLayout returned NULL");
42 unsafe { c::TessPageIteratorBegin(ptr.as_ptr()) };
43 LayoutIter {
44 ptr,
45 phantom: PhantomData,
46 }
47 }
48
49 #[inline]
50 fn as_ptr(&self) -> *mut c::TessBaseAPI {
51 self.base.ptr.as_ptr()
52 }
53}
54
55impl Default for LayoutAnalyzer {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl Deref for LayoutAnalyzer {
62 type Target = Tesseract;
63
64 fn deref(&self) -> &Self::Target {
65 &self.base
66 }
67}
68
69impl DerefMut for LayoutAnalyzer {
70 fn deref_mut(&mut self) -> &mut Self::Target {
71 &mut self.base
72 }
73}
74
75pub struct LayoutIter<'a> {
77 pub(crate) ptr: NonNull<c::TessPageIterator>,
78 #[allow(unused)]
79 pub(crate) phantom: PhantomData<&'a Tesseract>,
80}
81
82impl<'a> LayoutIter<'a> {
83 #[must_use]
86 pub fn next(&mut self, level: LayoutLevel) -> Option<Element<'_>> {
87 let ret = unsafe { c::TessPageIteratorNext(self.ptr.as_ptr(), level as u32) };
88 (ret != 0).then_some(Element { iter: self })
89 }
90}
91
92impl Drop for LayoutIter<'_> {
93 fn drop(&mut self) {
94 unsafe { c::TessPageIteratorDelete(self.ptr.as_ptr()) };
95 }
96}
97
98impl Clone for LayoutIter<'_> {
99 fn clone(&self) -> Self {
100 let ptr = unsafe { c::TessPageIteratorCopy(self.ptr.as_ptr()) };
101 let ptr = NonNull::new(ptr).expect("TessPageIteratorCopy returned NULL");
102 Self {
103 ptr,
104 phantom: PhantomData,
105 }
106 }
107}
108
109pub struct Element<'a> {
111 iter: &'a LayoutIter<'a>,
112}
113
114impl Element<'_> {
115 pub fn is_at_beginning_of(&self, level: LayoutLevel) -> bool {
117 let ret =
118 unsafe { c::TessPageIteratorIsAtBeginningOf(self.iter.ptr.as_ptr(), level as u32) };
119 ret != 0
120 }
121
122 pub fn is_at_final_element(&self, level: LayoutLevel, element: LayoutLevel) -> bool {
124 let ret = unsafe {
125 c::TessPageIteratorIsAtFinalElement(
126 self.iter.ptr.as_ptr(),
127 level as u32,
128 element as u32,
129 )
130 };
131 ret != 0
132 }
133
134 pub fn bounding_box(&self, level: LayoutLevel) -> Option<Rectangle> {
142 let mut left = 0;
143 let mut top = 0;
144 let mut right = 0;
145 let mut bottom = 0;
146 let ret = unsafe {
147 c::TessPageIteratorBoundingBox(
148 self.iter.ptr.as_ptr(),
149 level as u32,
150 &mut left,
151 &mut top,
152 &mut right,
153 &mut bottom,
154 )
155 };
156 (ret != 0).then_some(Rectangle {
157 left: left as u32,
158 top: top as u32,
159 width: (right - left) as u32,
160 height: (bottom - top) as u32,
161 })
162 }
163
164 pub fn block_type(&self) -> BlockType {
166 let ret = unsafe { c::TessPageIteratorBlockType(self.iter.ptr.as_ptr()) };
167 BlockType::from_raw(ret)
168 }
169
170 pub fn get_binary_image(&self, level: LayoutLevel) -> Image {
172 let ptr =
173 unsafe { c::TessPageIteratorGetBinaryImage(self.iter.ptr.as_ptr(), level as u32) };
174 let ptr = NonNull::new(ptr).expect("TessPageIteratorGetBinaryImage returned NULL");
175 Image { ptr }
176 }
177
178 pub fn get_image(
185 &self,
186 level: LayoutLevel,
187 padding: u32,
188 original: &Image,
189 ) -> (Image, i32, i32) {
190 let mut left: i32 = 0;
191 let mut top: i32 = 0;
192 let ptr = unsafe {
193 c::TessPageIteratorGetImage(
194 self.iter.ptr.as_ptr(),
195 level as u32,
196 padding as i32,
197 original.ptr.as_ptr(),
198 &mut left,
199 &mut top,
200 )
201 };
202 let ptr = NonNull::new(ptr).expect("TessPageIteratorGetImage returned NULL");
203 (Image { ptr }, left, top)
204 }
205
206 pub fn baseline(&self, level: LayoutLevel) -> Option<Line> {
208 let mut x1 = 0;
209 let mut y1 = 0;
210 let mut x2 = 0;
211 let mut y2 = 0;
212 let ret = unsafe {
213 c::TessPageIteratorBaseline(
214 self.iter.ptr.as_ptr(),
215 level as u32,
216 &mut x1,
217 &mut y1,
218 &mut x2,
219 &mut y2,
220 )
221 };
222 (ret != 0).then_some(Line {
223 x1: x1 as u32,
224 y1: y1 as u32,
225 x2: x2 as u32,
226 y2: y2 as u32,
227 })
228 }
229
230 pub fn orientation(&self) -> OrientationParams {
232 let mut orientation = 0;
233 let mut writing_direction = 0;
234 let mut textline_order = 0;
235 let mut deskew_angle: f32 = 0.0;
236 unsafe {
237 c::TessPageIteratorOrientation(
238 self.iter.ptr.as_ptr(),
239 &mut orientation,
240 &mut writing_direction,
241 &mut textline_order,
242 &mut deskew_angle,
243 )
244 };
245 OrientationParams {
246 orientation: Orientation::from_raw(orientation),
247 writing_direction: WritingDirection::from_raw(writing_direction),
248 textline_order: TextlineOrder::from_raw(textline_order),
249 deskew_angle,
250 }
251 }
252
253 pub fn paragraph_info(&self) -> ParagraphInfo {
255 let mut justification = 0;
256 let mut is_list_item = 0;
257 let mut is_crown = 0;
258 let mut first_line_indent = 0;
259 unsafe {
260 c::TessPageIteratorParagraphInfo(
261 self.iter.ptr.as_ptr(),
262 &mut justification,
263 &mut is_list_item,
264 &mut is_crown,
265 &mut first_line_indent,
266 )
267 };
268 ParagraphInfo {
269 justification: ParagraphJustification::from_raw(justification),
270 is_list_item: is_list_item != 0,
271 is_crown: is_crown != 0,
272 first_line_indent,
273 }
274 }
275}