1use rten_imageproc::{BoundingRect, Rect, RotatedRect};
19
20#[derive(Clone, Debug, PartialEq)]
22pub struct TextChar {
23 pub char: char,
24 pub rect: Rect,
25}
26
27#[derive(Clone, Debug, Default, PartialEq)]
29pub struct TextWord {
30 pub chars: Vec<TextChar>,
31}
32
33impl TextWord {
34 pub fn text(&self) -> String {
35 self.chars.iter().map(|c| c.char).collect()
36 }
37}
38
39#[derive(Clone, Debug, Default, PartialEq)]
41pub struct TextLine {
42 pub words: Vec<TextWord>,
43}
44
45impl TextLine {
46 pub fn new(chars: Vec<TextChar>) -> Self {
47 Self {
48 words: vec![TextWord { chars }],
49 }
50 }
51
52 pub fn text(&self) -> String {
53 self.words
54 .iter()
55 .map(TextWord::text)
56 .collect::<Vec<_>>()
57 .join(" ")
58 }
59}
60
61#[derive(Clone, Debug, PartialEq)]
63pub enum TextItem {
64 Line(TextLine),
65 Word(TextWord),
66 Char(TextChar),
67}
68
69impl TextItem {
70 pub fn as_line(&self) -> Option<&TextLine> {
71 match self {
72 Self::Line(l) => Some(l),
73 _ => None,
74 }
75 }
76}
77
78impl std::fmt::Display for TextLine {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 write!(f, "{}", self.text())
81 }
82}
83
84pub fn line_bounding_rect(words: &[RotatedRect]) -> Option<Rect> {
86 words.iter().fold(None, |br: Option<Rect>, r| match br {
87 Some(br) => Some(br.union(r.bounding_rect().integral_bounding_rect())),
88 None => Some(r.bounding_rect().integral_bounding_rect()),
89 })
90}