@kishannareshpal/expo-pdf 0.1.0 → 0.2.1
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/CHANGELOG.md +23 -1
- package/CONTRIBUTING.md +16 -41
- package/README.md +177 -76
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/kishannareshpal/expopdf/KJExpoPdfModule.kt +6 -2
- package/android/src/main/java/com/kishannareshpal/expopdf/KJExpoPdfView.kt +14 -13
- package/android/src/main/java/com/kishannareshpal/expopdf/lib/ContentPadding.kt +5 -5
- package/build/pdf-view.d.ts +2 -1
- package/build/pdf-view.d.ts.map +1 -1
- package/build/pdf-view.js +1 -1
- package/build/pdf-view.js.map +1 -1
- package/bunfig.toml +2 -0
- package/ios/KJExpoPdfModule.swift +8 -4
- package/ios/KJExpoPdfView.swift +57 -15
- package/ios/extensions/PdfViewExtensions.swift +22 -17
- package/package.json +35 -36
- package/src/pdf-view.tsx +4 -2
- package/bun.lock +0 -2278
|
@@ -6,18 +6,18 @@ import expo.modules.kotlin.records.Record
|
|
|
6
6
|
|
|
7
7
|
class ContentPadding: Record {
|
|
8
8
|
@Field
|
|
9
|
-
val left:
|
|
9
|
+
val left: Float = 0f
|
|
10
10
|
|
|
11
11
|
@Field
|
|
12
|
-
val top:
|
|
12
|
+
val top: Float = 0f
|
|
13
13
|
|
|
14
14
|
@Field
|
|
15
|
-
val right:
|
|
15
|
+
val right: Float = 0f
|
|
16
16
|
|
|
17
17
|
@Field
|
|
18
|
-
val bottom:
|
|
18
|
+
val bottom: Float = 0f
|
|
19
19
|
|
|
20
20
|
fun toRect(): Rect {
|
|
21
|
-
return Rect(this.left, this.top, this.right, this.bottom)
|
|
21
|
+
return Rect(this.left.toInt(), this.top.toInt(), this.right.toInt(), this.bottom.toInt())
|
|
22
22
|
}
|
|
23
23
|
}
|
package/build/pdf-view.d.ts
CHANGED
|
@@ -9,11 +9,12 @@ type BaseProps = {
|
|
|
9
9
|
uri: string;
|
|
10
10
|
password?: string;
|
|
11
11
|
pagingEnabled?: boolean;
|
|
12
|
-
|
|
12
|
+
doubleTapToZoom?: boolean;
|
|
13
13
|
horizontal?: boolean;
|
|
14
14
|
pageGap?: number;
|
|
15
15
|
contentPadding?: ContentPadding;
|
|
16
16
|
fitMode?: FitMode;
|
|
17
|
+
autoScale?: boolean;
|
|
17
18
|
};
|
|
18
19
|
export type PdfViewProps = BaseProps & {
|
|
19
20
|
onLoadComplete?: (params: OnLoadCompleteEventPayload) => void;
|
package/build/pdf-view.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pdf-view.d.ts","sourceRoot":"","sources":["../src/pdf-view.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAC9H,OAAO,EAAwB,SAAS,EAAc,SAAS,EAAE,MAAM,cAAc,CAAC;AAGtF,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,
|
|
1
|
+
{"version":3,"file":"pdf-view.d.ts","sourceRoot":"","sources":["../src/pdf-view.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAC9H,OAAO,EAAwB,SAAS,EAAc,SAAS,EAAE,MAAM,cAAc,CAAC;AAGtF,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAYD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG;IACrC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAC9D,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAC5D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAA;CAChD,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,6DAMrB,YAAY,sBAkBd,CAAA"}
|
package/build/pdf-view.js
CHANGED
|
@@ -4,7 +4,7 @@ import { StyleSheet } from 'react-native';
|
|
|
4
4
|
import { forwardNativeEventTo } from './utils';
|
|
5
5
|
const NativePdfView = requireNativeView('KJExpoPdf');
|
|
6
6
|
export const PdfView = ({ style, onLoadComplete, onError, onPageChanged, ...props }) => {
|
|
7
|
-
return (<NativePdfView style={[styles.container, style]} uri={props.uri}
|
|
7
|
+
return (<NativePdfView style={[styles.container, style]} uri={props.uri} doubleTapToZoom={props.doubleTapToZoom} horizontal={props.horizontal} pageGap={props.pageGap} pagingEnabled={props.pagingEnabled} password={props.password} contentPadding={props.contentPadding} fitMode={props.fitMode} autoScale={props.autoScale} onLoadComplete={forwardNativeEventTo(onLoadComplete)} onPageChanged={forwardNativeEventTo(onPageChanged)} onError={forwardNativeEventTo(onError)}/>);
|
|
8
8
|
};
|
|
9
9
|
const styles = StyleSheet.create({
|
|
10
10
|
container: {
|
package/build/pdf-view.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pdf-view.js","sourceRoot":"","sources":["../src/pdf-view.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAmC,UAAU,EAAa,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"pdf-view.js","sourceRoot":"","sources":["../src/pdf-view.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAmC,UAAU,EAAa,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAwB/C,MAAM,aAAa,GAA4C,iBAAiB,CAAC,WAAW,CAAC,CAAC;AAU9F,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,EACtB,KAAK,EACL,cAAc,EACd,OAAO,EACP,aAAa,EACb,GAAG,KAAK,EACK,EAAE,EAAE;IACjB,OAAO,CACL,CAAC,aAAa,CACZ,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CACjC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CACf,eAAe,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CACvC,UAAU,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAC7B,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CACvB,aAAa,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CACnC,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CACzB,cAAc,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CACrC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CACvB,SAAS,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAC3B,cAAc,CAAC,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC,CACrD,aAAa,CAAC,CAAC,oBAAoB,CAAC,aAAa,CAAC,CAAC,CACnD,OAAO,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,EACvC,CACH,CAAA;AACH,CAAC,CAAA;AAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE;QACT,eAAe,EAAE,SAAS;KAC3B;CACF,CAAC,CAAA","sourcesContent":["import { requireNativeView } from 'expo';\nimport * as React from 'react';\n\nimport { ContentPadding, FitMode, OnErrorEventPayload, OnLoadCompleteEventPayload, OnPageChangedEventPayload } from './types';\nimport { NativeSyntheticEvent, StyleProp, StyleSheet, ViewStyle } from 'react-native';\nimport { forwardNativeEventTo } from './utils';\n\ntype BaseProps = {\n style?: StyleProp<ViewStyle>;\n /**\n * The file URI. Accepts a remote resource (e.g. via HTTPs) or a local file path (e.g. file:///)\n */\n uri: string;\n password?: string;\n pagingEnabled?: boolean\n doubleTapToZoom?: boolean\n horizontal?: boolean\n pageGap?: number\n contentPadding?: ContentPadding\n fitMode?: FitMode\n autoScale?: boolean\n}\n\ntype NativePdfViewProps = BaseProps & {\n onLoadComplete?: (event: NativeSyntheticEvent<OnLoadCompleteEventPayload>) => void;\n onPageChanged?: (event: NativeSyntheticEvent<OnPageChangedEventPayload>) => void;\n onError?: (event: NativeSyntheticEvent<OnErrorEventPayload>) => void;\n};\n\nconst NativePdfView: React.ComponentType<NativePdfViewProps> = requireNativeView('KJExpoPdf');\n\n// -----------\n\nexport type PdfViewProps = BaseProps & {\n onLoadComplete?: (params: OnLoadCompleteEventPayload) => void,\n onPageChanged?: (params: OnPageChangedEventPayload) => void,\n onError?: (params: OnErrorEventPayload) => void\n};\n\nexport const PdfView = ({\n style,\n onLoadComplete,\n onError,\n onPageChanged,\n ...props\n}: PdfViewProps) => {\n return (\n <NativePdfView\n style={[styles.container, style]}\n uri={props.uri}\n doubleTapToZoom={props.doubleTapToZoom}\n horizontal={props.horizontal}\n pageGap={props.pageGap}\n pagingEnabled={props.pagingEnabled}\n password={props.password}\n contentPadding={props.contentPadding}\n fitMode={props.fitMode}\n autoScale={props.autoScale}\n onLoadComplete={forwardNativeEventTo(onLoadComplete)}\n onPageChanged={forwardNativeEventTo(onPageChanged)}\n onError={forwardNativeEventTo(onError)}\n />\n )\n}\n\nconst styles = StyleSheet.create({\n container: {\n backgroundColor: '#eeeeee'\n }\n})\n"]}
|
package/bunfig.toml
ADDED
|
@@ -27,16 +27,16 @@ public class KJExpoPdfModule: Module {
|
|
|
27
27
|
view.setPagingEnabled(enabled)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
Prop("
|
|
31
|
-
view.setDoubleTapZoomEnabled(
|
|
30
|
+
Prop("doubleTapToZoom") { (view: KJExpoPdfView, enabled: Bool?) in
|
|
31
|
+
view.setDoubleTapZoomEnabled(enabled)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
Prop("horizontal") { (view: KJExpoPdfView, enabled: Bool?) in
|
|
35
35
|
view.setHorizontalModeEnabled(enabled)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
Prop("pageGap") { (view: KJExpoPdfView,
|
|
39
|
-
view.setPageGap(
|
|
38
|
+
Prop("pageGap") { (view: KJExpoPdfView, gap: Int?) in
|
|
39
|
+
view.setPageGap(gap)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
Prop("contentPadding") { (view: KJExpoPdfView, contentPadding: ContentPadding?) in
|
|
@@ -46,6 +46,10 @@ public class KJExpoPdfModule: Module {
|
|
|
46
46
|
Prop("fitMode") { (view: KJExpoPdfView, fitMode: FitMode?) in
|
|
47
47
|
view.setFitMode(fitMode)
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
Prop("autoScale") { (view: KJExpoPdfView, enabled: Bool?) in
|
|
51
|
+
view.setAutoScaleEnabled(enabled)
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
}
|
package/ios/KJExpoPdfView.swift
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import CoreGraphics
|
|
1
2
|
import ExpoModulesCore
|
|
2
3
|
import PDFKit
|
|
3
4
|
import SwiftUI
|
|
@@ -10,6 +11,7 @@ class KJExpoPdfView: ExpoView {
|
|
|
10
11
|
static let DEFAULT_PAGE_GAP = 0
|
|
11
12
|
static let DEFAULT_CONTENT_PADDING = UIEdgeInsets.zero
|
|
12
13
|
static let DEFAULT_FIT_MODE = FitMode.both
|
|
14
|
+
static let DEFAULT_AUTO_SCALE_ENABLED = true
|
|
13
15
|
|
|
14
16
|
let onLoadComplete = EventDispatcher()
|
|
15
17
|
let onPageChanged = EventDispatcher()
|
|
@@ -24,6 +26,10 @@ class KJExpoPdfView: ExpoView {
|
|
|
24
26
|
|
|
25
27
|
private let pdfView = PDFView()
|
|
26
28
|
|
|
29
|
+
private var lastLayoutBounds: CGRect = .zero
|
|
30
|
+
private var isFirstLayoutComplete: Bool = false
|
|
31
|
+
|
|
32
|
+
// - MARK: Props
|
|
27
33
|
private var documentURL: URL? = nil
|
|
28
34
|
private var password: String? = nil
|
|
29
35
|
private var isPagingEnabled: Bool = DEFAULT_PAGING_ENABLED
|
|
@@ -32,6 +38,7 @@ class KJExpoPdfView: ExpoView {
|
|
|
32
38
|
private var pageGap: Int = DEFAULT_PAGE_GAP
|
|
33
39
|
private var contentPadding: UIEdgeInsets = DEFAULT_CONTENT_PADDING
|
|
34
40
|
private var fitMode: FitMode = DEFAULT_FIT_MODE
|
|
41
|
+
private var isAutoScaleEnabled: Bool = DEFAULT_AUTO_SCALE_ENABLED
|
|
35
42
|
|
|
36
43
|
required init(appContext: AppContext? = nil) {
|
|
37
44
|
super.init(appContext: appContext)
|
|
@@ -43,21 +50,40 @@ class KJExpoPdfView: ExpoView {
|
|
|
43
50
|
// style prop in the component (`style={{ backgroundColor: '#eee' }}`).
|
|
44
51
|
self.pdfView.backgroundColor = .clear
|
|
45
52
|
|
|
46
|
-
// We calculate the scaling manually via PDFView.scaleToFit(contentPadding:)
|
|
53
|
+
// We calculate the scaling manually via PDFView.scaleToFit(contentPadding:) and don't rely
|
|
54
|
+
// on native autoscaling because it doesn't take into account the content padding.
|
|
55
|
+
// - TODO: Maybe allow native autoscaling if no content padding is set? as that's the only reason we need the manual method.
|
|
47
56
|
self.pdfView.autoScales = false
|
|
48
57
|
|
|
49
58
|
addSubview(pdfView)
|
|
50
|
-
|
|
51
59
|
setupListeners()
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
override func layoutSubviews() {
|
|
63
|
+
// This method sometimes gets called with either one of bounds.height or bounds.width set as 0 initially
|
|
64
|
+
// so we defer until both values are available.
|
|
65
|
+
// - By delaying calling until the size in both dimensions are correct we are able to prevent duplicate measurements
|
|
66
|
+
// and detect the initial layout action.
|
|
67
|
+
let bothNewBoundsAreSet = bounds.width > 0 && bounds.height > 0
|
|
68
|
+
let previousBoundsAreSet = self.lastLayoutBounds.width > 0 || self.lastLayoutBounds.height > 0
|
|
69
|
+
let finishedInitialLayout = bothNewBoundsAreSet || previousBoundsAreSet
|
|
70
|
+
if !finishedInitialLayout {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
55
74
|
super.layoutSubviews()
|
|
56
|
-
pdfView.frame = bounds
|
|
75
|
+
self.pdfView.frame = bounds
|
|
76
|
+
self.lastLayoutBounds = bounds
|
|
77
|
+
|
|
78
|
+
// Force auto scale the document to fit the view on the intial layout by default
|
|
79
|
+
// - Do not autoscale on subsequent layouts if the consumer has manually disabled it
|
|
80
|
+
if !self.isFirstLayoutComplete || self.isAutoScaleEnabled {
|
|
81
|
+
// Maintain insets on rotation and reset the reading position (current scroll position) only on
|
|
82
|
+
// the first layout.
|
|
83
|
+
self.autoScale(resetScrollOffset: !self.isFirstLayoutComplete)
|
|
84
|
+
}
|
|
57
85
|
|
|
58
|
-
|
|
59
|
-
self.pdfView.scaleToFit(
|
|
60
|
-
contentPadding: self.contentPadding, fitMode: self.fitMode, resetOffset: false)
|
|
86
|
+
self.isFirstLayoutComplete = true
|
|
61
87
|
}
|
|
62
88
|
|
|
63
89
|
deinit {
|
|
@@ -122,7 +148,7 @@ class KJExpoPdfView: ExpoView {
|
|
|
122
148
|
right: padding.right - margins.right
|
|
123
149
|
)
|
|
124
150
|
self.pdfView.scaleToFit(
|
|
125
|
-
contentPadding: self.contentPadding, fitMode: self.fitMode,
|
|
151
|
+
contentPadding: self.contentPadding, fitMode: self.fitMode, resetScrollOffset: false)
|
|
126
152
|
}
|
|
127
153
|
|
|
128
154
|
func setContentPadding(_ padding: UIEdgeInsets?) {
|
|
@@ -142,14 +168,24 @@ class KJExpoPdfView: ExpoView {
|
|
|
142
168
|
right: padding.right - margins.right
|
|
143
169
|
)
|
|
144
170
|
self.pdfView.scaleToFit(
|
|
145
|
-
contentPadding: self.contentPadding, fitMode: self.fitMode,
|
|
171
|
+
contentPadding: self.contentPadding, fitMode: self.fitMode, resetScrollOffset: false)
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
func setFitMode(_ mode: FitMode?) {
|
|
149
175
|
self.fitMode = mode ?? Self.DEFAULT_FIT_MODE
|
|
150
176
|
|
|
151
177
|
self.pdfView.scaleToFit(
|
|
152
|
-
contentPadding: self.contentPadding, fitMode: self.fitMode,
|
|
178
|
+
contentPadding: self.contentPadding, fitMode: self.fitMode, resetScrollOffset: false)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
func setAutoScaleEnabled(_ enabled: Bool?) {
|
|
182
|
+
self.isAutoScaleEnabled = enabled ?? Self.DEFAULT_AUTO_SCALE_ENABLED
|
|
183
|
+
|
|
184
|
+
// Re-scale because a consumer will always expect a change when changing this prop.
|
|
185
|
+
if enabled == true {
|
|
186
|
+
self.pdfView.scaleToFit(
|
|
187
|
+
contentPadding: self.contentPadding, fitMode: self.fitMode, resetScrollOffset: true)
|
|
188
|
+
}
|
|
153
189
|
}
|
|
154
190
|
|
|
155
191
|
@objc private func handlePageChange() {
|
|
@@ -185,14 +221,9 @@ class KJExpoPdfView: ExpoView {
|
|
|
185
221
|
)
|
|
186
222
|
}
|
|
187
223
|
}
|
|
188
|
-
|
|
189
224
|
self.pdfView.document = document
|
|
190
225
|
|
|
191
|
-
|
|
192
|
-
DispatchQueue.main.async {
|
|
193
|
-
self.pdfView.scaleToFit(
|
|
194
|
-
contentPadding: self.contentPadding, fitMode: self.fitMode, resetOffset: true)
|
|
195
|
-
}
|
|
226
|
+
self.autoScale(resetScrollOffset: !self.isFirstLayoutComplete)
|
|
196
227
|
|
|
197
228
|
self.onLoadComplete([
|
|
198
229
|
"pageCount": document.pageCount
|
|
@@ -224,6 +255,17 @@ class KJExpoPdfView: ExpoView {
|
|
|
224
255
|
return document
|
|
225
256
|
}
|
|
226
257
|
|
|
258
|
+
private func autoScale(resetScrollOffset: Bool) {
|
|
259
|
+
// Dispatch async to allow PDFView to finish its initial layout
|
|
260
|
+
DispatchQueue.main.async {
|
|
261
|
+
self.pdfView.scaleToFit(
|
|
262
|
+
contentPadding: self.contentPadding,
|
|
263
|
+
fitMode: self.fitMode,
|
|
264
|
+
resetScrollOffset: resetScrollOffset
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
227
269
|
private func setupListeners() {
|
|
228
270
|
NotificationCenter.default.addObserver(
|
|
229
271
|
self,
|
|
@@ -5,30 +5,31 @@
|
|
|
5
5
|
// Created by Kishan Jadav on 05/01/2026.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
|
+
import ExpoModulesCore
|
|
8
9
|
import PDFKit
|
|
9
10
|
|
|
10
11
|
extension PDFView {
|
|
11
|
-
func scaleToFit(contentPadding: UIEdgeInsets, fitMode: FitMode,
|
|
12
|
+
func scaleToFit(contentPadding: UIEdgeInsets, fitMode: FitMode, resetScrollOffset: Bool = false) {
|
|
12
13
|
guard let page = self.currentPage else {
|
|
13
14
|
return
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
+
|
|
16
17
|
let viewSize = self.bounds.size
|
|
17
18
|
let pageSize = page.bounds(for: self.displayBox).size
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
// Ensure we have valid dimensions to avoid division by zero
|
|
20
21
|
guard viewSize.width > 0, pageSize.width > 0, pageSize.height > 0 else {
|
|
21
22
|
return
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
// Calculate the available space (View size minus Padding)
|
|
25
26
|
let availableWidth = viewSize.width - contentPadding.left - contentPadding.right
|
|
26
27
|
let availableHeight = viewSize.height - contentPadding.top - contentPadding.bottom
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
// Calculate potential scale factors
|
|
29
30
|
let widthScale = availableWidth / pageSize.width
|
|
30
31
|
let heightScale = availableHeight / pageSize.height
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
// Determine the target scale based on the requested FitMode
|
|
33
34
|
let targetScale: CGFloat
|
|
34
35
|
switch fitMode {
|
|
@@ -40,22 +41,22 @@ extension PDFView {
|
|
|
40
41
|
// "Aspect Fit": Choose the smaller scale to ensure the whole page is visible
|
|
41
42
|
targetScale = min(widthScale, heightScale)
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
|
|
44
45
|
// Apply new scale factor
|
|
45
46
|
if abs(self.scaleFactor - targetScale) > 0.001 {
|
|
46
|
-
self.minScaleFactor = targetScale
|
|
47
|
+
self.minScaleFactor = targetScale // Prevent zooming out further than the fit
|
|
47
48
|
self.scaleFactor = targetScale
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
-
self.applyContentPadding(contentPadding,
|
|
50
|
+
|
|
51
|
+
self.applyContentPadding(contentPadding, resetScrollOffset: resetScrollOffset)
|
|
51
52
|
}
|
|
52
|
-
|
|
53
|
-
func applyContentPadding(_ contentPadding: UIEdgeInsets,
|
|
53
|
+
|
|
54
|
+
func applyContentPadding(_ contentPadding: UIEdgeInsets, resetScrollOffset: Bool = false) {
|
|
54
55
|
// Iterate through the PDFView's subviews to find the scroll view
|
|
55
56
|
if let scrollView = self.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView {
|
|
56
57
|
scrollView.contentInset = contentPadding
|
|
57
58
|
|
|
58
|
-
if
|
|
59
|
+
if resetScrollOffset {
|
|
59
60
|
var offset = scrollView.contentOffset
|
|
60
61
|
if self.displayDirection == .horizontal {
|
|
61
62
|
offset.x = -contentPadding.left
|
|
@@ -66,24 +67,28 @@ extension PDFView {
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
+
|
|
70
71
|
func toggleDoubleTapToZoom(_ enabled: Bool) {
|
|
71
72
|
// Iterate through the PDFView's subviews to find the scroll view
|
|
72
73
|
for subview in self.subviews {
|
|
73
74
|
if let gestureRecognizers = subview.gestureRecognizers {
|
|
74
75
|
for gesture in gestureRecognizers {
|
|
75
|
-
if let tapGesture = gesture as? UITapGestureRecognizer,
|
|
76
|
+
if let tapGesture = gesture as? UITapGestureRecognizer,
|
|
77
|
+
tapGesture.numberOfTapsRequired == 2
|
|
78
|
+
{
|
|
76
79
|
// Disable the double-tap recognizer
|
|
77
80
|
tapGesture.isEnabled = enabled
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
|
-
|
|
84
|
+
|
|
82
85
|
// Sometimes the gesture is deeper, so we check sub-subviews (like the document view)
|
|
83
86
|
for internalSubview in subview.subviews {
|
|
84
87
|
if let gestureRecognizers = internalSubview.gestureRecognizers {
|
|
85
88
|
for gesture in gestureRecognizers {
|
|
86
|
-
if let tapGesture = gesture as? UITapGestureRecognizer,
|
|
89
|
+
if let tapGesture = gesture as? UITapGestureRecognizer,
|
|
90
|
+
tapGesture.numberOfTapsRequired == 2
|
|
91
|
+
{
|
|
87
92
|
tapGesture.isEnabled = enabled
|
|
88
93
|
}
|
|
89
94
|
}
|
package/package.json
CHANGED
|
@@ -1,40 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kishannareshpal/expo-pdf",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "A cross-platform, performant PDF viewer component for React Native and Expo",
|
|
5
|
-
"main": "build/index.js",
|
|
6
|
-
"types": "build/index.d.ts",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"build": "expo-module build",
|
|
9
|
-
"clean": "expo-module clean",
|
|
10
|
-
"lint": "expo-module lint",
|
|
11
|
-
"test": "expo-module test",
|
|
12
|
-
"prepublishOnly": "expo-module prepublishOnly",
|
|
13
|
-
"expo-module": "expo-module",
|
|
14
|
-
"open:ios": "xed example/ios",
|
|
15
|
-
"open:android": "open -a \"Android Studio\" example/android",
|
|
16
|
-
"format": "prettier --write .",
|
|
17
|
-
"format:check": "prettier --check .",
|
|
18
|
-
"prepare": "husky; expo-module prepare"
|
|
19
|
-
},
|
|
20
|
-
"keywords": [
|
|
21
|
-
"react-native",
|
|
22
|
-
"expo",
|
|
23
|
-
"@kishannareshpal/expo-pdf",
|
|
24
|
-
"rn-pdf",
|
|
25
|
-
"react-native-pdf",
|
|
26
|
-
"expo-pdf",
|
|
27
|
-
"pdf",
|
|
28
|
-
"expopdf"
|
|
29
|
-
],
|
|
30
|
-
"repository": "https://github.com/kishannareshpal/expo-pdf",
|
|
31
|
-
"bugs": {
|
|
32
|
-
"url": "https://github.com/kishannareshpal/expo-pdf/issues"
|
|
33
|
-
},
|
|
3
|
+
"version": "0.2.1",
|
|
34
4
|
"author": "Kishan Jadav <kishan_jadav@hotmail.com> (https://github.com/kishannareshpal)",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"dependencies": {},
|
|
5
|
+
"repository": "https://github.com/kishannareshpal/expo-pdf",
|
|
6
|
+
"main": "build/index.js",
|
|
38
7
|
"devDependencies": {
|
|
39
8
|
"@types/react": "~19.1.0",
|
|
40
9
|
"expo": "^54.0.27",
|
|
@@ -49,6 +18,22 @@
|
|
|
49
18
|
"react": "*",
|
|
50
19
|
"react-native": "*"
|
|
51
20
|
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/kishannareshpal/expo-pdf/issues"
|
|
23
|
+
},
|
|
24
|
+
"description": "A cross-platform, performant PDF viewer component for React Native and Expo",
|
|
25
|
+
"homepage": "https://github.com/kishannareshpal/expo-pdf#readme",
|
|
26
|
+
"keywords": [
|
|
27
|
+
"react-native",
|
|
28
|
+
"expo",
|
|
29
|
+
"@kishannareshpal/expo-pdf",
|
|
30
|
+
"rn-pdf",
|
|
31
|
+
"react-native-pdf",
|
|
32
|
+
"expo-pdf",
|
|
33
|
+
"pdf",
|
|
34
|
+
"expopdf"
|
|
35
|
+
],
|
|
36
|
+
"license": "MIT",
|
|
52
37
|
"lint-staged": {
|
|
53
38
|
"*.{js,jsx,ts,tsx,json,css,md,yml,yaml}": [
|
|
54
39
|
"prettier --write"
|
|
@@ -56,5 +41,19 @@
|
|
|
56
41
|
"*.{java,kt,swift,h,m,mm}": [
|
|
57
42
|
"prettier --write"
|
|
58
43
|
]
|
|
59
|
-
}
|
|
60
|
-
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "bun run em build",
|
|
47
|
+
"clean": "bun run em clean",
|
|
48
|
+
"lint": "bun run em lint",
|
|
49
|
+
"test": "bun run em test",
|
|
50
|
+
"prepublishOnly": "bun run em prepublishOnly",
|
|
51
|
+
"em": "bunx expo-module",
|
|
52
|
+
"open:ios": "xed example/ios",
|
|
53
|
+
"open:android": "open -a \"Android Studio\" example/android",
|
|
54
|
+
"format": "bunx prettier --write .",
|
|
55
|
+
"format:check": "bunx prettier --check .",
|
|
56
|
+
"prepare": "bunx husky; bun run em prepare"
|
|
57
|
+
},
|
|
58
|
+
"types": "build/index.d.ts"
|
|
59
|
+
}
|
package/src/pdf-view.tsx
CHANGED
|
@@ -13,11 +13,12 @@ type BaseProps = {
|
|
|
13
13
|
uri: string;
|
|
14
14
|
password?: string;
|
|
15
15
|
pagingEnabled?: boolean
|
|
16
|
-
|
|
16
|
+
doubleTapToZoom?: boolean
|
|
17
17
|
horizontal?: boolean
|
|
18
18
|
pageGap?: number
|
|
19
19
|
contentPadding?: ContentPadding
|
|
20
20
|
fitMode?: FitMode
|
|
21
|
+
autoScale?: boolean
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
type NativePdfViewProps = BaseProps & {
|
|
@@ -47,13 +48,14 @@ export const PdfView = ({
|
|
|
47
48
|
<NativePdfView
|
|
48
49
|
style={[styles.container, style]}
|
|
49
50
|
uri={props.uri}
|
|
50
|
-
|
|
51
|
+
doubleTapToZoom={props.doubleTapToZoom}
|
|
51
52
|
horizontal={props.horizontal}
|
|
52
53
|
pageGap={props.pageGap}
|
|
53
54
|
pagingEnabled={props.pagingEnabled}
|
|
54
55
|
password={props.password}
|
|
55
56
|
contentPadding={props.contentPadding}
|
|
56
57
|
fitMode={props.fitMode}
|
|
58
|
+
autoScale={props.autoScale}
|
|
57
59
|
onLoadComplete={forwardNativeEventTo(onLoadComplete)}
|
|
58
60
|
onPageChanged={forwardNativeEventTo(onPageChanged)}
|
|
59
61
|
onError={forwardNativeEventTo(onError)}
|