Skip to main content

pdfium_render/pdf/document/
metadata.rs

1//! Defines the [PdfMetadata] struct, a collection of all the metadata tags in a [PdfDocument].
2
3use crate::bindgen::FPDF_DOCUMENT;
4use crate::bindings::PdfiumLibraryBindings;
5use crate::utils::mem::create_byte_buffer;
6use crate::utils::utf16le::get_string_from_pdfium_utf16le_bytes;
7use std::os::raw::c_void;
8use std::slice::Iter;
9
10#[cfg(doc)]
11use crate::pdf::document::PdfDocument;
12
13/// Valid metadata tag types in a [PdfDocument].
14#[derive(Debug, Copy, Clone, PartialEq)]
15pub enum PdfDocumentMetadataTagType {
16    Title,
17    Author,
18    Subject,
19    Keywords,
20    Creator,
21    Producer,
22    CreationDate,
23    ModificationDate,
24}
25
26/// A single metadata tag in a [PdfDocument].
27#[derive(Debug, Clone, PartialEq)]
28pub struct PdfDocumentMetadataTag {
29    tag: PdfDocumentMetadataTagType,
30    value: String,
31}
32
33impl PdfDocumentMetadataTag {
34    #[inline]
35    pub(crate) fn new(tag: PdfDocumentMetadataTagType, value: String) -> Self {
36        PdfDocumentMetadataTag { tag, value }
37    }
38
39    /// Returns the type of this metadata tag.
40    #[inline]
41    pub fn tag_type(&self) -> PdfDocumentMetadataTagType {
42        self.tag
43    }
44
45    /// Returns the value of this metadata tag.
46    #[inline]
47    pub fn value(&self) -> &str {
48        self.value.as_str()
49    }
50}
51
52/// A collection of all the metadata tags in a [PdfDocument].
53pub struct PdfMetadata<'a> {
54    document_handle: FPDF_DOCUMENT,
55    bindings: &'a dyn PdfiumLibraryBindings,
56    tags: Vec<PdfDocumentMetadataTag>,
57}
58
59impl<'a> PdfMetadata<'a> {
60    pub(crate) fn from_pdfium(document_handle: FPDF_DOCUMENT, bindings: &'a dyn PdfiumLibraryBindings) -> Self {
61        let mut result = PdfMetadata {
62            document_handle,
63            bindings,
64            tags: vec![],
65        };
66
67        if let Some(tag) = result.get(PdfDocumentMetadataTagType::Title) {
68            result.tags.push(tag);
69        }
70
71        if let Some(tag) = result.get(PdfDocumentMetadataTagType::Author) {
72            result.tags.push(tag);
73        }
74
75        if let Some(tag) = result.get(PdfDocumentMetadataTagType::Subject) {
76            result.tags.push(tag);
77        }
78
79        if let Some(tag) = result.get(PdfDocumentMetadataTagType::Keywords) {
80            result.tags.push(tag);
81        }
82
83        if let Some(tag) = result.get(PdfDocumentMetadataTagType::Creator) {
84            result.tags.push(tag);
85        }
86
87        if let Some(tag) = result.get(PdfDocumentMetadataTagType::Producer) {
88            result.tags.push(tag);
89        }
90
91        if let Some(tag) = result.get(PdfDocumentMetadataTagType::CreationDate) {
92            result.tags.push(tag);
93        }
94
95        if let Some(tag) = result.get(PdfDocumentMetadataTagType::ModificationDate) {
96            result.tags.push(tag);
97        }
98
99        result
100    }
101
102    /// Returns the [PdfiumLibraryBindings] used by this [PdfMetadata] collection.
103    #[inline]
104    pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
105        self.bindings
106    }
107
108    /// Returns the number of metadata tags in this [PdfMetadata] collection.
109    #[inline]
110    pub fn len(&self) -> usize {
111        self.tags.len()
112    }
113
114    /// Returns true if this [PdfMetadata] collection is empty.
115    #[inline]
116    pub fn is_empty(&self) -> bool {
117        self.len() == 0
118    }
119
120    /// Returns one metadata tag from this [PdfMetadata] collection, if it is defined.
121    pub fn get(&self, tag: PdfDocumentMetadataTagType) -> Option<PdfDocumentMetadataTag> {
122        let result = match tag {
123            PdfDocumentMetadataTagType::Title => self.get_raw_metadata_tag("Title"),
124            PdfDocumentMetadataTagType::Author => self.get_raw_metadata_tag("Author"),
125            PdfDocumentMetadataTagType::Subject => self.get_raw_metadata_tag("Subject"),
126            PdfDocumentMetadataTagType::Keywords => self.get_raw_metadata_tag("Keywords"),
127            PdfDocumentMetadataTagType::Creator => self.get_raw_metadata_tag("Creator"),
128            PdfDocumentMetadataTagType::Producer => self.get_raw_metadata_tag("Producer"),
129            PdfDocumentMetadataTagType::CreationDate => self.get_raw_metadata_tag("CreationDate"),
130            PdfDocumentMetadataTagType::ModificationDate => self.get_raw_metadata_tag("ModificationDate"),
131        };
132
133        result.map(|value| PdfDocumentMetadataTag::new(tag, value))
134    }
135
136    #[inline]
137    fn get_raw_metadata_tag(&self, tag: &str) -> Option<String> {
138        let buffer_length = self
139            .bindings
140            .FPDF_GetMetaText(self.document_handle, tag, std::ptr::null_mut(), 0);
141
142        if buffer_length == 0 {
143            return None;
144        }
145
146        let mut buffer = create_byte_buffer(buffer_length as usize);
147
148        let result = self.bindings.FPDF_GetMetaText(
149            self.document_handle,
150            tag,
151            buffer.as_mut_ptr() as *mut c_void,
152            buffer_length,
153        );
154
155        assert_eq!(result, buffer_length);
156
157        get_string_from_pdfium_utf16le_bytes(buffer)
158    }
159
160    /// Returns an iterator over all the tags in this [PdfMetadata] collection.
161    #[inline]
162    pub fn iter(&self) -> Iter<'_, PdfDocumentMetadataTag> {
163        self.tags.iter()
164    }
165}