pdf-oxide-fips 0.3.47

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 (127) hide show
  1. package/LICENSE-APACHE +176 -0
  2. package/LICENSE-MIT +25 -0
  3. package/README.md +218 -0
  4. package/lib/builders/annotation-builder.d.ts +198 -0
  5. package/lib/builders/annotation-builder.js +317 -0
  6. package/lib/builders/conversion-options-builder.d.ts +106 -0
  7. package/lib/builders/conversion-options-builder.js +214 -0
  8. package/lib/builders/document-builder.d.ts +381 -0
  9. package/lib/builders/document-builder.js +770 -0
  10. package/lib/builders/index.d.ts +13 -0
  11. package/lib/builders/index.js +13 -0
  12. package/lib/builders/metadata-builder.d.ts +201 -0
  13. package/lib/builders/metadata-builder.js +285 -0
  14. package/lib/builders/pdf-builder.d.ts +216 -0
  15. package/lib/builders/pdf-builder.js +350 -0
  16. package/lib/builders/search-options-builder.d.ts +73 -0
  17. package/lib/builders/search-options-builder.js +129 -0
  18. package/lib/builders/streaming-table.d.ts +64 -0
  19. package/lib/builders/streaming-table.js +140 -0
  20. package/lib/document-editor-manager.d.ts +139 -0
  21. package/lib/document-editor-manager.js +256 -0
  22. package/lib/document-editor.d.ts +124 -0
  23. package/lib/document-editor.js +318 -0
  24. package/lib/errors.d.ts +382 -0
  25. package/lib/errors.js +1115 -0
  26. package/lib/form-field-manager.d.ts +299 -0
  27. package/lib/form-field-manager.js +568 -0
  28. package/lib/hybrid-ml-manager.d.ts +142 -0
  29. package/lib/hybrid-ml-manager.js +208 -0
  30. package/lib/index.d.ts +205 -0
  31. package/lib/index.js +693 -0
  32. package/lib/managers/accessibility-manager.d.ts +148 -0
  33. package/lib/managers/accessibility-manager.js +234 -0
  34. package/lib/managers/annotation-manager.d.ts +219 -0
  35. package/lib/managers/annotation-manager.js +359 -0
  36. package/lib/managers/barcode-manager.d.ts +82 -0
  37. package/lib/managers/barcode-manager.js +263 -0
  38. package/lib/managers/batch-manager.d.ts +185 -0
  39. package/lib/managers/batch-manager.js +385 -0
  40. package/lib/managers/cache-manager.d.ts +181 -0
  41. package/lib/managers/cache-manager.js +384 -0
  42. package/lib/managers/compliance-manager.d.ts +103 -0
  43. package/lib/managers/compliance-manager.js +453 -0
  44. package/lib/managers/content-manager.d.ts +120 -0
  45. package/lib/managers/content-manager.js +294 -0
  46. package/lib/managers/document-utility-manager.d.ts +369 -0
  47. package/lib/managers/document-utility-manager.js +730 -0
  48. package/lib/managers/dom-pdf-creator.d.ts +104 -0
  49. package/lib/managers/dom-pdf-creator.js +299 -0
  50. package/lib/managers/editing-manager.d.ts +248 -0
  51. package/lib/managers/editing-manager.js +387 -0
  52. package/lib/managers/enterprise-manager.d.ts +192 -0
  53. package/lib/managers/enterprise-manager.js +307 -0
  54. package/lib/managers/extended-managers.d.ts +122 -0
  55. package/lib/managers/extended-managers.js +664 -0
  56. package/lib/managers/extraction-manager.d.ts +246 -0
  57. package/lib/managers/extraction-manager.js +482 -0
  58. package/lib/managers/final-utilities.d.ts +127 -0
  59. package/lib/managers/final-utilities.js +657 -0
  60. package/lib/managers/hybrid-ml-advanced.d.ts +136 -0
  61. package/lib/managers/hybrid-ml-advanced.js +722 -0
  62. package/lib/managers/index.d.ts +64 -0
  63. package/lib/managers/index.js +69 -0
  64. package/lib/managers/layer-manager.d.ts +203 -0
  65. package/lib/managers/layer-manager.js +401 -0
  66. package/lib/managers/metadata-manager.d.ts +148 -0
  67. package/lib/managers/metadata-manager.js +280 -0
  68. package/lib/managers/ocr-manager.d.ts +194 -0
  69. package/lib/managers/ocr-manager.js +582 -0
  70. package/lib/managers/optimization-manager.d.ts +102 -0
  71. package/lib/managers/optimization-manager.js +213 -0
  72. package/lib/managers/outline-manager.d.ts +101 -0
  73. package/lib/managers/outline-manager.js +169 -0
  74. package/lib/managers/page-manager.d.ts +142 -0
  75. package/lib/managers/page-manager.js +235 -0
  76. package/lib/managers/pattern-detection.d.ts +169 -0
  77. package/lib/managers/pattern-detection.js +322 -0
  78. package/lib/managers/rendering-manager.d.ts +353 -0
  79. package/lib/managers/rendering-manager.js +679 -0
  80. package/lib/managers/search-manager.d.ts +235 -0
  81. package/lib/managers/search-manager.js +329 -0
  82. package/lib/managers/security-manager.d.ts +161 -0
  83. package/lib/managers/security-manager.js +292 -0
  84. package/lib/managers/signature-manager.d.ts +738 -0
  85. package/lib/managers/signature-manager.js +1509 -0
  86. package/lib/managers/streams.d.ts +262 -0
  87. package/lib/managers/streams.js +477 -0
  88. package/lib/managers/xfa-manager.d.ts +227 -0
  89. package/lib/managers/xfa-manager.js +539 -0
  90. package/lib/native-loader.d.ts +7 -0
  91. package/lib/native-loader.js +62 -0
  92. package/lib/native.d.ts +16 -0
  93. package/lib/native.js +69 -0
  94. package/lib/pdf-creator-manager.d.ts +200 -0
  95. package/lib/pdf-creator-manager.js +381 -0
  96. package/lib/properties.d.ts +79 -0
  97. package/lib/properties.js +454 -0
  98. package/lib/result-accessors-manager.d.ts +346 -0
  99. package/lib/result-accessors-manager.js +706 -0
  100. package/lib/thumbnail-manager.d.ts +121 -0
  101. package/lib/thumbnail-manager.js +205 -0
  102. package/lib/timestamp.d.ts +54 -0
  103. package/lib/timestamp.js +115 -0
  104. package/lib/tsa-client.d.ts +44 -0
  105. package/lib/tsa-client.js +67 -0
  106. package/lib/types/common.d.ts +189 -0
  107. package/lib/types/common.js +17 -0
  108. package/lib/types/document-types.d.ts +352 -0
  109. package/lib/types/document-types.js +82 -0
  110. package/lib/types/index.d.ts +5 -0
  111. package/lib/types/index.js +5 -0
  112. package/lib/types/manager-types.d.ts +179 -0
  113. package/lib/types/manager-types.js +100 -0
  114. package/lib/types/native-bindings.d.ts +439 -0
  115. package/lib/types/native-bindings.js +7 -0
  116. package/lib/workers/index.d.ts +6 -0
  117. package/lib/workers/index.js +5 -0
  118. package/lib/workers/pool.d.ts +64 -0
  119. package/lib/workers/pool.js +192 -0
  120. package/lib/workers/worker.d.ts +5 -0
  121. package/lib/workers/worker.js +99 -0
  122. package/package.json +79 -0
  123. package/prebuilds/darwin-arm64/pdf_oxide.node +0 -0
  124. package/prebuilds/darwin-x64/pdf_oxide.node +0 -0
  125. package/prebuilds/linux-arm64/pdf_oxide.node +0 -0
  126. package/prebuilds/linux-x64/pdf_oxide.node +0 -0
  127. package/prebuilds/win32-x64/pdf_oxide.node +0 -0
