@amafil/react-native-pdf-toolkit 1.1.17 → 1.1.18

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.
Files changed (134) hide show
  1. package/README.md +57 -0
  2. package/android/.gradle/8.5/executionHistory/executionHistory.bin +0 -0
  3. package/android/.gradle/8.5/executionHistory/executionHistory.lock +0 -0
  4. package/android/.gradle/8.5/fileHashes/fileHashes.bin +0 -0
  5. package/android/.gradle/8.5/fileHashes/fileHashes.lock +0 -0
  6. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  7. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  8. package/android/.gradle/file-system.probe +0 -0
  9. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.dex +0 -0
  10. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.dex +0 -0
  11. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfManager.dex +0 -0
  12. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$1.dex +0 -0
  13. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$2.dex +0 -0
  14. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$3.dex +0 -0
  15. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$AnnotationOverlayView$1.dex +0 -0
  16. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationHit.dex +0 -0
  17. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationSelectionHit.dex +0 -0
  18. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$AnnotationOverlayView.dex +0 -0
  19. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$PageRenderInfo.dex +0 -0
  20. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView.dex +0 -0
  21. package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
  22. package/android/build/.transforms/e40f3b884607647e8beb6dabe8165962/transformed/classes/classes_dex/classes.dex +0 -0
  23. package/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.java +30 -0
  24. package/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.java +10 -0
  25. package/android/build/generated/source/codegen/jni/react/renderer/components/rnpdf/Props.cpp +35 -0
  26. package/android/build/generated/source/codegen/jni/react/renderer/components/rnpdf/Props.h +7 -0
  27. package/android/build/generated/source/codegen/schema.json +1 -1
  28. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  29. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
  30. package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +1 -1
  31. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +1 -1
  32. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +1 -1
  33. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.class +0 -0
  34. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.class +0 -0
  35. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfManager.class +0 -0
  36. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$1.class +0 -0
  37. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$2.class +0 -0
  38. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$3.class +0 -0
  39. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$AnnotationOverlayView$1.class +0 -0
  40. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationHit.class +0 -0
  41. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationSelectionHit.class +0 -0
  42. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$AnnotationOverlayView.class +0 -0
  43. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$PageRenderInfo.class +0 -0
  44. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView.class +0 -0
  45. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.class +0 -0
  46. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.class +0 -0
  47. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfManager.class +0 -0
  48. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$1.class +0 -0
  49. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$2.class +0 -0
  50. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$3.class +0 -0
  51. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$AnnotationOverlayView$1.class +0 -0
  52. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationHit.class +0 -0
  53. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationSelectionHit.class +0 -0
  54. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$AnnotationOverlayView.class +0 -0
  55. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$PageRenderInfo.class +0 -0
  56. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView.class +0 -0
  57. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  58. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfManager.class.uniqueId7 +0 -0
  59. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/{PdfView$1.class.uniqueId0 → PdfView$1.class.uniqueId3} +0 -0
  60. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$2.class.uniqueId5 +0 -0
  61. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$AnnotationOverlayView$1.class.uniqueId6 +0 -0
  62. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$AnnotationOverlayView$AnnotationHit.class.uniqueId1 +0 -0
  63. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$AnnotationOverlayView$AnnotationSelectionHit.class.uniqueId9 +0 -0
  64. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$AnnotationOverlayView.class.uniqueId8 +0 -0
  65. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$PageRenderInfo.class.uniqueId0 +0 -0
  66. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView.class.uniqueId2 +0 -0
  67. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  68. package/android/src/main/java/org/wonday/pdf/PdfManager.java +56 -0
  69. package/android/src/main/java/org/wonday/pdf/PdfView.java +1249 -0
  70. package/android/src/paper/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.java +30 -0
  71. package/android/src/paper/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.java +10 -0
  72. package/annotationDocumentUtils.js +74 -0
  73. package/fabric/RNPDFPdfNativeComponent.js +18 -3
  74. package/index.d.ts +103 -1
  75. package/index.js +112 -0
  76. package/index.js.flow +73 -0
  77. package/ios/RNPDFPdf/RNPDFPdfView.h +11 -0
  78. package/ios/RNPDFPdf/RNPDFPdfView.mm +1414 -55
  79. package/ios/RNPDFPdf/RNPDFPdfViewManager.mm +37 -0
  80. package/package.json +6 -5
  81. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/results.bin +0 -1
  82. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.dex +0 -0
  83. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.dex +0 -0
  84. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/BuildConfig.dex +0 -0
  85. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfManager.dex +0 -0
  86. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$1.dex +0 -0
  87. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$2.dex +0 -0
  88. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$3.dex +0 -0
  89. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView.dex +0 -0
  90. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/RNPDFPackage.dex +0 -0
  91. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/events/TopChangeEvent.dex +0 -0
  92. package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
  93. package/android/build/.transforms/787b9137f14dcc9eebbb21ac62a7be22/results.bin +0 -1
  94. package/android/build/.transforms/787b9137f14dcc9eebbb21ac62a7be22/transformed/classes/classes_dex/classes.dex +0 -0
  95. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/results.bin +0 -1
  96. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.dex +0 -0
  97. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.dex +0 -0
  98. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/BuildConfig.dex +0 -0
  99. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfManager.dex +0 -0
  100. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$1.dex +0 -0
  101. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$2.dex +0 -0
  102. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$3.dex +0 -0
  103. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView.dex +0 -0
  104. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/RNPDFPackage.dex +0 -0
  105. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/events/TopChangeEvent.dex +0 -0
  106. package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
  107. package/android/build/.transforms/c29f40a753b0ae1d85db8c8c5812fc85/results.bin +0 -1
  108. package/android/build/.transforms/c29f40a753b0ae1d85db8c8c5812fc85/transformed/classes/classes_dex/classes.dex +0 -0
  109. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/AndroidManifest.xml +0 -7
  110. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/output-metadata.json +0 -18
  111. package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
  112. package/android/build/intermediates/aar_metadata/debug/aar-metadata.properties +0 -5
  113. package/android/build/intermediates/annotation_processor_list/debug/annotationProcessors.json +0 -1
  114. package/android/build/intermediates/annotation_processor_list/debugAndroidTest/annotationProcessors.json +0 -1
  115. package/android/build/intermediates/annotation_processor_list/debugUnitTest/annotationProcessors.json +0 -1
  116. package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
  117. package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
  118. package/android/build/intermediates/local_only_symbol_list/debug/R-def.txt +0 -2
  119. package/android/build/intermediates/manifest_merge_blame_file/debug/manifest-merger-blame-debug-report.txt +0 -8
  120. package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-react-native-pdf.jar +0 -0
  121. package/android/build/intermediates/merged_manifest/debug/AndroidManifest.xml +0 -7
  122. package/android/build/intermediates/navigation_json/debug/navigation.json +0 -1
  123. package/android/build/intermediates/packaged_manifests/debug/output-metadata.json +0 -18
  124. package/android/build/outputs/aar/react-native-pdf-debug.aar +0 -0
  125. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfManager.class.uniqueId3 +0 -0
  126. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfManager.class.uniqueId4 +0 -0
  127. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$2.class.uniqueId6 +0 -0
  128. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$3.class.uniqueId4 +0 -0
  129. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$3.class.uniqueId5 +0 -0
  130. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView.class.uniqueId3 +0 -0
  131. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNPDFPackage.class.uniqueId2 +0 -0
  132. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNPDFPdfViewManagerDelegate.class.uniqueId7 +0 -0
  133. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNPDFPdfViewManagerInterface.class.uniqueId1 +0 -0
  134. /package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/{RNPDFPackage.class.uniqueId1 → RNPDFPackage.class.uniqueId4} +0 -0
@@ -17,6 +17,7 @@ import android.os.Handler;
17
17
  import android.os.Looper;
18
18
  import android.os.ParcelFileDescriptor;
19
19
  import android.util.SizeF;
20
+ import android.util.SparseArray;
20
21
  import android.view.Choreographer;
21
22
  import android.view.View;
22
23
  import android.view.ViewGroup;
@@ -24,8 +25,21 @@ import android.util.Log;
24
25
  import android.net.Uri;
25
26
  import android.util.AttributeSet;
26
27
  import android.view.MotionEvent;
28
+ import android.view.ViewConfiguration;
27
29
  import android.graphics.Canvas;
