@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
@@ -53,6 +53,43 @@
53
53
 
54
54
  const float MAX_SCALE = 3.0f;
55
55
  const float MIN_SCALE = 1.0f;
56
+ static void *RNPDFPdfScrollViewContentOffsetContext = &RNPDFPdfScrollViewContentOffsetContext;
57
+
58
+ @class RNPDFAnnotationOverlay;
59
+
60
+ @interface RNPDFAnnotationOverlay : UIView <UITextViewDelegate>
61
+ @property(nonatomic, weak) PDFView *pdfView;
62
+ @property(nonatomic, weak) PDFDocument *pdfDocument;
63
+ @property(nonatomic, assign) BOOL annotationMode;
64
+ @property(nonatomic, assign) BOOL annotationEditable;
65
+ @property(nonatomic, copy) NSString *annotationTool;
66
+ @property(nonatomic, copy) NSString *annotationIdMode;
67
+ @property(nonatomic, copy) NSString *annotationInkColor;
68
+ @property(nonatomic, assign) CGFloat annotationInkThickness;
69
+
70
+ - (void)replaceAnnotationsJSONString:(NSString *)json editable:(BOOL)editable idMode:(NSString *)idMode;
71
+ - (void)setAnnotationMode:(BOOL)annotationMode tool:(NSString *)tool editable:(BOOL)editable idMode:(NSString *)idMode;
72
+ - (void)setInkDefaultsColor:(NSString *)color thickness:(CGFloat)thickness;
73
+ - (void)beginInkAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page;
74
+ - (void)appendInkPointAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page;
75
+ - (void)endInk;
76
+ - (void)beginMarkupAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page type:(NSString *)type;
77
+ - (void)updateMarkupAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page;
78
+ - (void)endMarkup;
79
+ - (void)createTextAnnotationAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page;
80
+ - (NSDictionary *)annotationSelectionHitAtPoint:(CGPoint)point includeHandles:(BOOL)includeHandles;
81
+ - (void)selectAnnotation:(NSDictionary *)annotation;
82
+ - (void)clearSelection;
83
+ - (void)deleteAnnotation:(NSDictionary *)annotation;
84
+ - (void)deleteSelectedAnnotation;
85
+ - (void)deleteAllAnnotations;
86
+ - (void)refreshDisplay;
87
+ - (void)beginSelectionInteractionAtPoint:(CGPoint)point hit:(NSDictionary *)hit;
88
+ - (void)updateSelectionInteractionAtPoint:(CGPoint)point;
89
+ - (void)endSelectionInteraction;
90
+ - (void)commitTextEditingIfNeeded;
91
+ - (NSString *)serializedDocumentJSONStringWithEditable:(BOOL)editable idMode:(NSString *)idMode;
92
+ @end
56
93
 
57
94
  @interface RNPDFPdfView() <PDFDocumentDelegate, PDFViewDelegate
58
95
  #ifdef RCT_NEW_ARCH_ENABLED
@@ -75,6 +112,8 @@ const float MIN_SCALE = 1.0f;
75
112
  UIPinchGestureRecognizer *_pinchRecognizer;
76
113
  UILongPressGestureRecognizer *_longPressRecognizer;
77
114
  UITapGestureRecognizer *_doubleTapEmptyRecognizer;
115
+ UIPanGestureRecognizer *_annotationPanRecognizer;
116
+ RNPDFAnnotationOverlay *_annotationOverlay;
78
117
 
79
118
  // Autoscroll
80
119
  CADisplayLink *_displayLink;
@@ -84,7 +123,8 @@ const float MIN_SCALE = 1.0f;
84
123
  BOOL _isAutoScrolling;
85
124
  BOOL _isUserDragging;
86
125
  UIScrollView *_pdfScrollView;
87
- __weak id<UIScrollViewDelegate> _originalScrollDelegate;
126
+ UIPanGestureRecognizer *_pdfScrollPanRecognizer;
127
+ BOOL _isObservingPdfScrollView;
88
128
  CGFloat _autoScrollCurrentOffset; // float accumulator – avoids re-reading UIKit's quantized contentOffset
89
129
  }
90
130
 
@@ -117,9 +157,12 @@ using namespace facebook::react;
117
157
  {
118
158
  const auto &newProps = *std::static_pointer_cast<const RNPDFPdfViewProps>(props);
119
159
  NSMutableArray<NSString *> *updatedPropNames = [NSMutableArray new];
120
- if (_path != RCTNSStringFromStringNilIfEmpty(newProps.path)) {
121
- _path = RCTNSStringFromStringNilIfEmpty(newProps.path);
122
- [updatedPropNames addObject:@"path"];
160
+ {
161
+ NSString *newPath = RCTNSStringFromStringNilIfEmpty(newProps.path);
162
+ if (_path != newPath && ![_path isEqualToString:newPath]) {
163
+ _path = newPath;
164
+ [updatedPropNames addObject:@"path"];
165
+ }
123
166
  }
124
167
  if (_page != newProps.page) {
125
168
  _page = newProps.page;
@@ -157,6 +200,37 @@ using namespace facebook::react;
157
200
  _enableDoubleTapZoom = newProps.enableDoubleTapZoom;
158
201
  [updatedPropNames addObject:@"enableDoubleTapZoom"];
159
202
  }
203
+ {
204
+ NSString *newAnnotations = RCTNSStringFromStringNilIfEmpty(newProps.annotations);
205
+ if (_annotations != newAnnotations && ![_annotations isEqualToString:newAnnotations]) {
206
+ _annotations = newAnnotations;
207
+ [updatedPropNames addObject:@"annotations"];
208
+ }
209
+ }
210
+ if (_annotationMode != newProps.annotationMode) {
211
+ _annotationMode = newProps.annotationMode;
212
+ [updatedPropNames addObject:@"annotationMode"];
213
+ }
214
+ if (_annotationTool != RCTNSStringFromStringNilIfEmpty(newProps.annotationTool)) {
215
+ _annotationTool = RCTNSStringFromStringNilIfEmpty(newProps.annotationTool);
216
+ [updatedPropNames addObject:@"annotationTool"];
217
+ }
218
+ if (_annotationEditable != newProps.annotationEditable) {
219
+ _annotationEditable = newProps.annotationEditable;
220
+ [updatedPropNames addObject:@"annotationEditable"];
221
+ }
222
+ if (_annotationIdMode != RCTNSStringFromStringNilIfEmpty(newProps.annotationIdMode)) {
223
+ _annotationIdMode = RCTNSStringFromStringNilIfEmpty(newProps.annotationIdMode);
224
+ [updatedPropNames addObject:@"annotationIdMode"];
225
+ }
226
+ if (_annotationInkColor != RCTNSStringFromStringNilIfEmpty(newProps.annotationInkColor)) {
227
+ _annotationInkColor = RCTNSStringFromStringNilIfEmpty(newProps.annotationInkColor);
228
+ [updatedPropNames addObject:@"annotationInkColor"];
229
+ }
230
+ if (_annotationInkThickness != newProps.annotationInkThickness) {
231
+ _annotationInkThickness = newProps.annotationInkThickness;
232
+ [updatedPropNames addObject:@"annotationInkThickness"];
233
+ }
160
234
  if (_fitPolicy != newProps.fitPolicy) {
161
235
  _fitPolicy = newProps.fitPolicy;
162
236
  [updatedPropNames addObject:@"fitPolicy"];
@@ -189,6 +263,16 @@ using namespace facebook::react;
189
263
 
190
264
  [super updateProps:props oldProps:oldProps];
191
265
  [self didSetProps:updatedPropNames];
266
+
267
+ if (_annotationOverlay) {
268
+ if ([updatedPropNames containsObject:@"annotations"] || [updatedPropNames containsObject:@"path"]) {
269
+ [_annotationOverlay replaceAnnotationsJSONString:_annotations editable:_annotationEditable idMode:_annotationIdMode];
270
+ }
271
+ [_annotationOverlay setAnnotationMode:_annotationMode tool:_annotationTool editable:_annotationEditable idMode:_annotationIdMode];
272
+ [_annotationOverlay setInkDefaultsColor:_annotationInkColor thickness:_annotationInkThickness];
273
+ _annotationOverlay.pdfView = _pdfView;
274
+ _annotationOverlay.pdfDocument = _pdfDocument;
275
+ }
192
276
  }
193
277
 
194
278
  // already added in case https://github.com/facebook/react-native/pull/35378 has been merged
@@ -202,8 +286,7 @@ using namespace facebook::react;
202
286
  [super prepareForRecycle];
203
287
 
204
288
  [self stopAutoScroll];
205
- _pdfScrollView = nil;
206
- _originalScrollDelegate = nil;
289
+ [self unbindPdfScrollViewObservation];
207
290
 
208
291
  [_pdfView removeFromSuperview];
209
292
  _pdfDocument = Nil;
@@ -219,6 +302,10 @@ using namespace facebook::react;
219
302
  [self removeGestureRecognizer:_pinchRecognizer];
220
303
  [self removeGestureRecognizer:_longPressRecognizer];
221
304
  [self removeGestureRecognizer:_doubleTapEmptyRecognizer];
305
+ [self removeGestureRecognizer:_annotationPanRecognizer];
306
+
307
+ [_annotationOverlay removeFromSuperview];
308
+ _annotationOverlay = nil;
222
309
 
223
310
  [self initCommonProps];
224
311
  }
@@ -228,6 +315,7 @@ using namespace facebook::react;
228
315
  // Fabric equivalent of `reactSetFrame` method
229
316
  [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
230
317
  _pdfView.frame = CGRectMake(0, 0, layoutMetrics.frame.size.width, layoutMetrics.frame.size.height);
318
+ _annotationOverlay.frame = CGRectMake(0, 0, layoutMetrics.frame.size.width, layoutMetrics.frame.size.height);
231
319
 
232
320
  NSMutableArray *mProps = [_changedProps mutableCopy];
233
321
  if (_initialed) {
@@ -289,6 +377,8 @@ using namespace facebook::react;
289
377
  _showsHorizontalScrollIndicator = YES;
290
378
  _showsVerticalScrollIndicator = YES;
291
379
  _scrollEnabled = YES;
380
+ _annotationInkColor = @"#111111";
381
+ _annotationInkThickness = 2.0f;
292
382
  _enableTextSelection = YES;
293
383
  _selectedText = nil;
294
384
  _currentPDFSelection = nil;
@@ -311,10 +401,18 @@ using namespace facebook::react;
311
401
  _isAutoScrolling = NO;
312
402
  _isUserDragging = NO;
313
403
  _pdfScrollView = nil;
314
- _originalScrollDelegate = nil;
404
+ _pdfScrollPanRecognizer = nil;
405
+ _isObservingPdfScrollView = NO;
315
406
  _autoScrollCurrentOffset = 0.0;
316
407
 
317
408
  [self addSubview:_pdfView];
409
+ _annotationOverlay = [[RNPDFAnnotationOverlay alloc] initWithFrame:self.bounds];
410
+ _annotationOverlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
411
+ _annotationOverlay.backgroundColor = UIColor.clearColor;
412
+ _annotationOverlay.userInteractionEnabled = YES;
413
+ [_annotationOverlay setInkDefaultsColor:_annotationInkColor thickness:_annotationInkThickness];
414
+ [self addSubview:_annotationOverlay];
415
+ [self bringSubviewToFront:_annotationOverlay];
318
416
 
319
417
 
320
418
  // register notification
@@ -425,6 +523,10 @@ using namespace facebook::react;
425
523
  }
426
524
 
427
525
  _pdfView.document = _pdfDocument;
526
+ if (_annotationOverlay) {
527
+ _annotationOverlay.pdfView = _pdfView;
528
+ _annotationOverlay.pdfDocument = _pdfDocument;
529
+ }
428
530
  } else {
429
531
 
430
532
  [self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"error|Load pdf failed. path=%s",_path.UTF8String]]];
