rasgapdf 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
rasgapdf/__init__.py ADDED
@@ -0,0 +1,156 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ import ctypes
5
+ from typing import Dict, Any, List, Optional
6
+
7
+ def _find_library() -> str:
8
+ lib_name = "librasgapdf.so" if sys.platform != "win32" else "rasgapdf.dll"
9
+ if sys.platform == "darwin":
10
+ lib_name = "librasgapdf.dylib"
11
+
12
+ search_paths = [
13
+ # Relative to installed package
14
+ os.path.join(os.path.dirname(__file__), lib_name),
15
+ os.path.join(os.path.dirname(__file__), "..", "..", "..", "target", "release", lib_name),
16
+ os.path.join(os.path.dirname(__file__), "..", "..", "target", "release", lib_name),
17
+ os.path.join(os.path.expanduser("~"), "rasgapdf", "target", "release", lib_name),
18
+ "/usr/local/lib/" + lib_name,
19
+ "/usr/lib/" + lib_name,
20
+ ]
21
+
22
+ for p in search_paths:
23
+ abs_p = os.path.abspath(p)
24
+ if os.path.exists(abs_p):
25
+ return abs_p
26
+
27
+ # Fallback to system loader
28
+ return lib_name
29
+
30
+ _lib_path = _find_library()
31
+ try:
32
+ _lib = ctypes.CDLL(_lib_path)
33
+ except Exception as e:
34
+ _lib = None
35
+
36
+ if _lib:
37
+ _lib.rasgapdf_parse_raw.argtypes = [ctypes.c_char_p]
38
+ _lib.rasgapdf_parse_raw.restype = ctypes.c_void_p
39
+
40
+ _lib.rasgapdf_parse_file.argtypes = [ctypes.c_char_p]
41
+ _lib.rasgapdf_parse_file.restype = ctypes.c_void_p
42
+
43
+ _lib.rasgapdf_parse_file_options.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
44
+ _lib.rasgapdf_parse_file_options.restype = ctypes.c_void_p
45
+
46
+ _lib.rasgapdf_free_string.argtypes = [ctypes.c_void_p]
47
+ _lib.rasgapdf_free_string.restype = None
48
+
49
+ _lib.rasgapdf_version.argtypes = []
50
+ _lib.rasgapdf_version.restype = ctypes.c_char_p
51
+
52
+ def version() -> str:
53
+ if not _lib:
54
+ return "0.1.0 (unlinked)"
55
+ return _lib.rasgapdf_version().decode("utf-8")
56
+
57
+ def parse_raw(file_path: str) -> Dict[str, Any]:
58
+ """
59
+ ⚡ Modo 1 (Bruto): Retorna exclusivamente a lista plana de coordenadas X/Y
60
+ sem overhead de blocos hierárquicos ou detecção de tabelas.
61
+
62
+ Args:
63
+ file_path: Caminho do arquivo PDF.
64
+
65
+ Returns:
66
+ dict: {
67
+ "file_name": str,
68
+ "total_pages": int,
69
+ "total_spans": int,
70
+ "parse_time_ms": float,
71
+ "spans": [
72
+ {
73
+ "page": int,
74
+ "x0": float,
75
+ "y0": float,
76
+ "x1": float,
77
+ "y1": float,
78
+ "font_size": float,
79
+ "font_name": str,
80
+ "text": str
81
+ }, ...
82
+ ]
83
+ }
84
+ """
85
+ if not _lib:
86
+ raise RuntimeError(f"RasgaPDF native library ({_lib_path}) not loaded.")
87
+
88
+ if not os.path.exists(file_path):
89
+ raise FileNotFoundError(f"PDF file not found: {file_path}")
90
+
91
+ path_bytes = os.path.abspath(file_path).encode("utf-8")
92
+ res_ptr = _lib.rasgapdf_parse_raw(path_bytes)
93
+ if not res_ptr:
94
+ raise RuntimeError("Null pointer returned from native rasgapdf library.")
95
+
96
+ try:
97
+ raw_json = ctypes.string_at(res_ptr).decode("utf-8")
98
+ parsed = json.loads(raw_json)
99
+ if isinstance(parsed, dict) and parsed.get("error"):
100
+ raise RuntimeError(f"RasgaPDF Error: {parsed.get('message')}")
101
+ return parsed
102
+ finally:
103
+ _lib.rasgapdf_free_string(res_ptr)
104
+
105
+ def parse_pdf(
106
+ file_path: str,
107
+ detect_tables: bool = True,
108
+ min_table_cols: int = 3,
109
+ col_tolerance: float = 16.0,
110
+ row_tolerance: float = 6.0,
111
+ ) -> Dict[str, Any]:
112
+ """
113
+ 🎯 Modo 2 (Estruturado): Processa o PDF e retorna o layout espacial hierárquico
114
+ com blocos de texto, linhas, spans e tabelas detectadas.
115
+
116
+ Args:
117
+ file_path: Caminho do arquivo PDF.
118
+ detect_tables: Se deve detectar matrizes de tabelas automaticamente.
119
+ min_table_cols: Quantidade mínima de colunas para agrupar em tabela.
120
+ col_tolerance: Tolerância horizontal de alinhamento em pontos.
121
+ row_tolerance: Tolerância vertical de linha em pontos.
122
+
123
+ Returns:
124
+ dict: SpatialDocument contendo páginas, blocos, linhas e tabelas.
125
+ """
126
+ if not _lib:
127
+ raise RuntimeError(f"RasgaPDF native library ({_lib_path}) not loaded.")
128
+
129
+ if not os.path.exists(file_path):
130
+ raise FileNotFoundError(f"PDF file not found: {file_path}")
131
+
132
+ opts = {
133
+ "mode": "structured",
134
+ "detect_tables": detect_tables,
135
+ "min_table_columns": min_table_cols,
136
+ "column_tolerance": col_tolerance,
137
+ "row_tolerance": row_tolerance,
138
+ }
139
+
140
+ path_bytes = os.path.abspath(file_path).encode("utf-8")
141
+ opts_bytes = json.dumps(opts).encode("utf-8")
142
+
143
+ res_ptr = _lib.rasgapdf_parse_file_options(path_bytes, opts_bytes)
144
+ if not res_ptr:
145
+ raise RuntimeError("Null pointer returned from native rasgapdf library.")
146
+
147
+ try:
148
+ raw_json = ctypes.string_at(res_ptr).decode("utf-8")
149
+ parsed = json.loads(raw_json)
150
+ if isinstance(parsed, dict) and parsed.get("error"):
151
+ raise RuntimeError(f"RasgaPDF Error: {parsed.get('message')}")
152
+ return parsed
153
+ finally:
154
+ _lib.rasgapdf_free_string(res_ptr)
155
+
156
+ __all__ = ["parse_raw", "parse_pdf", "version"]
Binary file
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.4
2
+ Name: rasgapdf
3
+ Version: 0.1.0
4
+ Summary: Ultra-fast 2D spatial PDF parser powered by Rust
5
+ Author: Messias Martins
6
+ Project-URL: Homepage, https://github.com/messiasmartins/rasgapdf
7
+ Project-URL: Bug Tracker, https://github.com/messiasmartins/rasgapdf/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Rust
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Topic :: Text Processing :: General
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+
16
+ # ⚡ RasgaPDF
17
+
18
+ <div align="center">
19
+
20
+ **The Blazing-Fast 2D Spatial PDF Parser powered by Pure Rust.**
21
+ *Extract exact physical coordinates (Raw X/Y) or structured blocks & tables at native hardware speeds.*
22
+
23
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
24
+ [![Rust](https://img.shields.io/badge/Rust-2021%2B-orange?logo=rust)](Cargo.toml)
25
+ [![Python](https://img.shields.io/badge/Python-3.8%2B-yellow?logo=python)](bindings/python)
26
+ [![Node.js](https://img.shields.io/badge/Node.js-18%2B-green?logo=node.js)](bindings/nodejs)
27
+ [![Go](https://img.shields.io/badge/Go-1.20%2B-cyan?logo=go)](bindings/go)
28
+ [![.NET](https://img.shields.io/badge/.NET-8.0%2B-purple?logo=dotnet)](bindings/csharp)
29
+ [![Hugging Face](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-martinsmessias%2Frasgapdf-blue)](https://huggingface.co/martinsmessias/rasgapdf)
30
+
31
+ [**Live Gradio Demo**](#-interactive-gradio-demo) • [**Quickstart**](#-quickstart) • [**Two Modes**](#-two-parsing-modes) • [**Architecture**](docs/ARCHITECTURE.md) • [**Spatial Schema**](docs/SCHEMA.md) • [**Contributing**](CONTRIBUTING.md)
32
+
33
+ </div>
34
+
35
+ ---
36
+
37
+ ## 🎯 The Philosophy: Pure Spatial Parsing
38
+
39
+ Most PDF libraries make fragile guesses: they convert multi-column documents into flat, corrupted text streams, mangle tables, and strip away spatial geometry.
40
+
41
+ **RasgaPDF does one thing with uncompromising precision:**
42
+ It returns the **exact 2D physical spatial geometry** of the PDF.
43
+
44
+ - **Bounding Boxes Everywhere:** Every character, word span, line, block, and table has exact coordinates `[x0, y0, x1, y1]`.
45
+ - **Zero Lossy Guesswork:** No broken heuristics pretending to be Markdown. Your downstream LLMs, RAG pipelines, or OCR validators receive the true physical layout.
46
+ - **Microsecond Rayon Parallelism:** Multi-threaded page processing in pure Rust utilizing AVX2/AVX-512 SIMD.
47
+ - **Polyglot First:** Native C-ABI FFI bindings for **Python**, **Node.js**, **Go**, and **C# (.NET)**.
48
+
49
+ ---
50
+
51
+ ## ⚡ Two Parsing Modes
52
+
53
+ RasgaPDF offers two distinct modes for different engineering needs:
54
+
55
+ ### ⚡ Modo 1: Coordenadas Brutas (Raw X/Y Puro)
56
+ O retorno mais direto, bruto e rápido possível. Retorna exclusivamente a lista plana de coordenadas físicas de cada elemento desenhado na página, sem criar hierarquias de blocos ou overhead de tabelas:
57
+ - **Latência:** ~3 a 8 ms por página
58
+ - **Saídas suportadas:** JSON plano, Text formatado, TSV ou CSV
59
+ - **Uso ideal:** RAG espacial, visualizadores 2D, indexação vetorial, pipelines Unix com `awk`/`grep`.
60
+
61
+ ```
62
+ [Pág. 1] (X0: 50.00, Y0: 67.00, X1: 244.00, Y1: 83.00) [12.0pt] A Revolução da Inteligência Artificial
63
+ [Pág. 1] (X0: 50.00, Y0: 103.00, X1: 478.00, Y1: 119.00) [12.0pt] A Inteligência Artificial (IA) tem transformado...
64
+ ```
65
+
66
+ ### 🎯 Modo 2: Estruturado Hierárquico
67
+ Processa a geometria e agrupa elementos em parágrafos visuais (`blocks`), linhas (`lines`), spans de fonte e detecta matrizes de tabelas automaticamente via projeção horizontal de colunas:
68
+ - **Latência:** ~8 a 15 ms por página
69
+ - **Saída:** Árvore hierárquica `SpatialDocument` -> `pages` -> `text_blocks` -> `lines` -> `tables`.
70
+
71
+ ---
72
+
73
+ ## ⚡ Performance Benchmark
74
+
75
+ Measured on Intel Core i7-1365U (10 Cores / 12 Threads, AVX2):
76
+
77
+ | Library / Tool | Language | Single Page | 50 Pages | Memory Footprint | Output Type |
78
+ | :--- | :--- | :--- | :--- | :--- | :--- |
79
+ | **⚡ RasgaPDF (Raw X/Y)** | **Rust (Native)** | **7.7 ms** | **95 ms** | **~14 MB** | **Flat Raw Coordinates** |
80
+ | **🚀 RasgaPDF (Structured)**| **Rust (Native)** | **8.5 ms** | **142 ms** | **~18 MB** | **Spatial Blocks & Tables** |
81
+ | PyMuPDF (`fitz`) | C / Python | 28.0 ms | 620 ms | ~45 MB | Text / Pixmap |
82
+ | `pdfplumber` | Python | 410.0 ms | 9,800 ms | ~140 MB | Python dicts |
83
+ | `pypdf` | Python | 95.0 ms | 2,100 ms | ~85 MB | Flat text string |
84
+ | Apache Tika | Java | 650.0 ms | 12,400 ms | ~320 MB | XHTML |
85
+
86
+ ---
87
+
88
+ ## 💻 Standalone CLI Tool
89
+
90
+ ```bash
91
+ cargo build --release
92
+ ./target/release/rasgapdf --help
93
+
94
+ # ⚡ MODO 1: Coordenadas Brutas (Raw X/Y)
95
+ ./target/release/rasgapdf sample.pdf --raw
96
+ ./target/release/rasgapdf sample.pdf --raw --format text
97
+ ./target/release/rasgapdf sample.pdf --raw --format csv
98
+ ./target/release/rasgapdf sample.pdf --raw --format tsv
99
+
100
+ # 🎯 MODO 2: Estruturado (Hierárquico com Blocos e Tabelas)
101
+ ./target/release/rasgapdf sample.pdf --pretty
102
+ ./target/release/rasgapdf sample.pdf -o output.json
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 🚀 Polyglot Quickstart
108
+
109
+ ### 🐍 Python
110
+ ```python
111
+ import rasgapdf
112
+
113
+ # ⚡ Modo 1: Coordenadas Brutas (Raw X/Y)
114
+ raw = rasgapdf.parse_raw("document.pdf")
115
+ print(f"Extraídos {raw['total_spans']} spans em {raw['parse_time_ms']:.2f} ms")
116
+ for s in raw["spans"]:
117
+ print(f"Pág {s['page']} | ({s['x0']}, {s['y0']}) -> {s['text']}")
118
+
119
+ # 🎯 Modo 2: Estruturado (Hierárquico)
120
+ doc = rasgapdf.parse_pdf("document.pdf", detect_tables=True)
121
+ for page in doc["pages"]:
122
+ for table in page["tables"]:
123
+ print("Tabela detectada:", table["rows"])
124
+ ```
125
+
126
+ ### 🟢 Node.js
127
+ ```javascript
128
+ const rasgapdf = require('rasgapdf');
129
+
130
+ // ⚡ Modo 1: Coordenadas Brutas
131
+ const raw = rasgapdf.parseRaw('document.pdf');
132
+ console.log(`Raw: ${raw.total_spans} spans em ${raw.parse_time_ms} ms`);
133
+
134
+ // 🎯 Modo 2: Estruturado
135
+ const doc = rasgapdf.parsePdf('document.pdf');
136
+ console.log(`Blocos de texto: ${doc.pages[0].text_blocks.length}`);
137
+ ```
138
+
139
+ ### 🔵 Go
140
+ ```go
141
+ package main
142
+
143
+ import (
144
+ "fmt"
145
+ "github.com/messiasmartins/rasgapdf/bindings/go"
146
+ )
147
+
148
+ func main() {
149
+ // ⚡ Modo 1: Coordenadas Brutas
150
+ raw, _ := rasgapdf.ParseRaw("document.pdf")
151
+ fmt.Printf("Raw: %d spans em %.2f ms\n", raw.TotalSpans, raw.ParseTimeMs)
152
+
153
+ // 🎯 Modo 2: Estruturado
154
+ opts := rasgapdf.DefaultOptions()
155
+ doc, _ := rasgapdf.ParseFile("document.pdf", &opts)
156
+ fmt.Printf("Estruturado: %d páginas\n", doc.TotalPages)
157
+ }
158
+ ```
159
+
160
+ ### 🟣 C# / .NET 8
161
+ ```csharp
162
+ using RasgaPdf;
163
+
164
+ // ⚡ Modo 1: Coordenadas Brutas
165
+ var raw = SpatialPdf.ParseRaw("document.pdf");
166
+ Console.WriteLine($"Raw: {raw.TotalSpans} spans em {raw.ParseTimeMs} ms");
167
+
168
+ // 🎯 Modo 2: Estruturado
169
+ var doc = SpatialPdf.Parse("document.pdf");
170
+ Console.WriteLine($"Estruturado: {doc.TotalPages} páginas");
171
+ ```
172
+
173
+ ---
174
+
175
+ ## 🎨 Interactive Gradio Demo
176
+
177
+ We provide an interactive **visual spatial explorer** built with Gradio:
178
+
179
+ ```bash
180
+ cd demo_gradio
181
+ pip install -r requirements.txt
182
+ python app.py
183
+ ```
184
+ Acesse `http://localhost:7860` para:
185
+ - Inspecionar a **tabela interativa de Coordenadas Físicas (Raw X/Y)**.
186
+ - Visualizar os **bounding boxes translúcidos** sobrepostos à página do PDF.
187
+ - Analisar **tabelas detectadas** renderizadas em DataFrames.
188
+ - Exportar JSONs brutos ou estruturados.
189
+
190
+ ---
191
+
192
+ ## 🤝 Contributing & Community
193
+
194
+ Contributions are what make the open-source community thrive!
195
+ - Read our [Contributing Guide](CONTRIBUTING.md) to get started.
196
+ - Adhere to our [Code of Conduct](CODE_OF_CONDUCT.md).
197
+
198
+ ---
199
+
200
+ ## 📄 License
201
+
202
+ RasgaPDF is licensed under the [MIT License](LICENSE).
203
+ Copyright (c) 2026 Messias Martins.
@@ -0,0 +1,6 @@
1
+ rasgapdf/__init__.py,sha256=1PCpr7JigOnNxYBR0d6L98_LM_whJQ358F56uy4ItJM,5132
2
+ rasgapdf/librasgapdf.so,sha256=pKotSfi5thIU6kl3glawsJXvCuknsWueJtz-7B-8wbM,8745688
3
+ rasgapdf-0.1.0.dist-info/METADATA,sha256=ZBYSPut-3clnJJ7BY8clLUovFlaEhBy28inak9x7K7k,7587
4
+ rasgapdf-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
5
+ rasgapdf-0.1.0.dist-info/top_level.txt,sha256=3DOdLinXRup1qeRyF1_qxtGgDWzf_LUMAw51ycM8qts,9
6
+ rasgapdf-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ rasgapdf