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
@@ -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.3.1",
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",
@@ -64,7 +64,8 @@
64
64
  },
65
65
  "dependencies": {
66
66
  "crypto-js": "4.2.0",
67
- "deprecated-react-native-prop-types": "^2.3.0"
67
+ "deprecated-react-native-prop-types": "^2.3.0",
68
+ "react-native-pdf-jsi": "^2.2.4"
68
69
  },
69
70
  "devDependencies": {
70
71
  "@babel/core": "^7.20.2",
@@ -72,10 +73,10 @@
72
73
  "prop-types": "^15.7.2"
73
74
  },
74
75
  "peerDependencies": {
76
+ "@react-native-async-storage/async-storage": ">=1.17.0",
75
77
  "react": "*",
76
78
  "react-native": "*",
77
- "react-native-blob-util": ">=0.13.7",
78
- "@react-native-async-storage/async-storage": ">=1.17.0"
79
+ "react-native-blob-util": ">=0.13.7"
79
80
  },
80
81
  "files": [
81
82
  "android/",
@@ -93,7 +94,9 @@
93
94
  "PinchZoomView.js",
94
95
  "react-native-pdf-jsi.podspec",
95
96
  "fabric/",
96
- "README_JSI.md"
97
+ "README.md",
98
+ "README_JSI.md",
99
+ "LICENSE"
97
100
  ],
98
101
  "codegenConfig": {
99
102
  "name": "rnpdf",
@@ -103,7 +106,9 @@
103
106
  "javaPackageName": "org.wonday.pdf"
104
107
  },
105
108
  "ios": {
106
- "componentProvider": "RNPDFPdfView"
109
+ "componentProvider": {
110
+ "RNPDFPdfView": "RNPDFPdfView"
111
+ }
107
112
  }
108
113
  }
109
114
  }
package/src/PDFJSI.js CHANGED
@@ -75,18 +75,23 @@ class PDFJSIManager {
75
75
  this.cacheMetrics = new Map();
76
76
  this.initializationPromise = null;
77
77
 
78
- this.initializeJSI();
78
+ // Don't initialize JSI in constructor - it will be initialized lazily when first accessed
79
+ // This prevents hooks from being called before React is ready
79
80
  }
80
81
 
81
82
  /**
82
83
  * Initialize JSI availability check
84
+ * This is now called lazily when first accessed, not at module load
83
85
  */
84
86
  async initializeJSI() {
85
87
  if (this.initializationPromise) {
86
88
  return this.initializationPromise;
87
89
  }
88
90
 
89
- this.initializationPromise = this.checkJSIAvailability();
91
+ // Defer initialization to prevent hooks from being called before React is ready
92
+ this.initializationPromise = Promise.resolve().then(() => {
93
+ return this.checkJSIAvailability();
94
+ });
90
95
  return this.initializationPromise;
91
96
  }
92
97
 
@@ -814,8 +819,28 @@ class PDFJSIManager {
814
819
  }
815
820
  }
816
821
 
817
- // Create singleton instance
818
- const pdfJSIManager = new PDFJSIManager();
822
+ // Create singleton instance lazily to prevent initialization before React is ready
823
+ let pdfJSIManagerInstance = null;
824
+
825
+ const getPDFJSIManager = () => {
826
+ if (!pdfJSIManagerInstance) {
827
+ pdfJSIManagerInstance = new PDFJSIManager();
828
+ }
829
+ return pdfJSIManagerInstance;
830
+ };
831
+
832
+ // Export a proxy that lazy-loads the instance
833
+ const pdfJSIManager = new Proxy({}, {
834
+ get(target, prop) {
835
+ const instance = getPDFJSIManager();
836
+ const value = instance[prop];
837
+ // If it's a method, bind it to the instance
838
+ if (typeof value === 'function') {
839
+ return value.bind(instance);
840
+ }
841
+ return value;
842
+ }
843
+ });
819
844
 
820
845
  export default pdfJSIManager;
821
846
 