30
+ import android.graphics.Color;
31
+ import android.graphics.Matrix;
32
+ import android.graphics.Paint;
33
+ import android.graphics.Path;
34
+ import android.graphics.PointF;
35
+ import android.graphics.RectF;
28
36
  import android.graphics.pdf.PdfRenderer;
37
+ import android.widget.EditText;
38
+ import android.text.Editable;
39
+ import android.text.TextWatcher;
40
+ import android.text.TextUtils;
41
+ import android.view.Gravity;
42
+ import android.widget.FrameLayout;
29
43
 
30
44
  import io.legere.pdfiumandroid.util.Config;
31
45
  import io.legere.pdfiumandroid.util.ConfigKt;
@@ -55,6 +69,10 @@ import com.facebook.react.uimanager.events.EventDispatcher;
55
69
  import com.facebook.react.uimanager.events.Event;
56
70
  import com.facebook.react.uimanager.events.RCTEventEmitter;
57
71
 
72
+ import org.json.JSONArray;
73
+ import org.json.JSONException;
74
+ import org.json.JSONObject;
75
+
58
76
  import static java.lang.String.format;
59
77
 
60
78
  import java.io.FileNotFoundException;
@@ -76,6 +94,14 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
76
94
  private boolean enableAntialiasing = true;
77
95
  private boolean enableAnnotationRendering = true;
78
96
  private boolean enableDoubleTapZoom = true;
97
+ private String annotations;
98
+ private boolean annotationMode = false;
99
+ private String annotationTool = "select";
100
+ private boolean annotationEditable = true;
101
+ private String annotationIdMode = "auto";
102
+ private String annotationInkColor = "#111111";
103
+ private float annotationInkThickness = 2f;
104
+ private AnnotationOverlayView annotationOverlayView;
79
105
 
80
106
  private boolean enablePaging = false;
81
107
  private boolean autoSpacing = false;
@@ -89,6 +115,9 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
89
115
  private float originalWidth = 0;
90
116
  private float lastPageWidth = 0;
91
117
  private float lastPageHeight = 0;
118
+ private final SparseArray<PageRenderInfo> pageRenderInfoByIndex = new SparseArray<>();
119
+ private final Matrix pageRenderMatrix = new Matrix();
120
+ private final float[] pageRenderMatrixValues = new float[9];
92
121
 
93
122
  // used to store the parameters for `super.onSizeChanged`
94
123
  private int oldW = 0;
@@ -105,10 +134,28 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
105
134
  private long lastFrameTimeNanos = 0;
106
135
  private float accumulatedScrollOffset = 0f; // float accumulator – avoids re-reading the rendering-quantised offset
107
136
 
137
+ private static final class PageRenderInfo {
138
+ final float left;
139
+ final float top;
140
+ final float width;
141
+ final float height;
142
+
143
+ PageRenderInfo(float left, float top, float width, float height) {
144
+ this.left = left;
145
+ this.top = top;
146
+ this.width = width;
147
+ this.height = height;
148
+ }
149
+ }
150
+
108
151
  public PdfView(Context context, AttributeSet set){
109
152
  super(context, set);
110
153
  ConfigKt.setPdfiumConfig(new Config(new DefaultLogger(), AlreadyClosedBehavior.IGNORE));
111
154
  autoScrollResumeHandler = new Handler(Looper.getMainLooper());
155
+ annotationOverlayView = new AnnotationOverlayView(context);
156
+ addView(annotationOverlayView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
157
+ bringChildToFront(annotationOverlayView);
158
+ updateAnnotationOverlayConfig();
112
159
  }
113
160
 
114
161
  @Override
@@ -150,6 +197,10 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
150
197
  new Handler(Looper.getMainLooper()).postDelayed(() -> dispatcher.dispatchEvent(tce), 10);
151
198
  }
152
199
 
200
+ if (annotationOverlayView != null) {
201
+ annotationOverlayView.invalidate();
202
+ }
203
+
153
204
  // ReactContext reactContext = (ReactContext)this.getContext();
154
205
  // reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
155
206
  // this.getId(),
@@ -239,6 +290,21 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
239
290
  // );
240
291
  }
241
292
 
293
+ private void notifyOnChangeWithMessage(String message) {
294
+ WritableMap event = Arguments.createMap();
295
+ event.putString("message", message);
296
+
297
+ ThemedReactContext context = (ThemedReactContext) getContext();
298
+ EventDispatcher dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, getId());
299
+ int surfaceId = UIManagerHelper.getSurfaceId(this);
300
+
301
+ TopChangeEvent tce = new TopChangeEvent(surfaceId, getId(), event);
302
+
303
+ if (dispatcher != null) {
304
+ dispatcher.dispatchEvent(tce);
305
+ }
306
+ }
307
+
242
308
  @Override
243
309
  public void onPageScrolled(int page, float positionOffset){
244
310
 
@@ -246,6 +312,10 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
246
312
  Constants.Pinch.MINIMUM_ZOOM = this.minScale;
247
313
  Constants.Pinch.MAXIMUM_ZOOM = this.maxScale;
248
314
 
315
+ if (annotationOverlayView != null) {
316
+ annotationOverlayView.invalidate();
317
+ }
318
+
249
319
  }
250
320
 
251
321
  @Override
@@ -285,6 +355,15 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
285
355
  originalWidth = pageWidth;
286
356
  }
287
357
 
358
+ canvas.getMatrix(pageRenderMatrix);
359
+ pageRenderMatrix.getValues(pageRenderMatrixValues);
360
+ pageRenderInfoByIndex.put(displayedPage, new PageRenderInfo(
361
+ pageRenderMatrixValues[Matrix.MTRANS_X] - getCurrentXOffset(),
362
+ pageRenderMatrixValues[Matrix.MTRANS_Y] - getCurrentYOffset(),
363
+ pageWidth,
364
+ pageHeight
365
+ ));
366
+
288
367
  if (lastPageWidth>0 && lastPageHeight>0 && (pageWidth!=lastPageWidth || pageHeight!=lastPageHeight)) {
289
368
  // maybe change by other instance, restore zoom setting
290
369
  Constants.Pinch.MINIMUM_ZOOM = this.minScale;
@@ -311,6 +390,56 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
311
390
 
312
391
  lastPageWidth = pageWidth;
313
392
  lastPageHeight = pageHeight;
393
+
394
+ if (annotationOverlayView != null) {
395
+ annotationOverlayView.invalidate();
396
+ }
397
+ }
398
+
399
+ private void clearPageRenderInfo() {
400
+ pageRenderInfoByIndex.clear();
401
+ originalWidth = 0;
402
+ lastPageWidth = 0;
403
+ lastPageHeight = 0;
404
+ }
405
+
406
+ private PageRenderInfo getPageRenderInfo(int pageIndex) {
407
+ PageRenderInfo pageRenderInfo = pageRenderInfoByIndex.get(pageIndex);
408
+ if (pageRenderInfo != null) {
409
+ return new PageRenderInfo(
410
+ pageRenderInfo.left + getCurrentXOffset(),
411
+ pageRenderInfo.top + getCurrentYOffset(),
412
+ pageRenderInfo.width,
413
+ pageRenderInfo.height
414
+ );
415
+ }
416
+
417
+ SizeF pageSize = getPageSize(pageIndex);
418
+ if (pageSize == null) {
419
+ return null;
420
+ }
421
+
422
+ float zoom = getZoom();
423
+ float scaledPageWidth = pageSize.getWidth() * zoom;
424
+ float scaledPageHeight = pageSize.getHeight() * zoom;
425
+ float horizontalMargin = Math.max(0f, (getWidth() - scaledPageWidth) / 2f);
426
+ float pageTop = getFallbackPageTop(pageIndex, zoom) + getCurrentYOffset();
427
+ return new PageRenderInfo(horizontalMargin, pageTop, scaledPageWidth, scaledPageHeight);
428
+ }
429
+
430
+ private float getFallbackPageTop(int pageIndex, float zoom) {
431
+ float pageTop = 0f;
432
+ for (int i = 0; i < pageIndex; i++) {
433
+ SizeF previousPageSize = getPageSize(i);
434
+ if (previousPageSize == null) {
435
+ continue;
436
+ }
437
+
438
+ pageTop += previousPageSize.getHeight() * zoom;
439
+ pageTop += spacing * zoom;
440
+ }
441
+
442
+ return pageTop;
314
443
  }
