Skip to main content

rasgapdf/
spatial.rs

1use std::error::Error;
2use std::path::Path;
3use crate::engine::PurePdfDoc;
4use crate::types::*;
5
6/// Modo 1: Parse Bruto Espacial (Raw X/Y)
7/// Retorna exclusivamente a lista plana e direta de coordenadas físicas de cada elemento
8pub fn parse_raw_file(pdf_path: &str) -> Result<RawSpatialDocument, Box<dyn Error + Send + Sync>> {
9    let path = Path::new(pdf_path);
10    if !path.exists() {
11        return Err(format!("PDF file not found: {}", pdf_path).into());
12    }
13
14    let file_name = path
15        .file_name()
16        .map(|f| f.to_string_lossy().to_string())
17        .unwrap_or_else(|| "document.pdf".to_string());
18
19    let bytes = std::fs::read(pdf_path)?;
20    let mut doc = PurePdfDoc::from_bytes(&bytes).map_err(|e| format!("Pure PDF parse error: {e}"))?;
21    let raw = doc.parse_raw(&file_name).map_err(|e| format!("Pure PDF parse_raw error: {e}"))?;
22    Ok(raw)
23}
24
25/// Modo 1 (Em Memória): Parse Bruto Espacial a partir de slice de bytes (`&[u8]`)
26pub fn parse_raw_bytes(pdf_bytes: &[u8]) -> Result<RawSpatialDocument, Box<dyn Error + Send + Sync>> {
27    let mut doc = PurePdfDoc::from_bytes(pdf_bytes).map_err(|e| format!("Pure PDF parse error: {e}"))?;
28    let raw = doc.parse_raw("memory.pdf").map_err(|e| format!("Pure PDF parse_raw error: {e}"))?;
29    Ok(raw)
30}
31
32/// Modo 2: Parse Estruturado (Hierárquico com Blocos e Tabelas)
33pub fn parse_file(pdf_path: &str, options: &ParserOptions) -> Result<SpatialDocument, Box<dyn Error + Send + Sync>> {
34    let path = Path::new(pdf_path);
35    if !path.exists() {
36        return Err(format!("PDF file not found: {}", pdf_path).into());
37    }
38
39    let file_name = path
40        .file_name()
41        .map(|f| f.to_string_lossy().to_string())
42        .unwrap_or_else(|| "document.pdf".to_string());
43
44    let bytes = std::fs::read(pdf_path)?;
45    let mut doc = PurePdfDoc::from_bytes(&bytes).map_err(|e| format!("Pure PDF parse error: {e}"))?;
46    let structured = doc.parse_structured(&file_name, options).map_err(|e| format!("Pure PDF parse_structured error: {e}"))?;
47    Ok(structured)
48}
49
50/// Modo 2 (Em Memória): Parse Estruturado a partir de slice de bytes (`&[u8]`)
51pub fn parse_bytes(pdf_bytes: &[u8], options: &ParserOptions) -> Result<SpatialDocument, Box<dyn Error + Send + Sync>> {
52    let mut doc = PurePdfDoc::from_bytes(pdf_bytes).map_err(|e| format!("Pure PDF parse error: {e}"))?;
53    let structured = doc.parse_structured("memory.pdf", options).map_err(|e| format!("Pure PDF parse_structured error: {e}"))?;
54    Ok(structured)
55}
56
57/// Extração paralela de texto puro em memória a partir de slice de bytes (`&[u8]`)
58pub fn extract_text_bytes(pdf_bytes: &[u8]) -> Result<String, Box<dyn Error + Send + Sync>> {
59    let mut doc = PurePdfDoc::from_bytes(pdf_bytes).map_err(|e| format!("Pure PDF parse error: {e}"))?;
60    let raw = doc.parse_raw("memory.pdf").map_err(|e| format!("Pure PDF parse_raw error: {e}"))?;
61
62    let mut out = String::with_capacity(raw.total_spans * 16);
63    let mut last_page = 0;
64    let mut last_y0 = -1.0;
65
66    for span in &raw.spans {
67        if span.page != last_page {
68            if last_page != 0 {
69                out.push('\n');
70                out.push('\n');
71            }
72            last_page = span.page;
73            last_y0 = span.y0;
74        } else if (span.y0 - last_y0).abs() > 4.0 {
75            out.push('\n');
76            last_y0 = span.y0;
77        } else if !out.ends_with(' ') && !span.text.starts_with(' ') {
78            out.push(' ');
79        }
80
81        out.push_str(&span.text);
82    }
83
84    if !out.ends_with('\n') && !out.is_empty() {
85        out.push('\n');
86    }
87
88    Ok(out)
89}