@papyrus-sdk/engine-native 0.1.1 → 0.1.3
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/LICENSE +21 -0
- package/android/build.gradle +40 -40
- package/android/src/main/AndroidManifest.xml +3 -3
- package/android/src/main/java/com/papyrus/engine/PapyrusEngineStore.java +71 -71
- package/android/src/main/java/com/papyrus/engine/PapyrusNativeEngineModule.java +533 -509
- package/android/src/main/java/com/papyrus/engine/PapyrusNativeEngineModule.kt +561 -0
- package/android/src/main/java/com/papyrus/engine/PapyrusOutline.java +20 -20
- package/android/src/main/java/com/papyrus/engine/PapyrusOutlineItem.java +13 -13
- package/android/src/main/java/com/papyrus/engine/PapyrusPackage.java +24 -24
- package/android/src/main/java/com/papyrus/engine/PapyrusPageView.java +86 -86
- package/android/src/main/java/com/papyrus/engine/PapyrusPageViewManager.java +16 -16
- package/android/src/main/java/com/papyrus/engine/PapyrusPageViewModule.kt +12 -0
- package/android/src/main/java/com/papyrus/engine/PapyrusTextHit.java +15 -15
- package/android/src/main/java/com/papyrus/engine/PapyrusTextSearch.java +20 -20
- package/android/src/main/java/com/papyrus/engine/PapyrusTextSelect.java +20 -20
- package/android/src/main/java/com/papyrus/engine/PapyrusTextSelection.java +11 -11
- package/dist/index.d.mts +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +213 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +214 -9
- package/dist/index.mjs.map +1 -1
- package/ios/PapyrusNativeEngine.podspec +1 -1
- package/ios/PapyrusPageViewManager.m +19 -19
- package/package.json +30 -30
- package/react-native.config.js +10 -10
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
package com.papyrus.engine
|
|
2
|
+
|
|
3
|
+
import android.content.ContentResolver
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.os.ParcelFileDescriptor
|
|
7
|
+
import android.view.View
|
|
8
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
9
|
+
import com.shockwave.pdfium.PdfDocument
|
|
10
|
+
import expo.modules.kotlin.Promise
|
|
11
|
+
import expo.modules.kotlin.modules.Module
|
|
12
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
13
|
+
import expo.modules.kotlin.typedarray.Uint8Array
|
|
14
|
+
import java.io.File
|
|
15
|
+
import java.io.FileOutputStream
|
|
16
|
+
import java.io.IOException
|
|
17
|
+
import java.io.InputStream
|
|
18
|
+
import java.lang.reflect.Method
|
|
19
|
+
import java.net.HttpURLConnection
|
|
20
|
+
import java.net.URL
|
|
21
|
+
import java.util.concurrent.ExecutorService
|
|
22
|
+
import java.util.concurrent.Executors
|
|
23
|
+
|
|
24
|
+
class PapyrusNativeEngineModule : Module() {
|
|
25
|
+
private val executor: ExecutorService = Executors.newSingleThreadExecutor()
|
|
26
|
+
|
|
27
|
+
override fun definition() = ModuleDefinition {
|
|
28
|
+
Name("PapyrusNativeEngine")
|
|
29
|
+
|
|
30
|
+
Function("createEngine") {
|
|
31
|
+
val context = appContext.reactContext ?: return@Function "default"
|
|
32
|
+
PapyrusEngineStore.createEngine(context)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Function("destroyEngine") { engineId: String ->
|
|
36
|
+
PapyrusEngineStore.destroyEngine(engineId)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
AsyncFunction("load") { engineId: String, source: Map<String, Any?>, promise: Promise ->
|
|
40
|
+
executor.execute {
|
|
41
|
+
try {
|
|
42
|
+
val state = PapyrusEngineStore.getEngine(engineId)
|
|
43
|
+
if (state == null) {
|
|
44
|
+
promise.reject("papyrus_no_engine", "Engine not found", null)
|
|
45
|
+
return@execute
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
val context = appContext.reactContext ?: throw IllegalStateException("React context missing")
|
|
49
|
+
val file = materializeSource(source, context)
|
|
50
|
+
if (file == null) {
|
|
51
|
+
promise.reject("papyrus_invalid_source", "Unsupported PDF source", null)
|
|
52
|
+
return@execute
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
val fd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
|
|
56
|
+
val document = state.pdfium.newDocument(fd)
|
|
57
|
+
PapyrusEngineStore.setDocument(state, document, fd, file.absolutePath)
|
|
58
|
+
|
|
59
|
+
val pageCount = state.pdfium.getPageCount(document)
|
|
60
|
+
promise.resolve(mapOf("pageCount" to pageCount))
|
|
61
|
+
} catch (error: Throwable) {
|
|
62
|
+
promise.reject("papyrus_load_failed", error.message, error)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Function("getPageCount") { engineId: String ->
|
|
68
|
+
val state = PapyrusEngineStore.getEngine(engineId)
|
|
69
|
+
if (state == null || state.document == null) return@Function 0
|
|
70
|
+
state.pdfium.getPageCount(state.document)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
AsyncFunction("renderPage") { engineId: String, pageIndex: Int, target: Int, scale: Double, zoom: Double, rotation: Int ->
|
|
74
|
+
val state = PapyrusEngineStore.getEngine(engineId) ?: return@AsyncFunction
|
|
75
|
+
UiThreadUtil.runOnUiThread {
|
|
76
|
+
val view = appContext.findView<View>(target)
|
|
77
|
+
if (view is PapyrusPageView) {
|
|
78
|
+
view.render(state, pageIndex, scale.toFloat(), zoom.toFloat(), rotation)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
AsyncFunction("renderTextLayer") { engineId: String, pageIndex: Int, target: Int, scale: Double, zoom: Double, rotation: Int ->
|
|
84
|
+
// no-op
|
|
85
|
+
Unit
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
AsyncFunction("getTextContent") { engineId: String, pageIndex: Int, promise: Promise ->
|
|
89
|
+
executor.execute {
|
|
90
|
+
val state = PapyrusEngineStore.getEngine(engineId)
|
|
91
|
+
if (state == null || state.document == null) {
|
|
92
|
+
promise.resolve(emptyList<Any>())
|
|
93
|
+
return@execute
|
|
94
|
+
}
|
|
95
|
+
val text = synchronized(state.pdfiumLock) { extractPageText(state, pageIndex) }
|
|
96
|
+
val items = mutableListOf<Map<String, Any>>()
|
|
97
|
+
if (!text.isNullOrEmpty()) {
|
|
98
|
+
items.add(
|
|
99
|
+
mapOf(
|
|
100
|
+
"str" to text,
|
|
101
|
+
"dir" to "ltr",
|
|
102
|
+
"width" to 0,
|
|
103
|
+
"height" to 0,
|
|
104
|
+
"transform" to listOf(1, 0, 0, 1, 0, 0),
|
|
105
|
+
"fontName" to ""
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
promise.resolve(items)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
AsyncFunction("getPageDimensions") { engineId: String, pageIndex: Int, promise: Promise ->
|
|
114
|
+
executor.execute {
|
|
115
|
+
val state = PapyrusEngineStore.getEngine(engineId)
|
|
116
|
+
if (state == null || state.document == null) {
|
|
117
|
+
promise.resolve(mapOf("width" to 0, "height" to 0))
|
|
118
|
+
return@execute
|
|
119
|
+
}
|
|
120
|
+
val (width, height) = synchronized(state.pdfiumLock) {
|
|
121
|
+
val w = state.pdfium.getPageWidthPoint(state.document, pageIndex)
|
|
122
|
+
val h = state.pdfium.getPageHeightPoint(state.document, pageIndex)
|
|
123
|
+
Pair(w, h)
|
|
124
|
+
}
|
|
125
|
+
promise.resolve(mapOf("width" to width, "height" to height))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
AsyncFunction("getOutline") { engineId: String, promise: Promise ->
|
|
130
|
+
executor.execute {
|
|
131
|
+
val state = PapyrusEngineStore.getEngine(engineId)
|
|
132
|
+
if (state == null || state.document == null) {
|
|
133
|
+
promise.resolve(emptyList<Any>())
|
|
134
|
+
return@execute
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
var items: Array<PapyrusOutlineItem>? = null
|
|
138
|
+
try {
|
|
139
|
+
if (PapyrusOutline.AVAILABLE) {
|
|
140
|
+
if (!state.sourcePath.isNullOrEmpty()) {
|
|
141
|
+
synchronized(state.pdfiumLock) {
|
|
142
|
+
items = PapyrusOutline.nativeGetOutlineFile(state.sourcePath)
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
val docPtr = synchronized(state.pdfiumLock) { extractNativeDocPointer(state.document) }
|
|
146
|
+
if (docPtr != 0L) {
|
|
147
|
+
synchronized(state.pdfiumLock) {
|
|
148
|
+
items = PapyrusOutline.nativeGetOutline(docPtr)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} catch (_: Throwable) {
|
|
154
|
+
items = null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
val result = mutableListOf<Map<String, Any?>>()
|
|
158
|
+
items?.forEach { item ->
|
|
159
|
+
result.add(serializeOutlineItem(item))
|
|
160
|
+
}
|
|
161
|
+
promise.resolve(result)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
AsyncFunction("getPageIndex") { engineId: String, dest: Map<String, Any?>, promise: Promise ->
|
|
166
|
+
val kind = dest["kind"] as? String
|
|
167
|
+
val value = dest["value"]
|
|
168
|
+
|
|
169
|
+
if (kind == "pageIndex" && value is Number) {
|
|
170
|
+
promise.resolve(value.toInt())
|
|
171
|
+
return@AsyncFunction
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (kind == "pageNumber" && value is Number) {
|
|
175
|
+
promise.resolve(maxOf(0, value.toInt() - 1))
|
|
176
|
+
return@AsyncFunction
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
promise.resolve(null)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
AsyncFunction("searchText") { engineId: String, query: String, promise: Promise ->
|
|
183
|
+
executor.execute {
|
|
184
|
+
val state = PapyrusEngineStore.getEngine(engineId)
|
|
185
|
+
if (state == null || state.document == null || query.length < 2) {
|
|
186
|
+
promise.resolve(emptyList<Any>())
|
|
187
|
+
return@execute
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
val pageCount = state.pdfium.getPageCount(state.document)
|
|
191
|
+
state.isSearching = true
|
|
192
|
+
try {
|
|
193
|
+
try {
|
|
194
|
+
if (PapyrusTextSearch.AVAILABLE) {
|
|
195
|
+
var hits: Array<PapyrusTextHit>? = null
|
|
196
|
+
if (!state.sourcePath.isNullOrEmpty()) {
|
|
197
|
+
synchronized(state.pdfiumLock) {
|
|
198
|
+
hits = PapyrusTextSearch.nativeSearchFile(state.sourcePath, query)
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
val docPtr = synchronized(state.pdfiumLock) { extractNativeDocPointer(state.document) }
|
|
202
|
+
if (docPtr != 0L) {
|
|
203
|
+
synchronized(state.pdfiumLock) {
|
|
204
|
+
hits = PapyrusTextSearch.nativeSearch(docPtr, pageCount, query)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!hits.isNullOrEmpty()) {
|
|
210
|
+
val results = mutableListOf<Map<String, Any?>>()
|
|
211
|
+
hits?.forEach { hit ->
|
|
212
|
+
val result = mutableMapOf<String, Any?>(
|
|
213
|
+
"pageIndex" to hit.pageIndex,
|
|
214
|
+
"text" to (hit.text ?: query),
|
|
215
|
+
"matchIndex" to hit.matchIndex
|
|
216
|
+
)
|
|
217
|
+
val rects = hit.rects
|
|
218
|
+
if (rects != null && rects.size >= 4) {
|
|
219
|
+
val rectList = mutableListOf<Map<String, Any>>()
|
|
220
|
+
var i = 0
|
|
221
|
+
while (i + 3 < rects.size) {
|
|
222
|
+
rectList.add(
|
|
223
|
+
mapOf(
|
|
224
|
+
"x" to rects[i],
|
|
225
|
+
"y" to rects[i + 1],
|
|
226
|
+
"width" to rects[i + 2],
|
|
227
|
+
"height" to rects[i + 3]
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
i += 4
|
|
231
|
+
}
|
|
232
|
+
result["rects"] = rectList
|
|
233
|
+
}
|
|
234
|
+
results.add(result)
|
|
235
|
+
}
|
|
236
|
+
promise.resolve(results)
|
|
237
|
+
return@execute
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} catch (_: Throwable) {
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
val normalizedQuery = query.lowercase()
|
|
244
|
+
val results = mutableListOf<Map<String, Any?>>()
|
|
245
|
+
|
|
246
|
+
for (pageIndex in 0 until pageCount) {
|
|
247
|
+
val text = synchronized(state.pdfiumLock) { extractPageText(state, pageIndex) } ?: ""
|
|
248
|
+
if (text.isEmpty()) continue
|
|
249
|
+
|
|
250
|
+
val lower = text.lowercase()
|
|
251
|
+
var pos = lower.indexOf(normalizedQuery)
|
|
252
|
+
var matchIndex = 0
|
|
253
|
+
while (pos != -1) {
|
|
254
|
+
val start = maxOf(0, pos - 20)
|
|
255
|
+
val end = minOf(text.length, pos + normalizedQuery.length + 20)
|
|
256
|
+
val preview = text.substring(start, end)
|
|
257
|
+
|
|
258
|
+
results.add(
|
|
259
|
+
mapOf(
|
|
260
|
+
"pageIndex" to pageIndex,
|
|
261
|
+
"text" to preview,
|
|
262
|
+
"matchIndex" to matchIndex++
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
pos = lower.indexOf(normalizedQuery, pos + 1)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
promise.resolve(results)
|
|
271
|
+
} finally {
|
|
272
|
+
state.isSearching = false
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
AsyncFunction("selectText") { engineId: String, pageIndex: Int, x: Double, y: Double, width: Double, height: Double, promise: Promise ->
|
|
278
|
+
executor.execute {
|
|
279
|
+
val state = PapyrusEngineStore.getEngine(engineId)
|
|
280
|
+
if (state == null || state.document == null || pageIndex < 0) {
|
|
281
|
+
promise.resolve(null)
|
|
282
|
+
return@execute
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!PapyrusTextSelect.AVAILABLE) {
|
|
286
|
+
promise.resolve(null)
|
|
287
|
+
return@execute
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
var selection: PapyrusTextSelection? = null
|
|
291
|
+
try {
|
|
292
|
+
if (!state.sourcePath.isNullOrEmpty()) {
|
|
293
|
+
synchronized(state.pdfiumLock) {
|
|
294
|
+
selection = PapyrusTextSelect.nativeSelectTextFile(
|
|
295
|
+
state.sourcePath,
|
|
296
|
+
pageIndex,
|
|
297
|
+
x.toFloat(),
|
|
298
|
+
y.toFloat(),
|
|
299
|
+
width.toFloat(),
|
|
300
|
+
height.toFloat()
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
val docPtr = synchronized(state.pdfiumLock) { extractNativeDocPointer(state.document) }
|
|
305
|
+
if (docPtr != 0L) {
|
|
306
|
+
synchronized(state.pdfiumLock) {
|
|
307
|
+
selection = PapyrusTextSelect.nativeSelectText(
|
|
308
|
+
docPtr,
|
|
309
|
+
pageIndex,
|
|
310
|
+
x.toFloat(),
|
|
311
|
+
y.toFloat(),
|
|
312
|
+
width.toFloat(),
|
|
313
|
+
height.toFloat()
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} catch (_: Throwable) {
|
|
319
|
+
selection = null
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
val rects = selection?.rects
|
|
323
|
+
if (selection == null || rects == null || rects.isEmpty()) {
|
|
324
|
+
promise.resolve(null)
|
|
325
|
+
return@execute
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
val rectList = mutableListOf<Map<String, Any>>()
|
|
329
|
+
var i = 0
|
|
330
|
+
while (i + 3 < rects.size) {
|
|
331
|
+
rectList.add(
|
|
332
|
+
mapOf(
|
|
333
|
+
"x" to rects[i],
|
|
334
|
+
"y" to rects[i + 1],
|
|
335
|
+
"width" to rects[i + 2],
|
|
336
|
+
"height" to rects[i + 3]
|
|
337
|
+
)
|
|
338
|
+
)
|
|
339
|
+
i += 4
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
promise.resolve(
|
|
343
|
+
mapOf(
|
|
344
|
+
"text" to (selection?.text ?: ""),
|
|
345
|
+
"rects" to rectList
|
|
346
|
+
)
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private fun extractPageText(state: PapyrusEngineStore.EngineState, pageIndex: Int): String? {
|
|
353
|
+
try {
|
|
354
|
+
state.pdfium.openPage(state.document, pageIndex)
|
|
355
|
+
} catch (_: Throwable) {
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
var method: Method? = null
|
|
360
|
+
method = try {
|
|
361
|
+
state.pdfium.javaClass.getDeclaredMethod("getPageText", PdfDocument::class.java, Int::class.javaPrimitiveType)
|
|
362
|
+
} catch (_: NoSuchMethodException) {
|
|
363
|
+
null
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (method == null) {
|
|
367
|
+
method = try {
|
|
368
|
+
state.pdfium.javaClass.getDeclaredMethod("nativeGetPageText", Long::class.javaPrimitiveType, Int::class.javaPrimitiveType)
|
|
369
|
+
} catch (_: NoSuchMethodException) {
|
|
370
|
+
null
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (method != null) {
|
|
375
|
+
method.isAccessible = true
|
|
376
|
+
val result = if (method.parameterTypes.size == 2 && method.parameterTypes[0] == PdfDocument::class.java) {
|
|
377
|
+
method.invoke(state.pdfium, state.document, pageIndex)
|
|
378
|
+
} else if (method.parameterTypes.size == 2 && method.parameterTypes[0] == Long::class.javaPrimitiveType) {
|
|
379
|
+
val docPtr = extractNativeDocPointer(state.document)
|
|
380
|
+
method.invoke(state.pdfium, docPtr, pageIndex)
|
|
381
|
+
} else {
|
|
382
|
+
null
|
|
383
|
+
}
|
|
384
|
+
return result?.toString() ?: ""
|
|
385
|
+
}
|
|
386
|
+
} catch (_: Throwable) {
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return ""
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private fun extractNativeDocPointer(document: PdfDocument?): Long {
|
|
393
|
+
if (document == null) return 0L
|
|
394
|
+
return try {
|
|
395
|
+
val field = PdfDocument::class.java.getDeclaredField("mNativeDocPtr")
|
|
396
|
+
field.isAccessible = true
|
|
397
|
+
val value = field.get(document)
|
|
398
|
+
if (value is Long) value else 0L
|
|
399
|
+
} catch (_: Throwable) {
|
|
400
|
+
0L
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private fun serializeOutlineItem(item: PapyrusOutlineItem): Map<String, Any?> {
|
|
405
|
+
val map = mutableMapOf<String, Any?>(
|
|
406
|
+
"title" to (item.title ?: ""),
|
|
407
|
+
"pageIndex" to item.pageIndex
|
|
408
|
+
)
|
|
409
|
+
val children = item.children
|
|
410
|
+
if (children != null && children.isNotEmpty()) {
|
|
411
|
+
val childMaps = mutableListOf<Map<String, Any?>>()
|
|
412
|
+
children.forEach { child ->
|
|
413
|
+
childMaps.add(serializeOutlineItem(child))
|
|
414
|
+
}
|
|
415
|
+
map["children"] = childMaps
|
|
416
|
+
}
|
|
417
|
+
return map
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
@Throws(IOException::class)
|
|
421
|
+
private fun materializeSource(source: Map<String, Any?>, context: Context): File? {
|
|
422
|
+
val uriValue = source["uri"]
|
|
423
|
+
if (uriValue is String) {
|
|
424
|
+
if (uriValue.startsWith("http://") || uriValue.startsWith("https://")) {
|
|
425
|
+
return downloadToCache(uriValue, context)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (uriValue.startsWith("asset:/")) {
|
|
429
|
+
return copyFromAsset(uriValue.substring("asset:/".length), context)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (uriValue.startsWith("file:///android_asset/")) {
|
|
433
|
+
return copyFromAsset(uriValue.substring("file:///android_asset/".length), context)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (uriValue.startsWith("content://")) {
|
|
437
|
+
return copyFromContentUri(Uri.parse(uriValue), context)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (uriValue.startsWith("file://")) {
|
|
441
|
+
return File(Uri.parse(uriValue).path ?: return null)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (uriValue.startsWith("res://")) {
|
|
445
|
+
val resourceName = uriValue.substring("res://".length)
|
|
446
|
+
val resourceId = context.resources.getIdentifier(resourceName, "raw", context.packageName)
|
|
447
|
+
if (resourceId != 0) {
|
|
448
|
+
return copyFromRawResource(resourceId, context)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
val resourceId = context.resources.getIdentifier(uriValue, "raw", context.packageName)
|
|
453
|
+
if (resourceId != 0) {
|
|
454
|
+
return copyFromRawResource(resourceId, context)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return File(uriValue)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
val dataValue = source["data"]
|
|
461
|
+
if (dataValue != null) {
|
|
462
|
+
val bytes = toByteArray(dataValue)
|
|
463
|
+
if (bytes != null) {
|
|
464
|
+
return writeBytesToCache(bytes, context)
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return null
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private fun toByteArray(value: Any): ByteArray? {
|
|
472
|
+
return when (value) {
|
|
473
|
+
is ByteArray -> value
|
|
474
|
+
is Uint8Array -> {
|
|
475
|
+
val bytes = ByteArray(value.byteLength)
|
|
476
|
+
value.read(bytes, 0, value.byteLength)
|
|
477
|
+
bytes
|
|
478
|
+
}
|
|
479
|
+
is List<*> -> {
|
|
480
|
+
val bytes = ByteArray(value.size)
|
|
481
|
+
value.forEachIndexed { index, item ->
|
|
482
|
+
val number = item as? Number ?: return null
|
|
483
|
+
bytes[index] = number.toInt().toByte()
|
|
484
|
+
}
|
|
485
|
+
bytes
|
|
486
|
+
}
|
|
487
|
+
else -> null
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
@Throws(IOException::class)
|
|
492
|
+
private fun downloadToCache(uri: String, context: Context): File {
|
|
493
|
+
val url = URL(uri)
|
|
494
|
+
val connection = url.openConnection() as HttpURLConnection
|
|
495
|
+
connection.connect()
|
|
496
|
+
if (connection.responseCode >= 400) {
|
|
497
|
+
throw IOException("Failed to download PDF")
|
|
498
|
+
}
|
|
499
|
+
val inputStream = connection.inputStream
|
|
500
|
+
val out = createTempFile(context)
|
|
501
|
+
writeStreamToFile(inputStream, out)
|
|
502
|
+
connection.disconnect()
|
|
503
|
+
return out
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
@Throws(IOException::class)
|
|
507
|
+
private fun copyFromContentUri(uri: Uri, context: Context): File {
|
|
508
|
+
val resolver: ContentResolver = context.contentResolver
|
|
509
|
+
val inputStream = resolver.openInputStream(uri) ?: throw IOException("Unable to read content URI")
|
|
510
|
+
val out = createTempFile(context)
|
|
511
|
+
writeStreamToFile(inputStream, out)
|
|
512
|
+
return out
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
@Throws(IOException::class)
|
|
516
|
+
private fun copyFromAsset(assetPath: String, context: Context): File {
|
|
517
|
+
val inputStream = context.assets.open(assetPath)
|
|
518
|
+
val out = createTempFile(context)
|
|
519
|
+
writeStreamToFile(inputStream, out)
|
|
520
|
+
return out
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
@Throws(IOException::class)
|
|
524
|
+
private fun copyFromRawResource(resourceId: Int, context: Context): File {
|
|
525
|
+
val inputStream = context.resources.openRawResource(resourceId)
|
|
526
|
+
val out = createTempFile(context)
|
|
527
|
+
writeStreamToFile(inputStream, out)
|
|
528
|
+
return out
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
@Throws(IOException::class)
|
|
532
|
+
private fun writeBytesToCache(bytes: ByteArray, context: Context): File {
|
|
533
|
+
val out = createTempFile(context)
|
|
534
|
+
FileOutputStream(out).use { fos ->
|
|
535
|
+
fos.write(bytes)
|
|
536
|
+
fos.flush()
|
|
537
|
+
}
|
|
538
|
+
return out
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
@Throws(IOException::class)
|
|
542
|
+
private fun createTempFile(context: Context): File {
|
|
543
|
+
val cacheDir = context.cacheDir
|
|
544
|
+
return File.createTempFile("papyrus", ".pdf", cacheDir)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
@Throws(IOException::class)
|
|
548
|
+
private fun writeStreamToFile(inputStream: InputStream, out: File) {
|
|
549
|
+
FileOutputStream(out).use { fos ->
|
|
550
|
+
val buffer = ByteArray(8192)
|
|
551
|
+
var read: Int
|
|
552
|
+
while (true) {
|
|
553
|
+
read = inputStream.read(buffer)
|
|
554
|
+
if (read == -1) break
|
|
555
|
+
fos.write(buffer, 0, read)
|
|
556
|
+
}
|
|
557
|
+
fos.flush()
|
|
558
|
+
}
|
|
559
|
+
inputStream.close()
|
|
560
|
+
}
|
|
561
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
package com.papyrus.engine;
|
|
2
|
-
|
|
3
|
-
final class PapyrusOutline {
|
|
4
|
-
static final boolean AVAILABLE;
|
|
5
|
-
|
|
6
|
-
static {
|
|
7
|
-
boolean available = false;
|
|
8
|
-
try {
|
|
9
|
-
System.loadLibrary("papyrus_text");
|
|
10
|
-
available = true;
|
|
11
|
-
} catch (Throwable ignored) {
|
|
12
|
-
available = false;
|
|
13
|
-
}
|
|
14
|
-
AVAILABLE = available;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static native PapyrusOutlineItem[] nativeGetOutline(long docPtr);
|
|
18
|
-
|
|
19
|
-
static native PapyrusOutlineItem[] nativeGetOutlineFile(String filePath);
|
|
20
|
-
}
|
|
1
|
+
package com.papyrus.engine;
|
|
2
|
+
|
|
3
|
+
final class PapyrusOutline {
|
|
4
|
+
static final boolean AVAILABLE;
|
|
5
|
+
|
|
6
|
+
static {
|
|
7
|
+
boolean available = false;
|
|
8
|
+
try {
|
|
9
|
+
System.loadLibrary("papyrus_text");
|
|
10
|
+
available = true;
|
|
11
|
+
} catch (Throwable ignored) {
|
|
12
|
+
available = false;
|
|
13
|
+
}
|
|
14
|
+
AVAILABLE = available;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static native PapyrusOutlineItem[] nativeGetOutline(long docPtr);
|
|
18
|
+
|
|
19
|
+
static native PapyrusOutlineItem[] nativeGetOutlineFile(String filePath);
|
|
20
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
package com.papyrus.engine;
|
|
2
|
-
|
|
3
|
-
final class PapyrusOutlineItem {
|
|
4
|
-
final String title;
|
|
5
|
-
final int pageIndex;
|
|
6
|
-
final PapyrusOutlineItem[] children;
|
|
7
|
-
|
|
8
|
-
PapyrusOutlineItem(String title, int pageIndex, PapyrusOutlineItem[] children) {
|
|
9
|
-
this.title = title != null ? title : "";
|
|
10
|
-
this.pageIndex = pageIndex;
|
|
11
|
-
this.children = children;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
1
|
+
package com.papyrus.engine;
|
|
2
|
+
|
|
3
|
+
final class PapyrusOutlineItem {
|
|
4
|
+
final String title;
|
|
5
|
+
final int pageIndex;
|
|
6
|
+
final PapyrusOutlineItem[] children;
|
|
7
|
+
|
|
8
|
+
PapyrusOutlineItem(String title, int pageIndex, PapyrusOutlineItem[] children) {
|
|
9
|
+
this.title = title != null ? title : "";
|
|
10
|
+
this.pageIndex = pageIndex;
|
|
11
|
+
this.children = children;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
package com.papyrus.engine;
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.ReactPackage;
|
|
4
|
-
import com.facebook.react.bridge.NativeModule;
|
|
5
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
-
import com.facebook.react.uimanager.ViewManager;
|
|
7
|
-
|
|
8
|
-
import java.util.ArrayList;
|
|
9
|
-
import java.util.Collections;
|
|
10
|
-
import java.util.List;
|
|
11
|
-
|
|
12
|
-
public class PapyrusPackage implements ReactPackage {
|
|
13
|
-
@Override
|
|
14
|
-
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
15
|
-
List<NativeModule> modules = new ArrayList<>();
|
|
16
|
-
modules.add(new PapyrusNativeEngineModule(reactContext));
|
|
17
|
-
return modules;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
@Override
|
|
21
|
-
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
22
|
-
return Collections.singletonList(new PapyrusPageViewManager());
|
|
23
|
-
}
|
|
24
|
-
}
|
|
1
|
+
package com.papyrus.engine;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage;
|
|
4
|
+
import com.facebook.react.bridge.NativeModule;
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
7
|
+
|
|
8
|
+
import java.util.ArrayList;
|
|
9
|
+
import java.util.Collections;
|
|
10
|
+
import java.util.List;
|
|
11
|
+
|
|
12
|
+
public class PapyrusPackage implements ReactPackage {
|
|
13
|
+
@Override
|
|
14
|
+
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
15
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
16
|
+
modules.add(new PapyrusNativeEngineModule(reactContext));
|
|
17
|
+
return modules;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Override
|
|
21
|
+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
22
|
+
return Collections.singletonList(new PapyrusPageViewManager());
|
|
23
|
+
}
|
|
24
|
+
}
|