react-native-pdf-jsi 3.4.0 → 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.
@@ -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);
@@ -28,9 +28,10 @@
28
28
  enableAnnotationRendering: ?boolean,
29
29
  showsHorizontalScrollIndicator: ?boolean,
30
30
  showsVerticalScrollIndicator: ?boolean,
31
- scrollEnabled: ?boolean,
32
- enableAntialiasing: ?boolean,
33
- enableDoubleTapZoom: ?boolean,
31
+ scrollEnabled: ?boolean,
32
+ enableMomentum: ?boolean,
33
+ enableAntialiasing: ?boolean,
34
+ enableDoubleTapZoom: ?boolean,
34
35
  fitPolicy: ?Int32,
35
36
  spacing: ?Int32,
36
37
  password: ?string,
@@ -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;
@@ -66,6 +66,8 @@ const float MIN_SCALE = 1.0f;
66
66
  RCTBridge *_bridge;
67
67
  PDFDocument *_pdfDocument;
68
68
  PDFView *_pdfView;
69
+ UIScrollView *_internalScrollView;
70
+ id<UIScrollViewDelegate> _originalScrollDelegate;
69
71
  PDFOutline *root;
70
72
  float _fixScaleFactor;
71
73
  bool _initialed;
@@ -103,13 +105,45 @@ using namespace facebook::react;
103
105
 
104
106
  + (ComponentDescriptorProvider)componentDescriptorProvider
105
107
  {
106
- return concreteComponentDescriptorProvider<RNPDFPdfViewComponentDescriptor>();
108
+ // Defensive check: Ensure the descriptor class exists before returning
109
+ // This prevents nil object insertion in RCTThirdPartyComponentsProvider
110
+ // The component name must match the codegen name: "RNPDFPdfView"
111
+ // Using static to ensure the provider is initialized only once
112
+ static ComponentDescriptorProvider provider = concreteComponentDescriptorProvider<RNPDFPdfViewComponentDescriptor>();
113
+ return provider;
107
114
  }
108
115
 
109
116
  // Needed because of this: https://github.com/facebook/react-native/pull/37274
110
117
  + (void)load
111
118
  {
112
119
  [super load];
120
+
121
+ // Force class to be loaded before React Native tries to register it
122
+ // This ensures RNPDFPdfViewCls() returns a valid class, preventing nil insertion
123
+ static dispatch_once_t onceToken;
124
+ dispatch_once(&onceToken, ^{
125
+ // Force class initialization by accessing the class
126
+ Class cls = [self class];
127
+ if (cls == nil) {
128
+ RCTLogError(@"RNPDFPdfView: Class is nil in +load");
129
+ return;
130
+ }
131
+
132
+ // Ensure component name is properly set for registration
133
+ // This helps React Native's RCTThirdPartyComponentsProvider find the component
134
+ // The component name must match the codegen name: "RNPDFPdfView"
135
+ NSString *componentName = NSStringFromClass(cls);
136
+ if (componentName == nil || componentName.length == 0) {
137
+ RCTLogError(@"RNPDFPdfView: Component name is nil or empty");
138
+ } else if (![componentName isEqualToString:@"RNPDFPdfView"]) {
139
+ RCTLogWarn(@"RNPDFPdfView: Component name mismatch. Expected 'RNPDFPdfView', got '%@'", componentName);
140
+ }
141
+
142
+ // Verify class is accessible (RNPDFPdfViewCls is defined later, so we just verify the class itself)
143
+ if (cls != RNPDFPdfView.class) {
144
+ RCTLogError(@"RNPDFPdfView: Class mismatch in +load");
145
+ }
146
+ });
113
147
  }
114
148
 
115
149
  - (instancetype)initWithFrame:(CGRect)frame
@@ -196,11 +230,6 @@ using namespace facebook::react;
196
230
  [updatedPropNames addObject:@"scrollEnabled"];