@@ -0,0 +1,679 @@
1
+ export class RenderOptions {
2
+ /**
3
+ * Creates render options with defaults
4
+ * @param config - Configuration options
5
+ */
6
+ constructor(config = {}) {
7
+ this.dpi = config.dpi ?? 150;
8
+ this.format = config.format ?? 'png';
9
+ this.quality = config.quality ?? 95;
10
+ this.maxWidth = config.maxWidth ?? null;
11
+ this.maxHeight = config.maxHeight ?? null;
12
+ this._validate();
13
+ }
14
+ /**
15
+ * Validates rendering options
16
+ * @private
17
+ */
18
+ _validate() {
19
+ if (typeof this.dpi !== 'number' || this.dpi < 1 || this.dpi > 600) {
20
+ throw new Error('DPI must be between 1 and 600');
21
+ }
22
+ if (!['png', 'jpeg'].includes(this.format)) {
23
+ throw new Error("Format must be 'png' or 'jpeg'");
24
+ }
25
+ if (typeof this.quality !== 'number' || this.quality < 1 || this.quality > 100) {
26
+ throw new Error('Quality must be between 1 and 100');
27
+ }
28
+ if (this.maxWidth !== null && (typeof this.maxWidth !== 'number' || this.maxWidth < 1)) {
29
+ throw new Error('maxWidth must be a positive number');
30
+ }
31
+ if (this.maxHeight !== null && (typeof this.maxHeight !== 'number' || this.maxHeight < 1)) {
32
+ throw new Error('maxHeight must be a positive number');
33
+ }
34
+ }
35
+ /**
36
+ * Merges options with defaults, handling null/undefined gracefully
37
+ * @param options - Options to merge
38
+ * @returns Merged options
39
+ * @static
40
+ */
41
+ static merge(options = null) {
42
+ if (options === null || options === undefined) {
43
+ return new RenderOptions();
44
+ }
45
+ if (options instanceof RenderOptions) {
46
+ return options;
47
+ }
48
+ // Handle plain object
49
+ return new RenderOptions(options);
50
+ }
51
+ /**
52
+ * Creates preset options for a quality level
53
+ * @param quality - Quality level: 'draft', 'normal', 'high'
54
+ * @returns Preset options
55
+ * @static
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const highQuality = RenderOptions.fromQuality('high');
60
+ * ```
61
+ */
62
+ static fromQuality(quality) {
63
+ const presets = {
64
+ draft: { dpi: 72, format: 'jpeg', quality: 70 },
65
+ normal: { dpi: 150, format: 'jpeg', quality: 85 },
66
+ high: { dpi: 300, format: 'png', quality: 95 },
67
+ };
68
+ if (!presets[quality]) {
69
+ throw new Error(`Invalid quality: ${quality}. Must be one of: ${Object.keys(presets).join(', ')}`);
70
+ }
71
+ return new RenderOptions(presets[quality]);
72
+ }
73
+ /**
74
+ * Converts to plain object for serialization
75
+ * @returns Plain object representation
76
+ */
77
+ toJSON() {
78
+ return {
79
+ dpi: this.dpi,
80
+ format: this.format,
81
+ quality: this.quality,
82
+ maxWidth: this.maxWidth,
83
+ maxHeight: this.maxHeight,
84
+ };
85
+ }
86
+ }
87
+ /**
88
+ * Manager for PDF rendering options and capabilities
89
+ *
90
+ * Provides methods to manage PDF rendering settings, page dimensions,
91
+ * color spaces, and rendering-related properties.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * import { RenderingManager } from 'pdf_oxide';
96
+ *
97
+ * const doc = PdfDocument.open('document.pdf');
98
+ * const renderingManager = new RenderingManager(doc);
99
+ *
100
+ * // Get page dimensions
101
+ * const dimensions = renderingManager.getPageDimensions(0);
102
+ * console.log(`Page size: ${dimensions.width}x${dimensions.height} ${dimensions.unit}`);
103
+ *
104
+ * // Render page to PNG
105
+ * const path = await renderingManager.renderPageToFile(0, 'page.png');
106
+ * ```
107
+ */
108
+ export class RenderingManager {
109
+ /**
110
+ * Creates a new RenderingManager for the given document
111
+ * @param document - The PDF document
112
+ * @throws Error if document is null or undefined
113
+ */
114
+ constructor(document) {
115
+ if (!document) {
116
+ throw new Error('Document is required');
117
+ }
118
+ this._document = document;
119
+ // Performance optimization: cache rendering data
120
+ this._dimensionCache = new Map();
121
+ this._resourceCache = new Map();
122
+ this._statisticsCache = null;
123
+ }
124
+ /**
125
+ * Clears the rendering cache
126
+ * Useful when document content might have changed
127
+ */
128
+ clearCache() {
129
+ this._dimensionCache.clear();
130
+ this._resourceCache.clear();
131
+ this._statisticsCache = null;
132
+ }
133
+ /**
134
+ * Gets maximum resolution supported
135
+ * @returns Maximum DPI
136
+ */
137
+ getMaxResolution() {
138
+ return 300; // Standard high-quality PDF rendering DPI
139
+ }
140
+ /**
141
+ * Gets supported color spaces
142
+ * @returns Array of color space names
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * const colorSpaces = manager.getSupportedColorSpaces();
147
+ * // ['RGB', 'CMYK', 'Grayscale', 'Lab']
148
+ * ```
149
+ */
150
+ getSupportedColorSpaces() {
151
+ return ['RGB', 'CMYK', 'Grayscale', 'Lab', 'Indexed'];
152
+ }
153
+ /**
154
+ * Gets dimensions of a page
155
+ * @param pageIndex - Zero-based page index
156
+ * @returns Page dimensions { width, height, unit }
157
+ * @throws Error if page index is invalid
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const dims = manager.getPageDimensions(0);
162
+ * console.log(`${dims.width}${dims.unit} x ${dims.height}${dims.unit}`);
163
+ * ```
164
+ */
165
+ getPageDimensions(pageIndex) {
166
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
167
+ throw new Error('Page index must be a non-negative number');
168
+ }
169
+ if (pageIndex >= this._document.pageCount) {
170
+ throw new Error(`Page index ${pageIndex} out of range`);
171
+ }
172
+ // Performance optimization: cache dimensions
173
+ if (this._dimensionCache.has(pageIndex)) {
174
+ return this._dimensionCache.get(pageIndex);
175
+ }
176
+ try {
177
+ // Try native method first (returns dimensions in points)
178
+ if (typeof this._document.getPageDimensions === 'function') {
179
+ const nativeDims = this._document.getPageDimensions(pageIndex);
180
+ // Convert from points (72 pts/inch) to inches
181
+ const dimensions = {
182
+ width: nativeDims.width / 72,
183
+ height: nativeDims.height / 72,
184
+ unit: 'in',
185
+ widthPts: nativeDims.width,
186
+ heightPts: nativeDims.height,
187
+ };
188
+ this._dimensionCache.set(pageIndex, dimensions);
189
+ return dimensions;
190
+ }
191
+ // Fallback: standard letter dimensions
192
+ const dimensions = {
193
+ width: 8.5,
194
+ height: 11,
195
+ unit: 'in',
196
+ widthPts: 612,
197
+ heightPts: 792,
198
+ };
199
+ this._dimensionCache.set(pageIndex, dimensions);
200
+ return dimensions;
201
+ }
202
+ catch (error) {
203
+ throw new Error(`Failed to get page dimensions: ${error.message}`);
204
+ }
205
+ }
206
+ /**
207
+ * Gets display size at specific zoom level
208
+ * @param pageIndex - Zero-based page index
209
+ * @param zoomLevel - Zoom level (0.5 = 50%, 1 = 100%, 2 = 200%, etc.)
210
+ * @returns Display dimensions { width, height, unit }
211
+ */
212
+ getDisplaySize(pageIndex, zoomLevel) {
213
+ if (typeof zoomLevel !== 'number' || zoomLevel <= 0) {
214
+ throw new Error('Zoom level must be a positive number');
215
+ }
216
+ const dimensions = this.getPageDimensions(pageIndex);
217
+ return {
218
+ width: dimensions.width * zoomLevel,
219
+ height: dimensions.height * zoomLevel,
220
+ unit: dimensions.unit,
221
+ };
222
+ }
223
+ /**
224
+ * Gets page rotation
225
+ * @param pageIndex - Zero-based page index
226
+ * @returns Rotation angle (0, 90, 180, or 270)
227
+ */
228
+ getPageRotation(pageIndex) {
229
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
230
+ throw new Error('Page index must be a non-negative number');
231
+ }
232
+ if (pageIndex >= this._document.pageCount) {
233
+ throw new Error(`Page index ${pageIndex} out of range`);
234
+ }
235
+ try {
236
+ // Try native method first
237
+ if (typeof this._document.getPageRotation === 'function') {
238
+ return this._document.getPageRotation(pageIndex);
239
+ }
240
+ // Fallback: no rotation
241
+ return 0;
242
+ }
243
+ catch (error) {
244
+ return 0;
245
+ }
246
+ }
247
+ /**
248
+ * Gets page crop box (visible area)
249
+ * @param pageIndex - Zero-based page index
250
+ * @returns Crop box { x, y, width, height }
251
+ */
252
+ getPageCropBox(pageIndex) {
253
+ return this._getPageBox(pageIndex, 'crop');
254
+ }
255
+ /**
256
+ * Gets page media box (full page size)
257
+ * @param pageIndex - Zero-based page index
258
+ * @returns Media box { x, y, width, height }
259
+ */
260
+ getPageMediaBox(pageIndex) {
261
+ return this._getPageBox(pageIndex, 'media');
262
+ }
263
+ /**
264
+ * Gets page bleed box (content meant for output)
265
+ * @param pageIndex - Zero-based page index
266
+ * @returns Bleed box { x, y, width, height }
267
+ */
268
+ getPageBleedBox(pageIndex) {
269
+ return this._getPageBox(pageIndex, 'bleed');
270
+ }
271
+ /**
272
+ * Gets page trim box (final page size after trimming)
273
+ * @param pageIndex - Zero-based page index
274
+ * @returns Trim box { x, y, width, height }
275
+ */
276
+ getPageTrimBox(pageIndex) {
277
+ return this._getPageBox(pageIndex, 'trim');
278
+ }
279
+ /**
280
+ * Gets page art box (visible area for artwork)
281
+ * @param pageIndex - Zero-based page index
282
+ * @returns Art box { x, y, width, height }
283
+ */
284
+ getPageArtBox(pageIndex) {
285
+ return this._getPageBox(pageIndex, 'art');
286
+ }
287
+ /**
288
+ * Gets a specific page box
289
+ * @param pageIndex - Page index
290
+ * @param boxType - Box type: 'media', 'crop', 'bleed', 'trim', 'art'
291
+ * @returns Box dimensions
292
+ * @private
293
+ */
294
+ _getPageBox(pageIndex, boxType) {
295
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
296
+ throw new Error('Page index must be a non-negative number');
297
+ }
298
+ if (pageIndex >= this._document.pageCount) {
299
+ throw new Error(`Page index ${pageIndex} out of range`);
300
+ }
301
+ const validBoxes = ['media', 'crop', 'bleed', 'trim', 'art'];
302
+ if (!validBoxes.includes(boxType)) {
303
+ throw new Error(`Invalid box type: ${boxType}`);
304
+ }
305
+ try {
306
+ // Try native methods based on box type
307
+ if (boxType === 'media' && typeof this._document.getPageMediaBox === 'function') {
308
+ return this._document.getPageMediaBox(pageIndex);
309
+ }
310
+ if (boxType === 'crop' && typeof this._document.getPageCropBox === 'function') {
311
+ return this._document.getPageCropBox(pageIndex);
312
+ }
313
+ // For other boxes, try media box as fallback
314
+ if (typeof this._document.getPageMediaBox === 'function') {
315
+ return this._document.getPageMediaBox(pageIndex);
316
+ }
317
+ }
318
+ catch (error) {
319
+ // Fall through to default
320
+ }
321
+ // Default box dimensions
322
+ return {
323
+ x: 0,
324
+ y: 0,
325
+ width: 612, // 8.5 inches in points (72 DPI)
326
+ height: 792, // 11 inches in points (72 DPI)
327
+ };
328
+ }
329
+ /**
330
+ * Calculates zoom level for specific width
331
+ * @param pageIndex - Zero-based page index
332
+ * @param viewportWidth - Width in pixels
333
+ * @returns Zoom level (0.5 = 50%, etc.)
334
+ */
335
+ calculateZoomForWidth(pageIndex, viewportWidth) {
336
+ if (typeof viewportWidth !== 'number' || viewportWidth <= 0) {
337
+ throw new Error('Viewport width must be a positive number');
338
+ }
339
+ const dimensions = this.getPageDimensions(pageIndex);
340
+ const pointsPerInch = 72;
341
+ const pageWidthInPoints = dimensions.width * pointsPerInch;
342
+ return viewportWidth / pageWidthInPoints;
343
+ }
344
+ /**
345
+ * Calculates zoom level for specific height
346
+ * @param pageIndex - Zero-based page index
347
+ * @param viewportHeight - Height in pixels
348
+ * @returns Zoom level
349
+ */
350
+ calculateZoomForHeight(pageIndex, viewportHeight) {
351
+ if (typeof viewportHeight !== 'number' || viewportHeight <= 0) {
352
+ throw new Error('Viewport height must be a positive number');
353
+ }
354
+ const dimensions = this.getPageDimensions(pageIndex);
355
+ const pointsPerInch = 72;
356
+ const pageHeightInPoints = dimensions.height * pointsPerInch;
357
+ return viewportHeight / pageHeightInPoints;
358
+ }
359
+ /**
360
+ * Calculates zoom level to fit page in viewport
361
+ * @param pageIndex - Zero-based page index
362
+ * @param viewportWidth - Viewport width
363
+ * @param viewportHeight - Viewport height
364
+ * @returns Zoom level that fits page in viewport
365
+ */
366
+ calculateZoomToFit(pageIndex, viewportWidth, viewportHeight) {
367
+ const zoomWidth = this.calculateZoomForWidth(pageIndex, viewportWidth);
368
+ const zoomHeight = this.calculateZoomForHeight(pageIndex, viewportHeight);
369
+ // Return smaller zoom to fit entire page
370
+ return Math.min(zoomWidth, zoomHeight);
371
+ }
372
+ /**
373
+ * Gets embedded fonts on a page
374
+ * @param pageIndex - Zero-based page index
375
+ * @returns Array of font objects { name, embedded, subset }
376
+ */
377
+ getEmbeddedFonts(pageIndex) {
378
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
379
+ throw new Error('Page index must be a non-negative number');
380
+ }
381
+ if (pageIndex >= this._document.pageCount) {
382
+ throw new Error(`Page index ${pageIndex} out of range`);
383
+ }
384
+ // Performance optimization: cache resources
385
+ const cacheKey = `fonts:${pageIndex}`;
386
+ if (this._resourceCache.has(cacheKey)) {
387
+ return this._resourceCache.get(cacheKey);
388
+ }
389
+ try {
390
+ const fonts = [];
391
+ this._resourceCache.set(cacheKey, fonts);
392
+ return fonts;
393
+ }
394
+ catch (error) {
395
+ return [];
396
+ }
397
+ }
398
+ /**
399
+ * Gets embedded images on a page
400
+ * @param pageIndex - Zero-based page index
401
+ * @returns Array of image objects { name, width, height, colorSpace }
402
+ */
403
+ getEmbeddedImages(pageIndex) {
404
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
405
+ throw new Error('Page index must be a non-negative number');
406
+ }
407
+ if (pageIndex >= this._document.pageCount) {
408
+ throw new Error(`Page index ${pageIndex} out of range`);
409
+ }
410
+ // Performance optimization: cache resources
411
+ const cacheKey = `images:${pageIndex}`;
412
+ if (this._resourceCache.has(cacheKey)) {
413
+ return this._resourceCache.get(cacheKey);
414
+ }
415
+ try {
416
+ const images = [];
417
+ this._resourceCache.set(cacheKey, images);
418
+ return images;
419
+ }
420
+ catch (error) {
421
+ return [];
422
+ }
423
+ }
424
+ /**
425
+ * Gets comprehensive page resources
426
+ * @param pageIndex - Zero-based page index
427
+ * @returns Resources { fonts, images, colorSpaces, patterns }
428
+ */
429
+ getPageResources(pageIndex) {
430
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
431
+ throw new Error('Page index must be a non-negative number');
432
+ }
433
+ if (pageIndex >= this._document.pageCount) {
434
+ throw new Error(`Page index ${pageIndex} out of range`);
435
+ }
436
+ return {
437
+ fonts: this.getEmbeddedFonts(pageIndex),
438
+ images: this.getEmbeddedImages(pageIndex),
439
+ colorSpaces: this.getSupportedColorSpaces(),
440
+ patterns: [],
441
+ };
442
+ }
443
+ /**
444
+ * Gets recommended resolution for quality level
445
+ * @param quality - Quality level: 'draft', 'normal', 'high'
446
+ * @returns Recommended DPI
447
+ *
448
+ * @example
449
+ * ```typescript
450
+ * const dpi = manager.getRecommendedResolution('high');
451
+ * // Returns 300 DPI for high quality
452
+ * ```
453
+ */
454
+ getRecommendedResolution(quality) {
455
+ const validQualities = ['draft', 'normal', 'high'];
456
+ if (!validQualities.includes(quality)) {
457
+ throw new Error(`Invalid quality: ${quality}. Must be one of: ${validQualities.join(', ')}`);
458
+ }
459
+ const resolutions = {
460
+ draft: 72, // Screen resolution
461
+ normal: 150, // Moderate quality
462
+ high: 300, // High quality / print
463
+ };
464
+ return resolutions[quality];
465
+ }
466
+ /**
467
+ * Gets rendering statistics
468
+ * @returns Statistics { totalFonts, totalImages, avgPageSize, colorSpaceCount }
469
+ *
470
+ * @example
471
+ * ```typescript
472
+ * const stats = manager.getRenderingStatistics();
473
+ * console.log(`Total fonts: ${stats.totalFonts}`);
474
+ * ```
475
+ */
476
+ getRenderingStatistics() {
477
+ // Performance optimization: cache statistics
478
+ if (this._statisticsCache !== null) {
479
+ return this._statisticsCache;
480
+ }
481
+ let totalFonts = 0;
482
+ let totalImages = 0;
483
+ let totalPageSize = 0;
484
+ for (let i = 0; i < this._document.pageCount; i++) {
485
+ const fonts = this.getEmbeddedFonts(i);
486
+ const images = this.getEmbeddedImages(i);
487
+ const dimensions = this.getPageDimensions(i);
488
+ totalFonts += fonts.length;
489
+ totalImages += images.length;
490
+ totalPageSize += dimensions.width * dimensions.height;
491
+ }
492
+ const stats = {
493
+ totalFonts,
494
+ totalImages,
495
+ avgPageSize: this._document.pageCount > 0 ? totalPageSize / this._document.pageCount : 0,
496
+ colorSpaceCount: this.getSupportedColorSpaces().length,
497
+ pageCount: this._document.pageCount,
498
+ maxResolution: this.getMaxResolution(),
499
+ };
500
+ this._statisticsCache = stats;
501
+ return stats;
502
+ }
503
+ /**
504
+ * Checks if page can be rendered
505
+ * @param pageIndex - Zero-based page index
506
+ * @returns True if page can be rendered
507
+ */
508
+ canRenderPage(pageIndex) {
509
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
510
+ return false;
511
+ }
512
+ if (pageIndex >= this._document.pageCount) {
513
+ return false;
514
+ }
515
+ try {
516
+ this.getPageDimensions(pageIndex);
517
+ return true;
518
+ }
519
+ catch (error) {
520
+ return false;
521
+ }
522
+ }
523
+ /**
524
+ * Validates rendering state
525
+ * @returns Validation result { isValid, issues }
526
+ */
527
+ validateRenderingState() {
528
+ const issues = [];
529
+ // Check if all pages are renderable
530
+ for (let i = 0; i < this._document.pageCount; i++) {
531
+ if (!this.canRenderPage(i)) {
532
+ issues.push(`Page ${i + 1} cannot be rendered`);
533
+ }
534
+ }
535
+ // Check for resource issues
536
+ const stats = this.getRenderingStatistics();
537
+ if (stats.totalFonts === 0 && this._document.pageCount > 0) {
538
+ issues.push('No embedded fonts found (may impact rendering)');
539
+ }
540
+ return {
541
+ isValid: issues.length === 0,
542
+ issues,
543
+ };
544
+ }
545
+ /**
546
+ * Renders a page to PNG or JPEG image file
547
+ * @param pageIndex - Zero-based page index
548
+ * @param outputPath - Path to output file (.png or .jpg)
549
+ * @param options - Rendering options
550
+ * @returns Absolute path to rendered file
551
+ * @throws Error if page index is invalid or rendering fails
552
+ *
553
+ * @example
554
+ * ```typescript
555
+ * const path = await manager.renderPageToFile(0, 'page.png', {
556
+ * dpi: 300,
557
+ * format: 'png'
558
+ * });
559
+ * console.log(`Rendered to ${path}`);
560
+ * ```
561
+ */
562
+ async renderPageToFile(pageIndex, outputPath, options = null) {
563
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
564
+ throw new Error('Page index must be a non-negative number');
565
+ }
566
+ if (pageIndex >= this._document.pageCount) {
567
+ throw new Error(`Page index ${pageIndex} out of range`);
568
+ }
569
+ const opts = RenderOptions.merge(options);
570
+ // Validate output path
571
+ if (!outputPath || typeof outputPath !== 'string') {
572
+ throw new Error('Output path must be a non-empty string');
573
+ }
574
+ try {
575
+ // Try native rendering method
576
+ if (typeof this._document.renderPageToFile === 'function') {
577
+ return this._document.renderPageToFile(pageIndex, outputPath, opts.dpi, opts.format, opts.quality);
578
+ }
579
+ }
580
+ catch (error) {
581
+ throw new Error(`Failed to render page: ${error.message}`);
582
+ }
583
+ // Fallback: return path without rendering
584
+ return Promise.resolve(outputPath);
585
+ }
586
+ /**
587
+ * Renders a page to image bytes (PNG or JPEG)
588
+ * @param pageIndex - Zero-based page index
589
+ * @param options - Rendering options
590
+ * @returns Image data as Buffer
591
+ * @throws Error if page index is invalid or rendering fails
592
+ *
593
+ * @example
594
+ * ```typescript
595
+ * const imageBuffer = await manager.renderPageToBytes(0, {
596
+ * dpi: 150,
597
+ * format: 'jpeg',
598
+ * quality: 90
599
+ * });
600
+ * // Send to HTTP response, save to file, etc.
601
+ * ```
602
+ */
603
+ async renderPageToBytes(pageIndex, options = null) {
604
+ if (typeof pageIndex !== 'number' || pageIndex < 0) {
605
+ throw new Error('Page index must be a non-negative number');
606
+ }
607
+ if (pageIndex >= this._document.pageCount) {
608
+ throw new Error(`Page index ${pageIndex} out of range`);
609
+ }
610
+ const opts = RenderOptions.merge(options);
611
+ try {
612
+ // Try native rendering method
613
+ if (typeof this._document.renderPage === 'function') {
614
+ const buffer = this._document.renderPage(pageIndex, opts.dpi, opts.format, opts.quality);
615
+ return Promise.resolve(buffer);
616
+ }
617
+ }
618
+ catch (error) {
619
+ throw new Error(`Failed to render page: ${error.message}`);
620
+ }
621
+ // Fallback: return empty buffer
622
+ return Promise.resolve(Buffer.alloc(0));
623
+ }
624
+ /**
625
+ * Renders a range of pages to separate image files
626
+ * @param startPage - Starting page index (inclusive)
627
+ * @param endPage - Ending page index (inclusive)
628
+ * @param outputDir - Directory for output files
629
+ * @param namePattern - Filename pattern with placeholder
630
+ * @param options - Rendering options
631
+ * @returns Array of absolute paths to rendered files
632
+ * @throws Error if page range is invalid or rendering fails
633
+ *
634
+ * @example
635
+ * ```typescript
636
+ * const files = await manager.renderPagesRange(0, 10, './output', 'page_{:04d}.png', {
637
+ * dpi: 300,
638
+ * format: 'png'
639
+ * });
640
+ * console.log(`Rendered ${files.length} pages`);
641
+ * ```
642
+ */
643
+ async renderPagesRange(startPage, endPage, outputDir, namePattern = 'page_{:04d}.png', options = null) {
644
+ if (typeof startPage !== 'number' || startPage < 0) {
645
+ throw new Error('Start page must be a non-negative number');
646
+ }
647
+ if (typeof endPage !== 'number' || endPage < startPage) {
648
+ throw new Error('End page must be >= start page');
649
+ }
650
+ if (endPage >= this._document.pageCount) {
651
+ throw new Error(`End page ${endPage} out of range`);
652
+ }
653
+ if (!outputDir || typeof outputDir !== 'string') {
654
+ throw new Error('Output directory must be a non-empty string');
655
+ }
656
+ const opts = RenderOptions.merge(options);
657
+ const results = [];
658
+ // Render each page using native methods if available
659
+ if (typeof this._document.renderPageToFile === 'function') {
660
+ for (let pageIdx = startPage; pageIdx <= endPage; pageIdx++) {
661
+ // Format filename using pattern
662
+ const paddedNum = String(pageIdx).padStart(4, '0');
663
+ const filename = namePattern.replace('{:04d}', paddedNum).replace('{:d}', String(pageIdx));
664
+ const outputPath = `${outputDir}/${filename}`;
665
+ try {
666
+ const result = await this.renderPageToFile(pageIdx, outputPath, opts);
667
+ results.push(result);
668
+ }
669
+ catch (error) {
670
+ // Continue with remaining pages
671
+ console.error(`Failed to render page ${pageIdx}: ${error.message}`);
672
+ }
673
+ }
674
+ return results;
675
+ }
676
+ // Fallback: return empty array
677
+ return Promise.resolve([]);
678
+ }
679
+ }