315
444
 
316
445
  @Override
@@ -332,6 +461,7 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
332
461
 
333
462
  public void drawPdf() {
334
463
  showLog(format("drawPdf path:%s %s", this.path, this.page));
464
+ clearPageRenderInfo();
335
465
 
336
466
  if (this.path != null){
337
467
 
@@ -402,11 +532,53 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
402
532
  }
403
533
 
404
534
  configurator.load();
535
+ updateAnnotationOverlayConfig();
536
+ if (annotationOverlayView != null) {
537
+ annotationOverlayView.invalidate();
538
+ }
405
539
  }
406
540
  }
407
541
 
408
542
  public void setEnableDoubleTapZoom(boolean enableDoubleTapZoom) {
409
543
  this.enableDoubleTapZoom = enableDoubleTapZoom;
544
+ updateAnnotationOverlayConfig();
545
+ }
546
+
547
+ public void setAnnotations(String annotations) {
548
+ this.annotations = annotations;
549
+ if (annotationOverlayView != null) {
550
+ annotationOverlayView.replaceAnnotations(annotations);
551
+ }
552
+ }
553
+
554
+ public void setAnnotationMode(boolean annotationMode) {
555
+ this.annotationMode = annotationMode;
556
+ updateAnnotationOverlayConfig();
557
+ }
558
+
559
+ public void setAnnotationTool(String annotationTool) {
560
+ this.annotationTool = annotationTool;
561
+ updateAnnotationOverlayConfig();
562
+ }
563
+
564
+ public void setAnnotationEditable(boolean annotationEditable) {
565
+ this.annotationEditable = annotationEditable;
566
+ updateAnnotationOverlayConfig();
567
+ }
568
+
569
+ public void setAnnotationIdMode(String annotationIdMode) {
570
+ this.annotationIdMode = annotationIdMode;
571
+ updateAnnotationOverlayConfig();
572
+ }
573
+
574
+ public void setAnnotationInkColor(String annotationInkColor) {
575
+ this.annotationInkColor = TextUtils.isEmpty(annotationInkColor) ? "#111111" : annotationInkColor;
576
+ updateAnnotationOverlayConfig();
577
+ }
578
+
579
+ public void setAnnotationInkThickness(float annotationInkThickness) {
580
+ this.annotationInkThickness = annotationInkThickness > 0f ? annotationInkThickness : 2f;
581
+ updateAnnotationOverlayConfig();
410
582
  }
411
583
 