197
231
  }
198
232
 
199
- if (_enableMomentum != newProps.enableMomentum) {
200
- _enableMomentum = newProps.enableMomentum;
201
- [updatedPropNames addObject:@"enableMomentum"];
202
- }
203
-
204
233
  [super updateProps:props oldProps:oldProps];
205
234
  [self didSetProps:updatedPropNames];
206
235
  }
@@ -255,7 +284,7 @@ using namespace facebook::react;
255
284
 
256
285
  - (void)setNativePage:(NSInteger)page
257
286
  {
258
- _page = page;
287
+ _page = (int)page;
259
288
  [self didSetProps:[NSArray arrayWithObject:@"page"]];
260
289
  }
261
290
 
@@ -289,7 +318,6 @@ using namespace facebook::react;
289
318
  _showsHorizontalScrollIndicator = YES;
290
319
  _showsVerticalScrollIndicator = YES;
291
320
  _scrollEnabled = YES;
292
- _enableMomentum = YES;
293
321
 
294
322
  // Enhanced properties
295
323
  _enableCaching = YES;
@@ -529,37 +557,21 @@ using namespace facebook::react;
529
557
  [self setScrollIndicators:self horizontal:_showsHorizontalScrollIndicator vertical:_showsVerticalScrollIndicator depth:0];
530
558
  }
531
559
 
532
- if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"scrollEnabled"])) {
533
- if (_scrollEnabled) {
534
- for (UIView *subview in _pdfView.subviews) {
535
- if ([subview isKindOfClass:[UIScrollView class]]) {
536
- UIScrollView *scrollView = (UIScrollView *)subview;
537
- scrollView.scrollEnabled = YES;
538
- }
539
- }
540
- } else {
541
- for (UIView *subview in _pdfView.subviews) {
542
- if ([subview isKindOfClass:[UIScrollView class]]) {
543
- UIScrollView *scrollView = (UIScrollView *)subview;
544
- scrollView.scrollEnabled = NO;
545
- }
546
- }
547
- }
548
- }
549
-
550
- // Apply momentum property
560
+ // Configure scroll view (scrollEnabled)
551
561
  if (_pdfDocument && ([changedProps containsObject:@"path"] ||
552
- [changedProps containsObject:@"enableMomentum"])) {
553
- for (UIView *subview in _pdfView.subviews) {
554
- if ([subview isKindOfClass:[UIScrollView class]]) {
555
- UIScrollView *scrollView = (UIScrollView *)subview;
556
-
557
- // Apply momentum (bounces)
558
- scrollView.bounces = _enableMomentum;
559
- scrollView.alwaysBounceVertical = _enableMomentum;
560
- scrollView.alwaysBounceHorizontal = _enableMomentum;
561
- }
562
+ [changedProps containsObject:@"scrollEnabled"])) {
563
+ // If path changed, restore original delegate before reconfiguring
564
+ if ([changedProps containsObject:@"path"] && _internalScrollView && _originalScrollDelegate) {
565
+ _internalScrollView.delegate = _originalScrollDelegate;
566
+ _internalScrollView = nil;
567
+ _originalScrollDelegate = nil;
562
568
  }
569
+
570
+ // Use dispatch_async to ensure view hierarchy is fully set up after document load
571
+ dispatch_async(dispatch_get_main_queue(), ^{
572
+ // Search within _pdfView's hierarchy for scroll views
573
+ [self configureScrollView:self->_pdfView enabled:self->_scrollEnabled depth:0];
574
+ });
563
575
  }
564
576
 
