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.
@@ -0,0 +1,687 @@
1
+ /**
2
+ * ExportManager - PDF Export and Conversion Manager
3
+ * Handles exporting PDFs to various formats and PDF operations
4
+ *
5
+ * LICENSE:
6
+ * - Export to text: MIT License (Free)
7
+ * - Export to images: Commercial License (Paid)
8
+ * - PDF operations: Commercial License (Paid)
9
+ *
10
+ * @author Punith M
11
+ * @version 1.0.0
12
+ */
13
+
14
+ import { NativeModules, Platform, Share } from 'react-native';
15
+ import PDFTextExtractor from '../utils/PDFTextExtractor';
16
+ import licenseManager, { ProFeature } from '../license/LicenseManager';
17
+
18
+ const { PDFExporter } = NativeModules;
19
+
20
+ /**
21
+ * Export formats supported
22
+ */
23
+ export const ExportFormat = {
24
+ TEXT: 'text',
25
+ JPEG: 'jpeg',
26
+ PNG: 'png',
27
+ PDF: 'pdf'
28
+ };
29
+
30
+ /**
31
+ * Export quality levels
32
+ */
33
+ export const ExportQuality = {
34
+ LOW: 0.5,
35
+ MEDIUM: 0.75,
36
+ HIGH: 0.9,
37
+ BEST: 1.0
38
+ };
39
+
40
+ /**
41
+ * ExportManager Class
42
+ */
43
+ export class ExportManager {
44
+ constructor() {
45
+ this.isNativeAvailable = !!PDFExporter;
46
+
47
+ if (!this.isNativeAvailable) {
48
+ console.warn('📤 ExportManager: Native module not available - using fallback methods');
49
+ } else {
50
+ console.log('📤 ExportManager: Initialized (version ' + PDFExporter.VERSION + ')');
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Check if export functionality is available
56
+ * @returns {boolean} True if available
57
+ */
58
+ isAvailable() {
59
+ return this.isNativeAvailable || PDFTextExtractor.isTextExtractionAvailable();
60
+ }
61
+
62
+ /**
63
+ * Convert quality string to numeric value
64
+ * @private
65
+ * @param {string|number} quality - Quality as string ('low', 'medium', 'high', 'best') or number (0.5-1.0)
66
+ * @returns {number} Normalized quality as number
67
+ */
68
+ _normalizeQuality(quality) {
69
+ // If already numeric, return as-is
70
+ if (typeof quality === 'number') {
71
+ return quality;
72
+ }
73
+
74
+ // Convert string to numeric
75
+ const qualityMap = {
76
+ 'low': ExportQuality.LOW, // 0.5
77
+ 'medium': ExportQuality.MEDIUM, // 0.75
78
+ 'high': ExportQuality.HIGH, // 0.9
79
+ 'best': ExportQuality.BEST // 1.0
80
+ };
81
+
82
+ const normalized = qualityMap[quality?.toLowerCase()];
83
+ return normalized !== undefined ? normalized : ExportQuality.HIGH; // default to HIGH
84
+ }
85
+
86
+ // ============================================
87
+ // EXPORT TO TEXT
88
+ // ============================================
89
+
90
+ /**
91
+ * Export PDF to plain text
92
+ * @param {string} filePath - Path to PDF file
93
+ * @param {Object} options - Export options
94
+ * @returns {Promise<string>} Exported text content
95
+ */
96
+ async exportToText(filePath, options = {}) {
97
+ const {
98
+ pages = null, // null = all pages, or array of page numbers
99
+ includePageNumbers = true,
100
+ separator = '\n\n--- Page {page} ---\n\n',
101
+ encoding = 'utf8'
102
+ } = options;
103
+
104
+ console.log('📤 ExportManager: Exporting to text...');
105
+
106
+ try {
107
+ // Use text extractor
108
+ let textMap;
109
+
110
+ if (pages) {
111
+ // Convert to 0-indexed
112
+ const pageIndices = pages.map(p => p - 1);
113
+ textMap = await PDFTextExtractor.extractTextFromPages(filePath, pageIndices);
114
+ } else {
115
+ textMap = await PDFTextExtractor.extractAllText(filePath);
116
+ }
117
+
118
+ // Build text document
119
+ let result = '';
120
+ const sortedPages = Array.from(textMap.keys()).sort((a, b) => a - b);
121
+
122
+ sortedPages.forEach((pageNum, index) => {
123
+ const text = textMap.get(pageNum);
124
+
125
+ if (includePageNumbers && index > 0) {
126
+ const sep = separator.replace('{page}', pageNum + 1);
127
+ result += sep;
128
+ }
129
+
130
+ result += text;
131
+ });
132
+
133
+ console.log(`📤 ExportManager: Exported ${result.length} characters from ${sortedPages.length} pages`);
134
+ return result;
135
+
136
+ } catch (error) {
137
+ console.error('📤 ExportManager: Export to text error:', error);
138
+ throw error;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Export page to text (single page)
144
+ * @param {string} filePath - Path to PDF file
145
+ * @param {number} pageNumber - Page number (1-indexed)
146
+ * @returns {Promise<string>} Page text
147
+ */
148
+ async exportPageToText(filePath, pageNumber) {
149
+ try {
150
+ const text = await PDFTextExtractor.extractTextFromPage(filePath, pageNumber - 1);
151
+ console.log(`📤 ExportManager: Exported page ${pageNumber} (${text.length} chars)`);
152
+ return text;
153
+ } catch (error) {
154
+ console.error('📤 ExportManager: Export page error:', error);
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ // ============================================
160
+ // EXPORT TO IMAGES
161
+ // ============================================
162
+
163
+ /**
164
+ * Export PDF pages to images
165
+ * @param {string} filePath - Path to PDF file
166
+ * @param {Object} options - Export options
167
+ * @returns {Promise<Array>} Array of image paths
168
+ */
169
+ async exportToImages(filePath, options = {}) {
170
+ // ⚠️ PRO FEATURE - Requires license
171
+ licenseManager.requirePro('Export to Images');
172
+
173
+ const {
174
+ pages = null, // null = all pages
175
+ format = ExportFormat.JPEG,
176
+ quality = ExportQuality.HIGH,
177
+ width = null, // null = original width
178
+ height = null, // null = original height
179
+ scale = 2.0 // Scale factor for rendering
180
+ } = options;
181
+
182
+ console.log('📤 ExportManager: Exporting to images...');
183
+
184
+ try {
185
+ if (this.isNativeAvailable) {
186
+ // Use native exporter
187
+ const images = await PDFExporter.exportToImages(filePath, {
188
+ pages: pages || [],
189
+ format,
190
+ quality,
191
+ width,
192
+ height,
193
+ scale
194
+ });
195
+
196
+ console.log(`📤 ExportManager: Exported ${images.length} images`);
197
+ return images;
198
+ } else {
199
+ // Fallback: Use screenshot-based approach
200
+ console.warn('📤 ExportManager: Native export not available, using fallback');
201
+ return this.exportToImagesFallback(filePath, options);
202
+ }
203
+
204
+ } catch (error) {
205
+ console.error('📤 ExportManager: Export to images error:', error);
206
+ throw error;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Export single page to image
212
+ * @param {string} filePath - Path to PDF file
213
+ * @param {number} pageNumber - Page number (1-indexed)
214
+ * @param {Object} options - Export options
215
+ * @returns {Promise<string>} Image file path
216
+ */
217
+ async exportPageToImage(filePath, pageNumber, options = {}) {
218
+ const {
219
+ format = ExportFormat.JPEG,
220
+ quality = ExportQuality.HIGH,
221
+ scale = 2.0
222
+ } = options;
223
+
224
+ console.log(`🖼️ [ExportManager] exportPageToImage - START`, {
225
+ filePath,
226
+ pageNumber,
227
+ format,
228
+ quality,
229
+ scale
230
+ });
231
+
232
+ try {
233
+ // ⚠️ PRO FEATURE - Requires license
234
+ console.log('🔑 [ExportManager] Checking license for Export to Images...');
235
+ licenseManager.requirePro('Export to Images');
236
+ console.log('✅ [ExportManager] License check passed');
237
+
238
+ if (this.isNativeAvailable) {
239
+ console.log('📱 [ExportManager] Calling native PDFExporter.exportPageToImage...');
240
+ console.log('📱 [ExportManager] Parameters:', {
241
+ pageIndex: pageNumber - 1,
242
+ format,
243
+ quality,
244
+ scale
245
+ });
246
+
247
+ const imagePath = await PDFExporter.exportPageToImage(filePath, pageNumber - 1, {
248
+ format,
249
+ quality: this._normalizeQuality(quality),
250
+ scale
251
+ });
252
+
253
+ console.log('✅ [ExportManager] exportPageToImage - SUCCESS', {
254
+ outputPath: imagePath
255
+ });
256
+ return imagePath;
257
+ } else {
258
+ console.error('❌ [ExportManager] Native export module not available');
259
+ throw new Error('Native export module not available');
260
+ }
261
+
262
+ } catch (error) {
263
+ console.error('❌ [ExportManager] exportPageToImage - ERROR:', error.message);
264
+ console.error('❌ [ExportManager] Error details:', error);
265
+ throw error;
266
+ }
267
+ }
268
+
269
+ // ============================================
270
+ // PDF OPERATIONS
271
+ // ============================================
272
+
273
+ /**
274
+ * Merge multiple PDFs into one
275
+ * @param {string[]} filePaths - Array of PDF file paths
276
+ * @param {string} outputPath - Output file path
277
+ * @returns {Promise<string>} Path to merged PDF
278
+ */
279
+ async mergePDFs(filePaths, outputPath = null) {
280
+ // ⚠️ PRO FEATURE - Requires license
281
+ licenseManager.requirePro('PDF Operations');
282
+
283
+ console.log(`📤 ExportManager: Merging ${filePaths.length} PDFs...`);
284
+
285
+ try {
286
+ if (this.isNativeAvailable) {
287
+ const mergedPath = await PDFExporter.mergePDFs(filePaths, outputPath);
288
+ console.log('📤 ExportManager: Merged to:', mergedPath);
289
+ return mergedPath;
290
+ } else {
291
+ throw new Error('PDF merge requires native module');
292
+ }
293
+
294
+ } catch (error) {
295
+ console.error('📤 ExportManager: Merge PDFs error:', error);
296
+ throw error;
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Split PDF into multiple files
302
+ * @param {string} filePath - Path to PDF file
303
+ * @param {Array} ranges - Flat array of page range pairs [start1, end1, start2, end2, ...]
304
+ * @param {string} outputDir - Output directory
305
+ * @returns {Promise<Array>} Array of split PDF paths
306
+ */
307
+ async splitPDF(filePath, ranges, outputDir = null) {
308
+ console.log(`✂️ [ExportManager] splitPDF - START`, {
309
+ filePath,
310
+ ranges,
311
+ outputDir,
312
+ rangeCount: ranges.length
313
+ });
314
+
315
+ try {
316
+ console.log('🔑 [ExportManager] Checking license for PDF Operations...');
317
+ licenseManager.requirePro('PDF Operations');
318
+ console.log('✅ [ExportManager] License check passed');
319
+
320
+ if (this.isNativeAvailable) {
321
+ console.log('📱 [ExportManager] Calling native PDFExporter.splitPDF...');
322
+ console.log('📱 [ExportManager] Ranges:', JSON.stringify(ranges));
323
+
324
+ const splitPaths = await PDFExporter.splitPDF(filePath, ranges, outputDir);
325
+
326
+ console.log(`✅ [ExportManager] splitPDF - SUCCESS - Split into ${splitPaths.length} files`);
327
+ console.log('📁 [ExportManager] Split files:', splitPaths);
328
+ return splitPaths;
329
+ } else {
330
+ console.error('❌ [ExportManager] PDF split requires native module');
331
+ throw new Error('PDF split requires native module');
332
+ }
333
+
334
+ } catch (error) {
335
+ console.error('❌ [ExportManager] splitPDF - ERROR:', error.message);
336
+ console.error('❌ [ExportManager] Error details:', error);
337
+ throw error;
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Extract pages from PDF
343
+ * @param {string} filePath - Path to PDF file
344
+ * @param {number[]} pages - Page numbers to extract (1-indexed)
345
+ * @param {string} outputPath - Output file path
346
+ * @returns {Promise<string>} Path to new PDF with extracted pages
347
+ */
348
+ async extractPages(filePath, pages, outputPath = null) {
349
+ console.log(`✂️ [ExportManager] extractPages - START`, {
350
+ filePath,
351
+ pages,
352
+ pageCount: pages.length,
353
+ outputPath
354
+ });
355
+
356
+ try {
357
+ console.log('🔑 [ExportManager] Checking license for PDF Operations...');
358
+ licenseManager.requirePro('PDF Operations');
359
+ console.log('✅ [ExportManager] License check passed');
360
+
361
+ if (this.isNativeAvailable) {
362
+ // Convert to 0-indexed
363
+ const pageIndices = pages.map(p => p - 1);
364
+
365
+ console.log('📱 [ExportManager] Calling native PDFExporter.extractPages...');
366
+ console.log('📱 [ExportManager] Parameters:', {
367
+ pageIndices,
368
+ outputPath: outputPath || 'auto-generated'
369
+ });
370
+
371
+ const extractedPath = await PDFExporter.extractPages(filePath, pageIndices, outputPath);
372
+
373
+ console.log('✅ [ExportManager] extractPages - SUCCESS', {
374
+ extractedPath
375
+ });
376
+ return extractedPath;
377
+ } else {
378
+ console.error('❌ [ExportManager] PDF page extraction requires native module');
379
+ throw new Error('PDF page extraction requires native module');
380
+ }
381
+
382
+ } catch (error) {
383
+ console.error('❌ [ExportManager] extractPages - ERROR:', error.message);
384
+ console.error('❌ [ExportManager] Error details:', error);
385
+ throw error;
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Rotate pages in PDF
391
+ * @param {string} filePath - Path to PDF file
392
+ * @param {Object} rotations - Map of page number to rotation degrees
393
+ * @param {string} outputPath - Output file path
394
+ * @returns {Promise<string>} Path to rotated PDF
395
+ */
396
+ async rotatePages(filePath, rotations, outputPath = null) {
397
+ console.log('📤 ExportManager: Rotating pages...');
398
+
399
+ try {
400
+ if (this.isNativeAvailable) {
401
+ const rotatedPath = await PDFExporter.rotatePages(filePath, rotations, outputPath);
402
+ console.log('📤 ExportManager: Rotated PDF saved to:', rotatedPath);
403
+ return rotatedPath;
404
+ } else {
405
+ throw new Error('PDF rotation requires native module');
406
+ }
407
+
408
+ } catch (error) {
409
+ console.error('📤 ExportManager: Rotate pages error:', error);
410
+ throw error;
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Delete pages from PDF
416
+ * @param {string} filePath - Path to PDF file
417
+ * @param {number[]} pages - Page numbers to delete (1-indexed)
418
+ * @param {string} outputPath - Output file path
419
+ * @returns {Promise<string>} Path to new PDF
420
+ */
421
+ async deletePages(filePath, pages, outputPath = null) {
422
+ console.log(`📤 ExportManager: Deleting ${pages.length} pages...`);
423
+
424
+ try {
425
+ if (this.isNativeAvailable) {
426
+ // Convert to 0-indexed
427
+ const pageIndices = pages.map(p => p - 1);
428
+ const newPath = await PDFExporter.deletePages(filePath, pageIndices, outputPath);
429
+ console.log('📤 ExportManager: New PDF saved to:', newPath);
430
+ return newPath;
431
+ } else {
432
+ throw new Error('PDF page deletion requires native module');
433
+ }
434
+
435
+ } catch (error) {
436
+ console.error('📤 ExportManager: Delete pages error:', error);
437
+ throw error;
438
+ }
439
+ }
440
+
441
+ // ============================================
442
+ // SHARE FUNCTIONALITY
443
+ // ============================================
444
+
445
+ /**
446
+ * Share exported content
447
+ * @param {string} content - Content to share (file path or text)
448
+ * @param {Object} options - Share options
449
+ * @returns {Promise<Object>} Share result
450
+ */
451
+ async share(content, options = {}) {
452
+ const {
453
+ title = 'Exported from PDF',
454
+ message = null,
455
+ url = null,
456
+ type = 'text' // 'text', 'file'
457
+ } = options;
458
+
459
+ try {
460
+ const shareOptions = {
461
+ title
462
+ };
463
+
464
+ if (type === 'text') {
465
+ shareOptions.message = content;
466
+ } else if (type === 'file') {
467
+ shareOptions.url = Platform.OS === 'ios' ? content : `file://${content}`;
468
+ shareOptions.message = message;
469
+ }
470
+
471
+ const result = await Share.share(shareOptions);
472
+ console.log('📤 ExportManager: Share result:', result);
473
+ return result;
474
+
475
+ } catch (error) {
476
+ console.error('📤 ExportManager: Share error:', error);
477
+ throw error;
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Export and share text
483
+ * @param {string} filePath - Path to PDF file
484
+ * @param {Object} options - Export options
485
+ * @returns {Promise<Object>} Share result
486
+ */
487
+ async exportAndShareText(filePath, options = {}) {
488
+ try {
489
+ const text = await this.exportToText(filePath, options);
490
+ return await this.share(text, {
491
+ title: 'PDF Text Export',
492
+ type: 'text'
493
+ });
494
+ } catch (error) {
495
+ console.error('📤 ExportManager: Export and share text error:', error);
496
+ throw error;
497
+ }
498
+ }
499
+
500
+ /**
501
+ * Export and share image
502
+ * @param {string} filePath - Path to PDF file
503
+ * @param {number} pageNumber - Page number (1-indexed)
504
+ * @param {Object} options - Export options
505
+ * @returns {Promise<Object>} Share result
506
+ */
507
+ async exportAndShareImage(filePath, pageNumber, options = {}) {
508
+ try {
509
+ const imagePath = await this.exportPageToImage(filePath, pageNumber, options);
510
+ return await this.share(imagePath, {
511
+ title: `PDF Page ${pageNumber}`,
512
+ type: 'file'
513
+ });
514
+ } catch (error) {
515
+ console.error('📤 ExportManager: Export and share image error:', error);
516
+ throw error;
517
+ }
518
+ }
519
+
520
+ // ============================================
521
+ // BATCH OPERATIONS
522
+ // ============================================
523
+
524
+ /**
525
+ * Export multiple pages to images with progress
526
+ * @param {string} filePath - Path to PDF file
527
+ * @param {number[]} pages - Page numbers (1-indexed)
528
+ * @param {Object} options - Export options
529
+ * @param {Function} onProgress - Progress callback
530
+ * @returns {Promise<Array>} Array of image paths
531
+ */
532
+ async exportPagesToImages(filePath, pages, options = {}, onProgress = null) {
533
+ console.log(`🖼️ [ExportManager] exportPagesToImages - START`, {
534
+ filePath,
535
+ pages,
536
+ pageCount: pages.length,
537
+ options
538
+ });
539
+
540
+ const imagePaths = [];
541
+
542
+ try {
543
+ for (let i = 0; i < pages.length; i++) {
544
+ const pageNum = pages[i];
545
+
546
+ console.log(`📊 [ExportManager] Exporting page ${i + 1}/${pages.length} (page number: ${pageNum})`);
547
+
548
+ const imagePath = await this.exportPageToImage(filePath, pageNum, options);
549
+ imagePaths.push(imagePath);
550
+
551
+ console.log(`✅ [ExportManager] Page ${pageNum} exported to: ${imagePath}`);
552
+
553
+ if (onProgress) {
554
+ onProgress(i + 1, pages.length, imagePath);
555
+ }
556
+ }
557
+
558
+ console.log(`✅ [ExportManager] exportPagesToImages - SUCCESS - Batch export complete: ${imagePaths.length} images`);
559
+ return imagePaths;
560
+
561
+ } catch (error) {
562
+ console.error('❌ [ExportManager] exportPagesToImages - ERROR:', error.message);
563
+ console.error('❌ [ExportManager] Error details:', error);
564
+ throw error;
565
+ }
566
+ }
567
+
568
+ /**
569
+ * Export pages to text with progress
570
+ * @param {string} filePath - Path to PDF file
571
+ * @param {number[]} pages - Page numbers (1-indexed)
572
+ * @param {Function} onProgress - Progress callback
573
+ * @returns {Promise<Map>} Map of page to text
574
+ */
575
+ async exportPagesToText(filePath, pages, onProgress = null) {
576
+ console.log(`📤 ExportManager: Batch exporting ${pages.length} pages to text...`);
577
+
578
+ try {
579
+ // Convert to 0-indexed
580
+ const pageIndices = pages.map(p => p - 1);
581
+
582
+ const textMap = await PDFTextExtractor.extractTextFromPages(filePath, pageIndices);
583
+
584
+ if (onProgress) {
585
+ onProgress(textMap.size, pages.length);
586
+ }
587
+
588
+ return textMap;
589
+
590
+ } catch (error) {
591
+ console.error('📤 ExportManager: Batch text export error:', error);
592
+ throw error;
593
+ }
594
+ }
595
+
596
+ // ============================================
597
+ // UTILITY METHODS
598
+ // ============================================
599
+
600
+ /**
601
+ * Get export capabilities
602
+ * @returns {Object} Capabilities object
603
+ */
604
+ getCapabilities() {
605
+ return {
606
+ exportToText: PDFTextExtractor.isTextExtractionAvailable(),
607
+ exportToImages: this.isNativeAvailable,
608
+ mergePDFs: this.isNativeAvailable,
609
+ splitPDF: this.isNativeAvailable,
610
+ extractPages: this.isNativeAvailable,
611
+ rotatePages: this.isNativeAvailable,
612
+ deletePages: this.isNativeAvailable,
613
+ share: true // Share API is always available
614
+ };
615
+ }
616
+
617
+ /**
618
+ * Get module information
619
+ * @returns {Object} Module info
620
+ */
621
+ getModuleInfo() {
622
+ return {
623
+ isAvailable: this.isNativeAvailable,
624
+ platform: Platform.OS,
625
+ version: PDFExporter?.VERSION || 'N/A',
626
+ capabilities: this.getCapabilities()
627
+ };
628
+ }
629
+
630
+ /**
631
+ * Estimate export time
632
+ * @param {number} pageCount - Number of pages to export
633
+ * @param {string} format - Export format
634
+ * @returns {Object} Time estimate
635
+ */
636
+ estimateExportTime(pageCount, format = ExportFormat.TEXT) {
637
+ let msPerPage;
638
+
639
+ switch (format) {
640
+ case ExportFormat.TEXT:
641
+ msPerPage = 80; // Text extraction avg
642
+ break;
643
+ case ExportFormat.JPEG:
644
+ case ExportFormat.PNG:
645
+ msPerPage = 200; // Image rendering avg
646
+ break;
647
+ default:
648
+ msPerPage = 100;
649
+ }
650
+
651
+ const totalMs = pageCount * msPerPage;
652
+ const seconds = Math.round(totalMs / 1000);
653
+
654
+ return {
655
+ milliseconds: totalMs,
656
+ seconds,
657
+ formatted: seconds < 60
658
+ ? `${seconds} seconds`
659
+ : `${Math.round(seconds / 60)} minutes`
660
+ };
661
+ }
662
+
663
+ // ============================================
664
+ // FALLBACK METHODS
665
+ // ============================================
666
+
667
+ /**
668
+ * Fallback image export (if native not available)
669
+ */
670
+ async exportToImagesFallback(filePath, options) {
671
+ console.warn('📤 ExportManager: Using fallback image export');
672
+ console.warn('📤 Note: Fallback export has limitations');
673
+
674
+ // In a real implementation, this could use:
675
+ // 1. Screenshot-based export
676
+ // 2. Canvas-based rendering
677
+ // 3. Server-side conversion
678
+
679
+ throw new Error('Native export module required for image export');
680
+ }
681
+ }
682
+
683
+ // Create singleton instance
684
+ const exportManager = new ExportManager();
685
+
686
+ export default exportManager;
687
+