Skip to main content

metadata/
document.rs

1use crate::types::{DocxMeta, PdfMeta};
2use roxmltree::Document;
3use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6
7pub fn read_pdf_info(path: &Path) -> Option<PdfMeta> {
8    let doc = lopdf::Document::load(path).ok()?;
9    let n = doc.get_pages().len();
10    if n == 0 {
11        return None;
12    }
13    Some(PdfMeta {
14        page_count: Some(n as u32),
15        author: None,
16        title: None,
17    })
18}
19
20/// Read `docProps/core.xml` from a DOCX (ZIP) package.
21pub fn read_docx_core(path: &Path) -> Option<DocxMeta> {
22    let file = File::open(path).ok()?;
23    let mut archive = zip::ZipArchive::new(file).ok()?;
24    let mut zf = archive.by_name("docProps/core.xml").ok()?;
25    let mut xml = String::new();
26    zf.read_to_string(&mut xml).ok()?;
27    let doc = Document::parse(&xml).ok()?;
28
29    let mut out = DocxMeta::default();
30    for node in doc.descendants() {
31        if !node.is_element() {
32            continue;
33        }
34        let name = node.tag_name().name();
35        let text = node.text().unwrap_or("").trim();
36        if text.is_empty() {
37            continue;
38        }
39        match name {
40            "creator" => out.creator = Some(text.to_string()),
41            "lastModifiedBy" => out.last_modified_by = Some(text.to_string()),
42            "revision" => out.revision = Some(text.to_string()),
43            _ => {}
44        }
45    }
46
47    if out.creator.is_none() && out.last_modified_by.is_none() && out.revision.is_none() {
48        None
49    } else {
50        Some(out)
51    }
52}