412
584
  public void cleanup() {
@@ -428,6 +600,7 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
428
600
 
429
601
  public void setEnableRTL(boolean enableRTL) {
430
602
  this.enableRTL = enableRTL;
603
+ updateAnnotationOverlayConfig();
431
604
  }
432
605
 
433
606
  public void setScale(float scale) {
@@ -444,6 +617,7 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
444
617
 
445
618
  public void setHorizontal(boolean horizontal) {
446
619
  this.horizontal = horizontal;
620
+ updateAnnotationOverlayConfig();
447
621
  }
448
622
 
449
623
  public void setScrollEnabled(boolean scrollEnabled) {
@@ -477,6 +651,7 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
477
651
  this.pageFling = false;
478
652
  this.pageSnap = false;
479
653
  }
654
+ updateAnnotationOverlayConfig();
480
655
  }
481
656
 
482
657
  public void setFitPolicy(int fitPolicy) {
@@ -499,6 +674,1058 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
499
674
 
500
675
  public void setSinglePage(boolean singlePage) {
501
676
  this.singlePage = singlePage;
677
+ updateAnnotationOverlayConfig();
678
+ }
679
+
680
+ private boolean isAnnotationEditingSupported() {
681
+ return !this.horizontal && !this.enablePaging && !this.enableRTL && !this.singlePage;
682
+ }
683
+
684
+ private void updateAnnotationOverlayConfig() {
685
+ if (annotationOverlayView != null) {
686
+ annotationOverlayView.setConfiguration(
687
+ this.annotationMode,
688
+ this.annotationTool,
689
+ this.annotationEditable,
690
+ this.annotationIdMode,
691
+ isAnnotationEditingSupported(),
692
+ this.annotationInkColor,
693
+ this.annotationInkThickness
694
+ );
695
+ }
696
+ }
697
+
698
+ private class AnnotationOverlayView extends FrameLayout {
699
+ private final java.util.ArrayList<JSONObject> draftAnnotations = new java.util.ArrayList<>();
700
+ private JSONObject activeInkAnnotation;
701
+ private JSONObject activeMarkupAnnotation;
702
+ private JSONObject activeTextAnnotation;
703
+ private PointF markupStartNormalized;
704
+ private EditText activeEditText;
705
+ private String selectedAnnotationId;
706
+ private JSONObject activeSelectionAnnotation;
707
+ private String activeSelectionHandle = "body";
708
+ private String activeSelectionMode = "none";
709
+ private RectF activeSelectionStartBounds;
710
+ private JSONArray activeSelectionStartPoints;
711
+ private int activeSelectionPageIndex = -1;
712
+ private float activeSelectionDownX;
713
+ private float activeSelectionDownY;
714
+ private boolean activeSelectionHasMoved;
715
+ private final float touchSlop;
716
+ private boolean annotationModeEnabled = false;
717
+ private String tool = "select";
718
+ private boolean editable = true;
719
+ private String idMode = "auto";
720
+ private boolean supported = true;
721
+ private String inkColor = "#111111";
722
+ private float inkThickness = 2f;
723
+
724
+ AnnotationOverlayView(Context context) {
725
+ super(context);
726
+ setWillNotDraw(false);
727
+ setBackgroundColor(Color.TRANSPARENT);
728
+ setClickable(true);
729
+ touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
730
+ }
731
+
732
+ void setConfiguration(boolean annotationMode, String annotationTool, boolean annotationEditable, String annotationIdMode, boolean annotationEditingSupported, String annotationInkColor, float annotationInkThickness) {
733
+ annotationModeEnabled = annotationMode;
734
+ tool = normalizeAnnotationType(annotationTool == null ? "select" : annotationTool);
735
+ editable = annotationEditable;
736
+ idMode = annotationIdMode == null ? "auto" : annotationIdMode;
737
+ supported = annotationEditingSupported;
738
+ inkColor = TextUtils.isEmpty(annotationInkColor) ? "#111111" : annotationInkColor;
739
+ inkThickness = annotationInkThickness > 0f ? annotationInkThickness : 2f;
740
+
741
+ if (!annotationModeEnabled || !editable || !supported) {
742
+ commitTextEditingIfNeeded();
743
+ }
744
+
745
+ if (!annotationModeEnabled || !editable || !supported) {
746
+ clearSelectionInteraction();
747
+ }
748
+
749
+ invalidate();
750
+ }
751
+
752
+ void deleteSelectedAnnotation() {
753
+ JSONObject selectedAnnotation = getSelectedAnnotation();
754
+ if (selectedAnnotation != null) {
755
+ deleteAnnotation(selectedAnnotation);
756
+ }
757
+ }
758
+
759
+ void deleteAllAnnotations() {
760
+ commitTextEditingIfNeeded();
761
+ draftAnnotations.clear();
762
+ selectedAnnotationId = null;
763
+ clearSelectionInteraction();
764
+ invalidate();
765
+ }
766
+
767
+ void replaceAnnotations(String json) {
768
+ draftAnnotations.clear();
769
+ clearSelectionInteraction();
770
+ if (TextUtils.isEmpty(json)) {
771
+ invalidate();
772
+ return;
773
+ }
774
+
775
+ try {
776
+ JSONArray annotationsArray;
777
+ try {
778
+ JSONObject document = new JSONObject(json);
779
+ annotationsArray = document.optJSONArray("annotations");
780
+ } catch (JSONException objectError) {
781
+ annotationsArray = null;
782
+ }
783
+
784
+ if (annotationsArray == null) {
785
+ annotationsArray = new JSONArray(json);
786
+ }
787
+
788
+ for (int i = 0; i < annotationsArray.length(); i++) {
789
+ JSONObject source = annotationsArray.optJSONObject(i);
790
+ if (source == null) {
791
+ continue;
792
+ }
793
+
794
+ JSONObject annotation = new JSONObject(source.toString());
795
+ String type = normalizeAnnotationType(annotation.optString("type", null));
796
+ if (!TextUtils.isEmpty(type)) {
797
+ annotation.put("type", type);
798
+ }
799
+ if (!annotation.has("id")) {
800
+ annotation.put("id", nextLocalAnnotationId());
801
+ }
802
+ if (!annotation.has("page")) {
803
+ annotation.put("page", 1);
804
+ }
805
+ draftAnnotations.add(annotation);
806
+ }
807
+ } catch (JSONException ignored) {
808
+ }
809
+
810
+ invalidate();
811
+ }
812
+
813
+ String serializeDocument() {
814
+ JSONObject document = new JSONObject();
815
+ try {
816
+ document.put("editable", editable);
817
+ document.put("idMode", idMode);
818
+ document.put("annotations", new JSONArray(draftAnnotations));
819
+ return document.toString();
820
+ } catch (JSONException e) {
821
+ return "{}";
822
+ }
823
+ }
824
+
825
+ @Override
826
+ public boolean dispatchTouchEvent(MotionEvent event) {
827
+ if (activeEditText != null && event.getActionMasked() == MotionEvent.ACTION_DOWN && !isPointInsideView(event.getX(), event.getY(), activeEditText)) {
828
+ commitTextEditingIfNeeded();
829
+ }
830
+
831
+ return super.dispatchTouchEvent(event);
832
+ }
833
+
834
+ @Override
835
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
836
+ if (!annotationModeEnabled || !editable || !supported) {
837
+ return false;
838
+ }
839
+
840
+ if (ev.getPointerCount() > 1) {
841
+ return false;
842
+ }
843
+
844
+ String currentTool = tool == null ? "select" : tool;
845
+ if ("select".equals(currentTool)) {
846
+ return hitTestAnnotation(ev.getX(), ev.getY(), true) != null;
847
+ }
848
+
849
+ return !TextUtils.isEmpty(currentTool);
850
+ }
851
+
852
+ @Override
853
+ public boolean onTouchEvent(MotionEvent event) {
854
+ if (!annotationModeEnabled || !editable || !supported) {
855
+ return false;
856
+ }
857
+
858
+ if (event.getPointerCount() > 1) {
859
+ return false;
860
+ }
861
+
862
+ String currentTool = tool == null ? "select" : tool;
863
+ if ("select".equals(currentTool)) {
864
+ return handleSelectTouch(event);
865
+ }
866
+
867
+ int action = event.getActionMasked();
868
+ if ("ink".equals(currentTool)) {
869
+ if (action == MotionEvent.ACTION_DOWN) {
870
+ AnnotationHit hit = hitTest(event.getX(), event.getY());
871
+ if (hit == null) {
872
+ return false;
873
+ }
874
+ beginInk(hit, event.getX(), event.getY());
875
+ return true;
876
+ } else if (action == MotionEvent.ACTION_MOVE) {
877
+ AnnotationHit hit = hitTest(event.getX(), event.getY());
878
+ if (hit != null) {
879
+ appendInkPoint(hit, event.getX(), event.getY());
880
+ }
881
+ return true;
882
+ } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
883
+ endInk();
884
+ return true;
885
+ }
886
+ } else if ("text".equals(currentTool)) {
887
+ if (action == MotionEvent.ACTION_UP) {
888
+ AnnotationHit hit = hitTest(event.getX(), event.getY());
889
+ if (hit != null) {
890
+ createTextAnnotation(hit, event.getX(), event.getY());
891
+ return true;
892
+ }
893
+ }
894
+ }
895
+
896
+ return false;
897
+ }
898
+
899
+ @Override
900
+ protected void onDraw(Canvas canvas) {
901
+ super.onDraw(canvas);
902
+
903
+ if (PdfView.this.getPageCount() <= 0) {
904
+ return;
905
+ }
906
+
907
+ Paint strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
908
+ Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
909
+ Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
910
+
911
+ for (JSONObject annotation : draftAnnotations) {
912
+ int pageIndex = annotation.optInt("page", 1) - 1;
913
+ if (pageIndex < 0 || pageIndex >= PdfView.this.getPageCount()) {
914
+ continue;
915
+ }
916
+
917
+ String type = normalizeAnnotationType(annotation.optString("type", ""));
918
+ RectF rect = viewRectForAnnotation(annotation, pageIndex);
919
+ if (rect == null && !"ink".equals(type)) {
920
+ continue;
921
+ }
922
+
923
+ if ("ink".equals(type)) {
924
+ JSONArray points = annotation.optJSONArray("points");
925
+ if (points == null || points.length() == 0) {
926
+ continue;
927
+ }
928
+
929
+ if (points.length() == 1) {
930
+ JSONObject point = points.optJSONObject(0);
931
+ if (point != null) {
932
+ float pageX = (float) point.optDouble("x", 0f);
933
+ float pageY = (float) point.optDouble("y", 0f);
934
+ PointF viewPoint = viewPointForNormalizedPoint(pageIndex, pageX, pageY);
935
+ float radius = Math.max(1f, (float) styleFor(annotation).optDouble("thickness", 2.0)) / 2f;
936
+ Paint dotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
937
+ dotPaint.setStyle(Paint.Style.FILL);
938
+ dotPaint.setColor(annotationColor(annotation, Color.BLACK));
939
+ canvas.drawCircle(viewPoint.x, viewPoint.y, radius, dotPaint);
940
+ }
941
+ continue;
942
+ }
943
+
944
+ Path path = new Path();
945
+ boolean first = true;
946
+ for (int i = 0; i < points.length(); i++) {
947
+ JSONObject point = points.optJSONObject(i);
948
+ if (point == null) {
949
+ continue;
950
+ }
951
+
952
+ float pageX = (float) point.optDouble("x", 0f);
953
+ float pageY = (float) point.optDouble("y", 0f);
954
+ PointF viewPoint = viewPointForNormalizedPoint(pageIndex, pageX, pageY);
955
+ if (first) {
956
+ path.moveTo(viewPoint.x, viewPoint.y);
957
+ first = false;
958
+ } else {
959
+ path.lineTo(viewPoint.x, viewPoint.y);
960
+ }
961
+ }
962
+
963
+ strokePaint.setStyle(Paint.Style.STROKE);
964
+ strokePaint.setStrokeJoin(Paint.Join.ROUND);
965
+ strokePaint.setStrokeCap(Paint.Cap.ROUND);
966
+ strokePaint.setStrokeWidth(Math.max(1f, (float) styleFor(annotation).optDouble("thickness", 2.0)));
967
+ strokePaint.setColor(annotationColor(annotation, Color.BLACK));
968
+ canvas.drawPath(path, strokePaint);
969
+ } else if ("text".equals(type)) {
970
+ fillPaint.setColor(Color.argb(200, 255, 255, 255));
971
+ canvas.drawRoundRect(rect, 4f, 4f, fillPaint);
972
+
973
+ strokePaint.setStyle(Paint.Style.STROKE);
974
+ strokePaint.setStrokeWidth(1f);
975
+ strokePaint.setColor(annotationColor(annotation, Color.rgb(34, 68, 170)));
976
+ canvas.drawRoundRect(rect, 4f, 4f, strokePaint);
977
+
978
+ textPaint.setColor(strokePaint.getColor());
979
+ textPaint.setTextSize((float) styleFor(annotation).optDouble("fontSize", 15.0));
980
+ textPaint.setTextAlign(Paint.Align.LEFT);
981
+ String text = annotation.optString("text", "");
982
+ canvas.drawText(text, rect.left + 8f, rect.top + Math.max(20f, textPaint.getTextSize() + 6f), textPaint);
983
+ } else if ("highlight".equals(type)) {
984
+ fillPaint.setColor(annotationColor(annotation, annotationFillColor(type)));
985
+ canvas.drawRect(rect, fillPaint);
986
+ }
987
+ }
988
+
989
+ drawSelectionDecorations(canvas);
990
+ updateEditTextFrame();
991
+ }
992
+
993
+ private boolean handleSelectTouch(MotionEvent event) {
994
+ int action = event.getActionMasked();
995
+ if (action == MotionEvent.ACTION_DOWN) {
996
+ clearSelectionInteraction();
997
+
998
+ AnnotationSelectionHit hit = hitTestAnnotation(event.getX(), event.getY(), true);
999
+ if (hit == null) {
1000
+ return false;
1001
+ }
1002
+
1003
+ selectAnnotation(hit.annotation);
1004
+ activeSelectionAnnotation = hit.annotation;
1005
+ activeSelectionHandle = hit.hitPart;
1006
+ activeSelectionMode = "resize".equals(hit.hitPart) ? "resize" : "move";
1007
+ activeSelectionPageIndex = hit.pageIndex;
1008
+ activeSelectionStartBounds = normalizedBoundsForAnnotation(hit.annotation);
1009
+ activeSelectionStartPoints = copyPointsForAnnotation(hit.annotation);
1010
+ activeSelectionDownX = event.getX();
1011
+ activeSelectionDownY = event.getY();
1012
+ activeSelectionHasMoved = false;
1013
+
1014
+ return true;
1015
+ }
1016
+
1017
+ if (activeSelectionAnnotation == null) {
1018
+ return false;
1019
+ }
1020
+
1021
+ if (action == MotionEvent.ACTION_MOVE) {
1022
+ float deltaX = event.getX() - activeSelectionDownX;
1023
+ float deltaY = event.getY() - activeSelectionDownY;
1024
+ if (!activeSelectionHasMoved) {
1025
+ if (Math.hypot(deltaX, deltaY) < touchSlop) {
1026
+ return true;
1027
+ }
1028
+ activeSelectionHasMoved = true;
1029
+ }
1030
+
1031
+ if ("move".equals(activeSelectionMode)) {
1032
+ moveSelectedAnnotation(deltaX, deltaY);
1033
+ } else if ("resize".equals(activeSelectionMode)) {
1034
+ resizeSelectedAnnotation(deltaX, deltaY);
1035
+ }
1036
+ return true;
1037
+ }
1038
+
1039
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
1040
+ if (!activeSelectionHasMoved && "body".equals(activeSelectionHandle)) {
1041
+ // keep the annotation selected; tap selection is enough for now.
1042
+ }
1043
+
1044
+ clearSelectionInteraction();
1045
+ return true;
1046
+ }
1047
+
1048
+ return true;
1049
+ }
1050
+
1051
+ private void drawSelectionDecorations(Canvas canvas) {
1052
+ if (!annotationModeEnabled || !editable || !supported) {
1053
+ return;
1054
+ }
1055
+
1056
+ JSONObject annotation = getSelectedAnnotation();
1057
+ if (annotation == null) {
1058
+ return;
1059
+ }
1060
+
1061
+ int pageIndex = annotation.optInt("page", 1) - 1;
1062
+ if (pageIndex < 0 || pageIndex >= PdfView.this.getPageCount()) {
1063
+ return;
1064
+ }
1065
+
1066
+ RectF rect = viewRectForAnnotation(annotation, pageIndex);
1067
+ if (rect == null) {
1068
+ return;
1069
+ }
1070
+
1071
+ Paint outlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
1072
+ outlinePaint.setStyle(Paint.Style.STROKE);
1073
+ outlinePaint.setColor(Color.argb(220, 34, 68, 170));
1074
+ outlinePaint.setStrokeWidth(2f);
1075
+
1076
+ Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
1077
+ fillPaint.setStyle(Paint.Style.FILL);
1078
+
1079
+ canvas.drawRoundRect(rect, 4f, 4f, outlinePaint);
1080
+
1081
+ if (annotationSupportsResize(annotation)) {
1082
+ RectF resizeHandle = resizeHandleRect(rect);
1083
+ fillPaint.setColor(Color.argb(235, 34, 68, 170));
1084
+ canvas.drawRoundRect(resizeHandle, 2f, 2f, fillPaint);
1085
+ }
1086
+ }
1087
+
1088
+ private void clearSelectionInteraction() {
1089
+ activeSelectionAnnotation = null;
1090
+ activeSelectionHandle = "body";
1091
+ activeSelectionMode = "none";
1092
+ activeSelectionStartBounds = null;
1093
+ activeSelectionStartPoints = null;
1094
+ activeSelectionPageIndex = -1;
1095
+ activeSelectionHasMoved = false;
1096
+ activeSelectionDownX = 0f;
1097
+ activeSelectionDownY = 0f;
1098
+ }
1099
+
1100
+ private void selectAnnotation(JSONObject annotation) {
1101
+ if (annotation == null) {
1102
+ selectedAnnotationId = null;
1103
+ invalidate();
1104
+ return;
1105
+ }
1106
+
1107
+ selectedAnnotationId = annotation.optString("id", null);
1108
+ invalidate();
1109
+ }
1110
+
1111
+ private JSONObject getSelectedAnnotation() {
1112
+ if (TextUtils.isEmpty(selectedAnnotationId)) {
1113
+ return null;
1114
+ }
1115
+
1116
+ for (int i = draftAnnotations.size() - 1; i >= 0; i--) {
1117
+ JSONObject annotation = draftAnnotations.get(i);
1118
+ if (selectedAnnotationId.equals(annotation.optString("id", null))) {
1119
+ return annotation;
1120
+ }
1121
+ }
1122
+
1123
+ return null;
1124
+ }
1125
+
1126
+ private void deleteAnnotation(JSONObject annotation) {
1127
+ if (annotation == null) {
1128
+ return;
1129
+ }
1130
+
1131
+ String annotationId = annotation.optString("id", null);
1132
+ for (int i = draftAnnotations.size() - 1; i >= 0; i--) {
1133
+ JSONObject candidate = draftAnnotations.get(i);
1134
+ if (annotationId != null && annotationId.equals(candidate.optString("id", null))) {
1135
+ draftAnnotations.remove(i);
1136
+ break;
1137
+ }
1138
+ }
1139
+
1140
+ if (annotationId != null && annotationId.equals(selectedAnnotationId)) {
1141
+ selectedAnnotationId = null;
1142
+ }
1143
+
1144
+ invalidate();
1145
+ }
1146
+
1147
+ private RectF normalizedBoundsForAnnotation(JSONObject annotation) {
1148
+ if (annotation == null) {
1149
+ return null;
1150
+ }
1151
+
1152
+ String type = annotation.optString("type", "");
1153
+ if ("ink".equals(type)) {
1154
+ JSONArray points = annotation.optJSONArray("points");
1155
+ if (points == null || points.length() == 0) {
1156
+ return null;
1157
+ }
1158
+
1159
+ float minX = Float.MAX_VALUE;
1160
+ float minY = Float.MAX_VALUE;
1161
+ float maxX = -Float.MAX_VALUE;
1162
+ float maxY = -Float.MAX_VALUE;
1163
+ for (int i = 0; i < points.length(); i++) {
1164
+ JSONObject point = points.optJSONObject(i);
1165
+ if (point == null) {
1166
+ continue;
1167
+ }
1168
+
1169
+ float x = (float) point.optDouble("x", 0f);
1170
+ float y = (float) point.optDouble("y", 0f);
1171
+ minX = Math.min(minX, x);
1172
+ minY = Math.min(minY, y);
1173
+ maxX = Math.max(maxX, x);
1174
+ maxY = Math.max(maxY, y);
1175
+ }
1176
+
1177
+ if (minX == Float.MAX_VALUE || minY == Float.MAX_VALUE || maxX == -Float.MAX_VALUE || maxY == -Float.MAX_VALUE) {
1178
+ return null;
1179
+ }
1180
+
1181
+ return new RectF(minX, minY, maxX, maxY);
1182
+ }
1183
+
1184
+ JSONObject bounds = annotation.optJSONObject("bounds");
1185
+ if (bounds == null) {
1186
+ return null;
1187
+ }
1188
+
1189
+ float x = (float) bounds.optDouble("x", 0f);
1190
+ float y = (float) bounds.optDouble("y", 0f);
1191
+ float width = (float) bounds.optDouble("width", 0f);
1192
+ float height = (float) bounds.optDouble("height", 0f);
1193
+ return new RectF(x, y, x + width, y + height);
1194
+ }
1195
+
1196
+ private JSONArray copyPointsForAnnotation(JSONObject annotation) {
1197
+ if (annotation == null || !"ink".equals(annotation.optString("type", ""))) {
1198
+ return null;
1199
+ }
1200
+
1201
+ JSONArray points = annotation.optJSONArray("points");
1202
+ if (points == null) {
1203
+ return null;
1204
+ }
1205
+
1206
+ try {
1207
+ return new JSONArray(points.toString());
1208
+ } catch (JSONException ignored) {
1209
+ return null;
1210
+ }
1211
+ }
1212
+
1213
+ private void moveSelectedAnnotation(float deltaX, float deltaY) {
1214
+ if (activeSelectionAnnotation == null || activeSelectionStartBounds == null || activeSelectionPageIndex < 0) {
1215
+ return;
1216
+ }
1217
+
1218
+ PageRenderInfo pageRenderInfo = PdfView.this.getPageRenderInfo(activeSelectionPageIndex);
1219
+ if (pageRenderInfo == null) {
1220
+ return;
1221
+ }
1222
+
1223
+ float scaledPageWidth = Math.max(1f, pageRenderInfo.width);
1224
+ float scaledPageHeight = Math.max(1f, pageRenderInfo.height);
1225
+ float normalizedDeltaX = deltaX / scaledPageWidth;
1226
+ float normalizedDeltaY = deltaY / scaledPageHeight;
1227
+
1228
+ RectF newBounds = new RectF(
1229
+ activeSelectionStartBounds.left + normalizedDeltaX,
1230
+ activeSelectionStartBounds.top + normalizedDeltaY,
1231
+ activeSelectionStartBounds.right + normalizedDeltaX,
1232
+ activeSelectionStartBounds.bottom + normalizedDeltaY
1233
+ );
1234
+
1235
+ setAnnotationFromNormalizedBounds(activeSelectionAnnotation, activeSelectionStartBounds, activeSelectionStartPoints, newBounds);
1236
+ invalidate();
1237
+ }
1238
+
1239
+ private void resizeSelectedAnnotation(float deltaX, float deltaY) {
1240
+ if (activeSelectionAnnotation == null || activeSelectionStartBounds == null || activeSelectionPageIndex < 0) {
1241
+ return;
1242
+ }
1243
+
1244
+ PageRenderInfo pageRenderInfo = PdfView.this.getPageRenderInfo(activeSelectionPageIndex);
1245
+ if (pageRenderInfo == null) {
1246
+ return;
1247
+ }
1248
+
1249
+ float scaledPageWidth = Math.max(1f, pageRenderInfo.width);
1250
+ float scaledPageHeight = Math.max(1f, pageRenderInfo.height);
1251
+ float normalizedDeltaX = deltaX / scaledPageWidth;
1252
+ float normalizedDeltaY = deltaY / scaledPageHeight;
1253
+
1254
+ RectF newBounds = new RectF(
1255
+ activeSelectionStartBounds.left,
1256
+ activeSelectionStartBounds.top,
1257
+ Math.max(activeSelectionStartBounds.left + 0.01f, activeSelectionStartBounds.right + normalizedDeltaX),
1258
+ Math.max(activeSelectionStartBounds.top + 0.01f, activeSelectionStartBounds.bottom + normalizedDeltaY)
1259
+ );
1260
+
1261
+ setAnnotationFromNormalizedBounds(activeSelectionAnnotation, activeSelectionStartBounds, activeSelectionStartPoints, newBounds);
1262
+ invalidate();
1263
+ }
1264
+
1265
+ private void setAnnotationFromNormalizedBounds(JSONObject annotation, RectF startBounds, JSONArray startPoints, RectF newBounds) {
1266
+ if (annotation == null || newBounds == null) {
1267
+ return;
1268
+ }
1269
+
1270
+ String type = annotation.optString("type", "");
1271
+ if ("ink".equals(type)) {
1272
+ if (startPoints == null || startBounds == null) {
1273
+ return;
1274
+ }
1275
+
1276
+ try {
1277
+ JSONArray points = new JSONArray();
1278
+ float startWidth = Math.max(0.001f, startBounds.width());
1279
+ float startHeight = Math.max(0.001f, startBounds.height());
1280
+ float newWidth = Math.max(0.001f, newBounds.width());
1281
+ float newHeight = Math.max(0.001f, newBounds.height());
1282
+
1283
+ for (int i = 0; i < startPoints.length(); i++) {
1284
+ JSONObject point = startPoints.optJSONObject(i);
1285
+ if (point == null) {
1286
+ continue;
1287
+ }
1288
+
1289
+ float pointX = (float) point.optDouble("x", 0f);
1290
+ float pointY = (float) point.optDouble("y", 0f);
1291
+ float xRatio = (pointX - startBounds.left) / startWidth;
1292
+ float yRatio = (pointY - startBounds.top) / startHeight;
1293
+
1294
+ JSONObject transformed = new JSONObject();
1295
+ transformed.put("x", Math.min(1f, Math.max(0f, newBounds.left + (xRatio * newWidth))));
1296
+ transformed.put("y", Math.min(1f, Math.max(0f, newBounds.top + (yRatio * newHeight))));
1297
+ points.put(transformed);
1298
+ }
1299
+
1300
+ annotation.put("points", points);
1301
+ } catch (JSONException ignored) {
1302
+ }
1303
+
1304
+ return;
1305
+ }
1306
+
1307
+ try {
1308
+ annotation.put("bounds", new JSONObject()
1309
+ .put("x", Math.min(1f, Math.max(0f, newBounds.left)))
1310
+ .put("y", Math.min(1f, Math.max(0f, newBounds.top)))
1311
+ .put("width", Math.min(1f, Math.max(0.01f, newBounds.width())))
1312
+ .put("height", Math.min(1f, Math.max(0.01f, newBounds.height()))));
1313
+ } catch (JSONException ignored) {
1314
+ }
1315
+ }
1316
+
1317
+ private RectF resizeHandleRect(RectF rect) {
1318
+ float size = Math.max(18f, Math.min(rect.width(), rect.height()) * 0.18f);
1319
+ return new RectF(rect.right - size, rect.bottom - size, rect.right, rect.bottom);
1320
+ }
1321
+
1322
+ private AnnotationSelectionHit hitTestAnnotation(float x, float y, boolean includeHandles) {
1323
+ if (PdfView.this.getPageCount() <= 0) {
1324
+ return null;
1325
+ }
1326
+
1327
+ for (int i = draftAnnotations.size() - 1; i >= 0; i--) {
1328
+ JSONObject annotation = draftAnnotations.get(i);
1329
+ int pageIndex = annotation.optInt("page", 1) - 1;
1330
+ if (pageIndex < 0 || pageIndex >= PdfView.this.getPageCount()) {
1331
+ continue;
1332
+ }
1333
+
1334
+ RectF rect = viewRectForAnnotation(annotation, pageIndex);
1335
+ if (rect == null) {
1336
+ continue;
1337
+ }
1338
+
1339
+ RectF hitRect = new RectF(rect);
1340
+ hitRect.inset(-12f, -12f);
1341
+ if (!hitRect.contains(x, y)) {
1342
+ continue;
1343
+ }
1344
+
1345
+ String hitPart = "body";
1346
+ if (includeHandles && selectedAnnotationId != null && selectedAnnotationId.equals(annotation.optString("id", null)) && annotationSupportsResize(annotation) && resizeHandleRect(rect).contains(x, y)) {
1347
+ hitPart = "resize";
1348
+ }
1349
+
1350
+ return new AnnotationSelectionHit(annotation, pageIndex, rect, hitPart);
1351
+ }
1352
+
1353
+ return null;
1354
+ }
1355
+
1356
+ private final class AnnotationSelectionHit {
1357
+ final JSONObject annotation;
1358
+ final int pageIndex;
1359
+ final RectF rect;
1360
+ final String hitPart;
1361
+
1362
+ AnnotationSelectionHit(JSONObject annotation, int pageIndex, RectF rect, String hitPart) {
1363
+ this.annotation = annotation;
1364
+ this.pageIndex = pageIndex;
1365
+ this.rect = rect;
1366
+ this.hitPart = hitPart;
1367
+ }
1368
+ }
1369
+
1370
+ private void beginInk(AnnotationHit hit, float x, float y) {
1371
+ try {
1372
+ JSONObject annotation = new JSONObject();
1373
+ annotation.put("id", nextLocalAnnotationId());
1374
+ annotation.put("page", hit.pageIndex + 1);
1375
+ annotation.put("type", "ink");
1376
+ annotation.put("points", new JSONArray());
1377
+ JSONObject style = new JSONObject();
1378
+ style.put("color", inkColor);
1379
+ style.put("thickness", inkThickness);
1380
+ annotation.put("style", style);
1381
+ draftAnnotations.add(annotation);
1382
+ activeInkAnnotation = annotation;
1383
+ appendInkPoint(hit, x, y);
1384
+ } catch (JSONException ignored) {
1385
+ }
1386
+ }
1387
+
1388
+ private void appendInkPoint(AnnotationHit hit, float x, float y) {
1389
+ if (activeInkAnnotation == null) {
1390
+ return;
1391
+ }
1392
+
1393
+ try {
1394
+ JSONArray points = activeInkAnnotation.optJSONArray("points");
1395
+ if (points == null) {
1396
+ points = new JSONArray();
1397
+ activeInkAnnotation.put("points", points);
1398
+ }
1399
+
1400
+ PointF normalized = normalizedPointFor(hit, x, y);
1401
+ JSONObject point = new JSONObject();
1402
+ point.put("x", normalized.x);
1403
+ point.put("y", normalized.y);
1404
+ points.put(point);
1405
+ invalidate();
1406
+ } catch (JSONException ignored) {
1407
+ }
1408
+ }
1409
+
1410
+ private void endInk() {
1411
+ activeInkAnnotation = null;
1412
+ invalidate();
1413
+ PdfView.this.notifyOnChangeWithMessage("strokeEnd");
1414
+ }
1415
+
1416
+ private void beginMarkup(AnnotationHit hit, float x, float y, String type) {
1417
+ try {
1418
+ markupStartNormalized = normalizedPointFor(hit, x, y);
1419
+ JSONObject annotation = new JSONObject();
1420
+ annotation.put("id", nextLocalAnnotationId());
1421
+ annotation.put("page", hit.pageIndex + 1);
1422
+ annotation.put("type", normalizeAnnotationType(type));
1423
+ annotation.put("bounds", new JSONObject().put("x", markupStartNormalized.x).put("y", markupStartNormalized.y).put("width", 0).put("height", 0));
1424
+ annotation.put("style", new JSONObject());
1425
+ draftAnnotations.add(annotation);
1426
+ activeMarkupAnnotation = annotation;
1427
+ invalidate();
1428
+ } catch (JSONException ignored) {
1429
+ }
1430
+ }
1431
+
1432
+ private void updateMarkup(AnnotationHit hit, float x, float y) {
1433
+ if (activeMarkupAnnotation == null || markupStartNormalized == null) {
1434
+ return;
1435
+ }
1436
+
1437
+ try {
1438
+ PointF normalized = normalizedPointFor(hit, x, y);
1439
+ float minX = Math.min(markupStartNormalized.x, normalized.x);
1440
+ float minY = Math.min(markupStartNormalized.y, normalized.y);
1441
+ float maxX = Math.max(markupStartNormalized.x, normalized.x);
1442
+ float maxY = Math.max(markupStartNormalized.y, normalized.y);
1443
+ activeMarkupAnnotation.put("bounds", new JSONObject()
1444
+ .put("x", minX)
1445
+ .put("y", minY)
1446
+ .put("width", Math.max(0f, maxX - minX))
1447
+ .put("height", Math.max(0f, maxY - minY)));
1448
+ invalidate();
1449
+ } catch (JSONException ignored) {
1450
+ }
1451
+ }
1452
+
1453
+ private void endMarkup() {
1454
+ activeMarkupAnnotation = null;
1455
+ markupStartNormalized = null;
1456
+ invalidate();
1457
+ }
1458
+
1459
+ private void createTextAnnotation(AnnotationHit hit, float x, float y) {
1460
+ if (!editable || !supported) {
1461
+ return;
1462
+ }
1463
+
1464
+ commitTextEditingIfNeeded();
1465
+
1466
+ try {
1467
+ PointF normalized = normalizedPointFor(hit, x, y);
1468
+ float width = 0.25f;
1469
+ float height = 0.12f;
1470
+ float clampedX = Math.min(Math.max(normalized.x, 0f), Math.max(0f, 1f - width));
1471
+ float clampedY = Math.min(Math.max(normalized.y, 0f), Math.max(0f, 1f - height));
1472
+
1473
+ JSONObject bounds = new JSONObject();
1474
+ bounds.put("x", clampedX);
1475
+ bounds.put("y", clampedY);
1476
+ bounds.put("width", width);
1477
+ bounds.put("height", height);
1478
+
1479
+ JSONObject annotation = new JSONObject();
1480
+ annotation.put("id", nextLocalAnnotationId());
1481
+ annotation.put("page", hit.pageIndex + 1);
1482
+ annotation.put("type", "text");
1483
+ annotation.put("bounds", bounds);
1484
+ annotation.put("text", "");
1485
+ JSONObject style = new JSONObject();
1486
+ style.put("color", "#2244aa");
1487
+ style.put("fontSize", 15.0f);
1488
+ style.put("textAlign", "left");
1489
+ annotation.put("style", style);
1490
+ draftAnnotations.add(annotation);
1491
+ activeTextAnnotation = annotation;
1492
+
1493
+ activeEditText = new EditText(getContext());
1494
+ activeEditText.setBackgroundColor(Color.argb(220, 255, 255, 255));
1495
+ activeEditText.setTextColor(Color.rgb(34, 68, 170));
1496
+ activeEditText.setPadding(12, 8, 12, 8);
1497
+ activeEditText.setSingleLine(false);
1498
+ activeEditText.setGravity(Gravity.TOP | Gravity.START);
1499
+ activeEditText.setTextSize(15f);
1500
+ activeEditText.addTextChangedListener(new TextWatcher() {
1501
+ @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
1502
+ @Override public void onTextChanged(CharSequence s, int start, int before, int count) { }
1503
+ @Override public void afterTextChanged(Editable s) {
1504
+ if (activeTextAnnotation != null) {
1505
+ try {
1506
+ activeTextAnnotation.put("text", s.toString());
1507
+ } catch (JSONException ignored) {
1508
+ }
1509
+ invalidate();
1510
+ }
1511
+ }
1512
+ });
1513
+
1514
+ addView(activeEditText, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
1515
+ activeEditText.requestFocus();
1516
+ updateEditTextFrame();
1517
+ invalidate();
1518
+ } catch (JSONException ignored) {
1519
+ }
1520
+ }
1521
+
1522
+ void commitTextEditingIfNeeded() {
1523
+ if (activeEditText == null || activeTextAnnotation == null) {
1524
+ return;
1525
+ }
1526
+
1527
+ try {
1528
+ activeTextAnnotation.put("text", activeEditText.getText() == null ? "" : activeEditText.getText().toString());
1529
+ } catch (JSONException ignored) {
1530
+ }
1531
+
1532
+ activeEditText.clearFocus();
1533
+ removeView(activeEditText);
1534
+ activeEditText = null;
1535
+ activeTextAnnotation = null;
1536
+ invalidate();
1537
+ }
1538
+
1539
+ private void updateEditTextFrame() {
1540
+ if (activeEditText == null || activeTextAnnotation == null) {
1541
+ return;
1542
+ }
1543
+
1544
+ RectF rect = viewRectForAnnotation(activeTextAnnotation, activeTextAnnotation.optInt("page", 1) - 1);
1545
+ if (rect == null) {
1546
+ return;
1547
+ }
1548
+
1549
+ ViewGroup.LayoutParams params = activeEditText.getLayoutParams();
1550
+ params.width = Math.max(1, Math.round(rect.width()));
1551
+ params.height = Math.max(1, Math.round(rect.height()));
1552
+ activeEditText.setLayoutParams(params);
1553
+ activeEditText.setX(rect.left);
1554
+ activeEditText.setY(rect.top);
1555
+ }
1556
+
1557
+ private boolean isPointInsideView(float x, float y, View view) {
1558
+ if (view == null) {
1559
+ return false;
1560
+ }
1561
+
1562
+ return x >= view.getX() && x <= view.getX() + view.getWidth() && y >= view.getY() && y <= view.getY() + view.getHeight();
1563
+ }
1564
+
1565
+ private AnnotationHit hitTest(float x, float y) {
1566
+ if (PdfView.this.getPageCount() <= 0) {
1567
+ return null;
1568
+ }
1569
+
1570
+ for (int i = 0; i < PdfView.this.getPageCount(); i++) {
1571
+ PageRenderInfo pageRenderInfo = PdfView.this.getPageRenderInfo(i);
1572
+ if (pageRenderInfo == null) {
1573
+ continue;
1574
+ }
1575
+
1576
+ if (x >= pageRenderInfo.left &&
1577
+ x <= pageRenderInfo.left + pageRenderInfo.width &&
1578
+ y >= pageRenderInfo.top &&
1579
+ y <= pageRenderInfo.top + pageRenderInfo.height) {
1580
+ return new AnnotationHit(i, pageRenderInfo);
1581
+ }
1582
+ }
1583
+
1584
+ return null;
1585
+ }
1586
+
1587
+ private PointF normalizedPointFor(AnnotationHit hit, float x, float y) {
1588
+ PageRenderInfo pageRenderInfo = hit.pageRenderInfo;
1589
+ float pageX = x - pageRenderInfo.left;
1590
+ float pageY = y - pageRenderInfo.top;
1591
+ return new PointF(
1592
+ pageX / Math.max(1f, pageRenderInfo.width),
1593
+ pageY / Math.max(1f, pageRenderInfo.height)
1594
+ );
1595
+ }
1596
+
1597
+ private PointF viewPointForNormalizedPoint(int pageIndex, float normalizedX, float normalizedY) {
1598
+ PageRenderInfo pageRenderInfo = PdfView.this.getPageRenderInfo(pageIndex);
1599
+ if (pageRenderInfo == null) {
1600
+ return new PointF();
1601
+ }
1602
+
1603
+ return new PointF(
1604
+ pageRenderInfo.left + (normalizedX * pageRenderInfo.width),
1605
+ pageRenderInfo.top + (normalizedY * pageRenderInfo.height)
1606
+ );
1607
+ }
1608
+
1609
+ private RectF viewRectForAnnotation(JSONObject annotation, int pageIndex) {
1610
+ JSONObject bounds = annotation.optJSONObject("bounds");
1611
+ String type = annotation.optString("type", "");
1612
+ if (bounds == null && !"ink".equals(type)) {
1613
+ return null;
1614
+ }
1615
+
1616
+ PageRenderInfo pageRenderInfo = PdfView.this.getPageRenderInfo(pageIndex);
1617
+ if (pageRenderInfo == null) {
1618
+ return null;
1619
+ }
1620
+
1621
+ if ("ink".equals(type)) {
1622
+ JSONArray points = annotation.optJSONArray("points");
1623
+ if (points == null || points.length() == 0) {
1624
+ return null;
1625
+ }
1626
+
1627
+ float minX = Float.MAX_VALUE;
1628
+ float minY = Float.MAX_VALUE;
1629
+ float maxX = -Float.MAX_VALUE;
1630
+ float maxY = -Float.MAX_VALUE;
1631
+ for (int i = 0; i < points.length(); i++) {
1632
+ JSONObject point = points.optJSONObject(i);
1633
+ if (point == null) {
1634
+ continue;
1635
+ }
1636
+
1637
+ float pointX = (float) point.optDouble("x", 0f);
1638
+ float pointY = (float) point.optDouble("y", 0f);
1639
+ minX = Math.min(minX, pointX);
1640
+ minY = Math.min(minY, pointY);
1641
+ maxX = Math.max(maxX, pointX);
1642
+ maxY = Math.max(maxY, pointY);
1643
+ }
1644
+
1645
+ if (minX == Float.MAX_VALUE || minY == Float.MAX_VALUE || maxX == -Float.MAX_VALUE || maxY == -Float.MAX_VALUE) {
1646
+ return null;
1647
+ }
1648
+
1649
+ float left = pageRenderInfo.left + (minX * pageRenderInfo.width);
1650
+ float top = pageRenderInfo.top + (minY * pageRenderInfo.height);
1651
+ float right = pageRenderInfo.left + (maxX * pageRenderInfo.width);
1652
+ float bottom = pageRenderInfo.top + (maxY * pageRenderInfo.height);
1653
+ return new RectF(Math.min(left, right), Math.min(top, bottom), Math.max(left, right), Math.max(top, bottom));
1654
+ }
1655
+
1656
+ float x = (float) bounds.optDouble("x", 0f);
1657
+ float y = (float) bounds.optDouble("y", 0f);
1658
+ float width = (float) bounds.optDouble("width", 0f);
1659
+ float height = (float) bounds.optDouble("height", 0f);
1660
+
1661
+ float left = pageRenderInfo.left + (x * pageRenderInfo.width);
1662
+ float top = pageRenderInfo.top + (y * pageRenderInfo.height);
1663
+ float right = pageRenderInfo.left + ((x + width) * pageRenderInfo.width);
1664
+ float bottom = pageRenderInfo.top + ((y + height) * pageRenderInfo.height);
1665
+
1666
+ return new RectF(Math.min(left, right), Math.min(top, bottom), Math.max(left, right), Math.max(top, bottom));
1667
+ }
1668
+
1669
+ private JSONObject styleFor(JSONObject annotation) {
1670
+ JSONObject style = annotation.optJSONObject("style");
1671
+ return style == null ? new JSONObject() : style;
1672
+ }
1673
+
1674
+ private String normalizeAnnotationType(String type) {
1675
+ if ("underline".equals(type) || "strikeout".equals(type)) {
1676
+ return "highlight";
1677
+ }
1678
+
1679
+ return type;
1680
+ }
1681
+
1682
+ private boolean annotationSupportsResize(JSONObject annotation) {
1683
+ String type = normalizeAnnotationType(annotation.optString("type", ""));
1684
+ return "text".equals(type) || "highlight".equals(type);
1685
+ }
1686
+
1687
+ private int annotationColor(JSONObject annotation, int fallback) {
1688
+ String color = styleFor(annotation).optString("color", null);
1689
+ if (TextUtils.isEmpty(color)) {
1690
+ return fallback;
1691
+ }
1692
+
1693
+ try {
1694
+ // Support CSS-standard #RRGGBBAA (alpha last) in addition to #RRGGBB
1695
+ if (color.startsWith("#") && color.length() == 9) {
1696
+ int r = Integer.parseInt(color.substring(1, 3), 16);
1697
+ int g = Integer.parseInt(color.substring(3, 5), 16);
1698
+ int b = Integer.parseInt(color.substring(5, 7), 16);
1699
+ int a = Integer.parseInt(color.substring(7, 9), 16);
1700
+ return Color.argb(a, r, g, b);
1701
+ }
1702
+ return Color.parseColor(color);
1703
+ } catch (IllegalArgumentException ex) {
1704
+ return fallback;
1705
+ }
1706
+ }
1707
+
1708
+ private int annotationFillColor(String type) {
1709
+ if ("highlight".equals(type) || "underline".equals(type) || "strikeout".equals(type)) {
1710
+ return Color.argb(90, 255, 230, 60);
1711
+ }
1712
+
1713
+ return Color.BLACK;
1714
+ }
1715
+
1716
+ private String nextLocalAnnotationId() {
1717
+ return "local-" + java.util.UUID.randomUUID().toString();
1718
+ }
1719
+
1720
+ private final class AnnotationHit {
1721
+ final int pageIndex;
1722
+ final PageRenderInfo pageRenderInfo;
1723
+
1724
+ AnnotationHit(int pageIndex, PageRenderInfo pageRenderInfo) {
1725
+ this.pageIndex = pageIndex;
1726
+ this.pageRenderInfo = pageRenderInfo;
1727
+ }
1728
+ }
502
1729
  }
503
1730
 
504
1731
  /**
@@ -546,6 +1773,28 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
546
1773
  this.jumpTo(page);
547
1774
  }
548
1775
 
1776
+ public void saveAnnotations() {
1777
+ if (annotationOverlayView != null) {
1778
+ annotationOverlayView.commitTextEditingIfNeeded();
1779
+ notifyOnChangeWithMessage("annotationSaveComplete|" + annotationOverlayView.serializeDocument());
1780
+ return;
1781
+ }
1782
+
1783
+ notifyOnChangeWithMessage("annotationSaveError|Annotation overlay unavailable");
1784
+ }
1785
+
1786
+ public void deleteSelectedAnnotation() {
1787
+ if (annotationOverlayView != null) {
1788
+ annotationOverlayView.deleteSelectedAnnotation();
1789
+ }
1790
+ }
1791
+
1792
+ public void deleteAllAnnotations() {
1793
+ if (annotationOverlayView != null) {
1794
+ annotationOverlayView.deleteAllAnnotations();
1795
+ }
1796
+ }
1797
+
549
1798
  private void showLog(final String str) {
550
1799
  Log.d("PdfView", str);
551
1800
  }