@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.
Files changed (170) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +985 -0
  3. package/binding.gyp +73 -0
  4. package/dist/annot.d.ts +458 -0
  5. package/dist/annot.d.ts.map +1 -0
  6. package/dist/annot.js +697 -0
  7. package/dist/annot.js.map +1 -0
  8. package/dist/archive.d.ts +128 -0
  9. package/dist/archive.d.ts.map +1 -0
  10. package/dist/archive.js +268 -0
  11. package/dist/archive.js.map +1 -0
  12. package/dist/buffer.d.ts +572 -0
  13. package/dist/buffer.d.ts.map +1 -0
  14. package/dist/buffer.js +971 -0
  15. package/dist/buffer.js.map +1 -0
  16. package/dist/colorspace.d.ts +287 -0
  17. package/dist/colorspace.d.ts.map +1 -0
  18. package/dist/colorspace.js +542 -0
  19. package/dist/colorspace.js.map +1 -0
  20. package/dist/context.d.ts +184 -0
  21. package/dist/context.d.ts.map +1 -0
  22. package/dist/context.js +320 -0
  23. package/dist/context.js.map +1 -0
  24. package/dist/cookie.d.ts +164 -0
  25. package/dist/cookie.d.ts.map +1 -0
  26. package/dist/cookie.js +306 -0
  27. package/dist/cookie.js.map +1 -0
  28. package/dist/device.d.ts +169 -0
  29. package/dist/device.d.ts.map +1 -0
  30. package/dist/device.js +350 -0
  31. package/dist/device.js.map +1 -0
  32. package/dist/display-list.d.ts +202 -0
  33. package/dist/display-list.d.ts.map +1 -0
  34. package/dist/display-list.js +410 -0
  35. package/dist/display-list.js.map +1 -0
  36. package/dist/document.d.ts +637 -0
  37. package/dist/document.d.ts.map +1 -0
  38. package/dist/document.js +902 -0
  39. package/dist/document.js.map +1 -0
  40. package/dist/easy.d.ts +423 -0
  41. package/dist/easy.d.ts.map +1 -0
  42. package/dist/easy.js +644 -0
  43. package/dist/easy.js.map +1 -0
  44. package/dist/enhanced.d.ts +226 -0
  45. package/dist/enhanced.d.ts.map +1 -0
  46. package/dist/enhanced.js +368 -0
  47. package/dist/enhanced.js.map +1 -0
  48. package/dist/filter.d.ts +51 -0
  49. package/dist/filter.d.ts.map +1 -0
  50. package/dist/filter.js +381 -0
  51. package/dist/filter.js.map +1 -0
  52. package/dist/font.d.ts +222 -0
  53. package/dist/font.d.ts.map +1 -0
  54. package/dist/font.js +381 -0
  55. package/dist/font.js.map +1 -0
  56. package/dist/form.d.ts +214 -0
  57. package/dist/form.d.ts.map +1 -0
  58. package/dist/form.js +497 -0
  59. package/dist/form.js.map +1 -0
  60. package/dist/geometry.d.ts +469 -0
  61. package/dist/geometry.d.ts.map +1 -0
  62. package/dist/geometry.js +780 -0
  63. package/dist/geometry.js.map +1 -0
  64. package/dist/image.d.ts +172 -0
  65. package/dist/image.d.ts.map +1 -0
  66. package/dist/image.js +348 -0
  67. package/dist/image.js.map +1 -0
  68. package/dist/index.d.ts +171 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +339 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/link.d.ts +168 -0
  73. package/dist/link.d.ts.map +1 -0
  74. package/dist/link.js +343 -0
  75. package/dist/link.js.map +1 -0
  76. package/dist/micropdf.d.ts +40 -0
  77. package/dist/micropdf.d.ts.map +1 -0
  78. package/dist/micropdf.js +45 -0
  79. package/dist/micropdf.js.map +1 -0
  80. package/dist/nanopdf.d.ts +40 -0
  81. package/dist/nanopdf.d.ts.map +1 -0
  82. package/dist/nanopdf.js +45 -0
  83. package/dist/nanopdf.js.map +1 -0
  84. package/dist/native.d.ts +242 -0
  85. package/dist/native.d.ts.map +1 -0
  86. package/dist/native.js +509 -0
  87. package/dist/native.js.map +1 -0
  88. package/dist/output.d.ts +166 -0
  89. package/dist/output.d.ts.map +1 -0
  90. package/dist/output.js +365 -0
  91. package/dist/output.js.map +1 -0
  92. package/dist/path.d.ts +420 -0
  93. package/dist/path.d.ts.map +1 -0
  94. package/dist/path.js +687 -0
  95. package/dist/path.js.map +1 -0
  96. package/dist/pdf/object.d.ts +489 -0
  97. package/dist/pdf/object.d.ts.map +1 -0
  98. package/dist/pdf/object.js +1045 -0
  99. package/dist/pdf/object.js.map +1 -0
  100. package/dist/pixmap.d.ts +315 -0
  101. package/dist/pixmap.d.ts.map +1 -0
  102. package/dist/pixmap.js +590 -0
  103. package/dist/pixmap.js.map +1 -0
  104. package/dist/profiler.d.ts +159 -0
  105. package/dist/profiler.d.ts.map +1 -0
  106. package/dist/profiler.js +380 -0
  107. package/dist/profiler.js.map +1 -0
  108. package/dist/render-options.d.ts +227 -0
  109. package/dist/render-options.d.ts.map +1 -0
  110. package/dist/render-options.js +130 -0
  111. package/dist/render-options.js.map +1 -0
  112. package/dist/resource-tracking.d.ts +332 -0
  113. package/dist/resource-tracking.d.ts.map +1 -0
  114. package/dist/resource-tracking.js +653 -0
  115. package/dist/resource-tracking.js.map +1 -0
  116. package/dist/simple.d.ts +276 -0
  117. package/dist/simple.d.ts.map +1 -0
  118. package/dist/simple.js +343 -0
  119. package/dist/simple.js.map +1 -0
  120. package/dist/stext.d.ts +290 -0
  121. package/dist/stext.d.ts.map +1 -0
  122. package/dist/stext.js +312 -0
  123. package/dist/stext.js.map +1 -0
  124. package/dist/stream.d.ts +174 -0
  125. package/dist/stream.d.ts.map +1 -0
  126. package/dist/stream.js +476 -0
  127. package/dist/stream.js.map +1 -0
  128. package/dist/text.d.ts +337 -0
  129. package/dist/text.d.ts.map +1 -0
  130. package/dist/text.js +454 -0
  131. package/dist/text.js.map +1 -0
  132. package/dist/typed-arrays.d.ts +127 -0
  133. package/dist/typed-arrays.d.ts.map +1 -0
  134. package/dist/typed-arrays.js +410 -0
  135. package/dist/typed-arrays.js.map +1 -0
  136. package/dist/types.d.ts +358 -0
  137. package/dist/types.d.ts.map +1 -0
  138. package/dist/types.js +216 -0
  139. package/dist/types.js.map +1 -0
  140. package/native/annot.cc +557 -0
  141. package/native/buffer.cc +204 -0
  142. package/native/colorspace.cc +166 -0
  143. package/native/context.cc +84 -0
  144. package/native/cookie.cc +179 -0
  145. package/native/device.cc +179 -0
  146. package/native/display_list.cc +179 -0
  147. package/native/document.cc +268 -0
  148. package/native/enhanced.cc +70 -0
  149. package/native/font.cc +282 -0
  150. package/native/form.cc +523 -0
  151. package/native/geometry.cc +255 -0
  152. package/native/image.cc +216 -0
  153. package/native/include/micropdf/enhanced.h +38 -0
  154. package/native/include/micropdf/types.h +36 -0
  155. package/native/include/micropdf.h +106 -0
  156. package/native/include/mupdf-ffi.h +39 -0
  157. package/native/include/mupdf.h +11 -0
  158. package/native/include/mupdf_minimal.h +381 -0
  159. package/native/lib/linux-x64/libmicropdf.a +0 -0
  160. package/native/link.cc +234 -0
  161. package/native/micropdf.cc +71 -0
  162. package/native/output.cc +229 -0
  163. package/native/page.cc +572 -0
  164. package/native/path.cc +259 -0
  165. package/native/pixmap.cc +240 -0
  166. package/native/stext.cc +610 -0
  167. package/native/stream.cc +239 -0
  168. package/package.json +120 -0
  169. package/scripts/build-from-rust.js +97 -0
  170. package/scripts/install.js +184 -0
@@ -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);