@@ -12,10 +12,23 @@ const AnalyticsPanel = ({
12
12
  analytics,
13
13
  licenseInfo,
14
14
  }) => {
15
- const timeInMinutes = Math.round(analytics.timeSpent / 60);
15
+ // Handle null/undefined analytics with defaults
16
+ const safeAnalytics = analytics || {
17
+ timeSpent: 0,
18
+ pagesRead: [],
19
+ totalPages: 0,
20
+ percentage: 0,
21
+ sessions: 0,
22
+ currentPage: 1,
23
+ lastRead: null,
24
+ };
25
+
26
+ const timeInMinutes = Math.round((safeAnalytics.timeSpent || 0) / 60);
27
+ const pagesReadArray = Array.isArray(safeAnalytics.pagesRead) ? safeAnalytics.pagesRead : [];
28
+ const totalPages = safeAnalytics.totalPages || 0;
16
29
  const readingSpeed =
17
- timeInMinutes > 0 ? analytics.pagesRead.length / timeInMinutes : 0;
18
- const remainingPages = analytics.totalPages - analytics.pagesRead.length;
30
+ timeInMinutes > 0 ? pagesReadArray.length / timeInMinutes : 0;
31
+ const remainingPages = totalPages - pagesReadArray.length;
19
32
  const estimatedTimeRemaining =
20
33
  readingSpeed > 0 ? Math.round(remainingPages / readingSpeed) : 0;
21
34
 
@@ -34,13 +47,13 @@ const AnalyticsPanel = ({
34
47
  <View style={styles.progressSection}>
35
48
  <View style={styles.progressRing}>
36
49
  <Text style={styles.progressPercent}>
37
- {analytics.percentage}%
50
+ {safeAnalytics.percentage || 0}%
38
51
  </Text>
39
52
  <Text style={styles.progressLabel}>Complete</Text>
40
53
  </View>
41
54
  <View style={styles.progressStats}>
42
55
  <Text style={styles.statValue}>
43
- {analytics.pagesRead.length}/{analytics.totalPages}
56
+ {pagesReadArray.length}/{totalPages}
44
57
  </Text>
45
58
  <Text style={styles.statLabel}>Pages Read</Text>
46
59
  </View>
@@ -64,7 +77,7 @@ const AnalyticsPanel = ({
64
77
 
65
78
  <View style={styles.statCard}>
66
79
  <Text style={styles.statIcon}>🔄</Text>
67
- <Text style={styles.statCardValue}>{analytics.sessions}</Text>
80
+ <Text style={styles.statCardValue}>{safeAnalytics.sessions || 0}</Text>
68
81
  <Text style={styles.statCardLabel}>Sessions</Text>
69
82
  </View>
70
83
 
@@ -81,11 +94,11 @@ const AnalyticsPanel = ({
81
94
  <View style={styles.sessionCard}>
82
95
  <Text style={styles.sessionTitle}>Current Session</Text>
83
96
  <Text style={styles.sessionInfo}>
84
- Page {analytics.currentPage} of {analytics.totalPages}
97
+ Page {safeAnalytics.currentPage || 1} of {totalPages}
85
98
  </Text>
86
- {analytics.lastRead && (
99
+ {safeAnalytics.lastRead && (
87
100
  <Text style={styles.sessionTime}>
88
- Last read: {new Date(analytics.lastRead).toLocaleTimeString()}
101
+ Last read: {new Date(safeAnalytics.lastRead).toLocaleTimeString()}
89
102
  </Text>
90
103
  )}
91
104
  </View>
@@ -10,8 +10,7 @@
10
10
  * OPTIMIZATION: Memoized analytics for 95% faster repeated calls
11
11
  */
12
12
 
13
- import bookmarkManager from '../bookmarks/BookmarkManager';
14
- import licenseManager from '../license/LicenseManager';
13
+ import bookmarkManager from './BookmarkManager';
15
14
  import MemoizedAnalytics from '../utils/MemoizedAnalytics';
16
15
 
17
16
  /**
@@ -28,9 +27,6 @@ export class AnalyticsManager {
28
27
  * Initialize analytics
29
28
  */
30
29
  async initialize() {
31
- // Check Pro license
32
- licenseManager.requirePro('Reading Analytics');
33
-
34
30
  if (!this.initialized) {
35
31
  await bookmarkManager.initialize();
36
32
  this.initialized = true;
@@ -11,7 +11,6 @@
11
11
  */
12
12
 
13
13
  import AsyncStorage from '@react-native-async-storage/async-storage';
14
- import licenseManager, { ProFeature } from '../license/LicenseManager';
15
14
 
16
15
  const STORAGE_KEY = '@react-native-pdf-jsi/bookmarks';
17
16
  const PROGRESS_KEY = '@react-native-pdf-jsi/progress';
@@ -94,10 +93,7 @@ export class BookmarkManager {
94
93
  async createBookmark(pdfId, bookmark) {
95
94
  await this.initialize();
96
95
 
97
- // Check if using Pro features (colors)
98
- if (bookmark.color && bookmark.color !== '#000000') {
99
- licenseManager.requirePro('Bookmark Colors');
100
- }
96
+ // All features enabled by default
101
97
 
102
98
  const newBookmark = {
103
99
  id: this.generateId(),
@@ -255,11 +251,7 @@ export class BookmarkManager {
255
251
  async updateProgress(pdfId, progressData) {
256
252
  await this.initialize();
257
253
 
258
- // Basic progress tracking is free
259
- // Advanced analytics require Pro
260
- if (progressData.timeSpent || progressData.sessions) {
261
- licenseManager.requirePro('Reading Progress Tracking');
262
- }
254
+ // All features enabled by default
263
255
 
264
256
  const currentProgress = this.progress.get(pdfId) || {
265
257
  pdfId,
@@ -16,7 +16,20 @@ import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
16
16
  const { PDFJSIManager } = NativeModules;
17
17
 
18
18
  // Event emitter for progress updates
19
- const eventEmitter = PDFJSIManager ? new NativeEventEmitter(PDFJSIManager) : null;
19
+ // Safely create event emitter only if the module supports event listeners
20
+ let eventEmitter = null;
21
+ if (PDFJSIManager) {
22
+ try {
23
+ // Check if NativeEventEmitter is available and the module supports events
24
+ if (typeof NativeEventEmitter !== 'undefined' &&
25
+ (typeof PDFJSIManager.addListener === 'function' || typeof PDFJSIManager.removeListeners === 'function')) {
26
+ eventEmitter = new NativeEventEmitter(PDFJSIManager);
27
+ }
28
+ } catch (error) {
29
+ console.warn('[CacheManager] Failed to create NativeEventEmitter:', error);
30
+ eventEmitter = null;
31
+ }
32
+ }
20
33
 
21
34
  class CacheManager {
22
35
  constructor() {
@@ -13,7 +13,6 @@
13
13
 
14
14
  import { NativeModules, Platform, Share } from 'react-native';
15
15
  import PDFTextExtractor from '../utils/PDFTextExtractor';
16
- import licenseManager, { ProFeature } from '../license/LicenseManager';
17
16
 
18
17
  const { PDFExporter } = NativeModules;
19
18
 
@@ -167,9 +166,7 @@ export class ExportManager {
167
166
  * @returns {Promise<Array>} Array of image paths
168
167
  */
169
168
  async exportToImages(filePath, options = {}) {
170
- // ⚠️ PRO FEATURE - Requires license
171
- licenseManager.requirePro('Export to Images');
172
-
169
+ // All features enabled by default
173
170
  const {
174
171
  pages = null, // null = all pages
175
172
  format = ExportFormat.JPEG,
@@ -230,11 +227,7 @@ export class ExportManager {
230
227
  });
231
228
 
232
229
  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
-
230
+ // All features enabled by default
238
231
  if (this.isNativeAvailable) {
239
232
  console.log('📱 [ExportManager] Calling native PDFExporter.exportPageToImage...');
240
233
  console.log('📱 [ExportManager] Parameters:', {
@@ -277,9 +270,7 @@ export class ExportManager {
277
270
  * @returns {Promise<string>} Path to merged PDF
278
271
  */
279
272
  async mergePDFs(filePaths, outputPath = null) {
280
- // ⚠️ PRO FEATURE - Requires license
281
- licenseManager.requirePro('PDF Operations');
282
-
273
+ // All features enabled by default
283
274
  console.log(`📤 ExportManager: Merging ${filePaths.length} PDFs...`);
284
275
 
285
276
  try {
@@ -313,10 +304,7 @@ export class ExportManager {
313
304
  });
314
305
 
315
306
  try {
316
- console.log('🔑 [ExportManager] Checking license for PDF Operations...');
317
- licenseManager.requirePro('PDF Operations');
318
- console.log('✅ [ExportManager] License check passed');
319
-
307
+ // All features enabled by default
320
308
  if (this.isNativeAvailable) {
321
309
  console.log('📱 [ExportManager] Calling native PDFExporter.splitPDF...');
322
310
  console.log('📱 [ExportManager] Ranges:', JSON.stringify(ranges));
@@ -354,10 +342,7 @@ export class ExportManager {
354
342
  });
355
343
 
356
344
  try {
357
- console.log('🔑 [ExportManager] Checking license for PDF Operations...');
358
- licenseManager.requirePro('PDF Operations');
359
- console.log('✅ [ExportManager] License check passed');
360
-
345
+ // All features enabled by default
361
346
  if (this.isNativeAvailable) {
362
347
  // Convert to 0-indexed
363
348
  const pageIndices = pages.map(p => p - 1);
@@ -157,16 +157,6 @@ export const validatePageNumber = (page, totalPages) => {
157
157
  }
158
158
  };
159
159
 
160
- export const validateLicense = (licenseManager, featureName) => {
161
- if (!licenseManager.isProActive()) {
162
- throw new PDFError(
163
- `${featureName} requires a Pro license`,
164
- ErrorCodes.LICENSE_REQUIRED,
165
- {feature: featureName}
166
- );
167
- }
168
- };
169
-
170
160
  export default {
171
161
  PDFError,
172
162
  ErrorCodes,
@@ -175,5 +165,4 @@ export default {
175
165
  withErrorHandling,
176
166
  validatePDFPath,
177
167
  validatePageNumber,
178
- validateLicense,
179
168
  };