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