565
577
  if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"enablePaging"] || [changedProps containsObject:@"horizontal"] || [changedProps containsObject:@"page"])) {
@@ -796,7 +808,7 @@ using namespace facebook::react;
796
808
  * Tap
797
809
  * zoom reset or zoom in
798
810
  *
799
- * @param recognizer
811
+ * @param recognizer The tap gesture recognizer
800
812
  */
801
813
  - (void)handleDoubleTap:(UITapGestureRecognizer *)recognizer
802
814
  {
@@ -869,7 +881,7 @@ using namespace facebook::react;
869
881
  * Single Tap
870
882
  * stop zoom
871
883
  *
872
- * @param recognizer
884
+ * @param sender The tap gesture recognizer
873
885
  */
874
886
  - (void)handleSingleTap:(UITapGestureRecognizer *)sender
875
887
  {
@@ -893,7 +905,7 @@ using namespace facebook::react;
893
905
  * Pinch
894
906
  *
895
907
  *
896
- * @param recognizer
908
+ * @param sender The pinch gesture recognizer
897
909
  */
898
910
  -(void)handlePinch:(UIPinchGestureRecognizer *)sender{
899
911
  [self onScaleChanged:Nil];
@@ -990,6 +1002,83 @@ using namespace facebook::react;
990
1002
  }
991
1003
  }
992
1004
 
1005
+ - (void)configureScrollView:(UIView *)view enabled:(BOOL)enabled depth:(int)depth {
1006
+ // max depth, prevent infinite loop
1007
+ if (depth > 10) {
1008
+ return;
1009
+ }
1010
+
1011
+ if ([view isKindOfClass:[UIScrollView class]]) {
1012
+ UIScrollView *scrollView = (UIScrollView *)view;
1013
+ // Since we're starting the recursion from _pdfView, all scroll views found are within its hierarchy
1014
+ // Configure scroll properties
1015
+ scrollView.scrollEnabled = enabled;
1016
+
1017
+ // Disable horizontal bouncing to prevent interference with navigation swipe-back
1018
+ scrollView.alwaysBounceHorizontal = NO;
1019
+ // Keep vertical bounce enabled for natural scrolling feel
1020
+ scrollView.bounces = YES;
1021
+
1022
+ // Set delegate for scroll tracking (only once to avoid conflicts)
1023
+ // Store original delegate before replacing it to preserve PDFView's internal scrolling
1024
+ if (!_internalScrollView) {
1025
+ _internalScrollView = scrollView;
1026
+ // Store original delegate if it exists and is not us
1027
+ if (scrollView.delegate && scrollView.delegate != self) {
1028
+ _originalScrollDelegate = scrollView.delegate;
1029
+ }
1030
+ scrollView.delegate = self;
1031
+ }
1032
+ }
1033
+
1034
+ for (UIView *subview in view.subviews) {
1035
+ [self configureScrollView:subview enabled:enabled depth:depth + 1];
1036
+ }
1037
+ }
1038
+
1039
+ #pragma mark - UIScrollViewDelegate
1040
+
1041
+ - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
1042
+ // Forward to original delegate first if it exists (important for PDFView's scrolling)
1043
+ if (_originalScrollDelegate && [_originalScrollDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
1044
+ [_originalScrollDelegate scrollViewDidScroll:scrollView];
1045
+ }
1046
+
1047
+ if (!_pdfDocument || _singlePage) {
1048
+ return;
1049
+ }
1050
+
1051
+ // Calculate visible page based on scroll position
1052
+ // Use the center point of the visible viewport
1053
+ CGPoint centerPoint = CGPointMake(
1054
+ scrollView.contentOffset.x + scrollView.bounds.size.width / 2,
1055
+ scrollView.contentOffset.y + scrollView.bounds.size.height / 2
1056
+ );
1057
+
1058
+ // Convert to PDFView coordinates
1059
+ CGPoint pdfPoint = [scrollView convertPoint:centerPoint toView:_pdfView];
1060
+ PDFPage *visiblePage = [_pdfView pageForPoint:pdfPoint nearest:YES];
1061
+
1062
+ if (visiblePage) {
1063
+ unsigned long pageIndex = [_pdfDocument indexForPage:visiblePage];
1064
+ int newPage = (int)pageIndex + 1;
1065
+
1066
+ // Only update if page actually changed and is valid
1067
+ if (newPage != _page && newPage > 0 && newPage <= (int)_pdfDocument.pageCount) {
1068
+ _page = newPage;
1069
+ _pageCount = (int)_pdfDocument.pageCount;
1070
+
1071
+ // Trigger preloading if enabled
1072
+ if (_enablePreloading) {
1073
+ [self preloadAdjacentPages:_page];
1074
+ }
1075
+
1076
+ // Notify about page change
1077
+ [self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"pageChanged|%d|%lu", newPage, _pdfDocument.pageCount]]];
1078
+ }
1079
+ }
1080
+ }
1081
+
993
1082
  // Enhanced progressive loading methods
