react-native-pdf-jsi 2.2.8 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,13 @@ public class RNPDFPackage implements ReactPackage {
26
26
  // Add JSI modules for enhanced PDF performance
27
27
  modules.add(new PDFJSIManager(reactContext));
28
28
  modules.add(new EnhancedPdfJSIBridge(reactContext));
29
+
30
+ // Add advanced feature modules
31
+ modules.add(new PDFExporter(reactContext));
32
+ modules.add(new FileDownloader(reactContext));
33
+ modules.add(new FileManager(reactContext));
34
+ modules.add(new LicenseVerifier(reactContext));
35
+
29
36
  return modules;
30
37
  }
31
38
 
package/index.js CHANGED
@@ -601,3 +601,61 @@ const styles = StyleSheet.create({
601
601
  height: 2
602
602
  }
603
603
  });
604
+
605
+ // ========================================
606
+ // TIER 2: Low-Level API (Managers)
607
+ // ========================================
608
+
609
+ import ExportManager from './src/managers/ExportManager';
610
+ import BookmarkManager from './src/managers/BookmarkManager';
611
+ import AnalyticsManager from './src/managers/AnalyticsManager';
612
+ import FileManager from './src/managers/FileManager';
613
+
614
+ export {
615
+ ExportManager,
616
+ BookmarkManager,
617
+ AnalyticsManager,
618
+ FileManager
619
+ };
620
+
621
+ // ========================================
622
+ // TIER 3: Pre-built UI Components
623
+ // ========================================
624
+
625
+ import Toolbar from './src/components/Toolbar';
626
+ import BookmarkModal from './src/components/BookmarkModal';
627
+ import BookmarkListModal from './src/components/BookmarkListModal';
628
+ import BookmarkIndicator from './src/components/BookmarkIndicator';
629
+ import ExportMenu from './src/components/ExportMenu';
630
+ import OperationsMenu from './src/components/OperationsMenu';
631
+ import AnalyticsPanel from './src/components/AnalyticsPanel';
632
+ import Toast from './src/components/Toast';
633
+ import LoadingOverlay from './src/components/LoadingOverlay';
634
+ import BottomSheet from './src/components/BottomSheet';
635
+ import SidePanel from './src/components/SidePanel';
636
+
637
+ export {
638
+ Toolbar,
639
+ BookmarkModal,
640
+ BookmarkListModal,
641
+ BookmarkIndicator,
642
+ ExportMenu,
643
+ OperationsMenu,
644
+ AnalyticsPanel,
645
+ Toast,
646
+ LoadingOverlay,
647
+ BottomSheet,
648
+ SidePanel
649
+ };
650
+
651
+ // ========================================
652
+ // TIER 4: Utility Modules
653
+ // ========================================
654
+
655
+ import ErrorHandler from './src/utils/ErrorHandler';
656
+ import TestData from './src/utils/TestData';
657
+
658
+ export {
659
+ ErrorHandler,
660
+ TestData
661
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * PDF Export Module (iOS)
3
+ * Exports PDF pages to images and performs PDF operations
4
+ *
5
+ * LICENSE: Commercial License (Pro feature)
6
+ *
7
+ * @author Punith M
8
+ * @version 1.0.0
9
+ */
10
+
11
+ #import <React/RCTBridgeModule.h>
12
+ #import <React/RCTEventEmitter.h>
13
+
14
+ @interface PDFExporter : NSObject <RCTBridgeModule>
15
+
16
+ @end
@@ -0,0 +1,537 @@
1
+ #import "PDFExporter.h"
2
+ #import <PDFKit/PDFKit.h>
3
+ #import <UIKit/UIKit.h>
4
+
5
+ @implementation PDFExporter {
6
+ LicenseVerifier *_licenseVerifier;
7
+ }
8
+
9
+ RCT_EXPORT_MODULE();
10
+
11
+ - (instancetype)init {
12
+ self = [super init];
13
+ if (self) {
14
+ _licenseVerifier = [[LicenseVerifier alloc] init];
15
+ }
16
+ return self;
17
+ }
18
+
19
+ //
20
+ RCT_EXPORT_METHOD(exportToImages:(NSString *)filePath
21
+ options:(NSDictionary *)options
22
+ resolver:(RCTPromiseResolveBlock)resolve
23
+ rejecter:(RCTPromiseRejectBlock)reject) {
24
+
25
+
26
+ if (![_licenseVerifier isProActive]) {
27
+ reject(@"LICENSE_REQUIRED", @"Export to Images requires a Pro license", nil);
28
+ return;
29
+ }
30
+
31
+ if (!filePath || filePath.length == 0) {
32
+ reject(@"INVALID_PATH", @"File path is required", nil);
33
+ return;
34
+ }
35
+
36
+ NSURL *pdfURL = [NSURL fileURLWithPath:filePath];
37
+ if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
38
+ reject(@"FILE_NOT_FOUND", [NSString stringWithFormat:@"PDF file not found: %@", filePath], nil);
39
+ return;
40
+ }
41
+
42
+
43
+ NSArray *pages = options[@"pages"];
44
+ NSNumber *dpi = options[@"dpi"] ?: @150;
45
+ NSString *format = options[@"format"] ?: @"png";
46
+ NSString *outputDir = options[@"outputDir"];
47
+
48
+
49
+ NSArray *exportedFiles = [self exportPagesToImages:pdfURL pages:pages dpi:dpi.intValue format:format outputDir:outputDir];
50
+
51
+ resolve(exportedFiles);
52
+ }
53
+
54
+ /**
55
+ * Export specific pages to images
56
+ */
57
+ - (NSArray *)exportPagesToImages:(NSURL *)pdfURL pages:(NSArray *)pages dpi:(int)dpi format:(NSString *)format outputDir:(NSString *)outputDir {
58
+ NSLog(@"🖼️ [EXPORT] exportPagesToImages - START - dpi: %d, format: %@", dpi, format);
59
+
60
+ NSMutableArray *exportedFiles = [NSMutableArray array];
61
+
62
+ PDFDocument *pdfDocument = [[PDFDocument alloc] initWithURL:pdfURL];
63
+ if (!pdfDocument) {
64
+ NSLog(@"❌ [EXPORT] Failed to load PDF document");
65
+ return exportedFiles;
66
+ }
67
+
68
+ NSUInteger pageCount = pdfDocument.pageCount;
69
+ NSLog(@"📁 [FILE] PDF has %lu total pages", (unsigned long)pageCount);
70
+
71
+ // Determine which pages to export
72
+ NSMutableArray *pagesToExport = [NSMutableArray array];
73
+ if (pages && pages.count > 0) {
74
+ for (NSNumber *pageNum in pages) {
75
+ int pageIndex = pageNum.intValue;
76
+ if (pageIndex >= 0 && pageIndex < pageCount) {
77
+ [pagesToExport addObject:@(pageIndex)];
78
+ }
79
+ }
80
+ NSLog(@"📊 [PROGRESS] Exporting %lu specific pages", (unsigned long)pagesToExport.count);
81
+ } else {
82
+ // Export all pages
83
+ for (int i = 0; i < pageCount; i++) {
84
+ [pagesToExport addObject:@(i)];
85
+ }
86
+ NSLog(@"📊 [PROGRESS] Exporting all %lu pages", (unsigned long)pageCount);
87
+ }
88
+
89
+ // Export each page
90
+ int current = 0;
91
+ for (NSNumber *pageNum in pagesToExport) {
92
+ int pageIndex = pageNum.intValue;
93
+ current++;
94
+
95
+ NSLog(@"📊 [PROGRESS] Exporting page %d/%lu (page number: %d)", current, (unsigned long)pagesToExport.count, pageIndex + 1);
96
+
97
+ PDFPage *page = [pdfDocument pageAtIndex:pageIndex];
98
+
99
+ if (page) {
100
+ // Calculate dimensions
101
+ CGRect pageRect = [page boundsForBox:kPDFDisplayBoxMediaBox];
102
+ float scale = dpi / 72.0f; // 72 DPI is default
103
+ CGSize imageSize = CGSizeMake(pageRect.size.width * scale, pageRect.size.height * scale);
104
+
105
+ NSLog(@"🖼️ [BITMAP] Creating %.0fx%.0f image", imageSize.width, imageSize.height);
106
+
107
+ // Create image context
108
+ UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
109
+ CGContextRef context = UIGraphicsGetCurrentContext();
110
+
111
+ // Fill background with white
112
+ CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
113
+ CGContextFillRect(context, CGRectMake(0, 0, imageSize.width, imageSize.height));
114
+
115
+ // Scale context
116
+ CGContextScaleCTM(context, scale, scale);
117
+
118
+ // Render page
119
+ NSLog(@"🖼️ [RENDER] Rendering page to image...");
120
+ [page drawWithBox:kPDFDisplayBoxMediaBox toContext:context];
121
+
122
+ // Get image
123
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
124
+ UIGraphicsEndImageContext();
125
+
126
+ // Save image
127
+ NSString *fileName = [NSString stringWithFormat:@"page_%d.%@", pageIndex + 1, format];
128
+ NSString *outputPath = [self saveImage:image fileName:fileName outputDir:outputDir];
129
+ if (outputPath) {
130
+ [exportedFiles addObject:outputPath];
131
+ NSLog(@"✅ [PROGRESS] Page %d exported to %@", pageIndex + 1, outputPath);
132
+ } else {
133
+ NSLog(@"❌ [EXPORT] Failed to save page %d", pageIndex + 1);
134
+ }
135
+ }
136
+ }
137
+
138
+ NSLog(@"✅ [EXPORT] exportPagesToImages - SUCCESS - Exported %lu pages", (unsigned long)exportedFiles.count);
139
+
140
+ return exportedFiles;
141
+ }
142
+
143
+ /**
144
+ * Save image to file
145
+ */
146
+ - (NSString *)saveImage:(UIImage *)image fileName:(NSString *)fileName outputDir:(NSString *)outputDir {
147
+ NSURL *outputURL;
148
+
149
+ if (outputDir && outputDir.length > 0) {
150
+ NSFileManager *fileManager = [NSFileManager defaultManager];
151
+ if (![fileManager fileExistsAtPath:outputDir]) {
152
+ [fileManager createDirectoryAtPath:outputDir withIntermediateDirectories:YES attributes:nil error:nil];
153
+ }
154
+ outputURL = [NSURL fileURLWithPath:[outputDir stringByAppendingPathComponent:fileName]];
155
+ } else {
156
+ // Save to app's documents directory
157
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
158
+ NSString *documentsDirectory = [paths objectAtIndex:0];
159
+ outputURL = [NSURL fileURLWithPath:[documentsDirectory stringByAppendingPathComponent:fileName]];
160
+ }
161
+
162
+ NSLog(@"📁 [FILE] Writing to: %@", outputURL.path);
163
+
164
+ // Convert image to data
165
+ NSData *imageData;
166
+ NSString *format;
167
+ if ([fileName.lowercaseString hasSuffix:@".png"]) {
168
+ imageData = UIImagePNGRepresentation(image);
169
+ format = @"PNG";
170
+ } else if ([fileName.lowercaseString hasSuffix:@".jpg"] || [fileName.lowercaseString hasSuffix:@".jpeg"]) {
171
+ imageData = UIImageJPEGRepresentation(image, 0.9);
172
+ format = @"JPEG at 90% quality";
173
+ } else {
174
+ imageData = UIImagePNGRepresentation(image);
175
+ format = @"PNG (default)";
176
+ }
177
+
178
+ NSLog(@"📁 [FILE] Compressing as %@", format);
179
+
180
+ // Save to file
181
+ NSError *error;
182
+ BOOL success = [imageData writeToURL:outputURL options:NSDataWritingAtomic error:&error];
183
+
184
+ if (success) {
185
+ unsigned long fileSize = (unsigned long)imageData.length;
186
+ NSLog(@"✅ [FILE] Saved - size: %lu bytes", fileSize);
187
+ return outputURL.path;
188
+ } else {
189
+ NSLog(@"❌ [FILE] Error saving image: %@", error.localizedDescription);
190
+ return nil;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Merge multiple PDFs
196
+ * PRO FEATURE: Requires Pro license
197
+ */
198
+ RCT_EXPORT_METHOD(mergePDFs:(NSArray *)filePaths
199
+ outputPath:(NSString *)outputPath
200
+ resolver:(RCTPromiseResolveBlock)resolve
201
+ rejecter:(RCTPromiseRejectBlock)reject) {
202
+
203
+ // Check Pro license
204
+ if (![_licenseVerifier isProActive]) {
205
+ reject(@"LICENSE_REQUIRED", @"PDF Operations requires a Pro license", nil);
206
+ return;
207
+ }
208
+
209
+ if (!filePaths || filePaths.count < 2) {
210
+ reject(@"INVALID_INPUT", @"At least 2 PDF files are required for merging", nil);
211
+ return;
212
+ }
213
+
214
+ // Create merged PDF
215
+ PDFDocument *mergedPDF = [[PDFDocument alloc] init];
216
+
217
+ for (NSString *filePath in filePaths) {
218
+ NSURL *pdfURL = [NSURL fileURLWithPath:filePath];
219
+ PDFDocument *pdfDoc = [[PDFDocument alloc] initWithURL:pdfURL];
220
+
221
+ if (pdfDoc) {
222
+ NSUInteger pageCount = pdfDoc.pageCount;
223
+ for (int i = 0; i < pageCount; i++) {
224
+ PDFPage *page = [pdfDoc pageAtIndex:i];
225
+ if (page) {
226
+ [mergedPDF insertPage:page atIndex:mergedPDF.pageCount];
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ // Save merged PDF
233
+ NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
234
+ BOOL success = [mergedPDF writeToURL:outputURL];
235
+
236
+ if (success) {
237
+ resolve(outputPath);
238
+ } else {
239
+ reject(@"MERGE_ERROR", @"Failed to save merged PDF", nil);
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Split PDF into multiple files
245
+ * PRO FEATURE: Requires Pro license
246
+ */
247
+ RCT_EXPORT_METHOD(splitPDF:(NSString *)filePath
248
+ pageRanges:(NSArray *)pageRanges
249
+ resolver:(RCTPromiseResolveBlock)resolve
250
+ rejecter:(RCTPromiseRejectBlock)reject) {
251
+
252
+ NSLog(@"✂️ [SPLIT] splitPDF - START - file: %@, ranges: %lu", filePath, (unsigned long)pageRanges.count);
253
+
254
+ // Check Pro license
255
+ BOOL licenseActive = [_licenseVerifier isProActive];
256
+ NSLog(@"🔑 [LICENSE] isProActive: %d", licenseActive);
257
+
258
+ if (!licenseActive) {
259
+ NSLog(@"❌ [SPLIT] License required");
260
+ reject(@"LICENSE_REQUIRED", @"PDF Operations requires a Pro license", nil);
261
+ return;
262
+ }
263
+
264
+ if (!filePath || filePath.length == 0) {
265
+ NSLog(@"❌ [SPLIT] Invalid path");
266
+ reject(@"INVALID_PATH", @"File path is required", nil);
267
+ return;
268
+ }
269
+
270
+ NSURL *pdfURL = [NSURL fileURLWithPath:filePath];
271
+ PDFDocument *pdfDocument = [[PDFDocument alloc] initWithURL:pdfURL];
272
+
273
+ if (!pdfDocument) {
274
+ NSLog(@"❌ [FILE] PDF not found: %@", filePath);
275
+ reject(@"FILE_NOT_FOUND", @"PDF file not found", nil);
276
+ return;
277
+ }
278
+
279
+ NSMutableArray *splitFiles = [NSMutableArray array];
280
+ NSUInteger pageCount = pdfDocument.pageCount;
281
+ NSLog(@"📁 [FILE] PDF opened, total pages: %lu", (unsigned long)pageCount);
282
+
283
+ int rangeIndex = 0;
284
+ for (NSDictionary *range in pageRanges) {
285
+ rangeIndex++;
286
+ NSNumber *startPage = range[@"start"];
287
+ NSNumber *endPage = range[@"end"];
288
+
289
+ if (startPage && endPage) {
290
+ int start = startPage.intValue;
291
+ int end = endPage.intValue;
292
+
293
+ NSLog(@"📊 [PROGRESS] Processing range %d/%lu: pages %d-%d", rangeIndex, (unsigned long)pageRanges.count, start + 1, end + 1);
294
+
295
+ if (start >= 0 && end < pageCount && start <= end) {
296
+ PDFDocument *splitPDF = [[PDFDocument alloc] init];
297
+
298
+ for (int i = start; i <= end; i++) {
299
+ NSLog(@"📊 [PROGRESS] Processing page %d", i + 1);
300
+ PDFPage *page = [pdfDocument pageAtIndex:i];
301
+ if (page) {
302
+ [splitPDF insertPage:page atIndex:splitPDF.pageCount];
303
+ }
304
+ }
305
+
306
+ // Save split PDF
307
+ NSString *fileName = [NSString stringWithFormat:@"split_%d_%d.pdf", start + 1, end + 1];
308
+ NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
309
+ NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
310
+
311
+ NSLog(@"📁 [FILE] Creating split file: %@", outputPath);
312
+
313
+ BOOL success = [splitPDF writeToURL:outputURL];
314
+ if (success) {
315
+ [splitFiles addObject:outputPath];
316
+ NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:outputPath error:nil];
317
+ unsigned long long fileSize = [fileAttrs fileSize];
318
+ NSLog(@"✅ [SPLIT] Created file: %@ (size: %llu bytes)", outputPath, fileSize);
319
+ } else {
320
+ NSLog(@"❌ [SPLIT] Failed to save split file");
321
+ }
322
+ } else {
323
+ NSLog(@"⚠️ [SPLIT] Invalid range: [%d, %d]", start + 1, end + 1);
324
+ }
325
+ }
326
+ }
327
+
328
+ NSLog(@"✅ [SPLIT] splitPDF - SUCCESS - Split into %lu files", (unsigned long)splitFiles.count);
329
+ resolve(splitFiles);
330
+ }
331
+
332
+ /**
333
+ * Extract specific pages from PDF
334
+ * PRO FEATURE: Requires Pro license
335
+ */
336
+ RCT_EXPORT_METHOD(extractPages:(NSString *)filePath
337
+ pageNumbers:(NSArray *)pageNumbers
338
+ outputPath:(NSString *)outputPath
339
+ resolver:(RCTPromiseResolveBlock)resolve
340
+ rejecter:(RCTPromiseRejectBlock)reject) {
341
+
342
+ NSLog(@"✂️ [EXTRACT] extractPages - START - file: %@, pages: %lu", filePath, (unsigned long)pageNumbers.count);
343
+
344
+ // Check Pro license
345
+ BOOL licenseActive = [_licenseVerifier isProActive];
346
+ NSLog(@"🔑 [LICENSE] isProActive: %d", licenseActive);
347
+
348
+ if (!licenseActive) {
349
+ NSLog(@"❌ [EXTRACT] License required");
350
+ reject(@"LICENSE_REQUIRED", @"PDF Operations requires a Pro license", nil);
351
+ return;
352
+ }
353
+
354
+ if (!filePath || filePath.length == 0) {
355
+ NSLog(@"❌ [EXTRACT] Invalid path");
356
+ reject(@"INVALID_PATH", @"File path is required", nil);
357
+ return;
358
+ }
359
+
360
+ NSURL *pdfURL = [NSURL fileURLWithPath:filePath];
361
+ PDFDocument *pdfDocument = [[PDFDocument alloc] initWithURL:pdfURL];
362
+
363
+ if (!pdfDocument) {
364
+ NSLog(@"❌ [FILE] PDF not found: %@", filePath);
365
+ reject(@"FILE_NOT_FOUND", @"PDF file not found", nil);
366
+ return;
367
+ }
368
+
369
+ NSUInteger totalPages = pdfDocument.pageCount;
370
+ NSLog(@"📁 [FILE] PDF opened, total pages: %lu", (unsigned long)totalPages);
371
+ NSLog(@"📊 [EXTRACT] Pages to extract: %@", [pageNumbers componentsJoinedByString:@", "]);
372
+ NSLog(@"📁 [FILE] Output path: %@", outputPath);
373
+
374
+ // Create extracted PDF
375
+ PDFDocument *extractedPDF = [[PDFDocument alloc] init];
376
+ int extractedCount = 0;
377
+ int current = 0;
378
+
379
+ for (NSNumber *pageNum in pageNumbers) {
380
+ int pageIndex = pageNum.intValue;
381
+ current++;
382
+
383
+ NSLog(@"📊 [PROGRESS] Processing page %d/%lu (page number: %d)", current, (unsigned long)pageNumbers.count, pageIndex);
384
+
385
+ if (pageIndex >= 0 && pageIndex < totalPages) {
386
+ PDFPage *page = [pdfDocument pageAtIndex:pageIndex];
387
+ if (page) {
388
+ [extractedPDF insertPage:page atIndex:extractedPDF.pageCount];
389
+ extractedCount++;
390
+ NSLog(@"✅ [PROGRESS] Extracted page %d", pageIndex);
391
+ }
392
+ } else {
393
+ NSLog(@"⚠️ [EXTRACT] Skipping invalid page index: %d", pageIndex);
394
+ }
395
+ }
396
+
397
+ // Save extracted PDF
398
+ NSLog(@"📁 [FILE] Writing extracted PDF...");
399
+ NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
400
+ BOOL success = [extractedPDF writeToURL:outputURL];
401
+
402
+ if (success) {
403
+ NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:outputPath error:nil];
404
+ unsigned long long fileSize = [fileAttrs fileSize];
405
+ NSLog(@"✅ [EXTRACT] extractPages - SUCCESS - Extracted %d pages to: %@ (size: %llu bytes)", extractedCount, outputPath, fileSize);
406
+ resolve(outputPath);
407
+ } else {
408
+ NSLog(@"❌ [EXTRACT] Failed to save extracted PDF");
409
+ reject(@"EXTRACT_ERROR", @"Failed to save extracted PDF", nil);
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Rotate a page in PDF
415
+ * PRO FEATURE: Requires Pro license
416
+ */
417
+ RCT_EXPORT_METHOD(rotatePage:(NSString *)filePath
418
+ pageNumber:(int)pageNumber
419
+ degrees:(int)degrees
420
+ resolver:(RCTPromiseResolveBlock)resolve
421
+ rejecter:(RCTPromiseRejectBlock)reject) {
422
+
423
+ // Check Pro license
424
+ if (![_licenseVerifier isProActive]) {
425
+ reject(@"LICENSE_REQUIRED", @"PDF Operations requires a Pro license", nil);
426
+ return;
427
+ }
428
+
429
+ if (!filePath || filePath.length == 0) {
430
+ reject(@"INVALID_PATH", @"File path is required", nil);
431
+ return;
432
+ }
433
+
434
+ NSURL *pdfURL = [NSURL fileURLWithPath:filePath];
435
+ PDFDocument *pdfDocument = [[PDFDocument alloc] initWithURL:pdfURL];
436
+
437
+ if (!pdfDocument) {
438
+ reject(@"FILE_NOT_FOUND", @"PDF file not found", nil);
439
+ return;
440
+ }
441
+
442
+ if (pageNumber < 0 || pageNumber >= pdfDocument.pageCount) {
443
+ reject(@"INVALID_PAGE", @"Invalid page number", nil);
444
+ return;
445
+ }
446
+
447
+ PDFPage *page = [pdfDocument pageAtIndex:pageNumber];
448
+ if (page) {
449
+ // Rotate page
450
+ int currentRotation = page.rotation;
451
+ int newRotation = (currentRotation + degrees) % 360;
452
+ page.rotation = newRotation;
453
+
454
+ // Save PDF
455
+ BOOL success = [pdfDocument writeToURL:pdfURL];
456
+
457
+ if (success) {
458
+ resolve(@YES);
459
+ } else {
460
+ reject(@"ROTATE_ERROR", @"Failed to save rotated PDF", nil);
461
+ }
462
+ } else {
463
+ reject(@"PAGE_NOT_FOUND", @"Page not found", nil);
464
+ }
465
+ }
466
+
467
+ /**
468
+ * Delete a page from PDF
469
+ * PRO FEATURE: Requires Pro license
470
+ */
471
+ RCT_EXPORT_METHOD(deletePage:(NSString *)filePath
472
+ pageNumber:(int)pageNumber
473
+ resolver:(RCTPromiseResolveBlock)resolve
474
+ rejecter:(RCTPromiseRejectBlock)reject) {
475
+
476
+ // Check Pro license
477
+ if (![_licenseVerifier isProActive]) {
478
+ reject(@"LICENSE_REQUIRED", @"PDF Operations requires a Pro license", nil);
479
+ return;
480
+ }
481
+
482
+ if (!filePath || filePath.length == 0) {
483
+ reject(@"INVALID_PATH", @"File path is required", nil);
484
+ return;
485
+ }
486
+
487
+ NSURL *pdfURL = [NSURL fileURLWithPath:filePath];
488
+ PDFDocument *pdfDocument = [[PDFDocument alloc] initWithURL:pdfURL];
489
+
490
+ if (!pdfDocument) {
491
+ reject(@"FILE_NOT_FOUND", @"PDF file not found", nil);
492
+ return;
493
+ }
494
+
495
+ if (pageNumber < 0 || pageNumber >= pdfDocument.pageCount) {
496
+ reject(@"INVALID_PAGE", @"Invalid page number", nil);
497
+ return;
498
+ }
499
+
500
+ // Delete page
501
+ [pdfDocument removePageAtIndex:pageNumber];
502
+
503
+ // Save PDF
504
+ BOOL success = [pdfDocument writeToURL:pdfURL];
505
+
506
+ if (success) {
507
+ resolve(@YES);
508
+ } else {
509
+ reject(@"DELETE_ERROR", @"Failed to save PDF after page deletion", nil);
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Get PDF page count
515
+ */
516
+ RCT_EXPORT_METHOD(getPageCount:(NSString *)filePath
517
+ resolver:(RCTPromiseResolveBlock)resolve
518
+ rejecter:(RCTPromiseRejectBlock)reject) {
519
+
520
+ if (!filePath || filePath.length == 0) {
521
+ reject(@"INVALID_PATH", @"File path is required", nil);
522
+ return;
523
+ }
524
+
525
+ NSURL *pdfURL = [NSURL fileURLWithPath:filePath];
526
+ PDFDocument *pdfDocument = [[PDFDocument alloc] initWithURL:pdfURL];
527
+
528
+ if (!pdfDocument) {
529
+ reject(@"FILE_NOT_FOUND", @"PDF file not found", nil);
530
+ return;
531
+ }
532
+
533
+ NSUInteger pageCount = pdfDocument.pageCount;
534
+ resolve(@(pageCount));
535
+ }
536
+
537
+ @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-pdf-jsi",
3
- "version": "2.2.8",
3
+ "version": "3.0.0",
4
4
  "summary": "High-performance React Native PDF viewer with JSI acceleration - up to 80x faster than traditional bridge",
5
5
  "description": "🚀 Ultra-fast React Native PDF viewer with JSI (JavaScript Interface) integration for maximum performance. Features lazy loading, smart caching, progressive loading, and zero-bridge overhead operations. Perfect for large PDF files with 30-day persistent cache and advanced memory optimization. Google Play 16KB page size compliant for Android 15+. Supports iOS, Android, and Windows platforms.",
6
6
  "main": "index.js",
@@ -74,7 +74,8 @@
74
74
  "peerDependencies": {
75
75
  "react": "*",
76
76
  "react-native": "*",
77
- "react-native-blob-util": ">=0.13.7"
77
+ "react-native-blob-util": ">=0.13.7",
78
+ "@react-native-async-storage/async-storage": ">=1.17.0"
78
79
  },
79
80
  "files": [
80
81
  "android/",