@lexmata/micropdf 0.4.0
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.
- package/LICENSE +191 -0
- package/README.md +985 -0
- package/binding.gyp +73 -0
- package/dist/annot.d.ts +458 -0
- package/dist/annot.d.ts.map +1 -0
- package/dist/annot.js +697 -0
- package/dist/annot.js.map +1 -0
- package/dist/archive.d.ts +128 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/archive.js +268 -0
- package/dist/archive.js.map +1 -0
- package/dist/buffer.d.ts +572 -0
- package/dist/buffer.d.ts.map +1 -0
- package/dist/buffer.js +971 -0
- package/dist/buffer.js.map +1 -0
- package/dist/colorspace.d.ts +287 -0
- package/dist/colorspace.d.ts.map +1 -0
- package/dist/colorspace.js +542 -0
- package/dist/colorspace.js.map +1 -0
- package/dist/context.d.ts +184 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +320 -0
- package/dist/context.js.map +1 -0
- package/dist/cookie.d.ts +164 -0
- package/dist/cookie.d.ts.map +1 -0
- package/dist/cookie.js +306 -0
- package/dist/cookie.js.map +1 -0
- package/dist/device.d.ts +169 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +350 -0
- package/dist/device.js.map +1 -0
- package/dist/display-list.d.ts +202 -0
- package/dist/display-list.d.ts.map +1 -0
- package/dist/display-list.js +410 -0
- package/dist/display-list.js.map +1 -0
- package/dist/document.d.ts +637 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +902 -0
- package/dist/document.js.map +1 -0
- package/dist/easy.d.ts +423 -0
- package/dist/easy.d.ts.map +1 -0
- package/dist/easy.js +644 -0
- package/dist/easy.js.map +1 -0
- package/dist/enhanced.d.ts +226 -0
- package/dist/enhanced.d.ts.map +1 -0
- package/dist/enhanced.js +368 -0
- package/dist/enhanced.js.map +1 -0
- package/dist/filter.d.ts +51 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +381 -0
- package/dist/filter.js.map +1 -0
- package/dist/font.d.ts +222 -0
- package/dist/font.d.ts.map +1 -0
- package/dist/font.js +381 -0
- package/dist/font.js.map +1 -0
- package/dist/form.d.ts +214 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +497 -0
- package/dist/form.js.map +1 -0
- package/dist/geometry.d.ts +469 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +780 -0
- package/dist/geometry.js.map +1 -0
- package/dist/image.d.ts +172 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +348 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +171 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +339 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +168 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +343 -0
- package/dist/link.js.map +1 -0
- package/dist/micropdf.d.ts +40 -0
- package/dist/micropdf.d.ts.map +1 -0
- package/dist/micropdf.js +45 -0
- package/dist/micropdf.js.map +1 -0
- package/dist/nanopdf.d.ts +40 -0
- package/dist/nanopdf.d.ts.map +1 -0
- package/dist/nanopdf.js +45 -0
- package/dist/nanopdf.js.map +1 -0
- package/dist/native.d.ts +242 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +509 -0
- package/dist/native.js.map +1 -0
- package/dist/output.d.ts +166 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +365 -0
- package/dist/output.js.map +1 -0
- package/dist/path.d.ts +420 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +687 -0
- package/dist/path.js.map +1 -0
- package/dist/pdf/object.d.ts +489 -0
- package/dist/pdf/object.d.ts.map +1 -0
- package/dist/pdf/object.js +1045 -0
- package/dist/pdf/object.js.map +1 -0
- package/dist/pixmap.d.ts +315 -0
- package/dist/pixmap.d.ts.map +1 -0
- package/dist/pixmap.js +590 -0
- package/dist/pixmap.js.map +1 -0
- package/dist/profiler.d.ts +159 -0
- package/dist/profiler.d.ts.map +1 -0
- package/dist/profiler.js +380 -0
- package/dist/profiler.js.map +1 -0
- package/dist/render-options.d.ts +227 -0
- package/dist/render-options.d.ts.map +1 -0
- package/dist/render-options.js +130 -0
- package/dist/render-options.js.map +1 -0
- package/dist/resource-tracking.d.ts +332 -0
- package/dist/resource-tracking.d.ts.map +1 -0
- package/dist/resource-tracking.js +653 -0
- package/dist/resource-tracking.js.map +1 -0
- package/dist/simple.d.ts +276 -0
- package/dist/simple.d.ts.map +1 -0
- package/dist/simple.js +343 -0
- package/dist/simple.js.map +1 -0
- package/dist/stext.d.ts +290 -0
- package/dist/stext.d.ts.map +1 -0
- package/dist/stext.js +312 -0
- package/dist/stext.js.map +1 -0
- package/dist/stream.d.ts +174 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +476 -0
- package/dist/stream.js.map +1 -0
- package/dist/text.d.ts +337 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +454 -0
- package/dist/text.js.map +1 -0
- package/dist/typed-arrays.d.ts +127 -0
- package/dist/typed-arrays.d.ts.map +1 -0
- package/dist/typed-arrays.js +410 -0
- package/dist/typed-arrays.js.map +1 -0
- package/dist/types.d.ts +358 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +216 -0
- package/dist/types.js.map +1 -0
- package/native/annot.cc +557 -0
- package/native/buffer.cc +204 -0
- package/native/colorspace.cc +166 -0
- package/native/context.cc +84 -0
- package/native/cookie.cc +179 -0
- package/native/device.cc +179 -0
- package/native/display_list.cc +179 -0
- package/native/document.cc +268 -0
- package/native/enhanced.cc +70 -0
- package/native/font.cc +282 -0
- package/native/form.cc +523 -0
- package/native/geometry.cc +255 -0
- package/native/image.cc +216 -0
- package/native/include/micropdf/enhanced.h +38 -0
- package/native/include/micropdf/types.h +36 -0
- package/native/include/micropdf.h +106 -0
- package/native/include/mupdf-ffi.h +39 -0
- package/native/include/mupdf.h +11 -0
- package/native/include/mupdf_minimal.h +381 -0
- package/native/lib/linux-x64/libmicropdf.a +0 -0
- package/native/link.cc +234 -0
- package/native/micropdf.cc +71 -0
- package/native/output.cc +229 -0
- package/native/page.cc +572 -0
- package/native/path.cc +259 -0
- package/native/pixmap.cc +240 -0
- package/native/stext.cc +610 -0
- package/native/stream.cc +239 -0
- package/package.json +120 -0
- package/scripts/build-from-rust.js +97 -0
- package/scripts/install.js +184 -0
package/native/stream.cc
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MicroPDF Stream Bindings
|
|
3
|
+
*
|
|
4
|
+
* N-API bindings for stream (input) operations.
|
|
5
|
+
* Streams provide sequential data reading from files or memory.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include <napi.h>
|
|
9
|
+
#include "include/mupdf_minimal.h"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Open stream from file
|
|
13
|
+
*
|
|
14
|
+
* @param ctx - Context handle
|
|
15
|
+
* @param filename - Path to file
|
|
16
|
+
* @returns Stream handle
|
|
17
|
+
*/
|
|
18
|
+
Napi::BigInt OpenFile(const Napi::CallbackInfo& info) {
|
|
19
|
+
Napi::Env env = info.Env();
|
|
20
|
+
|
|
21
|
+
if (info.Length() < 2) {
|
|
22
|
+
Napi::TypeError::New(env, "Expected 2 arguments: ctx, filename")
|
|
23
|
+
.ThrowAsJavaScriptException();
|
|
24
|
+
return Napi::BigInt::New(env, static_cast<uint64_t>(0));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
bool lossless;
|
|
28
|
+
uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
29
|
+
std::string filename = info[1].As<Napi::String>().Utf8Value();
|
|
30
|
+
|
|
31
|
+
uint64_t stream_handle = fz_open_file(ctx_handle, filename.c_str());
|
|
32
|
+
|
|
33
|
+
return Napi::BigInt::New(env, stream_handle);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Open stream from memory buffer
|
|
38
|
+
*
|
|
39
|
+
* @param ctx - Context handle
|
|
40
|
+
* @param data - Buffer containing data
|
|
41
|
+
* @returns Stream handle
|
|
42
|
+
*/
|
|
43
|
+
Napi::BigInt OpenMemory(const Napi::CallbackInfo& info) {
|
|
44
|
+
Napi::Env env = info.Env();
|
|
45
|
+
|
|
46
|
+
if (info.Length() < 2) {
|
|
47
|
+
Napi::TypeError::New(env, "Expected 2 arguments: ctx, data")
|
|
48
|
+
.ThrowAsJavaScriptException();
|
|
49
|
+
return Napi::BigInt::New(env, static_cast<uint64_t>(0));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
bool lossless;
|
|
53
|
+
uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
54
|
+
Napi::Buffer<uint8_t> buffer = info[1].As<Napi::Buffer<uint8_t>>();
|
|
55
|
+
|
|
56
|
+
uint64_t stream_handle = fz_open_memory(
|
|
57
|
+
ctx_handle,
|
|
58
|
+
buffer.Data(),
|
|
59
|
+
buffer.Length()
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return Napi::BigInt::New(env, stream_handle);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Drop stream handle
|
|
67
|
+
*
|
|
68
|
+
* @param ctx - Context handle
|
|
69
|
+
* @param stream - Stream handle
|
|
70
|
+
*/
|
|
71
|
+
Napi::Value DropStream(const Napi::CallbackInfo& info) {
|
|
72
|
+
Napi::Env env = info.Env();
|
|
73
|
+
|
|
74
|
+
if (info.Length() < 2) {
|
|
75
|
+
Napi::TypeError::New(env, "Expected 2 arguments: ctx, stream")
|
|
76
|
+
.ThrowAsJavaScriptException();
|
|
77
|
+
return env.Undefined();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
bool lossless;
|
|
81
|
+
uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
82
|
+
uint64_t stream_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
83
|
+
|
|
84
|
+
fz_drop_stream(ctx_handle, stream_handle);
|
|
85
|
+
|
|
86
|
+
return env.Undefined();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Read data from stream
|
|
91
|
+
*
|
|
92
|
+
* @param ctx - Context handle
|
|
93
|
+
* @param stream - Stream handle
|
|
94
|
+
* @param buffer - Buffer to read into
|
|
95
|
+
* @returns Number of bytes read
|
|
96
|
+
*/
|
|
97
|
+
Napi::Number Read(const Napi::CallbackInfo& info) {
|
|
98
|
+
Napi::Env env = info.Env();
|
|
99
|
+
|
|
100
|
+
if (info.Length() < 3) {
|
|
101
|
+
Napi::TypeError::New(env, "Expected 3 arguments: ctx, stream, buffer")
|
|
102
|
+
.ThrowAsJavaScriptException();
|
|
103
|
+
return Napi::Number::New(env, 0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
bool lossless;
|
|
107
|
+
uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
108
|
+
uint64_t stream_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
109
|
+
Napi::Buffer<uint8_t> buffer = info[2].As<Napi::Buffer<uint8_t>>();
|
|
110
|
+
|
|
111
|
+
size_t bytes_read = fz_read(
|
|
112
|
+
ctx_handle,
|
|
113
|
+
stream_handle,
|
|
114
|
+
buffer.Data(),
|
|
115
|
+
buffer.Length()
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return Napi::Number::New(env, bytes_read);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Read single byte from stream
|
|
123
|
+
*
|
|
124
|
+
* @param ctx - Context handle
|
|
125
|
+
* @param stream - Stream handle
|
|
126
|
+
* @returns Byte value (0-255) or -1 on EOF
|
|
127
|
+
*/
|
|
128
|
+
Napi::Number ReadByte(const Napi::CallbackInfo& info) {
|
|
129
|
+
Napi::Env env = info.Env();
|
|
130
|
+
|
|
131
|
+
if (info.Length() < 2) {
|
|
132
|
+
Napi::TypeError::New(env, "Expected 2 arguments: ctx, stream")
|
|
133
|
+
.ThrowAsJavaScriptException();
|
|
134
|
+
return Napi::Number::New(env, -1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
bool lossless;
|
|
138
|
+
uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
139
|
+
uint64_t stream_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
140
|
+
|
|
141
|
+
int32_t byte = fz_read_byte(ctx_handle, stream_handle);
|
|
142
|
+
|
|
143
|
+
return Napi::Number::New(env, byte);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Check if stream is at end-of-file
|
|
148
|
+
*
|
|
149
|
+
* @param ctx - Context handle
|
|
150
|
+
* @param stream - Stream handle
|
|
151
|
+
* @returns Boolean indicating EOF
|
|
152
|
+
*/
|
|
153
|
+
Napi::Boolean IsEOF(const Napi::CallbackInfo& info) {
|
|
154
|
+
Napi::Env env = info.Env();
|
|
155
|
+
|
|
156
|
+
if (info.Length() < 2) {
|
|
157
|
+
Napi::TypeError::New(env, "Expected 2 arguments: ctx, stream")
|
|
158
|
+
.ThrowAsJavaScriptException();
|
|
159
|
+
return Napi::Boolean::New(env, true);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
bool lossless;
|
|
163
|
+
uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
164
|
+
uint64_t stream_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
165
|
+
|
|
166
|
+
int32_t is_eof = fz_is_eof(ctx_handle, stream_handle);
|
|
167
|
+
|
|
168
|
+
return Napi::Boolean::New(env, is_eof != 0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Seek to position in stream
|
|
173
|
+
*
|
|
174
|
+
* @param ctx - Context handle
|
|
175
|
+
* @param stream - Stream handle
|
|
176
|
+
* @param offset - Offset to seek to
|
|
177
|
+
* @param whence - 0=SEEK_SET, 1=SEEK_CUR, 2=SEEK_END
|
|
178
|
+
*/
|
|
179
|
+
Napi::Value Seek(const Napi::CallbackInfo& info) {
|
|
180
|
+
Napi::Env env = info.Env();
|
|
181
|
+
|
|
182
|
+
if (info.Length() < 4) {
|
|
183
|
+
Napi::TypeError::New(env, "Expected 4 arguments: ctx, stream, offset, whence")
|
|
184
|
+
.ThrowAsJavaScriptException();
|
|
185
|
+
return env.Undefined();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
bool lossless;
|
|
189
|
+
uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
190
|
+
uint64_t stream_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
191
|
+
int64_t offset = info[2].As<Napi::Number>().Int64Value();
|
|
192
|
+
int32_t whence = info[3].As<Napi::Number>().Int32Value();
|
|
193
|
+
|
|
194
|
+
fz_seek(ctx_handle, stream_handle, offset, whence);
|
|
195
|
+
|
|
196
|
+
return env.Undefined();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get current position in stream
|
|
201
|
+
*
|
|
202
|
+
* @param ctx - Context handle
|
|
203
|
+
* @param stream - Stream handle
|
|
204
|
+
* @returns Current position
|
|
205
|
+
*/
|
|
206
|
+
Napi::Number Tell(const Napi::CallbackInfo& info) {
|
|
207
|
+
Napi::Env env = info.Env();
|
|
208
|
+
|
|
209
|
+
if (info.Length() < 2) {
|
|
210
|
+
Napi::TypeError::New(env, "Expected 2 arguments: ctx, stream")
|
|
211
|
+
.ThrowAsJavaScriptException();
|
|
212
|
+
return Napi::Number::New(env, 0);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
bool lossless;
|
|
216
|
+
uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
217
|
+
uint64_t stream_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
218
|
+
|
|
219
|
+
int64_t position = fz_tell(ctx_handle, stream_handle);
|
|
220
|
+
|
|
221
|
+
return Napi::Number::New(env, position);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Initialize Stream module exports
|
|
226
|
+
*/
|
|
227
|
+
Napi::Object InitStream(Napi::Env env, Napi::Object exports) {
|
|
228
|
+
exports.Set("openFile", Napi::Function::New(env, OpenFile));
|
|
229
|
+
exports.Set("openMemory", Napi::Function::New(env, OpenMemory));
|
|
230
|
+
exports.Set("dropStream", Napi::Function::New(env, DropStream));
|
|
231
|
+
exports.Set("read", Napi::Function::New(env, Read));
|
|
232
|
+
exports.Set("readByte", Napi::Function::New(env, ReadByte));
|
|
233
|
+
exports.Set("isEOF", Napi::Function::New(env, IsEOF));
|
|
234
|
+
exports.Set("seek", Napi::Function::New(env, Seek));
|
|
235
|
+
exports.Set("tell", Napi::Function::New(env, Tell));
|
|
236
|
+
|
|
237
|
+
return exports;
|
|
238
|
+
}
|
|
239
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lexmata/micropdf",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Node.js bindings for the MicroPDF library",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./easy": {
|
|
14
|
+
"import": "./dist/easy.js",
|
|
15
|
+
"types": "./dist/easy.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./simple": {
|
|
18
|
+
"import": "./dist/simple.js",
|
|
19
|
+
"types": "./dist/simple.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"native",
|
|
25
|
+
"prebuilds",
|
|
26
|
+
"binding.gyp",
|
|
27
|
+
"scripts"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"pdf",
|
|
31
|
+
"mupdf",
|
|
32
|
+
"document",
|
|
33
|
+
"parser",
|
|
34
|
+
"native",
|
|
35
|
+
"rust"
|
|
36
|
+
],
|
|
37
|
+
"author": "Lexmata",
|
|
38
|
+
"license": "Apache-2.0",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://bitbucket.org/lexmata/micropdf.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://bitbucket.org/lexmata/micropdf/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://bitbucket.org/lexmata/micropdf",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
},
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"node-addon-api": "^8.3.0",
|
|
52
|
+
"node-gyp-build": "^4.8.4"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@eslint/js": "^9.39.1",
|
|
56
|
+
"@jazzer.js/core": "^2.1.0",
|
|
57
|
+
"@types/node": "^22.10.1",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^8.18.1",
|
|
59
|
+
"@typescript-eslint/parser": "^8.18.1",
|
|
60
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
61
|
+
"eslint": "^9.17.0",
|
|
62
|
+
"eslint-config-prettier": "^9.1.0",
|
|
63
|
+
"eslint-plugin-import": "^2.31.0",
|
|
64
|
+
"eslint-plugin-jsdoc": "^50.6.1",
|
|
65
|
+
"eslint-plugin-node": "^11.1.0",
|
|
66
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
67
|
+
"eslint-plugin-promise": "^7.2.1",
|
|
68
|
+
"eslint-plugin-security": "^3.0.1",
|
|
69
|
+
"eslint-plugin-sonarjs": "^2.0.4",
|
|
70
|
+
"eslint-plugin-unicorn": "^56.0.1",
|
|
71
|
+
"node-gyp": "^10.3.1",
|
|
72
|
+
"prebuildify": "^6.0.1",
|
|
73
|
+
"prettier": "^3.4.2",
|
|
74
|
+
"tinybench": "^6.0.0",
|
|
75
|
+
"typedoc": "^0.28.15",
|
|
76
|
+
"typedoc-plugin-markdown": "^4.9.0",
|
|
77
|
+
"typescript": "^5.7.2",
|
|
78
|
+
"vitest": "^2.1.8"
|
|
79
|
+
},
|
|
80
|
+
"binary": {
|
|
81
|
+
"module_name": "micropdf",
|
|
82
|
+
"module_path": "./prebuilds/{platform}-{arch}",
|
|
83
|
+
"host": "https://bitbucket.org/lexmata/micropdf/downloads/",
|
|
84
|
+
"remote_path": "v{version}",
|
|
85
|
+
"package_name": "micropdf-{platform}-{arch}.tar.gz"
|
|
86
|
+
},
|
|
87
|
+
"scripts": {
|
|
88
|
+
"postinstall": "node scripts/install.js || true",
|
|
89
|
+
"build": "npm run build:ts",
|
|
90
|
+
"build:native": "node-gyp rebuild",
|
|
91
|
+
"build:full": "npm run build:native && npm run build:ts",
|
|
92
|
+
"build:ts": "tsc",
|
|
93
|
+
"build:from-rust": "node scripts/build-from-rust.js && npm run build:native",
|
|
94
|
+
"prebuild": "prebuildify --napi --strip",
|
|
95
|
+
"prebuild:all": "prebuildify --napi --strip --target 18.0.0 --target 20.0.0 --target 22.0.0",
|
|
96
|
+
"docs": "typedoc",
|
|
97
|
+
"docs:serve": "cd ../docs/api/nodejs && python3 -m http.server 8080",
|
|
98
|
+
"test": "vitest run",
|
|
99
|
+
"test:watch": "vitest",
|
|
100
|
+
"test:coverage": "vitest run --coverage",
|
|
101
|
+
"test:integration": "vitest run test/integration",
|
|
102
|
+
"test:integration:watch": "vitest test/integration",
|
|
103
|
+
"lint": "eslint . --ext .ts,.js",
|
|
104
|
+
"lint:fix": "eslint . --ext .ts,.js --fix",
|
|
105
|
+
"format": "prettier --write \"**/*.{ts,js,json,md}\"",
|
|
106
|
+
"format:check": "prettier --check \"**/*.{ts,js,json,md}\"",
|
|
107
|
+
"quality": "npm run lint && npm run format:check && npm run build:ts && npm run test",
|
|
108
|
+
"clean": "node-gyp clean && rm -rf dist",
|
|
109
|
+
"bench": "tsx bench/run.ts",
|
|
110
|
+
"bench:buffer": "tsx -e \"import('./bench/buffer.bench.js').then(m => m.bench.run().then(() => console.table(m.bench.table())))\"",
|
|
111
|
+
"bench:geometry": "tsx -e \"import('./bench/geometry.bench.js').then(m => m.bench.run().then(() => console.table(m.bench.table())))\"",
|
|
112
|
+
"fuzz": "npm run fuzz:pdf && npm run fuzz:buffer && npm run fuzz:geometry",
|
|
113
|
+
"fuzz:pdf": "jazzer fuzz/targets/pdf-parse.fuzz.ts --sync -t 300",
|
|
114
|
+
"fuzz:buffer": "jazzer fuzz/targets/buffer.fuzz.ts --sync -t 300",
|
|
115
|
+
"fuzz:geometry": "jazzer fuzz/targets/geometry.fuzz.ts --sync -t 300",
|
|
116
|
+
"fuzz:quick": "jazzer fuzz/targets/pdf-parse.fuzz.ts --sync -t 60 && jazzer fuzz/targets/buffer.fuzz.ts --sync -t 60 && jazzer fuzz/targets/geometry.fuzz.ts --sync -t 60",
|
|
117
|
+
"fuzz:extended": "jazzer fuzz/targets/pdf-parse.fuzz.ts --sync -t 3600 && jazzer fuzz/targets/buffer.fuzz.ts --sync -t 3600 && jazzer fuzz/targets/geometry.fuzz.ts --sync -t 3600",
|
|
118
|
+
"fuzz:deep": "jazzer fuzz/targets/pdf-parse.fuzz.ts --sync -t 86400"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build MicroPDF native library from Rust source
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import { existsSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const rootDir = join(__dirname, '..');
|
|
14
|
+
const rustDir = join(rootDir, '..', 'micropdf-rs');
|
|
15
|
+
|
|
16
|
+
const platform = process.platform;
|
|
17
|
+
const arch = process.arch;
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
console.log('Building MicroPDF from Rust source');
|
|
21
|
+
console.log(`Platform: ${platform}-${arch}`);
|
|
22
|
+
console.log('');
|
|
23
|
+
|
|
24
|
+
// Check for Rust source
|
|
25
|
+
if (!existsSync(join(rustDir, 'Cargo.toml'))) {
|
|
26
|
+
console.error('Error: Rust source not found at', rustDir);
|
|
27
|
+
console.error('Make sure you have the complete micropdf repository');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check for cargo
|
|
32
|
+
try {
|
|
33
|
+
const version = execSync('cargo --version', { encoding: 'utf-8' });
|
|
34
|
+
console.log(`Found: ${version.trim()}`);
|
|
35
|
+
} catch {
|
|
36
|
+
console.error('Error: Rust/Cargo not found');
|
|
37
|
+
console.error('Install Rust from: https://rustup.rs');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Build the Rust library
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log('Building Rust library...');
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
execSync('cargo build --release', {
|
|
47
|
+
cwd: rustDir,
|
|
48
|
+
stdio: 'inherit'
|
|
49
|
+
});
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Rust build failed');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Determine library name
|
|
56
|
+
const libDir = join(rootDir, 'native', 'lib', `${platform}-${arch}`);
|
|
57
|
+
let libName;
|
|
58
|
+
|
|
59
|
+
if (platform === 'win32') {
|
|
60
|
+
libName = 'micropdf.lib';
|
|
61
|
+
} else {
|
|
62
|
+
libName = 'libmicropdf.a';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Create output directory
|
|
66
|
+
mkdirSync(libDir, { recursive: true });
|
|
67
|
+
|
|
68
|
+
// Copy the library
|
|
69
|
+
const srcLib = join(rustDir, 'target', 'release', libName);
|
|
70
|
+
const destLib = join(libDir, libName);
|
|
71
|
+
|
|
72
|
+
if (!existsSync(srcLib)) {
|
|
73
|
+
console.error(`Error: Built library not found at ${srcLib}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
copyFileSync(srcLib, destLib);
|
|
78
|
+
console.log(`Library copied to: ${destLib}`);
|
|
79
|
+
|
|
80
|
+
// Also copy the C header if it exists
|
|
81
|
+
const headerSrc = join(rustDir, 'include', 'micropdf.h');
|
|
82
|
+
const headerDest = join(rootDir, 'native', 'include', 'micropdf.h');
|
|
83
|
+
|
|
84
|
+
if (existsSync(headerSrc)) {
|
|
85
|
+
copyFileSync(headerSrc, headerDest);
|
|
86
|
+
console.log(`Header copied to: ${headerDest}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log('Build complete!');
|
|
91
|
+
console.log('Run `npm run build:native` to build the Node.js addon');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
main().catch((error) => {
|
|
95
|
+
console.error('Build failed:', error.message);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MicroPDF Post-Install Script
|
|
5
|
+
*
|
|
6
|
+
* This script attempts to download prebuilt native bindings for the current
|
|
7
|
+
* platform if they're not already available. This allows users to install
|
|
8
|
+
* micropdf without needing Rust or a C++ compiler.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, mkdirSync, createWriteStream, unlinkSync } from 'fs';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { execSync } from 'child_process';
|
|
15
|
+
import https from 'https';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
const rootDir = join(__dirname, '..');
|
|
20
|
+
|
|
21
|
+
// Platform mappings
|
|
22
|
+
const platformMap = {
|
|
23
|
+
darwin: 'darwin',
|
|
24
|
+
linux: 'linux',
|
|
25
|
+
win32: 'win32'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const archMap = {
|
|
29
|
+
x64: 'x64',
|
|
30
|
+
arm64: 'arm64',
|
|
31
|
+
ia32: 'ia32'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const platform = platformMap[process.platform] || process.platform;
|
|
35
|
+
const arch = archMap[process.arch] || process.arch;
|
|
36
|
+
const prebuildDir = join(rootDir, 'prebuilds', `${platform}-${arch}`);
|
|
37
|
+
const prebuildFile = join(prebuildDir, 'micropdf.node');
|
|
38
|
+
|
|
39
|
+
// Version from package.json
|
|
40
|
+
let version;
|
|
41
|
+
try {
|
|
42
|
+
const pkg = await import(join(rootDir, 'package.json'), { with: { type: 'json' } });
|
|
43
|
+
version = pkg.default.version;
|
|
44
|
+
} catch {
|
|
45
|
+
version = '0.4.0';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Download URL
|
|
49
|
+
const downloadBase = 'https://bitbucket.org/lexmata/micropdf/downloads';
|
|
50
|
+
const archiveName = `micropdf-${platform}-${arch}.tar.gz`;
|
|
51
|
+
const downloadUrl = `${downloadBase}/${archiveName}`;
|
|
52
|
+
|
|
53
|
+
console.log(`[micropdf] Platform: ${platform}-${arch}`);
|
|
54
|
+
console.log(`[micropdf] Version: ${version}`);
|
|
55
|
+
|
|
56
|
+
// Check if prebuilt binary already exists
|
|
57
|
+
if (existsSync(prebuildFile)) {
|
|
58
|
+
console.log('[micropdf] Prebuilt binary found, skipping download');
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check if we should skip native build entirely
|
|
63
|
+
if (
|
|
64
|
+
process.env.MICROPDF_SKIP_BUILD === '1' ||
|
|
65
|
+
process.env.npm_config_build_from_source === 'false'
|
|
66
|
+
) {
|
|
67
|
+
console.log(
|
|
68
|
+
'[micropdf] Skipping native build (MICROPDF_SKIP_BUILD=1 or build_from_source=false)'
|
|
69
|
+
);
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(`[micropdf] No prebuilt binary found for ${platform}-${arch}`);
|
|
74
|
+
console.log(`[micropdf] Attempting to download from: ${downloadUrl}`);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Download a file from URL
|
|
78
|
+
*/
|
|
79
|
+
function download(url, dest) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const file = createWriteStream(dest);
|
|
82
|
+
|
|
83
|
+
const request = https.get(url, { timeout: 30000 }, (response) => {
|
|
84
|
+
// Handle redirects
|
|
85
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
86
|
+
file.close();
|
|
87
|
+
unlinkSync(dest);
|
|
88
|
+
download(response.headers.location, dest).then(resolve).catch(reject);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (response.statusCode !== 200) {
|
|
93
|
+
file.close();
|
|
94
|
+
unlinkSync(dest);
|
|
95
|
+
reject(new Error(`Download failed: HTTP ${response.statusCode}`));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
response.pipe(file);
|
|
100
|
+
|
|
101
|
+
file.on('finish', () => {
|
|
102
|
+
file.close();
|
|
103
|
+
resolve();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
request.on('error', (err) => {
|
|
108
|
+
file.close();
|
|
109
|
+
unlinkSync(dest);
|
|
110
|
+
reject(err);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
request.on('timeout', () => {
|
|
114
|
+
request.destroy();
|
|
115
|
+
file.close();
|
|
116
|
+
unlinkSync(dest);
|
|
117
|
+
reject(new Error('Download timeout'));
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Extract tar.gz archive
|
|
124
|
+
*/
|
|
125
|
+
function extractTarGz(archive, dest) {
|
|
126
|
+
try {
|
|
127
|
+
execSync(`tar -xzf "${archive}" -C "${dest}"`, { stdio: 'inherit' });
|
|
128
|
+
return true;
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function main() {
|
|
135
|
+
const tempDir = join(rootDir, '.download-temp');
|
|
136
|
+
const archivePath = join(tempDir, archiveName);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
// Create temp directory
|
|
140
|
+
mkdirSync(tempDir, { recursive: true });
|
|
141
|
+
|
|
142
|
+
// Download archive
|
|
143
|
+
console.log('[micropdf] Downloading prebuilt binary...');
|
|
144
|
+
await download(downloadUrl, archivePath);
|
|
145
|
+
|
|
146
|
+
// Create prebuilds directory
|
|
147
|
+
mkdirSync(prebuildDir, { recursive: true });
|
|
148
|
+
|
|
149
|
+
// Extract archive
|
|
150
|
+
console.log('[micropdf] Extracting...');
|
|
151
|
+
if (extractTarGz(archivePath, join(rootDir, 'prebuilds'))) {
|
|
152
|
+
console.log('[micropdf] Prebuilt binary installed successfully!');
|
|
153
|
+
} else {
|
|
154
|
+
throw new Error('Extraction failed');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Cleanup
|
|
158
|
+
unlinkSync(archivePath);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.log(`[micropdf] Download failed: ${err.message}`);
|
|
161
|
+
console.log('[micropdf] Falling back to building from source...');
|
|
162
|
+
console.log('[micropdf] Note: This requires Rust and a C++ compiler');
|
|
163
|
+
|
|
164
|
+
// Try to build from source
|
|
165
|
+
try {
|
|
166
|
+
execSync('npm run build:native', { cwd: rootDir, stdio: 'inherit' });
|
|
167
|
+
console.log('[micropdf] Built from source successfully');
|
|
168
|
+
} catch {
|
|
169
|
+
console.log('[micropdf] Warning: Could not build native module');
|
|
170
|
+
console.log(
|
|
171
|
+
'[micropdf] The library will use pure JavaScript fallback (limited functionality)'
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Cleanup temp directory
|
|
176
|
+
try {
|
|
177
|
+
execSync(`rm -rf "${tempDir}"`, { stdio: 'ignore' });
|
|
178
|
+
} catch {
|
|
179
|
+
// Ignore cleanup errors
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
main().catch(console.error);
|