use std::cmp::Ordering;
#[cfg(feature = "semantic")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "semantic",
derive(Serialize, Deserialize),
serde(tag = "type", rename_all = "snake_case")
)]
pub enum Element {
Title(ElementData),
Paragraph(ElementData),
Table(TableElementData),
Header(ElementData),
Footer(ElementData),
ListItem(ElementData),
Image(ImageElementData),
CodeBlock(ElementData),
KeyValue(KeyValueElementData),
}
impl Element {
pub fn text(&self) -> &str {
match self {
Self::Title(d)
| Self::Paragraph(d)
| Self::Header(d)
| Self::Footer(d)
| Self::ListItem(d)
| Self::CodeBlock(d) => &d.text,
Self::Table(t) => {
let _ = t;
""
}
Self::Image(img) => img.alt_text.as_deref().unwrap_or(""),
Self::KeyValue(kv) => &kv.value,
}
}
pub fn display_text(&self) -> String {
match self {
Self::Table(t) => t
.rows
.iter()
.map(|row| row.join(" | "))
.collect::<Vec<_>>()
.join("\n"),
Self::Image(img) => img.alt_text.clone().unwrap_or_default(),
Self::KeyValue(kv) => format!("{}: {}", kv.key, kv.value),
_ => self.text().to_string(),
}
}
pub fn page(&self) -> u32 {
self.metadata().page
}
pub fn bbox(&self) -> &ElementBBox {
&self.metadata().bbox
}
pub fn metadata(&self) -> &ElementMetadata {
match self {
Self::Title(d)
| Self::Paragraph(d)
| Self::Header(d)
| Self::Footer(d)
| Self::ListItem(d)
| Self::CodeBlock(d) => &d.metadata,
Self::Table(t) => &t.metadata,
Self::Image(img) => &img.metadata,
Self::KeyValue(kv) => &kv.metadata,
}
}
pub fn metadata_mut(&mut self) -> &mut ElementMetadata {
match self {
Self::Title(d)
| Self::Paragraph(d)
| Self::Header(d)
| Self::Footer(d)
| Self::ListItem(d)
| Self::CodeBlock(d) => &mut d.metadata,
Self::Table(t) => &mut t.metadata,
Self::Image(img) => &mut img.metadata,
Self::KeyValue(kv) => &mut kv.metadata,
}
}
pub fn type_name(&self) -> &'static str {
match self {
Self::Title(_) => "title",
Self::Paragraph(_) => "paragraph",
Self::Table(_) => "table",
Self::Header(_) => "header",
Self::Footer(_) => "footer",
Self::ListItem(_) => "list_item",
Self::Image(_) => "image",
Self::CodeBlock(_) => "code_block",
Self::KeyValue(_) => "key_value",
}
}
pub fn set_parent_heading(&mut self, heading: Option<String>) {
self.metadata_mut().parent_heading = heading;
}
pub fn row_count(&self) -> Option<usize> {
match self {
Self::Table(t) => Some(t.rows.len()),
_ => None,
}
}
pub fn column_count(&self) -> Option<usize> {
match self {
Self::Table(t) => t.rows.first().map(|r| r.len()),
_ => None,
}
}
pub fn cell(&self, row: usize, col: usize) -> Option<&str> {
match self {
Self::Table(t) => t.rows.get(row).and_then(|r| r.get(col)).map(|s| s.as_str()),
_ => None,
}
}
}
pub fn element_reading_order(a: &Element, b: &Element) -> Ordering {
let page_cmp = a.page().cmp(&b.page());
if page_cmp != Ordering::Equal {
return page_cmp;
}
let y_cmp = b.bbox().y.total_cmp(&a.bbox().y);
if y_cmp != Ordering::Equal {
return y_cmp;
}
a.bbox().x.total_cmp(&b.bbox().x)
}
impl PartialEq for Element {
fn eq(&self, other: &Self) -> bool {
std::mem::discriminant(self) == std::mem::discriminant(other) && self.content_eq(other)
}
}
impl Eq for Element {}
impl Element {
fn content_eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Title(a), Self::Title(b))
| (Self::Paragraph(a), Self::Paragraph(b))
| (Self::Header(a), Self::Header(b))
| (Self::Footer(a), Self::Footer(b))
| (Self::ListItem(a), Self::ListItem(b))
| (Self::CodeBlock(a), Self::CodeBlock(b)) => a.text == b.text,
(Self::Table(a), Self::Table(b)) => a.rows == b.rows,
(Self::Image(a), Self::Image(b)) => a.alt_text == b.alt_text,
(Self::KeyValue(a), Self::KeyValue(b)) => a.key == b.key && a.value == b.value,
_ => false,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "semantic", derive(Serialize, Deserialize))]
pub struct ElementData {
pub text: String,
pub metadata: ElementMetadata,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "semantic", derive(Serialize, Deserialize))]
pub struct TableElementData {
pub rows: Vec<Vec<String>>,
pub metadata: ElementMetadata,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "semantic", derive(Serialize, Deserialize))]
pub struct ImageElementData {
pub alt_text: Option<String>,
pub metadata: ElementMetadata,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "semantic", derive(Serialize, Deserialize))]
pub struct KeyValueElementData {
pub key: String,
pub value: String,
pub metadata: ElementMetadata,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "semantic", derive(Serialize, Deserialize))]
pub struct ElementMetadata {
pub page: u32,
pub bbox: ElementBBox,
pub confidence: f64,
pub font_name: Option<String>,
pub font_size: Option<f64>,
pub is_bold: bool,
pub is_italic: bool,
pub parent_heading: Option<String>,
}
impl Default for ElementMetadata {
fn default() -> Self {
Self {
page: 0,
bbox: ElementBBox::ZERO,
confidence: 1.0,
font_name: None,
font_size: None,
is_bold: false,
is_italic: false,
parent_heading: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "semantic", derive(Serialize, Deserialize))]
pub struct ElementBBox {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
}
impl ElementBBox {
pub const ZERO: Self = Self {
x: 0.0,
y: 0.0,
width: 0.0,
height: 0.0,
};
pub fn new(x: f64, y: f64, width: f64, height: f64) -> Self {
Self {
x,
y,
width,
height,
}
}
pub fn right(&self) -> f64 {
self.x + self.width
}
pub fn top(&self) -> f64 {
self.y + self.height
}
}