File size: 5,647 Bytes
98e73d0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | # ⚡ RasgaPDF (Node.js)
<div align="center">
**Ultra-Fast 2D Spatial PDF Parser powered by Pure Rust.**
*Extract exact physical coordinates (Raw X/Y) or structured blocks & tables with sub-millisecond latency.*
[](https://www.npmjs.com/package/rasgapdf)
[](https://www.gnu.org/licenses/agpl-3.0)
</div>
---
## 🚀 Installation
```bash
npm install rasgapdf
```
*Note: Bundles pre-compiled native Rust C-ABI binaries. Works out-of-the-box with optional `koffi` fast FFI and automatic CLI fallback.*
---
## ⚡ Two Extraction Modes
### ⚡ Mode 1: Flat Physical Coordinates (Raw X/Y)
Zero-overhead extraction returning exact physical 2D coordinates `[x0, y0, x1, y1]` for every text span (~3-8 ms):
```javascript
const { parseRaw } = require('rasgapdf');
const raw = parseRaw('sample.pdf');
console.log(`Parsed ${raw.total_spans} spans in ${raw.parse_time_ms.toFixed(2)} ms`);
// Iterate through raw physical coordinates
for (const span of raw.spans) {
console.log(`[Page ${span.page}] (${span.x0}, ${span.y0}, ${span.x1}, ${span.y1}) -> "${span.text}"`);
}
```
### 🎯 Mode 2: Hierarchical Structured Document
Extracts visual text blocks, lines, font sizes, and automatically detects matrix tables via column alignment:
```javascript
const { parsePdf } = require('rasgapdf');
const doc = parsePdf('invoice.pdf', {
detect_tables: true,
min_table_columns: 3
});
console.log(`Total Pages: ${doc.total_pages}`);
for (const page of doc.pages) {
// Access text blocks
for (const block of page.text_blocks) {
console.log(`Block #${block.block_id}: ${block.lines[0].text}`);
}
// Access detected tables
for (const table of page.tables) {
console.log('Table Headers:', table.headers);
console.log('Table Rows:', table.rows);
}
}
```
---
### 🖼️ Embedded Images & Vector Graphics (opt-in)
Both are **off by default**, so the JSON shape is unchanged unless you ask for them. Image bytes are never inlined: the tree carries metadata (dimensions, colour space, filter, SHA-256, placement `bbox`) and `extractImages()` writes the original streams to disk.
```javascript
const { parsePdf, extractImages } = require('rasgapdf');
const doc = parsePdf('report.pdf', {
extract_images: true,
extract_drawings: true
});
const page = doc.pages[0];
for (const image of page.images) {
console.log(image.name, image.pixel_width, 'x', image.pixel_height, image.filter, image.bbox);
}
for (const drawing of page.drawings) {
console.log(drawing.drawing_id, drawing.drawing_type, drawing.stroke_color);
}
// Writes page_001_img_001_xref7.jpg (etc.) plus manifest.json
const manifest = extractImages('report.pdf', './out');
console.log(`${manifest.written} of ${manifest.total_images} images written`);
```
---
## 📐 Schema
Returns a deterministic JSON structure representing the exact 2D canvas geometry of the PDF.
### `RawSpatialDocument`
```typescript
interface RawSpatialDocument {
file_name: string;
total_pages: number;
total_spans: number;
parse_time_ms: number;
spans: Array<{
page: number;
x0: number;
y0: number;
x1: number;
y1: number;
font_size: number;
font_name: string;
text: string;
}>;
}
```
### `SpatialDocument`
```typescript
interface SpatialDocument {
file_name: string;
total_pages: number;
parse_time_ms: number;
pages: Array<{
page_number: number;
width: number;
height: number;
rotation: number; // real page /Rotate (0, 90, 180, 270)
text_blocks: SpatialBlock[];
tables: SpatialTable[];
raw_span_count: number;
images: SpatialImage[]; // empty unless extract_images: true
drawings: SpatialDrawing[];// empty unless extract_drawings: true
}>;
}
interface SpatialImage {
image_id: number;
name: string;
xref: number;
bbox: { x0: number; y0: number; x1: number; y1: number; width: number; height: number } | null;
pixel_width: number;
pixel_height: number;
bits_per_component: number | null;
color_space: string | null;
filter: string | null; // e.g. "DCTDecode"
mime_type: string | null; // e.g. "image/jpeg"
byte_length: number | null;
sha256: string | null;
file: string | null; // filled only by extractImages()
}
interface SpatialDrawing {
drawing_id: number;
bbox: { x0: number; y0: number; x1: number; y1: number; width: number; height: number };
drawing_type: 'fill' | 'stroke' | 'fill_stroke';
item_count: number;
items: Array<{ // discriminated by `kind`
kind: 'line' | 'curve' | 'rect' | 'quad';
p1?: [number, number]; p2?: [number, number];
p3?: [number, number]; p4?: [number, number];
bbox?: { x0: number; y0: number; x1: number; y1: number; width: number; height: number };
orientation?: number; // 1 anti-clockwise, -1 clockwise
points?: Array<[number, number]>;
}>;
stroke_color: [number, number, number] | null;
fill_color: [number, number, number] | null;
width: number | null;
fill_opacity: number | null;
stroke_opacity: number | null;
even_odd: boolean | null;
dash_pattern: number[] | null;
dash_phase: number | null;
line_join: string | null;
line_cap: string | null;
layer: string | null;
seqno: number;
}
```
---
## 📄 License
GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). Copyright (c) 2026 Messias Martins.
RasgaPDF links against [MuPDF](https://mupdf.com/) (Artifex Software), licensed
under the AGPL-3.0 — hence the same license here.
|