@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 // 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"]}
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: convertedPoint)
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
- // Create annotation
558
- let bounds = page.bounds(for: pdfView.displayBox)
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: bounds, forType: .ink, withProperties: nil)
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(currentPath!)
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
- currentAnnotation = annotation
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: bounds, forType: .ink, withProperties: nil)
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(currentPath!)
660
+ annotation.add(pagePathForAnnotation)
661
+
662
+ print("✅ Adding highlighter annotation to page")
663
+ print(" Bounds: \(pathBounds)")
664
+
577
665
  page.addAnnotation(annotation)
578
- currentAnnotation = annotation
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": getAnnotations()
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@june24/expo-pdf-reader",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "A PDF reader for Expo apps with annotation and zoom controls",
5
5
  "homepage": "git@gitlab.com:june_241/expo-pdf-reader.git",
6
6
  "main": "build/index.js",