react-native-pdf-jsi 1.0.1

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 (91) hide show
  1. package/DoubleTapView.js +125 -0
  2. package/INTEGRATION_GUIDE.md +419 -0
  3. package/LICENSE +21 -0
  4. package/PdfManager.js +26 -0
  5. package/PdfPageView.js +53 -0
  6. package/PdfView.js +421 -0
  7. package/PdfViewFlatList.js +30 -0
  8. package/PinchZoomView.js +125 -0
  9. package/README.md +693 -0
  10. package/README_JSI.md +348 -0
  11. package/android/.gradle/5.6.1/fileChanges/last-build.bin +0 -0
  12. package/android/.gradle/5.6.1/fileHashes/fileHashes.lock +0 -0
  13. package/android/.gradle/5.6.1/gc.properties +0 -0
  14. package/android/.gradle/8.5/checksums/checksums.lock +0 -0
  15. package/android/.gradle/8.5/checksums/md5-checksums.bin +0 -0
  16. package/android/.gradle/8.5/checksums/sha1-checksums.bin +0 -0
  17. package/android/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock +0 -0
  18. package/android/.gradle/8.5/dependencies-accessors/gc.properties +0 -0
  19. package/android/.gradle/8.5/executionHistory/executionHistory.lock +0 -0
  20. package/android/.gradle/8.5/fileChanges/last-build.bin +0 -0
  21. package/android/.gradle/8.5/fileHashes/fileHashes.lock +0 -0
  22. package/android/.gradle/8.5/gc.properties +0 -0
  23. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  24. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  25. package/android/.gradle/vcs-1/gc.properties +0 -0
  26. package/android/build.gradle +198 -0
  27. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  28. package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  29. package/android/gradle.properties +2 -0
  30. package/android/gradlew +249 -0
  31. package/android/gradlew.bat +92 -0
  32. package/android/project.properties +12 -0
  33. package/android/src/main/AndroidManifest.xml +4 -0
  34. package/android/src/main/cpp/Android.mk +50 -0
  35. package/android/src/main/cpp/CMakeLists.txt +76 -0
  36. package/android/src/main/cpp/PDFJSI.cpp +190 -0
  37. package/android/src/main/cpp/PDFJSI.h +95 -0
  38. package/android/src/main/cpp/PDFJSIBridge.cpp +32 -0
  39. package/android/src/main/cpp/PDFJSIModule.cpp +31 -0
  40. package/android/src/main/java/org/wonday/pdf/EnhancedPdfJSIBridge.java +281 -0
  41. package/android/src/main/java/org/wonday/pdf/PDFJSIManager.java +317 -0
  42. package/android/src/main/java/org/wonday/pdf/PDFJSIModule.java +189 -0
  43. package/android/src/main/java/org/wonday/pdf/PdfManager.java +180 -0
  44. package/android/src/main/java/org/wonday/pdf/PdfView.java +505 -0
  45. package/android/src/main/java/org/wonday/pdf/RNPDFPackage.java +43 -0
  46. package/android/src/main/java/org/wonday/pdf/events/TopChangeEvent.java +26 -0
  47. package/android/src/main/jniLibs/arm64-v8a/libpdfjsi.so +0 -0
  48. package/android/src/main/jniLibs/armeabi-v7a/libpdfjsi.so +0 -0
  49. package/android/src/main/jniLibs/x86/libpdfjsi.so +0 -0
  50. package/android/src/main/jniLibs/x86_64/libpdfjsi.so +0 -0
  51. package/android/src/paper/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.java +92 -0
  52. package/android/src/paper/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.java +35 -0
  53. package/fabric/RNPDFPdfNativeComponent.js +48 -0
  54. package/index.d.ts +72 -0
  55. package/index.js +603 -0
  56. package/index.js.flow +67 -0
  57. package/ios/RNPDFPdf/PdfManager.h +23 -0
  58. package/ios/RNPDFPdf/PdfManager.mm +152 -0
  59. package/ios/RNPDFPdf/RNPDFPdfPageView.h +21 -0
  60. package/ios/RNPDFPdf/RNPDFPdfPageView.mm +185 -0
  61. package/ios/RNPDFPdf/RNPDFPdfPageViewManager.h +18 -0
  62. package/ios/RNPDFPdf/RNPDFPdfPageViewManager.mm +30 -0
  63. package/ios/RNPDFPdf/RNPDFPdfView.h +63 -0
  64. package/ios/RNPDFPdf/RNPDFPdfView.mm +1092 -0
  65. package/ios/RNPDFPdf/RNPDFPdfViewManager.h +18 -0
  66. package/ios/RNPDFPdf/RNPDFPdfViewManager.mm +68 -0
  67. package/ios/RNPDFPdf.xcodeproj/project.pbxproj +321 -0
  68. package/package.json +78 -0
  69. package/react-native-pdf.podspec +31 -0
  70. package/src/EnhancedPdfView.js +362 -0
  71. package/src/PDFJSI.js +519 -0
  72. package/src/examples/PDFJSIExample.js +296 -0
  73. package/src/hooks/usePDFJSI.js +346 -0
  74. package/src/index.js +32 -0
  75. package/windows/RCTPdf/PropertySheet.props +16 -0
  76. package/windows/RCTPdf/RCTPdf.def +3 -0
  77. package/windows/RCTPdf/RCTPdf.vcxproj +180 -0
  78. package/windows/RCTPdf/RCTPdf.vcxproj.filters +38 -0
  79. package/windows/RCTPdf/RCTPdfControl.cpp +667 -0
  80. package/windows/RCTPdf/RCTPdfControl.h +119 -0
  81. package/windows/RCTPdf/RCTPdfControl.idl +10 -0
  82. package/windows/RCTPdf/RCTPdfControl.xaml +33 -0
  83. package/windows/RCTPdf/RCTPdfViewManager.cpp +69 -0
  84. package/windows/RCTPdf/RCTPdfViewManager.h +51 -0
  85. package/windows/RCTPdf/ReactPackageProvider.cpp +15 -0
  86. package/windows/RCTPdf/ReactPackageProvider.h +16 -0
  87. package/windows/RCTPdf/ReactPackageProvider.idl +9 -0
  88. package/windows/RCTPdf/packages.config +4 -0
  89. package/windows/RCTPdf/pch.cpp +1 -0
  90. package/windows/RCTPdf/pch.h +31 -0
  91. package/windows/README.md +21 -0
