@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 CHANGED
@@ -1,13 +1,27 @@
1
1
  # Changelog
2
2
 
3
- ## Unpublished
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
- This project adheres to a code of conduct that all contributors are expected to follow. Please be respectful and constructive in all interactions.
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. **Fork the repository** on GitHub
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
- 3. **Add the upstream repository**:
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
+ ![NPM Version](https://img.shields.io/npm/v/%40kishannareshpal%2Fexpo-pdf?style=flat&logo=npm&label=%40kishannareshpal%2Fexpo-pdf)
4
+ ![GitHub Repo stars](https://img.shields.io/github/stars/kishannareshpal/expo-pdf?style=flat)
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
- ### Core features
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
- - [X] Supports Android and iOS
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
- - [x] Load PDFs from remote URLs or local file paths
13
- - [x] Supports local file URIs on android and iOS and ContentResolver URIs on android.
14
- - [x] Remote URLs cannot be passed directly to the PdfView component. You must download it and then use the local file URI to preview.
15
- - [X] Pinch-to-zoom / double-tap-to-zoom and drag gestures
16
- - [x] Support for password-protected PDFs
17
- - [x] Horizontal and vertical reading modes
18
- - [x] Support for content-insets
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
- ### Installation
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
- # bun add @kishannareshpal/expo-pdf
34
- # pnpm add @kishannareshpal/expo-pdf
33
+ ```bash
34
+ npx expo install @kishannareshpal/expo-pdf
35
35
  ```
36
36
 
37
- ### Usage
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
- #### Use a locally bundled PDF
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>(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].downloadAsync()
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
- <PdfView
64
- style={{ flex: 1 }}
65
- uri={uri}
66
- />
67
- )
68
- }
80
+ return <PdfView style={{ flex: 1 }} uri={uri} />;
81
+ };
69
82
  ```
70
83
 
71
- #### Load a file picked using a system picker
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
- #### Use remote URLs
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
- ### API
162
-
163
- | Props | Required | Type | Description | Default |
164
- | ------------------------ | -------- | --------------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------ |
165
- | `uri` | Required | `string` | PDF document URL or local file path. | - |
166
- | `password` | No | `string` | PDF document URL or local file path. | `undefined` |
167
- | `pagingEnabled` | No | `boolean` | PDF document URL or local file path. | `false` |
168
- | `disableDoubleTapToZoom` | No | `boolean` | PDF document URL or local file path. | `false` |
169
- | `horizontal` | No | `boolean` | PDF document URL or local file path. | `false` |
170
- | `pageGap` | No | `number` | PDF document URL or local file path. | `0` |
171
- | `contentPadding` | No | [`ContentPadding`](#contentpadding) | PDF document URL or local file path. | `{ top: 0, left: 0, right: 0, bottom: 0 }` |
172
- | `fitMode` | No | [`FitMode`](#fitmode) | PDF document URL or local file path. | `"width"` |
173
- | `onLoadComplete` | No | [`(OnLoadCompleteEventPayload) => void`](#onloadcompleteeventpayload) | Triggered once the document has been fully loaded. | - |
174
- | `onPageChanged` | No | [`(OnPageChangedPayload) => void`](#onpagechangedeventpayload) | Triggered when the user navigates to a different page. | - |
175
- | `onError` | No | [`(OnErrorPayload) => void`](#onerroreventpayload) | Triggered when the PDF fails to load or render. | - |
176
-
177
-
178
- #### API Reference
179
-
180
- ##### `ContentPadding`
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) =&gt; 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) =&gt; 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) =&gt; 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
- ##### `FitMode`
285
+ #### `FitMode`
187
286
 
188
287
  ```ts
189
- "width" | "height" | "both"
288
+ 'width' | 'height' | 'both';
190
289
  ```
191
290
 
192
- ##### `OnLoadCompleteEventPayload`
291
+ #### `OnLoadCompleteEventPayload`
193
292
 
194
293
  ```ts
