Skip to main content

docling_pdf/render/
objects.rs

1//! Small lopdf helpers shared by the renderer's modules.
2
3use lopdf::{Dictionary, Document, Object};
4
5/// Follow references (one level is enough: lopdf never nests them).
6pub fn deref<'a>(doc: &'a Document, obj: &'a Object) -> &'a Object {
7    match obj {
8        Object::Reference(id) => doc.get_object(*id).unwrap_or(obj),
9        o => o,
10    }
11}
12
13pub fn as_dict<'a>(doc: &'a Document, obj: &'a Object) -> Option<&'a Dictionary> {
14    match deref(doc, obj) {
15        Object::Dictionary(d) => Some(d),
16        Object::Stream(s) => Some(&s.dict),
17        _ => None,
18    }
19}
20
21pub fn as_stream<'a>(doc: &'a Document, obj: &'a Object) -> Option<&'a lopdf::Stream> {
22    match deref(doc, obj) {
23        Object::Stream(s) => Some(s),
24        _ => None,
25    }
26}
27
28pub fn num(o: &Object) -> Option<f64> {
29    match o {
30        Object::Integer(i) => Some(*i as f64),
31        Object::Real(r) => Some(f64::from(*r)),
32        _ => None,
33    }
34}
35
36/// An array of numbers (references resolved), `None` if not an array.
37pub fn nums(doc: &Document, o: &Object) -> Option<Vec<f64>> {
38    match deref(doc, o) {
39        Object::Array(a) => Some(a.iter().filter_map(|x| num(deref(doc, x))).collect()),
40        _ => None,
41    }
42}
43
44pub fn name(o: &Object) -> Option<&[u8]> {
45    match o {
46        Object::Name(n) => Some(n),
47        _ => None,
48    }
49}
50
51/// `dict[key]` dereferenced.
52pub fn get<'a>(doc: &'a Document, dict: &'a Dictionary, key: &[u8]) -> Option<&'a Object> {
53    dict.get(key).ok().map(|o| deref(doc, o))
54}
55
56/// `dict[key]` or `dict[alt]` (inline-image abbreviations), dereferenced.
57pub fn get2<'a>(
58    doc: &'a Document,
59    dict: &'a Dictionary,
60    key: &[u8],
61    alt: &[u8],
62) -> Option<&'a Object> {
63    get(doc, dict, key).or_else(|| get(doc, dict, alt))
64}
65
66/// `get_int` over `key` or its inline-image abbreviation `alt`.
67pub fn get_int2(doc: &Document, d: &Dictionary, key: &[u8], alt: &[u8]) -> Option<i64> {
68    get_int(doc, d, key).or_else(|| get_int(doc, d, alt))
69}
70
71/// `get_bool` over `key` or its inline-image abbreviation `alt`.
72pub fn get_bool2(doc: &Document, d: &Dictionary, key: &[u8], alt: &[u8]) -> Option<bool> {
73    get_bool(doc, d, key).or_else(|| get_bool(doc, d, alt))
74}
75
76pub fn get_num(doc: &Document, dict: &Dictionary, key: &[u8]) -> Option<f64> {
77    get(doc, dict, key).and_then(num)
78}
79
80pub fn get_int(doc: &Document, dict: &Dictionary, key: &[u8]) -> Option<i64> {
81    get(doc, dict, key).and_then(|o| match o {
82        Object::Integer(i) => Some(*i),
83        Object::Real(r) => Some(*r as i64),
84        _ => None,
85    })
86}
87
88pub fn get_bool(doc: &Document, dict: &Dictionary, key: &[u8]) -> Option<bool> {
89    get(doc, dict, key).and_then(|o| match o {
90        Object::Boolean(b) => Some(*b),
91        Object::Integer(i) => Some(*i != 0),
92        _ => None,
93    })
94}
95
96pub fn get_name<'a>(doc: &'a Document, dict: &'a Dictionary, key: &[u8]) -> Option<&'a [u8]> {
97    get(doc, dict, key).and_then(name)
98}
99
100pub fn get_dict<'a>(doc: &'a Document, dict: &'a Dictionary, key: &[u8]) -> Option<&'a Dictionary> {
101    dict.get(key).ok().and_then(|o| as_dict(doc, o))
102}
103
104/// A resource `kind`/`name` from a resources dictionary.
105pub fn resource<'a>(
106    doc: &'a Document,
107    res: Option<&'a Dictionary>,
108    kind: &[u8],
109    name: &[u8],
110) -> Option<&'a Object> {
111    res?.get(kind)
112        .ok()
113        .and_then(|o| as_dict(doc, o))
114        .and_then(|d| d.get(name).ok())
115}
116
117/// The decoded bytes of a stream, `None` on a filter this build cannot undo.
118pub fn stream_data(s: &lopdf::Stream) -> Option<Vec<u8>> {
119    s.decompressed_content().ok()
120}