@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,179 @@
1
+ /**
2
+ * MicroPDF Device Bindings
3
+ *
4
+ * N-API bindings for device operations.
5
+ * Devices are the destination for rendering operations.
6
+ */
7
+
8
+ #include <napi.h>
9
+ #include "include/mupdf_minimal.h"
10
+
11
+ /**
12
+ * Create a draw device for pixmap rendering
13
+ *
14
+ * @param ctx - Context handle
15
+ * @param pixmap - Pixmap handle
16
+ * @returns Device handle
17
+ */
18
+ Napi::BigInt NewDrawDevice(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, pixmap")
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
+ uint64_t pixmap_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
30
+
31
+ uint64_t device_handle = fz_new_draw_device(ctx_handle, pixmap_handle);
32
+
33
+ return Napi::BigInt::New(env, device_handle);
34
+ }
35
+
36
+ /**
37
+ * Create a display list device
38
+ *
39
+ * @param ctx - Context handle
40
+ * @param list - Display list handle
41
+ * @returns Device handle
42
+ */
43
+ Napi::BigInt NewListDevice(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, list")
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
+ uint64_t list_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
55
+
56
+ uint64_t device_handle = fz_new_list_device(ctx_handle, list_handle);
57
+
58
+ return Napi::BigInt::New(env, device_handle);
59
+ }
60
+
61
+ /**
62
+ * Drop device handle
63
+ *
64
+ * @param ctx - Context handle
65
+ * @param device - Device handle
66
+ */
67
+ Napi::Value DropDevice(const Napi::CallbackInfo& info) {
68
+ Napi::Env env = info.Env();
69
+
70
+ if (info.Length() < 2) {
71
+ Napi::TypeError::New(env, "Expected 2 arguments: ctx, device")
72
+ .ThrowAsJavaScriptException();
73
+ return env.Undefined();
74
+ }
75
+
76
+ bool lossless;
77
+ uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
78
+ uint64_t device_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
79
+
80
+ fz_drop_device(ctx_handle, device_handle);
81
+
82
+ return env.Undefined();
83
+ }
84
+
85
+ /**
86
+ * Close device (finish rendering)
87
+ *
88
+ * @param ctx - Context handle
89
+ * @param device - Device handle
90
+ */
91
+ Napi::Value CloseDevice(const Napi::CallbackInfo& info) {
92
+ Napi::Env env = info.Env();
93
+
94
+ if (info.Length() < 2) {
95
+ Napi::TypeError::New(env, "Expected 2 arguments: ctx, device")
96
+ .ThrowAsJavaScriptException();
97
+ return env.Undefined();
98
+ }
99
+
100
+ bool lossless;
101
+ uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
102
+ uint64_t device_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
103
+
104
+ fz_close_device(ctx_handle, device_handle);
105
+
106
+ return env.Undefined();
107
+ }
108
+
109
+ /**
110
+ * Begin new page on device
111
+ *
112
+ * @param ctx - Context handle
113
+ * @param device - Device handle
114
+ * @param rect - Page rectangle {x0, y0, x1, y1}
115
+ */
116
+ Napi::Value DeviceBeginPage(const Napi::CallbackInfo& info) {
117
+ Napi::Env env = info.Env();
118
+
119
+ if (info.Length() < 3) {
120
+ Napi::TypeError::New(env, "Expected 3 arguments: ctx, device, rect")
121
+ .ThrowAsJavaScriptException();
122
+ return env.Undefined();
123
+ }
124
+
125
+ bool lossless;
126
+ uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
127
+ uint64_t device_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
128
+
129
+ Napi::Object rect_obj = info[2].As<Napi::Object>();
130
+ fz_rect rect;
131
+ rect.x0 = rect_obj.Get("x0").As<Napi::Number>().FloatValue();
132
+ rect.y0 = rect_obj.Get("y0").As<Napi::Number>().FloatValue();
133
+ rect.x1 = rect_obj.Get("x1").As<Napi::Number>().FloatValue();
134
+ rect.y1 = rect_obj.Get("y1").As<Napi::Number>().FloatValue();
135
+
136
+ fz_begin_page(ctx_handle, device_handle, rect);
137
+
138
+ return env.Undefined();
139
+ }
140
+
141
+ /**
142
+ * End current page on device
143
+ *
144
+ * @param ctx - Context handle
145
+ * @param device - Device handle
146
+ */
147
+ Napi::Value DeviceEndPage(const Napi::CallbackInfo& info) {
148
+ Napi::Env env = info.Env();
149
+
150
+ if (info.Length() < 2) {
151
+ Napi::TypeError::New(env, "Expected 2 arguments: ctx, device")
152
+ .ThrowAsJavaScriptException();
153
+ return env.Undefined();
154
+ }
155
+
156
+ bool lossless;
157
+ uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
158
+ uint64_t device_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
159
+
160
+ fz_end_page(ctx_handle, device_handle);
161
+
162
+ return env.Undefined();
163
+ }
164
+
165
+ /**
166
+ * Initialize Device module exports
167
+ */
168
+ Napi::Object InitDevice(Napi::Env env, Napi::Object exports) {
169
+ exports.Set("newDrawDevice", Napi::Function::New(env, NewDrawDevice));
170
+ exports.Set("newListDevice", Napi::Function::New(env, NewListDevice));
171
+ exports.Set("dropDevice", Napi::Function::New(env, DropDevice));
172
+ exports.Set("closeDevice", Napi::Function::New(env, CloseDevice));
173
+ exports.Set("deviceBeginPage", Napi::Function::New(env, DeviceBeginPage));
174
+ exports.Set("deviceEndPage", Napi::Function::New(env, DeviceEndPage));
175
+
176
+ return exports;
177
+ }
178
+
179
+
@@ -0,0 +1,179 @@
1
+ /**
2
+ * MicroPDF Display List Bindings
3
+ *
4
+ * N-API bindings for PDF display list operations.
5
+ * Display lists cache rendering commands for efficient repeated rendering.
6
+ */
7
+
8
+ #include <napi.h>
9
+ #include "include/mupdf_minimal.h"
10
+
11
+ /**
12
+ * Create a new display list
13
+ *
14
+ * @param ctx - Context handle
15
+ * @param rect - Bounding rectangle {x0, y0, x1, y1}
16
+ * @returns Display list handle
17
+ */
18
+ Napi::BigInt NewDisplayList(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, rect")
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
+
30
+ Napi::Object rect_obj = info[1].As<Napi::Object>();
31
+ fz_rect rect;
32
+ rect.x0 = rect_obj.Get("x0").As<Napi::Number>().FloatValue();
33
+ rect.y0 = rect_obj.Get("y0").As<Napi::Number>().FloatValue();
34
+ rect.x1 = rect_obj.Get("x1").As<Napi::Number>().FloatValue();
35
+ rect.y1 = rect_obj.Get("y1").As<Napi::Number>().FloatValue();
36
+
37
+ uint64_t list_handle = fz_new_display_list(ctx_handle, rect);
38
+
39
+ return Napi::BigInt::New(env, list_handle);
40
+ }
41
+
42
+ /**
43
+ * Drop display list handle
44
+ *
45
+ * @param ctx - Context handle
46
+ * @param list - Display list handle
47
+ */
48
+ Napi::Value DropDisplayList(const Napi::CallbackInfo& info) {
49
+ Napi::Env env = info.Env();
50
+
51
+ if (info.Length() < 2) {
52
+ Napi::TypeError::New(env, "Expected 2 arguments: ctx, list")
53
+ .ThrowAsJavaScriptException();
54
+ return env.Undefined();
55
+ }
56
+
57
+ bool lossless;
58
+ uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
59
+ uint64_t list_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
60
+
61
+ fz_drop_display_list(ctx_handle, list_handle);
62
+
63
+ return env.Undefined();
64
+ }
65
+
66
+ /**
67
+ * Get display list bounds
68
+ *
69
+ * @param ctx - Context handle
70
+ * @param list - Display list handle
71
+ * @returns Rectangle {x0, y0, x1, y1}
72
+ */
73
+ Napi::Object GetDisplayListBounds(const Napi::CallbackInfo& info) {
74
+ Napi::Env env = info.Env();
75
+ Napi::Object rect = Napi::Object::New(env);
76
+
77
+ if (info.Length() < 2) {
78
+ Napi::TypeError::New(env, "Expected 2 arguments: ctx, list")
79
+ .ThrowAsJavaScriptException();
80
+ return rect;
81
+ }
82
+
83
+ bool lossless;
84
+ uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
85
+ uint64_t list_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
86
+
87
+ fz_rect bounds = fz_bound_display_list(ctx_handle, list_handle);
88
+
89
+ rect.Set("x0", Napi::Number::New(env, bounds.x0));
90
+ rect.Set("y0", Napi::Number::New(env, bounds.y0));
91
+ rect.Set("x1", Napi::Number::New(env, bounds.x1));
92
+ rect.Set("y1", Napi::Number::New(env, bounds.y1));
93
+
94
+ return rect;
95
+ }
96
+
97
+ /**
98
+ * Run display list on device
99
+ *
100
+ * @param ctx - Context handle
101
+ * @param list - Display list handle
102
+ * @param device - Device handle
103
+ * @param matrix - Transform matrix {a, b, c, d, e, f}
104
+ * @param rect - Clipping rectangle {x0, y0, x1, y1}
105
+ */
106
+ Napi::Value RunDisplayList(const Napi::CallbackInfo& info) {
107
+ Napi::Env env = info.Env();
108
+
109
+ if (info.Length() < 5) {
110
+ Napi::TypeError::New(env, "Expected 5 arguments: ctx, list, device, matrix, rect")
111
+ .ThrowAsJavaScriptException();
112
+ return env.Undefined();
113
+ }
114
+
115
+ bool lossless;
116
+ uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
117
+ uint64_t list_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
118
+ uint64_t device_handle = info[2].As<Napi::BigInt>().Uint64Value(&lossless);
119
+
120
+ Napi::Object matrix_obj = info[3].As<Napi::Object>();
121
+ fz_matrix matrix;
122
+ matrix.a = matrix_obj.Get("a").As<Napi::Number>().FloatValue();
123
+ matrix.b = matrix_obj.Get("b").As<Napi::Number>().FloatValue();
124
+ matrix.c = matrix_obj.Get("c").As<Napi::Number>().FloatValue();
125
+ matrix.d = matrix_obj.Get("d").As<Napi::Number>().FloatValue();
126
+ matrix.e = matrix_obj.Get("e").As<Napi::Number>().FloatValue();
127
+ matrix.f = matrix_obj.Get("f").As<Napi::Number>().FloatValue();
128
+
129
+ Napi::Object rect_obj = info[4].As<Napi::Object>();
130
+ fz_rect rect;
131
+ rect.x0 = rect_obj.Get("x0").As<Napi::Number>().FloatValue();
132
+ rect.y0 = rect_obj.Get("y0").As<Napi::Number>().FloatValue();
133
+ rect.x1 = rect_obj.Get("x1").As<Napi::Number>().FloatValue();
134
+ rect.y1 = rect_obj.Get("y1").As<Napi::Number>().FloatValue();
135
+
136
+ fz_run_display_list(ctx_handle, list_handle, device_handle, matrix, rect);
137
+
138
+ return env.Undefined();
139
+ }
140
+
141
+ /**
142
+ * Create display list from page
143
+ *
144
+ * @param ctx - Context handle
145
+ * @param page - Page handle
146
+ * @returns Display list handle
147
+ */
148
+ Napi::BigInt NewDisplayListFromPage(const Napi::CallbackInfo& info) {
149
+ Napi::Env env = info.Env();
150
+
151
+ if (info.Length() < 2) {
152
+ Napi::TypeError::New(env, "Expected 2 arguments: ctx, page")
153
+ .ThrowAsJavaScriptException();
154
+ return Napi::BigInt::New(env, static_cast<uint64_t>(0));
155
+ }
156
+
157
+ bool lossless;
158
+ uint64_t ctx_handle = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
159
+ uint64_t page_handle = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
160
+
161
+ uint64_t list_handle = fz_new_display_list_from_page(ctx_handle, page_handle);
162
+
163
+ return Napi::BigInt::New(env, list_handle);
164
+ }
165
+
166
+ /**
167
+ * Initialize DisplayList module exports
168
+ */
169
+ Napi::Object InitDisplayList(Napi::Env env, Napi::Object exports) {
170
+ exports.Set("newDisplayList", Napi::Function::New(env, NewDisplayList));
171
+ exports.Set("dropDisplayList", Napi::Function::New(env, DropDisplayList));
172
+ exports.Set("getDisplayListBounds", Napi::Function::New(env, GetDisplayListBounds));
173
+ exports.Set("runDisplayList", Napi::Function::New(env, RunDisplayList));
174
+ exports.Set("newDisplayListFromPage", Napi::Function::New(env, NewDisplayListFromPage));
175
+
176
+ return exports;
177
+ }
178
+
179
+
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Document FFI Bindings
3
+ *
4
+ * Implements N-API bindings for PDF document functions.
5
+ */
6
+
7
+ #include <napi.h>
8
+ #include "include/mupdf_minimal.h"
9
+ #include <cstring>
10
+
11
+ /**
12
+ * Helper: Extract context from object
13
+ */
14
+ static fz_context GetContext(const Napi::Object& obj) {
15
+ return obj.Get("_handle").As<Napi::Number>().Int32Value();
16
+ }
17
+
18
+ /**
19
+ * Helper: Extract document handle from object
20
+ */
21
+ static fz_document GetDocument(const Napi::Object& obj) {
22
+ return obj.Get("_handle").As<Napi::Number>().Int32Value();
23
+ }
24
+
25
+ /**
26
+ * Open document from file path
27
+ * JavaScript: openDocumentFromPath(ctx: NativeContext, path: string): NativeDocument
28
+ */
29
+ Napi::Value OpenDocumentFromPath(const Napi::CallbackInfo& info) {
30
+ Napi::Env env = info.Env();
31
+
32
+ if (info.Length() < 2 || !info[0].IsObject() || !info[1].IsString()) {
33
+ Napi::TypeError::New(env, "Expected (context, path)").ThrowAsJavaScriptException();
34
+ return env.Null();
35
+ }
36
+
37
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
38
+ std::string path = info[1].As<Napi::String>().Utf8Value();
39
+
40
+ fz_document doc = fz_open_document(ctx, path.c_str());
41
+ if (doc == 0) {
42
+ Napi::Error::New(env, "Failed to open document").ThrowAsJavaScriptException();
43
+ return env.Null();
44
+ }
45
+
46
+ Napi::Object obj = Napi::Object::New(env);
47
+ obj.Set("_handle", Napi::Number::New(env, doc));
48
+ return obj;
49
+ }
50
+
51
+ /**
52
+ * Open document from memory buffer
53
+ * JavaScript: openDocument(ctx: NativeContext, data: Buffer, magic: string): NativeDocument
54
+ */
55
+ Napi::Value OpenDocument(const Napi::CallbackInfo& info) {
56
+ Napi::Env env = info.Env();
57
+
58
+ if (info.Length() < 3 || !info[0].IsObject() || !info[1].IsBuffer() || !info[2].IsString()) {
59
+ Napi::TypeError::New(env, "Expected (context, buffer, magic)").ThrowAsJavaScriptException();
60
+ return env.Null();
61
+ }
62
+
63
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
64
+ Napi::Buffer<uint8_t> buffer = info[1].As<Napi::Buffer<uint8_t>>();
65
+ std::string magic = info[2].As<Napi::String>().Utf8Value();
66
+
67
+ fz_document doc = fz_open_document_with_buffer(ctx, magic.c_str(), buffer.Data(), buffer.Length());
68
+ if (doc == 0) {
69
+ Napi::Error::New(env, "Failed to open document").ThrowAsJavaScriptException();
70
+ return env.Null();
71
+ }
72
+
73
+ Napi::Object obj = Napi::Object::New(env);
74
+ obj.Set("_handle", Napi::Number::New(env, doc));
75
+ return obj;
76
+ }
77
+
78
+ /**
79
+ * Drop/free a document
80
+ * JavaScript: dropDocument(ctx: NativeContext, doc: NativeDocument): void
81
+ */
82
+ Napi::Value DropDocument(const Napi::CallbackInfo& info) {
83
+ Napi::Env env = info.Env();
84
+
85
+ if (info.Length() < 2 || !info[0].IsObject() || !info[1].IsObject()) {
86
+ Napi::TypeError::New(env, "Expected (context, document)").ThrowAsJavaScriptException();
87
+ return env.Undefined();
88
+ }
89
+
90
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
91
+ fz_document doc = GetDocument(info[1].As<Napi::Object>());
92
+
93
+ fz_drop_document(ctx, doc);
94
+
95
+ return env.Undefined();
96
+ }
97
+
98
+ /**
99
+ * Count pages in document
100
+ * JavaScript: countPages(ctx: NativeContext, doc: NativeDocument): number
101
+ */
102
+ Napi::Value CountPages(const Napi::CallbackInfo& info) {
103
+ Napi::Env env = info.Env();
104
+
105
+ if (info.Length() < 2 || !info[0].IsObject() || !info[1].IsObject()) {
106
+ Napi::TypeError::New(env, "Expected (context, document)").ThrowAsJavaScriptException();
107
+ return env.Null();
108
+ }
109
+
110
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
111
+ fz_document doc = GetDocument(info[1].As<Napi::Object>());
112
+
113
+ int count = fz_count_pages(ctx, doc);
114
+
115
+ return Napi::Number::New(env, count);
116
+ }
117
+
118
+ /**
119
+ * Check if document needs password
120
+ * JavaScript: needsPassword(ctx: NativeContext, doc: NativeDocument): boolean
121
+ */
122
+ Napi::Value NeedsPassword(const Napi::CallbackInfo& info) {
123
+ Napi::Env env = info.Env();
124
+
125
+ if (info.Length() < 2 || !info[0].IsObject() || !info[1].IsObject()) {
126
+ Napi::TypeError::New(env, "Expected (context, document)").ThrowAsJavaScriptException();
127
+ return env.Null();
128
+ }
129
+
130
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
131
+ fz_document doc = GetDocument(info[1].As<Napi::Object>());
132
+
133
+ int needs = fz_needs_password(ctx, doc);
134
+
135
+ return Napi::Boolean::New(env, needs != 0);
136
+ }
137
+
138
+ /**
139
+ * Authenticate document with password
140
+ * JavaScript: authenticatePassword(ctx: NativeContext, doc: NativeDocument, password: string): boolean
141
+ */
142
+ Napi::Value AuthenticatePassword(const Napi::CallbackInfo& info) {
143
+ Napi::Env env = info.Env();
144
+
145
+ if (info.Length() < 3 || !info[0].IsObject() || !info[1].IsObject() || !info[2].IsString()) {
146
+ Napi::TypeError::New(env, "Expected (context, document, password)").ThrowAsJavaScriptException();
147
+ return env.Null();
148
+ }
149
+
150
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
151
+ fz_document doc = GetDocument(info[1].As<Napi::Object>());
152
+ std::string password = info[2].As<Napi::String>().Utf8Value();
153
+
154
+ int success = fz_authenticate_password(ctx, doc, password.c_str());
155
+
156
+ return Napi::Boolean::New(env, success != 0);
157
+ }
158
+
159
+ /**
160
+ * Check document permission
161
+ * JavaScript: hasPermission(ctx: NativeContext, doc: NativeDocument, permission: number): boolean
162
+ */
163
+ Napi::Value HasPermission(const Napi::CallbackInfo& info) {
164
+ Napi::Env env = info.Env();
165
+
166
+ if (info.Length() < 3 || !info[0].IsObject() || !info[1].IsObject() || !info[2].IsNumber()) {
167
+ Napi::TypeError::New(env, "Expected (context, document, permission)").ThrowAsJavaScriptException();
168
+ return env.Null();
169
+ }
170
+
171
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
172
+ fz_document doc = GetDocument(info[1].As<Napi::Object>());
173
+ int permission = info[2].As<Napi::Number>().Int32Value();
174
+
175
+ int has = fz_has_permission(ctx, doc, permission);
176
+
177
+ return Napi::Boolean::New(env, has != 0);
178
+ }
179
+
180
+ /**
181
+ * Get document metadata
182
+ * JavaScript: getMetadata(ctx: NativeContext, doc: NativeDocument, key: string): string | null
183
+ */
184
+ Napi::Value GetMetadata(const Napi::CallbackInfo& info) {
185
+ Napi::Env env = info.Env();
186
+
187
+ if (info.Length() < 3 || !info[0].IsObject() || !info[1].IsObject() || !info[2].IsString()) {
188
+ Napi::TypeError::New(env, "Expected (context, document, key)").ThrowAsJavaScriptException();
189
+ return env.Null();
190
+ }
191
+
192
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
193
+ fz_document doc = GetDocument(info[1].As<Napi::Object>());
194
+ std::string key = info[2].As<Napi::String>().Utf8Value();
195
+
196
+ char buf[1024] = {0};
197
+ int len = fz_lookup_metadata(ctx, doc, key.c_str(), buf, sizeof(buf));
198
+
199
+ if (len > 0) {
200
+ return Napi::String::New(env, buf);
201
+ }
202
+
203
+ return env.Null();
204
+ }
205
+
206
+ /**
207
+ * Save document to file
208
+ * JavaScript: saveDocument(ctx: NativeContext, doc: NativeDocument, path: string): void
209
+ */
210
+ Napi::Value SaveDocument(const Napi::CallbackInfo& info) {
211
+ Napi::Env env = info.Env();
212
+
213
+ if (info.Length() < 3 || !info[0].IsObject() || !info[1].IsObject() || !info[2].IsString()) {
214
+ Napi::TypeError::New(env, "Expected (context, document, path)").ThrowAsJavaScriptException();
215
+ return env.Undefined();
216
+ }
217
+
218
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
219
+ fz_document doc = GetDocument(info[1].As<Napi::Object>());
220
+ std::string path = info[2].As<Napi::String>().Utf8Value();
221
+
222
+ pdf_save_document(ctx, doc, path.c_str(), nullptr);
223
+
224
+ return env.Undefined();
225
+ }
226
+
227
+ /**
228
+ * Resolve named destination
229
+ * JavaScript: resolveLink(ctx: NativeContext, doc: NativeDocument, name: string): number | null
230
+ */
231
+ Napi::Value ResolveLink(const Napi::CallbackInfo& info) {
232
+ Napi::Env env = info.Env();
233
+
234
+ if (info.Length() < 3 || !info[0].IsObject() || !info[1].IsObject() || !info[2].IsString()) {
235
+ Napi::TypeError::New(env, "Expected (context, document, name)").ThrowAsJavaScriptException();
236
+ return env.Null();
237
+ }
238
+
239
+ fz_context ctx = GetContext(info[0].As<Napi::Object>());
240
+ fz_document doc = GetDocument(info[1].As<Napi::Object>());
241
+ std::string name = info[2].As<Napi::String>().Utf8Value();
242
+
243
+ int page = pdf_lookup_named_dest(ctx, doc, name.c_str());
244
+
245
+ if (page >= 0) {
246
+ return Napi::Number::New(env, page);
247
+ }
248
+
249
+ return env.Null();
250
+ }
251
+
252
+ /**
253
+ * Initialize document exports
254
+ */
255
+ Napi::Object InitDocument(Napi::Env env, Napi::Object exports) {
256
+ exports.Set("openDocumentFromPath", Napi::Function::New(env, OpenDocumentFromPath));
257
+ exports.Set("openDocument", Napi::Function::New(env, OpenDocument));
258
+ exports.Set("dropDocument", Napi::Function::New(env, DropDocument));
259
+ exports.Set("countPages", Napi::Function::New(env, CountPages));
260
+ exports.Set("needsPassword", Napi::Function::New(env, NeedsPassword));
261
+ exports.Set("authenticatePassword", Napi::Function::New(env, AuthenticatePassword));
262
+ exports.Set("hasPermission", Napi::Function::New(env, HasPermission));
263
+ exports.Set("getMetadata", Napi::Function::New(env, GetMetadata));
264
+ exports.Set("saveDocument", Napi::Function::New(env, SaveDocument));
265
+ exports.Set("resolveLink", Napi::Function::New(env, ResolveLink));
266
+ return exports;
267
+ }
268
+
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Enhanced API FFI Bindings
3
+ *
4
+ * Implements N-API bindings for MicroPDF-specific enhanced functions.
5
+ */
6
+
7
+ #include <napi.h>
8
+ #include "include/micropdf/enhanced.h"
9
+ #include <vector>
10
+ #include <cstring>
11
+
12
+ /**
13
+ * Helper: Extract context handle from object
14
+ */
15
+ static int32_t GetContext(const Napi::Object& obj) {
16
+ return obj.Get("_handle").As<Napi::Number>().Int32Value();
17
+ }
18
+
19
+ /**
20
+ * Merge multiple PDFs into one output PDF
21
+ * JavaScript: npMergePDFs(ctx: NativeContext, paths: string[], count: number, outputPath: string): number
22
+ */
23
+ Napi::Value MergePDFs(const Napi::CallbackInfo& info) {
24
+ Napi::Env env = info.Env();
25
+
26
+ if (info.Length() < 4 ||
27
+ !info[0].IsObject() ||
28
+ !info[1].IsArray() ||
29
+ !info[2].IsNumber() ||
30
+ !info[3].IsString()) {
31
+ Napi::TypeError::New(env, "Expected (context, paths: string[], count: number, outputPath: string)")
32
+ .ThrowAsJavaScriptException();
33
+ return Napi::Number::New(env, -1);
34
+ }
35
+
36
+ int32_t ctx = GetContext(info[0].As<Napi::Object>());
37
+ Napi::Array pathsArray = info[1].As<Napi::Array>();
38
+ int32_t count = info[2].As<Napi::Number>().Int32Value();
39
+ std::string outputPath = info[3].As<Napi::String>().Utf8Value();
40
+
41
+ // Convert JavaScript string array to C string array
42
+ std::vector<const char*> paths(count);
43
+ std::vector<std::string> pathStrings(count);
44
+
45
+ for (int32_t i = 0; i < count; i++) {
46
+ Napi::Value val = pathsArray[i];
47
+ if (!val.IsString()) {
48
+ Napi::TypeError::New(env, "All paths must be strings")
49
+ .ThrowAsJavaScriptException();
50
+ return Napi::Number::New(env, -1);
51
+ }
52
+ pathStrings[i] = val.As<Napi::String>().Utf8Value();
53
+ paths[i] = pathStrings[i].c_str();
54
+ }
55
+
56
+ // Call the Rust FFI function
57
+ int32_t result = mp_merge_pdfs(ctx, paths.data(), count, outputPath.c_str());
58
+
59
+ return Napi::Number::New(env, result);
60
+ }
61
+
62
+ /**
63
+ * Initialize enhanced API exports
64
+ */
65
+ Napi::Object InitEnhanced(Napi::Env env, Napi::Object exports) {
66
+ exports.Set("npMergePDFs", Napi::Function::New(env, MergePDFs));
67
+
68
+ return exports;
69
+ }
70
+