195
- { pageCount: number }
294
+ {
295
+ pageCount: number;
296
+ }
196
297
  ```
197
298
 
198
- ##### `OnPageChangedEventPayload`
299
+ #### `OnPageChangedEventPayload`
199
300
 
200
301
  ```ts
201
302
  { pageIndex: number, pageCount: number }
202
303
  ```
203
304
 
204
- ##### `OnErrorEventPayload`
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
- ### Contributing
314
+ ## Contributing
214
315
 
215
316
  Contributions are welcome!
216
317
 
217
- #### How to contribute
318
+ ### How to contribute
218
319
 
219
320
  Please read [CONTRIBUTING.md](./CONTRIBUTING.md)
220
321
 
221
- #### Publishing a new version to the registry
322
+ ### How to publish a new version to the registry
222
323
 
223
- > [!NOTE]
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
@@ -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.05.5'
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("disableDoubleTapToZoom") { view: KJExpoPdfView, disabled: Boolean? ->
40
- view.setDoubleTapZoomEnabled(disabled != true)
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.FitPolicy
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 = if (rect != null) {
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: Int = 0
9
+ val left: Float = 0f
10
10
 
11
11
  @Field
12
- val top: Int = 0
12
+ val top: Float = 0f
13
13
 
14
14
  @Field
15
- val right: Int = 0
15
+ val right: Float = 0f
16
16
 
17
17
  @Field
18
- val bottom: Int = 0
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
  }
@@ -9,11 +9,12 @@ type BaseProps = {
9
9
  uri: string;
10
10
  password?: string;
11
11
  pagingEnabled?: boolean;
12
- disableDoubleTapToZoom?: boolean;
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;
@@ -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,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,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,sBAiBd,CAAA"}
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} disableDoubleTapToZoom={props.disableDoubleTapToZoom} horizontal={props.horizontal} pageGap={props.pageGap} pagingEnabled={props.pagingEnabled} password={props.password} contentPadding={props.contentPadding} fitMode={props.fitMode} onLoadComplete={forwardNativeEventTo(onLoadComplete)} onPageChanged={forwardNativeEventTo(onPageChanged)} onError={forwardNativeEventTo(onError)}/>);
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: {
@@ -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;AAuB/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,sBAAsB,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,CACrD,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,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 disableDoubleTapToZoom?: boolean\n horizontal?: boolean\n pageGap?: number\n contentPadding?: ContentPadding\n fitMode?: FitMode\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 disableDoubleTapToZoom={props.disableDoubleTapToZoom}\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 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"]}
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("disableDoubleTapToZoom") { (view: KJExpoPdfView, disabled: Bool?) in
31
- view.setDoubleTapZoomEnabled(disabled != true)
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, gapPx: Int?) in
39
- view.setPageGap(gapPx)
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
  }
@@ -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
- // Maintain insets on rotation, but don't reset reading position
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, resetOffset: false)
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, resetOffset: false)
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, resetOffset: false)
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
- // Dispatch async to allow PDFView to finish its initial layout
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, resetOffset: Bool = false) {
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 // Prevent zooming out further than the fit
47
+ self.minScaleFactor = targetScale // Prevent zooming out further than the fit
47
48
  self.scaleFactor = targetScale
48
49
  }
49
-
50
- self.applyContentPadding(contentPadding, resetOffset: resetOffset)
50
+
51
+ self.applyContentPadding(contentPadding, resetScrollOffset: resetScrollOffset)
51
52
  }
52
-
53
- func applyContentPadding(_ contentPadding: UIEdgeInsets, resetOffset: Bool = false) {
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 resetOffset {
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, tapGesture.numberOfTapsRequired == 2 {
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, tapGesture.numberOfTapsRequired == 2 {
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.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
- "license": "MIT",
36
- "homepage": "https://github.com/kishannareshpal/expo-pdf#readme",
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
- disableDoubleTapToZoom?: boolean
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
- disableDoubleTapToZoom={props.disableDoubleTapToZoom}
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)}