@@ -0,0 +1,1092 @@
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
+ #import "RNPDFPdfView.h"
10
+
11
+ #import <Foundation/Foundation.h>
12
+ #import <QuartzCore/QuartzCore.h>
13
+ #import <PDFKit/PDFKit.h>
14
+
15
+ #if __has_include(<React/RCTAssert.h>)
16
+ #import <React/RCTBridgeModule.h>
17
+ #import <React/RCTEventDispatcher.h>
18
+ #import <React/UIView+React.h>
19
+ #import <React/RCTLog.h>
20
+ #import <React/RCTBlobManager.h>
21
+ #else
22
+ #import "RCTBridgeModule.h"
23
+ #import "RCTEventDispatcher.h"
24
+ #import "UIView+React.h"
25
+ #import "RCTLog.h"
26
+ #import <RCTBlobManager.h">
27
+ #endif
28
+
29
+ #ifdef RCT_NEW_ARCH_ENABLED
30
+ #import <React/RCTConversions.h>
31
+ #import <React/RCTFabricComponentsPlugins.h>
32
+ #import <react/renderer/components/rnpdf/ComponentDescriptors.h>
33
+ #import <react/renderer/components/rnpdf/Props.h>
34
+ #import <react/renderer/components/rnpdf/RCTComponentViewHelpers.h>
35
+
36
+ // Some RN private method hacking below similar to how it is done in RNScreens:
37
+ // https://github.com/software-mansion/react-native-screens/blob/90e548739f35b5ded2524a9d6410033fc233f586/ios/RNSScreenStackHeaderConfig.mm#L30
38
+ @interface RCTBridge (Private)
39
+ + (RCTBridge *)currentBridge;
40
+ @end
41
+
42
+ #endif
43
+
44
+ #ifndef __OPTIMIZE__
45
+ // only output log when debug
46
+ #define DLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
47
+ #else
48
+ #define DLog( s, ... )
49
+ #endif
50
+
51
+ // output log both debug and release
52
+ #define RLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
53
+
54
+ const float MAX_SCALE = 3.0f;
55
+ const float MIN_SCALE = 1.0f;
56
+
57
+ @interface RNPDFPdfView() <PDFDocumentDelegate, PDFViewDelegate
58
+ #ifdef RCT_NEW_ARCH_ENABLED
59
+ , RCTRNPDFPdfViewViewProtocol
60
+ #endif
61
+ >
62
+ @end
63
+
64
+ @implementation RNPDFPdfView
65
+ {
66
+ RCTBridge *_bridge;
67
+ PDFDocument *_pdfDocument;
68
+ PDFView *_pdfView;
69
+ PDFOutline *root;
70
+ float _fixScaleFactor;
71
+ bool _initialed;
72
+ NSArray<NSString *> *_changedProps;
73
+ UITapGestureRecognizer *_doubleTapRecognizer;
74
+ UITapGestureRecognizer *_singleTapRecognizer;
75
+ UIPinchGestureRecognizer *_pinchRecognizer;
76
+ UILongPressGestureRecognizer *_longPressRecognizer;
77
+ UITapGestureRecognizer *_doubleTapEmptyRecognizer;
78
+
79
+ // Enhanced progressive loading properties
80
+ BOOL _enableCaching;
81
+ BOOL _enablePreloading;
82
+ int _preloadRadius;
83
+ BOOL _enableTextSelection;
84
+ BOOL _showPerformanceMetrics;
85
+ int _cacheSize;
86
+ int _renderQuality;
87
+
88
+ // Performance tracking
89
+ CFAbsoluteTime _loadStartTime;
90
+ CFAbsoluteTime _loadTime;
91
+ int _pageCount;
92
+ NSMutableDictionary *_pageCache;
93
+ NSMutableSet *_preloadedPages;
94
+ NSMutableDictionary *_performanceMetrics;
95
+ NSMutableDictionary *_searchCache;
96
+ NSString *_currentPdfId;
97
+ NSOperationQueue *_preloadQueue;
98
+ }
99
+
100
+ #ifdef RCT_NEW_ARCH_ENABLED
101
+
102
+ using namespace facebook::react;
103
+
104
+ + (ComponentDescriptorProvider)componentDescriptorProvider
105
+ {
106
+ return concreteComponentDescriptorProvider<RNPDFPdfViewComponentDescriptor>();
107
+ }
108
+
109
+ // Needed because of this: https://github.com/facebook/react-native/pull/37274
110
+ + (void)load
111
+ {
112
+ [super load];
113
+ }
114
+
115
+ - (instancetype)initWithFrame:(CGRect)frame
116
+ {
117
+ if (self = [super initWithFrame:frame]) {
118
+ static const auto defaultProps = std::make_shared<const RNPDFPdfViewProps>();
119
+ _props = defaultProps;
120
+ [self initCommonProps];
121
+ }
122
+ return self;
123
+ }
124
+
125
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
126
+ {
127
+ const auto &newProps = *std::static_pointer_cast<const RNPDFPdfViewProps>(props);
128
+ NSMutableArray<NSString *> *updatedPropNames = [NSMutableArray new];
129
+ if (_path != RCTNSStringFromStringNilIfEmpty(newProps.path)) {
130
+ _path = RCTNSStringFromStringNilIfEmpty(newProps.path);
131
+ [updatedPropNames addObject:@"path"];
132
+ }
133
+ if (_page != newProps.page) {
134
+ _page = newProps.page;
135
+ [updatedPropNames addObject:@"page"];
136
+ }
137
+ if (_scale != newProps.scale) {
138
+ _scale = newProps.scale;
139
+ [updatedPropNames addObject:@"scale"];
140
+ }
141
+ if (_minScale != newProps.minScale) {
142
+ _minScale = newProps.minScale;
143
+ [updatedPropNames addObject:@"minScale"];
144
+ }
145
+ if (_maxScale != newProps.maxScale) {
146
+ _maxScale = newProps.maxScale;
147
+ [updatedPropNames addObject:@"maxScale"];
148
+ }
149
+ if (_horizontal != newProps.horizontal) {
150
+ _horizontal = newProps.horizontal;
151
+ [updatedPropNames addObject:@"horizontal"];
152
+ }
153
+ if (_enablePaging != newProps.enablePaging) {
154
+ _enablePaging = newProps.enablePaging;
155
+ [updatedPropNames addObject:@"enablePaging"];
156
+ }
157
+ if (_enableRTL != newProps.enableRTL) {
158
+ _enableRTL = newProps.enableRTL;
159
+ [updatedPropNames addObject:@"enableRTL"];
160
+ }
161
+ if (_enableAnnotationRendering != newProps.enableAnnotationRendering) {
162
+ _enableAnnotationRendering = newProps.enableAnnotationRendering;
163
+ [updatedPropNames addObject:@"enableAnnotationRendering"];
164
+ }
165
+ if (_enableDoubleTapZoom != newProps.enableDoubleTapZoom) {
166
+ _enableDoubleTapZoom = newProps.enableDoubleTapZoom;
167
+ [updatedPropNames addObject:@"enableDoubleTapZoom"];
168
+ }
169
+ if (_fitPolicy != newProps.fitPolicy) {
170
+ _fitPolicy = newProps.fitPolicy;
171
+ [updatedPropNames addObject:@"fitPolicy"];
172
+ }
173
+ if (_spacing != newProps.spacing) {
174
+ _spacing = newProps.spacing;
175
+ [updatedPropNames addObject:@"spacing"];
176
+ }
177
+ if (_password != RCTNSStringFromStringNilIfEmpty(newProps.password)) {
178
+ _password = RCTNSStringFromStringNilIfEmpty(newProps.password);
179
+ [updatedPropNames addObject:@"password"];
180
+ }
181
+ if (_singlePage != newProps.singlePage) {
182
+ _singlePage = newProps.singlePage;
183
+ [updatedPropNames addObject:@"singlePage"];
184
+ }
185
+ if (_showsHorizontalScrollIndicator != newProps.showsHorizontalScrollIndicator) {
186
+ _showsHorizontalScrollIndicator = newProps.showsHorizontalScrollIndicator;
187
+ [updatedPropNames addObject:@"showsHorizontalScrollIndicator"];
188
+ }
189
+ if (_showsVerticalScrollIndicator != newProps.showsVerticalScrollIndicator) {
190
+ _showsVerticalScrollIndicator = newProps.showsVerticalScrollIndicator;
191
+ [updatedPropNames addObject:@"showsVerticalScrollIndicator"];
192
+ }
193
+
194
+ if (_scrollEnabled != newProps.scrollEnabled) {
195
+ _scrollEnabled = newProps.scrollEnabled;
196
+ [updatedPropNames addObject:@"scrollEnabled"];
197
+ }
198
+
199
+ [super updateProps:props oldProps:oldProps];
200
+ [self didSetProps:updatedPropNames];
201
+ }
202
+
203
+ // already added in case https://github.com/facebook/react-native/pull/35378 has been merged
204
+ - (BOOL)shouldBeRecycled
205
+ {
206
+ return NO;
207
+ }
208
+
209
+ - (void)prepareForRecycle
210
+ {
211
+ [super prepareForRecycle];
212
+
213
+ [_pdfView removeFromSuperview];
214
+ _pdfDocument = Nil;
215
+ _pdfView = Nil;
216
+ //Remove notifications
217
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"PDFViewDocumentChangedNotification" object:nil];
218
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"PDFViewPageChangedNotification" object:nil];
219
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"PDFViewScaleChangedNotification" object:nil];
220
+
221
+ // remove old recognizers before adding new ones
222
+ [self removeGestureRecognizer:_doubleTapRecognizer];
223
+ [self removeGestureRecognizer:_singleTapRecognizer];
224
+ [self removeGestureRecognizer:_pinchRecognizer];
225
+ [self removeGestureRecognizer:_longPressRecognizer];
226
+ [self removeGestureRecognizer:_doubleTapEmptyRecognizer];
227
+
228
+ [self initCommonProps];
229
+ }
230
+
231
+ - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics
232
+ {
233
+ // Fabric equivalent of `reactSetFrame` method
234
+ [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
235
+ _pdfView.frame = CGRectMake(0, 0, layoutMetrics.frame.size.width, layoutMetrics.frame.size.height);
236
+
237
+ NSMutableArray *mProps = [_changedProps mutableCopy];
238
+ if (_initialed) {
239
+ [mProps removeObject:@"path"];
240
+ }
241
+ _initialed = YES;
242
+
243
+ [self didSetProps:mProps];
244
+ }
245
+
246
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
247
+ {
248
+ RCTRNPDFPdfViewHandleCommand(self, commandName, args);
249
+ }
250
+
251
+ - (void)setNativePage:(NSInteger)page
252
+ {
253
+ _page = page;
254
+ [self didSetProps:[NSArray arrayWithObject:@"page"]];
255
+ }
256
+
257
+ #endif
258
+
259
+ - (instancetype)initWithBridge:(RCTBridge *)bridge
260
+ {
261
+ self = [super init];
262
+ if (self) {
263
+ _bridge = bridge;
264
+ [self initCommonProps];
265
+ }
266
+
267
+ return self;
268
+ }
269
+
270
+ - (void)initCommonProps
271
+ {
272
+ _page = 1;
273
+ _scale = 1;
274
+ _minScale = MIN_SCALE;
275
+ _maxScale = MAX_SCALE;
276
+ _horizontal = NO;
277
+ _enablePaging = NO;
278
+ _enableRTL = NO;
279
+ _enableAnnotationRendering = YES;
280
+ _enableDoubleTapZoom = YES;
281
+ _fitPolicy = 2;
282
+ _spacing = 10;
283
+ _singlePage = NO;
284
+ _showsHorizontalScrollIndicator = YES;
285
+ _showsVerticalScrollIndicator = YES;
286
+ _scrollEnabled = YES;
287
+
288
+ // Enhanced properties
289
+ _enableCaching = YES;
290
+ _enablePreloading = YES;
291
+ _preloadRadius = 3;
292
+ _enableTextSelection = NO;
293
+ _showPerformanceMetrics = NO;
294
+ _cacheSize = 32768; // 32MB
295
+ _renderQuality = 2; // High quality
296
+
297
+ // Initialize enhanced features
298
+ _pageCache = [NSMutableDictionary dictionary];
299
+ _preloadedPages = [NSMutableSet set];
300
+ _performanceMetrics = [NSMutableDictionary dictionary];
301
+ _searchCache = [NSMutableDictionary dictionary];
302
+
303
+ // Create preload queue
304
+ _preloadQueue = [[NSOperationQueue alloc] init];
305
+ _preloadQueue.maxConcurrentOperationCount = 3;
306
+ _preloadQueue.qualityOfService = NSQualityOfServiceBackground;
307
+
308
+ // init and config PDFView
309
+ _pdfView = [[PDFView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
310
+ _pdfView.displayMode = kPDFDisplaySinglePageContinuous;
311
+ _pdfView.autoScales = YES;
312
+ _pdfView.displaysPageBreaks = YES;
313
+ _pdfView.displayBox = kPDFDisplayBoxCropBox;
314
+ _pdfView.backgroundColor = [UIColor clearColor];
315
+
316
+ _fixScaleFactor = -1.0f;
317
+ _initialed = NO;
318
+ _changedProps = NULL;
319
+
320
+ [self addSubview:_pdfView];
321
+
322
+
323
+ // register notification
324
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
325
+ [center addObserver:self selector:@selector(onDocumentChanged:) name:PDFViewDocumentChangedNotification object:_pdfView];
326
+ [center addObserver:self selector:@selector(onPageChanged:) name:PDFViewPageChangedNotification object:_pdfView];
327
+ [center addObserver:self selector:@selector(onScaleChanged:) name:PDFViewScaleChangedNotification object:_pdfView];
328
+
329
+ [[_pdfView document] setDelegate: self];
330
+ [_pdfView setDelegate: self];
331
+
332
+ // Disable built-in double tap, so as not to conflict with custom recognizers.
333
+ for (UIGestureRecognizer *recognizer in _pdfView.gestureRecognizers) {
334
+ if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
335
+ recognizer.enabled = NO;
336
+ }
337
+ }
338
+
339
+ [self bindTap];
340
+ }
341
+
342
+ - (void)PDFViewWillClickOnLink:(PDFView *)sender withURL:(NSURL *)url
343
+ {
344
+ NSString *_url = url.absoluteString;
345
+ [self notifyOnChangeWithMessage:
346
+ [[NSString alloc] initWithString:
347
+ [NSString stringWithFormat:
348
+ @"linkPressed|%s", _url.UTF8String]]];
349
+ }
350
+
351
+ - (void)didSetProps:(NSArray<NSString *> *)changedProps
352
+ {
353
+ if (!_initialed) {
354
+
355
+ _changedProps = changedProps;
356
+
357
+ } else {
358
+
359
+ if ([changedProps containsObject:@"path"]) {
360
+
361
+
362
+ if (_pdfDocument != Nil) {
363
+ //Release old doc
364
+ _pdfDocument = Nil;
365
+ }
366
+
367
+ if ([_path hasPrefix:@"blob:"]) {
368
+ RCTBlobManager *blobManager = [
369
+ #ifdef RCT_NEW_ARCH_ENABLED
370
+ [RCTBridge currentBridge]
371
+ #else
372
+ _bridge
373
+ #endif // RCT_NEW_ARCH_ENABLED
374
+ moduleForName:@"BlobModule"];
375
+ NSURL *blobURL = [NSURL URLWithString:_path];
376
+ NSData *blobData = [blobManager resolveURL:blobURL];
377
+ if (blobData != nil) {
378
+ _pdfDocument = [[PDFDocument alloc] initWithData:blobData];
379
+ }
380
+ } else {
381
+
382
+ // decode file path
383
+ _path = (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapes(NULL, (CFStringRef)_path, CFSTR(""));
384
+ NSURL *fileURL = [NSURL fileURLWithPath:_path];
385
+ _pdfDocument = [[PDFDocument alloc] initWithURL:fileURL];
386
+ }
387
+
388
+ if (_pdfDocument) {
389
+
390
+ //check need password or not
391
+ if (_pdfDocument.isLocked && ![_pdfDocument unlockWithPassword:_password]) {
392
+
393
+ [self notifyOnChangeWithMessage:@"error|Password required or incorrect password."];
394
+
395
+ _pdfDocument = Nil;
396
+ return;
397
+ }
398
+
399
+ _pdfView.document = _pdfDocument;
400
+ } else {
401
+
402
+ [self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"error|Load pdf failed. path=%s",_path.UTF8String]]];
403
+
404
+ _pdfDocument = Nil;
405
+ return;
406
+ }
407
+ }
408
+
409
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"spacing"])) {
410
+ if (_horizontal) {
411
+ _pdfView.pageBreakMargins = UIEdgeInsetsMake(0,_spacing,0,0);
412
+ if (_spacing==0) {
413
+ if (@available(iOS 12.0, *)) {
414
+ _pdfView.pageShadowsEnabled = NO;
415
+ }
416
+ } else {
417
+ if (@available(iOS 12.0, *)) {
418
+ _pdfView.pageShadowsEnabled = YES;
419
+ }
420
+ }
421
+ } else {
422
+ _pdfView.pageBreakMargins = UIEdgeInsetsMake(0,0,_spacing,0);
423
+ if (_spacing==0) {
424
+ if (@available(iOS 12.0, *)) {
425
+ _pdfView.pageShadowsEnabled = NO;
426
+ }
427
+ } else {
428
+ if (@available(iOS 12.0, *)) {
429
+ _pdfView.pageShadowsEnabled = YES;
430
+ }
431
+ }
432
+ }
433
+ }
434
+
435
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"enableRTL"])) {
436
+ _pdfView.displaysRTL = _enableRTL;
437
+ }
438
+
439
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"enableAnnotationRendering"])) {
440
+ if (!_enableAnnotationRendering) {
441
+ for (unsigned long i=0; i<_pdfView.document.pageCount; i++) {
442
+ PDFPage *pdfPage = [_pdfView.document pageAtIndex:i];
443
+ for (unsigned long j=0; j<pdfPage.annotations.count; j++) {
444
+ pdfPage.annotations[j].shouldDisplay = _enableAnnotationRendering;
445
+ }
446
+ }
447
+ }
448
+ }
449
+
450
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"fitPolicy"] || [changedProps containsObject:@"minScale"] || [changedProps containsObject:@"maxScale"])) {
451
+
452
+ PDFPage *pdfPage = _pdfView.currentPage ? _pdfView.currentPage : [_pdfDocument pageAtIndex:_pdfDocument.pageCount-1];
453
+ CGRect pdfPageRect = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
454
+
455
+ // some pdf with rotation, then adjust it
456
+ if (pdfPage.rotation == 90 || pdfPage.rotation == 270) {
457
+ pdfPageRect = CGRectMake(0, 0, pdfPageRect.size.height, pdfPageRect.size.width);
458
+ }
459
+
460
+ if (_fitPolicy == 0) {
461
+ _fixScaleFactor = self.frame.size.width/pdfPageRect.size.width;
462
+ _pdfView.scaleFactor = _scale * _fixScaleFactor;
463
+ _pdfView.minScaleFactor = _fixScaleFactor*_minScale;
464
+ _pdfView.maxScaleFactor = _fixScaleFactor*_maxScale;
465
+ } else if (_fitPolicy == 1) {
466
+ _fixScaleFactor = self.frame.size.height/pdfPageRect.size.height;
467
+ _pdfView.scaleFactor = _scale * _fixScaleFactor;
468
+ _pdfView.minScaleFactor = _fixScaleFactor*_minScale;
469
+ _pdfView.maxScaleFactor = _fixScaleFactor*_maxScale;
470
+ } else {
471
+ float pageAspect = pdfPageRect.size.width/pdfPageRect.size.height;
472
+ float reactViewAspect = self.frame.size.width/self.frame.size.height;
473
+ if (reactViewAspect>pageAspect) {
474
+ _fixScaleFactor = self.frame.size.height/pdfPageRect.size.height;
475
+ _pdfView.scaleFactor = _scale * _fixScaleFactor;
476
+ _pdfView.minScaleFactor = _fixScaleFactor*_minScale;
477
+ _pdfView.maxScaleFactor = _fixScaleFactor*_maxScale;
478
+ } else {
479
+ _fixScaleFactor = self.frame.size.width/pdfPageRect.size.width;
480
+ _pdfView.scaleFactor = _scale * _fixScaleFactor;
481
+ _pdfView.minScaleFactor = _fixScaleFactor*_minScale;
482
+ _pdfView.maxScaleFactor = _fixScaleFactor*_maxScale;
483
+ }
484
+ }
485
+
486
+ }
487
+
488
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"scale"])) {
489
+ _pdfView.scaleFactor = _scale * _fixScaleFactor;
490
+ if (_pdfView.scaleFactor>_pdfView.maxScaleFactor) _pdfView.scaleFactor = _pdfView.maxScaleFactor;
491
+ if (_pdfView.scaleFactor<_pdfView.minScaleFactor) _pdfView.scaleFactor = _pdfView.minScaleFactor;
492
+ }
493
+
494
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"horizontal"])) {
495
+ if (_horizontal) {
496
+ _pdfView.displayDirection = kPDFDisplayDirectionHorizontal;
497
+ _pdfView.pageBreakMargins = UIEdgeInsetsMake(0,_spacing,0,0);
498
+ } else {
499
+ _pdfView.displayDirection = kPDFDisplayDirectionVertical;
500
+ _pdfView.pageBreakMargins = UIEdgeInsetsMake(0,0,_spacing,0);
501
+ }
502
+ }
503
+
504
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"enablePaging"])) {
505
+ if (_enablePaging) {
506
+ [_pdfView usePageViewController:YES withViewOptions:@{UIPageViewControllerOptionSpineLocationKey:@(UIPageViewControllerSpineLocationMin),UIPageViewControllerOptionInterPageSpacingKey:@(_spacing)}];
507
+ } else {
508
+ [_pdfView usePageViewController:NO withViewOptions:Nil];
509
+ }
510
+ }
511
+
512
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"singlePage"])) {
513
+ if (_singlePage) {
514
+ _pdfView.displayMode = kPDFDisplaySinglePage;
515
+ _pdfView.userInteractionEnabled = NO;
516
+ } else {
517
+ _pdfView.displayMode = kPDFDisplaySinglePageContinuous;
518
+ _pdfView.userInteractionEnabled = YES;
519
+ }
520
+ }
521
+
522
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"showsHorizontalScrollIndicator"] || [changedProps containsObject:@"showsVerticalScrollIndicator"])) {
523
+ [self setScrollIndicators:self horizontal:_showsHorizontalScrollIndicator vertical:_showsVerticalScrollIndicator depth:0];
524
+ }
525
+
526
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"scrollEnabled"])) {
527
+ if (_scrollEnabled) {
528
+ for (UIView *subview in _pdfView.subviews) {
529
+ if ([subview isKindOfClass:[UIScrollView class]]) {
530
+ UIScrollView *scrollView = (UIScrollView *)subview;
531
+ scrollView.scrollEnabled = YES;
532
+ }
533
+ }
534
+ } else {
535
+ for (UIView *subview in _pdfView.subviews) {
536
+ if ([subview isKindOfClass:[UIScrollView class]]) {
537
+ UIScrollView *scrollView = (UIScrollView *)subview;
538
+ scrollView.scrollEnabled = NO;
539
+ }
540
+ }
541
+ }
542
+ }
543
+
544
+ if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"enablePaging"] || [changedProps containsObject:@"horizontal"] || [changedProps containsObject:@"page"])) {
545
+
546
+ PDFPage *pdfPage = [_pdfDocument pageAtIndex:_page-1];
547
+ if (pdfPage && _page == 1) {
548
+ // goToDestination() would be better. However, there is an
549
+ // error in the pointLeftTop computation that often results in
550
+ // scrolling to the middle of the page.
551
+ // Special case workaround to make starting at the first page
552
+ // align acceptably.
553
+ dispatch_async(dispatch_get_main_queue(), ^{
554
+ [self->_pdfView goToRect:CGRectMake(0, NSUIntegerMax, 1, 1) onPage:pdfPage];
555
+ });
556
+ } else if (pdfPage) {
557
+ CGRect pdfPageRect = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
558
+
559
+ // some pdf with rotation, then adjust it
560
+ if (pdfPage.rotation == 90 || pdfPage.rotation == 270) {
561
+ pdfPageRect = CGRectMake(0, 0, pdfPageRect.size.height, pdfPageRect.size.width);
562
+ }
563
+
564
+ CGPoint pointLeftTop = CGPointMake(0, pdfPageRect.size.height);
565
+ PDFDestination *pdfDest = [[PDFDestination alloc] initWithPage:pdfPage atPoint:pointLeftTop];
566
+ [_pdfView goToDestination:pdfDest];
567
+ _pdfView.scaleFactor = _fixScaleFactor*_scale;
568
+ }
569
+ }
570
+
571
+ _pdfView.backgroundColor = [UIColor clearColor];
572
+ [_pdfView layoutDocumentView];
573
+ [self setNeedsDisplay];
574
+ }
575
+ }
576
+
577
+
578
+ - (void)reactSetFrame:(CGRect)frame
579
+ {
580
+ [super reactSetFrame:frame];
581
+ _pdfView.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
582
+
583
+ NSMutableArray *mProps = [_changedProps mutableCopy];
584
+ if (_initialed) {
585
+ [mProps removeObject:@"path"];
586
+ }
587
+ _initialed = YES;
588
+
589
+ [self didSetProps:mProps];
590
+ }
591
+
592
+
593
+ - (void)notifyOnChangeWithMessage:(NSString *)message
594
+ {
595
+ #ifdef RCT_NEW_ARCH_ENABLED
596
+ if (_eventEmitter != nullptr) {
597
+ std::dynamic_pointer_cast<const RNPDFPdfViewEventEmitter>(_eventEmitter)
598
+ ->onChange(RNPDFPdfViewEventEmitter::OnChange{.message = RCTStringFromNSString(message)});
599
+ }
600
+ #else
601
+ _onChange(@{ @"message": message});
602
+ #endif
603
+ }
604
+
605
+ - (void)dealloc{
606
+ [_preloadQueue cancelAllOperations];
607
+ _preloadQueue = nil;
608
+
609
+ // Clear caches
610
+ [_pageCache removeAllObjects];
611
+ [_preloadedPages removeAllObjects];
612
+ [_performanceMetrics removeAllObjects];
613
+ [_searchCache removeAllObjects];
614
+
615
+ _pdfDocument = Nil;
616
+ _pdfView = Nil;
617
+
618
+ //Remove notifications
619
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"PDFViewDocumentChangedNotification" object:nil];
620
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"PDFViewPageChangedNotification" object:nil];
621
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"PDFViewScaleChangedNotification" object:nil];
622
+
623
+ _doubleTapRecognizer = nil;
624
+ _singleTapRecognizer = nil;
625
+ _pinchRecognizer = nil;
626
+ _longPressRecognizer = nil;
627
+ _doubleTapEmptyRecognizer = nil;
628
+ }
629
+
630
+ #pragma mark notification process
631
+ - (void)onDocumentChanged:(NSNotification *)noti
632
+ {
633
+
634
+ if (_pdfDocument) {
635
+
636
+ unsigned long numberOfPages = _pdfDocument.pageCount;
637
+ PDFPage *page = [_pdfDocument pageAtIndex:_pdfDocument.pageCount-1];
638
+ CGSize pageSize = [_pdfView rowSizeForPage:page];
639
+ NSString *jsonString = [self getTableContents];
640
+
641
+ [self notifyOnChangeWithMessage:
642
+ [[NSString alloc] initWithString:[NSString stringWithFormat:@"loadComplete|%lu|%f|%f|%@", numberOfPages, pageSize.width, pageSize.height,jsonString]]];
643
+ }
644
+
645
+ }
646
+
647
+ -(NSString *) getTableContents
648
+ {
649
+
650
+ NSMutableArray<PDFOutline *> *arrTableOfContents = [[NSMutableArray alloc] init];
651
+
652
+ if (_pdfDocument.outlineRoot) {
653
+
654
+ PDFOutline *currentRoot = _pdfDocument.outlineRoot;
655
+ NSMutableArray<PDFOutline *> *stack = [[NSMutableArray alloc] init];
656
+
657
+ [stack addObject:currentRoot];
658
+
659
+ while (stack.count > 0) {
660
+
661
+ PDFOutline *currentOutline = stack.lastObject;
662
+ [stack removeLastObject];
663
+
664
+ if (currentOutline.label.length > 0){
665
+ [arrTableOfContents addObject:currentOutline];
666
+ }
667
+
668
+ for ( NSInteger i= currentOutline.numberOfChildren; i > 0; i-- )
669
+ {
670
+ [stack addObject:[currentOutline childAtIndex:i-1]];
671
+ }
672
+ }
673
+ }
674
+
675
+ NSMutableArray *arrParentsContents = [[NSMutableArray alloc] init];
676
+
677
+ for ( NSInteger i= 0; i < arrTableOfContents.count; i++ )
678
+ {
679
+ PDFOutline *currentOutline = [arrTableOfContents objectAtIndex:i];
680
+
681
+ NSInteger indentationLevel = -1;
682
+
683
+ PDFOutline *parentOutline = currentOutline.parent;
684
+
685
+ while (parentOutline != nil) {
686
+ indentationLevel += 1;
687
+ parentOutline = parentOutline.parent;
688
+ }
689
+
690
+ if (indentationLevel == 0) {
691
+
692
+ NSMutableDictionary *DXParentsContent = [[NSMutableDictionary alloc] init];
693
+
694
+ [DXParentsContent setObject:[[NSMutableArray alloc] init] forKey:@"children"];
695
+ [DXParentsContent setObject:@"" forKey:@"mNativePtr"];
696
+ [DXParentsContent setObject:[NSString stringWithFormat:@"%lu", [_pdfDocument indexForPage:currentOutline.destination.page]] forKey:@"pageIdx"];
697
+ [DXParentsContent setObject:currentOutline.label forKey:@"title"];
698
+
699
+ //currentOutlin
700
+ //mNativePtr
701
+ [arrParentsContents addObject:DXParentsContent];
702
+ }
703
+ else {
704
+ NSMutableDictionary *DXParentsContent = [arrParentsContents lastObject];
705
+
706
+ NSMutableArray *arrChildren = [DXParentsContent valueForKey:@"children"];
707
+
708
+ while (indentationLevel > 1) {
709
+ NSMutableDictionary *DXchild = [arrChildren lastObject];
710
+ arrChildren = [DXchild valueForKey:@"children"];
711
+ indentationLevel--;
712
+ }
713
+
714
+ NSMutableDictionary *DXChildContent = [[NSMutableDictionary alloc] init];
715
+ [DXChildContent setObject:[[NSMutableArray alloc] init] forKey:@"children"];
716
+ [DXChildContent setObject:@"" forKey:@"mNativePtr"];
717
+ [DXChildContent setObject:[NSString stringWithFormat:@"%lu", [_pdfDocument indexForPage:currentOutline.destination.page]] forKey:@"pageIdx"];
718
+ [DXChildContent setObject:currentOutline.label forKey:@"title"];
719
+ [arrChildren addObject:DXChildContent];
720
+
721
+ }
722
+ }
723
+
724
+ NSError *error;
725
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arrParentsContents options:NSJSONWritingPrettyPrinted error:&error];
726
+
727
+ NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
728
+
729
+ return jsonString;
730
+
731
+ }
732
+
733
+ - (void)onPageChanged:(NSNotification *)noti
734
+ {
735
+
736
+ if (_pdfDocument) {
737
+ PDFPage *currentPage = _pdfView.currentPage;
738
+ unsigned long page = [_pdfDocument indexForPage:currentPage];
739
+ unsigned long numberOfPages = _pdfDocument.pageCount;
740
+
741
+ // Update current page for preloading
742
+ _page = (int)page + 1;
743
+ _pageCount = (int)numberOfPages;
744
+ if (_enablePreloading) {
745
+ [self preloadAdjacentPages:_page];
746
+ }
747
+
748
+ RLog(@"Enhanced PDF: Navigated to page %d", _page);
749
+ [self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"pageChanged|%lu|%lu", page+1, numberOfPages]]];
750
+ }
751
+
752
+ }
753
+
754
+ - (void)onScaleChanged:(NSNotification *)noti
755
+ {
756
+
757
+ if (_initialed && _fixScaleFactor>0) {
758
+ if (_scale != _pdfView.scaleFactor/_fixScaleFactor) {
759
+ _scale = _pdfView.scaleFactor/_fixScaleFactor;
760
+ [self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"scaleChanged|%f", _scale]]];
761
+ }
762
+ }
763
+ }
764
+
765
+ #pragma mark gesture process
766
+
767
+ /**
768
+ * Empty double tap handler
769
+ *
770
+ *
771
+ */
772
+ - (void)handleDoubleTapEmpty:(UITapGestureRecognizer *)recognizer {}
773
+
774
+ /**
775
+ * Tap
776
+ * zoom reset or zoom in
777
+ *
778
+ * @param recognizer
779
+ */
780
+ - (void)handleDoubleTap:(UITapGestureRecognizer *)recognizer
781
+ {
782
+
783
+ // Prevent double tap from selecting text.
784
+ dispatch_async(dispatch_get_main_queue(), ^{
785
+ [self->_pdfView clearSelection];
786
+ });
787
+
788
+ // Event appears to be consumed; broadcast for JS.
789
+ // _onChange(@{ @"message": @"pageDoubleTap" });
790
+
791
+ if (!_enableDoubleTapZoom) {
792
+ return;
793
+ }
794
+
795
+ // Cycle through min/mid/max scale factors to be consistent with Android
796
+ float min = self->_pdfView.minScaleFactor/self->_fixScaleFactor;
797
+ float max = self->_pdfView.maxScaleFactor/self->_fixScaleFactor;
798
+ float mid = (max - min) / 2 + min;
799
+ float scale = self->_scale;
800
+ if (self->_scale < mid) {
801
+ scale = mid;
802
+ } else if (self->_scale < max) {
803
+ scale = max;
804
+ } else {
805
+ scale = min;
806
+ }
807
+
808
+ CGFloat newScale = scale * self->_fixScaleFactor;
809
+ CGPoint tapPoint = [recognizer locationInView:self->_pdfView];
810
+
811
+ PDFPage *tappedPdfPage = [_pdfView pageForPoint:tapPoint nearest:NO];
812
+ PDFPage *pageRef;
813
+ if (tappedPdfPage) {
814
+ pageRef = tappedPdfPage;
815
+ } else {
816
+ pageRef = self->_pdfView.currentPage;
817
+ }
818
+ tapPoint = [self->_pdfView convertPoint:tapPoint toPage:pageRef];
819
+
820
+ CGRect tempZoomRect = CGRectZero;
821
+ tempZoomRect.size.width = self->_pdfView.frame.size.width;
822
+ tempZoomRect.size.height = 1;
823
+ tempZoomRect.origin = tapPoint;
824
+
825
+ dispatch_async(dispatch_get_main_queue(), ^{
826
+ [UIView animateWithDuration:0.3 animations:^{
827
+ [self->_pdfView setScaleFactor:newScale];
828
+
829
+ [self->_pdfView goToRect:tempZoomRect onPage:pageRef];
830
+ CGPoint defZoomOrigin = [self->_pdfView convertPoint:tempZoomRect.origin fromPage:pageRef];
831
+ defZoomOrigin.x = defZoomOrigin.x - self->_pdfView.frame.size.width / 2;
832
+ defZoomOrigin.y = defZoomOrigin.y - self->_pdfView.frame.size.height / 2;
833
+ defZoomOrigin = [self->_pdfView convertPoint:defZoomOrigin toPage:pageRef];
834
+ CGRect defZoomRect = CGRectOffset(
835
+ tempZoomRect,
836
+ defZoomOrigin.x - tempZoomRect.origin.x,
837
+ defZoomOrigin.y - tempZoomRect.origin.y
838
+ );
839
+ [self->_pdfView goToRect:defZoomRect onPage:pageRef];
840
+
841
+ [self setNeedsDisplay];
842
+ [self onScaleChanged:Nil];
843
+ }];
844
+ });
845
+ }
846
+
847
+ /**
848
+ * Single Tap
849
+ * stop zoom
850
+ *
851
+ * @param recognizer
852
+ */
853
+ - (void)handleSingleTap:(UITapGestureRecognizer *)sender
854
+ {
855
+ //_pdfView.scaleFactor = _pdfView.minScaleFactor;
856
+
857
+ CGPoint point = [sender locationInView:self];
858
+ PDFPage *pdfPage = [_pdfView pageForPoint:point nearest:NO];
859
+ if (pdfPage) {
860
+ unsigned long page = [_pdfDocument indexForPage:pdfPage];
861
+ [self notifyOnChangeWithMessage:
862
+ [[NSString alloc] initWithString:[NSString stringWithFormat:@"pageSingleTap|%lu|%f|%f", page+1, point.x, point.y]]];
863
+ }
864
+
865
+ //[self setNeedsDisplay];
866
+ //[self onScaleChanged:Nil];
867
+
868
+
869
+ }
870
+
871
+ /**
872
+ * Pinch
873
+ *
874
+ *
875
+ * @param recognizer
876
+ */
877
+ -(void)handlePinch:(UIPinchGestureRecognizer *)sender{
878
+ [self onScaleChanged:Nil];
879
+ }
880
+
881
+ /**
882
+ * Do nothing on long Press
883
+ *
884
+ *
885
+ */
886
+ - (void)handleLongPress:(UILongPressGestureRecognizer *)sender{
887
+
888
+ }
889
+
890
+ /**
891
+ * Bind tap
892
+ *
893
+ *
894
+ */
895
+ - (void)bindTap
896
+ {
897
+ UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
898
+ action:@selector(handleDoubleTap:)];
899
+ //trigger by one finger and double touch
900
+ doubleTapRecognizer.numberOfTapsRequired = 2;
901
+ doubleTapRecognizer.numberOfTouchesRequired = 1;
902
+ doubleTapRecognizer.delegate = self;
903
+
904
+ [self addGestureRecognizer:doubleTapRecognizer];
905
+ _doubleTapRecognizer = doubleTapRecognizer;
906
+
907
+ UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
908
+ action:@selector(handleSingleTap:)];
909
+ //trigger by one finger and one touch
910
+ singleTapRecognizer.numberOfTapsRequired = 1;
911
+ singleTapRecognizer.numberOfTouchesRequired = 1;
912
+ singleTapRecognizer.delegate = self;
913
+
914
+ [self addGestureRecognizer:singleTapRecognizer];
915
+ _singleTapRecognizer = singleTapRecognizer;
916
+
917
+ [singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
918
+
919
+ UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
920
+ action:@selector(handlePinch:)];
921
+ [self addGestureRecognizer:pinchRecognizer];
922
+ _pinchRecognizer = pinchRecognizer;
923
+
924
+ pinchRecognizer.delegate = self;
925
+
926
+ UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self
927
+ action:@selector(handleLongPress:)];
928
+ // Making sure the allowable movement isn not too narrow
929
+ longPressRecognizer.allowableMovement=100;
930
+ // Important: The duration must be long enough to allow taps but not longer than the period in which view opens the magnifying glass
931
+ longPressRecognizer.minimumPressDuration=0.3;
932
+
933
+ [self addGestureRecognizer:longPressRecognizer];
934
+ _longPressRecognizer = longPressRecognizer;
935
+
936
+ // Override the _pdfView double tap gesture recognizer so that it doesn't confilict with custom double tap
937
+ UITapGestureRecognizer *doubleTapEmptyRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
938
+ action:@selector(handleDoubleTapEmpty:)];
939
+ doubleTapEmptyRecognizer.numberOfTapsRequired = 2;
940
+ [_pdfView addGestureRecognizer:doubleTapEmptyRecognizer];
941
+ _doubleTapEmptyRecognizer = doubleTapEmptyRecognizer;
942
+ }
943
+
944
+ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
945
+
946
+ {
947
+ return !_singlePage;
948
+ }
949
+
950
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
951
+ {
952
+ return !_singlePage;
953
+ }
954
+
955
+ - (void)setScrollIndicators:(UIView *)view horizontal:(BOOL)horizontal vertical:(BOOL)vertical depth:(int)depth {
956
+ // max depth, prevent infinite loop
957
+ if (depth > 10) {
958
+ return;
959
+ }
960
+
961
+ if ([view isKindOfClass:[UIScrollView class]]) {
962
+ UIScrollView *scrollView = (UIScrollView *)view;
963
+ scrollView.showsHorizontalScrollIndicator = horizontal;
964
+ scrollView.showsVerticalScrollIndicator = vertical;
965
+ }
966
+
967
+ for (UIView *subview in view.subviews) {
968
+ [self setScrollIndicators:subview horizontal:horizontal vertical:vertical depth:depth + 1];
969
+ }
970
+ }
971
+
972
+ // Enhanced progressive loading methods
973
+ - (void)preloadAdjacentPages:(int)currentPage
974
+ {
975
+ if (!_enablePreloading || !_pdfDocument) {
976
+ return;
977
+ }
978
+
979
+ int startPage = MAX(1, currentPage - _preloadRadius);
980
+ int endPage = MIN((int)_pdfDocument.pageCount, currentPage + _preloadRadius);
981
+
982
+ for (int page = startPage; page <= endPage; page++) {
983
+ if (![_preloadedPages containsObject:@(page)]) {
984
+ [_preloadedPages addObject:@(page)];
985
+
986
+ // Add preload operation to queue
987
+ NSBlockOperation *preloadOp = [NSBlockOperation blockOperationWithBlock:^{
988
+ // Preload page content (this is a simplified version)
989
+ // In a real implementation, you might preload page thumbnails or other content
990
+ RLog(@"Enhanced PDF: Preloading page %d", page);
991
+ }];
992
+
993
+ [_preloadQueue addOperation:preloadOp];
994
+ }
995
+ }
996
+ }
997
+
998
+ - (NSDictionary *)getPerformanceMetrics
999
+ {
1000
+ NSMutableDictionary *metrics = [_performanceMetrics mutableCopy];
1001
+ metrics[@"cacheHitCount"] = @([_pageCache count]);
1002
+ metrics[@"preloadedPages"] = @([_preloadedPages count]);
1003
+ metrics[@"cacheSize"] = @(_cacheSize);
1004
+ return metrics;
1005
+ }
1006
+
1007
+ - (void)clearCache
1008
+ {
1009
+ [_pageCache removeAllObjects];
1010
+ [_preloadedPages removeAllObjects];
1011
+ [_searchCache removeAllObjects];
1012
+ RLog(@"Enhanced PDF: Cache cleared");
1013
+ }
1014
+
1015
+ - (void)preloadPagesFrom:(int)startPage to:(int)endPage
1016
+ {
1017
+ if (!_enablePreloading || !_pdfDocument) {
1018
+ return;
1019
+ }
1020
+
1021
+ int actualStartPage = MAX(1, startPage);
1022
+ int actualEndPage = MIN((int)_pdfDocument.pageCount, endPage);
1023
+
1024
+ for (int page = actualStartPage; page <= actualEndPage; page++) {
1025
+ if (![_preloadedPages containsObject:@(page)]) {
1026
+ [_preloadedPages addObject:@(page)];
1027
+ RLog(@"Enhanced PDF: Preloading page %d", page);
1028
+ }
1029
+ }
1030
+ }
1031
+
1032
+ - (NSDictionary *)searchText:(NSString *)searchTerm
1033
+ {
1034
+ if (!searchTerm || searchTerm.length == 0 || !_pdfDocument) {
1035
+ return @{@"totalMatches": @0, @"results": @[]};
1036
+ }
1037
+
1038
+ // Check cache first
1039
+ NSString *cacheKey = [NSString stringWithFormat:@"%@_%@", _currentPdfId, searchTerm];
1040
+ if (_searchCache[cacheKey]) {
1041
+ RLog(@"Enhanced PDF: Search cache hit for '%@'", searchTerm);
1042
+ return _searchCache[cacheKey];
1043
+ }
1044
+
1045
+ NSMutableArray *results = [NSMutableArray array];
1046
+ int totalMatches = 0;
1047
+
1048
+ for (int pageIndex = 0; pageIndex < _pdfDocument.pageCount; pageIndex++) {
1049
+ PDFPage *page = [_pdfDocument pageAtIndex:pageIndex];
1050
+
1051
+ // Get the page bounds
1052
+ CGRect pageBounds = [page boundsForBox:kPDFDisplayBoxCropBox];
1053
+
1054
+ // Search for text in the page
1055
+ PDFSelection *selection = [page selectionForRange:NSMakeRange(0, page.string.length)];
1056
+ if (selection && selection.string.length > 0) {
1057
+ NSString *text = selection.string;
1058
+ if ([text localizedCaseInsensitiveContainsString:searchTerm]) {
1059
+ // Get the bounds of the selection
1060
+ CGRect selectionBounds = [selection boundsForPage:page];
1061
+
1062
+ [results addObject:@{
1063
+ @"page": @(pageIndex + 1),
1064
+ @"text": text,
1065
+ @"rect": NSStringFromCGRect(selectionBounds)
1066
+ }];
1067
+ totalMatches++;
1068
+ }
1069
+ }
1070
+ }
1071
+
1072
+ NSDictionary *searchResults = @{
1073
+ @"totalMatches": @(totalMatches),
1074
+ @"results": results
1075
+ };
1076
+
1077
+ // Cache the results
1078
+ _searchCache[cacheKey] = searchResults;
1079
+
1080
+ RLog(@"Enhanced PDF: Search completed for '%@', found %d matches", searchTerm, totalMatches);
1081
+ return searchResults;
1082
+ }
1083
+
1084
+ @end
1085
+
1086
+ #ifdef RCT_NEW_ARCH_ENABLED
1087
+ Class<RCTComponentViewProtocol> RNPDFPdfViewCls(void)
1088
+ {
1089
+ return RNPDFPdfView.class;
1090
+ }
1091
+
1092
+ #endif