994
1083
  - (void)preloadAdjacentPages:(int)currentPage
995
1084
  {
@@ -1069,9 +1158,6 @@ using namespace facebook::react;
1069
1158
  for (int pageIndex = 0; pageIndex < _pdfDocument.pageCount; pageIndex++) {
1070
1159
  PDFPage *page = [_pdfDocument pageAtIndex:pageIndex];
1071
1160
 
1072
- // Get the page bounds
1073
- CGRect pageBounds = [page boundsForBox:kPDFDisplayBoxCropBox];
1074
-
1075
1161
  // Search for text in the page
1076
1162
  PDFSelection *selection = [page selectionForRange:NSMakeRange(0, page.string.length)];
1077
1163
  if (selection && selection.string.length > 0) {
@@ -1105,9 +1191,33 @@ using namespace facebook::react;
1105
1191
  @end
1106
1192
 
1107
1193
  #ifdef RCT_NEW_ARCH_ENABLED
1194
+
1195
+ #ifdef __cplusplus
1196
+ extern "C" {
1197
+ #endif
1198
+
1108
1199
  Class<RCTComponentViewProtocol> RNPDFPdfViewCls(void)
1109
1200
  {
1110
- return RNPDFPdfView.class;
1201
+ // Defensive check: Ensure class is loaded and valid before returning
1202
+ // This prevents nil object insertion in RCTThirdPartyComponentsProvider
1203
+ Class cls = RNPDFPdfView.class;
1204
+ if (cls == nil) {
1205
+ RCTLogError(@"RNPDFPdfView: Class is nil in RNPDFPdfViewCls");
1206
+ // Return a fallback to prevent crash, though this shouldn't happen
1207
+ return [RCTViewComponentView class];
1208
+ }
1209
+ return cls;
1210
+ }
1211
+
1212
+ // Alias function based on codegen name "rnpdf" - ensures codegen can find the function
1213
+ // even if it uses the codegen name instead of componentProvider name
1214
+ Class<RCTComponentViewProtocol> rnpdfCls(void)
1215
+ {
1216
+ return RNPDFPdfViewCls();
1111
1217
  }
1112
1218
 
1219
+ #ifdef __cplusplus
1220
+ }
1221
+ #endif
1222
+
1113
1223
  #endif
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-pdf-jsi",
3
- "version": "3.4.0",
3
+ "version": "3.4.2",
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",
@@ -65,7 +65,7 @@
65
65
  "dependencies": {
66
66
  "crypto-js": "4.2.0",
67
67
  "deprecated-react-native-prop-types": "^2.3.0",
68
- "react-native-pdf-jsi": "^2.2.8"
68
+ "react-native-pdf-jsi": "^2.2.4"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@babel/core": "^7.20.2",
@@ -106,7 +106,9 @@
106
106
  "javaPackageName": "org.wonday.pdf"
107
107
  },
108
108
  "ios": {
109
- "componentProvider": "RNPDFPdfView"
109
+ "componentProvider": {
110
+ "RNPDFPdfView": "RNPDFPdfView"
111
+ }
110
112
  }
111
113
  }
112
114
  }