@thatkid02/react-native-pdf-viewer 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,30 +3,20 @@
3
3
  [![npm version](https://badge.fury.io/js/@thatkid02%2Freact-native-pdf-viewer.svg)](https://www.npmjs.com/package/@thatkid02/react-native-pdf-viewer)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@thatkid02/react-native-pdf-viewer.svg)](https://www.npmjs.com/package/@thatkid02/react-native-pdf-viewer)
5
5
 
6
- High-performance PDF viewer for React Native, built with [Nitro Modules](https://nitro.margelo.com/) for native rendering performance.
6
+ High-performance PDF viewer for React Native, built with [Nitro Modules](https://nitro.margelo.com/) for native rendering.
7
+
8
+ <p align="center">
9
+ <video src="docs/pdf.mp4" width="300" controls></video>
10
+ </p>
7
11
 
8
12
  ## Features
9
13
 
10
- **High-performance PDF rendering** with native APIs (PDFKit on iOS, PdfRenderer on Android)
11
- **Pinch-to-zoom and pan gestures** - Smooth, native gesture handling
12
- **Thumbnail generation** for page previews
13
- **Local and remote PDF support** (file://, http://, https://)
14
- **Page navigation** with events
15
- **TypeScript support** - Full type safety
16
- ✅ **Memory efficient** - Virtualizes pages, only renders visible content
17
- ✅ **iOS-specific layouts** - Horizontal scroll and paging modes
18
-
19
- ## Platform Support
20
-
21
- | Feature | Android | iOS | Notes |
22
- |---------|---------|-----|-------|
23
- | Local Files (file://) | ✅ | ✅ | |
24
- | Remote URLs (http/https) | ✅ | ✅ | Prefer HTTPS in production |
25
- | Zoom Controls | ✅(2 scale) | ✅ | |
26
- | Page Navigation | ✅ | ✅ | |
27
- | Horizontal Scroll | ❌ | ✅ | iOS only |
28
- | Paging Mode | ❌ | ✅ | iOS only |
29
- | Thumbnail Generation | ✅ | ✅ | Async on both platforms |
14
+ - 📄 **Native PDF rendering** PDFKit (iOS) & PdfRenderer (Android)
15
+ - 🔍 **Pinch-to-zoom & pan** Smooth gesture handling
16
+ - 🖼️ **Thumbnail generation** Async page previews
17
+ - 🌐 **Local & remote files** — `file://`, `http://`, `https://`
18
+ - 📱 **iOS layouts** Horizontal scroll & paging modes
19
+ - 💾 **Memory efficient** Virtualized pages, LRU cache
30
20
 
31
21
  ## Installation
32
22
 
@@ -34,285 +24,180 @@ High-performance PDF viewer for React Native, built with [Nitro Modules](https:/
34
24
  npm install @thatkid02/react-native-pdf-viewer react-native-nitro-modules
35
25
  ```
36
26
 
37
- > `react-native-nitro-modules` is required as this library relies on [Nitro Modules](https://nitro.margelo.com/).
27
+ ### iOS
38
28
 
39
- ## Usage
29
+ ```sh
30
+ cd ios && pod install
31
+ ```
40
32
 
41
- ### Basic Example
33
+ ## Quick Start
42
34
 
43
35
  ```tsx
44
36
  import { PdfViewerView } from '@thatkid02/react-native-pdf-viewer';
45
37
 
46
- export default function App() {
38
+ function App() {
47
39
  return (
48
40
  <PdfViewerView
49
41
  source="https://example.com/document.pdf"
50
42
  style={{ flex: 1 }}
51
- onLoadComplete={(event) => {
52
- console.log('PDF loaded:', event.pageCount, 'pages');
53
- }}
54
- onError={(event) => {
55
- console.error('PDF error:', event.message);
56
- }}
43
+ onLoadComplete={(e) => console.log(`Loaded ${e.pageCount} pages`)}
44
+ onError={(e) => console.error(e.message)}
57
45
  />
58
46
  );
59
47
  }
60
48
  ```
61
49
 
62
- ### Advanced Example with All Features
50
+ ## Usage with Ref
63
51
 
64
- - callback is exposed by nitro modules to avoid re-renders [here](https://nitro.margelo.com/docs/view-components#callbacks-have-to-be-wrapped)
52
+ Callbacks must be wrapped with `callback()` from nitro-modules to avoid re-renders. [Learn more](https://nitro.margelo.com/docs/view-components#callbacks-have-to-be-wrapped)
65
53
 
66
54
  ```tsx
67
- import React, { useRef } from 'react';
68
- import { View, Button, StyleSheet } from 'react-native';
69
- import { PdfViewerView, type PdfViewer } from '@thatkid02/react-native-pdf-viewer';
70
-
71
- export default function AdvancedPdfViewer() {
72
- const pdfRef = useRef<PdfViewer>(null);
73
-
74
- const handleLoadComplete = (event) => {
75
- console.log(`PDF loaded: ${event.pageCount} pages`);
76
- console.log(`Page size: ${event.pageWidth}x${event.pageHeight}`);
77
-
78
- // Generate thumbnails for all pages
79
- pdfRef.current?.generateAllThumbnails();
80
- };
81
-
82
- const handlePageChange = (event) => {
83
- console.log(`Current page: ${event.page + 1} of ${event.pageCount}`);
84
- };
85
-
86
- const handleScaleChange = (event) => {
87
- console.log(`Zoom scale: ${event.scale}`);
88
- };
89
-
90
- const handleThumbnailGenerated = (event) => {
91
- console.log(`Thumbnail for page ${event.page}: ${event.uri}`);
92
- };
93
-
94
- const goToPage = (page: number) => {
95
- pdfRef.current?.goToPage(page);
96
- };
97
-
98
- const zoomIn = () => {
99
- pdfRef.current?.setScale(2.0);
100
- };
101
-
55
+ import { useRef } from 'react';
56
+ import { View, Button } from 'react-native';
57
+ import {
58
+ PdfViewerView,
59
+ callback,
60
+ type PdfViewerRef
61
+ } from '@thatkid02/react-native-pdf-viewer';
62
+
63
+ function PdfScreen() {
64
+ const pdfRef = useRef<PdfViewerRef>(null);
102
65
 
103
66
  return (
104
- <View style={styles.container}>
67
+ <View style={{ flex: 1 }}>
105
68
  <PdfViewerView
106
- hybridRef={callback((ref: PdfViewerRef | null) => {
69
+ hybridRef={callback((ref) => { pdfRef.current = ref; })}
107
70
  source="https://example.com/document.pdf"
108
- style={styles.pdf}
109
- // Layout options
110
- spacing={16}
71
+ style={{ flex: 1 }}
111
72
  enableZoom={true}
112
73
  minScale={0.5}
113
- maxScale={5.0}
114
- // iOS-only options
115
- horizontal={false} // iOS only
116
- enablePaging={false} // iOS only
117
- // Loading indicator
118
- showsActivityIndicator={true}
119
- // Event handlers
120
- onLoadComplete={callback(handleLoadComplete)}
121
- onPageChange={callback(handlePageChange)}
122
- onScaleChange={callback(handleScaleChange)}
123
- onError={callback(handleError)}
124
- onThumbnailGenerated={callback(handleThumbnailGenerated)}
125
- onLoadingChange={callback(handleLoadingChange)}
74
+ maxScale={4.0}
75
+ onLoadComplete={callback((e) => {
76
+ console.log(`${e.pageCount} pages, ${e.pageWidth}x${e.pageHeight}`);
77
+ })}
78
+ onPageChange={callback((e) => {
79
+ console.log(`Page ${e.page} of ${e.pageCount}`);
80
+ })}
81
+ onThumbnailGenerated={callback((e) => {
82
+ console.log(`Thumbnail page ${e.page}: ${e.uri}`);
83
+ })}
126
84
  />
127
85
 
128
- <View style={styles.controls}>
129
- <Button title="Go to Page 5" onPress={() => goToPage(4)} />
130
- <Button title="Zoom In" onPress={zoomIn} />
86
+ <View style={{ flexDirection: 'row', padding: 16, gap: 8 }}>
87
+ <Button title="Page 1" onPress={() => pdfRef.current?.goToPage(0)} />
88
+ <Button title="Zoom 2x" onPress={() => pdfRef.current?.setScale(2)} />
89
+ <Button title="Thumbnails" onPress={() => pdfRef.current?.generateAllThumbnails()} />
131
90
  </View>
132
91
  </View>
133
92
  );
134
93
  }
135
-
136
- const styles = StyleSheet.create({
137
- container: {
138
- flex: 1,
139
- },
140
- pdf: {
141
- flex: 1,
142
- },
143
- controls: {
144
- flexDirection: 'row',
145
- justifyContent: 'space-around',
146
- padding: 16,
147
- },
148
- });
149
94
  ```
150
95
 
151
- ### Loading Local Files
96
+ ## iOS-Only Features
152
97
 
153
98
  ```tsx
154
- <PdfViewerView
155
- source="file:///path/to/document.pdf"
156
- style={{ flex: 1 }}
157
- />
99
+ // Horizontal scrolling
100
+ <PdfViewerView source={url} horizontal={true} />
101
+
102
+ // Paging mode (swipe between pages)
103
+ <PdfViewerView source={url} enablePaging={true} />
104
+
105
+ // Combined
106
+ <PdfViewerView source={url} horizontal={true} enablePaging={true} />
158
107
  ```
159
108
 
160
- ### iOS-Specific Features
109
+ ## Glass UI / Transparent Bars
161
110
 
162
- ```tsx
163
- // Horizontal scrolling (iOS only)
164
- <PdfViewerView
165
- source="https://example.com/document.pdf"
166
- horizontal={true}
167
- style={{ flex: 1 }}
168
- />
111
+ Content scrolls behind transparent headers/toolbars:
169
112
 
170
- // Paging mode with swipe transitions (iOS only)
113
+ ```tsx
171
114
  <PdfViewerView
172
- source="https://example.com/document.pdf"
173
- enablePaging={true}
115
+ source={url}
116
+ contentInsetTop={100} // Header height
117
+ contentInsetBottom={80} // Toolbar height
174
118
  style={{ flex: 1 }}
175
119
  />
176
120
  ```
177
121
 
178
- ## API Reference
179
-
180
- ### Props
122
+ ## Props
181
123
 
182
124
  | Prop | Type | Default | Description |
183
125
  |------|------|---------|-------------|
184
- | `source` | `string` | - | PDF source URI (file://, http://, https://) |
185
- | `spacing` | `number` | `8` | Space between pages in pixels |
186
- | `enableZoom` | `boolean` | `true` | Enable pinch-to-zoom gestures |
126
+ | `source` | `string` | | PDF URI (`file://`, `http://`, `https://`) |
127
+ | `spacing` | `number` | `8` | Space between pages (px) |
128
+ | `enableZoom` | `boolean` | `true` | Enable pinch-to-zoom |
187
129
  | `minScale` | `number` | `0.5` | Minimum zoom scale |
188
130
  | `maxScale` | `number` | `4.0` | Maximum zoom scale |
189
- | `showsActivityIndicator` | `boolean` | `true` | Show loading indicator |
190
- | `horizontal` | `boolean` | `false` | Horizontal scroll (iOS only) |
191
- | `enablePaging` | `boolean` | `false` | Enable paging mode (iOS only) |
192
- | `contentInsetTop` | `number` | `0` | Top content inset (for glass topbars) |
193
- | `contentInsetBottom` | `number` | `0` | Bottom content inset (for glass toolbars) |
194
- | `contentInsetLeft` | `number` | `0` | Left content inset |
195
- | `contentInsetRight` | `number` | `0` | Right content inset |
196
- | `onLoadComplete` | `(event) => void` | - | Called when PDF loads |
197
- | `onPageChange` | `(event) => void` | - | Called when page changes |
198
- | `onScaleChange` | `(event) => void` | - | Called when zoom changes |
199
- | `onError` | `(event) => void` | - | Called on error |
200
- | `onThumbnailGenerated` | `(event) => void` | - | Called when thumbnail is ready |
201
- | `onLoadingChange` | `(event) => void` | - | Called when loading state changes |
202
-
203
- #### Glass UI / Transparent Bars
204
-
205
- Use `contentInset` props to make the PDF scroll behind transparent headers and toolbars:
131
+ | `showsActivityIndicator` | `boolean` | `true` | Show loading spinner |
132
+ | `horizontal` | `boolean` | `false` | Horizontal scroll *(iOS only)* |
133
+ | `enablePaging` | `boolean` | `false` | Paging mode *(iOS only)* |
134
+ | `contentInsetTop` | `number` | `0` | Top inset for glass UI |
135
+ | `contentInsetBottom` | `number` | `0` | Bottom inset for glass UI |
136
+ | `contentInsetLeft` | `number` | `0` | Left inset |
137
+ | `contentInsetRight` | `number` | `0` | Right inset |
138
+
139
+ ## Events
140
+
141
+ | Event | Payload | Description |
142
+ |-------|---------|-------------|
143
+ | `onLoadComplete` | `{ pageCount, pageWidth, pageHeight }` | PDF loaded successfully |
144
+ | `onPageChange` | `{ page, pageCount }` | Visible page changed |
145
+ | `onScaleChange` | `{ scale }` | Zoom level changed |
146
+ | `onError` | `{ message, code }` | Error occurred |
147
+ | `onThumbnailGenerated` | `{ page, uri }` | Thumbnail ready |
148
+ | `onLoadingChange` | `{ isLoading }` | Loading state changed |
149
+
150
+ ## Methods
206
151
 
207
152
  ```tsx
208
- <PdfViewerView
209
- source="https://example.com/document.pdf"
210
- contentInsetTop={80} // Height of your transparent top bar
211
- contentInsetBottom={60} // Height of your transparent bottom toolbar
212
- style={{ flex: 1 }}
213
- />
214
- ```
215
-
216
- This creates a modern "glass" effect where:
217
- - PDF content starts below the top bar
218
- - Content scrolls behind transparent bars
219
- - Content ends above the bottom toolbar
153
+ const pdfRef = useRef<PdfViewerRef>(null);
220
154
 
221
- ### Methods
155
+ // Navigate to page (0-indexed)
156
+ pdfRef.current?.goToPage(0);
222
157
 
223
- Access methods via ref:
224
-
225
- ```tsx
226
- const pdfRef = useRef<PdfViewer>(null);
227
-
228
- // Navigate to specific page (0-indexed)
229
- pdfRef.current?.goToPage(pageIndex);
230
-
231
- // Set zoom scale
158
+ // Set zoom level
232
159
  pdfRef.current?.setScale(2.0);
233
160
 
234
- // Generate thumbnail for specific page
235
- pdfRef.current?.generateThumbnail(pageIndex);
161
+ // Generate single thumbnail
162
+ pdfRef.current?.generateThumbnail(0);
236
163
 
237
- // Generate thumbnails for all pages
164
+ // Generate all thumbnails
238
165
  pdfRef.current?.generateAllThumbnails();
239
- ```
240
-
241
- ### Events
242
-
243
- #### LoadCompleteEvent
244
- ```typescript
245
- {
246
- pageCount: number; // Total number of pages
247
- pageWidth: number; // Width of first page
248
- pageHeight: number; // Height of first page
249
- }
250
- ```
251
-
252
- #### PageChangeEvent
253
- ```typescript
254
- {
255
- page: number; // Current page index (0-based)
256
- pageCount: number; // Total pages
257
- }
258
- ```
259
-
260
- #### ScaleChangeEvent
261
- ```typescript
262
- {
263
- scale: number; // Current zoom scale
264
- }
265
- ```
266
-
267
- #### ErrorEvent
268
- ```typescript
269
- {
270
- message: string; // Error description
271
- code: string; // Error code (e.g., "FILE_NOT_FOUND")
272
- }
273
- ```
274
166
 
275
- #### ThumbnailGeneratedEvent
276
- ```typescript
277
- {
278
- page: number; // Page index
279
- uri: string; // File URI of thumbnail image
280
- }
167
+ // Get document info
168
+ const info = pdfRef.current?.getDocumentInfo();
169
+ // { pageCount, pageWidth, pageHeight, currentPage }
281
170
  ```
282
171
 
283
- ## Performance
172
+ ## Platform Differences
284
173
 
285
- - **Memory efficient**: Virtualizes pages, only renders visible content
286
- - **Smooth scrolling**: 60 FPS on most devices
287
- - **Large PDFs**: Tested with 500+ page documents
288
- - **Smart caching**: Automatic memory management with LRU cache
174
+ | Feature | Android | iOS |
175
+ |---------|:-------:|:---:|
176
+ | Vertical scroll | | |
177
+ | Horizontal scroll | | |
178
+ | Paging mode | — | ✅ |
179
+ | Pinch-to-zoom | ✅ | ✅ |
180
+ | Double-tap zoom | ✅ | ✅ |
181
+ | Pan when zoomed | ✅ | ✅ |
182
+ | Thumbnails | ✅ | ✅ |
183
+ | Remote URLs | ✅ | ✅ |
184
+ | Local files | ✅ | ✅ |
289
185
 
290
186
  ## Troubleshooting
291
187
 
292
- ### PDF fails to load from URL
188
+ **PDF fails to load from URL**
189
+ Ensure the URL is accessible. Use HTTPS in production.
293
190
 
294
- Ensure the URL is accessible and returns a valid PDF. For production apps, always use HTTPS URLs for security.
191
+ **Out of memory on large PDFs**
192
+ Lower `maxScale` to reduce memory usage. The viewer automatically manages memory with dynamic quality scaling.
295
193
 
296
- ### Out of memory errors on large PDFs
297
-
298
- The library automatically manages memory, but for very large documents at high zoom levels:
299
- - Limit `maxScale` to reduce memory usage
300
- - The Android implementation dynamically reduces quality at high zoom levels
301
-
302
- ### iOS horizontal/paging mode not working
303
-
304
- These features are iOS-only. On Android, the viewer always uses vertical scrolling.
194
+ **Horizontal/paging not working**
195
+ These are iOS-only features. Android uses vertical scroll.
305
196
 
306
197
  ## Contributing
307
198
 
308
- - [Development workflow](CONTRIBUTING.md#development-workflow)
309
- - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
310
- - [Code of conduct](CODE_OF_CONDUCT.md)
199
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup.
311
200
 
312
201
  ## License
313
202
 
314
203
  MIT
315
-
316
- ---
317
-
318
- Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -36,8 +36,6 @@ apply plugin: "com.android.library"
36
36
  apply plugin: "kotlin-android"
37
37
  apply from: '../nitrogen/generated/android/pdfviewer+autolinking.gradle'
38
38
 
39
- apply plugin: "com.facebook.react"
40
-
41
39
  android {
42
40
  namespace "com.margelo.nitro.pdfviewer"
43
41
 
@@ -89,7 +87,10 @@ android {
89
87
  "**/libreactnativejni.so",
90
88
  "**/libturbomodulejsijni.so",
91
89
  "**/libreact_nativemodule_core.so",
92
- "**/libjscexecutor.so"
90
+ "**/libjscexecutor.so",
91
+ // Exclude react-native-safe-area-context generated code to prevent duplicate classes
92
+ "com/facebook/react/viewmanagers/RNCSafeAreaProviderManagerDelegate**",
93
+ "com/facebook/react/viewmanagers/RNCSafeAreaViewManagerDelegate**"
93
94
  ]
94
95
  }
95
96
 
@@ -118,4 +119,5 @@ dependencies {
118
119
  implementation "com.facebook.react:react-android"
119
120
  implementation project(":react-native-nitro-modules")
120
121
  implementation "androidx.recyclerview:recyclerview:1.3.2"
122
+ implementation "androidx.lifecycle:lifecycle-process:2.6.2"
121
123
  }
@@ -190,4 +190,14 @@ class HybridPdfViewer(private val reactContext: ThemedReactContext) : HybridPdfV
190
190
  override fun generateAllThumbnails() {
191
191
  documentViewer.generateAllThumbnails()
192
192
  }
193
+
194
+ override fun getDocumentInfo(): DocumentInfo? {
195
+ val info = documentViewer.getDocumentInfo() ?: return null
196
+ return DocumentInfo(
197
+ pageCount = (info["pageCount"] as? Int)?.toDouble() ?: 0.0,
198
+ pageWidth = (info["pageWidth"] as? Int)?.toDouble() ?: 0.0,
199
+ pageHeight = (info["pageHeight"] as? Int)?.toDouble() ?: 0.0,
200
+ currentPage = (info["currentPage"] as? Int)?.toDouble() ?: 0.0
201
+ )
202
+ }
193
203
  }