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.
Files changed (37) hide show
  1. package/README.md +45 -6
  2. package/android/src/main/java/org/wonday/pdf/FileManager.java +2 -0
  3. package/android/src/main/java/org/wonday/pdf/MemoryMappedCache.java +1 -1
  4. package/android/src/main/java/org/wonday/pdf/PDFExporter.java +0 -54
  5. package/android/src/main/java/org/wonday/pdf/PdfManager.java +5 -0
  6. package/android/src/main/java/org/wonday/pdf/PdfView.java +12 -2
  7. package/android/src/main/java/org/wonday/pdf/RNPDFJSIPackage.java +50 -0
  8. package/android/src/main/java/org/wonday/pdf/RNPDFPackage.java +0 -1
  9. package/fabric/RNPDFPdfNativeComponent.js +8 -3
  10. package/index.js +148 -23
  11. package/ios/RNPDFPdf/RNPDFPdfView.h +19 -1
  12. package/ios/RNPDFPdf/RNPDFPdfView.mm +154 -44
  13. package/package.json +11 -6
  14. package/src/PDFJSI.js +29 -4
  15. package/src/components/AnalyticsPanel.jsx +22 -9
  16. package/src/managers/AnalyticsManager.js +1 -5
  17. package/src/managers/BookmarkManager.js +2 -10
  18. package/src/managers/CacheManager.js +14 -1
  19. package/src/managers/ExportManager.js +5 -20
  20. package/src/utils/ErrorHandler.js +0 -11
  21. package/src/utils/PDFTextExtractor.js +82 -0
  22. package/android/.gradle/5.6.1/fileChanges/last-build.bin +0 -0
  23. package/android/.gradle/5.6.1/fileHashes/fileHashes.lock +0 -0
  24. package/android/.gradle/5.6.1/gc.properties +0 -0
  25. package/android/.gradle/8.5/checksums/checksums.lock +0 -0
  26. package/android/.gradle/8.5/checksums/md5-checksums.bin +0 -0
  27. package/android/.gradle/8.5/checksums/sha1-checksums.bin +0 -0
  28. package/android/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock +0 -0
  29. package/android/.gradle/8.5/dependencies-accessors/gc.properties +0 -0
  30. package/android/.gradle/8.5/executionHistory/executionHistory.lock +0 -0
  31. package/android/.gradle/8.5/fileChanges/last-build.bin +0 -0
  32. package/android/.gradle/8.5/fileHashes/fileHashes.lock +0 -0
  33. package/android/.gradle/8.5/gc.properties +0 -0
  34. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  35. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  36. package/android/.gradle/vcs-1/gc.properties +0 -0
  37. 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.3.0)
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.RNPDFPackage;
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 RNPDFPackage() // This includes JSI modules
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.RNPDFPackage { *; }
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.3.0 (2025-11-12) - Latest ✅ STREAMING BASE64 & CACHE MANAGER
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
- **v2.2.7 - iOS Codegen Fix & New Architecture Support**
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.*
@@ -12,6 +12,8 @@ import com.facebook.react.bridge.ReactMethod;
12
12
  import com.facebook.react.bridge.WritableMap;
13
13
  import com.facebook.react.bridge.Arguments;
14
14
 
15
+ import java.io.File;
16
+
15
17
  /**
16
18
  * FileManager - Native module for file operations like opening folders
17
19
  */
@@ -31,7 +31,7 @@ public class MemoryMappedCache {
31
31
  // Statistics
32
32
  private int totalMaps = 0;
33
33
  private int totalUnmaps = 0;
34
- private long totalBytesM apped = 0;
34
+ private long totalBytesMapped = 0;
35
35
 
36
36
  /**
37
37
  * Memory-map a PDF file for zero-copy access
@@ -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
- event.putString("message", "loadComplete|"+numberOfPages+"|"+width+"|"+height+"|"+gson.toJson(this.getTableOfContents()));
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
- this.page = page>1?page:1;
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
- scrollEnabled: ?boolean,
28
- enableAntialiasing: ?boolean,
29
- enableDoubleTapZoom: ?boolean,
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
- import PdfViewNativeComponent, {
24
- Commands as PdfViewCommands,
25
- } from './fabric/RNPDFPdfNativeComponent';
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: true,
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: decodeURIComponent(uri.replace(/file:\/\//i, '')),
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
- this.lastRNBFTask = ReactNativeBlobUtil.config({
288
- // response data will be saved to this path if it has access right.
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
- trusty: this.props.trustAllCerts,
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
- try {
503
- tableContents = message[4]&&JSON.parse(message[4]);
504
- } catch(e) {
505
- tableContents = message[4];
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
- this.props.onLoadComplete && this.props.onLoadComplete(Number(message[1]), this.state.path, {
508
- width: Number(message[2]),
509
- height: Number(message[3]),
510
- },
511
- tableContents
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
- var PdfCustom = PdfViewNativeComponent;
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;