pdf-oxide 0.3.24
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/README.md +218 -0
- package/binding.gyp +35 -0
- package/package.json +78 -0
- package/src/builders/annotation-builder.ts +367 -0
- package/src/builders/conversion-options-builder.ts +257 -0
- package/src/builders/index.ts +12 -0
- package/src/builders/metadata-builder.ts +317 -0
- package/src/builders/pdf-builder.ts +386 -0
- package/src/builders/search-options-builder.ts +151 -0
- package/src/document-editor-manager.ts +318 -0
- package/src/errors.ts +1629 -0
- package/src/form-field-manager.ts +666 -0
- package/src/hybrid-ml-manager.ts +283 -0
- package/src/index.ts +453 -0
- package/src/managers/accessibility-manager.ts +338 -0
- package/src/managers/annotation-manager.ts +439 -0
- package/src/managers/barcode-manager.ts +235 -0
- package/src/managers/batch-manager.ts +533 -0
- package/src/managers/cache-manager.ts +486 -0
- package/src/managers/compliance-manager.ts +375 -0
- package/src/managers/content-manager.ts +339 -0
- package/src/managers/document-utility-manager.ts +922 -0
- package/src/managers/dom-pdf-creator.ts +365 -0
- package/src/managers/editing-manager.ts +514 -0
- package/src/managers/enterprise-manager.ts +478 -0
- package/src/managers/extended-managers.ts +437 -0
- package/src/managers/extraction-manager.ts +583 -0
- package/src/managers/final-utilities.ts +429 -0
- package/src/managers/hybrid-ml-advanced.ts +479 -0
- package/src/managers/index.ts +239 -0
- package/src/managers/layer-manager.ts +500 -0
- package/src/managers/metadata-manager.ts +303 -0
- package/src/managers/ocr-manager.ts +756 -0
- package/src/managers/optimization-manager.ts +262 -0
- package/src/managers/outline-manager.ts +196 -0
- package/src/managers/page-manager.ts +289 -0
- package/src/managers/pattern-detection.ts +440 -0
- package/src/managers/rendering-manager.ts +863 -0
- package/src/managers/search-manager.ts +385 -0
- package/src/managers/security-manager.ts +345 -0
- package/src/managers/signature-manager.ts +1664 -0
- package/src/managers/streams.ts +618 -0
- package/src/managers/xfa-manager.ts +500 -0
- package/src/pdf-creator-manager.ts +494 -0
- package/src/properties.ts +522 -0
- package/src/result-accessors-manager.ts +867 -0
- package/src/tests/advanced-features.test.ts +414 -0
- package/src/tests/advanced.test.ts +266 -0
- package/src/tests/extended-managers.test.ts +316 -0
- package/src/tests/final-utilities.test.ts +455 -0
- package/src/tests/foundation.test.ts +315 -0
- package/src/tests/high-demand.test.ts +257 -0
- package/src/tests/specialized.test.ts +97 -0
- package/src/thumbnail-manager.ts +272 -0
- package/src/types/common.ts +142 -0
- package/src/types/document-types.ts +457 -0
- package/src/types/index.ts +6 -0
- package/src/types/manager-types.ts +284 -0
- package/src/types/native-bindings.ts +517 -0
- package/src/workers/index.ts +7 -0
- package/src/workers/pool.ts +274 -0
- package/src/workers/worker.ts +131 -0
|
@@ -0,0 +1,922 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document Utility Manager - Document optimization and manipulation utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides comprehensive document utilities:
|
|
5
|
+
* - Document optimization and compression
|
|
6
|
+
* - PDF linearization (fast web view)
|
|
7
|
+
* - Font optimization and subsetting
|
|
8
|
+
* - Image optimization and recompression
|
|
9
|
+
* - Page manipulation utilities
|
|
10
|
+
* - Document repair
|
|
11
|
+
* - Resource cleanup
|
|
12
|
+
*
|
|
13
|
+
* This completes the document utility coverage for 100% FFI parity.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { EventEmitter } from 'events';
|
|
17
|
+
import { promises as fs } from 'fs';
|
|
18
|
+
import { dirname } from 'path';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Type Definitions
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Optimization level enumeration
|
|
26
|
+
*/
|
|
27
|
+
export enum OptimizationLevel {
|
|
28
|
+
/** No optimization */
|
|
29
|
+
NONE = 'none',
|
|
30
|
+
/** Light optimization - fast, minimal size reduction */
|
|
31
|
+
LIGHT = 'light',
|
|
32
|
+
/** Balanced optimization - good balance of speed and size */
|
|
33
|
+
BALANCED = 'balanced',
|
|
34
|
+
/** Aggressive optimization - maximum size reduction, slower */
|
|
35
|
+
AGGRESSIVE = 'aggressive',
|
|
36
|
+
/** Maximum optimization - smallest possible size */
|
|
37
|
+
MAXIMUM = 'maximum',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Image compression type
|
|
42
|
+
*/
|
|
43
|
+
export enum ImageCompressionType {
|
|
44
|
+
NONE = 'none',
|
|
45
|
+
JPEG = 'jpeg',
|
|
46
|
+
JPEG2000 = 'jpeg2000',
|
|
47
|
+
JBIG2 = 'jbig2',
|
|
48
|
+
FLATE = 'flate',
|
|
49
|
+
LZW = 'lzw',
|
|
50
|
+
RUN_LENGTH = 'run_length',
|
|
51
|
+
CCITT_FAX = 'ccitt_fax',
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Color space type
|
|
56
|
+
*/
|
|
57
|
+
export enum ColorSpaceType {
|
|
58
|
+
RGB = 'rgb',
|
|
59
|
+
CMYK = 'cmyk',
|
|
60
|
+
GRAYSCALE = 'grayscale',
|
|
61
|
+
INDEXED = 'indexed',
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Font embedding mode
|
|
66
|
+
*/
|
|
67
|
+
export enum FontEmbeddingMode {
|
|
68
|
+
/** Embed full fonts */
|
|
69
|
+
FULL = 'full',
|
|
70
|
+
/** Embed subset of used glyphs only */
|
|
71
|
+
SUBSET = 'subset',
|
|
72
|
+
/** Remove all font embedding */
|
|
73
|
+
REMOVE = 'remove',
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Page range specification
|
|
78
|
+
*/
|
|
79
|
+
export interface PageRange {
|
|
80
|
+
readonly start: number;
|
|
81
|
+
readonly end: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Optimization options
|
|
86
|
+
*/
|
|
87
|
+
export interface OptimizationOptions {
|
|
88
|
+
readonly level?: OptimizationLevel;
|
|
89
|
+
readonly compressImages?: boolean;
|
|
90
|
+
readonly imageQuality?: number; // 1-100
|
|
91
|
+
readonly imageCompression?: ImageCompressionType;
|
|
92
|
+
readonly downsampleImages?: boolean;
|
|
93
|
+
readonly maxImageDpi?: number;
|
|
94
|
+
readonly removeUnusedObjects?: boolean;
|
|
95
|
+
readonly removeMetadata?: boolean;
|
|
96
|
+
readonly removeThumbnails?: boolean;
|
|
97
|
+
readonly removeBookmarks?: boolean;
|
|
98
|
+
readonly removeAnnotations?: boolean;
|
|
99
|
+
readonly removeJavaScript?: boolean;
|
|
100
|
+
readonly removeFormFields?: boolean;
|
|
101
|
+
readonly removeEmbeddedFiles?: boolean;
|
|
102
|
+
readonly linearize?: boolean;
|
|
103
|
+
readonly fontEmbedding?: FontEmbeddingMode;
|
|
104
|
+
readonly convertColorSpace?: ColorSpaceType;
|
|
105
|
+
readonly flattenTransparency?: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Compression options
|
|
110
|
+
*/
|
|
111
|
+
export interface CompressionOptions {
|
|
112
|
+
readonly compressStreams?: boolean;
|
|
113
|
+
readonly compressObjects?: boolean;
|
|
114
|
+
readonly algorithm?: 'flate' | 'lzw' | 'none';
|
|
115
|
+
readonly level?: number; // 1-9
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Image optimization options
|
|
120
|
+
*/
|
|
121
|
+
export interface ImageOptimizationOptions {
|
|
122
|
+
readonly maxDpi?: number;
|
|
123
|
+
readonly minDpi?: number;
|
|
124
|
+
readonly targetQuality?: number;
|
|
125
|
+
readonly targetFormat?: ImageCompressionType;
|
|
126
|
+
readonly convertColor?: ColorSpaceType;
|
|
127
|
+
readonly removeIccProfiles?: boolean;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Font optimization options
|
|
132
|
+
*/
|
|
133
|
+
export interface FontOptimizationOptions {
|
|
134
|
+
readonly mode?: FontEmbeddingMode;
|
|
135
|
+
readonly removeUnusedFonts?: boolean;
|
|
136
|
+
readonly mergeSubsets?: boolean;
|
|
137
|
+
readonly convertToType1?: boolean;
|
|
138
|
+
readonly convertToOpenType?: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Linearization options
|
|
143
|
+
*/
|
|
144
|
+
export interface LinearizationOptions {
|
|
145
|
+
readonly firstPageEnd?: number;
|
|
146
|
+
readonly primaryHint?: boolean;
|
|
147
|
+
readonly overflowHint?: boolean;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Optimization result
|
|
152
|
+
*/
|
|
153
|
+
export interface OptimizationResult {
|
|
154
|
+
readonly success: boolean;
|
|
155
|
+
readonly originalSize: number;
|
|
156
|
+
readonly optimizedSize: number;
|
|
157
|
+
readonly reductionPercent: number;
|
|
158
|
+
readonly reductionBytes: number;
|
|
159
|
+
readonly duration: number;
|
|
160
|
+
readonly details?: {
|
|
161
|
+
readonly imagesOptimized?: number;
|
|
162
|
+
readonly fontsOptimized?: number;
|
|
163
|
+
readonly objectsRemoved?: number;
|
|
164
|
+
readonly streamsCompressed?: number;
|
|
165
|
+
};
|
|
166
|
+
readonly error?: string;
|
|
167
|
+
readonly warnings?: readonly string[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Document repair result
|
|
172
|
+
*/
|
|
173
|
+
export interface RepairResult {
|
|
174
|
+
readonly success: boolean;
|
|
175
|
+
readonly issuesFound: number;
|
|
176
|
+
readonly issuesFixed: number;
|
|
177
|
+
readonly issues: readonly string[];
|
|
178
|
+
readonly error?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Document statistics
|
|
183
|
+
*/
|
|
184
|
+
export interface DocumentStatistics {
|
|
185
|
+
readonly pageCount: number;
|
|
186
|
+
readonly fileSize: number;
|
|
187
|
+
readonly objectCount: number;
|
|
188
|
+
readonly streamCount: number;
|
|
189
|
+
readonly imageCount: number;
|
|
190
|
+
readonly fontCount: number;
|
|
191
|
+
readonly annotationCount: number;
|
|
192
|
+
readonly bookmarkCount: number;
|
|
193
|
+
readonly embeddedFileCount: number;
|
|
194
|
+
readonly formFieldCount: number;
|
|
195
|
+
readonly signatureCount: number;
|
|
196
|
+
readonly hasJavaScript: boolean;
|
|
197
|
+
readonly hasXfa: boolean;
|
|
198
|
+
readonly isLinearized: boolean;
|
|
199
|
+
readonly isEncrypted: boolean;
|
|
200
|
+
readonly pdfVersion: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Page information
|
|
205
|
+
*/
|
|
206
|
+
export interface PageInfo {
|
|
207
|
+
readonly index: number;
|
|
208
|
+
readonly width: number;
|
|
209
|
+
readonly height: number;
|
|
210
|
+
readonly rotation: number;
|
|
211
|
+
readonly hasAnnotations: boolean;
|
|
212
|
+
readonly hasText: boolean;
|
|
213
|
+
readonly hasImages: boolean;
|
|
214
|
+
readonly mediaBox: readonly [number, number, number, number];
|
|
215
|
+
readonly cropBox?: readonly [number, number, number, number];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ============================================================================
|
|
219
|
+
// Document Utility Manager
|
|
220
|
+
// ============================================================================
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Document Utility Manager - Complete document optimization and manipulation
|
|
224
|
+
*
|
|
225
|
+
* Provides 35 functions for document utilities.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const doc = await PdfDocument.open('large-document.pdf');
|
|
230
|
+
* const utilityManager = new DocumentUtilityManager(doc);
|
|
231
|
+
*
|
|
232
|
+
* // Optimize document
|
|
233
|
+
* const result = await utilityManager.optimizeDocument({
|
|
234
|
+
* level: OptimizationLevel.AGGRESSIVE,
|
|
235
|
+
* compressImages: true,
|
|
236
|
+
* imageQuality: 75,
|
|
237
|
+
* linearize: true,
|
|
238
|
+
* });
|
|
239
|
+
*
|
|
240
|
+
* console.log(`Reduced by ${result.reductionPercent}%`);
|
|
241
|
+
*
|
|
242
|
+
* // Save optimized document
|
|
243
|
+
* await utilityManager.saveOptimized('optimized.pdf');
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
export class DocumentUtilityManager extends EventEmitter {
|
|
247
|
+
private readonly document: any;
|
|
248
|
+
private lastOptimizationResult: OptimizationResult | null = null;
|
|
249
|
+
|
|
250
|
+
constructor(document: any) {
|
|
251
|
+
super();
|
|
252
|
+
if (!document) {
|
|
253
|
+
throw new Error('Document cannot be null or undefined');
|
|
254
|
+
}
|
|
255
|
+
this.document = document;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ==========================================================================
|
|
259
|
+
// Optimization Functions (8 functions)
|
|
260
|
+
// ==========================================================================
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Optimizes the document with specified options
|
|
264
|
+
*/
|
|
265
|
+
async optimizeDocument(options?: OptimizationOptions): Promise<OptimizationResult> {
|
|
266
|
+
const startTime = Date.now();
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const originalSize = await this.getFileSize();
|
|
270
|
+
|
|
271
|
+
const result = await this.document?.optimizeDocument?.({
|
|
272
|
+
level: options?.level ?? OptimizationLevel.BALANCED,
|
|
273
|
+
compressImages: options?.compressImages ?? true,
|
|
274
|
+
imageQuality: options?.imageQuality ?? 85,
|
|
275
|
+
imageCompression: options?.imageCompression ?? ImageCompressionType.JPEG,
|
|
276
|
+
downsampleImages: options?.downsampleImages ?? true,
|
|
277
|
+
maxImageDpi: options?.maxImageDpi ?? 150,
|
|
278
|
+
removeUnusedObjects: options?.removeUnusedObjects ?? true,
|
|
279
|
+
removeMetadata: options?.removeMetadata ?? false,
|
|
280
|
+
removeThumbnails: options?.removeThumbnails ?? true,
|
|
281
|
+
removeBookmarks: options?.removeBookmarks ?? false,
|
|
282
|
+
removeAnnotations: options?.removeAnnotations ?? false,
|
|
283
|
+
removeJavaScript: options?.removeJavaScript ?? false,
|
|
284
|
+
removeFormFields: options?.removeFormFields ?? false,
|
|
285
|
+
removeEmbeddedFiles: options?.removeEmbeddedFiles ?? false,
|
|
286
|
+
linearize: options?.linearize ?? false,
|
|
287
|
+
fontEmbedding: options?.fontEmbedding ?? FontEmbeddingMode.SUBSET,
|
|
288
|
+
convertColorSpace: options?.convertColorSpace,
|
|
289
|
+
flattenTransparency: options?.flattenTransparency ?? false,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const optimizedSize = await this.getFileSize();
|
|
293
|
+
const reductionBytes = originalSize - optimizedSize;
|
|
294
|
+
const reductionPercent = originalSize > 0 ? (reductionBytes / originalSize) * 100 : 0;
|
|
295
|
+
const duration = Date.now() - startTime;
|
|
296
|
+
|
|
297
|
+
const optimizationResult: OptimizationResult = {
|
|
298
|
+
success: true,
|
|
299
|
+
originalSize,
|
|
300
|
+
optimizedSize,
|
|
301
|
+
reductionPercent: Math.round(reductionPercent * 100) / 100,
|
|
302
|
+
reductionBytes,
|
|
303
|
+
duration,
|
|
304
|
+
details: result?.details,
|
|
305
|
+
warnings: result?.warnings,
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
this.lastOptimizationResult = optimizationResult;
|
|
309
|
+
this.emit('optimization-complete', optimizationResult);
|
|
310
|
+
|
|
311
|
+
return optimizationResult;
|
|
312
|
+
} catch (error) {
|
|
313
|
+
const duration = Date.now() - startTime;
|
|
314
|
+
const errorResult: OptimizationResult = {
|
|
315
|
+
success: false,
|
|
316
|
+
originalSize: 0,
|
|
317
|
+
optimizedSize: 0,
|
|
318
|
+
reductionPercent: 0,
|
|
319
|
+
reductionBytes: 0,
|
|
320
|
+
duration,
|
|
321
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
this.emit('error', error);
|
|
325
|
+
return errorResult;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Compresses document streams
|
|
331
|
+
*/
|
|
332
|
+
async compressStreams(options?: CompressionOptions): Promise<boolean> {
|
|
333
|
+
try {
|
|
334
|
+
const result = await this.document?.compressStreams?.({
|
|
335
|
+
algorithm: options?.algorithm ?? 'flate',
|
|
336
|
+
level: options?.level ?? 6,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
this.emit('streams-compressed');
|
|
340
|
+
return !!result;
|
|
341
|
+
} catch (error) {
|
|
342
|
+
this.emit('error', error);
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Optimizes images in the document
|
|
349
|
+
*/
|
|
350
|
+
async optimizeImages(options?: ImageOptimizationOptions): Promise<number> {
|
|
351
|
+
try {
|
|
352
|
+
const result = await this.document?.optimizeImages?.({
|
|
353
|
+
maxDpi: options?.maxDpi ?? 150,
|
|
354
|
+
minDpi: options?.minDpi ?? 72,
|
|
355
|
+
targetQuality: options?.targetQuality ?? 85,
|
|
356
|
+
targetFormat: options?.targetFormat ?? ImageCompressionType.JPEG,
|
|
357
|
+
convertColor: options?.convertColor,
|
|
358
|
+
removeIccProfiles: options?.removeIccProfiles ?? false,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const count = result?.optimizedCount ?? 0;
|
|
362
|
+
this.emit('images-optimized', { count });
|
|
363
|
+
return count;
|
|
364
|
+
} catch (error) {
|
|
365
|
+
this.emit('error', error);
|
|
366
|
+
return 0;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Optimizes fonts in the document
|
|
372
|
+
*/
|
|
373
|
+
async optimizeFonts(options?: FontOptimizationOptions): Promise<number> {
|
|
374
|
+
try {
|
|
375
|
+
const result = await this.document?.optimizeFonts?.({
|
|
376
|
+
mode: options?.mode ?? FontEmbeddingMode.SUBSET,
|
|
377
|
+
removeUnusedFonts: options?.removeUnusedFonts ?? true,
|
|
378
|
+
mergeSubsets: options?.mergeSubsets ?? true,
|
|
379
|
+
convertToType1: options?.convertToType1 ?? false,
|
|
380
|
+
convertToOpenType: options?.convertToOpenType ?? false,
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const count = result?.optimizedCount ?? 0;
|
|
384
|
+
this.emit('fonts-optimized', { count });
|
|
385
|
+
return count;
|
|
386
|
+
} catch (error) {
|
|
387
|
+
this.emit('error', error);
|
|
388
|
+
return 0;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Linearizes the document for fast web view
|
|
394
|
+
*/
|
|
395
|
+
async linearize(options?: LinearizationOptions): Promise<boolean> {
|
|
396
|
+
try {
|
|
397
|
+
const result = await this.document?.linearize?.({
|
|
398
|
+
firstPageEnd: options?.firstPageEnd,
|
|
399
|
+
primaryHint: options?.primaryHint ?? true,
|
|
400
|
+
overflowHint: options?.overflowHint ?? true,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
this.emit('document-linearized');
|
|
404
|
+
return !!result;
|
|
405
|
+
} catch (error) {
|
|
406
|
+
this.emit('error', error);
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Removes unused objects from the document
|
|
413
|
+
*/
|
|
414
|
+
async removeUnusedObjects(): Promise<number> {
|
|
415
|
+
try {
|
|
416
|
+
const result = await this.document?.removeUnusedObjects?.();
|
|
417
|
+
const count = result ?? 0;
|
|
418
|
+
this.emit('unused-objects-removed', { count });
|
|
419
|
+
return count;
|
|
420
|
+
} catch (error) {
|
|
421
|
+
this.emit('error', error);
|
|
422
|
+
return 0;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Flattens transparency in the document
|
|
428
|
+
*/
|
|
429
|
+
async flattenTransparency(): Promise<boolean> {
|
|
430
|
+
try {
|
|
431
|
+
const result = await this.document?.flattenTransparency?.();
|
|
432
|
+
this.emit('transparency-flattened');
|
|
433
|
+
return !!result;
|
|
434
|
+
} catch (error) {
|
|
435
|
+
this.emit('error', error);
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Converts color space
|
|
442
|
+
*/
|
|
443
|
+
async convertColorSpace(targetColorSpace: ColorSpaceType): Promise<boolean> {
|
|
444
|
+
try {
|
|
445
|
+
const result = await this.document?.convertColorSpace?.(targetColorSpace);
|
|
446
|
+
this.emit('color-space-converted', { targetColorSpace });
|
|
447
|
+
return !!result;
|
|
448
|
+
} catch (error) {
|
|
449
|
+
this.emit('error', error);
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ==========================================================================
|
|
455
|
+
// Page Manipulation (8 functions)
|
|
456
|
+
// ==========================================================================
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Rotates pages
|
|
460
|
+
*/
|
|
461
|
+
async rotatePages(pageRange: PageRange | 'all', degrees: 90 | 180 | 270): Promise<number> {
|
|
462
|
+
try {
|
|
463
|
+
const range = pageRange === 'all' ? null : pageRange;
|
|
464
|
+
const result = await this.document?.rotatePages?.(range, degrees);
|
|
465
|
+
const count = result ?? 0;
|
|
466
|
+
this.emit('pages-rotated', { count, degrees });
|
|
467
|
+
return count;
|
|
468
|
+
} catch (error) {
|
|
469
|
+
this.emit('error', error);
|
|
470
|
+
return 0;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Scales pages
|
|
476
|
+
*/
|
|
477
|
+
async scalePages(
|
|
478
|
+
pageRange: PageRange | 'all',
|
|
479
|
+
scaleX: number,
|
|
480
|
+
scaleY: number
|
|
481
|
+
): Promise<number> {
|
|
482
|
+
try {
|
|
483
|
+
const range = pageRange === 'all' ? null : pageRange;
|
|
484
|
+
const result = await this.document?.scalePages?.(range, scaleX, scaleY);
|
|
485
|
+
const count = result ?? 0;
|
|
486
|
+
this.emit('pages-scaled', { count, scaleX, scaleY });
|
|
487
|
+
return count;
|
|
488
|
+
} catch (error) {
|
|
489
|
+
this.emit('error', error);
|
|
490
|
+
return 0;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Crops pages
|
|
496
|
+
*/
|
|
497
|
+
async cropPages(
|
|
498
|
+
pageRange: PageRange | 'all',
|
|
499
|
+
cropBox: readonly [number, number, number, number]
|
|
500
|
+
): Promise<number> {
|
|
501
|
+
try {
|
|
502
|
+
const range = pageRange === 'all' ? null : pageRange;
|
|
503
|
+
const result = await this.document?.cropPages?.(range, cropBox);
|
|
504
|
+
const count = result ?? 0;
|
|
505
|
+
this.emit('pages-cropped', { count });
|
|
506
|
+
return count;
|
|
507
|
+
} catch (error) {
|
|
508
|
+
this.emit('error', error);
|
|
509
|
+
return 0;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Removes pages
|
|
515
|
+
*/
|
|
516
|
+
async removePages(pageIndices: readonly number[]): Promise<boolean> {
|
|
517
|
+
try {
|
|
518
|
+
const result = await this.document?.removePages?.(pageIndices);
|
|
519
|
+
this.emit('pages-removed', { count: pageIndices.length });
|
|
520
|
+
return !!result;
|
|
521
|
+
} catch (error) {
|
|
522
|
+
this.emit('error', error);
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Reorders pages
|
|
529
|
+
*/
|
|
530
|
+
async reorderPages(newOrder: readonly number[]): Promise<boolean> {
|
|
531
|
+
try {
|
|
532
|
+
const result = await this.document?.reorderPages?.(newOrder);
|
|
533
|
+
this.emit('pages-reordered');
|
|
534
|
+
return !!result;
|
|
535
|
+
} catch (error) {
|
|
536
|
+
this.emit('error', error);
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Duplicates pages
|
|
543
|
+
*/
|
|
544
|
+
async duplicatePages(pageIndices: readonly number[], times: number = 1): Promise<boolean> {
|
|
545
|
+
try {
|
|
546
|
+
const result = await this.document?.duplicatePages?.(pageIndices, times);
|
|
547
|
+
this.emit('pages-duplicated', { count: pageIndices.length * times });
|
|
548
|
+
return !!result;
|
|
549
|
+
} catch (error) {
|
|
550
|
+
this.emit('error', error);
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Extracts pages to a new document
|
|
557
|
+
*/
|
|
558
|
+
async extractPages(pageIndices: readonly number[]): Promise<Buffer | null> {
|
|
559
|
+
try {
|
|
560
|
+
const result = await this.document?.extractPages?.(pageIndices);
|
|
561
|
+
this.emit('pages-extracted', { count: pageIndices.length });
|
|
562
|
+
return result ?? null;
|
|
563
|
+
} catch (error) {
|
|
564
|
+
this.emit('error', error);
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Inserts blank pages
|
|
571
|
+
*/
|
|
572
|
+
async insertBlankPages(
|
|
573
|
+
afterPageIndex: number,
|
|
574
|
+
count: number,
|
|
575
|
+
width?: number,
|
|
576
|
+
height?: number
|
|
577
|
+
): Promise<boolean> {
|
|
578
|
+
try {
|
|
579
|
+
const result = await this.document?.insertBlankPages?.(
|
|
580
|
+
afterPageIndex,
|
|
581
|
+
count,
|
|
582
|
+
width ?? 612,
|
|
583
|
+
height ?? 792
|
|
584
|
+
);
|
|
585
|
+
this.emit('blank-pages-inserted', { afterPageIndex, count });
|
|
586
|
+
return !!result;
|
|
587
|
+
} catch (error) {
|
|
588
|
+
this.emit('error', error);
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// ==========================================================================
|
|
594
|
+
// Document Information (6 functions)
|
|
595
|
+
// ==========================================================================
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Gets document statistics
|
|
599
|
+
*/
|
|
600
|
+
async getDocumentStatistics(): Promise<DocumentStatistics | null> {
|
|
601
|
+
try {
|
|
602
|
+
const stats = await this.document?.getDocumentStatistics?.();
|
|
603
|
+
|
|
604
|
+
return stats ?? {
|
|
605
|
+
pageCount: await this.getPageCount(),
|
|
606
|
+
fileSize: await this.getFileSize(),
|
|
607
|
+
objectCount: 0,
|
|
608
|
+
streamCount: 0,
|
|
609
|
+
imageCount: 0,
|
|
610
|
+
fontCount: 0,
|
|
611
|
+
annotationCount: 0,
|
|
612
|
+
bookmarkCount: 0,
|
|
613
|
+
embeddedFileCount: 0,
|
|
614
|
+
formFieldCount: 0,
|
|
615
|
+
signatureCount: 0,
|
|
616
|
+
hasJavaScript: false,
|
|
617
|
+
hasXfa: false,
|
|
618
|
+
isLinearized: false,
|
|
619
|
+
isEncrypted: false,
|
|
620
|
+
pdfVersion: '1.7',
|
|
621
|
+
};
|
|
622
|
+
} catch (error) {
|
|
623
|
+
this.emit('error', error);
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Gets page information
|
|
630
|
+
*/
|
|
631
|
+
async getPageInfo(pageIndex: number): Promise<PageInfo | null> {
|
|
632
|
+
try {
|
|
633
|
+
const info = await this.document?.getPageInfo?.(pageIndex);
|
|
634
|
+
return info ?? null;
|
|
635
|
+
} catch (error) {
|
|
636
|
+
this.emit('error', error);
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Gets page count
|
|
643
|
+
*/
|
|
644
|
+
async getPageCount(): Promise<number> {
|
|
645
|
+
try {
|
|
646
|
+
return await this.document?.getPageCount?.() ?? 0;
|
|
647
|
+
} catch (error) {
|
|
648
|
+
this.emit('error', error);
|
|
649
|
+
return 0;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Gets file size in bytes
|
|
655
|
+
*/
|
|
656
|
+
async getFileSize(): Promise<number> {
|
|
657
|
+
try {
|
|
658
|
+
return await this.document?.getFileSize?.() ?? 0;
|
|
659
|
+
} catch (error) {
|
|
660
|
+
this.emit('error', error);
|
|
661
|
+
return 0;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Checks if document is linearized
|
|
667
|
+
*/
|
|
668
|
+
async isLinearized(): Promise<boolean> {
|
|
669
|
+
try {
|
|
670
|
+
return await this.document?.isLinearized?.() ?? false;
|
|
671
|
+
} catch (error) {
|
|
672
|
+
this.emit('error', error);
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Gets last optimization result
|
|
679
|
+
*/
|
|
680
|
+
getLastOptimizationResult(): OptimizationResult | null {
|
|
681
|
+
return this.lastOptimizationResult;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// ==========================================================================
|
|
685
|
+
// Repair and Cleanup (5 functions)
|
|
686
|
+
// ==========================================================================
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Repairs the document
|
|
690
|
+
*/
|
|
691
|
+
async repairDocument(): Promise<RepairResult> {
|
|
692
|
+
try {
|
|
693
|
+
const result = await this.document?.repairDocument?.();
|
|
694
|
+
|
|
695
|
+
const repairResult: RepairResult = {
|
|
696
|
+
success: result?.success ?? true,
|
|
697
|
+
issuesFound: result?.issuesFound ?? 0,
|
|
698
|
+
issuesFixed: result?.issuesFixed ?? 0,
|
|
699
|
+
issues: result?.issues ?? [],
|
|
700
|
+
error: result?.error,
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
this.emit('document-repaired', repairResult);
|
|
704
|
+
return repairResult;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
this.emit('error', error);
|
|
707
|
+
return {
|
|
708
|
+
success: false,
|
|
709
|
+
issuesFound: 0,
|
|
710
|
+
issuesFixed: 0,
|
|
711
|
+
issues: [error instanceof Error ? error.message : 'Unknown error'],
|
|
712
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Removes all metadata
|
|
719
|
+
*/
|
|
720
|
+
async removeAllMetadata(): Promise<boolean> {
|
|
721
|
+
try {
|
|
722
|
+
const result = await this.document?.removeAllMetadata?.();
|
|
723
|
+
this.emit('metadata-removed');
|
|
724
|
+
return !!result;
|
|
725
|
+
} catch (error) {
|
|
726
|
+
this.emit('error', error);
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Removes all JavaScript
|
|
733
|
+
*/
|
|
734
|
+
async removeAllJavaScript(): Promise<boolean> {
|
|
735
|
+
try {
|
|
736
|
+
const result = await this.document?.removeAllJavaScript?.();
|
|
737
|
+
this.emit('javascript-removed');
|
|
738
|
+
return !!result;
|
|
739
|
+
} catch (error) {
|
|
740
|
+
this.emit('error', error);
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Removes all annotations
|
|
747
|
+
*/
|
|
748
|
+
async removeAllAnnotations(): Promise<number> {
|
|
749
|
+
try {
|
|
750
|
+
const result = await this.document?.removeAllAnnotations?.();
|
|
751
|
+
const count = result ?? 0;
|
|
752
|
+
this.emit('annotations-removed', { count });
|
|
753
|
+
return count;
|
|
754
|
+
} catch (error) {
|
|
755
|
+
this.emit('error', error);
|
|
756
|
+
return 0;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Removes all embedded files
|
|
762
|
+
*/
|
|
763
|
+
async removeAllEmbeddedFiles(): Promise<number> {
|
|
764
|
+
try {
|
|
765
|
+
const result = await this.document?.removeAllEmbeddedFiles?.();
|
|
766
|
+
const count = result ?? 0;
|
|
767
|
+
this.emit('embedded-files-removed', { count });
|
|
768
|
+
return count;
|
|
769
|
+
} catch (error) {
|
|
770
|
+
this.emit('error', error);
|
|
771
|
+
return 0;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// ==========================================================================
|
|
776
|
+
// Save Functions (5 functions)
|
|
777
|
+
// ==========================================================================
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Saves the optimized document to a file
|
|
781
|
+
*/
|
|
782
|
+
async saveOptimized(filePath: string): Promise<boolean> {
|
|
783
|
+
try {
|
|
784
|
+
await fs.mkdir(dirname(filePath), { recursive: true });
|
|
785
|
+
const result = await this.document?.save?.(filePath);
|
|
786
|
+
this.emit('document-saved', { filePath });
|
|
787
|
+
return !!result;
|
|
788
|
+
} catch (error) {
|
|
789
|
+
this.emit('error', error);
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Saves the document to bytes
|
|
796
|
+
*/
|
|
797
|
+
async saveToBytes(): Promise<Buffer | null> {
|
|
798
|
+
try {
|
|
799
|
+
return await this.document?.saveToBytes?.() ?? null;
|
|
800
|
+
} catch (error) {
|
|
801
|
+
this.emit('error', error);
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Saves with incremental update
|
|
808
|
+
*/
|
|
809
|
+
async saveIncremental(filePath: string): Promise<boolean> {
|
|
810
|
+
try {
|
|
811
|
+
await fs.mkdir(dirname(filePath), { recursive: true });
|
|
812
|
+
const result = await this.document?.saveIncremental?.(filePath);
|
|
813
|
+
this.emit('document-saved-incremental', { filePath });
|
|
814
|
+
return !!result;
|
|
815
|
+
} catch (error) {
|
|
816
|
+
this.emit('error', error);
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Saves with specific PDF version
|
|
823
|
+
*/
|
|
824
|
+
async saveWithVersion(filePath: string, version: string): Promise<boolean> {
|
|
825
|
+
try {
|
|
826
|
+
await fs.mkdir(dirname(filePath), { recursive: true });
|
|
827
|
+
const result = await this.document?.saveWithVersion?.(filePath, version);
|
|
828
|
+
this.emit('document-saved', { filePath, version });
|
|
829
|
+
return !!result;
|
|
830
|
+
} catch (error) {
|
|
831
|
+
this.emit('error', error);
|
|
832
|
+
return false;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Exports pages as separate PDFs
|
|
838
|
+
*/
|
|
839
|
+
async exportPagesAsSeparateFiles(outputDir: string, prefix: string = 'page'): Promise<number> {
|
|
840
|
+
try {
|
|
841
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
842
|
+
const pageCount = await this.getPageCount();
|
|
843
|
+
let exported = 0;
|
|
844
|
+
|
|
845
|
+
for (let i = 0; i < pageCount; i++) {
|
|
846
|
+
const pageBuffer = await this.extractPages([i]);
|
|
847
|
+
if (pageBuffer) {
|
|
848
|
+
const filePath = `${outputDir}/${prefix}_${i + 1}.pdf`;
|
|
849
|
+
await fs.writeFile(filePath, pageBuffer);
|
|
850
|
+
exported++;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
this.emit('pages-exported-as-files', { count: exported, outputDir });
|
|
855
|
+
return exported;
|
|
856
|
+
} catch (error) {
|
|
857
|
+
this.emit('error', error);
|
|
858
|
+
return 0;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// ==========================================================================
|
|
863
|
+
// Merge Functions (3 functions)
|
|
864
|
+
// ==========================================================================
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* Merges another PDF into this document
|
|
868
|
+
*/
|
|
869
|
+
async mergePdf(otherPdfBuffer: Buffer, atPageIndex?: number): Promise<boolean> {
|
|
870
|
+
try {
|
|
871
|
+
const result = await this.document?.mergePdf?.(otherPdfBuffer, atPageIndex);
|
|
872
|
+
this.emit('pdf-merged');
|
|
873
|
+
return !!result;
|
|
874
|
+
} catch (error) {
|
|
875
|
+
this.emit('error', error);
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Merges multiple PDFs
|
|
882
|
+
*/
|
|
883
|
+
async mergeMultiplePdfs(pdfBuffers: readonly Buffer[]): Promise<boolean> {
|
|
884
|
+
try {
|
|
885
|
+
for (const buffer of pdfBuffers) {
|
|
886
|
+
const success = await this.mergePdf(buffer);
|
|
887
|
+
if (!success) return false;
|
|
888
|
+
}
|
|
889
|
+
return true;
|
|
890
|
+
} catch (error) {
|
|
891
|
+
this.emit('error', error);
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Appends pages from another PDF
|
|
898
|
+
*/
|
|
899
|
+
async appendPagesFromPdf(
|
|
900
|
+
otherPdfBuffer: Buffer,
|
|
901
|
+
pageIndices?: readonly number[]
|
|
902
|
+
): Promise<boolean> {
|
|
903
|
+
try {
|
|
904
|
+
const result = await this.document?.appendPagesFromPdf?.(otherPdfBuffer, pageIndices);
|
|
905
|
+
this.emit('pages-appended');
|
|
906
|
+
return !!result;
|
|
907
|
+
} catch (error) {
|
|
908
|
+
this.emit('error', error);
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Cleanup resources
|
|
915
|
+
*/
|
|
916
|
+
destroy(): void {
|
|
917
|
+
this.lastOptimizationResult = null;
|
|
918
|
+
this.removeAllListeners();
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
export default DocumentUtilityManager;
|