Skip to main content

oxipdf_ir/node/
mod.rs

1pub mod content;
2
3pub use content::*;
4
5use crate::semantic::SemanticRole;
6use crate::style::ResolvedStyle;
7
8/// A stable, unique identifier for a node in the `StyledTree`.
9///
10/// `NodeId` values are assigned during tree construction and must persist
11/// unchanged through layout, fragmentation, and emission. This invariant
12/// is critical for link/annotation emission (§6.5).
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
14pub struct NodeId(u32);
15
16impl NodeId {
17    /// Create a `NodeId` from a raw index.
18    #[must_use]
19    pub const fn from_raw(index: u32) -> Self {
20        Self(index)
21    }
22
23    /// Return the raw index.
24    #[must_use]
25    pub const fn raw(self) -> u32 {
26        self.0
27    }
28}
29
30impl std::fmt::Display for NodeId {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        write!(f, "node#{}", self.0)
33    }
34}
35
36/// A node in the styled IR tree.
37#[derive(Debug, Clone)]
38pub struct Node {
39    pub id: NodeId,
40    pub content: ContentVariant,
41    pub style: ResolvedStyle,
42    /// Ordered child node IDs. Empty for leaf nodes.
43    pub children: Vec<NodeId>,
44    pub semantic_role: Option<SemanticRole>,
45    /// Optional element ID for cross-reference resolution (§5.3).
46    pub element_id: Option<String>,
47}
48
49/// The content variant of an IR node (§5.1).
50#[derive(Debug, Clone)]
51#[non_exhaustive]
52pub enum ContentVariant {
53    Text(TextContent),
54    Svg(SvgContent),
55    Math(MathContent),
56    Image(ImageContent),
57    Table(TableContent),
58    Container,
59    Link(LinkContent),
60    Form(FormContent),
61    /// A footnote. Renders as a superscript marker inline; children are
62    /// the footnote body, placed at the page bottom during emission.
63    Footnote(FootnoteContent),
64    /// An invisible index entry marker. Produces no visible output; the
65    /// engine collects these after fragmentation to generate the index.
66    IndexEntry(IndexEntryContent),
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn node_id_ordering() {
75        assert!(NodeId::from_raw(0) < NodeId::from_raw(1));
76    }
77
78    #[test]
79    fn node_id_display() {
80        assert_eq!(NodeId::from_raw(42).to_string(), "node#42");
81    }
82}