@@ -551,22 +653,12 @@ using namespace facebook::react;
551
653
  [self setScrollIndicators:self horizontal:_showsHorizontalScrollIndicator vertical:_showsVerticalScrollIndicator depth:0];
552
654
  }
553
655
 
554
- if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"scrollEnabled"])) {
555
- if (_scrollEnabled) {
556
- for (UIView *subview in _pdfView.subviews) {
557
- if ([subview isKindOfClass:[UIScrollView class]]) {
558
- UIScrollView *scrollView = (UIScrollView *)subview;
559
- scrollView.scrollEnabled = YES;
560
- }
561
- }
562
- } else {
563
- for (UIView *subview in _pdfView.subviews) {
564
- if ([subview isKindOfClass:[UIScrollView class]]) {
565
- UIScrollView *scrollView = (UIScrollView *)subview;
566
- scrollView.scrollEnabled = NO;
567
- }
568
- }
569
- }
656
+ if ([changedProps containsObject:@"path"] ||
657
+ [changedProps containsObject:@"scrollEnabled"] ||
658
+ [changedProps containsObject:@"annotationMode"] ||
659
+ [changedProps containsObject:@"enablePaging"] ||
660
+ [changedProps containsObject:@"singlePage"]) {
661
+ [self updatePdfScrollInteractionMode];
570
662
  }
571
663
 
572
664
  if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"enablePaging"] || [changedProps containsObject:@"horizontal"] || [changedProps containsObject:@"page"])) {
@@ -598,8 +690,19 @@ using namespace facebook::react;
598
690
  }
599
691
  }
600
692
 
693
+ if (_annotationOverlay) {
694
+ if ([changedProps containsObject:@"annotations"] || [changedProps containsObject:@"path"]) {
695
+ [_annotationOverlay replaceAnnotationsJSONString:_annotations editable:_annotationEditable idMode:_annotationIdMode];
696
+ }
697
+ [_annotationOverlay setAnnotationMode:_annotationMode tool:_annotationTool editable:_annotationEditable idMode:_annotationIdMode];
698
+ [_annotationOverlay setInkDefaultsColor:_annotationInkColor thickness:_annotationInkThickness];
699
+ _annotationOverlay.pdfView = _pdfView;
700
+ _annotationOverlay.pdfDocument = _pdfDocument;
701
+ }
702
+
601
703
  _pdfView.backgroundColor = [UIColor clearColor];
602
704
  [_pdfView layoutDocumentView];
705
+ [self refreshPdfScrollViewBinding];
603
706
  [self setNeedsDisplay];
604
707
  }
605
708
  }
@@ -635,6 +738,7 @@ using namespace facebook::react;
635
738
  - (void)dealloc{
636
739
 
637
740
  [self stopAutoScroll];
741
+ [self unbindPdfScrollViewObservation];
638
742
 
639
743
  _pdfDocument = Nil;
640
744
  _pdfView = Nil;
@@ -766,6 +870,8 @@ using namespace facebook::react;
766
870
  [self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"pageChanged|%lu|%lu", page+1, numberOfPages]]];
767
871
  }
768
872
 
873
+ [_annotationOverlay refreshDisplay];
874
+
769
875
  }
770
876
 
771
877
  - (void)onScaleChanged:(NSNotification *)noti
@@ -777,6 +883,8 @@ using namespace facebook::react;
777
883
  [self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"scaleChanged|%f", _scale]]];
778
884
  }
779
885
  }
886
+
887
+ [_annotationOverlay refreshDisplay];
780
888
  }
781
889
 
782
890
  #pragma mark gesture process
@@ -875,6 +983,37 @@ using namespace facebook::react;
875
983
  PDFPage *pdfPage = [_pdfView pageForPoint:point nearest:NO];
876
984
  if (pdfPage) {
877
985
  unsigned long page = [_pdfDocument indexForPage:pdfPage];
986
+ if (_annotationMode && _annotationEditable) {
987
+ if ([_annotationTool isEqualToString:@"select"]) {
988
+ [_annotationOverlay commitTextEditingIfNeeded];
989
+
990
+ NSDictionary *hit = [_annotationOverlay annotationSelectionHitAtPoint:point includeHandles:YES];
991
+ if (hit) {
992
+ NSDictionary *annotation = hit[@"annotation"];
993
+ [_annotationOverlay selectAnnotation:annotation];
994
+
995
+ return;
996
+ }
997
+
998
+ [_annotationOverlay clearSelection];
999
+ [self notifyOnChangeWithMessage:
1000
+ [[NSString alloc] initWithString:[NSString stringWithFormat:@"pageSingleTap|%lu|%f|%f", page+1, point.x, point.y]]];
1001
+ return;
1002
+ }
1003
+
1004
+ if ([_annotationTool isEqualToString:@"text"]) {
1005
+ [_annotationOverlay createTextAnnotationAtViewPoint:point page:pdfPage];
1006
+ return;
1007
+ }
1008
+
1009
+ if ([_annotationTool isEqualToString:@"ink"]) {
1010
+ [_annotationOverlay beginInkAtViewPoint:point page:pdfPage];
1011
+ [_annotationOverlay endInk];
1012
+ [self notifyOnChangeWithMessage:@"strokeEnd"];
1013
+ return;
1014
+ }
1015
+ }
1016
+
878
1017
  [self notifyOnChangeWithMessage:
879
1018
  [[NSString alloc] initWithString:[NSString stringWithFormat:@"pageSingleTap|%lu|%f|%f", page+1, point.x, point.y]]];
880
1019
  }
@@ -904,6 +1043,81 @@ using namespace facebook::react;
904
1043
 
905
1044
  }
906
1045
 
