@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.
- package/README.md +57 -0
- package/android/.gradle/8.5/executionHistory/executionHistory.bin +0 -0
- package/android/.gradle/8.5/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.5/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/8.5/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
- package/android/.gradle/file-system.probe +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfManager.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$1.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$2.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$3.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$AnnotationOverlayView$1.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationHit.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationSelectionHit.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$AnnotationOverlayView.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$PageRenderInfo.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView.dex +0 -0
- package/android/build/.transforms/2892d280277a194daf04e3af971b529e/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
- package/android/build/.transforms/e40f3b884607647e8beb6dabe8165962/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.java +30 -0
- package/android/build/generated/source/codegen/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.java +10 -0
- package/android/build/generated/source/codegen/jni/react/renderer/components/rnpdf/Props.cpp +35 -0
- package/android/build/generated/source/codegen/jni/react/renderer/components/rnpdf/Props.h +7 -0
- package/android/build/generated/source/codegen/schema.json +1 -1
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +1 -1
- package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +1 -1
- package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +1 -1
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfManager.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$1.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$2.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$3.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$AnnotationOverlayView$1.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationHit.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationSelectionHit.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$AnnotationOverlayView.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView$PageRenderInfo.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/wonday/pdf/PdfView.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfManager.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$1.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$2.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$3.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$AnnotationOverlayView$1.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationHit.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$AnnotationOverlayView$AnnotationSelectionHit.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$AnnotationOverlayView.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView$PageRenderInfo.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/org/wonday/pdf/PdfView.class +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfManager.class.uniqueId7 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/{PdfView$1.class.uniqueId0 → PdfView$1.class.uniqueId3} +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$2.class.uniqueId5 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$AnnotationOverlayView$1.class.uniqueId6 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$AnnotationOverlayView$AnnotationHit.class.uniqueId1 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$AnnotationOverlayView$AnnotationSelectionHit.class.uniqueId9 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$AnnotationOverlayView.class.uniqueId8 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$PageRenderInfo.class.uniqueId0 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView.class.uniqueId2 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/src/main/java/org/wonday/pdf/PdfManager.java +56 -0
- package/android/src/main/java/org/wonday/pdf/PdfView.java +1249 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.java +30 -0
- package/android/src/paper/java/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.java +10 -0
- package/annotationDocumentUtils.js +74 -0
- package/fabric/RNPDFPdfNativeComponent.js +18 -3
- package/index.d.ts +103 -1
- package/index.js +112 -0
- package/index.js.flow +73 -0
- package/ios/RNPDFPdf/RNPDFPdfView.h +11 -0
- package/ios/RNPDFPdf/RNPDFPdfView.mm +1414 -55
- package/ios/RNPDFPdf/RNPDFPdfViewManager.mm +37 -0
- package/package.json +6 -5
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/results.bin +0 -1
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/BuildConfig.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfManager.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$1.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$2.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$3.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/RNPDFPackage.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/events/TopChangeEvent.dex +0 -0
- package/android/build/.transforms/3337b950908a4f6985ba400cb8386b0b/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
- package/android/build/.transforms/787b9137f14dcc9eebbb21ac62a7be22/results.bin +0 -1
- package/android/build/.transforms/787b9137f14dcc9eebbb21ac62a7be22/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/results.bin +0 -1
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerDelegate.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/facebook/react/viewmanagers/RNPDFPdfViewManagerInterface.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/BuildConfig.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfManager.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$1.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$2.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView$3.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/PdfView.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/RNPDFPackage.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/org/wonday/pdf/events/TopChangeEvent.dex +0 -0
- package/android/build/.transforms/79ca1220a602ed0c9c23366e0e92a6d1/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
- package/android/build/.transforms/c29f40a753b0ae1d85db8c8c5812fc85/results.bin +0 -1
- package/android/build/.transforms/c29f40a753b0ae1d85db8c8c5812fc85/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/AndroidManifest.xml +0 -7
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/output-metadata.json +0 -18
- package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
- package/android/build/intermediates/aar_metadata/debug/aar-metadata.properties +0 -5
- package/android/build/intermediates/annotation_processor_list/debug/annotationProcessors.json +0 -1
- package/android/build/intermediates/annotation_processor_list/debugAndroidTest/annotationProcessors.json +0 -1
- package/android/build/intermediates/annotation_processor_list/debugUnitTest/annotationProcessors.json +0 -1
- package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
- package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
- package/android/build/intermediates/local_only_symbol_list/debug/R-def.txt +0 -2
- package/android/build/intermediates/manifest_merge_blame_file/debug/manifest-merger-blame-debug-report.txt +0 -8
- package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-react-native-pdf.jar +0 -0
- package/android/build/intermediates/merged_manifest/debug/AndroidManifest.xml +0 -7
- package/android/build/intermediates/navigation_json/debug/navigation.json +0 -1
- package/android/build/intermediates/packaged_manifests/debug/output-metadata.json +0 -18
- package/android/build/outputs/aar/react-native-pdf-debug.aar +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfManager.class.uniqueId3 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfManager.class.uniqueId4 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$2.class.uniqueId6 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$3.class.uniqueId4 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView$3.class.uniqueId5 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/PdfView.class.uniqueId3 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNPDFPackage.class.uniqueId2 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNPDFPdfViewManagerDelegate.class.uniqueId7 +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNPDFPdfViewManagerInterface.class.uniqueId1 +0 -0
- /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
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
[
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
-
|
|
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)
|
|
1458
|
+
- (void)observeValueForKeyPath:(NSString *)keyPath
|
|
1459
|
+
ofObject:(id)object
|
|
1460
|
+
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
|
|
1461
|
+
context:(void *)context
|
|
1098
1462
|
{
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
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 (
|
|
1105
|
-
[
|
|
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
|
-
|
|
1514
|
+
static NSString *RNPDFGenerateAnnotationId(void)
|
|
1110
1515
|
{
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
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
|
-
|
|
1119
|
-
|
|
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)
|
|
1680
|
+
- (void)refreshDisplay
|
|
1124
1681
|
{
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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
|
-
|
|
1131
|
-
|
|
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)
|
|
2494
|
+
- (void)textViewDidEndEditing:(UITextView *)textView
|
|
1136
2495
|
{
|
|
1137
|
-
if (
|
|
1138
|
-
[
|
|
2496
|
+
if (textView == _activeTextView) {
|
|
2497
|
+
[self commitTextEditingIfNeeded];
|
|
1139
2498
|
}
|
|
1140
2499
|
}
|
|
1141
2500
|
|