#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rect {
pub x0: f32, pub y0: f32, pub x1: f32, pub y1: f32, }
impl Rect {
pub fn new(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
Self {
x0: x0.min(x1),
y0: y0.min(y1),
x1: x0.max(x1),
y1: y0.max(y1),
}
}
pub fn width(&self) -> f32 {
(self.x1 - self.x0).max(0.0)
}
pub fn height(&self) -> f32 {
(self.y1 - self.y0).max(0.0)
}
pub fn union(&self, other: &Rect) -> Rect {
Rect {
x0: self.x0.min(other.x0),
y0: self.y0.min(other.y0),
x1: self.x1.max(other.x1),
y1: self.y1.max(other.y1),
}
}
pub fn expand(&self, pad_x: f32, pad_y: f32) -> Rect {
Rect {
x0: self.x0 - pad_x,
y0: self.y0 - pad_y,
x1: self.x1 + pad_x,
y1: self.y1 + pad_y,
}
}
pub fn contains_point(&self, x: f32, y: f32) -> bool {
x >= self.x0 && x <= self.x1 && y >= self.y0 && y <= self.y1
}
pub fn overlaps(&self, other: &Rect) -> bool {
self.x0 < other.x1 && self.x1 > other.x0 && self.y0 < other.y1 && self.y1 > other.y0
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TextToken {
pub text: String,
pub normalized_text: String,
pub bounds: Rect,
pub page_index: usize,
pub column_index: usize,
pub line_index: usize,
pub trailing_space: bool,
}
impl TextToken {
pub fn new(
text: String,
normalized_text: String,
bounds: Rect,
page_index: usize,
column_index: usize,
line_index: usize,
trailing_space: bool,
) -> Self {
Self {
text,
normalized_text,
bounds,
page_index,
column_index,
line_index,
trailing_space,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DiffOpKind {
Equal,
Delete,
Insert,
Replace,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HighlightSpan {
pub bounds: Rect,
pub op: DiffOpKind,
pub is_modified_token: bool,
}
impl HighlightSpan {
pub fn new(bounds: Rect, op: DiffOpKind, is_modified_token: bool) -> Self {
Self {
bounds,
op,
is_modified_token,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PageText {
pub tokens: Vec<TextToken>,
pub width: f32,
pub height: f32,
pub page_index: usize,
}
impl PageText {
pub fn new(tokens: Vec<TextToken>, width: f32, height: f32, page_index: usize) -> Self {
Self {
tokens,
width,
height,
page_index,
}
}
pub fn is_empty(&self) -> bool {
self.tokens.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rect_math() {
let r1 = Rect::new(10.0, 20.0, 50.0, 60.0);
assert_eq!(r1.width(), 40.0);
assert_eq!(r1.height(), 40.0);
assert!(r1.contains_point(30.0, 40.0));
assert!(!r1.contains_point(5.0, 40.0));
let r2 = Rect::new(40.0, 50.0, 80.0, 90.0);
assert!(r1.overlaps(&r2));
let u = r1.union(&r2);
assert_eq!(u, Rect::new(10.0, 20.0, 80.0, 90.0));
let exp = r1.expand(0.5, 1.0);
assert_eq!(exp, Rect::new(9.5, 19.0, 50.5, 61.0));
}
#[test]
fn test_rect_new_inverted_coordinates() {
let r = Rect::new(50.0, 60.0, 10.0, 20.0);
assert_eq!(
r,
Rect {
x0: 10.0,
y0: 20.0,
x1: 50.0,
y1: 60.0,
}
);
assert_eq!(r.width(), 40.0);
assert_eq!(r.height(), 40.0);
}
#[test]
fn test_token_and_page_text() {
let rect = Rect::new(0.0, 0.0, 100.0, 20.0);
let token = TextToken::new(
"Hello".to_string(),
"Hello".to_string(),
rect,
0,
0,
0,
true,
);
assert_eq!(token.text, "Hello");
assert!(token.trailing_space);
let page = PageText::new(vec![token], 612.0, 792.0, 0);
assert!(!page.is_empty());
assert_eq!(page.tokens.len(), 1);
}
}