use crate::geometry::Rect;
use crate::text::RichLine;
use lightweight_pdf_core::{Align, Border, Color, ImageFormat, TextStyle};
use std::sync::Arc;
#[derive(Clone, Debug)]
pub enum RenderNode {
Empty,
TextLines {
area: Rect,
style: TextStyle,
lines: Vec<String>,
paragraph_end: Vec<bool>,
line_height_pt: f32,
url: Option<String>,
anchor: Option<String>,
link_to: Option<String>,
outline_level: Option<u8>,
},
RichTextLines {
area: Rect,
align: Align,
lines: Vec<RichLine>,
},
Rect {
area: Rect,
background: Option<Color>,
border: Option<Border>,
corner_radius: f32,
},
Line {
x1: f32,
y1: f32,
x2: f32,
y2: f32,
thickness: f32,
color: Color,
},
Image {
area: Rect,
bytes: Arc<[u8]>,
format: ImageFormat,
width_px: u32,
height_px: u32,
components: u8,
alt: Option<String>,
},
Group {
area: Rect,
clip: bool,
background: Option<Color>,
border: Option<Border>,
corner_radius: f32,
children: Vec<RenderNode>,
},
Tagged {
role: StructRole,
inner: Box<RenderNode>,
},
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum StructRole {
Document,
Heading(u8),
Paragraph,
Figure,
Table,
TableRow,
TableHeaderCell,
TableCell,
List,
ListItem,
ListItemLabel,
ListItemBody,
Toc,
TocItem,
Artifact,
}
impl StructRole {
pub fn is_grouping(self) -> bool {
matches!(
self,
StructRole::Document
| StructRole::Table
| StructRole::TableRow
| StructRole::TableHeaderCell
| StructRole::TableCell
| StructRole::List
| StructRole::ListItem
| StructRole::ListItemLabel
| StructRole::ListItemBody
| StructRole::Toc
)
}
pub fn tag_name(self) -> &'static str {
match self {
StructRole::Document => "Document",
StructRole::Heading(n) => match n.clamp(1, 6) {
1 => "H1",
2 => "H2",
3 => "H3",
4 => "H4",
5 => "H5",
_ => "H6",
},
StructRole::Paragraph => "P",
StructRole::Figure => "Figure",
StructRole::Table => "Table",
StructRole::TableRow => "TR",
StructRole::TableHeaderCell => "TH",
StructRole::TableCell => "TD",
StructRole::List => "L",
StructRole::ListItem => "LI",
StructRole::ListItemLabel => "Lbl",
StructRole::ListItemBody => "LBody",
StructRole::Toc => "TOC",
StructRole::TocItem => "TOCI",
StructRole::Artifact => "Artifact",
}
}
}
impl RenderNode {
pub fn clipped(area: Rect, inner: RenderNode) -> RenderNode {
RenderNode::Group {
area,
clip: true,
background: None,
border: None,
corner_radius: 0.0,
children: vec![inner],
}
}
pub fn height(&self) -> f32 {
match self {
RenderNode::Empty => 0.0,
RenderNode::TextLines { area, .. }
| RenderNode::RichTextLines { area, .. }
| RenderNode::Rect { area, .. }
| RenderNode::Group { area, .. }
| RenderNode::Image { area, .. } => area.height,
RenderNode::Line { .. } => 0.0,
RenderNode::Tagged { inner, .. } => inner.height(),
}
}
#[cfg(test)]
pub fn untagged(&self) -> &RenderNode {
match self {
RenderNode::Tagged { inner, .. } => inner.untagged(),
other => other,
}
}
pub fn tagged(role: StructRole, inner: RenderNode) -> RenderNode {
RenderNode::Tagged {
role,
inner: Box::new(inner),
}
}
}
pub fn align_offset(align: Align, available: f32, used: f32) -> f32 {
match align {
Align::Start | Align::Justify => 0.0,
Align::Center => ((available - used) / 2.0).max(0.0),
Align::End => (available - used).max(0.0),
}
}