@papyrus-sdk/engine-native 0.2.10 → 0.2.11

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.
@@ -1,18 +1,18 @@
1
- package android.support.v4.util;
2
-
3
- import java.util.HashMap;
4
- import java.util.Map;
5
-
6
- public class ArrayMap<K, V> extends HashMap<K, V> {
7
- public ArrayMap() {
8
- super();
9
- }
10
-
11
- public ArrayMap(int capacity) {
12
- super(capacity);
13
- }
14
-
15
- public ArrayMap(Map<? extends K, ? extends V> map) {
16
- super(map);
17
- }
18
- }
1
+ package android.support.v4.util;
2
+
3
+ import java.util.HashMap;
4
+ import java.util.Map;
5
+
6
+ public class ArrayMap<K, V> extends HashMap<K, V> {
7
+ public ArrayMap() {
8
+ super();
9
+ }
10
+
11
+ public ArrayMap(int capacity) {
12
+ super(capacity);
13
+ }
14
+
15
+ public ArrayMap(Map<? extends K, ? extends V> map) {
16
+ super(map);
17
+ }
18
+ }
@@ -451,25 +451,25 @@ public class PapyrusNativeEngineModule extends ReactContextBaseJavaModule {
451
451
  return copyFromContentUri(Uri.parse(uriString), context);
452
452
  }
453
453
 
454
- if (uriString.startsWith("file://")) {
455
- return new File(Uri.parse(uriString).getPath());
456
- }
457
-
458
- if (uriString.startsWith("res://")) {
459
- String resourceName = uriString.substring("res://".length());
460
- File resourceFile = copyFromRawResource(resourceName, context);
461
- if (resourceFile != null) {
462
- return resourceFile;
463
- }
464
- }
465
-
466
- File resourceFile = copyFromRawResource(uriString, context);
467
- if (resourceFile != null) {
468
- return resourceFile;
469
- }
470
-
471
- return new File(uriString);
472
- }
454
+ if (uriString.startsWith("file://")) {
455
+ return new File(Uri.parse(uriString).getPath());
456
+ }
457
+
458
+ if (uriString.startsWith("res://")) {
459
+ String resourceName = uriString.substring("res://".length());
460
+ File resourceFile = copyFromRawResource(resourceName, context);
461
+ if (resourceFile != null) {
462
+ return resourceFile;
463
+ }
464
+ }
465
+
466
+ File resourceFile = copyFromRawResource(uriString, context);
467
+ if (resourceFile != null) {
468
+ return resourceFile;
469
+ }
470
+
471
+ return new File(uriString);
472
+ }
473
473
 
