@june24/expo-pdf-reader 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoPdfReader.types.js","sourceRoot":"","sources":["../src/ExpoPdfReader.types.ts"],"names":[],"mappings":"","sourcesContent":["import { ViewProps, StyleProp, ViewStyle } from 'react-native';\n\nexport type AnnotationTool = 'none' | 'pen' | 'highlighter' | 'text' | 'note' | 'eraser';\n\nexport type DisplayMode = 'single' | 'continuous' | 'twoUp' | 'twoUpContinuous';\n\nexport type Point = { x: number; y: number };\n\nexport type Annotation = {\n type: 'pen' | 'highlighter' | 'text';\n color: string;\n page?: number;\n points?: Point[][]; // For pen/highlighter\n width?: number; // Stroke width or font size for text? Let's use fontSize for text explicitly\n text?: string; // For text\n fontSize?: number; // For text\n x?: number; // For text\n y?: number; // For text\n};\n\nexport type AnnotationChangeEvent = {\n nativeEvent: {\n annotations: Annotation[];\n };\n};\n\nexport type PageChangeEvent = {\n nativeEvent: {\n page: number;\n total: number;\n };\n};\n\nexport type ScrollEvent = {\n nativeEvent: {\n x: number;\n y: number;\n contentWidth: number;\n contentHeight: number;\n layoutWidth: number;\n layoutHeight: number;\n };\n};\n\nexport type SearchResult = {\n page: number;\n x: number;\n y: number;\n width: number;\n height: number;\n textSnippet?: string;\n};\n\n// Public API: group drawing configuration into a single object\nexport type AnnotationOptions = {\n tool?: AnnotationTool;\n color?: string; // Hex color\n fontSize?: number;\n text?: string;\n strokeWidth?: number; // line thickness for pen/highlighter\n};\n\n// Props exposed to JS users of the component\nexport type ExpoPdfReaderViewProps = ViewProps & {\n url: string;\n style?: StyleProp<ViewStyle>;\n displayMode?: DisplayMode; // 'single' | 'continuous' | 'twoUp' | 'twoUpContinuous'\n initialPage?: number; // starting page index (0-based)\n minZoom?: number;\n maxZoom?: number;\n annotationOptions?: AnnotationOptions;\n initialAnnotations?: Annotation[];\n onAnnotationChange?: (event: AnnotationChangeEvent) => void;\n onPageChange?: (event: PageChangeEvent) => void;\n onScroll?: (event: ScrollEvent) => void;\n};\n\n// Internal props that the native view actually receives.\n// This keeps backwards-compat with the native module while giving\n// JS a cleaner grouped API via `annotationOptions`.\nexport type NativeExpoPdfReaderViewProps = ViewProps & {\n url: string;\n style?: StyleProp<ViewStyle>;\n displayMode?: DisplayMode;\n initialPage?: number;\n minZoom?: number;\n maxZoom?: number;\n annotationTool?: AnnotationTool;\n annotationColor?: string; // Hex color\n annotationFontSize?: number;\n annotationText?: string; // Default text to place\n
|
|
1
|
+
{"version":3,"file":"ExpoPdfReader.types.js","sourceRoot":"","sources":["../src/ExpoPdfReader.types.ts"],"names":[],"mappings":"","sourcesContent":["import { ViewProps, StyleProp, ViewStyle } from 'react-native';\n\nexport type AnnotationTool = 'none' | 'pen' | 'highlighter' | 'text' | 'note' | 'eraser';\n\nexport type DisplayMode = 'single' | 'continuous' | 'twoUp' | 'twoUpContinuous';\n\nexport type Point = { x: number; y: number };\n\nexport type Annotation = {\n type: 'pen' | 'highlighter' | 'text';\n color: string;\n page?: number;\n points?: Point[][]; // For pen/highlighter\n width?: number; // Stroke width or font size for text? Let's use fontSize for text explicitly\n text?: string; // For text\n fontSize?: number; // For text\n x?: number; // For text\n y?: number; // For text\n};\n\nexport type AnnotationChangeEvent = {\n nativeEvent: {\n annotations: Annotation[];\n };\n};\n\nexport type PageChangeEvent = {\n nativeEvent: {\n page: number;\n total: number;\n };\n};\n\nexport type ScrollEvent = {\n nativeEvent: {\n x: number;\n y: number;\n contentWidth: number;\n contentHeight: number;\n layoutWidth: number;\n layoutHeight: number;\n };\n};\n\nexport type SearchResult = {\n page: number;\n x: number;\n y: number;\n width: number;\n height: number;\n textSnippet?: string;\n};\n\n// Public API: group drawing configuration into a single object\nexport type AnnotationOptions = {\n tool?: AnnotationTool;\n color?: string; // Hex color\n fontSize?: number;\n text?: string;\n strokeWidth?: number; // line thickness for pen/highlighter\n};\n\n// Props exposed to JS users of the component\nexport type ExpoPdfReaderViewProps = ViewProps & {\n url: string;\n style?: StyleProp<ViewStyle>;\n displayMode?: DisplayMode; // 'single' | 'continuous' | 'twoUp' | 'twoUpContinuous'\n initialPage?: number; // starting page index (0-based)\n minZoom?: number;\n maxZoom?: number;\n annotationOptions?: AnnotationOptions;\n initialAnnotations?: Annotation[];\n onAnnotationChange?: (event: AnnotationChangeEvent) => void;\n onPageChange?: (event: PageChangeEvent) => void;\n onScroll?: (event: ScrollEvent) => void;\n};\n\n// Internal props that the native view actually receives.\n// This keeps backwards-compat with the native module while giving\n// JS a cleaner grouped API via `annotationOptions`.\nexport type NativeExpoPdfReaderViewProps = ViewProps & {\n url: string;\n style?: StyleProp<ViewStyle>;\n displayMode?: DisplayMode;\n initialPage?: number;\n minZoom?: number;\n maxZoom?: number;\n annotationTool?: AnnotationTool;\n annotationColor?: string; // Hex color\n annotationFontSize?: number;\n annotationText?: string; // Default text to place\n // Native-only props derived from AnnotationOptions\n annotationStrokeWidth?: number;\n initialAnnotations?: Annotation[];\n onAnnotationChange?: (event: AnnotationChangeEvent) => void;\n onPageChange?: (event: PageChangeEvent) => void;\n onScroll?: (event: ScrollEvent) => void;\n};\n\n"]}
|
|
@@ -23,6 +23,9 @@ class ExpoPdfReaderView: ExpoView, UIGestureRecognizerDelegate {
|
|
|
23
23
|
private var currentAnnotation: PDFAnnotation?
|
|
24
24
|
private var currentPath: UIBezierPath?
|
|
25
25
|
|
|
26
|
+
// Drawing overlay layer for live preview
|
|
27
|
+
private var drawingLayer: CAShapeLayer?
|
|
28
|
+
|
|
26
29
|
// Undo/Redo stacks
|
|
27
30
|
private var undoStack: [PDFAnnotation] = []
|
|
28
31
|
private var redoStack: [PDFAnnotation] = []
|
|
@@ -501,6 +504,7 @@ class ExpoPdfReaderView: ExpoView, UIGestureRecognizerDelegate {
|
|
|
501
504
|
guard let page = pdfView.currentPage else { return }
|
|
502
505
|
let location = gesture.location(in: pdfView)
|
|
503
506
|
let convertedPoint = pdfView.convert(location, to: page)
|
|
507
|
+
let viewLocation = location // Keep view coordinates for drawing layer
|
|
504
508
|
|
|
505
509
|
// Handle eraser
|
|
506
510
|
if currentTool == "eraser" {
|
|
@@ -552,38 +556,120 @@ class ExpoPdfReaderView: ExpoView, UIGestureRecognizerDelegate {
|
|
|
552
556
|
switch gesture.state {
|
|
553
557
|
case .began:
|
|
554
558
|
currentPath = UIBezierPath()
|
|
555
|
-
currentPath?.move(to:
|
|
559
|
+
currentPath?.move(to: viewLocation) // Use view coordinates for drawing layer
|
|
560
|
+
|
|
561
|
+
// Create drawing layer for live preview in view coordinates
|
|
562
|
+
if drawingLayer == nil {
|
|
563
|
+
drawingLayer = CAShapeLayer()
|
|
564
|
+
drawingLayer?.fillColor = UIColor.clear.cgColor
|
|
565
|
+
drawingLayer?.lineCap = .round
|
|
566
|
+
drawingLayer?.lineJoin = .round
|
|
567
|
+
pdfView.layer.addSublayer(drawingLayer!)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
case .changed:
|
|
571
|
+
guard let path = currentPath else { return }
|
|
572
|
+
path.addLine(to: viewLocation) // Use view coordinates for drawing layer
|
|
573
|
+
|
|
574
|
+
// Update drawing layer to show live preview
|
|
575
|
+
drawingLayer?.path = path.cgPath
|
|
576
|
+
if currentTool == "pen" {
|
|
577
|
+
drawingLayer?.strokeColor = currentColor.cgColor
|
|
578
|
+
drawingLayer?.lineWidth = currentStrokeWidth
|
|
579
|
+
drawingLayer?.opacity = 1.0
|
|
580
|
+
} else if currentTool == "highlighter" {
|
|
581
|
+
drawingLayer?.strokeColor = currentColor.cgColor
|
|
582
|
+
drawingLayer?.lineWidth = currentStrokeWidth * 3.0
|
|
583
|
+
drawingLayer?.opacity = 0.3
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
case .ended:
|
|
587
|
+
guard let viewPath = currentPath else {
|
|
588
|
+
print("❌ No path to save!")
|
|
589
|
+
return
|
|
590
|
+
}
|
|
591
|
+
viewPath.addLine(to: viewLocation) // Complete view path
|
|
592
|
+
|
|
593
|
+
print("✏️ Pen gesture ended, saving annotation...")
|
|
594
|
+
print(" Tool: \(currentTool)")
|
|
595
|
+
print(" Color: \(currentColor)")
|
|
596
|
+
print(" Stroke width: \(currentStrokeWidth)")
|
|
597
|
+
|
|
598
|
+
// Remove drawing layer
|
|
599
|
+
drawingLayer?.removeFromSuperlayer()
|
|
600
|
+
drawingLayer = nil
|
|
601
|
+
|
|
602
|
+
// Convert view path to page coordinates
|
|
603
|
+
let pagePathForAnnotation = UIBezierPath()
|
|
604
|
+
viewPath.cgPath.applyWithBlock { element in
|
|
605
|
+
let points = element.pointee.points
|
|
606
|
+
switch element.pointee.type {
|
|
607
|
+
case .moveToPoint:
|
|
608
|
+
let pagePoint = self.pdfView.convert(points[0], to: page)
|
|
609
|
+
pagePathForAnnotation.move(to: pagePoint)
|
|
610
|
+
case .addLineToPoint:
|
|
611
|
+
let pagePoint = self.pdfView.convert(points[0], to: page)
|
|
612
|
+
pagePathForAnnotation.addLine(to: pagePoint)
|
|
613
|
+
case .addQuadCurveToPoint:
|
|
614
|
+
let cp = self.pdfView.convert(points[0], to: page)
|
|
615
|
+
let pt = self.pdfView.convert(points[1], to: page)
|
|
616
|
+
pagePathForAnnotation.addQuadCurve(to: pt, controlPoint: cp)
|
|
617
|
+
case .addCurveToPoint:
|
|
618
|
+
let cp1 = self.pdfView.convert(points[0], to: page)
|
|
619
|
+
let cp2 = self.pdfView.convert(points[1], to: page)
|
|
620
|
+
let pt = self.pdfView.convert(points[2], to: page)
|
|
621
|
+
pagePathForAnnotation.addCurve(to: pt, controlPoint1: cp1, controlPoint2: cp2)
|
|
622
|
+
case .closeSubpath:
|
|
623
|
+
pagePathForAnnotation.close()
|
|
624
|
+
@unknown default:
|
|
625
|
+
break
|
|
626
|
+
}
|
|
627
|
+
}
|
|
556
628
|
|
|
557
|
-
//
|
|
558
|
-
|
|
629
|
+
// Calculate proper bounds from path
|
|
630
|
+
var pathBounds = pagePathForAnnotation.bounds
|
|
631
|
+
// Expand bounds slightly to accommodate stroke width
|
|
632
|
+
let expansion = max(currentStrokeWidth * 2, 20)
|
|
633
|
+
pathBounds = pathBounds.insetBy(dx: -expansion, dy: -expansion)
|
|
559
634
|
|
|
560
635
|
if currentTool == "pen" {
|
|
561
|
-
let annotation = PDFAnnotation(bounds:
|
|
636
|
+
let annotation = PDFAnnotation(bounds: pathBounds, forType: .ink, withProperties: nil)
|
|
562
637
|
annotation.color = currentColor
|
|
563
638
|
let border = PDFBorder()
|
|
564
639
|
border.lineWidth = currentStrokeWidth
|
|
565
640
|
annotation.border = border
|
|
566
|
-
annotation.add(
|
|
641
|
+
annotation.add(pagePathForAnnotation)
|
|
642
|
+
|
|
643
|
+
print("✅ Adding pen annotation to page")
|
|
644
|
+
print(" Bounds: \(pathBounds)")
|
|
645
|
+
print(" Paths count: \(annotation.paths?.count ?? 0)")
|
|
646
|
+
|
|
567
647
|
page.addAnnotation(annotation)
|
|
568
|
-
|
|
648
|
+
|
|
649
|
+
print(" Total annotations on page: \(page.annotations.count)")
|
|
650
|
+
|
|
651
|
+
// Force PDF view to update
|
|
652
|
+
pdfView.setNeedsDisplay()
|
|
569
653
|
}
|
|
570
654
|
else if currentTool == "highlighter" {
|
|
571
|
-
let annotation = PDFAnnotation(bounds:
|
|
655
|
+
let annotation = PDFAnnotation(bounds: pathBounds, forType: .ink, withProperties: nil)
|
|
572
656
|
annotation.color = currentColor.withAlphaComponent(0.3)
|
|
573
657
|
let border = PDFBorder()
|
|
574
658
|
border.lineWidth = currentStrokeWidth * 3.0
|
|
575
659
|
annotation.border = border
|
|
576
|
-
annotation.add(
|
|
660
|
+
annotation.add(pagePathForAnnotation)
|
|
661
|
+
|
|
662
|
+
print("✅ Adding highlighter annotation to page")
|
|
663
|
+
print(" Bounds: \(pathBounds)")
|
|
664
|
+
|
|
577
665
|
page.addAnnotation(annotation)
|
|
578
|
-
|
|
666
|
+
|
|
667
|
+
print(" Total annotations on page: \(page.annotations.count)")
|
|
668
|
+
|
|
669
|
+
// Force PDF view to update
|
|
670
|
+
pdfView.setNeedsDisplay()
|
|
579
671
|
}
|
|
580
672
|
|
|
581
|
-
case .changed:
|
|
582
|
-
guard let path = currentPath, let annotation = currentAnnotation else { return }
|
|
583
|
-
path.addLine(to: convertedPoint)
|
|
584
|
-
annotation.add(path) // Update path
|
|
585
|
-
|
|
586
|
-
case .ended, .cancelled:
|
|
587
673
|
// Clear redo stack when new annotation is added
|
|
588
674
|
redoStack.removeAll()
|
|
589
675
|
|
|
@@ -591,10 +677,19 @@ class ExpoPdfReaderView: ExpoView, UIGestureRecognizerDelegate {
|
|
|
591
677
|
currentAnnotation = nil
|
|
592
678
|
|
|
593
679
|
// Emit event
|
|
680
|
+
let allAnnotations = getAnnotations()
|
|
681
|
+
print("📤 Emitting annotation change event: \(allAnnotations.count) annotations")
|
|
594
682
|
onAnnotationChange([
|
|
595
|
-
"annotations":
|
|
683
|
+
"annotations": allAnnotations
|
|
596
684
|
])
|
|
597
685
|
|
|
686
|
+
case .cancelled:
|
|
687
|
+
// Remove drawing layer and clear without adding annotation
|
|
688
|
+
drawingLayer?.removeFromSuperlayer()
|
|
689
|
+
drawingLayer = nil
|
|
690
|
+
currentPath = nil
|
|
691
|
+
currentAnnotation = nil
|
|
692
|
+
|
|
598
693
|
default:
|
|
599
694
|
break
|
|
600
695
|
}
|
package/package.json
CHANGED