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.
- package/DoubleTapView.js +125 -0
- package/INTEGRATION_GUIDE.md +419 -0
- package/LICENSE +21 -0
- package/PdfManager.js +26 -0
- package/PdfPageView.js +53 -0
- package/PdfView.js +421 -0
- package/PdfViewFlatList.js +30 -0
- package/PinchZoomView.js +125 -0
- package/README.md +693 -0
- package/README_JSI.md +348 -0
- package/android/.gradle/5.6.1/fileChanges/last-build.bin +0 -0
- package/android/.gradle/5.6.1/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/5.6.1/gc.properties +0 -0
- package/android/.gradle/8.5/checksums/checksums.lock +0 -0
- package/android/.gradle/8.5/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.5/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock +0 -0
- package/android/.gradle/8.5/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.5/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.5/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.5/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.5/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +198 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/android/gradle.properties +2 -0
- package/android/gradlew +249 -0
- package/android/gradlew.bat +92 -0
- package/android/project.properties +12 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/cpp/Android.mk +50 -0
- package/android/src/main/cpp/CMakeLists.txt +76 -0
- package/android/src/main/cpp/PDFJSI.cpp +190 -0
- package/android/src/main/cpp/PDFJSI.h +95 -0
- package/android/src/main/cpp/PDFJSIBridge.cpp +32 -0
- package/android/src/main/cpp/PDFJSIModule.cpp +31 -0
- package/android/src/main/java/org/wonday/pdf/EnhancedPdfJSIBridge.java +281 -0
- package/android/src/main/java/org/wonday/pdf/PDFJSIManager.java +317 -0
- package/android/src/main/java/org/wonday/pdf/PDFJSIModule.java +189 -0
- package/android/src/main/java/org/wonday/pdf/PdfManager.java +180 -0
- package/android/src/main/java/org/wonday/pdf/PdfView.java +505 -0
- package/android/src/main/java/org/wonday/pdf/RNPDFPackage.java +43 -0
- package/android/src/main/java/org/wonday/pdf/events/TopChangeEvent.java +26 -0
- package/android/src/main/jniLibs/arm64-v8a/libpdfjsi.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libpdfjsi.so +0 -0
- package/android/src/main/jniLibs/x86/libpdfjsi.so +0 -0
- package/android/src/main/jniLibs/x86_64/libpdfjsi.so +0 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.java +92 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.java +35 -0
- package/fabric/RNPDFPdfNativeComponent.js +48 -0
- package/index.d.ts +72 -0
- package/index.js +603 -0
- package/index.js.flow +67 -0
- package/ios/RNPDFPdf/PdfManager.h +23 -0
- package/ios/RNPDFPdf/PdfManager.mm +152 -0
- package/ios/RNPDFPdf/RNPDFPdfPageView.h +21 -0
- package/ios/RNPDFPdf/RNPDFPdfPageView.mm +185 -0
- package/ios/RNPDFPdf/RNPDFPdfPageViewManager.h +18 -0
- package/ios/RNPDFPdf/RNPDFPdfPageViewManager.mm +30 -0
- package/ios/RNPDFPdf/RNPDFPdfView.h +63 -0
- package/ios/RNPDFPdf/RNPDFPdfView.mm +1092 -0
- package/ios/RNPDFPdf/RNPDFPdfViewManager.h +18 -0
- package/ios/RNPDFPdf/RNPDFPdfViewManager.mm +68 -0
- package/ios/RNPDFPdf.xcodeproj/project.pbxproj +321 -0
- package/package.json +78 -0
- package/react-native-pdf.podspec +31 -0
- package/src/EnhancedPdfView.js +362 -0
- package/src/PDFJSI.js +519 -0
- package/src/examples/PDFJSIExample.js +296 -0
- package/src/hooks/usePDFJSI.js +346 -0
- package/src/index.js +32 -0
- package/windows/RCTPdf/PropertySheet.props +16 -0
- package/windows/RCTPdf/RCTPdf.def +3 -0
- package/windows/RCTPdf/RCTPdf.vcxproj +180 -0
- package/windows/RCTPdf/RCTPdf.vcxproj.filters +38 -0
- package/windows/RCTPdf/RCTPdfControl.cpp +667 -0
- package/windows/RCTPdf/RCTPdfControl.h +119 -0
- package/windows/RCTPdf/RCTPdfControl.idl +10 -0
- package/windows/RCTPdf/RCTPdfControl.xaml +33 -0
- package/windows/RCTPdf/RCTPdfViewManager.cpp +69 -0
- package/windows/RCTPdf/RCTPdfViewManager.h +51 -0
- package/windows/RCTPdf/ReactPackageProvider.cpp +15 -0
- package/windows/RCTPdf/ReactPackageProvider.h +16 -0
- package/windows/RCTPdf/ReactPackageProvider.idl +9 -0
- package/windows/RCTPdf/packages.config +4 -0
- package/windows/RCTPdf/pch.cpp +1 -0
- package/windows/RCTPdf/pch.h +31 -0
- 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
|