@kishannareshpal/expo-pdf 0.1.0 → 0.2.0
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 +15 -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/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/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!--
|
|
4
|
+
## x.y.z - YYYY-MM-DD
|
|
4
5
|
|
|
5
6
|
### 🛠 Breaking changes
|
|
7
|
+
- [ios|android] Describe change or paste PR title (<link to pr or if commit if no pr> by @username)
|
|
8
|
+
|
|
6
9
|
### 🎉 New features
|
|
7
10
|
### 🐛 Bug fixes
|
|
8
11
|
### 💡 Others
|
|
9
12
|
|
|
10
13
|
_This version does not introduce any user-facing changes._
|
|
14
|
+
-->
|
|
15
|
+
|
|
16
|
+
## 0.2.0 - 2026-01-09
|
|
17
|
+
|
|
18
|
+
### 🎉 New features
|
|
19
|
+
|
|
20
|
+
- Add support for the `autoScale` prop to control scaling on view resize (https://github.com/kishannareshpal/expo-pdf/pull/5 by @kishannareshpal)
|
|
21
|
+
|
|
22
|
+
### 💡 Others
|
|
23
|
+
|
|
24
|
+
- Added a new example for bottom sheet to showcase the use of the new `autoScale` prop (https://github.com/kishannareshpal/expo-pdf/pull/6 by @kishannareshpal)
|
|
11
25
|
|
|
12
26
|
## 0.1.0 - 2026-01-06
|
|
13
27
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -2,56 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
Thank you for your interest in contributing to `@kishannarehpal/expo-pdf`! This document provides guidelines and instructions for contributing to the project.
|
|
4
4
|
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Contributing to @kishannareshpal/expo-pdf](#contributing-to-kishannareshpalexpo-pdf)
|
|
8
|
-
- [Table of Contents](#table-of-contents)
|
|
9
|
-
- [Code of Conduct](#code-of-conduct)
|
|
10
|
-
- [Getting Started](#getting-started)
|
|
11
|
-
- [Development Setup](#development-setup)
|
|
12
|
-
- [Prerequisites](#prerequisites)
|
|
13
|
-
- [Installation](#installation)
|
|
14
|
-
- [Edit the module source code](#edit-the-module-source-code)
|
|
15
|
-
- [TypeScript](#typescript)
|
|
16
|
-
- [Android](#android)
|
|
17
|
-
- [iOS](#ios)
|
|
18
|
-
- [Resources](#resources)
|
|
19
|
-
- [Running the Example App](#running-the-example-app)
|
|
20
|
-
- [Code Style Guidelines](#code-style-guidelines)
|
|
21
|
-
- [Testing](#testing)
|
|
22
|
-
- [Manual Testing](#manual-testing)
|
|
23
|
-
- [Submitting Changes](#submitting-changes)
|
|
24
|
-
- [Before Submitting](#before-submitting)
|
|
25
|
-
- [Pull Request Process](#pull-request-process)
|
|
26
|
-
- [Pull Request Guidelines](#pull-request-guidelines)
|
|
27
|
-
- [Reporting Bugs](#reporting-bugs)
|
|
28
|
-
- [Requesting Features](#requesting-features)
|
|
29
|
-
- [Questions?](#questions)
|
|
30
|
-
|
|
31
5
|
## Code of Conduct
|
|
32
6
|
|
|
33
|
-
|
|
7
|
+
Please be respectful and constructive in all interactions.
|
|
34
8
|
|
|
35
9
|
## Getting Started
|
|
36
10
|
|
|
37
11
|
Before you begin:
|
|
38
12
|
|
|
39
|
-
1.
|
|
40
|
-
2. **Clone your fork** locally:
|
|
13
|
+
1. Fork and clone the repo:
|
|
41
14
|
```bash
|
|
42
15
|
git clone https://github.com/your-username/expo-pdf.git
|
|
43
16
|
cd expo-pdf
|
|
44
17
|
```
|
|
45
|
-
|
|
18
|
+
2. Add the upstream repository:
|
|
46
19
|
```bash
|
|
47
20
|
git remote add upstream https://github.com/kishannareshpal/expo-pdf.git
|
|
48
21
|
```
|
|
22
|
+
3. Install dependencies
|
|
23
|
+
```bash
|
|
24
|
+
bun install
|
|
25
|
+
```
|
|
49
26
|
|
|
50
27
|
## Development Setup
|
|
51
28
|
|
|
52
29
|
### Prerequisites
|
|
53
30
|
|
|
54
|
-
- **bun** (https://bun.com
|
|
31
|
+
- **bun** (https://bun.com)
|
|
55
32
|
- **For iOS development**:
|
|
56
33
|
- macOS (required)
|
|
57
34
|
- Xcode (latest stable version)
|
|
@@ -68,6 +45,7 @@ Before you begin:
|
|
|
68
45
|
```bash
|
|
69
46
|
bun install
|
|
70
47
|
```
|
|
48
|
+
|
|
71
49
|
2. **Install example app dependencies**:
|
|
72
50
|
|
|
73
51
|
```bash
|
|
@@ -111,12 +89,14 @@ The `example/` directory contains a test app to develop and test the changes you
|
|
|
111
89
|
cd example
|
|
112
90
|
bun start
|
|
113
91
|
```
|
|
92
|
+
|
|
114
93
|
2. Install and run the app on iOS:
|
|
115
94
|
|
|
116
95
|
```bash
|
|
117
96
|
cd example
|
|
118
97
|
bun ios
|
|
119
98
|
```
|
|
99
|
+
|
|
120
100
|
3. Install and run the app on Android:
|
|
121
101
|
|
|
122
102
|
```bash
|
|
@@ -127,23 +107,19 @@ The `example/` directory contains a test app to develop and test the changes you
|
|
|
127
107
|
### Code Style Guidelines
|
|
128
108
|
|
|
129
109
|
1. **TypeScript/React**:
|
|
130
|
-
|
|
131
110
|
- Use functional components with hooks
|
|
132
111
|
- Prefer `const` over `let` when possible
|
|
133
112
|
- Use meaningful, self-documenting variable and function names
|
|
134
113
|
- Add TSDoc comments for public APIs when necessary
|
|
135
114
|
2. **Swift**:
|
|
136
|
-
|
|
137
115
|
- Follow Swift style guide conventions
|
|
138
116
|
- Use meaningful, self-documenting names for classes, functions, and variables
|
|
139
117
|
- Add documentation comments for public APIs
|
|
140
118
|
3. **Kotlin**:
|
|
141
|
-
|
|
142
119
|
- Follow Kotlin style guide conventions
|
|
143
120
|
- Use meaningful names for classes, functions, and variables
|
|
144
121
|
- Add KDoc comments for public APIs
|
|
145
122
|
4. **General**:
|
|
146
|
-
|
|
147
123
|
- Keep functions focused and small
|
|
148
124
|
- Write self-documenting code
|
|
149
125
|
- Comment complex logic
|
|
@@ -161,7 +137,6 @@ The `example/` directory contains a test app to develop and test the changes you
|
|
|
161
137
|
### Before Submitting
|
|
162
138
|
|
|
163
139
|
1. **Update the CHANGELOG.md**:
|
|
164
|
-
|
|
165
140
|
- Add your changes under the appropriate version
|
|
166
141
|
- Use the following format:
|
|
167
142
|
- `### 🎉 New features` for new features
|
|
@@ -169,7 +144,6 @@ The `example/` directory contains a test app to develop and test the changes you
|
|
|
169
144
|
- `### 💡 Others` for other changes
|
|
170
145
|
- `### 🛠 Breaking changes` for breaking changes
|
|
171
146
|
2. **Test your changes**:
|
|
172
|
-
|
|
173
147
|
- Test on iOS
|
|
174
148
|
- Test on Android
|
|
175
149
|
- Verify the example app works correctly
|
|
@@ -183,12 +157,13 @@ The `example/` directory contains a test app to develop and test the changes you
|
|
|
183
157
|
# or
|
|
184
158
|
git checkout -b username/your-bug-fix
|
|
185
159
|
```
|
|
186
|
-
2. **Make your changes**:
|
|
187
160
|
|
|
161
|
+
2. **Make your changes**:
|
|
188
162
|
- Write clean, well-documented code
|
|
189
163
|
- Follow existing code patterns
|
|
190
164
|
- Add tests if applicable
|
|
191
165
|
- Update documentation as needed
|
|
166
|
+
|
|
192
167
|
3. **Commit your changes**:
|
|
193
168
|
|
|
194
169
|
```bash
|
|
@@ -197,7 +172,6 @@ The `example/` directory contains a test app to develop and test the changes you
|
|
|
197
172
|
```
|
|
198
173
|
|
|
199
174
|
Use conventional commit messages:
|
|
200
|
-
|
|
201
175
|
- `feat:` for new features
|
|
202
176
|
- `fix:` for bug fixes
|
|
203
177
|
- `docs:` for documentation changes
|
|
@@ -205,13 +179,14 @@ The `example/` directory contains a test app to develop and test the changes you
|
|
|
205
179
|
- `refactor:` for code refactoring
|
|
206
180
|
- `test:` for adding tests
|
|
207
181
|
- `chore:` for maintenance tasks
|
|
182
|
+
|
|
208
183
|
4. **Push to your fork**:
|
|
209
184
|
|
|
210
185
|
```bash
|
|
211
186
|
git push origin feature/your-feature-name
|
|
212
187
|
```
|
|
213
|
-
5. **Create a Pull Request**:
|
|
214
188
|
|
|
189
|
+
5. **Create a Pull Request**:
|
|
215
190
|
- Go to the GitHub repository
|
|
216
191
|
- Click "New Pull Request"
|
|
217
192
|
- Select your branch
|
|
@@ -220,8 +195,8 @@ The `example/` directory contains a test app to develop and test the changes you
|
|
|
220
195
|
- Testing performed
|
|
221
196
|
- Screenshots/videos if applicable
|
|
222
197
|
- Related issues (if any)
|
|
223
|
-
6. **Respond to feedback**:
|
|
224
198
|
|
|
199
|
+
6. **Respond to feedback**:
|
|
225
200
|
- Address any review comments
|
|
226
201
|
- Make requested changes
|
|
227
202
|
- Keep the PR up to date with `main`
|
package/README.md
CHANGED
|
@@ -1,74 +1,87 @@
|
|
|
1
1
|
# `@kishannareshpal/expo-pdf`
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
3
6
|
A cross-platform, high-performance PDF viewer for React Native and Expo, built on top of native PDF rendering engines.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
| [iOS](./docs/demo-ios.mp4) | [Android](./docs/demo-android.mp4) |
|
|
9
|
+
| ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
10
|
+
| <video src="https://github.com/user-attachments/assets/57000914-4f7a-4139-9079-9cfd8ece740b" /> | <video src="https://github.com/user-attachments/assets/14faf4f1-b7cc-4e8b-97a5-c0da94fd26c2" /> |
|
|
11
|
+
|
|
12
|
+
## Features
|
|
6
13
|
|
|
7
|
-
-
|
|
14
|
+
- Supports Android and iOS
|
|
8
15
|
- Uses Apple's [`PDFKit`](https://developer.apple.com/documentation/pdfkit/pdfview) on iOS
|
|
9
16
|
- Uses [kishannareshpal/AndroidPdfViewer](https://github.com/kishannareshpal/AndroidPdfViewer) on Android which is a maintained
|
|
10
17
|
fork of [barteksc/AndroidPdfViewerV2](https://github.com/kishannareshpal/AndroidPdfViewer) which uses the open-source [PDFium](https://pdfium.googlesource.com/pdfium/+/HEAD/docs/getting-started.md) PDF rendering engine.
|
|
11
18
|
- Note: We'll be looking to switch to [`androidx.pdf`](https://developer.android.com/jetpack/androidx/releases/pdf) on Android once that becomes stable.
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
### Demo
|
|
21
|
-
|
|
22
|
-
| [iOS](./docs/demo-ios.mp4) | [Android](./docs/demo-android.mp4) |
|
|
23
|
-
| ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
24
|
-
| <video src="https://github.com/user-attachments/assets/6c88deb7-7801-4e3a-b93e-88f9688435a1" /> | <video src="https://github.com/user-attachments/assets/ce9bd00f-0a4e-47ec-800c-cbdcba5c5554" /> |
|
|
19
|
+
- Load PDFs from remote URLs or local file paths
|
|
20
|
+
- Supports local file URIs on android and iOS and ContentResolver URIs on android.
|
|
21
|
+
- Remote URLs cannot be passed directly to the PdfView component. You must download it and then use the local file URI to preview.
|
|
22
|
+
- Pinch-to-zoom / double-tap-to-zoom and drag gestures
|
|
23
|
+
- Support for password-protected PDFs
|
|
24
|
+
- Horizontal and vertical reading modes
|
|
25
|
+
- Support for content-insets
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
## Installation
|
|
27
28
|
|
|
28
29
|
This package works with both Expo and framework-less React Native projects but Expo provides a more streamlined experience.
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
npm install @kishannareshpal/expo-pdf
|
|
31
|
+
### Expo
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
```bash
|
|
34
|
+
npx expo install @kishannareshpal/expo-pdf
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
###
|
|
37
|
+
### Bare React Native
|
|
38
|
+
|
|
39
|
+
1. Install this package
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm add @kishannareshpal/expo-pdf
|
|
43
|
+
|
|
44
|
+
# bun add @kishannareshpal/expo-pdf
|
|
45
|
+
# pnpm add @kishannareshpal/expo-pdf
|
|
46
|
+
# yarn add @kishannareshpal/expo-pdf
|
|
47
|
+
```
|
|
38
48
|
|
|
39
|
-
|
|
49
|
+
2. Install CocoaPods dependencies
|
|
50
|
+
```bash
|
|
51
|
+
npx pod-install
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Use a locally bundled PDF
|
|
40
57
|
|
|
41
58
|
```jsx
|
|
42
|
-
import { PdfView } from '@kishannareshpal/expo-pdf'
|
|
59
|
+
import { PdfView } from '@kishannareshpal/expo-pdf';
|
|
43
60
|
|
|
44
61
|
export const App = () => {
|
|
45
62
|
const [assets] = useAssets([require('./assets/sample.pdf')]);
|
|
46
|
-
const [uri, setUri] = useState<string | null>
|
|
63
|
+
const [uri, setUri] = (useState < string) | (null > null);
|
|
47
64
|
|
|
48
65
|
useEffect(() => {
|
|
49
66
|
if (!assets?.length) {
|
|
50
67
|
return;
|
|
51
68
|
}
|
|
52
69
|
|
|
53
|
-
assets[0]
|
|
70
|
+
assets[0]
|
|
71
|
+
.downloadAsync()
|
|
54
72
|
.then((asset) => setUri(asset.localUri))
|
|
55
|
-
.catch(console.error)
|
|
56
|
-
}, [assets])
|
|
73
|
+
.catch(console.error);
|
|
74
|
+
}, [assets]);
|
|
57
75
|
|
|
58
76
|
if (!uri) {
|
|
59
77
|
return null;
|
|
60
78
|
}
|
|
61
79
|
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
style={{ flex: 1 }}
|
|
65
|
-
uri={uri}
|
|
66
|
-
/>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
80
|
+
return <PdfView style={{ flex: 1 }} uri={uri} />;
|
|
81
|
+
};
|
|
69
82
|
```
|
|
70
83
|
|
|
71
|
-
|
|
84
|
+
### Load a file picked using a system picker
|
|
72
85
|
|
|
73
86
|
```jsx
|
|
74
87
|
import { File } from 'expo-file-system';
|
|
@@ -95,11 +108,11 @@ export const App = () => {
|
|
|
95
108
|
return (
|
|
96
109
|
<View>
|
|
97
110
|
<Button onPress={pickFile}>Pick a file</Button>
|
|
98
|
-
|
|
111
|
+
|
|
99
112
|
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
100
113
|
{uri ? (
|
|
101
|
-
<PdfView
|
|
102
|
-
style={{ flex: 1 }}
|
|
114
|
+
<PdfView
|
|
115
|
+
style={{ flex: 1 }}
|
|
103
116
|
uri={uri}
|
|
104
117
|
/>
|
|
105
118
|
) : (
|
|
@@ -111,7 +124,7 @@ export const App = () => {
|
|
|
111
124
|
}
|
|
112
125
|
```
|
|
113
126
|
|
|
114
|
-
|
|
127
|
+
### Use remote URLs
|
|
115
128
|
|
|
116
129
|
```jsx
|
|
117
130
|
import { File } from 'expo-file-system';
|
|
@@ -142,11 +155,11 @@ export const App = () => {
|
|
|
142
155
|
<Button onPress={() => loadFromUrl("https://pdfobject.com/pdf/sample.pdf")}>
|
|
143
156
|
Download and load from URL
|
|
144
157
|
</Button>
|
|
145
|
-
|
|
158
|
+
|
|
146
159
|
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
147
160
|
{uri ? (
|
|
148
|
-
<PdfView
|
|
149
|
-
style={{ flex: 1 }}
|
|
161
|
+
<PdfView
|
|
162
|
+
style={{ flex: 1 }}
|
|
150
163
|
uri={uri}
|
|
151
164
|
/>
|
|
152
165
|
) : (
|
|
@@ -158,75 +171,164 @@ export const App = () => {
|
|
|
158
171
|
}
|
|
159
172
|
```
|
|
160
173
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
174
|
+
## API
|
|
175
|
+
|
|
176
|
+
<table>
|
|
177
|
+
<thead>
|
|
178
|
+
<tr>
|
|
179
|
+
<th>Props</th>
|
|
180
|
+
<th>Required</th>
|
|
181
|
+
<th>Type</th>
|
|
182
|
+
<th>Description</th>
|
|
183
|
+
<th>Default</th>
|
|
184
|
+
</tr>
|
|
185
|
+
</thead>
|
|
186
|
+
<tbody>
|
|
187
|
+
<tr>
|
|
188
|
+
<td><code>uri</code></td>
|
|
189
|
+
<td>Required</td>
|
|
190
|
+
<td><code>string</code></td>
|
|
191
|
+
<td>PDF document URL or local file path.</td>
|
|
192
|
+
<td>-</td>
|
|
193
|
+
</tr>
|
|
194
|
+
<tr>
|
|
195
|
+
<td><code>password</code></td>
|
|
196
|
+
<td>No</td>
|
|
197
|
+
<td><code>string</code></td>
|
|
198
|
+
<td>PDF document URL or local file path.</td>
|
|
199
|
+
<td><code>undefined</code></td>
|
|
200
|
+
</tr>
|
|
201
|
+
<tr>
|
|
202
|
+
<td><code>pagingEnabled</code></td>
|
|
203
|
+
<td>No</td>
|
|
204
|
+
<td><code>boolean</code></td>
|
|
205
|
+
<td>PDF document URL or local file path.</td>
|
|
206
|
+
<td><code>false</code></td>
|
|
207
|
+
</tr>
|
|
208
|
+
<tr>
|
|
209
|
+
<td><code>doubleTapToZoom</code></td>
|
|
210
|
+
<td>No</td>
|
|
211
|
+
<td><code>boolean</code></td>
|
|
212
|
+
<td>PDF document URL or local file path.</td>
|
|
213
|
+
<td><code>true</code></td>
|
|
214
|
+
</tr>
|
|
215
|
+
<tr>
|
|
216
|
+
<td><code>horizontal</code></td>
|
|
217
|
+
<td>No</td>
|
|
218
|
+
<td><code>boolean</code></td>
|
|
219
|
+
<td>PDF document URL or local file path.</td>
|
|
220
|
+
<td><code>false</code></td>
|
|
221
|
+
</tr>
|
|
222
|
+
<tr>
|
|
223
|
+
<td><code>pageGap</code></td>
|
|
224
|
+
<td>No</td>
|
|
225
|
+
<td><code>number</code></td>
|
|
226
|
+
<td>PDF document URL or local file path.</td>
|
|
227
|
+
<td><code>0</code></td>
|
|
228
|
+
</tr>
|
|
229
|
+
<tr>
|
|
230
|
+
<td><code>contentPadding</code></td>
|
|
231
|
+
<td>No</td>
|
|
232
|
+
<td><a href="#contentpadding"><code>ContentPadding</code></a></td>
|
|
233
|
+
<td>PDF document URL or local file path.</td>
|
|
234
|
+
<td><code>{ top: 0, left: 0, right: 0, bottom: 0 }</code></td>
|
|
235
|
+
</tr>
|
|
236
|
+
<tr>
|
|
237
|
+
<td><code>fitMode</code></td>
|
|
238
|
+
<td>No</td>
|
|
239
|
+
<td><a href="#fitmode"><code>FitMode</code></a></td>
|
|
240
|
+
<td>PDF document URL or local file path.</td>
|
|
241
|
+
<td><code>"width"</code></td>
|
|
242
|
+
</tr>
|
|
243
|
+
<tr>
|
|
244
|
+
<td><code>autoScale</code></td>
|
|
245
|
+
<td>No</td>
|
|
246
|
+
<td><code>boolean</code></td>
|
|
247
|
+
<td>
|
|
248
|
+
Whether the document should auto-scale when itself or its parent view changes its layout metrics.
|
|
249
|
+
Please note that the initial render will always auto-scale to fit whatever <code>fitMode</code> is set to.
|
|
250
|
+
This prop only affects subsequent layout changes.</td>
|
|
251
|
+
<td><code>true</code></td>
|
|
252
|
+
</tr>
|
|
253
|
+
<tr>
|
|
254
|
+
<td><code>onLoadComplete</code></td>
|
|
255
|
+
<td>No</td>
|
|
256
|
+
<td><a href="#onloadcompleteeventpayload"><code>(OnLoadCompleteEventPayload) => void</code></a></td>
|
|
257
|
+
<td>Triggered once the document has been fully loaded.</td>
|
|
258
|
+
<td>-</td>
|
|
259
|
+
</tr>
|
|
260
|
+
<tr>
|
|
261
|
+
<td><code>onPageChanged</code></td>
|
|
262
|
+
<td>No</td>
|
|
263
|
+
<td><a href="#onpagechangedeventpayload"><code>(OnPageChangedPayload) => void</code></a></td>
|
|
264
|
+
<td>Triggered when the user navigates to a different page.</td>
|
|
265
|
+
<td>-</td>
|
|
266
|
+
</tr>
|
|
267
|
+
<tr>
|
|
268
|
+
<td><code>onError</code></td>
|
|
269
|
+
<td>No</td>
|
|
270
|
+
<td><a href="#onerroreventpayload"><code>(OnErrorPayload) => void</code></a></td>
|
|
271
|
+
<td>Triggered when the PDF fails to load or render.</td>
|
|
272
|
+
<td>-</td>
|
|
273
|
+
</tr>
|
|
274
|
+
</tbody>
|
|
275
|
+
</table>
|
|
276
|
+
|
|
277
|
+
### API Reference
|
|
278
|
+
|
|
279
|
+
#### `ContentPadding`
|
|
181
280
|
|
|
182
281
|
```ts
|
|
183
282
|
{ top?: number, left?: number, right?: number, bottom?: number }
|
|
184
283
|
```
|
|
185
284
|
|
|
186
|
-
|
|
285
|
+
#### `FitMode`
|
|
187
286
|
|
|
188
287
|
```ts
|
|
189
|
-
|
|
288
|
+
'width' | 'height' | 'both';
|
|
190
289
|
```
|
|
191
290
|
|
|
192
|
-
|
|
291
|
+
#### `OnLoadCompleteEventPayload`
|
|
193
292
|
|
|
194
293
|
```ts
|
|
195
|
-
{
|
|
294
|
+
{
|
|
295
|
+
pageCount: number;
|
|
296
|
+
}
|
|
196
297
|
```
|
|
197
298
|
|
|
198
|
-
|
|
299
|
+
#### `OnPageChangedEventPayload`
|
|
199
300
|
|
|
200
301
|
```ts
|
|
201
302
|
{ pageIndex: number, pageCount: number }
|
|
202
303
|
```
|
|
203
304
|
|
|
204
|
-
|
|
305
|
+
#### `OnErrorEventPayload`
|
|
205
306
|
|
|
206
307
|
```ts
|
|
207
308
|
{
|
|
208
|
-
code: 'invalid_url' | 'invalid_document' | 'password_required' | 'password_incorrect',
|
|
309
|
+
code: 'invalid_url' | 'invalid_document' | 'password_required' | 'password_incorrect',
|
|
209
310
|
message: string
|
|
210
311
|
}
|
|
211
312
|
```
|
|
212
313
|
|
|
213
|
-
|
|
314
|
+
## Contributing
|
|
214
315
|
|
|
215
316
|
Contributions are welcome!
|
|
216
317
|
|
|
217
|
-
|
|
318
|
+
### How to contribute
|
|
218
319
|
|
|
219
320
|
Please read [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
220
321
|
|
|
221
|
-
|
|
322
|
+
### How to publish a new version to the registry
|
|
222
323
|
|
|
223
|
-
>
|
|
324
|
+
> **NOTE**
|
|
224
325
|
>
|
|
225
326
|
> This package follows semantic versioning with the format: `major.minor.patch`.
|
|
327
|
+
>
|
|
226
328
|
> - Major version: Increment when making incompatible API changes.
|
|
227
329
|
> - Minor version: Increment when adding new functionality in a backward-compatible way.
|
|
228
330
|
> - Patch version: Increment when fixing bugs in a backward-compatible manner.
|
|
229
|
-
|
|
331
|
+
|
|
230
332
|
1. Bump `package.json` version using one of `npm version patch|minor|major` - this will create a new `tag`.
|
|
231
333
|
2. `git push --tags` to push the new changes including the newly created tag.
|
|
232
334
|
3. Navigate to [Create a New Release](https://github.com/kishannareshpal/expo-pdf/releases/new)
|
|
@@ -237,7 +339,6 @@ Please read [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
|
237
339
|
8. A GitHub action will automatically run to publish the new version of the package to the registry.
|
|
238
340
|
- Monitor the status at [kishannareshpal/expo-pdf/actions](https://github.com/kishannareshpal/expo-pdf/actions)
|
|
239
341
|
|
|
240
|
-
|
|
241
|
-
### License
|
|
342
|
+
## License
|
|
242
343
|
|
|
243
344
|
MIT
|
package/android/build.gradle
CHANGED
|
@@ -47,7 +47,7 @@ dependencies {
|
|
|
47
47
|
// more updates coming from that repo.
|
|
48
48
|
// - com.github.kishannareshpal:AndroidPdfViewer repo is based on PdfiumAndroidKt, a much newer
|
|
49
49
|
// fork of PdfiumAndroid, with better maintenance and updated native libraries.
|
|
50
|
-
implementation 'com.github.kishannareshpal:AndroidPdfViewer:2026.01.
|
|
50
|
+
implementation 'com.github.kishannareshpal:AndroidPdfViewer:2026.01.07.2'
|
|
51
51
|
|
|
52
52
|
// Explicitly define the version of PdfiumAndroidKt so this can be updated independently of
|
|
53
53
|
// AndroidPdfViewer as updates are provided.
|
|
@@ -36,8 +36,8 @@ class KJExpoPdfModule : Module() {
|
|
|
36
36
|
view.setPagingEnabled(enabled)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
Prop("
|
|
40
|
-
view.setDoubleTapZoomEnabled(
|
|
39
|
+
Prop("doubleTapToZoom") { view: KJExpoPdfView, enabled: Boolean? ->
|
|
40
|
+
view.setDoubleTapZoomEnabled(enabled)
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
Prop("horizontal") { view: KJExpoPdfView, enabled: Boolean? ->
|
|
@@ -55,6 +55,10 @@ class KJExpoPdfModule : Module() {
|
|
|
55
55
|
Prop("fitMode") { view: KJExpoPdfView, mode: FitMode? ->
|
|
56
56
|
view.setFitMode(mode)
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
Prop("autoScale") { view: KJExpoPdfView, enabled: Boolean? ->
|
|
60
|
+
view.setAutoScaleEnabled(enabled)
|
|
61
|
+
}
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
@@ -10,17 +10,16 @@ import com.github.barteksc.pdfviewer.PDFView
|
|
|
10
10
|
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
11
11
|
import java.io.FileNotFoundException
|
|
12
12
|
import androidx.core.net.toUri
|
|
13
|
-
import com.github.barteksc.pdfviewer.util.
|
|
13
|
+
import com.github.barteksc.pdfviewer.util.SnapEdge
|
|
14
14
|
import com.kishannareshpal.expopdf.lib.FitMode
|
|
15
15
|
|
|
16
|
-
// TODO: Refresh the content on prop change
|
|
17
|
-
|
|
18
16
|
class KJExpoPdfView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
|
|
19
17
|
companion object {
|
|
20
|
-
internal val DEFAULT_PAGING_ENABLED = false
|
|
21
|
-
internal val DEFAULT_DOUBLE_TAP_ZOOM_ENABLED = true
|
|
22
|
-
internal val DEFAULT_HORIZONTAL_MODE_ENABLED = false
|
|
23
|
-
internal val DEFAULT_PAGE_GAP = 0
|
|
18
|
+
internal const val DEFAULT_PAGING_ENABLED = false
|
|
19
|
+
internal const val DEFAULT_DOUBLE_TAP_ZOOM_ENABLED = true
|
|
20
|
+
internal const val DEFAULT_HORIZONTAL_MODE_ENABLED = false
|
|
21
|
+
internal const val DEFAULT_PAGE_GAP = 0
|
|
22
|
+
internal const val DEFAULT_AUTO_SCALE_ENABLED = true
|
|
24
23
|
internal val DEFAULT_CONTENT_PADDING = Rect(0, 0, 0, 0)
|
|
25
24
|
internal val DEFAULT_FIT_MODE = FitMode.both
|
|
26
25
|
}
|
|
@@ -44,6 +43,7 @@ class KJExpoPdfView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
44
43
|
private var pageGap: Int = DEFAULT_PAGE_GAP
|
|
45
44
|
private var contentPadding: Rect = DEFAULT_CONTENT_PADDING
|
|
46
45
|
private var fitMode: FitMode = DEFAULT_FIT_MODE
|
|
46
|
+
private var autoScaleEnabled: Boolean = DEFAULT_AUTO_SCALE_ENABLED
|
|
47
47
|
|
|
48
48
|
internal val pdfView = PDFView(context, null).apply {
|
|
49
49
|
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
@@ -104,12 +104,7 @@ class KJExpoPdfView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
fun setContentPadding(rect: Rect?) {
|
|
107
|
-
this.contentPadding =
|
|
108
|
-
Rect(rect.left, rect.top, rect.right, rect.bottom)
|
|
109
|
-
} else {
|
|
110
|
-
DEFAULT_CONTENT_PADDING
|
|
111
|
-
}
|
|
112
|
-
|
|
107
|
+
this.contentPadding = rect ?: DEFAULT_CONTENT_PADDING
|
|
113
108
|
this.reloadPdf()
|
|
114
109
|
}
|
|
115
110
|
|
|
@@ -118,6 +113,11 @@ class KJExpoPdfView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
118
113
|
this.reloadPdf()
|
|
119
114
|
}
|
|
120
115
|
|
|
116
|
+
fun setAutoScaleEnabled(enabled: Boolean?) {
|
|
117
|
+
this.autoScaleEnabled = enabled ?: DEFAULT_AUTO_SCALE_ENABLED
|
|
118
|
+
this.reloadPdf()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
121
|
private fun reloadPdf() {
|
|
122
122
|
if (!this.pdfView.isRecycled) {
|
|
123
123
|
this.pdfView.recycle()
|
|
@@ -144,6 +144,7 @@ class KJExpoPdfView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
144
144
|
.enableDoubletap(this.isDoubleTapZoomEnabled)
|
|
145
145
|
.swipeHorizontal(this.isHorizontalModeEnabled)
|
|
146
146
|
.spacing(this.pageGap)
|
|
147
|
+
.autoCenterOnResize(this.autoScaleEnabled)
|
|
147
148
|
.contentPadding(
|
|
148
149
|
this.contentPadding.left,
|
|
149
150
|
this.contentPadding.top,
|
|
@@ -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"]}
|
|
@@ -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.
|
|
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.0",
|
|
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": "expo-module build",
|
|
47
|
+
"clean": "expo-module clean",
|
|
48
|
+
"lint": "expo-module lint",
|
|
49
|
+
"test": "expo-module test",
|
|
50
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
51
|
+
"expo-module": "expo-module",
|
|
52
|
+
"open:ios": "xed example/ios",
|
|
53
|
+
"open:android": "open -a \"Android Studio\" example/android",
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"format:check": "prettier --check .",
|
|
56
|
+
"prepare": "husky; expo-module 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)}
|