474
474
  if (source.hasKey("data") && source.getType("data") == ReadableType.Array) {
475
475
  ReadableArray array = source.getArray("data");
@@ -507,24 +507,24 @@ public class PapyrusNativeEngineModule extends ReactContextBaseJavaModule {
507
507
  return out;
508
508
  }
509
509
 
510
- private static File copyFromAsset(String assetPath, Context context) throws IOException {
511
- InputStream inputStream = context.getAssets().open(assetPath);
512
- File out = createTempFile(context);
513
- writeStreamToFile(inputStream, out);
514
- return out;
515
- }
516
-
517
- private static File copyFromRawResource(String resourceName, Context context) throws IOException {
518
- int resId = context.getResources().getIdentifier(resourceName, "raw", context.getPackageName());
519
- if (resId == 0) {
520
- return null;
521
- }
522
- InputStream inputStream = context.getResources().openRawResource(resId);
523
- File out = createTempFile(context);
524
- writeStreamToFile(inputStream, out);
525
- return out;
526
- }
527
-
510
+ private static File copyFromAsset(String assetPath, Context context) throws IOException {
511
+ InputStream inputStream = context.getAssets().open(assetPath);
512
+ File out = createTempFile(context);
513
+ writeStreamToFile(inputStream, out);
514
+ return out;
515
+ }
516
+
517
+ private static File copyFromRawResource(String resourceName, Context context) throws IOException {
518
+ int resId = context.getResources().getIdentifier(resourceName, "raw", context.getPackageName());
519
+ if (resId == 0) {
520
+ return null;
521
+ }
522
+ InputStream inputStream = context.getResources().openRawResource(resId);
523
+ File out = createTempFile(context);
524
+ writeStreamToFile(inputStream, out);
525
+ return out;
526
+ }
527
+
528
528
  private static File writeBytesToCache(byte[] bytes, Context context) throws IOException {
529
529
  File out = createTempFile(context);
530
530
  FileOutputStream fos = new FileOutputStream(out);
@@ -437,25 +437,25 @@ class PapyrusNativeEngineModule : Module() {
437
437
  return copyFromContentUri(Uri.parse(uriValue), context)
438
438
  }
439
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
- }
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
459
 
460
460
  val dataValue = source["data"]
461
461
  if (dataValue != null) {
@@ -513,21 +513,21 @@ class PapyrusNativeEngineModule : Module() {
513
513
  }
514
514
 
515
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
-
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
531
  @Throws(IOException::class)
532
532
  private fun writeBytesToCache(bytes: ByteArray, context: Context): File {
533
533
  val out = createTempFile(context)
@@ -1,34 +1,34 @@
1
- package com.papyrus.engine;
2
-
3
- final class PapyrusOutline {
4
- interface LibraryLoader {
5
- void load() throws Throwable;
6
- }
7
-
8
- interface OutlineSupportProbe {
9
- boolean isSupported() throws Throwable;
10
- }
11
-
12
- static final boolean AVAILABLE;
13
-
14
- static {
15
- AVAILABLE = computeAvailability(
16
- () -> System.loadLibrary("papyrus_text"),
17
- PapyrusOutline::nativeIsOutlineSupported);
18
- }
19
-
20
- static boolean computeAvailability(LibraryLoader loader, OutlineSupportProbe probe) {
21
- try {
22
- loader.load();
23
- return probe.isSupported();
24
- } catch (Throwable ignored) {
25
- return false;
26
- }
27
- }
28
-
29
- static native boolean nativeIsOutlineSupported();
30
-
31
- static native PapyrusOutlineItem[] nativeGetOutline(long docPtr);
32
-
33
- static native PapyrusOutlineItem[] nativeGetOutlineFile(String filePath);
1
+ package com.papyrus.engine;
2
+
3
+ final class PapyrusOutline {
4
+ interface LibraryLoader {
5
+ void load() throws Throwable;
6
+ }
7
+
8
+ interface OutlineSupportProbe {
9
+ boolean isSupported() throws Throwable;
10
+ }
11
+
12
+ static final boolean AVAILABLE;
13
+
14
+ static {
15
+ AVAILABLE = computeAvailability(
16
+ () -> System.loadLibrary("papyrus_text"),
17
+ PapyrusOutline::nativeIsOutlineSupported);
18
+ }
19
+
20
+ static boolean computeAvailability(LibraryLoader loader, OutlineSupportProbe probe) {
21
+ try {
22
+ loader.load();
23
+ return probe.isSupported();
24
+ } catch (Throwable ignored) {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ static native boolean nativeIsOutlineSupported();
30
+
31
+ static native PapyrusOutlineItem[] nativeGetOutline(long docPtr);
32
+
33
+ static native PapyrusOutlineItem[] nativeGetOutlineFile(String filePath);
34
34
  }
@@ -6,6 +6,7 @@ import android.graphics.Canvas;
6
6
  import android.graphics.Paint;
7
7
  import android.graphics.Rect;
8
8
  import android.util.AttributeSet;
9
+ import android.util.Log;
9
10
  import android.view.View;
10
11
 
11
12
  import com.shockwave.pdfium.PdfDocument;
@@ -14,6 +15,9 @@ import java.util.concurrent.ExecutorService;
14
15
  import java.util.concurrent.Executors;
15
16
 
16
17
  public class PapyrusPageView extends View {
18
+ static final long MAX_RENDER_PIXELS = 8L * 1024L * 1024L;
19
+ static final int MAX_RENDER_EDGE = 4096;
20
+ private static final String TAG = "PapyrusPageView";
17
21
  private static final ExecutorService RENDER_EXECUTOR = Executors.newSingleThreadExecutor();
18
22
 
19
23
  private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -43,8 +47,12 @@ public class PapyrusPageView extends View {
43
47
  final int viewHeight = getHeight();
44
48
  final float clampedZoom = Math.max(0.1f, Math.min(5.0f, zoom));
45
49
  final float targetScale = Math.max(0.1f, scale) * clampedZoom;
46
- final int renderWidth = Math.max(1, (int) (viewWidth * targetScale));
47
- final int renderHeight = Math.max(1, (int) (viewHeight * targetScale));
50
+ final int[] renderSize = constrainRenderSize(
51
+ Math.max(1, (int) (viewWidth * targetScale)),
52
+ Math.max(1, (int) (viewHeight * targetScale))
53
+ );
54
+ final int renderWidth = renderSize[0];
55
+ final int renderHeight = renderSize[1];
48
56
 
49
57
  RENDER_EXECUTOR.execute(() -> {
50
58
  PdfDocument doc = state.document;
@@ -81,6 +89,40 @@ public class PapyrusPageView extends View {
81
89
  super.onDraw(canvas);
82
90
  if (bitmap == null) return;
83
91
  Rect dest = new Rect(0, 0, getWidth(), getHeight());
84
- canvas.drawBitmap(bitmap, null, dest, paint);
92
+ try {
93
+ canvas.drawBitmap(bitmap, null, dest, paint);
94
+ } catch (RuntimeException error) {
95
+ Log.w(TAG, "Failed to draw rendered page bitmap safely", error);
96
+ if (bitmap != null && !bitmap.isRecycled()) {
97
+ bitmap.recycle();
98
+ }
99
+ bitmap = null;
100
+ }
101
+ }
102
+
103
+ static int[] constrainRenderSize(int requestedWidth, int requestedHeight) {
104
+ int width = Math.max(1, requestedWidth);
105
+ int height = Math.max(1, requestedHeight);
106
+ double scale = 1.0d;
107
+
108
+ if (width > MAX_RENDER_EDGE || height > MAX_RENDER_EDGE) {
109
+ scale = Math.min(
110
+ scale,
111
+ Math.min((double) MAX_RENDER_EDGE / width, (double) MAX_RENDER_EDGE / height)
112
+ );
113
+ }
114
+
115
+ long pixels = (long) width * (long) height;
116
+ if (pixels > MAX_RENDER_PIXELS) {
117
+ scale = Math.min(scale, Math.sqrt((double) MAX_RENDER_PIXELS / (double) pixels));
118
+ }
119
+
120
+ if (scale >= 1.0d) {
121
+ return new int[] { width, height };
122
+ }
123
+
124
+ int safeWidth = Math.max(1, (int) Math.floor(width * scale));
125
+ int safeHeight = Math.max(1, (int) Math.floor(height * scale));
126
+ return new int[] { safeWidth, safeHeight };
85
127
  }
86
128
  }
@@ -1,40 +1,40 @@
1
- package com.papyrus.engine;
2
-
3
- import static org.junit.Assert.assertFalse;
4
- import static org.junit.Assert.assertTrue;
5
-
6
- import org.junit.Test;
7
-
8
- public class PapyrusOutlineTest {
9
- @Test
10
- public void computeAvailabilityReturnsTrueWhenLoaderAndProbeSucceed() {
11
- boolean available = PapyrusOutline.computeAvailability(() -> {}, () -> true);
12
-
13
- assertTrue(available);
14
- }
15
-
16
- @Test
17
- public void computeAvailabilityReturnsFalseWhenProbeReturnsFalse() {
18
- boolean available = PapyrusOutline.computeAvailability(() -> {}, () -> false);
19
-
20
- assertFalse(available);
21
- }
22
-
23
- @Test
24
- public void computeAvailabilityReturnsFalseWhenProbeThrows() {
25
- boolean available = PapyrusOutline.computeAvailability(() -> {}, () -> {
26
- throw new UnsatisfiedLinkError("missing probe");
27
- });
28
-
29
- assertFalse(available);
30
- }
31
-
32
- @Test
33
- public void computeAvailabilityReturnsFalseWhenLibraryLoadThrows() {
34
- boolean available = PapyrusOutline.computeAvailability(() -> {
35
- throw new UnsatisfiedLinkError("missing lib");
36
- }, () -> true);
37
-
38
- assertFalse(available);
39
- }
1
+ package com.papyrus.engine;
2
+
3
+ import static org.junit.Assert.assertFalse;
4
+ import static org.junit.Assert.assertTrue;
5
+
6
+ import org.junit.Test;
7
+
8
+ public class PapyrusOutlineTest {
9
+ @Test
10
+ public void computeAvailabilityReturnsTrueWhenLoaderAndProbeSucceed() {
11
+ boolean available = PapyrusOutline.computeAvailability(() -> {}, () -> true);
12
+
13
+ assertTrue(available);
14
+ }
15
+
16
+ @Test
17
+ public void computeAvailabilityReturnsFalseWhenProbeReturnsFalse() {
18
+ boolean available = PapyrusOutline.computeAvailability(() -> {}, () -> false);
19
+
20
+ assertFalse(available);
21
+ }
22
+
23
+ @Test
24
+ public void computeAvailabilityReturnsFalseWhenProbeThrows() {
25
+ boolean available = PapyrusOutline.computeAvailability(() -> {}, () -> {
26
+ throw new UnsatisfiedLinkError("missing probe");
27
+ });
28
+
29
+ assertFalse(available);
30
+ }
31
+
32
+ @Test
33
+ public void computeAvailabilityReturnsFalseWhenLibraryLoadThrows() {
34
+ boolean available = PapyrusOutline.computeAvailability(() -> {
35
+ throw new UnsatisfiedLinkError("missing lib");
36
+ }, () -> true);
37
+
38
+ assertFalse(available);
39
+ }
40
40
  }
@@ -0,0 +1,30 @@
1
+ package com.papyrus.engine;
2
+
3
+ import static org.junit.Assert.assertArrayEquals;
4
+ import static org.junit.Assert.assertTrue;
5
+
6
+ import org.junit.Test;
7
+
8
+ public class PapyrusPageViewTest {
9
+ @Test
10
+ public void constrainRenderSizeKeepsSafeSizesUntouched() {
11
+ int[] size = PapyrusPageView.constrainRenderSize(1200, 1600);
12
+
13
+ assertArrayEquals(new int[] {1200, 1600}, size);
14
+ }
15
+
16
+ @Test
17
+ public void constrainRenderSizeCapsOversizedBitmapsPreservingAspectRatio() {
18
+ int[] size = PapyrusPageView.constrainRenderSize(6000, 8000);
19
+
20
+ assertTrue(size[0] > 0);
21
+ assertTrue(size[1] > 0);
22
+ assertTrue(size[0] <= PapyrusPageView.MAX_RENDER_EDGE);
23
+ assertTrue(size[1] <= PapyrusPageView.MAX_RENDER_EDGE);
24
+ assertTrue(
25
+ ((long) size[0] * (long) size[1]) <= PapyrusPageView.MAX_RENDER_PIXELS
26
+ );
27
+ assertTrue(size[0] < 6000);
28
+ assertTrue(size[1] < 8000);
29
+ }
30
+ }