1046
+ - (void)handleAnnotationPan:(UIPanGestureRecognizer *)sender
1047
+ {
1048
+ if (!_annotationMode || !_annotationEditable || _annotationTool == nil) {
1049
+ return;
1050
+ }
1051
+
1052
+ CGPoint point = [sender locationInView:self];
1053
+ PDFPage *pdfPage = [_pdfDocument pageAtIndex:MAX(0, [_pdfDocument indexForPage:[_pdfView pageForPoint:point nearest:NO]])];
1054
+ if (!pdfPage) {
1055
+ return;
1056
+ }
1057
+
1058
+ if ([_annotationTool isEqualToString:@"select"]) {
1059
+ if (sender.state == UIGestureRecognizerStateBegan) {
1060
+ [_annotationOverlay commitTextEditingIfNeeded];
1061
+
1062
+ NSDictionary *hit = [_annotationOverlay annotationSelectionHitAtPoint:point includeHandles:YES];
1063
+ if (!hit) {
1064
+ return;
1065
+ }
1066
+
1067
+ [_annotationOverlay selectAnnotation:hit[@"annotation"]];
1068
+ [_annotationOverlay beginSelectionInteractionAtPoint:point hit:hit];
1069
+ } else if (sender.state == UIGestureRecognizerStateChanged) {
1070
+ [_annotationOverlay updateSelectionInteractionAtPoint:point];
1071
+ } else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed) {
1072
+ [_annotationOverlay endSelectionInteraction];
1073
+ }
1074
+ return;
1075
+ }
1076
+
1077
+ if (sender.state == UIGestureRecognizerStateBegan) {
1078
+ if ([_annotationTool isEqualToString:@"ink"]) {
1079
+ [_annotationOverlay beginInkAtViewPoint:point page:pdfPage];
1080
+ }
1081
+ } else if (sender.state == UIGestureRecognizerStateChanged) {
1082
+ if ([_annotationTool isEqualToString:@"ink"]) {
1083
+ [_annotationOverlay appendInkPointAtViewPoint:point page:pdfPage];
1084
+ }
1085
+ } else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed) {
1086
+ if ([_annotationTool isEqualToString:@"ink"]) {
1087
+ [_annotationOverlay endInk];
1088
+ [self notifyOnChangeWithMessage:@"strokeEnd"];
1089
+ }
1090
+ }
1091
+ }
1092
+
1093
+ - (void)saveAnnotations
1094
+ {
1095
+ if (_annotationOverlay) {
1096
+ [_annotationOverlay commitTextEditingIfNeeded];
1097
+ NSString *jsonString = [_annotationOverlay serializedDocumentJSONStringWithEditable:_annotationEditable idMode:_annotationIdMode];
1098
+ [self notifyOnChangeWithMessage:[NSString stringWithFormat:@"annotationSaveComplete|%@", jsonString]];
1099
+ return;
1100
+ }
1101
+
1102
+ [self notifyOnChangeWithMessage:@"annotationSaveError|Annotation overlay unavailable"];
1103
+ }
1104
+
1105
+ - (void)deleteSelectedAnnotation
1106
+ {
1107
+ if (_annotationOverlay) {
1108
+ [_annotationOverlay commitTextEditingIfNeeded];
1109
+ [_annotationOverlay deleteSelectedAnnotation];
1110
+ }
1111
+ }
1112
+
1113
+ - (void)deleteAllAnnotations
1114
+ {
1115
+ if (_annotationOverlay) {
1116
+ [_annotationOverlay commitTextEditingIfNeeded];
1117
+ [_annotationOverlay deleteAllAnnotations];
1118
+ }
1119
+ }
1120
+
907
1121
  /**
908
1122
  * Bind tap
909
1123
  *
@@ -950,6 +1164,15 @@ using namespace facebook::react;
950
1164
  [self addGestureRecognizer:longPressRecognizer];
951
1165
  _longPressRecognizer = longPressRecognizer;
952
1166
 
1167
+ UIPanGestureRecognizer *annotationPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
1168
+ action:@selector(handleAnnotationPan:)];
1169
+ annotationPanRecognizer.maximumNumberOfTouches = 1;
1170
+ annotationPanRecognizer.minimumNumberOfTouches = 1;
1171
+ annotationPanRecognizer.delegate = self;
1172
+ annotationPanRecognizer.cancelsTouchesInView = YES;
1173
+ [self addGestureRecognizer:annotationPanRecognizer];
1174
+ _annotationPanRecognizer = annotationPanRecognizer;
1175
+
953
1176
  // Override the _pdfView double tap gesture recognizer so that it doesn't confilict with custom double tap
954
1177
  UITapGestureRecognizer *doubleTapEmptyRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
955
1178
  action:@selector(handleDoubleTapEmpty:)];
@@ -961,11 +1184,28 @@ using namespace facebook::react;
961
1184
  - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
962
1185
 
963
1186
  {
1187
+ if (gestureRecognizer == _annotationPanRecognizer) {
1188
+ if (!_annotationMode || !_annotationEditable || _annotationTool == nil) {
1189
+ return NO;
1190
+ }
1191
+
1192
+ if ([_annotationTool isEqualToString:@"select"]) {
1193
+ CGPoint point = [gestureRecognizer locationInView:self];
1194
+ return [_annotationOverlay annotationSelectionHitAtPoint:point includeHandles:YES] != nil;
1195
+ }
1196
+
1197
+ return [_annotationTool isEqualToString:@"ink"];
1198
+ }
1199
+
964
1200
  return !_singlePage;
965
1201
  }
966
1202
 
967
1203
  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
968
1204
  {
1205
+ if (gestureRecognizer == _annotationPanRecognizer || otherGestureRecognizer == _annotationPanRecognizer) {
1206
+ return NO;
1207
+ }
1208
+
969
1209
  return !_singlePage;
970
1210
  }
971
1211
 
@@ -986,19 +1226,116 @@ using namespace facebook::react;
986
1226
  }
987
1227
  }
988
1228
 
1229
+ - (void)updatePdfScrollInteractionMode
1230
+ {
1231
+ [self refreshPdfScrollViewBinding];
1232
+ [self applyScrollInteractionModeToView:_pdfView depth:0];
1233
+ }
1234
+
1235
+ - (void)unbindPdfScrollViewObservation
1236
+ {
1237
+ if (_pdfScrollPanRecognizer != nil) {
1238
+ [_pdfScrollPanRecognizer removeTarget:self action:@selector(handlePdfScrollPanGesture:)];
1239
+ _pdfScrollPanRecognizer = nil;
1240
+ }
1241
+
1242
+ if (_isObservingPdfScrollView && _pdfScrollView != nil) {
1243
+ [_pdfScrollView removeObserver:self forKeyPath:@"contentOffset" context:RNPDFPdfScrollViewContentOffsetContext];
1244
+ _isObservingPdfScrollView = NO;
1245
+ }
1246
+
1247
+ _pdfScrollView = nil;
1248
+ }
1249
+
1250
+ - (UIScrollView *)preferredPdfScrollViewInView:(UIView *)view depth:(int)depth
1251
+ {
1252
+ if (view == nil || depth > 10) {
1253
+ return nil;
1254
+ }
1255
+
1256
+ UIScrollView *bestScrollView = nil;
1257
+ CGFloat bestScrollableExtent = 0.0f;
1258
+
1259
+ if ([view isKindOfClass:[UIScrollView class]]) {
1260
+ UIScrollView *scrollView = (UIScrollView *)view;
1261
+ CGFloat verticalOverflow = MAX(scrollView.contentSize.height - scrollView.bounds.size.height, 0.0f);
1262
+ CGFloat horizontalOverflow = MAX(scrollView.contentSize.width - scrollView.bounds.size.width, 0.0f);
1263
+ bestScrollableExtent = MAX(verticalOverflow, horizontalOverflow);
1264
+ bestScrollView = scrollView;
1265
+ }
1266
+
1267
+ for (UIView *subview in view.subviews) {
1268
+ UIScrollView *candidate = [self preferredPdfScrollViewInView:subview depth:depth + 1];
1269
+ if (!candidate) {
1270
+ continue;
1271
+ }
1272
+
1273
+ CGFloat candidateVerticalOverflow = MAX(candidate.contentSize.height - candidate.bounds.size.height, 0.0f);
1274
+ CGFloat candidateHorizontalOverflow = MAX(candidate.contentSize.width - candidate.bounds.size.width, 0.0f);
1275
+ CGFloat candidateScrollableExtent = MAX(candidateVerticalOverflow, candidateHorizontalOverflow);
1276
+ if (bestScrollView == nil || candidateScrollableExtent >= bestScrollableExtent) {
1277
+ bestScrollView = candidate;
1278
+ bestScrollableExtent = candidateScrollableExtent;
1279
+ }
1280
+ }
1281
+
1282
+ return bestScrollView;
1283
+ }
1284
+
1285
+ - (void)refreshPdfScrollViewBinding
1286
+ {
1287
+ UIScrollView *preferredScrollView = [self preferredPdfScrollViewInView:_pdfView depth:0];
1288
+ if (preferredScrollView == _pdfScrollView) {
1289
+ return;
1290
+ }
1291
+
1292
+ [self unbindPdfScrollViewObservation];
1293
+
1294
+ _pdfScrollView = preferredScrollView;
1295
+
1296
+ if (preferredScrollView) {
1297
+ _pdfScrollPanRecognizer = preferredScrollView.panGestureRecognizer;
1298
+ if (_pdfScrollPanRecognizer != nil) {
1299
+ [_pdfScrollPanRecognizer addTarget:self action:@selector(handlePdfScrollPanGesture:)];
1300
+ }
1301
+
1302
+ [preferredScrollView addObserver:self
1303
+ forKeyPath:@"contentOffset"
1304
+ options:NSKeyValueObservingOptionNew
1305
+ context:RNPDFPdfScrollViewContentOffsetContext];
1306
+ _isObservingPdfScrollView = YES;
1307
+
1308
+ [_annotationOverlay refreshDisplay];
1309
+ }
1310
+ }
1311
+
1312
+ - (void)applyScrollInteractionModeToView:(UIView *)view depth:(int)depth
1313
+ {
1314
+ if (view == nil || depth > 10) {
1315
+ return;
1316
+ }
1317
+
1318
+ if ([view isKindOfClass:[UIScrollView class]]) {
1319
+ UIScrollView *scrollView = (UIScrollView *)view;
1320
+ scrollView.scrollEnabled = _scrollEnabled && !_singlePage;
1321
+
1322
+ UIPanGestureRecognizer *panRecognizer = scrollView.panGestureRecognizer;
1323
+ if (panRecognizer != nil) {
1324
+ panRecognizer.minimumNumberOfTouches = _annotationMode ? 2 : 1;
1325
+ }
1326
+ }
1327
+
1328
+ for (UIView *subview in view.subviews) {
1329
+ [self applyScrollInteractionModeToView:subview depth:depth + 1];
1330
+ }
1331
+ }
1332
+
989
1333
  #pragma mark - Autoscroll
990
1334
 
991
1335
  - (UIScrollView *)findPdfScrollView
992
1336
  {
1337
+ [self refreshPdfScrollViewBinding];
993
1338
  if (_pdfScrollView) return _pdfScrollView;
994
- for (UIView *subview in _pdfView.subviews) {
995
- if ([subview isKindOfClass:[UIScrollView class]]) {
996
- _pdfScrollView = (UIScrollView *)subview;
997
- _originalScrollDelegate = _pdfScrollView.delegate;
998
- _pdfScrollView.delegate = self;
999
- return _pdfScrollView;
1000
- }
1001
- }
1002
1339
  return nil;
1003
1340
  }
1004
1341
 
@@ -1092,50 +1429,1072 @@ using namespace facebook::react;
1092
1429
  }
1093
1430
  }
1094
1431
 
1095
- #pragma mark - UIScrollViewDelegate (autoscroll pause/resume)
1432
+ - (void)handlePdfScrollPanGesture:(UIPanGestureRecognizer *)gestureRecognizer
1433
+ {
1434
+ if (!_isAutoScrolling || gestureRecognizer != _pdfScrollPanRecognizer) {
1435
+ return;
1436
+ }
1437
+
1438
+ UIGestureRecognizerState state = gestureRecognizer.state;
1439
+ if (state == UIGestureRecognizerStateBegan) {
1440
+ _isUserDragging = YES;
1441
+ [self stopDisplayLink];
1442
+ [_autoScrollResumeTimer invalidate];
1443
+ _autoScrollResumeTimer = nil;
1444
+ return;
1445
+ }
1446
+
1447
+ if ((state == UIGestureRecognizerStateEnded ||
1448
+ state == UIGestureRecognizerStateCancelled ||
1449
+ state == UIGestureRecognizerStateFailed) &&
1450
+ _pdfScrollView != nil &&
1451
+ !_pdfScrollView.isDragging &&
1452
+ !_pdfScrollView.isDecelerating) {
1453
+ _isUserDragging = NO;
1454
+ [self scheduleAutoScrollResume];
1455
+ }
1456
+ }
1096
1457
 
1097
- - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1458
+ - (void)observeValueForKeyPath:(NSString *)keyPath
1459
+ ofObject:(id)object
1460
+ change:(NSDictionary<NSKeyValueChangeKey,id> *)change
1461
+ context:(void *)context
1098
1462
  {
1099
- _isUserDragging = YES;
1100
- [self stopDisplayLink];
1101
- [_autoScrollResumeTimer invalidate];
1102
- _autoScrollResumeTimer = nil;
1463
+ if (context == RNPDFPdfScrollViewContentOffsetContext) {
1464
+ if (object == _pdfScrollView) {
1465
+ [_annotationOverlay refreshDisplay];
1466
+
1467
+ if (_isAutoScrolling &&
1468
+ _isUserDragging &&
1469
+ _pdfScrollView != nil &&
1470
+ !_pdfScrollView.isDragging &&
1471
+ !_pdfScrollView.isDecelerating) {
1472
+ _isUserDragging = NO;
1473
+ [self scheduleAutoScrollResume];
1474
+ }
1475
+ }
1476
+ return;
1477
+ }
1478
+
1479
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
1480
+ }
1481
+
1482
+ @end
1483
+
1484
+ static UIColor *RNPDFColorFromHexString(NSString *hexString, UIColor *fallback)
1485
+ {
1486
+ if (![hexString isKindOfClass:[NSString class]] || hexString.length == 0) {
1487
+ return fallback;
1488
+ }
1489
+
1490
+ NSString *cleanHex = [[hexString stringByReplacingOccurrencesOfString:@"#" withString:@""] uppercaseString];
1491
+ unsigned int rgbValue = 0;
1492
+ if (cleanHex.length == 6) {
1493
+ NSScanner *scanner = [NSScanner scannerWithString:cleanHex];
1494
+ [scanner scanHexInt:&rgbValue];
1495
+ return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0f
1496
+ green:((rgbValue & 0x00FF00) >> 8) / 255.0f
1497
+ blue:(rgbValue & 0x0000FF) / 255.0f
1498
+ alpha:1.0f];
1499
+ }
1103
1500
 
1104
- if ([_originalScrollDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
1105
- [_originalScrollDelegate scrollViewWillBeginDragging:scrollView];
1501
+ if (cleanHex.length == 8) {
1502
+ NSScanner *scanner = [NSScanner scannerWithString:cleanHex];
1503
+ [scanner scanHexInt:&rgbValue];
1504
+ // CSS/RN standard: #RRGGBBAA (alpha is last two hex digits)
1505
+ return [UIColor colorWithRed:((rgbValue & 0xFF000000) >> 24) / 255.0f
1506
+ green:((rgbValue & 0x00FF0000) >> 16) / 255.0f
1507
+ blue:((rgbValue & 0x0000FF00) >> 8) / 255.0f
1508
+ alpha:(rgbValue & 0x000000FF) / 255.0f];
1106
1509
  }
1510
+
1511
+ return fallback;
1107
1512
  }
1108
1513
 
1109
- - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1514
+ static NSString *RNPDFGenerateAnnotationId(void)
1110
1515
  {
1111
- if (!decelerate) {
1112
- _isUserDragging = NO;
1113
- if (_isAutoScrolling) {
1114
- [self scheduleAutoScrollResume];
1516
+ return [[NSUUID UUID] UUIDString];
1517
+ }
1518
+
1519
+ @implementation RNPDFAnnotationOverlay
1520
+ {
1521
+ NSMutableArray<NSMutableDictionary *> *_draftAnnotations;
1522
+ NSMutableDictionary *_activeInkAnnotation;
1523
+ NSMutableDictionary *_activeMarkupAnnotation;
1524
+ NSMutableDictionary *_activeTextAnnotation;
1525
+ NSString *_selectedAnnotationId;
1526
+ NSMutableDictionary *_activeSelectionAnnotation;
1527
+ NSString *_activeSelectionMode;
1528
+ NSString *_activeSelectionHandle;
1529
+ CGPoint _selectionStartPoint;
1530
+ CGRect _selectionStartBounds;
1531
+ NSArray *_selectionStartPoints;
1532
+ NSInteger _selectionPageIndex;
1533
+ CGPoint _activeMarkupStartNormalized;
1534
+ UITextView *_activeTextView;
1535
+ }
1536
+
1537
+ - (instancetype)initWithFrame:(CGRect)frame
1538
+ {
1539
+ if (self = [super initWithFrame:frame]) {
1540
+ _draftAnnotations = [NSMutableArray new];
1541
+ _annotationMode = NO;
1542
+ _annotationEditable = YES;
1543
+ _annotationTool = @"select";
1544
+ _annotationIdMode = @"auto";
1545
+ _annotationInkColor = @"#111111";
1546
+ _annotationInkThickness = 2.0f;
1547
+ self.backgroundColor = UIColor.clearColor;
1548
+ self.opaque = NO;
1549
+ }
1550
+ return self;
1551
+ }
1552
+
1553
+ - (void)setPdfView:(PDFView *)pdfView
1554
+ {
1555
+ _pdfView = pdfView;
1556
+ [self refreshDisplay];
1557
+ }
1558
+
1559
+ - (void)setPdfDocument:(PDFDocument *)pdfDocument
1560
+ {
1561
+ _pdfDocument = pdfDocument;
1562
+ [self refreshDisplay];
1563
+ }
1564
+
1565
+ - (void)replaceAnnotationsJSONString:(NSString *)json editable:(BOOL)editable idMode:(NSString *)idMode
1566
+ {
1567
+ self.annotationEditable = editable;
1568
+ self.annotationIdMode = idMode ?: @"auto";
1569
+
1570
+ NSArray *parsedAnnotations = [self parseAnnotationsFromJSONString:json];
1571
+ _draftAnnotations = [parsedAnnotations mutableCopy] ?: [NSMutableArray new];
1572
+
1573
+ [self commitTextEditingIfNeeded];
1574
+ [self refreshDisplay];
1575
+ }
1576
+
1577
+ - (void)setAnnotationMode:(BOOL)annotationMode tool:(NSString *)tool editable:(BOOL)editable idMode:(NSString *)idMode
1578
+ {
1579
+ _annotationMode = annotationMode;
1580
+ _annotationEditable = editable;
1581
+ _annotationTool = [self normalizedAnnotationType:(tool ?: @"select")];
1582
+ _annotationIdMode = idMode ?: @"auto";
1583
+
1584
+ if (!annotationMode) {
1585
+ [self commitTextEditingIfNeeded];
1586
+ }
1587
+ }
1588
+
1589
+ - (void)setInkDefaultsColor:(NSString *)color thickness:(CGFloat)thickness
1590
+ {
1591
+ _annotationInkColor = color.length > 0 ? color : @"#111111";
1592
+ _annotationInkThickness = thickness > 0 ? thickness : 2.0f;
1593
+ }
1594
+
1595
+ - (NSString *)normalizedAnnotationType:(NSString *)type
1596
+ {
1597
+ if ([type isEqualToString:@"underline"] || [type isEqualToString:@"strikeout"]) {
1598
+ return @"highlight";
1599
+ }
1600
+
1601
+ return type;
1602
+ }
1603
+
1604
+ - (BOOL)annotationSupportsResize:(NSDictionary *)annotation
1605
+ {
1606
+ NSString *type = [self normalizedAnnotationType:annotation[@"type"]];
1607
+ return [type isEqualToString:@"text"] || [type isEqualToString:@"highlight"];
1608
+ }
1609
+
1610
+ - (NSArray *)parseAnnotationsFromJSONString:(NSString *)json
1611
+ {
1612
+ if (![json isKindOfClass:[NSString class]] || json.length == 0) {
1613
+ return @[];
1614
+ }
1615
+
1616
+ NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
1617
+ if (!data) {
1618
+ return @[];
1619
+ }
1620
+
1621
+ NSError *error = nil;
1622
+ id parsed = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
1623
+ if (error || !parsed) {
1624
+ return @[];
1625
+ }
1626
+
1627
+ NSArray *items = nil;
1628
+ if ([parsed isKindOfClass:[NSDictionary class]]) {
1629
+ id candidate = parsed[@"annotations"];
1630
+ if ([candidate isKindOfClass:[NSArray class]]) {
1631
+ items = candidate;
1632
+ }
1633
+ } else if ([parsed isKindOfClass:[NSArray class]]) {
1634
+ items = parsed;
1635
+ }
1636
+
1637
+ if (![items isKindOfClass:[NSArray class]]) {
1638
+ return @[];
1639
+ }
1640
+
1641
+ NSMutableArray *normalized = [NSMutableArray arrayWithCapacity:items.count];
1642
+ for (id item in items) {
1643
+ if (![item isKindOfClass:[NSDictionary class]]) {
1644
+ continue;
1115
1645
  }
1646
+
1647
+ NSMutableDictionary *annotation = [item mutableCopy];
1648
+ NSString *type = [self normalizedAnnotationType:annotation[@"type"]];
1649
+ if (type.length > 0) {
1650
+ annotation[@"type"] = type;
1651
+ }
1652
+ if (!annotation[@"id"]) {
1653
+ annotation[@"id"] = RNPDFGenerateAnnotationId();
1654
+ }
1655
+ if (!annotation[@"page"]) {
1656
+ annotation[@"page"] = @(1);
1657
+ }
1658
+ [normalized addObject:annotation];
1116
1659
  }
1117
1660
 
1118
- if ([_originalScrollDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) {
1119
- [_originalScrollDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
1661
+ return normalized;
1662
+ }
1663
+
1664
+ - (NSString *)serializedDocumentJSONStringWithEditable:(BOOL)editable idMode:(NSString *)idMode
1665
+ {
1666
+ NSMutableDictionary *document = [NSMutableDictionary dictionary];
1667
+ document[@"editable"] = @(editable);
1668
+ document[@"idMode"] = idMode ?: @"auto";
1669
+ document[@"annotations"] = [_draftAnnotations copy] ?: @[];
1670
+
1671
+ NSError *error = nil;
1672
+ NSData *data = [NSJSONSerialization dataWithJSONObject:document options:0 error:&error];
1673
+ if (error || !data) {
1674
+ return @"{}";
1120
1675
  }
1676
+
1677
+ return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] ?: @"{}";
1121
1678
  }
1122
1679
 
1123
- - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
1680
+ - (void)refreshDisplay
1124
1681
  {
1125
- _isUserDragging = NO;
1126
- if (_isAutoScrolling) {
1127
- [self scheduleAutoScrollResume];
1682
+ [self setNeedsDisplay];
1683
+ [self updateActiveTextEditorFrame];
1684
+ }
1685
+
1686
+ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
1687
+ {
1688
+ if (_activeTextView) {
1689
+ if (CGRectContainsPoint(_activeTextView.frame, point)) {
1690
+ return _activeTextView;
1691
+ }
1692
+
1693
+ return self;
1694
+ }
1695
+
1696
+ return nil;
1697
+ }
1698
+
1699
+ - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
1700
+ {
1701
+ if (_activeTextView) {
1702
+ [self commitTextEditingIfNeeded];
1703
+ }
1704
+
1705
+ [super touchesBegan:touches withEvent:event];
1706
+ }
1707
+
1708
+ - (void)layoutSubviews
1709
+ {
1710
+ [super layoutSubviews];
1711
+ [self updateActiveTextEditorFrame];
1712
+ }
1713
+
1714
+ - (CGPoint)normalizedPointForViewPoint:(CGPoint)viewPoint page:(PDFPage *)page
1715
+ {
1716
+ if (!self.pdfView || !page) {
1717
+ return CGPointZero;
1718
+ }
1719
+
1720
+ CGPoint pagePoint = [self.pdfView convertPoint:viewPoint toPage:page];
1721
+ CGRect pageBounds = [page boundsForBox:kPDFDisplayBoxCropBox];
1722
+ if (page.rotation == 90 || page.rotation == 270) {
1723
+ pageBounds = CGRectMake(0, 0, pageBounds.size.height, pageBounds.size.width);
1128
1724
  }
1129
1725
 
1130
- if ([_originalScrollDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) {
1131
- [_originalScrollDelegate scrollViewDidEndDecelerating:scrollView];
1726
+ CGFloat width = MAX(pageBounds.size.width, 1.0f);
1727
+ CGFloat height = MAX(pageBounds.size.height, 1.0f);
1728
+ return CGPointMake(pagePoint.x / width, 1.0f - (pagePoint.y / height));
1729
+ }
1730
+
1731
+ - (CGPoint)viewPointForNormalizedPoint:(CGPoint)normalizedPoint page:(PDFPage *)page
1732
+ {
1733
+ if (!self.pdfView || !page) {
1734
+ return CGPointZero;
1735
+ }
1736
+
1737
+ CGRect pageBounds = [page boundsForBox:kPDFDisplayBoxCropBox];
1738
+ if (page.rotation == 90 || page.rotation == 270) {
1739
+ pageBounds = CGRectMake(0, 0, pageBounds.size.height, pageBounds.size.width);
1740
+ }
1741
+
1742
+ CGFloat width = MAX(pageBounds.size.width, 1.0f);
1743
+ CGFloat height = MAX(pageBounds.size.height, 1.0f);
1744
+ CGPoint pagePoint = CGPointMake(normalizedPoint.x * width, (1.0f - normalizedPoint.y) * height);
1745
+ return [self.pdfView convertPoint:pagePoint fromPage:page];
1746
+ }
1747
+
1748
+ - (CGRect)viewRectForNormalizedBounds:(NSDictionary *)bounds page:(PDFPage *)page
1749
+ {
1750
+ if (!bounds || !page) {
1751
+ return CGRectZero;
1752
+ }
1753
+
1754
+ CGFloat x = [bounds[@"x"] doubleValue];
1755
+ CGFloat y = [bounds[@"y"] doubleValue];
1756
+ CGFloat width = [bounds[@"width"] doubleValue];
1757
+ CGFloat height = [bounds[@"height"] doubleValue];
1758
+
1759
+ CGPoint topLeft = [self viewPointForNormalizedPoint:CGPointMake(x, y) page:page];
1760
+ CGPoint topRight = [self viewPointForNormalizedPoint:CGPointMake(x + width, y) page:page];
1761
+ CGPoint bottomLeft = [self viewPointForNormalizedPoint:CGPointMake(x, y + height) page:page];
1762
+ CGPoint bottomRight = [self viewPointForNormalizedPoint:CGPointMake(x + width, y + height) page:page];
1763
+
1764
+ CGFloat minX = MIN(MIN(topLeft.x, topRight.x), MIN(bottomLeft.x, bottomRight.x));
1765
+ CGFloat maxX = MAX(MAX(topLeft.x, topRight.x), MAX(bottomLeft.x, bottomRight.x));
1766
+ CGFloat minY = MIN(MIN(topLeft.y, topRight.y), MIN(bottomLeft.y, bottomRight.y));
1767
+ CGFloat maxY = MAX(MAX(topLeft.y, topRight.y), MAX(bottomLeft.y, bottomRight.y));
1768
+
1769
+ return CGRectMake(minX, minY, MAX(maxX - minX, 1.0f), MAX(maxY - minY, 1.0f));
1770
+ }
1771
+
1772
+ - (CGRect)viewRectForAnnotation:(NSDictionary *)annotation page:(PDFPage *)page
1773
+ {
1774
+ if (!annotation || !page) {
1775
+ return CGRectZero;
1776
+ }
1777
+
1778
+ NSString *type = annotation[@"type"];
1779
+ if ([type isEqualToString:@"ink"]) {
1780
+ NSArray *points = annotation[@"points"];
1781
+ if (![points isKindOfClass:[NSArray class]] || points.count == 0) {
1782
+ return CGRectZero;
1783
+ }
1784
+
1785
+ CGFloat minX = CGFLOAT_MAX;
1786
+ CGFloat minY = CGFLOAT_MAX;
1787
+ CGFloat maxX = -CGFLOAT_MAX;
1788
+ CGFloat maxY = -CGFLOAT_MAX;
1789
+
1790
+ for (NSDictionary *point in points) {
1791
+ if (![point isKindOfClass:[NSDictionary class]]) {
1792
+ continue;
1793
+ }
1794
+
1795
+ CGPoint viewPoint = [self viewPointForNormalizedPoint:CGPointMake([point[@"x"] doubleValue], [point[@"y"] doubleValue]) page:page];
1796
+ minX = MIN(minX, viewPoint.x);
1797
+ minY = MIN(minY, viewPoint.y);
1798
+ maxX = MAX(maxX, viewPoint.x);
1799
+ maxY = MAX(maxY, viewPoint.y);
1800
+ }
1801
+
1802
+ if (minX == CGFLOAT_MAX || minY == CGFLOAT_MAX || maxX == -CGFLOAT_MAX || maxY == -CGFLOAT_MAX) {
1803
+ return CGRectZero;
1804
+ }
1805
+
1806
+ return CGRectMake(minX, minY, MAX(maxX - minX, 1.0f), MAX(maxY - minY, 1.0f));
1807
+ }
1808
+
1809
+ NSDictionary *bounds = annotation[@"bounds"];
1810
+ return [self viewRectForNormalizedBounds:bounds page:page];
1811
+ }
1812
+
1813
+ - (CGRect)normalizedBoundsForAnnotation:(NSDictionary *)annotation
1814
+ {
1815
+ if (!annotation) {
1816
+ return CGRectZero;
1817
+ }
1818
+
1819
+ NSString *type = annotation[@"type"];
1820
+ if ([type isEqualToString:@"ink"]) {
1821
+ NSArray *points = annotation[@"points"];
1822
+ if (![points isKindOfClass:[NSArray class]] || points.count == 0) {
1823
+ return CGRectZero;
1824
+ }
1825
+
1826
+ CGFloat minX = CGFLOAT_MAX;
1827
+ CGFloat minY = CGFLOAT_MAX;
1828
+ CGFloat maxX = -CGFLOAT_MAX;
1829
+ CGFloat maxY = -CGFLOAT_MAX;
1830
+
1831
+ for (NSDictionary *point in points) {
1832
+ if (![point isKindOfClass:[NSDictionary class]]) {
1833
+ continue;
1834
+ }
1835
+
1836
+ CGFloat x = [point[@"x"] doubleValue];
1837
+ CGFloat y = [point[@"y"] doubleValue];
1838
+ minX = MIN(minX, x);
1839
+ minY = MIN(minY, y);
1840
+ maxX = MAX(maxX, x);
1841
+ maxY = MAX(maxY, y);
1842
+ }
1843
+
1844
+ if (minX == CGFLOAT_MAX || minY == CGFLOAT_MAX || maxX == -CGFLOAT_MAX || maxY == -CGFLOAT_MAX) {
1845
+ return CGRectZero;
1846
+ }
1847
+
1848
+ return CGRectMake(minX, minY, MAX(maxX - minX, 0.001f), MAX(maxY - minY, 0.001f));
1849
+ }
1850
+
1851
+ NSDictionary *bounds = annotation[@"bounds"];
1852
+ if (![bounds isKindOfClass:[NSDictionary class]]) {
1853
+ return CGRectZero;
1854
+ }
1855
+
1856
+ CGFloat x = [bounds[@"x"] doubleValue];
1857
+ CGFloat y = [bounds[@"y"] doubleValue];
1858
+ CGFloat width = [bounds[@"width"] doubleValue];
1859
+ CGFloat height = [bounds[@"height"] doubleValue];
1860
+ return CGRectMake(x, y, MAX(width, 0.001f), MAX(height, 0.001f));
1861
+ }
1862
+
1863
+ - (NSArray *)copyPointsForAnnotation:(NSDictionary *)annotation
1864
+ {
1865
+ if (![annotation[@"type"] isEqualToString:@"ink"]) {
1866
+ return nil;
1867
+ }
1868
+
1869
+ NSArray *points = annotation[@"points"];
1870
+ if (![points isKindOfClass:[NSArray class]]) {
1871
+ return nil;
1872
+ }
1873
+
1874
+ return [points copy];
1875
+ }
1876
+
1877
+ - (NSDictionary *)selectedAnnotation
1878
+ {
1879
+ if (_selectedAnnotationId.length == 0) {
1880
+ return nil;
1881
+ }
1882
+
1883
+ for (NSDictionary *annotation in [_draftAnnotations reverseObjectEnumerator]) {
1884
+ if ([_selectedAnnotationId isEqualToString:annotation[@"id"]]) {
1885
+ return annotation;
1886
+ }
1887
+ }
1888
+
1889
+ return nil;
1890
+ }
1891
+
1892
+ - (CGRect)resizeHandleRectForRect:(CGRect)rect
1893
+ {
1894
+ CGFloat size = MAX(16.0f, MIN(rect.size.width, rect.size.height) * 0.18f);
1895
+ return CGRectMake(CGRectGetMaxX(rect) - size, CGRectGetMaxY(rect) - size, size, size);
1896
+ }
1897
+
1898
+ - (NSDictionary *)annotationSelectionHitAtPoint:(CGPoint)point includeHandles:(BOOL)includeHandles
1899
+ {
1900
+ if (!self.pdfDocument || _draftAnnotations.count == 0) {
1901
+ return nil;
1902
+ }
1903
+
1904
+ PDFPage *page = [self.pdfView pageForPoint:point nearest:NO];
1905
+ if (!page) {
1906
+ return nil;
1907
+ }
1908
+
1909
+ NSInteger pageIndex = [self.pdfDocument indexForPage:page];
1910
+ for (NSDictionary *annotation in [_draftAnnotations reverseObjectEnumerator]) {
1911
+ NSNumber *annotationPageNumber = annotation[@"page"];
1912
+ if (!annotationPageNumber || annotationPageNumber.integerValue - 1 != pageIndex) {
1913
+ continue;
1914
+ }
1915
+
1916
+ CGRect rect = [self viewRectForAnnotation:annotation page:page];
1917
+ if (CGRectIsEmpty(rect)) {
1918
+ continue;
1919
+ }
1920
+
1921
+ CGRect hitRect = CGRectInset(rect, -12.0f, -12.0f);
1922
+ if (!CGRectContainsPoint(hitRect, point)) {
1923
+ continue;
1924
+ }
1925
+
1926
+ NSString *hitPart = @"body";
1927
+ if (includeHandles && [_selectedAnnotationId isEqualToString:annotation[@"id"]] && [self annotationSupportsResize:annotation] && CGRectContainsPoint([self resizeHandleRectForRect:rect], point)) {
1928
+ hitPart = @"resize";
1929
+ }
1930
+
1931
+ return @{@"annotation": annotation,
1932
+ @"pageIndex": @(pageIndex),
1933
+ @"hitPart": hitPart,
1934
+ @"rect": [NSValue valueWithCGRect:rect]};
1935
+ }
1936
+
1937
+ return nil;
1938
+ }
1939
+
1940
+ - (void)selectAnnotation:(NSDictionary *)annotation
1941
+ {
1942
+ _selectedAnnotationId = [annotation[@"id"] copy];
1943
+ [self refreshDisplay];
1944
+ }
1945
+
1946
+ - (void)clearSelection
1947
+ {
1948
+ _selectedAnnotationId = nil;
1949
+ [self refreshDisplay];
1950
+ }
1951
+
1952
+ - (void)deleteAnnotation:(NSDictionary *)annotation
1953
+ {
1954
+ if (!annotation) {
1955
+ return;
1956
+ }
1957
+
1958
+ [self commitTextEditingIfNeeded];
1959
+
1960
+ NSString *annotationId = annotation[@"id"];
1961
+ if (annotationId.length == 0) {
1962
+ return;
1963
+ }
1964
+
1965
+ NSUInteger removeIndex = NSNotFound;
1966
+ for (NSUInteger index = 0; index < _draftAnnotations.count; index++) {
1967
+ NSDictionary *candidate = _draftAnnotations[index];
1968
+ if ([annotationId isEqualToString:candidate[@"id"]]) {
1969
+ removeIndex = index;
1970
+ break;
1971
+ }
1972
+ }
1973
+
1974
+ if (removeIndex != NSNotFound) {
1975
+ [_draftAnnotations removeObjectAtIndex:removeIndex];
1976
+ }
1977
+
1978
+ if ([_selectedAnnotationId isEqualToString:annotationId]) {
1979
+ _selectedAnnotationId = nil;
1980
+ }
1981
+
1982
+ [self endSelectionInteraction];
1983
+
1984
+ [self refreshDisplay];
1985
+ }
1986
+
1987
+ - (void)deleteSelectedAnnotation
1988
+ {
1989
+ NSDictionary *annotation = [self selectedAnnotation];
1990
+ if (annotation) {
1991
+ [self deleteAnnotation:annotation];
1992
+ }
1993
+ }
1994
+
1995
+ - (void)deleteAllAnnotations
1996
+ {
1997
+ [self commitTextEditingIfNeeded];
1998
+ [_draftAnnotations removeAllObjects];
1999
+ _selectedAnnotationId = nil;
2000
+ [self endSelectionInteraction];
2001
+ [self refreshDisplay];
2002
+ }
2003
+
2004
+ - (void)beginSelectionInteractionAtPoint:(CGPoint)point hit:(NSDictionary *)hit
2005
+ {
2006
+ NSDictionary *annotation = hit[@"annotation"];
2007
+ if (![annotation isKindOfClass:[NSDictionary class]]) {
2008
+ return;
2009
+ }
2010
+
2011
+ _activeSelectionAnnotation = (NSMutableDictionary *)annotation;
2012
+ _activeSelectionMode = [hit[@"hitPart"] isEqualToString:@"resize"] ? @"resize" : @"move";
2013
+ _activeSelectionHandle = hit[@"hitPart"] ?: @"body";
2014
+ _selectionStartPoint = point;
2015
+ _selectionPageIndex = [hit[@"pageIndex"] integerValue];
2016
+ _selectionStartBounds = [self normalizedBoundsForAnnotation:annotation];
2017
+ _selectionStartPoints = [self copyPointsForAnnotation:annotation];
2018
+ }
2019
+
2020
+ - (void)updateSelectionInteractionAtPoint:(CGPoint)point
2021
+ {
2022
+ if (!_activeSelectionAnnotation || _selectionPageIndex < 0 || !_pdfDocument || _selectionPageIndex >= _pdfDocument.pageCount) {
2023
+ return;
2024
+ }
2025
+
2026
+ PDFPage *page = [_pdfDocument pageAtIndex:_selectionPageIndex];
2027
+ if (!page) {
2028
+ return;
2029
+ }
2030
+
2031
+ CGPoint startNormalized = [self normalizedPointForViewPoint:_selectionStartPoint page:page];
2032
+ CGPoint currentNormalized = [self normalizedPointForViewPoint:point page:page];
2033
+ CGFloat deltaX = currentNormalized.x - startNormalized.x;
2034
+ CGFloat deltaY = currentNormalized.y - startNormalized.y;
2035
+
2036
+ CGRect newBounds = _selectionStartBounds;
2037
+ if ([_activeSelectionMode isEqualToString:@"resize"]) {
2038
+ newBounds.size.width = MAX(0.01f, _selectionStartBounds.size.width + deltaX);
2039
+ newBounds.size.height = MAX(0.01f, _selectionStartBounds.size.height + deltaY);
2040
+ } else {
2041
+ newBounds.origin.x = _selectionStartBounds.origin.x + deltaX;
2042
+ newBounds.origin.y = _selectionStartBounds.origin.y + deltaY;
2043
+ }
2044
+
2045
+ [self applySelectionBounds:newBounds toAnnotation:_activeSelectionAnnotation startBounds:_selectionStartBounds startPoints:_selectionStartPoints];
2046
+ [self refreshDisplay];
2047
+ }
2048
+
2049
+ - (void)endSelectionInteraction
2050
+ {
2051
+ _activeSelectionAnnotation = nil;
2052
+ _activeSelectionMode = nil;
2053
+ _activeSelectionHandle = nil;
2054
+ _selectionStartPoints = nil;
2055
+ _selectionPageIndex = -1;
2056
+ }
2057
+
2058
+ - (void)applySelectionBounds:(CGRect)newBounds toAnnotation:(NSMutableDictionary *)annotation startBounds:(CGRect)startBounds startPoints:(NSArray *)startPoints
2059
+ {
2060
+ if (!annotation) {
2061
+ return;
2062
+ }
2063
+
2064
+ NSString *type = annotation[@"type"];
2065
+ if ([type isEqualToString:@"ink"]) {
2066
+ if (![startPoints isKindOfClass:[NSArray class]]) {
2067
+ return;
2068
+ }
2069
+
2070
+ CGFloat startWidth = MAX(startBounds.size.width, 0.001f);
2071
+ CGFloat startHeight = MAX(startBounds.size.height, 0.001f);
2072
+ CGFloat newWidth = MAX(newBounds.size.width, 0.001f);
2073
+ CGFloat newHeight = MAX(newBounds.size.height, 0.001f);
2074
+
2075
+ NSMutableArray *transformedPoints = [NSMutableArray arrayWithCapacity:startPoints.count];
2076
+ for (NSDictionary *point in startPoints) {
2077
+ if (![point isKindOfClass:[NSDictionary class]]) {
2078
+ continue;
2079
+ }
2080
+
2081
+ CGFloat pointX = [point[@"x"] doubleValue];
2082
+ CGFloat pointY = [point[@"y"] doubleValue];
2083
+ CGFloat xRatio = (pointX - startBounds.origin.x) / startWidth;
2084
+ CGFloat yRatio = (pointY - startBounds.origin.y) / startHeight;
2085
+ CGFloat x = MIN(1.0f, MAX(0.0f, newBounds.origin.x + (xRatio * newWidth)));
2086
+ CGFloat y = MIN(1.0f, MAX(0.0f, newBounds.origin.y + (yRatio * newHeight)));
2087
+ [transformedPoints addObject:@{@"x": @(x), @"y": @(y)}];
2088
+ }
2089
+
2090
+ annotation[@"points"] = transformedPoints;
2091
+ return;
2092
+ }
2093
+
2094
+ CGFloat x = MIN(1.0f, MAX(0.0f, newBounds.origin.x));
2095
+ CGFloat y = MIN(1.0f, MAX(0.0f, newBounds.origin.y));
2096
+ CGFloat width = MIN(1.0f, MAX(0.01f, newBounds.size.width));
2097
+ CGFloat height = MIN(1.0f, MAX(0.01f, newBounds.size.height));
2098
+ annotation[@"bounds"] = @{@"x": @(x),
2099
+ @"y": @(y),
2100
+ @"width": @(width),
2101
+ @"height": @(height)};
2102
+ }
2103
+
2104
+ - (void)drawSelectionDecoration
2105
+ {
2106
+ if (!_annotationMode || !_annotationEditable || ![_annotationTool isEqualToString:@"select"]) {
2107
+ return;
2108
+ }
2109
+
2110
+ NSDictionary *annotation = [self selectedAnnotation];
2111
+ if (!annotation) {
2112
+ return;
2113
+ }
2114
+
2115
+ NSNumber *pageIndexValue = annotation[@"page"];
2116
+ NSInteger pageIndex = pageIndexValue.integerValue - 1;
2117
+ if (pageIndex < 0 || pageIndex >= _pdfDocument.pageCount) {
2118
+ return;
2119
+ }
2120
+
2121
+ PDFPage *page = [_pdfDocument pageAtIndex:pageIndex];
2122
+ CGRect rect = [self viewRectForAnnotation:annotation page:page];
2123
+ if (CGRectIsEmpty(rect)) {
2124
+ return;
2125
+ }
2126
+
2127
+ UIColor *outlineColor = [UIColor colorWithRed:0.13 green:0.27 blue:0.67 alpha:0.9];
2128
+ UIBezierPath *outlinePath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:4.0f];
2129
+ outlinePath.lineWidth = 2.0f;
2130
+ [outlineColor setStroke];
2131
+ [outlinePath stroke];
2132
+
2133
+ if ([self annotationSupportsResize:annotation]) {
2134
+ CGRect resizeHandle = [self resizeHandleRectForRect:rect];
2135
+ [[UIColor colorWithRed:0.13 green:0.27 blue:0.67 alpha:0.95] setFill];
2136
+ UIBezierPath *resizePath = [UIBezierPath bezierPathWithRoundedRect:resizeHandle cornerRadius:2.0f];
2137
+ [resizePath fill];
2138
+ }
2139
+ }
2140
+
2141
+ - (NSDictionary *)normalizedBoundsForViewRect:(CGRect)viewRect page:(PDFPage *)page
2142
+ {
2143
+ if (!self.pdfView || !page) {
2144
+ return @{@"x": @0, @"y": @0, @"width": @0, @"height": @0};
2145
+ }
2146
+
2147
+ CGPoint topLeft = [self normalizedPointForViewPoint:viewRect.origin page:page];
2148
+ CGPoint bottomRight = [self normalizedPointForViewPoint:CGPointMake(CGRectGetMaxX(viewRect), CGRectGetMaxY(viewRect)) page:page];
2149
+
2150
+ CGFloat minX = MIN(topLeft.x, bottomRight.x);
2151
+ CGFloat maxX = MAX(topLeft.x, bottomRight.x);
2152
+ CGFloat minY = MIN(topLeft.y, bottomRight.y);
2153
+ CGFloat maxY = MAX(topLeft.y, bottomRight.y);
2154
+
2155
+ return @{@"x": @(MAX(0, minX)),
2156
+ @"y": @(MAX(0, minY)),
2157
+ @"width": @(MAX(0, maxX - minX)),
2158
+ @"height": @(MAX(0, maxY - minY))};
2159
+ }
2160
+
2161
+ - (NSString *)nextLocalAnnotationId
2162
+ {
2163
+ return [NSString stringWithFormat:@"local-%@", RNPDFGenerateAnnotationId()];
2164
+ }
2165
+
2166
+ - (UIColor *)colorForAnnotationType:(NSString *)type style:(NSDictionary *)style
2167
+ {
2168
+ NSString *normalizedType = [self normalizedAnnotationType:type];
2169
+ NSString *colorString = [style isKindOfClass:[NSDictionary class]] ? style[@"color"] : nil;
2170
+ if (colorString) {
2171
+ return RNPDFColorFromHexString(colorString, UIColor.blackColor);
2172
+ }
2173
+
2174
+ if ([normalizedType isEqualToString:@"highlight"]) {
2175
+ return [UIColor colorWithRed:1.0 green:0.93 blue:0.2 alpha:0.35];
2176
+ }
2177
+ if ([normalizedType isEqualToString:@"text"]) {
2178
+ return [UIColor colorWithRed:0.13 green:0.27 blue:0.67 alpha:1.0];
2179
+ }
2180
+
2181
+ return UIColor.blackColor;
2182
+ }
2183
+
2184
+ - (CGFloat)lineWidthForAnnotation:(NSDictionary *)annotation
2185
+ {
2186
+ NSDictionary *style = [annotation[@"style"] isKindOfClass:[NSDictionary class]] ? annotation[@"style"] : nil;
2187
+ NSNumber *thickness = style[@"thickness"];
2188
+ if ([thickness isKindOfClass:[NSNumber class]]) {
2189
+ return MAX(1.0f, thickness.floatValue);
2190
+ }
2191
+
2192
+ return 2.0f;
2193
+ }
2194
+
2195
+ - (UIFont *)fontForAnnotation:(NSDictionary *)annotation
2196
+ {
2197
+ NSDictionary *style = [annotation[@"style"] isKindOfClass:[NSDictionary class]] ? annotation[@"style"] : nil;
2198
+ NSNumber *fontSize = style[@"fontSize"];
2199
+ CGFloat size = [fontSize isKindOfClass:[NSNumber class]] ? MAX(10.0f, fontSize.floatValue) : 15.0f;
2200
+ return [UIFont systemFontOfSize:size];
2201
+ }
2202
+
2203
+ - (NSTextAlignment)alignmentForAnnotation:(NSDictionary *)annotation
2204
+ {
2205
+ NSDictionary *style = [annotation[@"style"] isKindOfClass:[NSDictionary class]] ? annotation[@"style"] : nil;
2206
+ NSString *alignment = style[@"textAlign"];
2207
+ if ([alignment isEqualToString:@"center"]) {
2208
+ return NSTextAlignmentCenter;
2209
+ }
2210
+ if ([alignment isEqualToString:@"right"]) {
2211
+ return NSTextAlignmentRight;
2212
+ }
2213
+
2214
+ return NSTextAlignmentLeft;
2215
+ }
2216
+
2217
+ - (void)drawRect:(CGRect)rect
2218
+ {
2219
+ [super drawRect:rect];
2220
+
2221
+ if (!_pdfDocument || _draftAnnotations.count == 0) {
2222
+ return;
2223
+ }
2224
+
2225
+ CGContextRef context = UIGraphicsGetCurrentContext();
2226
+ if (!context) {
2227
+ return;
2228
+ }
2229
+
2230
+ for (NSDictionary *annotation in _draftAnnotations) {
2231
+ NSNumber *pageIndexValue = annotation[@"page"];
2232
+ NSInteger pageIndex = pageIndexValue.integerValue - 1;
2233
+ if (pageIndex < 0 || pageIndex >= _pdfDocument.pageCount) {
2234
+ continue;
2235
+ }
2236
+
2237
+ PDFPage *page = [_pdfDocument pageAtIndex:pageIndex];
2238
+ NSString *type = [self normalizedAnnotationType:annotation[@"type"]];
2239
+ if ([type isEqualToString:@"ink"]) {
2240
+ NSArray *points = annotation[@"points"];
2241
+ if (points.count == 0) {
2242
+ continue;
2243
+ }
2244
+
2245
+ if (points.count == 1) {
2246
+ NSDictionary *point = points[0];
2247
+ CGPoint normalizedPoint = CGPointMake([point[@"x"] doubleValue], [point[@"y"] doubleValue]);
2248
+ CGPoint viewPoint = [self viewPointForNormalizedPoint:normalizedPoint page:page];
2249
+ CGFloat radius = [self lineWidthForAnnotation:annotation] / 2.0;
2250
+ CGRect dotRect = CGRectMake(viewPoint.x - radius, viewPoint.y - radius, radius * 2, radius * 2);
2251
+ [[self colorForAnnotationType:type style:annotation[@"style"]] setFill];
2252
+ [[UIBezierPath bezierPathWithOvalInRect:dotRect] fill];
2253
+ continue;
2254
+ }
2255
+
2256
+ UIBezierPath *path = [UIBezierPath bezierPath];
2257
+ BOOL firstPoint = YES;
2258
+ for (NSDictionary *point in points) {
2259
+ CGPoint normalizedPoint = CGPointMake([point[@"x"] doubleValue], [point[@"y"] doubleValue]);
2260
+ CGPoint viewPoint = [self viewPointForNormalizedPoint:normalizedPoint page:page];
2261
+ if (firstPoint) {
2262
+ [path moveToPoint:viewPoint];
2263
+ firstPoint = NO;
2264
+ } else {
2265
+ [path addLineToPoint:viewPoint];
2266
+ }
2267
+ }
2268
+
2269
+ [[self colorForAnnotationType:type style:annotation[@"style"]] setStroke];
2270
+ path.lineWidth = [self lineWidthForAnnotation:annotation];
2271
+ path.lineJoinStyle = kCGLineJoinRound;
2272
+ path.lineCapStyle = kCGLineCapRound;
2273
+ [path stroke];
2274
+ } else if ([type isEqualToString:@"text"]) {
2275
+ NSDictionary *bounds = annotation[@"bounds"];
2276
+ CGRect viewRect = [self viewRectForNormalizedBounds:bounds page:page];
2277
+ if (CGRectIsEmpty(viewRect)) {
2278
+ continue;
2279
+ }
2280
+
2281
+ UIColor *borderColor = [self colorForAnnotationType:type style:annotation[@"style"]];
2282
+ [[UIColor colorWithWhite:1.0 alpha:0.78] setFill];
2283
+ UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:viewRect cornerRadius:4.0f];
2284
+ [path fill];
2285
+ [borderColor setStroke];
2286
+ path.lineWidth = 1.0f;
2287
+ [path stroke];
2288
+
2289
+ NSString *text = annotation[@"text"];
2290
+ if (![text isKindOfClass:[NSString class]]) {
2291
+ text = @"";
2292
+ }
2293
+
2294
+ NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
2295
+ paragraphStyle.alignment = [self alignmentForAnnotation:annotation];
2296
+ NSDictionary *attributes = @{
2297
+ NSFontAttributeName: [self fontForAnnotation:annotation],
2298
+ NSForegroundColorAttributeName: borderColor,
2299
+ NSParagraphStyleAttributeName: paragraphStyle,
2300
+ };
2301
+ [text drawInRect:CGRectInset(viewRect, 6.0f, 4.0f) withAttributes:attributes];
2302
+ } else if ([type isEqualToString:@"highlight"]) {
2303
+ NSDictionary *bounds = annotation[@"bounds"];
2304
+ CGRect viewRect = [self viewRectForNormalizedBounds:bounds page:page];
2305
+ if (CGRectIsEmpty(viewRect)) {
2306
+ continue;
2307
+ }
2308
+
2309
+ UIColor *fillColor = [self colorForAnnotationType:type style:annotation[@"style"]];
2310
+ CGContextSetFillColorWithColor(context, fillColor.CGColor);
2311
+ CGContextFillRect(context, viewRect);
2312
+ }
2313
+ }
2314
+
2315
+ [self drawSelectionDecoration];
2316
+ }
2317
+
2318
+ - (void)beginInkAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page
2319
+ {
2320
+ if (!self.annotationEditable || !page) {
2321
+ return;
2322
+ }
2323
+
2324
+ NSMutableDictionary *annotation = [@{
2325
+ @"id": [self nextLocalAnnotationId],
2326
+ @"page": @([_pdfDocument indexForPage:page] + 1),
2327
+ @"type": @"ink",
2328
+ @"points": [NSMutableArray array],
2329
+ @"style": @{@"color": _annotationInkColor ?: @"#111111", @"thickness": @(_annotationInkThickness > 0 ? _annotationInkThickness : 2.0f)}
2330
+ } mutableCopy];
2331
+
2332
+ [_draftAnnotations addObject:annotation];
2333
+ _activeInkAnnotation = annotation;
2334
+ [self appendInkPointAtViewPoint:viewPoint page:page];
2335
+ }
2336
+
2337
+ - (void)appendInkPointAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page
2338
+ {
2339
+ if (!_activeInkAnnotation || !page) {
2340
+ return;
2341
+ }
2342
+
2343
+ CGPoint normalizedPoint = [self normalizedPointForViewPoint:viewPoint page:page];
2344
+ NSMutableArray *points = _activeInkAnnotation[@"points"];
2345
+ if (![points isKindOfClass:[NSMutableArray class]]) {
2346
+ points = [NSMutableArray array];
2347
+ _activeInkAnnotation[@"points"] = points;
2348
+ }
2349
+
2350
+ [points addObject:@{@"x": @(normalizedPoint.x), @"y": @(normalizedPoint.y)}];
2351
+ [self refreshDisplay];
2352
+ }
2353
+
2354
+ - (void)endInk
2355
+ {
2356
+ _activeInkAnnotation = nil;
2357
+ [self refreshDisplay];
2358
+ }
2359
+
2360
+ - (void)beginMarkupAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page type:(NSString *)type
2361
+ {
2362
+ if (!self.annotationEditable || !page) {
2363
+ return;
2364
+ }
2365
+
2366
+ CGPoint normalizedPoint = [self normalizedPointForViewPoint:viewPoint page:page];
2367
+ _activeMarkupStartNormalized = normalizedPoint;
2368
+
2369
+ NSMutableDictionary *annotation = [@{
2370
+ @"id": [self nextLocalAnnotationId],
2371
+ @"page": @([_pdfDocument indexForPage:page] + 1),
2372
+ @"type": @"highlight",
2373
+ @"bounds": @{@"x": @(normalizedPoint.x), @"y": @(normalizedPoint.y), @"width": @0, @"height": @0},
2374
+ @"style": @{}
2375
+ } mutableCopy];
2376
+
2377
+ [_draftAnnotations addObject:annotation];
2378
+ _activeMarkupAnnotation = annotation;
2379
+ [self refreshDisplay];
2380
+ }
2381
+
2382
+ - (void)updateMarkupAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page
2383
+ {
2384
+ if (!_activeMarkupAnnotation || !page) {
2385
+ return;
2386
+ }
2387
+
2388
+ CGPoint currentPoint = [self normalizedPointForViewPoint:viewPoint page:page];
2389
+ CGFloat minX = MIN(_activeMarkupStartNormalized.x, currentPoint.x);
2390
+ CGFloat minY = MIN(_activeMarkupStartNormalized.y, currentPoint.y);
2391
+ CGFloat maxX = MAX(_activeMarkupStartNormalized.x, currentPoint.x);
2392
+ CGFloat maxY = MAX(_activeMarkupStartNormalized.y, currentPoint.y);
2393
+
2394
+ _activeMarkupAnnotation[@"bounds"] = @{@"x": @(minX), @"y": @(minY), @"width": @(MAX(0, maxX - minX)), @"height": @(MAX(0, maxY - minY))};
2395
+ [self refreshDisplay];
2396
+ }
2397
+
2398
+ - (void)endMarkup
2399
+ {
2400
+ _activeMarkupAnnotation = nil;
2401
+ [self refreshDisplay];
2402
+ }
2403
+
2404
+ - (void)createTextAnnotationAtViewPoint:(CGPoint)viewPoint page:(PDFPage *)page
2405
+ {
2406
+ if (!self.annotationEditable || !page) {
2407
+ return;
2408
+ }
2409
+
2410
+ [self commitTextEditingIfNeeded];
2411
+
2412
+ CGPoint normalizedPoint = [self normalizedPointForViewPoint:viewPoint page:page];
2413
+ CGFloat width = 0.25f;
2414
+ CGFloat height = 0.12f;
2415
+ CGFloat maxX = MAX(0.0f, 1.0f - width);
2416
+ CGFloat maxY = MAX(0.0f, 1.0f - height);
2417
+ CGFloat x = MIN(MAX(normalizedPoint.x, 0.0f), maxX);
2418
+ CGFloat y = MIN(MAX(normalizedPoint.y, 0.0f), maxY);
2419
+ NSDictionary *bounds = @{@"x": @(x), @"y": @(y), @"width": @(width), @"height": @(height)};
2420
+
2421
+ NSMutableDictionary *annotation = [@{
2422
+ @"id": [self nextLocalAnnotationId],
2423
+ @"page": @([_pdfDocument indexForPage:page] + 1),
2424
+ @"type": @"text",
2425
+ @"bounds": bounds,
2426
+ @"text": @"",
2427
+ @"style": @{@"color": @"#2244aa", @"fontSize": @(15.0f), @"textAlign": @"left"}
2428
+ } mutableCopy];
2429
+
2430
+ [_draftAnnotations addObject:annotation];
2431
+ _activeTextAnnotation = annotation;
2432
+
2433
+ UITextView *textView = [[UITextView alloc] initWithFrame:[self viewRectForNormalizedBounds:bounds page:page]];
2434
+ textView.delegate = self;
2435
+ textView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9f];
2436
+ textView.layer.borderColor = [UIColor colorWithRed:0.2 green:0.4 blue:1.0 alpha:0.9].CGColor;
2437
+ textView.layer.borderWidth = 1.0f;
2438
+ textView.layer.cornerRadius = 4.0f;
2439
+ textView.clipsToBounds = YES;
2440
+ textView.textColor = [self colorForAnnotationType:@"text" style:annotation[@"style"]];
2441
+ textView.font = [self fontForAnnotation:annotation];
2442
+ textView.textAlignment = [self alignmentForAnnotation:annotation];
2443
+ textView.scrollEnabled = YES;
2444
+ textView.returnKeyType = UIReturnKeyDefault;
2445
+
2446
+ if (_activeTextView) {
2447
+ [_activeTextView removeFromSuperview];
2448
+ }
2449
+
2450
+ _activeTextView = textView;
2451
+ [self addSubview:_activeTextView];
2452
+ [_activeTextView becomeFirstResponder];
2453
+ [self refreshDisplay];
2454
+ }
2455
+
2456
+ - (void)updateActiveTextEditorFrame
2457
+ {
2458
+ if (!_activeTextView || !_activeTextAnnotation || !self.pdfDocument || !self.pdfView) {
2459
+ return;
2460
+ }
2461
+
2462
+ NSNumber *pageNumber = _activeTextAnnotation[@"page"];
2463
+ NSInteger pageIndex = pageNumber.integerValue - 1;
2464
+ if (pageIndex < 0 || pageIndex >= self.pdfDocument.pageCount) {
2465
+ return;
2466
+ }
2467
+
2468
+ PDFPage *page = [self.pdfDocument pageAtIndex:pageIndex];
2469
+ _activeTextView.frame = [self viewRectForNormalizedBounds:_activeTextAnnotation[@"bounds"] page:page];
2470
+ }
2471
+
2472
+ - (void)commitTextEditingIfNeeded
2473
+ {
2474
+ if (!_activeTextView || !_activeTextAnnotation) {
2475
+ return;
2476
+ }
2477
+
2478
+ _activeTextAnnotation[@"text"] = _activeTextView.text ?: @"";
2479
+ [_activeTextView resignFirstResponder];
2480
+ [_activeTextView removeFromSuperview];
2481
+ _activeTextView = nil;
2482
+ _activeTextAnnotation = nil;
2483
+ [self refreshDisplay];
2484
+ }
2485
+
2486
+ - (void)textViewDidChange:(UITextView *)textView
2487
+ {
2488
+ if (textView == _activeTextView && _activeTextAnnotation) {
2489
+ _activeTextAnnotation[@"text"] = textView.text ?: @"";
2490
+ [self setNeedsDisplay];
1132
2491
  }
1133
2492
  }
1134
2493
 
1135
- - (void)scrollViewDidScroll:(UIScrollView *)scrollView
2494
+ - (void)textViewDidEndEditing:(UITextView *)textView
1136
2495
  {
1137
- if ([_originalScrollDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
1138
- [_originalScrollDelegate scrollViewDidScroll:scrollView];
2496
+ if (textView == _activeTextView) {
2497
+ [self commitTextEditingIfNeeded];
1139
2498
  }
1140
2499
  }
1141
2500