@ooneex/pdf 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ooneex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # @ooneex/pdf
@@ -0,0 +1,521 @@
1
+ /**
2
+ * Options for PDF to image conversion
3
+ */
4
+ interface PDFOptionsType {
5
+ /**
6
+ * Password for encrypted PDFs
7
+ */
8
+ password?: string;
9
+ /**
10
+ * Scale factor for image quality (default: 3)
11
+ * Use higher values for PDFs with high resolution images
12
+ */
13
+ scale?: number;
14
+ }
15
+ /**
16
+ * Options for converting PDF pages to images and saving to disk
17
+ */
18
+ interface PDFToImagesOptionsType {
19
+ /**
20
+ * Output directory to save the images
21
+ */
22
+ outputDir: string;
23
+ /**
24
+ * Prefix for the output file names (default: "page")
25
+ */
26
+ prefix?: string;
27
+ }
28
+ /**
29
+ * Result of converting a PDF page to an image saved to disk
30
+ */
31
+ interface PDFPageImageResultType {
32
+ /**
33
+ * Page number (1-indexed)
34
+ */
35
+ page: number;
36
+ /**
37
+ * Full path to the saved image file
38
+ */
39
+ path: string;
40
+ }
41
+ /**
42
+ * Options for splitting a PDF and saving to disk
43
+ */
44
+ interface PDFSplitOptionsType {
45
+ /**
46
+ * Output directory to save the split PDF files
47
+ */
48
+ outputDir: string;
49
+ /**
50
+ * Page ranges to extract (1-indexed)
51
+ * Each range is an array of [start, end] or a single page number
52
+ * Example: [[1, 3], [5], [7, 10]] splits into pages 1-3, page 5, and pages 7-10
53
+ * If not provided, splits into individual pages
54
+ */
55
+ ranges?: (number | [number, number])[];
56
+ /**
57
+ * Prefix for the output file names (default: "page")
58
+ */
59
+ prefix?: string;
60
+ }
61
+ /**
62
+ * Result of splitting a PDF and saving to disk
63
+ */
64
+ interface PDFSplitResultType {
65
+ /**
66
+ * Page range that was extracted (1-indexed)
67
+ */
68
+ pages: {
69
+ start: number;
70
+ end: number;
71
+ };
72
+ /**
73
+ * Full path to the saved PDF file
74
+ */
75
+ path: string;
76
+ }
77
+ /**
78
+ * Result of removing pages from a PDF
79
+ */
80
+ interface PDFRemovePagesResultType {
81
+ remainingPages: number;
82
+ }
83
+ /**
84
+ * Options for creating a new PDF document
85
+ */
86
+ interface PDFCreateOptionsType {
87
+ /**
88
+ * Title of the PDF document
89
+ */
90
+ title?: string;
91
+ /**
92
+ * Author of the PDF document
93
+ */
94
+ author?: string;
95
+ /**
96
+ * Subject of the PDF document
97
+ */
98
+ subject?: string;
99
+ /**
100
+ * Keywords for the PDF document
101
+ */
102
+ keywords?: string[];
103
+ /**
104
+ * Producer of the PDF document
105
+ */
106
+ producer?: string;
107
+ /**
108
+ * Creator of the PDF document
109
+ */
110
+ creator?: string;
111
+ }
112
+ /**
113
+ * Result of creating a new PDF document
114
+ */
115
+ interface PDFCreateResultType {
116
+ /**
117
+ * Number of pages in the document
118
+ */
119
+ pageCount: number;
120
+ }
121
+ /**
122
+ * Options for adding a page to a PDF document
123
+ */
124
+ interface PDFAddPageOptionsType {
125
+ /**
126
+ * Text content to add to the page
127
+ */
128
+ content?: string;
129
+ /**
130
+ * Font size for the content (default: 12)
131
+ */
132
+ fontSize?: number;
133
+ }
134
+ /**
135
+ * Result of adding a page to a PDF document
136
+ */
137
+ interface PDFAddPageResultType {
138
+ /**
139
+ * Total number of pages after adding
140
+ */
141
+ pageCount: number;
142
+ }
143
+ /**
144
+ * Result of getting PDF metadata
145
+ */
146
+ interface PDFMetadataResultType {
147
+ /**
148
+ * Title of the PDF document
149
+ */
150
+ title?: string | undefined;
151
+ /**
152
+ * Author of the PDF document
153
+ */
154
+ author?: string | undefined;
155
+ /**
156
+ * Subject of the PDF document
157
+ */
158
+ subject?: string | undefined;
159
+ /**
160
+ * Keywords for the PDF document
161
+ */
162
+ keywords?: string | undefined;
163
+ /**
164
+ * Producer of the PDF document
165
+ */
166
+ producer?: string | undefined;
167
+ /**
168
+ * Creator of the PDF document
169
+ */
170
+ creator?: string | undefined;
171
+ /**
172
+ * Creation date of the PDF document
173
+ */
174
+ creationDate?: Date | undefined;
175
+ /**
176
+ * Modification date of the PDF document
177
+ */
178
+ modificationDate?: Date | undefined;
179
+ /**
180
+ * Total number of pages in the document
181
+ */
182
+ pageCount: number;
183
+ }
184
+ /**
185
+ * Options for updating PDF metadata
186
+ */
187
+ interface PDFUpdateMetadataOptionsType {
188
+ /**
189
+ * Title of the PDF document
190
+ */
191
+ title?: string;
192
+ /**
193
+ * Author of the PDF document
194
+ */
195
+ author?: string;
196
+ /**
197
+ * Subject of the PDF document
198
+ */
199
+ subject?: string;
200
+ /**
201
+ * Keywords for the PDF document
202
+ */
203
+ keywords?: string[];
204
+ /**
205
+ * Producer of the PDF document
206
+ */
207
+ producer?: string;
208
+ /**
209
+ * Creator of the PDF document
210
+ */
211
+ creator?: string;
212
+ /**
213
+ * Creation date of the PDF document
214
+ */
215
+ creationDate?: Date;
216
+ /**
217
+ * Modification date of the PDF document
218
+ */
219
+ modificationDate?: Date;
220
+ }
221
+ /**
222
+ * Options for extracting images from PDF pages
223
+ */
224
+ interface PDFGetImagesOptionsType {
225
+ /**
226
+ * Output directory to save the images
227
+ */
228
+ outputDir: string;
229
+ /**
230
+ * Prefix for the output file names (default: "image")
231
+ */
232
+ prefix?: string;
233
+ /**
234
+ * Page number to extract images from (1-indexed). If not provided, extracts from all pages
235
+ */
236
+ pageNumber?: number;
237
+ }
238
+ /**
239
+ * Extracted image saved to disk
240
+ */
241
+ interface PDFExtractedImageType {
242
+ /**
243
+ * Page number the image was extracted from (1-indexed)
244
+ */
245
+ page: number;
246
+ /**
247
+ * Full path to the saved image file
248
+ */
249
+ path: string;
250
+ /**
251
+ * Width of the image in pixels
252
+ */
253
+ width: number;
254
+ /**
255
+ * Height of the image in pixels
256
+ */
257
+ height: number;
258
+ }
259
+ /**
260
+ * Result of extracting images from PDF pages
261
+ */
262
+ type PDFGetImagesResultType = PDFExtractedImageType[];
263
+ /**
264
+ * Interface for PDF class
265
+ */
266
+ interface IPDF {
267
+ /**
268
+ * Create a new PDF document and save to the source path
269
+ * @param options - Optional content and metadata options for the PDF document
270
+ * @returns Result containing the page count
271
+ */
272
+ create(options?: PDFCreateOptionsType): Promise<PDFCreateResultType>;
273
+ /**
274
+ * Add a page to an existing PDF document
275
+ * @param options - Optional content options for the page
276
+ * @returns Result containing the total page count
277
+ */
278
+ addPage(options?: PDFAddPageOptionsType): Promise<PDFAddPageResultType>;
279
+ /**
280
+ * Get metadata from the PDF document
281
+ * @returns PDF metadata including title, author, dates, and page count
282
+ */
283
+ getMetadata(): Promise<PDFMetadataResultType>;
284
+ /**
285
+ * Update metadata of an existing PDF document
286
+ * @param options - Metadata options to update
287
+ */
288
+ updateMetadata(options: PDFUpdateMetadataOptionsType): Promise<void>;
289
+ /**
290
+ * Get the total number of pages in the PDF
291
+ */
292
+ getPageCount(): Promise<number>;
293
+ /**
294
+ * Convert all pages to images and save to disk
295
+ * @param options - Options including output directory and optional prefix
296
+ * @returns Array of page image results with page numbers and file paths
297
+ */
298
+ toImages(options: PDFToImagesOptionsType): Promise<PDFPageImageResultType[]>;
299
+ /**
300
+ * Convert a specific page to an image and save to disk
301
+ * @param pageNumber - Page number (1-indexed)
302
+ * @param options - Options including output directory and optional prefix
303
+ */
304
+ getPageImage(pageNumber: number, options: PDFToImagesOptionsType): Promise<PDFPageImageResultType>;
305
+ /**
306
+ * Split the PDF into separate documents and save to disk
307
+ * @param options - Split options with output directory, page ranges, and optional prefix
308
+ * @returns Array of split PDF results with page ranges and file paths
309
+ */
310
+ split(options: PDFSplitOptionsType): Promise<PDFSplitResultType[]>;
311
+ /**
312
+ * Remove specified pages from the PDF
313
+ * @param pages - Page numbers to remove (1-indexed). Can be individual numbers or ranges [start, end]
314
+ * @returns Result with remaining page count and PDF buffer
315
+ */
316
+ removePages(pages: (number | [number, number])[]): Promise<PDFRemovePagesResultType>;
317
+ /**
318
+ * Get the text content of a specific page
319
+ * @param pageNumber - Page number (1-indexed)
320
+ * @returns Extracted text content from the page
321
+ */
322
+ getPageContent(pageNumber: number): Promise<string>;
323
+ /**
324
+ * Extract images from PDF pages and save to disk
325
+ * @param options - Options including output directory, optional prefix, and optional page number
326
+ * @returns Result containing total pages and array of extracted images with file paths
327
+ */
328
+ getImages(options: PDFGetImagesOptionsType): Promise<PDFGetImagesResultType>;
329
+ }
330
+ declare class PDF implements IPDF {
331
+ private readonly source;
332
+ private readonly options;
333
+ /**
334
+ * Create a new PDF instance
335
+ * @param source - Path to PDF file
336
+ * @param options - Options for PDF processing
337
+ */
338
+ constructor(source: string, options?: PDFOptionsType);
339
+ /**
340
+ * Create a new PDF document and save to the source path
341
+ * @param options - Optional content and metadata options for the PDF document
342
+ * @returns Result containing the page count
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * // Create a simple empty PDF
347
+ * const pdf = new PDF("/path/to/output.pdf");
348
+ * const result = await pdf.create();
349
+ *
350
+ * // Create a PDF with metadata
351
+ * const pdf = new PDF("/path/to/output.pdf");
352
+ * const result = await pdf.create({
353
+ * title: "My Document",
354
+ * author: "John Doe",
355
+ * subject: "Example PDF",
356
+ * keywords: ["example", "pdf", "document"],
357
+ * creator: "My App",
358
+ * producer: "pdf-lib",
359
+ * });
360
+ * ```
361
+ */
362
+ create(options?: PDFCreateOptionsType): Promise<PDFCreateResultType>;
363
+ /**
364
+ * Add a page to an existing PDF document
365
+ * @param options - Optional content options for the page
366
+ * @returns Result containing the total page count
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * const pdf = new PDF("/path/to/document.pdf");
371
+ *
372
+ * // Add an empty page
373
+ * await pdf.addPage();
374
+ *
375
+ * // Add a page with content
376
+ * await pdf.addPage({
377
+ * content: "Hello, World!",
378
+ * fontSize: 24,
379
+ * });
380
+ * ```
381
+ */
382
+ addPage(options?: PDFAddPageOptionsType): Promise<PDFAddPageResultType>;
383
+ /**
384
+ * Get metadata from the PDF document
385
+ * @returns PDF metadata including title, author, dates, and page count
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * const pdf = new PDF("/path/to/document.pdf");
390
+ * const metadata = await pdf.getMetadata();
391
+ *
392
+ * console.log(metadata.title);
393
+ * console.log(metadata.author);
394
+ * console.log(metadata.pageCount);
395
+ * ```
396
+ */
397
+ getMetadata(): Promise<PDFMetadataResultType>;
398
+ /**
399
+ * Update metadata of an existing PDF document
400
+ * @param options - Metadata options to update
401
+ *
402
+ * @example
403
+ * ```typescript
404
+ * const pdf = new PDF("/path/to/document.pdf");
405
+ * await pdf.updateMetadata({
406
+ * title: "Updated Title",
407
+ * author: "New Author",
408
+ * subject: "Updated Subject",
409
+ * keywords: ["updated", "keywords"],
410
+ * producer: "My App",
411
+ * creator: "pdf-lib",
412
+ * creationDate: new Date("2020-01-01"),
413
+ * modificationDate: new Date(),
414
+ * });
415
+ * ```
416
+ */
417
+ updateMetadata(options: PDFUpdateMetadataOptionsType): Promise<void>;
418
+ /**
419
+ * Get the total number of pages in the PDF
420
+ */
421
+ getPageCount(): Promise<number>;
422
+ /**
423
+ * Get the text content of a specific page
424
+ * @param pageNumber - Page number (1-indexed)
425
+ * @returns Extracted text content from the page
426
+ *
427
+ * @example
428
+ * ```typescript
429
+ * const pdf = new PDF("/path/to/document.pdf");
430
+ *
431
+ * // Get content from page 1
432
+ * const content = await pdf.getPageContent(1);
433
+ * console.log(content);
434
+ *
435
+ * // Get content from a specific page
436
+ * const page3Content = await pdf.getPageContent(3);
437
+ * console.log(page3Content); // Text content of page 3
438
+ * ```
439
+ */
440
+ getPageContent(pageNumber: number): Promise<string>;
441
+ /**
442
+ * Extract images from PDF pages and save to disk
443
+ * @param options - Options including output directory, optional prefix, and optional page number
444
+ * @returns Result containing total pages and array of extracted images with file paths
445
+ *
446
+ * @example
447
+ * ```typescript
448
+ * const pdf = new PDF("/path/to/document.pdf");
449
+ *
450
+ * // Extract images from all pages
451
+ * const images = await pdf.getImages({ outputDir: "/output" });
452
+ * console.log(`Found ${images.length} images`);
453
+ *
454
+ * // Extract images from a specific page
455
+ * const page1Images = await pdf.getImages({ outputDir: "/output", prefix: "doc", pageNumber: 1 });
456
+ * for (const image of page1Images) {
457
+ * console.log(`Image: ${image.path}, ${image.width}x${image.height}`);
458
+ * }
459
+ * ```
460
+ */
461
+ getImages(options: PDFGetImagesOptionsType): Promise<PDFGetImagesResultType>;
462
+ /**
463
+ * Convert all pages to images and save to disk
464
+ * @param options - Options including output directory and optional prefix
465
+ * @returns Array of page image results with page numbers and file paths
466
+ */
467
+ toImages(options: PDFToImagesOptionsType): Promise<PDFPageImageResultType[]>;
468
+ /**
469
+ * Convert a specific page to an image and save to disk
470
+ * @param pageNumber - Page number (1-indexed)
471
+ * @param options - Options including output directory and optional prefix
472
+ * @returns Page image result with page number and file path
473
+ */
474
+ getPageImage(pageNumber: number, options: PDFToImagesOptionsType): Promise<PDFPageImageResultType>;
475
+ /**
476
+ * Split the PDF into separate documents and save to disk
477
+ * @param options - Split options with output directory, page ranges, and optional prefix
478
+ * @returns Array of split PDF results with page ranges and file paths
479
+ */
480
+ split(options: PDFSplitOptionsType): Promise<PDFSplitResultType[]>;
481
+ /**
482
+ * Remove specified pages from the PDF
483
+ * @param pages - Page numbers to remove (1-indexed). Can be individual numbers or ranges [start, end]
484
+ * @returns Result with remaining page count and PDF buffer
485
+ *
486
+ * @example
487
+ * ```typescript
488
+ * const pdf = new PDF("/path/to/document.pdf");
489
+ *
490
+ * // Remove individual pages (pages 2 and 5)
491
+ * const result1 = await pdf.removePages([2, 5]);
492
+ *
493
+ * // Remove a range of pages (pages 3 to 6)
494
+ * const result2 = await pdf.removePages([[3, 6]]);
495
+ *
496
+ * // Remove mixed: individual pages and ranges (pages 1, 4-6, and 10)
497
+ * const result3 = await pdf.removePages([1, [4, 6], 10]);
498
+ *
499
+ * console.log(result3.remainingPages); // Number of pages left
500
+ * console.log(result3.buffer); // Buffer containing the resulting PDF
501
+ *
502
+ * // Save to file
503
+ * await Bun.write("/path/to/output.pdf", result3.buffer);
504
+ * ```
505
+ */
506
+ removePages(pages: (number | [number, number])[]): Promise<PDFRemovePagesResultType>;
507
+ /**
508
+ * Normalize page numbers into a flat array of unique valid page numbers
509
+ */
510
+ private normalizePageNumbers;
511
+ /**
512
+ * Normalize page ranges for splitting
513
+ * If no ranges provided, creates individual page ranges
514
+ */
515
+ private normalizeRanges;
516
+ }
517
+ import { Exception } from "@ooneex/exception";
518
+ declare class PDFException extends Exception {
519
+ constructor(message: string, data?: Record<string, unknown>);
520
+ }
521
+ export { PDFUpdateMetadataOptionsType, PDFToImagesOptionsType, PDFSplitResultType, PDFSplitOptionsType, PDFRemovePagesResultType, PDFPageImageResultType, PDFOptionsType, PDFMetadataResultType, PDFGetImagesResultType, PDFGetImagesOptionsType, PDFExtractedImageType, PDFException, PDFCreateResultType, PDFCreateOptionsType, PDFAddPageResultType, PDFAddPageOptionsType, PDF, IPDF };
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // @bun
2
+ import V from"path";import{PDFDocument as X,rgb as z,StandardFonts as B}from"pdf-lib";import{pdf as $}from"pdf-to-img";import y from"sharp";import{extractImages as h,extractText as E,getDocumentProxy as S}from"unpdf";import{Exception as v}from"@ooneex/exception";import{HttpStatus as O}from"@ooneex/http-status";class G extends v{constructor(C,j={}){super(C,{status:O.Code.InternalServerError,data:j});this.name="PDFException"}}class I{source;options;constructor(C,j={}){this.source=V.join(...C.split(/[/\\]/)),this.options={scale:j.scale??3,...j.password!==void 0&&{password:j.password}}}async create(C={}){try{let j=await X.create();if(C.title)j.setTitle(C.title);if(C.author)j.setAuthor(C.author);if(C.subject)j.setSubject(C.subject);if(C.keywords)j.setKeywords(C.keywords);if(C.producer)j.setProducer(C.producer);if(C.creator)j.setCreator(C.creator);let k=await j.save();return await Bun.write(this.source,k),{pageCount:j.getPageCount()}}catch(j){if(j instanceof G)throw j;throw new G("Failed to create PDF document",{source:this.source,error:j instanceof Error?j.message:String(j)})}}async addPage(C={}){try{let j=await Bun.file(this.source).arrayBuffer(),k=await X.load(j,{ignoreEncryption:this.options.password!==void 0}),q=k.addPage();if(C.content){let H=await k.embedFont(B.Helvetica),J=C.fontSize??12,L=50,Q=J*1.2,{height:M}=q.getSize(),K=M-50,W=C.content.split(`
3
+ `);for(let U of W){if(K<50)break;q.drawText(U,{x:50,y:K,size:J,font:H,color:z(0,0,0)}),K-=Q}}let A=await k.save();return await Bun.write(this.source,A),{pageCount:k.getPageCount()}}catch(j){if(j instanceof G)throw j;throw new G("Failed to add page to PDF",{source:this.source,error:j instanceof Error?j.message:String(j)})}}async getMetadata(){try{let C=await Bun.file(this.source).arrayBuffer(),j=await X.load(C,{ignoreEncryption:this.options.password!==void 0,updateMetadata:!1});return{title:j.getTitle(),author:j.getAuthor(),subject:j.getSubject(),keywords:j.getKeywords(),producer:j.getProducer(),creator:j.getCreator(),creationDate:j.getCreationDate(),modificationDate:j.getModificationDate(),pageCount:j.getPageCount()}}catch(C){if(C instanceof G)throw C;throw new G("Failed to get PDF metadata",{source:this.source,error:C instanceof Error?C.message:String(C)})}}async updateMetadata(C){try{let j=await Bun.file(this.source).arrayBuffer(),k=await X.load(j,{ignoreEncryption:this.options.password!==void 0});if(C.title!==void 0)k.setTitle(C.title);if(C.author!==void 0)k.setAuthor(C.author);if(C.subject!==void 0)k.setSubject(C.subject);if(C.keywords!==void 0)k.setKeywords(C.keywords);if(C.producer!==void 0)k.setProducer(C.producer);if(C.creator!==void 0)k.setCreator(C.creator);if(C.creationDate!==void 0)k.setCreationDate(C.creationDate);if(C.modificationDate!==void 0)k.setModificationDate(C.modificationDate);let q=await k.save();await Bun.write(this.source,q)}catch(j){if(j instanceof G)throw j;throw new G("Failed to update PDF metadata",{source:this.source,error:j instanceof Error?j.message:String(j)})}}async getPageCount(){try{return(await $(this.source,this.options)).length}catch(C){throw new G("Failed to get page count",{source:this.source,error:C instanceof Error?C.message:String(C)})}}async getPageContent(C){if(C<1||!Number.isInteger(C))throw new G("Page number must be a positive integer",{pageNumber:C});try{let j=await Bun.file(this.source).arrayBuffer(),k=await S(new Uint8Array(j)),q=k.numPages;if(C>q)throw new G("Page number exceeds total pages",{pageNumber:C,totalPages:q});let{text:A}=await E(k,{mergePages:!1});return A[C-1]??""}catch(j){if(j instanceof G)throw j;throw new G("Failed to get page content",{source:this.source,pageNumber:C,error:j instanceof Error?j.message:String(j)})}}async getImages(C){let{pageNumber:j}=C;if(j!==void 0&&(j<1||!Number.isInteger(j)))throw new G("Page number must be a positive integer",{pageNumber:j});let k=V.join(...C.outputDir.split(/[/\\]/)),q=C.prefix??"image";try{let A=await Bun.file(this.source).arrayBuffer(),H=await S(new Uint8Array(A)),J=H.numPages;if(j!==void 0&&j>J)throw new G("Page number exceeds total pages",{pageNumber:j,totalPages:J});let L=[],Q=0,M=async(K)=>{let W=await h(H,K);for(let U of W){Q++;let Z=`${q}-${K}-${Q}.png`,Y=V.join(k,Z),_=await y(U.data,{raw:{width:U.width,height:U.height,channels:U.channels}}).png().toBuffer();await Bun.write(Y,_),L.push({page:K,path:Y,width:U.width,height:U.height})}};if(j!==void 0)await M(j);else for(let K=1;K<=J;K++)await M(K);return L}catch(A){if(A instanceof G)throw A;throw new G("Failed to extract images from PDF",{source:this.source,outputDir:k,pageNumber:j,error:A instanceof Error?A.message:String(A)})}}async toImages(C){let j=V.join(...C.outputDir.split(/[/\\]/)),k=C.prefix??"page";try{let q=await $(this.source,this.options),A=[],H=1;for await(let J of q){let L=`${k}-${H}.png`,Q=V.join(j,L);await Bun.write(Q,Buffer.from(J)),A.push({page:H,path:Q}),H++}return A}catch(q){throw new G("Failed to convert PDF to images",{source:this.source,outputDir:j,error:q instanceof Error?q.message:String(q)})}}async getPageImage(C,j){if(C<1||!Number.isInteger(C))throw new G("Page number must be a positive integer",{pageNumber:C});let k=V.join(...j.outputDir.split(/[/\\]/)),q=j.prefix??"page";try{let A=await $(this.source,this.options);if(C>A.length)throw new G("Page number exceeds total pages",{pageNumber:C,totalPages:A.length});let H=await A.getPage(C),J=`${q}-${C}.png`,L=V.join(k,J);return await Bun.write(L,Buffer.from(H)),{page:C,path:L}}catch(A){if(A instanceof G)throw A;throw new G("Failed to get page image",{source:this.source,pageNumber:C,outputDir:k,error:A instanceof Error?A.message:String(A)})}}async split(C){let j=V.join(...C.outputDir.split(/[/\\]/)),k=C.prefix??"page";try{let q=await Bun.file(this.source).arrayBuffer(),A=await X.load(q,{ignoreEncryption:this.options.password!==void 0}),H=A.getPageCount();if(H===0)throw new G("PDF has no pages",{source:this.source});let J=this.normalizeRanges(C.ranges,H),L=[];for(let Q of J){let{start:M,end:K}=Q;if(M<1||K>H||M>K)throw new G("Invalid page range",{start:M,end:K,totalPages:H});let W=await X.create(),U=Array.from({length:K-M+1},(R,T)=>M-1+T),Z=await W.copyPages(A,U);for(let R of Z)W.addPage(R);let Y=await W.save(),_=M===K?`${k}-${M}.pdf`:`${k}-${M}-${K}.pdf`,w=V.join(j,_);await Bun.write(w,Y),L.push({pages:{start:M,end:K},path:w})}return L}catch(q){if(q instanceof G)throw q;throw new G("Failed to split PDF",{source:this.source,outputDir:j,error:q instanceof Error?q.message:String(q)})}}async removePages(C){try{let j=await Bun.file(this.source).arrayBuffer(),k=await X.load(j,{ignoreEncryption:this.options.password!==void 0}),q=k.getPageCount();if(q===0)throw new G("PDF has no pages",{source:this.source});let A=this.normalizePageNumbers(C,q);if(A.length===0)throw new G("No valid pages specified for removal",{pages:C});if(A.length>=q)throw new G("Cannot remove all pages from PDF",{pagesToRemove:A,totalPages:q});let H=[...A].sort((L,Q)=>Q-L);for(let L of H)k.removePage(L-1);let J=await k.save();return await Bun.write(this.source,J),{remainingPages:k.getPageCount()}}catch(j){if(j instanceof G)throw j;throw new G("Failed to remove pages from PDF",{source:this.source,pages:C,error:j instanceof Error?j.message:String(j)})}}normalizePageNumbers(C,j){let k=new Set;for(let q of C)if(typeof q==="number"){if(q>=1&&q<=j&&Number.isInteger(q))k.add(q)}else{let[A,H]=q;if(A<=H){for(let J=Math.max(1,A);J<=Math.min(j,H);J++)if(Number.isInteger(J))k.add(J)}}return Array.from(k)}normalizeRanges(C,j){if(!C||C.length===0)return Array.from({length:j},(k,q)=>({start:q+1,end:q+1}));return C.map((k)=>{if(typeof k==="number")return{start:k,end:k};return{start:k[0],end:k[1]}})}}export{G as PDFException,I as PDF};
4
+
5
+ //# debugId=CF588A6CD2614F8864756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/PDF.ts", "src/PDFException.ts"],
4
+ "sourcesContent": [
5
+ "import path from \"node:path\";\nimport { PDFDocument, rgb, StandardFonts } from \"pdf-lib\";\nimport { pdf } from \"pdf-to-img\";\nimport sharp from \"sharp\";\nimport { extractImages, extractText, getDocumentProxy } from \"unpdf\";\nimport { PDFException } from \"./PDFException\";\nimport type {\n IPDF,\n PDFAddPageOptionsType,\n PDFAddPageResultType,\n PDFCreateOptionsType,\n PDFCreateResultType,\n PDFExtractedImageType,\n PDFGetImagesOptionsType,\n PDFGetImagesResultType,\n PDFMetadataResultType,\n PDFOptionsType,\n PDFPageImageResultType,\n PDFRemovePagesResultType,\n PDFSplitOptionsType,\n PDFSplitResultType,\n PDFToImagesOptionsType,\n PDFUpdateMetadataOptionsType,\n} from \"./types\";\n\nexport class PDF implements IPDF {\n private readonly source: string;\n private readonly options: PDFOptionsType;\n\n /**\n * Create a new PDF instance\n * @param source - Path to PDF file\n * @param options - Options for PDF processing\n */\n constructor(source: string, options: PDFOptionsType = {}) {\n this.source = path.join(...source.split(/[/\\\\]/));\n this.options = {\n scale: options.scale ?? 3,\n ...(options.password !== undefined && { password: options.password }),\n };\n }\n\n /**\n * Create a new PDF document and save to the source path\n * @param options - Optional content and metadata options for the PDF document\n * @returns Result containing the page count\n *\n * @example\n * ```typescript\n * // Create a simple empty PDF\n * const pdf = new PDF(\"/path/to/output.pdf\");\n * const result = await pdf.create();\n *\n * // Create a PDF with metadata\n * const pdf = new PDF(\"/path/to/output.pdf\");\n * const result = await pdf.create({\n * title: \"My Document\",\n * author: \"John Doe\",\n * subject: \"Example PDF\",\n * keywords: [\"example\", \"pdf\", \"document\"],\n * creator: \"My App\",\n * producer: \"pdf-lib\",\n * });\n * ```\n */\n public async create(options: PDFCreateOptionsType = {}): Promise<PDFCreateResultType> {\n try {\n const pdfDoc = await PDFDocument.create();\n\n // Set metadata if provided\n if (options.title) {\n pdfDoc.setTitle(options.title);\n }\n if (options.author) {\n pdfDoc.setAuthor(options.author);\n }\n if (options.subject) {\n pdfDoc.setSubject(options.subject);\n }\n if (options.keywords) {\n pdfDoc.setKeywords(options.keywords);\n }\n if (options.producer) {\n pdfDoc.setProducer(options.producer);\n }\n if (options.creator) {\n pdfDoc.setCreator(options.creator);\n }\n\n const pdfBytes = await pdfDoc.save();\n\n await Bun.write(this.source, pdfBytes);\n\n return {\n pageCount: pdfDoc.getPageCount(),\n };\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to create PDF document\", {\n source: this.source,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Add a page to an existing PDF document\n * @param options - Optional content options for the page\n * @returns Result containing the total page count\n *\n * @example\n * ```typescript\n * const pdf = new PDF(\"/path/to/document.pdf\");\n *\n * // Add an empty page\n * await pdf.addPage();\n *\n * // Add a page with content\n * await pdf.addPage({\n * content: \"Hello, World!\",\n * fontSize: 24,\n * });\n * ```\n */\n public async addPage(options: PDFAddPageOptionsType = {}): Promise<PDFAddPageResultType> {\n try {\n const sourceBytes = await Bun.file(this.source).arrayBuffer();\n\n const pdfDoc = await PDFDocument.load(sourceBytes, {\n ignoreEncryption: this.options.password !== undefined,\n });\n\n const page = pdfDoc.addPage();\n\n // Add content if provided\n if (options.content) {\n const font = await pdfDoc.embedFont(StandardFonts.Helvetica);\n const fontSize = options.fontSize ?? 12;\n const margin = 50;\n const lineHeight = fontSize * 1.2;\n\n const { height } = page.getSize();\n let y = height - margin;\n\n const lines = options.content.split(\"\\n\");\n\n for (const line of lines) {\n if (y < margin) {\n break;\n }\n\n page.drawText(line, {\n x: margin,\n y,\n size: fontSize,\n font,\n color: rgb(0, 0, 0),\n });\n\n y -= lineHeight;\n }\n }\n\n const pdfBytes = await pdfDoc.save();\n\n await Bun.write(this.source, pdfBytes);\n\n return {\n pageCount: pdfDoc.getPageCount(),\n };\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to add page to PDF\", {\n source: this.source,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Get metadata from the PDF document\n * @returns PDF metadata including title, author, dates, and page count\n *\n * @example\n * ```typescript\n * const pdf = new PDF(\"/path/to/document.pdf\");\n * const metadata = await pdf.getMetadata();\n *\n * console.log(metadata.title);\n * console.log(metadata.author);\n * console.log(metadata.pageCount);\n * ```\n */\n public async getMetadata(): Promise<PDFMetadataResultType> {\n try {\n const sourceBytes = await Bun.file(this.source).arrayBuffer();\n\n const pdfDoc = await PDFDocument.load(sourceBytes, {\n ignoreEncryption: this.options.password !== undefined,\n updateMetadata: false,\n });\n\n return {\n title: pdfDoc.getTitle(),\n author: pdfDoc.getAuthor(),\n subject: pdfDoc.getSubject(),\n keywords: pdfDoc.getKeywords(),\n producer: pdfDoc.getProducer(),\n creator: pdfDoc.getCreator(),\n creationDate: pdfDoc.getCreationDate(),\n modificationDate: pdfDoc.getModificationDate(),\n pageCount: pdfDoc.getPageCount(),\n };\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to get PDF metadata\", {\n source: this.source,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Update metadata of an existing PDF document\n * @param options - Metadata options to update\n *\n * @example\n * ```typescript\n * const pdf = new PDF(\"/path/to/document.pdf\");\n * await pdf.updateMetadata({\n * title: \"Updated Title\",\n * author: \"New Author\",\n * subject: \"Updated Subject\",\n * keywords: [\"updated\", \"keywords\"],\n * producer: \"My App\",\n * creator: \"pdf-lib\",\n * creationDate: new Date(\"2020-01-01\"),\n * modificationDate: new Date(),\n * });\n * ```\n */\n public async updateMetadata(options: PDFUpdateMetadataOptionsType): Promise<void> {\n try {\n const sourceBytes = await Bun.file(this.source).arrayBuffer();\n\n const pdfDoc = await PDFDocument.load(sourceBytes, {\n ignoreEncryption: this.options.password !== undefined,\n });\n\n if (options.title !== undefined) {\n pdfDoc.setTitle(options.title);\n }\n if (options.author !== undefined) {\n pdfDoc.setAuthor(options.author);\n }\n if (options.subject !== undefined) {\n pdfDoc.setSubject(options.subject);\n }\n if (options.keywords !== undefined) {\n pdfDoc.setKeywords(options.keywords);\n }\n if (options.producer !== undefined) {\n pdfDoc.setProducer(options.producer);\n }\n if (options.creator !== undefined) {\n pdfDoc.setCreator(options.creator);\n }\n if (options.creationDate !== undefined) {\n pdfDoc.setCreationDate(options.creationDate);\n }\n if (options.modificationDate !== undefined) {\n pdfDoc.setModificationDate(options.modificationDate);\n }\n\n const pdfBytes = await pdfDoc.save();\n\n await Bun.write(this.source, pdfBytes);\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to update PDF metadata\", {\n source: this.source,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Get the total number of pages in the PDF\n */\n public async getPageCount(): Promise<number> {\n try {\n const document = await pdf(this.source, this.options);\n return document.length;\n } catch (error) {\n throw new PDFException(\"Failed to get page count\", {\n source: this.source,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Get the text content of a specific page\n * @param pageNumber - Page number (1-indexed)\n * @returns Extracted text content from the page\n *\n * @example\n * ```typescript\n * const pdf = new PDF(\"/path/to/document.pdf\");\n *\n * // Get content from page 1\n * const content = await pdf.getPageContent(1);\n * console.log(content);\n *\n * // Get content from a specific page\n * const page3Content = await pdf.getPageContent(3);\n * console.log(page3Content); // Text content of page 3\n * ```\n */\n public async getPageContent(pageNumber: number): Promise<string> {\n if (pageNumber < 1 || !Number.isInteger(pageNumber)) {\n throw new PDFException(\"Page number must be a positive integer\", {\n pageNumber,\n });\n }\n\n try {\n const sourceBytes = await Bun.file(this.source).arrayBuffer();\n const document = await getDocumentProxy(new Uint8Array(sourceBytes));\n\n const totalPages = document.numPages;\n\n if (pageNumber > totalPages) {\n throw new PDFException(\"Page number exceeds total pages\", {\n pageNumber,\n totalPages,\n });\n }\n\n const { text } = await extractText(document, { mergePages: false });\n\n return text[pageNumber - 1] ?? \"\";\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to get page content\", {\n source: this.source,\n pageNumber,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Extract images from PDF pages and save to disk\n * @param options - Options including output directory, optional prefix, and optional page number\n * @returns Result containing total pages and array of extracted images with file paths\n *\n * @example\n * ```typescript\n * const pdf = new PDF(\"/path/to/document.pdf\");\n *\n * // Extract images from all pages\n * const images = await pdf.getImages({ outputDir: \"/output\" });\n * console.log(`Found ${images.length} images`);\n *\n * // Extract images from a specific page\n * const page1Images = await pdf.getImages({ outputDir: \"/output\", prefix: \"doc\", pageNumber: 1 });\n * for (const image of page1Images) {\n * console.log(`Image: ${image.path}, ${image.width}x${image.height}`);\n * }\n * ```\n */\n public async getImages(options: PDFGetImagesOptionsType): Promise<PDFGetImagesResultType> {\n const { pageNumber } = options;\n\n if (pageNumber !== undefined && (pageNumber < 1 || !Number.isInteger(pageNumber))) {\n throw new PDFException(\"Page number must be a positive integer\", {\n pageNumber,\n });\n }\n\n const normalizedOutputDir = path.join(...options.outputDir.split(/[/\\\\]/));\n const prefix = options.prefix ?? \"image\";\n\n try {\n const sourceBytes = await Bun.file(this.source).arrayBuffer();\n const document = await getDocumentProxy(new Uint8Array(sourceBytes));\n const totalPages = document.numPages;\n\n if (pageNumber !== undefined && pageNumber > totalPages) {\n throw new PDFException(\"Page number exceeds total pages\", {\n pageNumber,\n totalPages,\n });\n }\n\n const images: PDFExtractedImageType[] = [];\n let imageIndex = 0;\n\n const processPage = async (page: number) => {\n const pageImages = await extractImages(document, page);\n for (const img of pageImages) {\n imageIndex++;\n const fileName = `${prefix}-${page}-${imageIndex}.png`;\n const filePath = path.join(normalizedOutputDir, fileName);\n\n // Convert raw image data to PNG using sharp\n const pngBuffer = await sharp(img.data, {\n raw: {\n width: img.width,\n height: img.height,\n channels: img.channels,\n },\n })\n .png()\n .toBuffer();\n\n await Bun.write(filePath, pngBuffer);\n\n images.push({\n page,\n path: filePath,\n width: img.width,\n height: img.height,\n });\n }\n };\n\n if (pageNumber !== undefined) {\n await processPage(pageNumber);\n } else {\n for (let page = 1; page <= totalPages; page++) {\n await processPage(page);\n }\n }\n\n return images;\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to extract images from PDF\", {\n source: this.source,\n outputDir: normalizedOutputDir,\n pageNumber,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Convert all pages to images and save to disk\n * @param options - Options including output directory and optional prefix\n * @returns Array of page image results with page numbers and file paths\n */\n public async toImages(options: PDFToImagesOptionsType): Promise<PDFPageImageResultType[]> {\n const normalizedOutputDir = path.join(...options.outputDir.split(/[/\\\\]/));\n const prefix = options.prefix ?? \"page\";\n\n try {\n const document = await pdf(this.source, this.options);\n const results: PDFPageImageResultType[] = [];\n let pageNumber = 1;\n\n for await (const image of document) {\n const fileName = `${prefix}-${pageNumber}.png`;\n const filePath = path.join(normalizedOutputDir, fileName);\n\n await Bun.write(filePath, Buffer.from(image));\n\n results.push({\n page: pageNumber,\n path: filePath,\n });\n pageNumber++;\n }\n\n return results;\n } catch (error) {\n throw new PDFException(\"Failed to convert PDF to images\", {\n source: this.source,\n outputDir: normalizedOutputDir,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Convert a specific page to an image and save to disk\n * @param pageNumber - Page number (1-indexed)\n * @param options - Options including output directory and optional prefix\n * @returns Page image result with page number and file path\n */\n public async getPageImage(pageNumber: number, options: PDFToImagesOptionsType): Promise<PDFPageImageResultType> {\n if (pageNumber < 1 || !Number.isInteger(pageNumber)) {\n throw new PDFException(\"Page number must be a positive integer\", {\n pageNumber,\n });\n }\n\n const normalizedOutputDir = path.join(...options.outputDir.split(/[/\\\\]/));\n const prefix = options.prefix ?? \"page\";\n\n try {\n const document = await pdf(this.source, this.options);\n\n if (pageNumber > document.length) {\n throw new PDFException(\"Page number exceeds total pages\", {\n pageNumber,\n totalPages: document.length,\n });\n }\n\n const image = await document.getPage(pageNumber);\n\n const fileName = `${prefix}-${pageNumber}.png`;\n const filePath = path.join(normalizedOutputDir, fileName);\n\n await Bun.write(filePath, Buffer.from(image));\n\n return {\n page: pageNumber,\n path: filePath,\n };\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to get page image\", {\n source: this.source,\n pageNumber,\n outputDir: normalizedOutputDir,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Split the PDF into separate documents and save to disk\n * @param options - Split options with output directory, page ranges, and optional prefix\n * @returns Array of split PDF results with page ranges and file paths\n */\n public async split(options: PDFSplitOptionsType): Promise<PDFSplitResultType[]> {\n const normalizedOutputDir = path.join(...options.outputDir.split(/[/\\\\]/));\n const prefix = options.prefix ?? \"page\";\n\n try {\n const sourceBytes = await Bun.file(this.source).arrayBuffer();\n\n const sourcePdf = await PDFDocument.load(sourceBytes, {\n ignoreEncryption: this.options.password !== undefined,\n });\n\n const totalPages = sourcePdf.getPageCount();\n\n if (totalPages === 0) {\n throw new PDFException(\"PDF has no pages\", {\n source: this.source,\n });\n }\n\n const ranges = this.normalizeRanges(options.ranges, totalPages);\n const results: PDFSplitResultType[] = [];\n\n for (const range of ranges) {\n const { start, end } = range;\n\n if (start < 1 || end > totalPages || start > end) {\n throw new PDFException(\"Invalid page range\", {\n start,\n end,\n totalPages,\n });\n }\n\n const newPdf = await PDFDocument.create();\n const pageIndices = Array.from({ length: end - start + 1 }, (_, i) => start - 1 + i);\n const copiedPages = await newPdf.copyPages(sourcePdf, pageIndices);\n\n for (const page of copiedPages) {\n newPdf.addPage(page);\n }\n\n const pdfBytes = await newPdf.save();\n\n const fileName = start === end ? `${prefix}-${start}.pdf` : `${prefix}-${start}-${end}.pdf`;\n const filePath = path.join(normalizedOutputDir, fileName);\n\n await Bun.write(filePath, pdfBytes);\n\n results.push({\n pages: { start, end },\n path: filePath,\n });\n }\n\n return results;\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to split PDF\", {\n source: this.source,\n outputDir: normalizedOutputDir,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Remove specified pages from the PDF\n * @param pages - Page numbers to remove (1-indexed). Can be individual numbers or ranges [start, end]\n * @returns Result with remaining page count and PDF buffer\n *\n * @example\n * ```typescript\n * const pdf = new PDF(\"/path/to/document.pdf\");\n *\n * // Remove individual pages (pages 2 and 5)\n * const result1 = await pdf.removePages([2, 5]);\n *\n * // Remove a range of pages (pages 3 to 6)\n * const result2 = await pdf.removePages([[3, 6]]);\n *\n * // Remove mixed: individual pages and ranges (pages 1, 4-6, and 10)\n * const result3 = await pdf.removePages([1, [4, 6], 10]);\n *\n * console.log(result3.remainingPages); // Number of pages left\n * console.log(result3.buffer); // Buffer containing the resulting PDF\n *\n * // Save to file\n * await Bun.write(\"/path/to/output.pdf\", result3.buffer);\n * ```\n */\n public async removePages(pages: (number | [number, number])[]): Promise<PDFRemovePagesResultType> {\n try {\n const sourceBytes = await Bun.file(this.source).arrayBuffer();\n\n const pdfDoc = await PDFDocument.load(sourceBytes, {\n ignoreEncryption: this.options.password !== undefined,\n });\n\n const totalPages = pdfDoc.getPageCount();\n\n if (totalPages === 0) {\n throw new PDFException(\"PDF has no pages\", {\n source: this.source,\n });\n }\n\n // Normalize page numbers to remove into a flat sorted array\n const pagesToRemove = this.normalizePageNumbers(pages, totalPages);\n\n if (pagesToRemove.length === 0) {\n throw new PDFException(\"No valid pages specified for removal\", {\n pages,\n });\n }\n\n if (pagesToRemove.length >= totalPages) {\n throw new PDFException(\"Cannot remove all pages from PDF\", {\n pagesToRemove,\n totalPages,\n });\n }\n\n // Remove pages in reverse order to maintain correct indices\n const sortedDescending = [...pagesToRemove].sort((a, b) => b - a);\n for (const pageNum of sortedDescending) {\n pdfDoc.removePage(pageNum - 1); // Convert to 0-indexed\n }\n\n const pdfBytes = await pdfDoc.save();\n\n await Bun.write(this.source, pdfBytes);\n\n return {\n remainingPages: pdfDoc.getPageCount(),\n };\n } catch (error) {\n if (error instanceof PDFException) {\n throw error;\n }\n throw new PDFException(\"Failed to remove pages from PDF\", {\n source: this.source,\n pages,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Normalize page numbers into a flat array of unique valid page numbers\n */\n private normalizePageNumbers(pages: (number | [number, number])[], totalPages: number): number[] {\n const pageSet = new Set<number>();\n\n for (const page of pages) {\n if (typeof page === \"number\") {\n if (page >= 1 && page <= totalPages && Number.isInteger(page)) {\n pageSet.add(page);\n }\n } else {\n const [start, end] = page;\n if (start <= end) {\n for (let i = Math.max(1, start); i <= Math.min(totalPages, end); i++) {\n if (Number.isInteger(i)) {\n pageSet.add(i);\n }\n }\n }\n }\n }\n\n return Array.from(pageSet);\n }\n\n /**\n * Normalize page ranges for splitting\n * If no ranges provided, creates individual page ranges\n */\n private normalizeRanges(\n ranges: PDFSplitOptionsType[\"ranges\"] | undefined,\n totalPages: number,\n ): { start: number; end: number }[] {\n if (!ranges || ranges.length === 0) {\n return Array.from({ length: totalPages }, (_, i) => ({\n start: i + 1,\n end: i + 1,\n }));\n }\n\n return ranges.map((range) => {\n if (typeof range === \"number\") {\n return { start: range, end: range };\n }\n return { start: range[0], end: range[1] };\n });\n }\n}\n",
6
+ "import { Exception } from \"@ooneex/exception\";\nimport { HttpStatus } from \"@ooneex/http-status\";\n\nexport class PDFException extends Exception {\n constructor(message: string, data: Record<string, unknown> = {}) {\n super(message, {\n status: HttpStatus.Code.InternalServerError,\n data,\n });\n this.name = \"PDFException\";\n }\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "CF588A6CD2614F8864756E2164756E21",
10
+ "names": []
11
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@ooneex/pdf",
3
+ "description": "PDF generation, manipulation, and image conversion utilities with support for creating, editing, and extracting content from PDF documents",
4
+ "version": "0.0.4",
5
+ "type": "module",
6
+ "files": [
7
+ "dist",
8
+ "LICENSE",
9
+ "README.md",
10
+ "package.json"
11
+ ],
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "license": "MIT",
24
+ "scripts": {
25
+ "test": "bun test tests",
26
+ "build": "bunup",
27
+ "lint": "tsgo --noEmit && bunx biome lint",
28
+ "publish": "bun publish --access public || true"
29
+ },
30
+ "dependencies": {
31
+ "@ooneex/exception": "0.0.1",
32
+ "@ooneex/http-status": "0.0.1",
33
+ "pdf-lib": "^1.17.1",
34
+ "pdf-to-img": "^5.0.0",
35
+ "sharp": "^0.33.5",
36
+ "unpdf": "^0.12.1"
37
+ },
38
+ "keywords": [
39
+ "bun",
40
+ "document",
41
+ "generator",
42
+ "ooneex",
43
+ "pdf",
44
+ "pdf-lib",
45
+ "typescript"
46
+ ]
47
+ }