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,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025-present, Punith M (punithm300@gmail.com)
|
|
3
|
+
* Enhanced PDF JSI Manager with high-performance operations
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* JSI Manager for high-performance PDF operations
|
|
7
|
+
* Provides React Native bridge integration for JSI PDF functions
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
package org.wonday.pdf;
|
|
11
|
+
|
|
12
|
+
import android.util.Log;
|
|
13
|
+
|
|
14
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
15
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
16
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
17
|
+
import com.facebook.react.bridge.Promise;
|
|
18
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
19
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
20
|
+
import com.facebook.react.bridge.WritableMap;
|
|
21
|
+
import com.facebook.react.bridge.Arguments;
|
|
22
|
+
|
|
23
|
+
// import com.facebook.react.turbomodule.core.CallInvokerHolder; // Not available in this RN version
|
|
24
|
+
import com.facebook.soloader.SoLoader;
|
|
25
|
+
|
|
26
|
+
import java.util.concurrent.ExecutorService;
|
|
27
|
+
import java.util.concurrent.Executors;
|
|
28
|
+
|
|
29
|
+
public class PDFJSIManager extends ReactContextBaseJavaModule {
|
|
30
|
+
private static final String MODULE_NAME = "PDFJSIManager";
|
|
31
|
+
private static final String TAG = "PDFJSI";
|
|
32
|
+
|
|
33
|
+
private ExecutorService backgroundExecutor;
|
|
34
|
+
private boolean isJSIInitialized = false;
|
|
35
|
+
|
|
36
|
+
// Load native library
|
|
37
|
+
static {
|
|
38
|
+
try {
|
|
39
|
+
SoLoader.loadLibrary("pdfjsi");
|
|
40
|
+
Log.d(TAG, "PDF JSI native library loaded successfully");
|
|
41
|
+
} catch (UnsatisfiedLinkError e) {
|
|
42
|
+
Log.e(TAG, "Failed to load PDF JSI native library", e);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public PDFJSIManager(ReactApplicationContext reactContext) {
|
|
47
|
+
super(reactContext);
|
|
48
|
+
this.backgroundExecutor = Executors.newFixedThreadPool(2);
|
|
49
|
+
|
|
50
|
+
Log.d(TAG, "PDFJSIManager: Initializing high-performance PDF JSI manager");
|
|
51
|
+
initializeJSI(reactContext);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Override
|
|
55
|
+
public String getName() {
|
|
56
|
+
return MODULE_NAME;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Initialize JSI integration
|
|
61
|
+
*/
|
|
62
|
+
private void initializeJSI(ReactApplicationContext reactContext) {
|
|
63
|
+
try {
|
|
64
|
+
// Initialize JSI on background thread
|
|
65
|
+
backgroundExecutor.execute(() -> {
|
|
66
|
+
try {
|
|
67
|
+
// Initialize JSI module without CallInvokerHolder (fallback mode)
|
|
68
|
+
nativeInitializeJSI(null);
|
|
69
|
+
isJSIInitialized = true;
|
|
70
|
+
Log.d(TAG, "PDF JSI initialized successfully (fallback mode)");
|
|
71
|
+
} catch (Exception e) {
|
|
72
|
+
Log.e(TAG, "Failed to initialize PDF JSI", e);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
} catch (Exception e) {
|
|
76
|
+
Log.e(TAG, "Error initializing PDF JSI", e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if JSI is available and initialized
|
|
82
|
+
*/
|
|
83
|
+
@ReactMethod
|
|
84
|
+
public void isJSIAvailable(Promise promise) {
|
|
85
|
+
try {
|
|
86
|
+
boolean available = isJSIInitialized && nativeIsJSIAvailable();
|
|
87
|
+
Log.d(TAG, "JSI Availability check: " + available);
|
|
88
|
+
promise.resolve(available);
|
|
89
|
+
} catch (Exception e) {
|
|
90
|
+
Log.e(TAG, "Error checking JSI availability", e);
|
|
91
|
+
promise.reject("JSI_CHECK_ERROR", e.getMessage());
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Render page directly via JSI (high-performance)
|
|
97
|
+
*/
|
|
98
|
+
@ReactMethod
|
|
99
|
+
public void renderPageDirect(String pdfId, int pageNumber, double scale, String base64Data, Promise promise) {
|
|
100
|
+
if (!isJSIInitialized) {
|
|
101
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
backgroundExecutor.execute(() -> {
|
|
106
|
+
try {
|
|
107
|
+
Log.d(TAG, "Rendering page " + pageNumber + " via JSI for PDF " + pdfId);
|
|
108
|
+
WritableMap result = nativeRenderPageDirect(pdfId, pageNumber, (float) scale, base64Data);
|
|
109
|
+
promise.resolve(result);
|
|
110
|
+
} catch (Exception e) {
|
|
111
|
+
Log.e(TAG, "Error rendering page via JSI", e);
|
|
112
|
+
promise.reject("RENDER_ERROR", e.getMessage());
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get page metrics via JSI
|
|
119
|
+
*/
|
|
120
|
+
@ReactMethod
|
|
121
|
+
public void getPageMetrics(String pdfId, int pageNumber, Promise promise) {
|
|
122
|
+
if (!isJSIInitialized) {
|
|
123
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
Log.d(TAG, "Getting page metrics via JSI for page " + pageNumber);
|
|
129
|
+
WritableMap metrics = nativeGetPageMetrics(pdfId, pageNumber);
|
|
130
|
+
promise.resolve(metrics);
|
|
131
|
+
} catch (Exception e) {
|
|
132
|
+
Log.e(TAG, "Error getting page metrics via JSI", e);
|
|
133
|
+
promise.reject("METRICS_ERROR", e.getMessage());
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Preload pages directly via JSI
|
|
139
|
+
*/
|
|
140
|
+
@ReactMethod
|
|
141
|
+
public void preloadPagesDirect(String pdfId, int startPage, int endPage, Promise promise) {
|
|
142
|
+
if (!isJSIInitialized) {
|
|
143
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
backgroundExecutor.execute(() -> {
|
|
148
|
+
try {
|
|
149
|
+
Log.d(TAG, "Preloading pages " + startPage + "-" + endPage + " via JSI");
|
|
150
|
+
boolean success = nativePreloadPagesDirect(pdfId, startPage, endPage);
|
|
151
|
+
promise.resolve(success);
|
|
152
|
+
} catch (Exception e) {
|
|
153
|
+
Log.e(TAG, "Error preloading pages via JSI", e);
|
|
154
|
+
promise.reject("PRELOAD_ERROR", e.getMessage());
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get cache metrics via JSI
|
|
161
|
+
*/
|
|
162
|
+
@ReactMethod
|
|
163
|
+
public void getCacheMetrics(String pdfId, Promise promise) {
|
|
164
|
+
if (!isJSIInitialized) {
|
|
165
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
Log.d(TAG, "Getting cache metrics via JSI for PDF " + pdfId);
|
|
171
|
+
WritableMap metrics = nativeGetCacheMetrics(pdfId);
|
|
172
|
+
promise.resolve(metrics);
|
|
173
|
+
} catch (Exception e) {
|
|
174
|
+
Log.e(TAG, "Error getting cache metrics via JSI", e);
|
|
175
|
+
promise.reject("CACHE_METRICS_ERROR", e.getMessage());
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Clear cache directly via JSI
|
|
181
|
+
*/
|
|
182
|
+
@ReactMethod
|
|
183
|
+
public void clearCacheDirect(String pdfId, String cacheType, Promise promise) {
|
|
184
|
+
if (!isJSIInitialized) {
|
|
185
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
backgroundExecutor.execute(() -> {
|
|
190
|
+
try {
|
|
191
|
+
Log.d(TAG, "Clearing cache via JSI for PDF " + pdfId + ", type: " + cacheType);
|
|
192
|
+
boolean success = nativeClearCacheDirect(pdfId, cacheType);
|
|
193
|
+
promise.resolve(success);
|
|
194
|
+
} catch (Exception e) {
|
|
195
|
+
Log.e(TAG, "Error clearing cache via JSI", e);
|
|
196
|
+
promise.reject("CLEAR_CACHE_ERROR", e.getMessage());
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Optimize memory via JSI
|
|
203
|
+
*/
|
|
204
|
+
@ReactMethod
|
|
205
|
+
public void optimizeMemory(String pdfId, Promise promise) {
|
|
206
|
+
if (!isJSIInitialized) {
|
|
207
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
backgroundExecutor.execute(() -> {
|
|
212
|
+
try {
|
|
213
|
+
Log.d(TAG, "Optimizing memory via JSI for PDF " + pdfId);
|
|
214
|
+
boolean success = nativeOptimizeMemory(pdfId);
|
|
215
|
+
promise.resolve(success);
|
|
216
|
+
} catch (Exception e) {
|
|
217
|
+
Log.e(TAG, "Error optimizing memory via JSI", e);
|
|
218
|
+
promise.reject("OPTIMIZE_MEMORY_ERROR", e.getMessage());
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Search text directly via JSI
|
|
225
|
+
*/
|
|
226
|
+
@ReactMethod
|
|
227
|
+
public void searchTextDirect(String pdfId, String searchTerm, int startPage, int endPage, Promise promise) {
|
|
228
|
+
if (!isJSIInitialized) {
|
|
229
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
backgroundExecutor.execute(() -> {
|
|
234
|
+
try {
|
|
235
|
+
Log.d(TAG, "Searching text via JSI: '" + searchTerm + "' in pages " + startPage + "-" + endPage);
|
|
236
|
+
ReadableArray results = nativeSearchTextDirect(pdfId, searchTerm, startPage, endPage);
|
|
237
|
+
promise.resolve(results);
|
|
238
|
+
} catch (Exception e) {
|
|
239
|
+
Log.e(TAG, "Error searching text via JSI", e);
|
|
240
|
+
promise.reject("SEARCH_ERROR", e.getMessage());
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get performance metrics via JSI
|
|
247
|
+
*/
|
|
248
|
+
@ReactMethod
|
|
249
|
+
public void getPerformanceMetrics(String pdfId, Promise promise) {
|
|
250
|
+
if (!isJSIInitialized) {
|
|
251
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
Log.d(TAG, "Getting performance metrics via JSI for PDF " + pdfId);
|
|
257
|
+
WritableMap metrics = nativeGetPerformanceMetrics(pdfId);
|
|
258
|
+
promise.resolve(metrics);
|
|
259
|
+
} catch (Exception e) {
|
|
260
|
+
Log.e(TAG, "Error getting performance metrics via JSI", e);
|
|
261
|
+
promise.reject("PERFORMANCE_METRICS_ERROR", e.getMessage());
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Set render quality via JSI
|
|
267
|
+
*/
|
|
268
|
+
@ReactMethod
|
|
269
|
+
public void setRenderQuality(String pdfId, int quality, Promise promise) {
|
|
270
|
+
if (!isJSIInitialized) {
|
|
271
|
+
promise.reject("JSI_NOT_INITIALIZED", "JSI is not initialized");
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
Log.d(TAG, "Setting render quality via JSI to " + quality + " for PDF " + pdfId);
|
|
277
|
+
boolean success = nativeSetRenderQuality(pdfId, quality);
|
|
278
|
+
promise.resolve(success);
|
|
279
|
+
} catch (Exception e) {
|
|
280
|
+
Log.e(TAG, "Error setting render quality via JSI", e);
|
|
281
|
+
promise.reject("SET_RENDER_QUALITY_ERROR", e.getMessage());
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Cleanup resources
|
|
287
|
+
*/
|
|
288
|
+
// Note: onCatalystInstanceDestroy is deprecated, using onCatalystInstanceDestroy for compatibility
|
|
289
|
+
@Override
|
|
290
|
+
public void onCatalystInstanceDestroy() {
|
|
291
|
+
super.onCatalystInstanceDestroy();
|
|
292
|
+
|
|
293
|
+
if (backgroundExecutor != null && !backgroundExecutor.isShutdown()) {
|
|
294
|
+
backgroundExecutor.shutdown();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (isJSIInitialized) {
|
|
298
|
+
nativeCleanupJSI();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
Log.d(TAG, "PDFJSIManager: Cleaned up resources");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Native method declarations
|
|
305
|
+
private native void nativeInitializeJSI(Object callInvokerHolder);
|
|
306
|
+
private native boolean nativeIsJSIAvailable();
|
|
307
|
+
private native WritableMap nativeRenderPageDirect(String pdfId, int pageNumber, float scale, String base64Data);
|
|
308
|
+
private native WritableMap nativeGetPageMetrics(String pdfId, int pageNumber);
|
|
309
|
+
private native boolean nativePreloadPagesDirect(String pdfId, int startPage, int endPage);
|
|
310
|
+
private native WritableMap nativeGetCacheMetrics(String pdfId);
|
|
311
|
+
private native boolean nativeClearCacheDirect(String pdfId, String cacheType);
|
|
312
|
+
private native boolean nativeOptimizeMemory(String pdfId);
|
|
313
|
+
private native ReadableArray nativeSearchTextDirect(String pdfId, String searchTerm, int startPage, int endPage);
|
|
314
|
+
private native WritableMap nativeGetPerformanceMetrics(String pdfId);
|
|
315
|
+
private native boolean nativeSetRenderQuality(String pdfId, int quality);
|
|
316
|
+
private native void nativeCleanupJSI();
|
|
317
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025-present, Punith M (punithm300@gmail.com)
|
|
3
|
+
* Enhanced PDF JSI Module with high-performance operations
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* React Native module for JSI PDF operations
|
|
7
|
+
* Provides high-performance PDF operations via JSI
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
package org.wonday.pdf;
|
|
11
|
+
|
|
12
|
+
import android.util.Log;
|
|
13
|
+
|
|
14
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
15
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
16
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
17
|
+
import com.facebook.react.bridge.Promise;
|
|
18
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
19
|
+
import com.facebook.react.bridge.WritableMap;
|
|
20
|
+
import com.facebook.react.bridge.Arguments;
|
|
21
|
+
|
|
22
|
+
// import com.facebook.react.turbomodule.core.CallInvokerHolder; // Not available in this RN version
|
|
23
|
+
import com.facebook.soloader.SoLoader;
|
|
24
|
+
|
|
25
|
+
import java.util.concurrent.ExecutorService;
|
|
26
|
+
import java.util.concurrent.Executors;
|
|
27
|
+
|
|
28
|
+
public class PDFJSIModule extends ReactContextBaseJavaModule {
|
|
29
|
+
private static final String MODULE_NAME = "PDFJSIModule";
|
|
30
|
+
private static final String TAG = "PDFJSIModule";
|
|
31
|
+
|
|
32
|
+
private ExecutorService backgroundExecutor;
|
|
33
|
+
private boolean isJSIInitialized = false;
|
|
34
|
+
|
|
35
|
+
// Load native library
|
|
36
|
+
static {
|
|
37
|
+
try {
|
|
38
|
+
SoLoader.loadLibrary("pdfjsi");
|
|
39
|
+
Log.d(TAG, "PDF JSI native library loaded successfully");
|
|
40
|
+
} catch (UnsatisfiedLinkError e) {
|
|
41
|
+
Log.e(TAG, "Failed to load PDF JSI native library", e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public PDFJSIModule(ReactApplicationContext reactContext) {
|
|
46
|
+
super(reactContext);
|
|
47
|
+
this.backgroundExecutor = Executors.newFixedThreadPool(2);
|
|
48
|
+
|
|
49
|
+
Log.d(TAG, "PDFJSIModule: Initializing high-performance PDF JSI module");
|
|
50
|
+
initializeJSI(reactContext);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@Override
|
|
54
|
+
public String getName() {
|
|
55
|
+
return MODULE_NAME;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Initialize JSI integration
|
|
60
|
+
*/
|
|
61
|
+
private void initializeJSI(ReactApplicationContext reactContext) {
|
|
62
|
+
try {
|
|
63
|
+
// Initialize JSI on background thread
|
|
64
|
+
backgroundExecutor.execute(() -> {
|
|
65
|
+
try {
|
|
66
|
+
// Initialize JSI module without CallInvokerHolder (fallback mode)
|
|
67
|
+
nativeInitializeJSI(null);
|
|
68
|
+
isJSIInitialized = true;
|
|
69
|
+
Log.d(TAG, "PDF JSI initialized successfully (fallback mode)");
|
|
70
|
+
} catch (Exception e) {
|
|
71
|
+
Log.e(TAG, "Failed to initialize PDF JSI", e);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
} catch (Exception e) {
|
|
75
|
+
Log.e(TAG, "Error initializing PDF JSI", e);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if JSI is available and initialized
|
|
81
|
+
*/
|
|
82
|
+
@ReactMethod
|
|
83
|
+
public void isJSIAvailable(Promise promise) {
|
|
84
|
+
try {
|
|
85
|
+
boolean available = isJSIInitialized && nativeIsJSIAvailable();
|
|
86
|
+
Log.d(TAG, "JSI Availability check: " + available);
|
|
87
|
+
promise.resolve(available);
|
|
88
|
+
} catch (Exception e) {
|
|
89
|
+
Log.e(TAG, "Error checking JSI availability", e);
|
|
90
|
+
promise.reject("JSI_CHECK_ERROR", e.getMessage());
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get JSI performance statistics
|
|
96
|
+
*/
|
|
97
|
+
@ReactMethod
|
|
98
|
+
public void getJSIStats(Promise promise) {
|
|
99
|
+
try {
|
|
100
|
+
Log.d(TAG, "Getting JSI stats");
|
|
101
|
+
|
|
102
|
+
WritableMap stats = Arguments.createMap();
|
|
103
|
+
stats.putString("version", "1.0.0");
|
|
104
|
+
stats.putString("buildDate", "2025-01-01");
|
|
105
|
+
stats.putBoolean("jsiEnabled", true);
|
|
106
|
+
stats.putBoolean("bridgeOptimized", true);
|
|
107
|
+
stats.putBoolean("directMemoryAccess", true);
|
|
108
|
+
stats.putInt("availableMethods", 9);
|
|
109
|
+
stats.putString("performanceLevel", "HIGH");
|
|
110
|
+
stats.putBoolean("isInitialized", isJSIInitialized);
|
|
111
|
+
|
|
112
|
+
promise.resolve(stats);
|
|
113
|
+
|
|
114
|
+
} catch (Exception e) {
|
|
115
|
+
Log.e(TAG, "Error getting JSI stats", e);
|
|
116
|
+
promise.reject("GET_JSI_STATS_ERROR", e.getMessage());
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Initialize JSI with custom configuration
|
|
122
|
+
*/
|
|
123
|
+
@ReactMethod
|
|
124
|
+
public void initializeJSIConfig(ReadableMap config, Promise promise) {
|
|
125
|
+
try {
|
|
126
|
+
Log.d(TAG, "Initializing JSI with custom configuration");
|
|
127
|
+
|
|
128
|
+
boolean enablePerformanceTracking = config.getBoolean("enablePerformanceTracking");
|
|
129
|
+
boolean enableCaching = config.getBoolean("enableCaching");
|
|
130
|
+
int maxCacheSize = config.getInt("maxCacheSize");
|
|
131
|
+
|
|
132
|
+
boolean success = nativeInitializeJSIConfig(enablePerformanceTracking, enableCaching, maxCacheSize);
|
|
133
|
+
|
|
134
|
+
WritableMap result = Arguments.createMap();
|
|
135
|
+
result.putBoolean("success", success);
|
|
136
|
+
result.putBoolean("jsiEnabled", success);
|
|
137
|
+
result.putString("message", success ? "JSI configured successfully" : "Failed to configure JSI");
|
|
138
|
+
|
|
139
|
+
promise.resolve(result);
|
|
140
|
+
|
|
141
|
+
} catch (Exception e) {
|
|
142
|
+
Log.e(TAG, "Error initializing JSI config", e);
|
|
143
|
+
promise.reject("JSI_CONFIG_ERROR", e.getMessage());
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get memory usage statistics
|
|
149
|
+
*/
|
|
150
|
+
@ReactMethod
|
|
151
|
+
public void getMemoryStats(Promise promise) {
|
|
152
|
+
try {
|
|
153
|
+
Log.d(TAG, "Getting memory statistics");
|
|
154
|
+
|
|
155
|
+
WritableMap stats = nativeGetMemoryStats();
|
|
156
|
+
promise.resolve(stats);
|
|
157
|
+
|
|
158
|
+
} catch (Exception e) {
|
|
159
|
+
Log.e(TAG, "Error getting memory stats", e);
|
|
160
|
+
promise.reject("MEMORY_STATS_ERROR", e.getMessage());
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Cleanup resources
|
|
166
|
+
*/
|
|
167
|
+
// Note: onCatalystInstanceDestroy is deprecated, using onCatalystInstanceDestroy for compatibility
|
|
168
|
+
@Override
|
|
169
|
+
public void onCatalystInstanceDestroy() {
|
|
170
|
+
super.onCatalystInstanceDestroy();
|
|
171
|
+
|
|
172
|
+
if (backgroundExecutor != null && !backgroundExecutor.isShutdown()) {
|
|
173
|
+
backgroundExecutor.shutdown();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (isJSIInitialized) {
|
|
177
|
+
nativeCleanupJSI();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Log.d(TAG, "PDFJSIModule: Cleaned up resources");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Native method declarations
|
|
184
|
+
private native void nativeInitializeJSI(Object callInvokerHolder);
|
|
185
|
+
private native boolean nativeIsJSIAvailable();
|
|
186
|
+
private native boolean nativeInitializeJSIConfig(boolean enablePerformanceTracking, boolean enableCaching, int maxCacheSize);
|
|
187
|
+
private native WritableMap nativeGetMemoryStats();
|
|
188
|
+
private native void nativeCleanupJSI();
|
|
189
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
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
|
+
package org.wonday.pdf;
|
|
10
|
+
|
|
11
|
+
import android.content.Context;
|
|
12
|
+
|
|
13
|
+
import androidx.annotation.NonNull;
|
|
14
|
+
import androidx.annotation.Nullable;
|
|
15
|
+
|
|
16
|
+
import com.facebook.infer.annotation.Assertions;
|
|
17
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
18
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
19
|
+
import com.facebook.react.module.annotations.ReactModule;
|
|
20
|
+
import com.facebook.react.uimanager.SimpleViewManager;
|
|
21
|
+
import com.facebook.react.uimanager.ThemedReactContext;
|
|
22
|
+
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
23
|
+
|
|
24
|
+
import com.facebook.react.uimanager.ViewManagerDelegate;
|
|
25
|
+
import com.facebook.react.viewmanagers.RNPDFPdfViewManagerDelegate;
|
|
26
|
+
import com.facebook.react.viewmanagers.RNPDFPdfViewManagerInterface;
|
|
27
|
+
|
|
28
|
+
@ReactModule(name = PdfManager.REACT_CLASS)
|
|
29
|
+
public class PdfManager extends SimpleViewManager<PdfView> implements RNPDFPdfViewManagerInterface<PdfView> {
|
|
30
|
+
public static final String REACT_CLASS = "RNPDFPdfView";
|
|
31
|
+
private Context context;
|
|
32
|
+
private PdfView pdfView;
|
|
33
|
+
private final ViewManagerDelegate<PdfView> mDelegate;
|
|
34
|
+
|
|
35
|
+
@Nullable
|
|
36
|
+
@Override
|
|
37
|
+
protected ViewManagerDelegate<PdfView> getDelegate() {
|
|
38
|
+
return mDelegate;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public PdfManager() {
|
|
42
|
+
mDelegate = new RNPDFPdfViewManagerDelegate<>(this);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public PdfManager(ReactApplicationContext reactContext){
|
|
46
|
+
this.context = reactContext;
|
|
47
|
+
mDelegate = new RNPDFPdfViewManagerDelegate<>(this);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
public String getName() {
|
|
52
|
+
return REACT_CLASS;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Override
|
|
56
|
+
public PdfView createViewInstance(ThemedReactContext context) {
|
|
57
|
+
this.pdfView = new PdfView(context,null);
|
|
58
|
+
return pdfView;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Override
|
|
62
|
+
public void onDropViewInstance(PdfView pdfView) {
|
|
63
|
+
pdfView = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@ReactProp(name = "path")
|
|
67
|
+
public void setPath(PdfView pdfView, String path) {
|
|
68
|
+
pdfView.setPath(path);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// page start from 1
|
|
72
|
+
@ReactProp(name = "page")
|
|
73
|
+
public void setPage(PdfView pdfView, int page) {
|
|
74
|
+
pdfView.setPage(page);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@ReactProp(name = "scale")
|
|
78
|
+
public void setScale(PdfView pdfView, float scale) {
|
|
79
|
+
pdfView.setScale(scale);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@ReactProp(name = "minScale")
|
|
83
|
+
public void setMinScale(PdfView pdfView, float minScale) {
|
|
84
|
+
pdfView.setMinScale(minScale);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@ReactProp(name = "maxScale")
|
|
88
|
+
public void setMaxScale(PdfView pdfView, float maxScale) {
|
|
89
|
+
pdfView.setMaxScale(maxScale);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@ReactProp(name = "horizontal")
|
|
93
|
+
public void setHorizontal(PdfView pdfView, boolean horizontal) {
|
|
94
|
+
pdfView.setHorizontal(horizontal);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Override
|
|
98
|
+
public void setShowsHorizontalScrollIndicator(PdfView view, boolean value) {
|
|
99
|
+
// NOOP on Android
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@Override
|
|
103
|
+
public void setShowsVerticalScrollIndicator(PdfView view, boolean value) {
|
|
104
|
+
// NOOP on Android
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@ReactProp(name = "scrollEnabled")
|
|
108
|
+
public void setScrollEnabled(PdfView view, boolean scrollEnabled) {
|
|
109
|
+
pdfView.setScrollEnabled(scrollEnabled);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@ReactProp(name = "spacing")
|
|
113
|
+
public void setSpacing(PdfView pdfView, int spacing) {
|
|
114
|
+
pdfView.setSpacing(spacing);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@ReactProp(name = "password")
|
|
118
|
+
public void setPassword(PdfView pdfView, String password) {
|
|
119
|
+
pdfView.setPassword(password);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@ReactProp(name = "enableAntialiasing")
|
|
123
|
+
public void setEnableAntialiasing(PdfView pdfView, boolean enableAntialiasing) {
|
|
124
|
+
pdfView.setEnableAntialiasing(enableAntialiasing);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@ReactProp(name = "enableAnnotationRendering")
|
|
128
|
+
public void setEnableAnnotationRendering(PdfView pdfView, boolean enableAnnotationRendering) {
|
|
129
|
+
pdfView.setEnableAnnotationRendering(enableAnnotationRendering);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@ReactProp(name = "enableDoubleTapZoom")
|
|
133
|
+
public void setEnableDoubleTapZoom(PdfView pdfView, boolean enableDoubleTap) {
|
|
134
|
+
pdfView.setEnableDoubleTapZoom(enableDoubleTap);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@ReactProp(name = "enablePaging")
|
|
138
|
+
public void setEnablePaging(PdfView pdfView, boolean enablePaging) {
|
|
139
|
+
pdfView.setEnablePaging(enablePaging);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@Override
|
|
143
|
+
public void setEnableRTL(PdfView view, boolean value) {
|
|
144
|
+
// NOOP on Android
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@ReactProp(name = "fitPolicy")
|
|
148
|
+
public void setFitPolicy(PdfView pdfView, int fitPolicy) {
|
|
149
|
+
pdfView.setFitPolicy(fitPolicy);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@ReactProp(name = "singlePage")
|
|
153
|
+
public void setSinglePage(PdfView pdfView, boolean singlePage) {
|
|
154
|
+
pdfView.setSinglePage(singlePage);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// It seems funny, but this method is called through delegate on Paper, but on Fabric we need to
|
|
158
|
+
// use `receiveCommand` method and call this one there
|
|
159
|
+
@Override
|
|
160
|
+
public void setNativePage(PdfView view, int page) {
|
|
161
|
+
pdfView.setPage(page);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@Override
|
|
165
|
+
public void receiveCommand(@NonNull PdfView root, String commandId, @androidx.annotation.Nullable ReadableArray args) {
|
|
166
|
+
Assertions.assertNotNull(root);
|
|
167
|
+
if ("setNativePage".equals(commandId)) {
|
|
168
|
+
Assertions.assertNotNull(args);
|
|
169
|
+
assert args != null;
|
|
170
|
+
setNativePage(root, args.getInt(0));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@Override
|
|
175
|
+
public void onAfterUpdateTransaction(PdfView pdfView) {
|
|
176
|
+
super.onAfterUpdateTransaction(pdfView);
|
|
177
|
+
pdfView.drawPdf();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
}
|