react-native-pdf-jsi 3.3.1 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -6
- package/android/src/main/java/org/wonday/pdf/FileManager.java +2 -0
- package/android/src/main/java/org/wonday/pdf/MemoryMappedCache.java +1 -1
- package/android/src/main/java/org/wonday/pdf/PDFExporter.java +0 -54
- package/android/src/main/java/org/wonday/pdf/PdfManager.java +5 -0
- package/android/src/main/java/org/wonday/pdf/PdfView.java +12 -2
- package/android/src/main/java/org/wonday/pdf/RNPDFJSIPackage.java +50 -0
- package/android/src/main/java/org/wonday/pdf/RNPDFPackage.java +0 -1
- package/fabric/RNPDFPdfNativeComponent.js +8 -3
- package/index.js +148 -23
- package/ios/RNPDFPdf/RNPDFPdfView.h +19 -1
- package/ios/RNPDFPdf/RNPDFPdfView.mm +154 -44
- package/package.json +11 -6
- package/src/PDFJSI.js +29 -4
- package/src/components/AnalyticsPanel.jsx +22 -9
- package/src/managers/AnalyticsManager.js +1 -5
- package/src/managers/BookmarkManager.js +2 -10
- package/src/managers/CacheManager.js +14 -1
- package/src/managers/ExportManager.js +5 -20
- package/src/utils/ErrorHandler.js +0 -11
- package/src/utils/PDFTextExtractor.js +82 -0
- package/android/.gradle/5.6.1/fileChanges/last-build.bin +0 -0
- package/android/.gradle/5.6.1/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/5.6.1/gc.properties +0 -0
- package/android/.gradle/8.5/checksums/checksums.lock +0 -0
- package/android/.gradle/8.5/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.5/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock +0 -0
- package/android/.gradle/8.5/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.5/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.5/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.5/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.5/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/src/main/java/org/wonday/pdf/LicenseVerifier.java +0 -311
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## ⚡ Performance Benchmarks (v3.
|
|
11
|
+
## ⚡ Performance Benchmarks (v3.4.0)
|
|
12
12
|
|
|
13
13
|
**World-class performance proven with real-world testing:**
|
|
14
14
|
|
|
@@ -87,6 +87,27 @@ Starting November 1, 2025, Google Play will require apps to support 16KB page si
|
|
|
87
87
|
- ✅ **Google Play Approved** - Meets all current and future requirements
|
|
88
88
|
- ✅ **Drop-in Replacement** - Easy migration from existing libraries
|
|
89
89
|
|
|
90
|
+
## 🎉 Version 3.4.0 - Enhanced Navigation & File Path Handling!
|
|
91
|
+
|
|
92
|
+
**Major improvements to navigation reliability and file path tracking for better bookmarking, export, and PDF operations!**
|
|
93
|
+
|
|
94
|
+
### 🚀 **What's New in v3.4.0:**
|
|
95
|
+
|
|
96
|
+
#### ✨ **New Features**
|
|
97
|
+
- **🎯 Enhanced Navigation** - Immediate page navigation support in `setPage()` method. PDF now jumps to the specified page instantly when navigation is triggered programmatically
|
|
98
|
+
- **📁 Improved File Path Handling** - Added `downloadedFilePath` instance variable for reliable path tracking during PDF loading. Paths are available immediately, even before React state updates
|
|
99
|
+
- **🔍 New `getPath()` Method** - Public method to retrieve the current PDF file path. Returns the most reliable path source (instance variable > state path)
|
|
100
|
+
- **📡 Enhanced Path Extraction** - Improved `onLoadComplete` callback to receive file path directly from native module, ensuring reliable path availability for bookmarking, export, and PDF operations
|
|
101
|
+
|
|
102
|
+
#### 🐛 **Bug Fixes**
|
|
103
|
+
- **🧭 Navigation Reliability** - Fixed issue where programmatic page navigation would not scroll to the target page. Native `setPage()` method now calls `jumpTo()` immediately when page changes
|
|
104
|
+
- **📄 File Path Availability** - Fixed issue where `pdfFilePath` was sometimes empty in `onLoadComplete`, causing failures in export and PDF operations. Path is now reliably extracted from multiple sources with proper fallbacks
|
|
105
|
+
- **🔧 Native Event Handling** - Enhanced `loadComplete` event message format to include file path, improving reliability of path extraction in JavaScript
|
|
106
|
+
|
|
107
|
+
#### 🔄 **Technical Improvements**
|
|
108
|
+
- **Native Message Format** - Updated `loadComplete` event format from `loadComplete|pages|width|height|tableContents` to `loadComplete|pages|width|height|path|tableContents` with automatic detection of old/new formats for backward compatibility
|
|
109
|
+
- **Path Tracking** - File paths are now stored in an instance variable (`downloadedFilePath`) immediately upon file preparation/download completion, in addition to React state, ensuring synchronous access
|
|
110
|
+
|
|
90
111
|
## 🎉 Version 3.0.0 - Major Release with Complete Feature Sync!
|
|
91
112
|
|
|
92
113
|
**Complete synchronization of all features from development package with enhanced Android capabilities!**
|
|
@@ -432,13 +453,13 @@ Register the JSI package in your React Native application:
|
|
|
432
453
|
|
|
433
454
|
```java
|
|
434
455
|
// MainApplication.java
|
|
435
|
-
import org.wonday.pdf.
|
|
456
|
+
import org.wonday.pdf.RNPDFJSIPackage;
|
|
436
457
|
|
|
437
458
|
@Override
|
|
438
459
|
protected List<ReactPackage> getPackages() {
|
|
439
460
|
return Arrays.<ReactPackage>asList(
|
|
440
461
|
new MainReactPackage(),
|
|
441
|
-
new
|
|
462
|
+
new RNPDFJSIPackage() // This includes JSI modules
|
|
442
463
|
);
|
|
443
464
|
}
|
|
444
465
|
```
|
|
@@ -1028,7 +1049,7 @@ private static final String FOLDER_NAME = "YourAppName"; // Change this
|
|
|
1028
1049
|
-keep class org.wonday.pdf.PDFJSIManager { *; }
|
|
1029
1050
|
-keep class org.wonday.pdf.PDFJSIModule { *; }
|
|
1030
1051
|
-keep class org.wonday.pdf.EnhancedPdfJSIBridge { *; }
|
|
1031
|
-
-keep class org.wonday.pdf.
|
|
1052
|
+
-keep class org.wonday.pdf.RNPDFJSIPackage { *; }
|
|
1032
1053
|
-keep class org.wonday.pdf.PdfManager { *; }
|
|
1033
1054
|
-keep class org.wonday.pdf.PDFNativeCacheManager { *; }
|
|
1034
1055
|
-keep class org.wonday.pdf.PdfView { *; }
|
|
@@ -1252,7 +1273,25 @@ const Pdf = PdfModule.default;
|
|
|
1252
1273
|
|
|
1253
1274
|
## 📝 Changelog
|
|
1254
1275
|
|
|
1255
|
-
### v3.
|
|
1276
|
+
### v3.4.0 (2025-11-29) - Latest ✅ ENHANCED NAVIGATION & FILE PATH HANDLING
|
|
1277
|
+
|
|
1278
|
+
#### ✨ **New Features**
|
|
1279
|
+
- **Enhanced Navigation** - Added immediate page navigation support in `setPage()` method. PDF now jumps to the specified page instantly when navigation is triggered programmatically
|
|
1280
|
+
- **Improved File Path Handling** - Added `downloadedFilePath` instance variable for reliable path tracking during PDF loading
|
|
1281
|
+
- **New `getPath()` Method** - Added public method to retrieve the current PDF file path with multiple fallback sources
|
|
1282
|
+
- **Enhanced Path Extraction** - Improved `onLoadComplete` callback to receive file path directly from native module
|
|
1283
|
+
|
|
1284
|
+
#### 🐛 **Bug Fixes**
|
|
1285
|
+
- **Navigation Reliability** - Fixed programmatic page navigation not scrolling to target page
|
|
1286
|
+
- **File Path Availability** - Fixed empty `pdfFilePath` in `onLoadComplete` causing export/operation failures
|
|
1287
|
+
- **Native Event Handling** - Enhanced `loadComplete` event message format to include file path
|
|
1288
|
+
|
|
1289
|
+
#### 🔄 **Technical Changes**
|
|
1290
|
+
- Native `loadComplete` event now includes file path for improved reliability
|
|
1291
|
+
- Backward compatible with old message format (auto-detects)
|
|
1292
|
+
- Path stored in instance variable immediately upon file preparation
|
|
1293
|
+
|
|
1294
|
+
### v3.3.0 (2025-11-12) - STREAMING BASE64 & CACHE MANAGER
|
|
1256
1295
|
|
|
1257
1296
|
#### 🚀 **Major Features**
|
|
1258
1297
|
|
|
@@ -1969,7 +2008,7 @@ For bug reports, include:
|
|
|
1969
2008
|
|
|
1970
2009
|
*Transform your PDF viewing experience with enterprise-grade performance and reliability.*
|
|
1971
2010
|
|
|
1972
|
-
**
|
|
2011
|
+
**v3.4.0 - Enhanced Navigation & File Path Handling**
|
|
1973
2012
|
**Copyright (c) 2025-present, Punith M (punithm300@gmail.com). Enhanced PDF JSI Integration. All rights reserved.**
|
|
1974
2013
|
|
|
1975
2014
|
*Original work Copyright (c) 2017-present, Wonday (@wonday.org). All rights reserved.*
|
|
@@ -31,13 +31,11 @@ import android.graphics.pdf.PdfDocument;
|
|
|
31
31
|
|
|
32
32
|
public class PDFExporter extends ReactContextBaseJavaModule {
|
|
33
33
|
private static final String TAG = "PDFExporter";
|
|
34
|
-
private LicenseVerifier licenseVerifier;
|
|
35
34
|
// OPTIMIZATION: Bitmap pool for 90% reduction in bitmap allocations
|
|
36
35
|
private final BitmapPool bitmapPool = new BitmapPool();
|
|
37
36
|
|
|
38
37
|
public PDFExporter(ReactApplicationContext reactContext) {
|
|
39
38
|
super(reactContext);
|
|
40
|
-
licenseVerifier = new LicenseVerifier(reactContext);
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
@Override
|
|
@@ -69,11 +67,6 @@ public class PDFExporter extends ReactContextBaseJavaModule {
|
|
|
69
67
|
@ReactMethod
|
|
70
68
|
public void exportToImages(String filePath, ReadableMap options, Promise promise) {
|
|
71
69
|
try {
|
|
72
|
-
if (!licenseVerifier.isProActive()) {
|
|
73
|
-
promise.reject("LICENSE_REQUIRED", "Export to Images requires a Pro license");
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
70
|
if (filePath == null || filePath.isEmpty()) {
|
|
78
71
|
promise.reject("INVALID_PATH", "File path is required");
|
|
79
72
|
return;
|
|
@@ -113,15 +106,6 @@ public class PDFExporter extends ReactContextBaseJavaModule {
|
|
|
113
106
|
double scale = options.hasKey("scale") ? options.getDouble("scale") : 2.0;
|
|
114
107
|
|
|
115
108
|
Log.i(TAG, "🖼️ [EXPORT] exportPageToImage - START - page: " + pageIndex + ", format: " + format + ", quality: " + quality + ", scale: " + scale);
|
|
116
|
-
|
|
117
|
-
boolean licenseActive = licenseVerifier.isProActive();
|
|
118
|
-
Log.i(TAG, "🔑 [LICENSE] isProActive: " + licenseActive);
|
|
119
|
-
|
|
120
|
-
if (!licenseActive) {
|
|
121
|
-
Log.e(TAG, "❌ [EXPORT] exportPageToImage - FAILED - License required");
|
|
122
|
-
promise.reject("LICENSE_REQUIRED", "Export to Images requires a Pro license");
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
109
|
|
|
126
110
|
if (filePath == null || filePath.isEmpty()) {
|
|
127
111
|
Log.e(TAG, "❌ [EXPORT] exportPageToImage - FAILED - Invalid path");
|
|
@@ -339,12 +323,6 @@ public class PDFExporter extends ReactContextBaseJavaModule {
|
|
|
339
323
|
@ReactMethod
|
|
340
324
|
public void mergePDFs(ReadableArray filePaths, String outputPath, Promise promise) {
|
|
341
325
|
try {
|
|
342
|
-
// Check Pro license
|
|
343
|
-
if (!licenseVerifier.isProActive()) {
|
|
344
|
-
promise.reject("LICENSE_REQUIRED", "PDF Operations requires a Pro license");
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
326
|
if (filePaths == null || filePaths.size() < 2) {
|
|
349
327
|
promise.reject("INVALID_INPUT", "At least 2 PDF files are required for merging");
|
|
350
328
|
return;
|
|
@@ -478,16 +456,6 @@ public class PDFExporter extends ReactContextBaseJavaModule {
|
|
|
478
456
|
|
|
479
457
|
// Original code continues...
|
|
480
458
|
Log.i(TAG, "✂️ [SPLIT] splitPDF - START - file: " + filePath + ", ranges: " + pageRanges.size());
|
|
481
|
-
|
|
482
|
-
// Check Pro license
|
|
483
|
-
boolean licenseActive = licenseVerifier.isProActive();
|
|
484
|
-
Log.i(TAG, "🔑 [LICENSE] isProActive: " + licenseActive);
|
|
485
|
-
|
|
486
|
-
if (!licenseActive) {
|
|
487
|
-
Log.e(TAG, "❌ [SPLIT] License required");
|
|
488
|
-
promise.reject("LICENSE_REQUIRED", "PDF Operations requires a Pro license");
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
459
|
|
|
492
460
|
if (filePath == null || filePath.isEmpty()) {
|
|
493
461
|
Log.e(TAG, "❌ [SPLIT] Invalid path");
|
|
@@ -607,16 +575,6 @@ public class PDFExporter extends ReactContextBaseJavaModule {
|
|
|
607
575
|
public void extractPages(String filePath, ReadableArray pageNumbers, String outputPath, Promise promise) {
|
|
608
576
|
try {
|
|
609
577
|
Log.i(TAG, "✂️ [EXTRACT] extractPages - START - file: " + filePath + ", pages: " + pageNumbers.size());
|
|
610
|
-
|
|
611
|
-
// Check Pro license
|
|
612
|
-
boolean licenseActive = licenseVerifier.isProActive();
|
|
613
|
-
Log.i(TAG, "🔑 [LICENSE] isProActive: " + licenseActive);
|
|
614
|
-
|
|
615
|
-
if (!licenseActive) {
|
|
616
|
-
Log.e(TAG, "❌ [EXTRACT] License required");
|
|
617
|
-
promise.reject("LICENSE_REQUIRED", "PDF Operations requires a Pro license");
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
578
|
|
|
621
579
|
if (filePath == null || filePath.isEmpty()) {
|
|
622
580
|
Log.e(TAG, "❌ [EXTRACT] Invalid path");
|
|
@@ -717,12 +675,6 @@ public class PDFExporter extends ReactContextBaseJavaModule {
|
|
|
717
675
|
@ReactMethod
|
|
718
676
|
public void rotatePage(String filePath, int pageNumber, int degrees, Promise promise) {
|
|
719
677
|
try {
|
|
720
|
-
// Check Pro license
|
|
721
|
-
if (!licenseVerifier.isProActive()) {
|
|
722
|
-
promise.reject("LICENSE_REQUIRED", "PDF Operations requires a Pro license");
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
678
|
if (filePath == null || filePath.isEmpty()) {
|
|
727
679
|
promise.reject("INVALID_PATH", "File path is required");
|
|
728
680
|
return;
|
|
@@ -740,12 +692,6 @@ public class PDFExporter extends ReactContextBaseJavaModule {
|
|
|
740
692
|
@ReactMethod
|
|
741
693
|
public void deletePage(String filePath, int pageNumber, Promise promise) {
|
|
742
694
|
try {
|
|
743
|
-
// Check Pro license
|
|
744
|
-
if (!licenseVerifier.isProActive()) {
|
|
745
|
-
promise.reject("LICENSE_REQUIRED", "PDF Operations requires a Pro license");
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
695
|
if (filePath == null || filePath.isEmpty()) {
|
|
750
696
|
promise.reject("INVALID_PATH", "File path is required");
|
|
751
697
|
return;
|
|
@@ -144,6 +144,11 @@ public class PdfManager extends SimpleViewManager<PdfView> implements RNPDFPdfVi
|
|
|
144
144
|
// NOOP on Android
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
@Override
|
|
148
|
+
public void setEnableMomentum(PdfView view, boolean value) {
|
|
149
|
+
// NOOP on Android - momentum scrolling is handled automatically by Android's ScrollView
|
|
150
|
+
}
|
|
151
|
+
|
|
147
152
|
@ReactProp(name = "fitPolicy")
|
|
148
153
|
public void setFitPolicy(PdfView pdfView, int fitPolicy) {
|
|
149
154
|
pdfView.setFitPolicy(fitPolicy);
|
|
@@ -154,7 +154,9 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
|
|
|
154
154
|
|
|
155
155
|
//create a new json Object for the TableOfContents
|
|
156
156
|
Gson gson = new Gson();
|
|
157
|
-
|
|
157
|
+
// Include path in loadComplete message for reliable access in JS
|
|
158
|
+
String pathValue = this.path != null ? this.path : "";
|
|
159
|
+
event.putString("message", "loadComplete|"+numberOfPages+"|"+width+"|"+height+"|"+pathValue+"|"+gson.toJson(this.getTableOfContents()));
|
|
158
160
|
|
|
159
161
|
ThemedReactContext context = (ThemedReactContext) getContext();
|
|
160
162
|
EventDispatcher dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, getId());
|
|
@@ -369,7 +371,15 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
|
|
|
369
371
|
|
|
370
372
|
// page start from 1
|
|
371
373
|
public void setPage(int page) {
|
|
372
|
-
|
|
374
|
+
int newPage = page>1?page:1;
|
|
375
|
+
int oldPage = this.page;
|
|
376
|
+
this.page = newPage;
|
|
377
|
+
|
|
378
|
+
// If page changed and PDF is already loaded, jump to the new page immediately
|
|
379
|
+
if (newPage != oldPage && !needsReload && this.path != null && !this.isRecycled()) {
|
|
380
|
+
showLog(format("setPage: Jumping to page %d (from %d)", newPage, oldPage));
|
|
381
|
+
this.jumpTo(newPage - 1, false);
|
|
382
|
+
}
|
|
373
383
|
}
|
|
374
384
|
|
|
375
385
|
public void setScale(float scale) {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2017-present, Wonday (@wonday.org)
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
package org.wonday.pdf;
|
|
10
|
+
|
|
11
|
+
import java.util.Collections;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
import java.util.ArrayList;
|
|
14
|
+
|
|
15
|
+
import com.facebook.react.ReactPackage;
|
|
16
|
+
import com.facebook.react.bridge.JavaScriptModule;
|
|
17
|
+
import com.facebook.react.bridge.NativeModule;
|
|
18
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
19
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
20
|
+
|
|
21
|
+
public class RNPDFJSIPackage implements ReactPackage {
|
|
22
|
+
|
|
23
|
+
@Override
|
|
24
|
+
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
25
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
26
|
+
// Add JSI modules for enhanced PDF performance
|
|
27
|
+
modules.add(new PDFJSIManager(reactContext));
|
|
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
|
+
|
|
35
|
+
return modules;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Deprecated as of RN 0.47.0
|
|
39
|
+
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
|
40
|
+
return Collections.emptyList();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Override
|
|
44
|
+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
45
|
+
List<ViewManager> modules = new ArrayList<>();
|
|
46
|
+
modules.add(new PdfManager(reactContext));
|
|
47
|
+
return modules;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
@@ -31,7 +31,6 @@ public class RNPDFPackage implements ReactPackage {
|
|
|
31
31
|
modules.add(new PDFExporter(reactContext));
|
|
32
32
|
modules.add(new FileDownloader(reactContext));
|
|
33
33
|
modules.add(new FileManager(reactContext));
|
|
34
|
-
modules.add(new LicenseVerifier(reactContext));
|
|
35
34
|
|
|
36
35
|
return modules;
|
|
37
36
|
}
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
+
// Note: Deep imports are required for codegen components
|
|
8
|
+
// Suppressing deprecation warnings as these are the correct imports for codegen
|
|
9
|
+
// @ts-ignore - React Native codegen requires these deep imports
|
|
7
10
|
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
11
|
+
// @ts-ignore - React Native codegen requires these deep imports
|
|
8
12
|
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
9
13
|
|
|
10
14
|
type ChangeEvent = $ReadOnly<{|
|
|
@@ -24,9 +28,10 @@
|
|
|
24
28
|
enableAnnotationRendering: ?boolean,
|
|
25
29
|
showsHorizontalScrollIndicator: ?boolean,
|
|
26
30
|
showsVerticalScrollIndicator: ?boolean,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
scrollEnabled: ?boolean,
|
|
32
|
+
enableMomentum: ?boolean,
|
|
33
|
+
enableAntialiasing: ?boolean,
|
|
34
|
+
enableDoubleTapZoom: ?boolean,
|
|
30
35
|
fitPolicy: ?Int32,
|
|
31
36
|
spacing: ?Int32,
|
|
32
37
|
password: ?string,
|
package/index.js
CHANGED
|
@@ -20,9 +20,9 @@ import {
|
|
|
20
20
|
Text,
|
|
21
21
|
requireNativeComponent
|
|
22
22
|
} from 'react-native';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
// Codegen component variables - will be loaded lazily to prevent hooks errors
|
|
24
|
+
let PdfViewNativeComponent = null;
|
|
25
|
+
let PdfViewCommands = null;
|
|
26
26
|
import ReactNativeBlobUtil from 'react-native-blob-util'
|
|
27
27
|
import {ViewPropTypes} from 'deprecated-react-native-prop-types';
|
|
28
28
|
const SHA1 = require('crypto-js/sha1');
|
|
@@ -94,7 +94,7 @@ export default class Pdf extends Component {
|
|
|
94
94
|
scrollEnabled: true,
|
|
95
95
|
enablePaging: false,
|
|
96
96
|
enableRTL: false,
|
|
97
|
-
trustAllCerts:
|
|
97
|
+
trustAllCerts: false,
|
|
98
98
|
usePDFKit: true,
|
|
99
99
|
singlePage: false,
|
|
100
100
|
onLoadProgress: (percent) => {
|
|
@@ -123,6 +123,10 @@ export default class Pdf extends Component {
|
|
|
123
123
|
jsiAvailable: false,
|
|
124
124
|
};
|
|
125
125
|
|
|
126
|
+
// Store downloaded file path in instance variable for immediate access
|
|
127
|
+
// This ensures path is available when onLoadComplete fires, even before state updates
|
|
128
|
+
this.downloadedFilePath = '';
|
|
129
|
+
|
|
126
130
|
this.lastRNBFTask = null;
|
|
127
131
|
this.pdfJSI = PDFJSI;
|
|
128
132
|
this.initializeJSI();
|
|
@@ -184,6 +188,7 @@ export default class Pdf extends Component {
|
|
|
184
188
|
|
|
185
189
|
let uri = source.uri || '';
|
|
186
190
|
// first set to initial state
|
|
191
|
+
this.downloadedFilePath = ''; // Reset instance variable
|
|
187
192
|
if (this._mounted) {
|
|
188
193
|
this.setState({isDownloaded: false, path: '', progress: 0});
|
|
189
194
|
}
|
|
@@ -195,6 +200,8 @@ export default class Pdf extends Component {
|
|
|
195
200
|
.stat(cacheFile)
|
|
196
201
|
.then(stats => {
|
|
197
202
|
if (!Boolean(source.expiration) || (source.expiration * 1000 + stats.lastModified) > (new Date().getTime())) {
|
|
203
|
+
// Store in instance variable immediately for onLoadComplete callback
|
|
204
|
+
this.downloadedFilePath = cacheFile;
|
|
198
205
|
if (this._mounted) {
|
|
199
206
|
this.setState({path: cacheFile, isDownloaded: true});
|
|
200
207
|
}
|
|
@@ -234,6 +241,8 @@ export default class Pdf extends Component {
|
|
|
234
241
|
ReactNativeBlobUtil.fs
|
|
235
242
|
.cp(uri, cacheFile)
|
|
236
243
|
.then(() => {
|
|
244
|
+
// Store in instance variable immediately for onLoadComplete callback
|
|
245
|
+
this.downloadedFilePath = cacheFile;
|
|
237
246
|
if (this._mounted) {
|
|
238
247
|
this.setState({path: cacheFile, isDownloaded: true, progress: 1});
|
|
239
248
|
}
|
|
@@ -247,6 +256,8 @@ export default class Pdf extends Component {
|
|
|
247
256
|
ReactNativeBlobUtil.fs
|
|
248
257
|
.writeFile(cacheFile, data, 'base64')
|
|
249
258
|
.then(() => {
|
|
259
|
+
// Store in instance variable immediately for onLoadComplete callback
|
|
260
|
+
this.downloadedFilePath = cacheFile;
|
|
250
261
|
if (this._mounted) {
|
|
251
262
|
this.setState({path: cacheFile, isDownloaded: true, progress: 1});
|
|
252
263
|
}
|
|
@@ -256,9 +267,13 @@ export default class Pdf extends Component {
|
|
|
256
267
|
this._onError(error)
|
|
257
268
|
});
|
|
258
269
|
} else {
|
|
270
|
+
// Local file path
|
|
271
|
+
const localPath = decodeURIComponent(uri.replace(/file:\/\//i, ''));
|
|
272
|
+
// Store in instance variable immediately for onLoadComplete callback
|
|
273
|
+
this.downloadedFilePath = localPath;
|
|
259
274
|
if (this._mounted) {
|
|
260
275
|
this.setState({
|
|
261
|
-
path:
|
|
276
|
+
path: localPath,
|
|
262
277
|
isDownloaded: true,
|
|
263
278
|
});
|
|
264
279
|
}
|
|
@@ -284,11 +299,32 @@ export default class Pdf extends Component {
|
|
|
284
299
|
const tempCacheFile = cacheFile + '.tmp';
|
|
285
300
|
this._unlinkFile(tempCacheFile);
|
|
286
301
|
|
|
287
|
-
|
|
288
|
-
|
|
302
|
+
// Ensure cache directory exists before downloading
|
|
303
|
+
const cacheDir = ReactNativeBlobUtil.fs.dirs.CacheDir;
|
|
304
|
+
try {
|
|
305
|
+
const dirExists = await ReactNativeBlobUtil.fs.exists(cacheDir);
|
|
306
|
+
if (!dirExists) {
|
|
307
|
+
await ReactNativeBlobUtil.fs.mkdir(cacheDir);
|
|
308
|
+
}
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.warn('Failed to ensure cache directory exists:', error);
|
|
311
|
+
// Continue anyway - ReactNativeBlobUtil might handle it
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Build config object - conditionally include trusty based on URL protocol and trustAllCerts
|
|
315
|
+
const isHttps = source.uri && source.uri.startsWith('https://');
|
|
316
|
+
const config = {
|
|
289
317
|
path: tempCacheFile,
|
|
290
|
-
|
|
291
|
-
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// Only include trusty option for HTTPS URLs and only if trustAllCerts is explicitly true
|
|
321
|
+
// For HTTP URLs, never include trusty option to avoid trust manager errors
|
|
322
|
+
if (isHttps && this.props.trustAllCerts === true) {
|
|
323
|
+
config.trusty = true;
|
|
324
|
+
}
|
|
325
|
+
// For HTTP or when trustAllCerts is false, omit trusty option entirely
|
|
326
|
+
|
|
327
|
+
this.lastRNBFTask = ReactNativeBlobUtil.config(config)
|
|
292
328
|
.fetch(
|
|
293
329
|
source.method ? source.method : 'GET',
|
|
294
330
|
source.uri,
|
|
@@ -336,6 +372,9 @@ export default class Pdf extends Component {
|
|
|
336
372
|
ReactNativeBlobUtil.fs
|
|
337
373
|
.cp(tempCacheFile, cacheFile)
|
|
338
374
|
.then(() => {
|
|
375
|
+
// Store in instance variable immediately for onLoadComplete callback
|
|
376
|
+
// This ensures path is available even if state hasn't updated yet
|
|
377
|
+
this.downloadedFilePath = cacheFile;
|
|
339
378
|
if (this._mounted) {
|
|
340
379
|
this.setState({path: cacheFile, isDownloaded: true, progress: 1});
|
|
341
380
|
}
|
|
@@ -367,6 +406,12 @@ export default class Pdf extends Component {
|
|
|
367
406
|
}
|
|
368
407
|
};
|
|
369
408
|
|
|
409
|
+
// Public method to get the current PDF file path
|
|
410
|
+
getPath() {
|
|
411
|
+
// Return instance variable first (most reliable), then state path
|
|
412
|
+
return this.downloadedFilePath || this.state.path || '';
|
|
413
|
+
}
|
|
414
|
+
|
|
370
415
|
setPage( pageNumber ) {
|
|
371
416
|
if ( (pageNumber === null) || (isNaN(pageNumber)) ) {
|
|
372
417
|
throw new Error('Specified pageNumber is not a number');
|
|
@@ -385,6 +430,16 @@ export default class Pdf extends Component {
|
|
|
385
430
|
|
|
386
431
|
if (!!global?.nativeFabricUIManager ) {
|
|
387
432
|
if (this._root) {
|
|
433
|
+
// Lazy load PdfViewCommands if not already loaded
|
|
434
|
+
if (!PdfViewCommands) {
|
|
435
|
+
try {
|
|
436
|
+
const codegenModule = require('./fabric/RNPDFPdfNativeComponent');
|
|
437
|
+
PdfViewCommands = codegenModule.Commands;
|
|
438
|
+
} catch (error) {
|
|
439
|
+
console.warn('PdfViewCommands not available:', error);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
388
443
|
PdfViewCommands.setNativePage(
|
|
389
444
|
this._root,
|
|
390
445
|
pageNumber,
|
|
@@ -494,22 +549,79 @@ export default class Pdf extends Component {
|
|
|
494
549
|
let message = event.nativeEvent.message.split('|');
|
|
495
550
|
//__DEV__ && console.log("onChange: " + message);
|
|
496
551
|
if (message.length > 0) {
|
|
497
|
-
if (message.length > 5) {
|
|
498
|
-
message[4] = message.splice(4).join('|');
|
|
499
|
-
}
|
|
500
552
|
if (message[0] === 'loadComplete') {
|
|
501
553
|
let tableContents;
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
554
|
+
let filePath;
|
|
555
|
+
|
|
556
|
+
// Handle both old format (without path) and new format (with path)
|
|
557
|
+
// Old format: loadComplete|pages|width|height|tableContents
|
|
558
|
+
// New format: loadComplete|pages|width|height|path|tableContents
|
|
559
|
+
|
|
560
|
+
// First, check if we have the new format (6+ parts before splice)
|
|
561
|
+
const originalLength = message.length;
|
|
562
|
+
const hasPath = originalLength >= 6;
|
|
563
|
+
|
|
564
|
+
if (hasPath) {
|
|
565
|
+
// New format: extract path from message[4], rest is tableContents
|
|
566
|
+
filePath = message[4] || '';
|
|
567
|
+
// Join everything after path (index 5+) as tableContents JSON
|
|
568
|
+
const tableContentsStr = message.slice(5).join('|');
|
|
569
|
+
try {
|
|
570
|
+
tableContents = tableContentsStr && JSON.parse(tableContentsStr);
|
|
571
|
+
} catch(e) {
|
|
572
|
+
tableContents = tableContentsStr;
|
|
573
|
+
}
|
|
574
|
+
} else {
|
|
575
|
+
// Old format: no path, everything after height (index 4+) is tableContents
|
|
576
|
+
filePath = this.downloadedFilePath || this.state.path || '';
|
|
577
|
+
// Handle old splice logic for tableContents that might contain |
|
|
578
|
+
if (originalLength > 5) {
|
|
579
|
+
message[4] = message.splice(4).join('|');
|
|
580
|
+
}
|
|
581
|
+
try {
|
|
582
|
+
tableContents = message[4] && JSON.parse(message[4]);
|
|
583
|
+
} catch(e) {
|
|
584
|
+
tableContents = message[4];
|
|
585
|
+
}
|
|
506
586
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
587
|
+
|
|
588
|
+
// Final fallback: use instance variable or state if path from native is empty
|
|
589
|
+
if (!filePath || filePath.trim() === '') {
|
|
590
|
+
filePath = this.downloadedFilePath || this.state.path || '';
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Log path extraction for debugging
|
|
594
|
+
if (__DEV__) {
|
|
595
|
+
console.log('📁 [Pdf] loadComplete - Path extraction:', {
|
|
596
|
+
originalMessageLength: originalLength,
|
|
597
|
+
hasPathInMessage: hasPath,
|
|
598
|
+
fromNativeMessage: hasPath ? (message[4] || 'empty') : 'not in message',
|
|
599
|
+
fromInstance: this.downloadedFilePath || 'empty',
|
|
600
|
+
fromState: this.state.path || 'empty',
|
|
601
|
+
final: filePath || 'empty',
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Always call onLoadComplete callback
|
|
606
|
+
if (this.props.onLoadComplete) {
|
|
607
|
+
console.log('📁 [Pdf] Calling onLoadComplete callback with:', {
|
|
608
|
+
pages: Number(message[1]),
|
|
609
|
+
path: filePath,
|
|
610
|
+
width: Number(message[2]),
|
|
611
|
+
height: Number(message[3]),
|
|
612
|
+
});
|
|
613
|
+
this.props.onLoadComplete(Number(message[1]), filePath, {
|
|
614
|
+
width: Number(message[2]),
|
|
615
|
+
height: Number(message[3]),
|
|
616
|
+
},
|
|
617
|
+
tableContents
|
|
618
|
+
);
|
|
619
|
+
} else {
|
|
620
|
+
console.warn('⚠️ [Pdf] onLoadComplete callback not provided');
|
|
621
|
+
}
|
|
622
|
+
} else if (message.length > 5) {
|
|
623
|
+
// Only apply splice logic for non-loadComplete messages
|
|
624
|
+
message[4] = message.splice(4).join('|');
|
|
513
625
|
} else if (message[0] === 'pageChanged') {
|
|
514
626
|
this.props.onPageChanged && this.props.onPageChanged(Number(message[1]), Number(message[2]));
|
|
515
627
|
} else if (message[0] === 'error') {
|
|
@@ -583,7 +695,20 @@ export default class Pdf extends Component {
|
|
|
583
695
|
}
|
|
584
696
|
|
|
585
697
|
if (Platform.OS === "android" || Platform.OS === "ios") {
|
|
586
|
-
|
|
698
|
+
// Load codegen component immediately - it should work after React is initialized
|
|
699
|
+
try {
|
|
700
|
+
const codegenModule = require('./fabric/RNPDFPdfNativeComponent');
|
|
701
|
+
const CodegenComponent = codegenModule.default;
|
|
702
|
+
PdfViewNativeComponent = CodegenComponent;
|
|
703
|
+
PdfViewCommands = codegenModule.Commands;
|
|
704
|
+
var PdfCustom = CodegenComponent;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
console.warn('Failed to load codegen component, using fallback:', error);
|
|
707
|
+
// Use the correct native component name for Android/iOS
|
|
708
|
+
var PdfCustom = requireNativeComponent('RNPDFPdfView', Pdf, {
|
|
709
|
+
nativeOnly: {path: true, onChange: true},
|
|
710
|
+
});
|
|
711
|
+
}
|
|
587
712
|
} else if (Platform.OS === "windows") {
|
|
588
713
|
var PdfCustom = requireNativeComponent('RCTPdf', Pdf, {
|
|
589
714
|
nativeOnly: {path: true, onChange: true},
|
|
@@ -19,6 +19,24 @@
|
|
|
19
19
|
|
|
20
20
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
21
21
|
#import <React/RCTViewComponentView.h>
|
|
22
|
+
#import <React/RCTComponentViewProtocol.h>
|
|
23
|
+
|
|
24
|
+
#ifdef __cplusplus
|
|
25
|
+
extern "C" {
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
// Forward declaration for codegen - ensures RNPDFPdfViewCls() is visible to RCTThirdPartyComponentsProvider
|
|
29
|
+
// Using extern "C" for proper C linkage, matching React Native's pattern
|
|
30
|
+
Class<RCTComponentViewProtocol> RNPDFPdfViewCls(void);
|
|
31
|
+
|
|
32
|
+
// Alias function based on codegen name "rnpdf" - ensures codegen can find the function
|
|
33
|
+
// even if it uses the codegen name instead of componentProvider name
|
|
34
|
+
Class<RCTComponentViewProtocol> rnpdfCls(void);
|
|
35
|
+
|
|
36
|
+
#ifdef __cplusplus
|
|
37
|
+
}
|
|
38
|
+
#endif
|
|
39
|
+
|
|
22
40
|
#endif
|
|
23
41
|
|
|
24
42
|
@class RCTEventDispatcher;
|
|
@@ -29,7 +47,7 @@ RCTViewComponentView
|
|
|
29
47
|
#else
|
|
30
48
|
UIView
|
|
31
49
|
#endif
|
|
32
|
-
<UIGestureRecognizerDelegate>
|
|
50
|
+
<UIGestureRecognizerDelegate, UIScrollViewDelegate>
|
|
33
51
|
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
|
34
52
|
|
|
35
53
|
@property(nonatomic, strong) NSString *path;
|