react-native-pdf-jsi 4.3.1 → 4.4.0
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/PdfView.js +1 -1
- package/README.md +30 -1
- package/android/src/main/java/org/wonday/pdf/PDFJSIManager.java +138 -3
- package/android/src/main/java/org/wonday/pdf/PdfManager.java +10 -0
- package/android/src/main/java/org/wonday/pdf/PdfView.java +110 -3
- package/android/src/main/java/org/wonday/pdf/SearchRegistry.java +44 -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/fabric/RNPDFPdfNativeComponent.js +2 -0
- package/index.d.ts +34 -0
- package/index.js +29 -2
- package/ios/RNPDFPdf/PDFJSIManager.m +89 -4
- package/ios/RNPDFPdf/RNPDFPdfView.h +2 -0
- package/ios/RNPDFPdf/RNPDFPdfView.mm +251 -13
- package/ios/RNPDFPdf/RNPDFPdfViewManager.mm +5 -1
- package/ios/RNPDFPdf/SearchRegistry.h +21 -0
- package/ios/RNPDFPdf/SearchRegistry.m +71 -0
- package/package.json +4 -2
- package/src/PDFJSI.js +1 -1
- 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 +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
#import "PDFJSIManager.h"
|
|
10
10
|
#import "PDFNativeCacheManager.h"
|
|
11
|
+
#import "SearchRegistry.h"
|
|
11
12
|
#import <React/RCTLog.h>
|
|
12
13
|
#import <React/RCTUtils.h>
|
|
13
14
|
#import <React/RCTBridge.h>
|
|
15
|
+
#import <PDFKit/PDFKit.h>
|
|
14
16
|
#import <dispatch/dispatch.h>
|
|
15
17
|
|
|
16
18
|
@implementation PDFJSIManager {
|
|
@@ -248,6 +250,21 @@ RCT_EXPORT_METHOD(optimizeMemory:(NSString *)pdfId
|
|
|
248
250
|
});
|
|
249
251
|
}
|
|
250
252
|
|
|
253
|
+
RCT_EXPORT_METHOD(registerPathForSearch:(NSString *)pdfId
|
|
254
|
+
path:(NSString *)path
|
|
255
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
256
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
257
|
+
{
|
|
258
|
+
if (pdfId.length && path.length) {
|
|
259
|
+
[SearchRegistry registerPath:pdfId path:path];
|
|
260
|
+
RCTLogInfo(@"✅ [SearchRegistry] Registered path for pdfId: %@ (path length %lu)", pdfId, (unsigned long)path.length);
|
|
261
|
+
resolve(@YES);
|
|
262
|
+
} else {
|
|
263
|
+
RCTLogWarn(@"⚠️ [SearchRegistry] registerPathForSearch skipped: pdfId length=%lu path length=%lu", (unsigned long)pdfId.length, (unsigned long)path.length);
|
|
264
|
+
resolve(@NO);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
251
268
|
RCT_EXPORT_METHOD(searchTextDirect:(NSString *)pdfId
|
|
252
269
|
searchTerm:(NSString *)searchTerm
|
|
253
270
|
startPage:(NSInteger)startPage
|
|
@@ -259,14 +276,82 @@ RCT_EXPORT_METHOD(searchTextDirect:(NSString *)pdfId
|
|
|
259
276
|
reject(@"JSI_NOT_INITIALIZED", @"JSI is not initialized", nil);
|
|
260
277
|
return;
|
|
261
278
|
}
|
|
279
|
+
if (!searchTerm || searchTerm.length == 0) {
|
|
280
|
+
resolve(@[]);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
262
283
|
|
|
263
284
|
dispatch_async(_backgroundQueue, ^{
|
|
264
285
|
@try {
|
|
265
286
|
RCTLogInfo(@"🔍 Searching text via JSI: '%@' in pages %ld-%ld", searchTerm, (long)startPage, (long)endPage);
|
|
266
287
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
288
|
+
NSString *path = [SearchRegistry pathForPdfId:pdfId];
|
|
289
|
+
if (!path || path.length == 0) {
|
|
290
|
+
RCTLogWarn(@"❌ [Search] No path registered for pdfId: %@ - ensure onLoadComplete ran and pdfId is set on Pdf", pdfId);
|
|
291
|
+
resolve(@[]);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if ([path hasPrefix:@"http://"] || [path hasPrefix:@"https://"]) {
|
|
295
|
+
RCTLogWarn(@"❌ [Search] Path for pdfId %@ is a URI (not a local file path) - cannot open for search", pdfId);
|
|
296
|
+
resolve(@[]);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
RCTLogInfo(@"📂 [Search] Path for pdfId '%@': length %lu", pdfId, (unsigned long)path.length);
|
|
300
|
+
if ([path hasPrefix:@"file://"]) {
|
|
301
|
+
path = [path substringFromIndex:7];
|
|
302
|
+
}
|
|
303
|
+
BOOL readable = [[NSFileManager defaultManager] isReadableFileAtPath:path];
|
|
304
|
+
if (!readable) {
|
|
305
|
+
RCTLogWarn(@"❌ [Search] File not readable at path (length %lu)", (unsigned long)path.length);
|
|
306
|
+
resolve(@[]);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
NSURL *fileURL = [NSURL fileURLWithPath:path];
|
|
310
|
+
PDFDocument *doc = [[PDFDocument alloc] initWithURL:fileURL];
|
|
311
|
+
if (!doc || doc.pageCount == 0) {
|
|
312
|
+
RCTLogWarn(@"❌ [Search] PDFDocument init failed or empty: doc=%p pageCount=%lu", (__bridge void *)doc, (unsigned long)doc.pageCount);
|
|
313
|
+
resolve(@[]);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
NSInteger from = MAX(1, startPage);
|
|
318
|
+
NSInteger to = MIN((NSInteger)doc.pageCount, endPage);
|
|
319
|
+
NSMutableArray *out = [NSMutableArray array];
|
|
320
|
+
|
|
321
|
+
// findString:withOptions: returns selections; each can span multiple pages
|
|
322
|
+
NSArray<PDFSelection *> *selections = [doc findString:searchTerm withOptions:NSCaseInsensitiveSearch];
|
|
323
|
+
RCTLogInfo(@"📄 [Search] findString returned %lu selection(s) for '%@'", (unsigned long)selections.count, searchTerm);
|
|
324
|
+
for (PDFSelection *sel in selections) {
|
|
325
|
+
for (PDFPage *page in sel.pages) {
|
|
326
|
+
NSInteger pageIndex1Based = [doc indexForPage:page] + 1;
|
|
327
|
+
if (pageIndex1Based < from || pageIndex1Based > to) continue;
|
|
328
|
+
|
|
329
|
+
CGRect bounds = [sel boundsForPage:page];
|
|
330
|
+
// PDF page coords: origin bottom-left. Serialize as "left,top,right,bottom" (y-up: top > bottom)
|
|
331
|
+
CGFloat left = bounds.origin.x;
|
|
332
|
+
CGFloat bottom = bounds.origin.y;
|
|
333
|
+
CGFloat right = bounds.origin.x + bounds.size.width;
|
|
334
|
+
CGFloat top = bounds.origin.y + bounds.size.height;
|
|
335
|
+
NSString *rectStr = [NSString stringWithFormat:@"%g,%g,%g,%g", left, top, right, bottom];
|
|
336
|
+
|
|
337
|
+
[out addObject:@{
|
|
338
|
+
@"page": @(pageIndex1Based),
|
|
339
|
+
@"text": sel.string ?: @"",
|
|
340
|
+
@"rect": rectStr
|
|
341
|
+
}];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Register page sizes in points for highlight scaling (use first page of range if we have selections)
|
|
346
|
+
for (NSInteger idx = from; idx <= to; idx++) {
|
|
347
|
+
PDFPage *page = [doc pageAtIndex:(NSUInteger)(idx - 1)];
|
|
348
|
+
if (page) {
|
|
349
|
+
CGRect box = [page boundsForBox:kPDFDisplayBoxMediaBox];
|
|
350
|
+
[SearchRegistry registerPageSizePointsForPdfId:pdfId pageIndex0Based:(idx - 1) widthPt:box.size.width heightPt:box.size.height];
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
resolve([out copy]);
|
|
270
355
|
|
|
271
356
|
} @catch (NSException *exception) {
|
|
272
357
|
RCTLogError(@"❌ Error searching text via JSI: %@", exception.reason);
|
|
@@ -580,7 +665,7 @@ RCT_EXPORT_METHOD(testNativeCache:(RCTPromiseResolveBlock)resolve
|
|
|
580
665
|
|
|
581
666
|
// iOS doesn't have the same 16KB page size requirements as Android
|
|
582
667
|
// but we still check for compatibility
|
|
583
|
-
|
|
668
|
+
(void)[self checkiOS16KBSupport];
|
|
584
669
|
|
|
585
670
|
NSDictionary *result = @{
|
|
586
671
|
@"supported": @YES, // iOS is generally compatible
|
|
@@ -68,6 +68,8 @@ UIView
|
|
|
68
68
|
@property(nonatomic) int spacing;
|
|
69
69
|
@property(nonatomic, strong) NSString *password;
|
|
70
70
|
@property(nonatomic) BOOL singlePage;
|
|
71
|
+
@property(nonatomic, strong) NSString *pdfId;
|
|
72
|
+
@property(nonatomic, copy) NSArray *highlightRects;
|
|
71
73
|
|
|
72
74
|
@property(nonatomic, copy) RCTBubblingEventBlock onChange;
|
|
73
75
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
#import "RNPDFPdfView.h"
|
|
10
|
+
#import "SearchRegistry.h"
|
|
10
11
|
|
|
11
12
|
#import <Foundation/Foundation.h>
|
|
12
13
|
#import <QuartzCore/QuartzCore.h>
|
|
@@ -55,6 +56,37 @@ const float MAX_SCALE = 3.0f;
|
|
|
55
56
|
const float MIN_SCALE = 1.0f;
|
|
56
57
|
|
|
57
58
|
|
|
59
|
+
/** Overlay that draws highlight rects on top of the PDF view. */
|
|
60
|
+
@interface HighlightOverlayView : UIView
|
|
61
|
+
@property (nonatomic, weak) PDFView *pdfView;
|
|
62
|
+
@property (nonatomic, copy) NSArray<NSDictionary *> *highlightRects;
|
|
63
|
+
@end
|
|
64
|
+
|
|
65
|
+
@implementation HighlightOverlayView
|
|
66
|
+
- (void)drawRect:(CGRect)rect {
|
|
67
|
+
PDFView *pv = self.pdfView;
|
|
68
|
+
NSArray *items = self.highlightRects;
|
|
69
|
+
if (!pv || !pv.document || !items.count) return;
|
|
70
|
+
PDFDocument *doc = pv.document;
|
|
71
|
+
[[UIColor colorWithRed:1 green:1 blue:0 alpha:0.35] setFill];
|
|
72
|
+
for (NSDictionary *item in items) {
|
|
73
|
+
NSNumber *pageNum = item[@"page"];
|
|
74
|
+
NSString *rectStr = item[@"rect"];
|
|
75
|
+
if (!pageNum || !rectStr.length) continue;
|
|
76
|
+
int page1 = pageNum.intValue;
|
|
77
|
+
if (page1 < 1) continue;
|
|
78
|
+
PDFPage *page = [doc pageAtIndex:(NSUInteger)(page1 - 1)];
|
|
79
|
+
if (!page) continue;
|
|
80
|
+
NSArray<NSString *> *parts = [rectStr componentsSeparatedByString:@","];
|
|
81
|
+
if (parts.count != 4) continue;
|
|
82
|
+
CGFloat left = parts[0].doubleValue, top = parts[1].doubleValue, right = parts[2].doubleValue, bottom = parts[3].doubleValue;
|
|
83
|
+
CGRect pageRect = CGRectMake(left, bottom, right - left, top - bottom);
|
|
84
|
+
CGRect viewRect = [pv convertRect:pageRect fromPage:page];
|
|
85
|
+
CGContextFillRect(UIGraphicsGetCurrentContext(), viewRect);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
@end
|
|
89
|
+
|
|
58
90
|
@interface RNPDFScrollViewDelegateProxy : NSObject <UIScrollViewDelegate>
|
|
59
91
|
- (instancetype)initWithPrimary:(id<UIScrollViewDelegate>)primary secondary:(id<UIScrollViewDelegate>)secondary;
|
|
60
92
|
@end
|
|
@@ -98,10 +130,33 @@ const float MIN_SCALE = 1.0f;
|
|
|
98
130
|
}
|
|
99
131
|
|
|
100
132
|
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
|
|
133
|
+
// First check if primary delegate (PDFView's internal) handles it
|
|
101
134
|
if (_primary && [_primary respondsToSelector:@selector(viewForZoomingInScrollView:)]) {
|
|
102
|
-
|
|
135
|
+
UIView *zoomView = [_primary viewForZoomingInScrollView:scrollView];
|
|
136
|
+
if (zoomView != nil) {
|
|
137
|
+
NSLog(@"🔍 [iOS Zoom Delegate] Primary delegate returned zoom view: %@", NSStringFromClass([zoomView class]));
|
|
138
|
+
return zoomView;
|
|
139
|
+
}
|
|
103
140
|
}
|
|
104
|
-
|
|
141
|
+
|
|
142
|
+
// PDFKit's scroll view needs to zoom the PDFDocumentView
|
|
143
|
+
// Search for it in the hierarchy
|
|
144
|
+
for (UIView *subview in scrollView.subviews) {
|
|
145
|
+
NSString *className = NSStringFromClass([subview class]);
|
|
146
|
+
if ([className containsString:@"PDFDocumentView"] || [className containsString:@"PDFPage"]) {
|
|
147
|
+
NSLog(@"🔍 [iOS Zoom Delegate] Found PDF view for zooming: %@", className);
|
|
148
|
+
return subview;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Fallback to first subview if it exists
|
|
153
|
+
UIView *fallback = scrollView.subviews.firstObject;
|
|
154
|
+
if (fallback) {
|
|
155
|
+
NSLog(@"🔍 [iOS Zoom Delegate] Using fallback zoom view: %@", NSStringFromClass([fallback class]));
|
|
156
|
+
} else {
|
|
157
|
+
NSLog(@"⚠️ [iOS Zoom Delegate] WARNING: No view found for zooming! Scroll view has %lu subviews", (unsigned long)scrollView.subviews.count);
|
|
158
|
+
}
|
|
159
|
+
return fallback;
|
|
105
160
|
}
|
|
106
161
|
|
|
107
162
|
@end
|
|
@@ -158,6 +213,13 @@ const float MIN_SCALE = 1.0f;
|
|
|
158
213
|
// Track usePageViewController state to prevent unnecessary reconfiguration
|
|
159
214
|
BOOL _currentUsePageViewController;
|
|
160
215
|
BOOL _usePageViewControllerStateInitialized;
|
|
216
|
+
|
|
217
|
+
// Search and highlight (iOS parity with Android)
|
|
218
|
+
NSString *_pdfId;
|
|
219
|
+
NSArray *_highlightRects;
|
|
220
|
+
HighlightOverlayView *_highlightOverlay;
|
|
221
|
+
/// Local file path when document loaded (used for SearchRegistry; may differ from _path which can be URI)
|
|
222
|
+
NSString *_lastLoadedPath;
|
|
161
223
|
}
|
|
162
224
|
|
|
163
225
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
@@ -292,6 +354,30 @@ using namespace facebook::react;
|
|
|
292
354
|
_scrollEnabled = newProps.scrollEnabled;
|
|
293
355
|
[updatedPropNames addObject:@"scrollEnabled"];
|
|
294
356
|
}
|
|
357
|
+
NSString *newPdfId = RCTNSStringFromStringNilIfEmpty(newProps.pdfId);
|
|
358
|
+
if (_pdfId != newPdfId && ![newPdfId isEqualToString:_pdfId]) {
|
|
359
|
+
if (_pdfId.length) [SearchRegistry unregisterPath:_pdfId];
|
|
360
|
+
_pdfId = [newPdfId copy];
|
|
361
|
+
[updatedPropNames addObject:@"pdfId"];
|
|
362
|
+
// Only register local file paths; never register URIs - onDocumentChanged will register when we have local path
|
|
363
|
+
NSString *pathToRegister = nil;
|
|
364
|
+
if (_lastLoadedPath.length > 0) {
|
|
365
|
+
pathToRegister = _lastLoadedPath;
|
|
366
|
+
} else if (_path.length > 0 && [_path hasPrefix:@"/"]) {
|
|
367
|
+
pathToRegister = _path;
|
|
368
|
+
}
|
|
369
|
+
if (_pdfId.length && pathToRegister.length > 0) {
|
|
370
|
+
[SearchRegistry registerPath:_pdfId path:pathToRegister];
|
|
371
|
+
RCTLogInfo(@"✅ [iOS] SearchRegistry registered path for pdfId: %@ (from updateProps)", _pdfId);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// Convert codegen vector of {page, rect} to NSArray for setHighlightRects
|
|
375
|
+
NSMutableArray *newHighlightRects = [NSMutableArray array];
|
|
376
|
+
for (const auto &item : newProps.highlightRects) {
|
|
377
|
+
[newHighlightRects addObject:@{ @"page": @(item.page), @"rect": [NSString stringWithUTF8String:item.rect.c_str()] }];
|
|
378
|
+
}
|
|
379
|
+
[self setHighlightRects:[newHighlightRects copy]];
|
|
380
|
+
[updatedPropNames addObject:@"highlightRects"];
|
|
295
381
|
|
|
296
382
|
[super updateProps:props oldProps:oldProps];
|
|
297
383
|
[self didSetProps:updatedPropNames];
|
|
@@ -306,10 +392,11 @@ using namespace facebook::react;
|
|
|
306
392
|
- (void)prepareForRecycle
|
|
307
393
|
{
|
|
308
394
|
[super prepareForRecycle];
|
|
309
|
-
|
|
395
|
+
if (_pdfId.length) [SearchRegistry unregisterPath:_pdfId];
|
|
310
396
|
[_pdfView removeFromSuperview];
|
|
311
397
|
_pdfDocument = Nil;
|
|
312
398
|
_pdfView = Nil;
|
|
399
|
+
_highlightOverlay = Nil;
|
|
313
400
|
//Remove notifications
|
|
314
401
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"PDFViewDocumentChangedNotification" object:nil];
|
|
315
402
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"PDFViewPageChangedNotification" object:nil];
|
|
@@ -419,6 +506,11 @@ using namespace facebook::react;
|
|
|
419
506
|
_preloadQueue.maxConcurrentOperationCount = 3;
|
|
420
507
|
_preloadQueue.qualityOfService = NSQualityOfServiceBackground;
|
|
421
508
|
|
|
509
|
+
_pdfId = nil;
|
|
510
|
+
_highlightRects = nil;
|
|
511
|
+
_lastLoadedPath = nil;
|
|
512
|
+
_highlightOverlay = nil;
|
|
513
|
+
|
|
422
514
|
// init and config PDFView
|
|
423
515
|
_pdfView = [[PDFView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
|
|
424
516
|
_pdfView.displayMode = kPDFDisplaySinglePageContinuous;
|
|
@@ -443,10 +535,14 @@ using namespace facebook::react;
|
|
|
443
535
|
[[_pdfView document] setDelegate: self];
|
|
444
536
|
[_pdfView setDelegate: self];
|
|
445
537
|
|
|
446
|
-
//
|
|
538
|
+
// Only disable double-tap recognizers to avoid conflicts with custom double-tap
|
|
539
|
+
// Leave all other gestures (including pinch) enabled
|
|
447
540
|
for (UIGestureRecognizer *recognizer in _pdfView.gestureRecognizers) {
|
|
448
541
|
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
|
|
449
|
-
|
|
542
|
+
UITapGestureRecognizer *tapGesture = (UITapGestureRecognizer *)recognizer;
|
|
543
|
+
if (tapGesture.numberOfTapsRequired == 2) {
|
|
544
|
+
recognizer.enabled = NO;
|
|
545
|
+
}
|
|
450
546
|
}
|
|
451
547
|
}
|
|
452
548
|
|
|
@@ -661,6 +757,18 @@ using namespace facebook::react;
|
|
|
661
757
|
_pdfView.maxScaleFactor = _fixScaleFactor*_maxScale;
|
|
662
758
|
}
|
|
663
759
|
}
|
|
760
|
+
|
|
761
|
+
// CRITICAL: Also configure the internal scroll view zoom scales
|
|
762
|
+
// This must be done AFTER _fixScaleFactor is set above
|
|
763
|
+
if (_internalScrollView && _fixScaleFactor > 0) {
|
|
764
|
+
_internalScrollView.minimumZoomScale = _fixScaleFactor * _minScale;
|
|
765
|
+
_internalScrollView.maximumZoomScale = _fixScaleFactor * _maxScale;
|
|
766
|
+
_internalScrollView.zoomScale = _pdfView.scaleFactor;
|
|
767
|
+
RCTLogInfo(@"🔍 [iOS Zoom] Configured internal scroll view zoom scales - min=%f, max=%f, current=%f",
|
|
768
|
+
_internalScrollView.minimumZoomScale,
|
|
769
|
+
_internalScrollView.maximumZoomScale,
|
|
770
|
+
_internalScrollView.zoomScale);
|
|
771
|
+
}
|
|
664
772
|
|
|
665
773
|
}
|
|
666
774
|
|
|
@@ -668,6 +776,12 @@ using namespace facebook::react;
|
|
|
668
776
|
_pdfView.scaleFactor = _scale * _fixScaleFactor;
|
|
669
777
|
if (_pdfView.scaleFactor>_pdfView.maxScaleFactor) _pdfView.scaleFactor = _pdfView.maxScaleFactor;
|
|
670
778
|
if (_pdfView.scaleFactor<_pdfView.minScaleFactor) _pdfView.scaleFactor = _pdfView.minScaleFactor;
|
|
779
|
+
|
|
780
|
+
// Also update internal scroll view zoom scale when scale changes
|
|
781
|
+
if (_internalScrollView && _fixScaleFactor > 0) {
|
|
782
|
+
_internalScrollView.zoomScale = _pdfView.scaleFactor;
|
|
783
|
+
RCTLogInfo(@"🔍 [iOS Zoom] Updated internal scroll view zoom scale to %f", _internalScrollView.zoomScale);
|
|
784
|
+
}
|
|
671
785
|
}
|
|
672
786
|
|
|
673
787
|
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"horizontal"])) {
|
|
@@ -952,10 +1066,58 @@ using namespace facebook::react;
|
|
|
952
1066
|
RCTLogInfo(@"🔍 [iOS] loadComplete message: %@", message);
|
|
953
1067
|
|
|
954
1068
|
[self notifyOnChangeWithMessage:message];
|
|
1069
|
+
|
|
1070
|
+
// Store local path so we can register when pdfId is set (Fabric may set pdfId after document load)
|
|
1071
|
+
_lastLoadedPath = [pathValue copy];
|
|
1072
|
+
// Register path for searchTextDirect (iOS parity with Android)
|
|
1073
|
+
if (_pdfId.length && pathValue.length) {
|
|
1074
|
+
[SearchRegistry registerPath:_pdfId path:pathValue];
|
|
1075
|
+
RCTLogInfo(@"✅ [iOS] SearchRegistry registered path for pdfId: %@ (from onDocumentChanged)", _pdfId);
|
|
1076
|
+
}
|
|
955
1077
|
}
|
|
956
1078
|
|
|
957
1079
|
}
|
|
958
1080
|
|
|
1081
|
+
- (void)setPdfId:(NSString *)pdfId {
|
|
1082
|
+
if (_pdfId.length && ![pdfId isEqualToString:_pdfId]) {
|
|
1083
|
+
[SearchRegistry unregisterPath:_pdfId];
|
|
1084
|
+
}
|
|
1085
|
+
_pdfId = [pdfId copy];
|
|
1086
|
+
// If document already loaded, register path now (Fabric may set pdfId after path/document load).
|
|
1087
|
+
// Only register local file paths; never register URIs (http/https) - PDFDocument needs file path.
|
|
1088
|
+
NSString *pathToRegister = nil;
|
|
1089
|
+
if (_lastLoadedPath.length > 0) {
|
|
1090
|
+
pathToRegister = _lastLoadedPath;
|
|
1091
|
+
} else if (_path.length > 0 && [_path hasPrefix:@"/"]) {
|
|
1092
|
+
pathToRegister = _path;
|
|
1093
|
+
}
|
|
1094
|
+
if (_pdfId.length && pathToRegister.length > 0) {
|
|
1095
|
+
[SearchRegistry registerPath:_pdfId path:pathToRegister];
|
|
1096
|
+
RCTLogInfo(@"✅ [iOS] SearchRegistry registered path for pdfId: %@ (from setPdfId)", _pdfId);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
- (void)setHighlightRects:(NSArray *)highlightRects {
|
|
1101
|
+
_highlightRects = [highlightRects copy];
|
|
1102
|
+
if (!_pdfView) return;
|
|
1103
|
+
if (_highlightRects.count > 0) {
|
|
1104
|
+
if (!_highlightOverlay) {
|
|
1105
|
+
_highlightOverlay = [[HighlightOverlayView alloc] initWithFrame:_pdfView.bounds];
|
|
1106
|
+
_highlightOverlay.backgroundColor = [UIColor clearColor];
|
|
1107
|
+
_highlightOverlay.userInteractionEnabled = NO;
|
|
1108
|
+
_highlightOverlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
1109
|
+
_highlightOverlay.pdfView = _pdfView;
|
|
1110
|
+
[_pdfView addSubview:_highlightOverlay];
|
|
1111
|
+
[_pdfView bringSubviewToFront:_highlightOverlay];
|
|
1112
|
+
}
|
|
1113
|
+
_highlightOverlay.highlightRects = _highlightRects;
|
|
1114
|
+
[_highlightOverlay setNeedsDisplay];
|
|
1115
|
+
} else if (_highlightOverlay) {
|
|
1116
|
+
_highlightOverlay.highlightRects = @[];
|
|
1117
|
+
[_highlightOverlay setNeedsDisplay];
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
959
1121
|
-(NSString *) getTableContents
|
|
960
1122
|
{
|
|
961
1123
|
|
|
@@ -1073,6 +1235,7 @@ using namespace facebook::react;
|
|
|
1073
1235
|
|
|
1074
1236
|
RLog(@"Enhanced PDF: Navigated to page %d", _page);
|
|
1075
1237
|
[self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"pageChanged|%lu|%lu", page+1, numberOfPages]]];
|
|
1238
|
+
if (_highlightOverlay) [_highlightOverlay setNeedsDisplay];
|
|
1076
1239
|
}
|
|
1077
1240
|
|
|
1078
1241
|
}
|
|
@@ -1087,6 +1250,7 @@ using namespace facebook::react;
|
|
|
1087
1250
|
[self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"scaleChanged|%f", _scale]]];
|
|
1088
1251
|
}
|
|
1089
1252
|
}
|
|
1253
|
+
if (_highlightOverlay) [_highlightOverlay setNeedsDisplay];
|
|
1090
1254
|
}
|
|
1091
1255
|
|
|
1092
1256
|
#pragma mark gesture process
|
|
@@ -1328,6 +1492,18 @@ using namespace facebook::react;
|
|
|
1328
1492
|
// Keep vertical bounce enabled for natural scrolling feel
|
|
1329
1493
|
scrollView.bounces = YES;
|
|
1330
1494
|
|
|
1495
|
+
// Configure scroll view zoom scales to match PDFView's scale factors
|
|
1496
|
+
// This enables native pinch-to-zoom gestures
|
|
1497
|
+
if (_fixScaleFactor > 0) {
|
|
1498
|
+
scrollView.minimumZoomScale = _fixScaleFactor * _minScale;
|
|
1499
|
+
scrollView.maximumZoomScale = _fixScaleFactor * _maxScale;
|
|
1500
|
+
scrollView.zoomScale = _pdfView.scaleFactor;
|
|
1501
|
+
RCTLogInfo(@"🔍 [iOS Zoom] Configured zoom scales - min=%f, max=%f, current=%f",
|
|
1502
|
+
scrollView.minimumZoomScale,
|
|
1503
|
+
scrollView.maximumZoomScale,
|
|
1504
|
+
scrollView.zoomScale);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1331
1507
|
RCTLogInfo(@"📊 [iOS Scroll] ScrollView config - scrollEnabled=%d, alwaysBounceHorizontal=%d, bounces=%d, delegate=%@",
|
|
1332
1508
|
scrollView.scrollEnabled,
|
|
1333
1509
|
scrollView.alwaysBounceHorizontal,
|
|
@@ -1336,23 +1512,30 @@ using namespace facebook::react;
|
|
|
1336
1512
|
|
|
1337
1513
|
// IMPORTANT: PDFKit relies on the scrollView delegate for pinch-zoom (viewForZoomingInScrollView).
|
|
1338
1514
|
// Install a proxy delegate that forwards to the original delegate, while still letting us observe scroll events.
|
|
1339
|
-
|
|
1340
|
-
|
|
1515
|
+
// CRITICAL FIX: Always set up delegate for new scroll views (PDFView may recreate scroll view on document load)
|
|
1516
|
+
if (!_internalScrollView || _internalScrollView != scrollView) {
|
|
1517
|
+
RCTLogInfo(@"✅ [iOS Scroll] Setting up scroll view delegate (new=%d)", _internalScrollView == nil);
|
|
1341
1518
|
_internalScrollView = scrollView;
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1519
|
+
|
|
1520
|
+
// Get the current delegate (might be PDFView's internal delegate)
|
|
1521
|
+
id<UIScrollViewDelegate> currentDelegate = scrollView.delegate;
|
|
1522
|
+
|
|
1523
|
+
// Only capture original delegate if it's not us or our proxy
|
|
1524
|
+
if (currentDelegate && currentDelegate != self && ![currentDelegate isKindOfClass:[RNPDFScrollViewDelegateProxy class]]) {
|
|
1525
|
+
_originalScrollDelegate = currentDelegate;
|
|
1526
|
+
RCTLogInfo(@"📝 [iOS Scroll] Captured original scroll delegate: %@", NSStringFromClass([currentDelegate class]));
|
|
1345
1527
|
}
|
|
1528
|
+
|
|
1346
1529
|
if (_originalScrollDelegate) {
|
|
1347
1530
|
_scrollDelegateProxy = [[RNPDFScrollViewDelegateProxy alloc] initWithPrimary:_originalScrollDelegate secondary:(id<UIScrollViewDelegate>)self];
|
|
1348
1531
|
scrollView.delegate = (id<UIScrollViewDelegate>)_scrollDelegateProxy;
|
|
1349
1532
|
RCTLogInfo(@"🔗 [iOS Scroll] Installed scroll delegate proxy");
|
|
1350
1533
|
} else {
|
|
1351
1534
|
scrollView.delegate = self;
|
|
1352
|
-
RCTLogInfo(@"🔗 [iOS Scroll] Set self as scroll delegate");
|
|
1535
|
+
RCTLogInfo(@"🔗 [iOS Scroll] Set self as scroll delegate (no original delegate)");
|
|
1353
1536
|
}
|
|
1354
1537
|
} else {
|
|
1355
|
-
RCTLogInfo(@"⚠️ [iOS Scroll]
|
|
1538
|
+
RCTLogInfo(@"⚠️ [iOS Scroll] Same scroll view, delegate already configured");
|
|
1356
1539
|
}
|
|
1357
1540
|
}
|
|
1358
1541
|
|
|
@@ -1371,9 +1554,11 @@ using namespace facebook::react;
|
|
|
1371
1554
|
#pragma mark - UIScrollViewDelegate
|
|
1372
1555
|
|
|
1373
1556
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
|
1557
|
+
// Redraw highlight overlay so rects stay aligned when user scrolls (pan)
|
|
1558
|
+
if (_highlightOverlay) [_highlightOverlay setNeedsDisplay];
|
|
1374
1559
|
static int scrollEventCount = 0;
|
|
1375
1560
|
scrollEventCount++;
|
|
1376
|
-
|
|
1561
|
+
|
|
1377
1562
|
// Log scroll events periodically (every 10th event to avoid spam)
|
|
1378
1563
|
if (scrollEventCount % 10 == 0) {
|
|
1379
1564
|
RCTLogInfo(@"📜 [iOS Scroll] scrollViewDidScroll #%d - offset=(%.2f, %.2f), contentSize=(%.2f, %.2f), bounds=(%.2f, %.2f), scrollEnabled=%d",
|
|
@@ -1440,6 +1625,59 @@ using namespace facebook::react;
|
|
|
1440
1625
|
}
|
|
1441
1626
|
}
|
|
1442
1627
|
|
|
1628
|
+
#pragma mark - UIScrollViewDelegate Zoom Support
|
|
1629
|
+
|
|
1630
|
+
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
|
|
1631
|
+
// Called during pinch-to-zoom — redraw highlight overlay so rects stay aligned with zoomed content
|
|
1632
|
+
if (_highlightOverlay) [_highlightOverlay setNeedsDisplay];
|
|
1633
|
+
if (_fixScaleFactor > 0 && _pdfView.scaleFactor > 0) {
|
|
1634
|
+
float newScale = _pdfView.scaleFactor / _fixScaleFactor;
|
|
1635
|
+
|
|
1636
|
+
// Only notify if scale changed significantly (prevent spam)
|
|
1637
|
+
if (fabs(_scale - newScale) > 0.01f) {
|
|
1638
|
+
_scale = newScale;
|
|
1639
|
+
RCTLogInfo(@"🔍 [iOS Zoom] Pinch zoom - scale changed to %f", _scale);
|
|
1640
|
+
[self notifyOnChangeWithMessage:[[NSString alloc] initWithString:
|
|
1641
|
+
[NSString stringWithFormat:@"scaleChanged|%f", _scale]]];
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// CRITICAL: Return the view that should be zoomed
|
|
1647
|
+
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
|
|
1648
|
+
// Search for PDFDocumentView in the scroll view's hierarchy
|
|
1649
|
+
for (UIView *subview in scrollView.subviews) {
|
|
1650
|
+
NSString *className = NSStringFromClass([subview class]);
|
|
1651
|
+
if ([className containsString:@"PDFDocumentView"] || [className containsString:@"PDFPage"]) {
|
|
1652
|
+
RCTLogInfo(@"🔍 [iOS Zoom] viewForZoomingInScrollView returning: %@", className);
|
|
1653
|
+
return subview;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
// Fallback to first subview
|
|
1658
|
+
UIView *fallback = scrollView.subviews.firstObject;
|
|
1659
|
+
if (fallback) {
|
|
1660
|
+
RCTLogInfo(@"🔍 [iOS Zoom] viewForZoomingInScrollView using fallback: %@", NSStringFromClass([fallback class]));
|
|
1661
|
+
return fallback;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
RCTLogInfo(@"⚠️ [iOS Zoom] viewForZoomingInScrollView returning NIL - no view to zoom!");
|
|
1665
|
+
return nil;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
|
|
1669
|
+
// Optional: Track zoom start
|
|
1670
|
+
RCTLogInfo(@"🔍 [iOS Zoom] Will begin zooming");
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView
|
|
1674
|
+
withView:(UIView *)view
|
|
1675
|
+
atScale:(CGFloat)scale {
|
|
1676
|
+
// Redraw highlight overlay so rects match final zoom level
|
|
1677
|
+
if (_highlightOverlay) [_highlightOverlay setNeedsDisplay];
|
|
1678
|
+
RCTLogInfo(@"🔍 [iOS Zoom] Did end zooming at scale %f", scale);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1443
1681
|
// Enhanced progressive loading methods
|
|
1444
1682
|
- (void)preloadAdjacentPages:(int)currentPage
|
|
1445
1683
|
{
|
|
@@ -52,6 +52,8 @@ RCT_EXPORT_VIEW_PROPERTY(spacing, int);
|
|
|
52
52
|
RCT_EXPORT_VIEW_PROPERTY(password, NSString);
|
|
53
53
|
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock);
|
|
54
54
|
RCT_EXPORT_VIEW_PROPERTY(singlePage, BOOL);
|
|
55
|
+
RCT_EXPORT_VIEW_PROPERTY(pdfId, NSString);
|
|
56
|
+
RCT_EXPORT_VIEW_PROPERTY(highlightRects, NSArray);
|
|
55
57
|
|
|
56
58
|
RCT_EXPORT_METHOD(supportPDFKit:(RCTResponseSenderBlock)callback)
|
|
57
59
|
{
|
|
@@ -177,12 +179,14 @@ RCT_EXPORT_METHOD(optimizeMemory:(NSString *)pdfId
|
|
|
177
179
|
|
|
178
180
|
RCT_EXPORT_METHOD(searchTextDirect:(NSString *)pdfId
|
|
179
181
|
searchTerm:(NSString *)searchTerm
|
|
182
|
+
startPage:(NSInteger)startPage
|
|
183
|
+
endPage:(NSInteger)endPage
|
|
180
184
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
181
185
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
182
186
|
{
|
|
183
187
|
PDFJSIManager *jsiManager = [self.bridge moduleForClass:[PDFJSIManager class]];
|
|
184
188
|
if (jsiManager) {
|
|
185
|
-
[jsiManager searchTextDirect:pdfId searchTerm:searchTerm startPage:
|
|
189
|
+
[jsiManager searchTextDirect:pdfId searchTerm:searchTerm startPage:startPage endPage:endPage resolver:resolve rejecter:reject];
|
|
186
190
|
} else {
|
|
187
191
|
reject(@"JSI_NOT_AVAILABLE", @"PDFJSIManager not available", nil);
|
|
188
192
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry mapping pdfId to current PDF file path for programmatic search.
|
|
3
|
+
* RNPDFPdfView registers when a document loads with pdfId; searchTextDirect looks up path by pdfId.
|
|
4
|
+
* Also stores PDF page sizes in points (per pdfId + pageIndex) for highlight coordinate scaling.
|
|
5
|
+
*/
|
|
6
|
+
#import <Foundation/Foundation.h>
|
|
7
|
+
|
|
8
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
9
|
+
|
|
10
|
+
@interface SearchRegistry : NSObject
|
|
11
|
+
|
|
12
|
+
+ (void)registerPath:(NSString *)pdfId path:(NSString *)path;
|
|
13
|
+
+ (void)unregisterPath:(NSString *)pdfId;
|
|
14
|
+
+ (nullable NSString *)pathForPdfId:(NSString *)pdfId;
|
|
15
|
+
|
|
16
|
+
+ (void)registerPageSizePointsForPdfId:(NSString *)pdfId pageIndex0Based:(NSInteger)pageIndex widthPt:(CGFloat)widthPt heightPt:(CGFloat)heightPt;
|
|
17
|
+
+ (void)getPageSizePointsForPdfId:(NSString *)pdfId pageIndex0Based:(NSInteger)pageIndex widthOut:(CGFloat *)widthOut heightOut:(CGFloat *)heightOut;
|
|
18
|
+
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry mapping pdfId to current PDF file path for programmatic search.
|
|
3
|
+
*/
|
|
4
|
+
#import "SearchRegistry.h"
|
|
5
|
+
|
|
6
|
+
@implementation SearchRegistry
|
|
7
|
+
|
|
8
|
+
static NSMutableDictionary<NSString *, NSString *> *_pathByPdfId;
|
|
9
|
+
static NSMutableDictionary<NSString *, NSValue *> *_pageSizeByKey; // key = "pdfId_pageIndex", value = NSValue with CGSize
|
|
10
|
+
static dispatch_queue_t _queue;
|
|
11
|
+
|
|
12
|
+
+ (void)initialize {
|
|
13
|
+
if (self == [SearchRegistry class]) {
|
|
14
|
+
_pathByPdfId = [NSMutableDictionary new];
|
|
15
|
+
_pageSizeByKey = [NSMutableDictionary new];
|
|
16
|
+
_queue = dispatch_queue_create("com.rnpdf.searchregistry", DISPATCH_QUEUE_SERIAL);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
+ (void)registerPath:(NSString *)pdfId path:(NSString *)path {
|
|
21
|
+
if (!pdfId.length || !path.length) return;
|
|
22
|
+
dispatch_sync(_queue, ^{
|
|
23
|
+
_pathByPdfId[pdfId] = path;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
+ (void)unregisterPath:(NSString *)pdfId {
|
|
28
|
+
if (!pdfId.length) return;
|
|
29
|
+
dispatch_sync(_queue, ^{
|
|
30
|
+
[_pathByPdfId removeObjectForKey:pdfId];
|
|
31
|
+
NSString *prefix = [pdfId stringByAppendingString:@"_"];
|
|
32
|
+
NSArray *keysToRemove = [_pageSizeByKey.allKeys filteredArrayUsingPredicate:
|
|
33
|
+
[NSPredicate predicateWithBlock:^BOOL(NSString *key, id _) { return [key hasPrefix:prefix]; }]];
|
|
34
|
+
[_pageSizeByKey removeObjectsForKeys:keysToRemove];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
+ (NSString *)pathForPdfId:(NSString *)pdfId {
|
|
39
|
+
if (!pdfId.length) return nil;
|
|
40
|
+
__block NSString *path = nil;
|
|
41
|
+
dispatch_sync(_queue, ^{
|
|
42
|
+
path = _pathByPdfId[pdfId];
|
|
43
|
+
});
|
|
44
|
+
return path;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
+ (void)registerPageSizePointsForPdfId:(NSString *)pdfId pageIndex0Based:(NSInteger)pageIndex widthPt:(CGFloat)widthPt heightPt:(CGFloat)heightPt {
|
|
48
|
+
if (!pdfId.length || widthPt <= 0 || heightPt <= 0) return;
|
|
49
|
+
NSString *key = [NSString stringWithFormat:@"%@_%ld", pdfId, (long)pageIndex];
|
|
50
|
+
dispatch_sync(_queue, ^{
|
|
51
|
+
_pageSizeByKey[key] = [NSValue valueWithCGSize:CGSizeMake(widthPt, heightPt)];
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
+ (void)getPageSizePointsForPdfId:(NSString *)pdfId pageIndex0Based:(NSInteger)pageIndex widthOut:(CGFloat *)widthOut heightOut:(CGFloat *)heightOut {
|
|
56
|
+
if (!pdfId.length || !widthOut || !heightOut) return;
|
|
57
|
+
*widthOut = 0;
|
|
58
|
+
*heightOut = 0;
|
|
59
|
+
NSString *key = [NSString stringWithFormat:@"%@_%ld", pdfId, (long)pageIndex];
|
|
60
|
+
__block NSValue *val = nil;
|
|
61
|
+
dispatch_sync(_queue, ^{
|
|
62
|
+
val = _pageSizeByKey[key];
|
|
63
|
+
});
|
|
64
|
+
if (val) {
|
|
65
|
+
CGSize s = [val CGSizeValue];
|
|
66
|
+
*widthOut = s.width;
|
|
67
|
+
*heightOut = s.height;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@end
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-pdf-jsi",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"summary": "High-performance React Native PDF viewer with JSI acceleration - up to 80x faster than traditional bridge",
|
|
5
5
|
"description": "🚀 Ultra-fast React Native PDF viewer with JSI (JavaScript Interface) integration for maximum performance. Features lazy loading, smart caching, progressive loading, and zero-bridge overhead operations. Perfect for large PDF files with 30-day persistent cache and advanced memory optimization. Google Play 16KB page size compliant for Android 15+. Supports iOS, Android, and Windows platforms.",
|
|
6
6
|
"main": "index.js",
|
|
@@ -80,7 +80,9 @@
|
|
|
80
80
|
},
|
|
81
81
|
"scripts": {
|
|
82
82
|
"build:plugin": "tsc -p plugin/tsconfig.json",
|
|
83
|
-
"prepublishOnly": "npm run build:plugin"
|
|
83
|
+
"prepublishOnly": "npm run build:plugin",
|
|
84
|
+
"clean:for-publish": "node -e \"const fs=require('fs'),path=require('path');['android/.cxx','android/.gradle','android/build'].forEach(p=>{try{fs.rmSync(path.join(__dirname,p),{recursive:true});console.log('Removed',p);}catch(e){}})\"",
|
|
85
|
+
"prepack": "npm run clean:for-publish"
|
|
84
86
|
},
|
|
85
87
|
"peerDependencies": {
|
|
86
88
|
"@react-native-async-storage/async-storage": ">=1.17.0",
|