Skip to main content

pdfium_render/pdf/document/page/
struct_tree.rs

1//! Defines the [PdfStructTree] struct, exposing the structure tree of a PDF page.
2
3use crate::bindgen::FPDF_STRUCTTREE;
4use crate::bindings::PdfiumLibraryBindings;
5use crate::pdf::document::page::struct_element::PdfStructElement;
6use std::os::raw::c_int;
7
8/// The structure tree of a PDF page, providing semantic document structure
9/// for tagged PDFs.
10///
11/// Elements in the tree have types like P (paragraph), H1-H6 (headings),
12/// Table, Figure, etc. The tree must be closed when it is no longer needed;
13/// this is handled automatically via the `Drop` implementation.
14///
15/// The tree handle is owned by this struct and will be released when it
16/// goes out of scope.
17pub struct PdfStructTree<'a> {
18    tree_handle: FPDF_STRUCTTREE,
19    bindings: &'a dyn PdfiumLibraryBindings,
20}
21
22impl<'a> PdfStructTree<'a> {
23    pub(crate) fn from_pdfium(tree_handle: FPDF_STRUCTTREE, bindings: &'a dyn PdfiumLibraryBindings) -> Self {
24        Self { tree_handle, bindings }
25    }
26
27    /// Returns the number of top-level children in this structure tree.
28    pub fn children_count(&self) -> usize {
29        let count = self.bindings.FPDF_StructTree_CountChildren(self.tree_handle);
30        if count < 0 { 0 } else { count as usize }
31    }
32
33    /// Returns the top-level child element at the given index, or `None` on error
34    /// or out-of-bounds index.
35    pub fn child_at_index(&self, index: usize) -> Option<PdfStructElement<'_>> {
36        let handle = self
37            .bindings
38            .FPDF_StructTree_GetChildAtIndex(self.tree_handle, index as c_int);
39        if handle.is_null() {
40            None
41        } else {
42            Some(PdfStructElement::from_pdfium(handle, self.bindings))
43        }
44    }
45
46    /// Returns an iterator over the top-level children of this structure tree.
47    pub fn children(&self) -> PdfStructTreeChildrenIterator<'_> {
48        PdfStructTreeChildrenIterator {
49            tree: self,
50            count: self.children_count(),
51            index: 0,
52        }
53    }
54
55    /// Returns a depth-first iterator over all elements in the structure tree.
56    ///
57    /// Each item is a tuple of `(element, depth)` where depth starts at 0 for
58    /// top-level (root) elements and increases by 1 for each level of nesting.
59    pub fn iter(&self) -> PdfStructTreeIterator<'_> {
60        let mut stack = Vec::new();
61        let count = self.children_count();
62        for i in (0..count).rev() {
63            if let Some(child) = self.child_at_index(i) {
64                stack.push((child, 0usize));
65            }
66        }
67        PdfStructTreeIterator { stack }
68    }
69}
70
71impl Drop for PdfStructTree<'_> {
72    fn drop(&mut self) {
73        self.bindings.FPDF_StructTree_Close(self.tree_handle);
74    }
75}
76
77/// An iterator over the top-level children of a [PdfStructTree].
78pub struct PdfStructTreeChildrenIterator<'a> {
79    tree: &'a PdfStructTree<'a>,
80    count: usize,
81    index: usize,
82}
83
84impl<'a> Iterator for PdfStructTreeChildrenIterator<'a> {
85    type Item = PdfStructElement<'a>;
86
87    fn next(&mut self) -> Option<Self::Item> {
88        while self.index < self.count {
89            let current = self.index;
90            self.index += 1;
91            if let Some(child) = self.tree.child_at_index(current) {
92                return Some(child);
93            }
94        }
95        None
96    }
97}
98
99/// A depth-first iterator over all elements in a [PdfStructTree].
100///
101/// Each item yielded is a tuple of `(PdfStructElement, depth)` where depth
102/// starts at 0 for root-level elements and increases for nested elements.
103pub struct PdfStructTreeIterator<'a> {
104    stack: Vec<(PdfStructElement<'a>, usize)>,
105}
106
107impl<'a> Iterator for PdfStructTreeIterator<'a> {
108    type Item = (PdfStructElement<'a>, usize);
109
110    fn next(&mut self) -> Option<Self::Item> {
111        let (element, depth) = self.stack.pop()?;
112
113        let child_count = element.children_count();
114        for i in (0..child_count).rev() {
115            if let Some(child) = element.child_at_index(i) {
116                self.stack.push((child, depth + 1));
117            }
118        }
119
120        Some((element, depth))
121    }
122}