@june24/expo-pdf-reader 0.1.2 → 0.1.4

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"]}
package/build/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import ExpoPdfReaderView from './ExpoPdfReaderView';
2
- import { ExpoPdfReaderViewProps } from './ExpoPdfReader.types';
3
- export { ExpoPdfReaderView, ExpoPdfReaderViewProps };
1
+ import ExpoPdfReaderView, { ExpoPdfReaderRef } from './ExpoPdfReaderView';
2
+ import { ExpoPdfReaderViewProps, Annotation, AnnotationOptions, AnnotationTool, AnnotationChangeEvent, PageChangeEvent, ScrollEvent, SearchResult, DisplayMode } from './ExpoPdfReader.types';
3
+ export { ExpoPdfReaderView, ExpoPdfReaderRef, ExpoPdfReaderViewProps, Annotation, AnnotationOptions, AnnotationTool, AnnotationChangeEvent, PageChangeEvent, ScrollEvent, SearchResult, DisplayMode, };
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,EAAE,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAG,MAAM,uBAAuB,CAAC;AAE/L,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,GAAG,CAAC"}
package/build/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import ExpoPdfReaderView from './ExpoPdfReaderView';
2
- export { ExpoPdfReaderView };
2
+ export { ExpoPdfReaderView, };
3
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,iBAAiB,EAA0B,CAAC","sourcesContent":["import ExpoPdfReaderView from './ExpoPdfReaderView';\nimport { ExpoPdfReaderViewProps } from './ExpoPdfReader.types';\n\nexport { ExpoPdfReaderView, ExpoPdfReaderViewProps };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAuC,MAAM,qBAAqB,CAAC;AAG1E,OAAO,EAAE,iBAAiB,GAA4K,CAAC","sourcesContent":["import ExpoPdfReaderView, { ExpoPdfReaderRef } from './ExpoPdfReaderView';\nimport { ExpoPdfReaderViewProps, Annotation, AnnotationOptions, AnnotationTool, AnnotationChangeEvent, PageChangeEvent, ScrollEvent, SearchResult, DisplayMode, } from './ExpoPdfReader.types';\n\nexport { ExpoPdfReaderView, ExpoPdfReaderRef, ExpoPdfReaderViewProps, Annotation, AnnotationOptions, AnnotationTool, AnnotationChangeEvent, PageChangeEvent, ScrollEvent, SearchResult, DisplayMode, };\n"]}
@@ -1,7 +1,7 @@
1
1
  import ExpoModulesCore
2
2
  import PDFKit
3
3
 
4
- class ExpoPdfReaderView: ExpoView {
4
+ class ExpoPdfReaderView: ExpoView, UIGestureRecognizerDelegate {
5
5
  let pdfView = PDFView()
6
6
  var currentTool: String = "none"
7
7
  var currentColor: UIColor = .black
@@ -23,6 +23,9 @@ class ExpoPdfReaderView: ExpoView {
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] = []
@@ -40,7 +43,9 @@ class ExpoPdfReaderView: ExpoView {
40
43
 
41
44
  // Setup gestures
42
45
  panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
46
+ panGesture?.delegate = self
43
47
  tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
48
+ tapGesture?.delegate = self
44
49
 
45
50
  // Initially disabled
46
51
  panGesture?.isEnabled = false
@@ -195,11 +200,18 @@ class ExpoPdfReaderView: ExpoView {
195
200
  let isDrawing = tool == "pen" || tool == "highlighter"
196
201
  let isEraser = tool == "eraser"
197
202
  panGesture?.isEnabled = isDrawing || isEraser
198
- pdfView.isUserInteractionEnabled = !isDrawing && !isEraser // Disable PDF interaction when drawing/erasing to prevent scrolling
199
203
 
200
- // Re-enable scrolling if not drawing or erasing
201
- if !isDrawing && !isEraser {
202
- pdfView.isUserInteractionEnabled = true
204
+ // Disable PDF's built-in gestures when drawing/erasing
205
+ if isDrawing || isEraser {
206
+ // Disable scroll and other PDF interactions
207
+ if let scrollView = pdfView.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView {
208
+ scrollView.isScrollEnabled = false
209
+ }
210
+ } else {
211
+ // Re-enable scrolling
212
+ if let scrollView = pdfView.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView {
213
+ scrollView.isScrollEnabled = true
214
+ }
203
215
  }
204
216
 
205
217
  if tool == "text" || tool == "note" {
@@ -492,6 +504,7 @@ class ExpoPdfReaderView: ExpoView {
492
504
  guard let page = pdfView.currentPage else { return }
493
505
  let location = gesture.location(in: pdfView)
494
506
  let convertedPoint = pdfView.convert(location, to: page)
507
+ let viewLocation = location // Keep view coordinates for drawing layer
495
508
 
496
509
  // Handle eraser
497
510
  if currentTool == "eraser" {
@@ -543,38 +556,99 @@ class ExpoPdfReaderView: ExpoView {
543
556
  switch gesture.state {
544
557
  case .began:
545
558
  currentPath = UIBezierPath()
546
- 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
+ }
547
569
 
548
- // Create annotation
549
- let bounds = page.bounds(for: pdfView.displayBox)
570
+ case .changed:
571
+ guard let path = currentPath else { return }
572
+ path.addLine(to: viewLocation) // Use view coordinates for drawing layer
550
573
 
574
+ // Update drawing layer to show live preview
575
+ drawingLayer?.path = path.cgPath
551
576
  if currentTool == "pen" {
552
- let annotation = PDFAnnotation(bounds: bounds, forType: .ink, withProperties: nil)
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 { return }
588
+ viewPath.addLine(to: viewLocation) // Complete view path
589
+
590
+ // Remove drawing layer
591
+ drawingLayer?.removeFromSuperlayer()
592
+ drawingLayer = nil
593
+
594
+ // Convert view path to page coordinates
595
+ let pagePathForAnnotation = UIBezierPath()
596
+ viewPath.cgPath.applyWithBlock { element in
597
+ let points = element.pointee.points
598
+ switch element.pointee.type {
599
+ case .moveToPoint:
600
+ let pagePoint = self.pdfView.convert(points[0], to: page)
601
+ pagePathForAnnotation.move(to: pagePoint)
602
+ case .addLineToPoint:
603
+ let pagePoint = self.pdfView.convert(points[0], to: page)
604
+ pagePathForAnnotation.addLine(to: pagePoint)
605
+ case .addQuadCurveToPoint:
606
+ let cp = self.pdfView.convert(points[0], to: page)
607
+ let pt = self.pdfView.convert(points[1], to: page)
608
+ pagePathForAnnotation.addQuadCurve(to: pt, controlPoint: cp)
609
+ case .addCurveToPoint:
610
+ let cp1 = self.pdfView.convert(points[0], to: page)
611
+ let cp2 = self.pdfView.convert(points[1], to: page)
612
+ let pt = self.pdfView.convert(points[2], to: page)
613
+ pagePathForAnnotation.addCurve(to: pt, controlPoint1: cp1, controlPoint2: cp2)
614
+ case .closeSubpath:
615
+ pagePathForAnnotation.close()
616
+ @unknown default:
617
+ break
618
+ }
619
+ }
620
+
621
+ // Calculate proper bounds from path
622
+ var pathBounds = pagePathForAnnotation.bounds
623
+ // Expand bounds slightly to accommodate stroke width
624
+ let expansion = max(currentStrokeWidth * 2, 20)
625
+ pathBounds = pathBounds.insetBy(dx: -expansion, dy: -expansion)
626
+
627
+ if currentTool == "pen" {
628
+ let annotation = PDFAnnotation(bounds: pathBounds, forType: .ink, withProperties: nil)
553
629
  annotation.color = currentColor
554
630
  let border = PDFBorder()
555
631
  border.lineWidth = currentStrokeWidth
556
632
  annotation.border = border
557
- annotation.add(currentPath!)
633
+ annotation.add(pagePathForAnnotation)
558
634
  page.addAnnotation(annotation)
559
- currentAnnotation = annotation
635
+
636
+ // Force PDF view to update
637
+ pdfView.setNeedsDisplay()
560
638
  }
561
639
  else if currentTool == "highlighter" {
562
- let annotation = PDFAnnotation(bounds: bounds, forType: .ink, withProperties: nil)
640
+ let annotation = PDFAnnotation(bounds: pathBounds, forType: .ink, withProperties: nil)
563
641
  annotation.color = currentColor.withAlphaComponent(0.3)
564
642
  let border = PDFBorder()
565
643
  border.lineWidth = currentStrokeWidth * 3.0
566
644
  annotation.border = border
567
- annotation.add(currentPath!)
645
+ annotation.add(pagePathForAnnotation)
568
646
  page.addAnnotation(annotation)
569
- currentAnnotation = annotation
647
+
648
+ // Force PDF view to update
649
+ pdfView.setNeedsDisplay()
570
650
  }
571
651
 
572
- case .changed:
573
- guard let path = currentPath, let annotation = currentAnnotation else { return }
574
- path.addLine(to: convertedPoint)
575
- annotation.add(path) // Update path
576
-
577
- case .ended, .cancelled:
578
652
  // Clear redo stack when new annotation is added
579
653
  redoStack.removeAll()
580
654
 
@@ -586,6 +660,13 @@ class ExpoPdfReaderView: ExpoView {
586
660
  "annotations": getAnnotations()
587
661
  ])
588
662
 
663
+ case .cancelled:
664
+ // Remove drawing layer and clear without adding annotation
665
+ drawingLayer?.removeFromSuperlayer()
666
+ drawingLayer = nil
667
+ currentPath = nil
668
+ currentAnnotation = nil
669
+
589
670
  default:
590
671
  break
591
672
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@june24/expo-pdf-reader",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",
@@ -22,7 +22,7 @@
22
22
  "author": "User",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "expo-modules-core": "^4.0.0"
25
+ "expo-modules-core": "^3.0.29"
26
26
  },
27
27
  "devDependencies": {
28
28
  "expo-module-scripts": "^5.0.8",
@@ -89,7 +89,7 @@ export type NativeExpoPdfReaderViewProps = ViewProps & {
89
89
  annotationColor?: string; // Hex color
90
90
  annotationFontSize?: number;
91
91
  annotationText?: string; // Default text to place
92
- // Native-only props derived from AnnotationOptions
92
+ // Native-only props derived from AnnotationOptions
93
93
  annotationStrokeWidth?: number;
94
94
  initialAnnotations?: Annotation[];
95
95
  onAnnotationChange?: (event: AnnotationChangeEvent) => void;
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import ExpoPdfReaderView from './ExpoPdfReaderView';
2
- import { ExpoPdfReaderViewProps } from './ExpoPdfReader.types';
1
+ import ExpoPdfReaderView, { ExpoPdfReaderRef } from './ExpoPdfReaderView';
2
+ import { ExpoPdfReaderViewProps, Annotation, AnnotationOptions, AnnotationTool, AnnotationChangeEvent, PageChangeEvent, ScrollEvent, SearchResult, DisplayMode, } from './ExpoPdfReader.types';
3
3
 
4
- export { ExpoPdfReaderView, ExpoPdfReaderViewProps };
4
+ export { ExpoPdfReaderView, ExpoPdfReaderRef, ExpoPdfReaderViewProps, Annotation, AnnotationOptions, AnnotationTool, AnnotationChangeEvent, PageChangeEvent, ScrollEvent, SearchResult, DisplayMode, };