@thatkid02/react-native-pdf-viewer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/PdfViewer.podspec +28 -0
- package/README.md +290 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +121 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/pdfviewer/HybridPdfViewer.kt +169 -0
- package/android/src/main/java/com/margelo/nitro/pdfviewer/PdfViewer.kt +996 -0
- package/android/src/main/java/com/margelo/nitro/pdfviewer/PdfViewerPackage.kt +26 -0
- package/ios/PdfViewer.swift +696 -0
- package/lib/module/PdfViewer.nitro.js +4 -0
- package/lib/module/PdfViewer.nitro.js.map +1 -0
- package/lib/module/index.js +13 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/PdfViewer.nitro.d.ts +67 -0
- package/lib/typescript/src/PdfViewer.nitro.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +8 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/nitro.json +17 -0
- package/nitrogen/generated/android/c++/JErrorEvent.hpp +57 -0
- package/nitrogen/generated/android/c++/JFunc_void_ErrorEvent.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_LoadCompleteEvent.hpp +76 -0
- package/nitrogen/generated/android/c++/JFunc_void_LoadingChangeEvent.hpp +76 -0
- package/nitrogen/generated/android/c++/JFunc_void_PageChangeEvent.hpp +76 -0
- package/nitrogen/generated/android/c++/JFunc_void_ScaleChangeEvent.hpp +76 -0
- package/nitrogen/generated/android/c++/JFunc_void_ThumbnailGeneratedEvent.hpp +77 -0
- package/nitrogen/generated/android/c++/JHybridPdfViewerSpec.cpp +273 -0
- package/nitrogen/generated/android/c++/JHybridPdfViewerSpec.hpp +94 -0
- package/nitrogen/generated/android/c++/JLoadCompleteEvent.hpp +61 -0
- package/nitrogen/generated/android/c++/JLoadingChangeEvent.hpp +53 -0
- package/nitrogen/generated/android/c++/JPageChangeEvent.hpp +57 -0
- package/nitrogen/generated/android/c++/JScaleChangeEvent.hpp +53 -0
- package/nitrogen/generated/android/c++/JThumbnailGeneratedEvent.hpp +57 -0
- package/nitrogen/generated/android/c++/views/JHybridPdfViewerStateUpdater.cpp +108 -0
- package/nitrogen/generated/android/c++/views/JHybridPdfViewerStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/ErrorEvent.kt +32 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/Func_void_ErrorEvent.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/Func_void_LoadCompleteEvent.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/Func_void_LoadingChangeEvent.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/Func_void_PageChangeEvent.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/Func_void_ScaleChangeEvent.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/Func_void_ThumbnailGeneratedEvent.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/HybridPdfViewerSpec.kt +195 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/LoadCompleteEvent.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/LoadingChangeEvent.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/PageChangeEvent.kt +32 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/ScaleChangeEvent.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/ThumbnailGeneratedEvent.kt +32 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/pdfviewerOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/views/HybridPdfViewerManager.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/pdfviewer/views/HybridPdfViewerStateUpdater.kt +23 -0
- package/nitrogen/generated/android/pdfviewer+autolinking.cmake +83 -0
- package/nitrogen/generated/android/pdfviewer+autolinking.gradle +27 -0
- package/nitrogen/generated/android/pdfviewerOnLoad.cpp +58 -0
- package/nitrogen/generated/android/pdfviewerOnLoad.hpp +25 -0
- package/nitrogen/generated/ios/PdfViewer+autolinking.rb +60 -0
- package/nitrogen/generated/ios/PdfViewer-Swift-Cxx-Bridge.cpp +80 -0
- package/nitrogen/generated/ios/PdfViewer-Swift-Cxx-Bridge.hpp +339 -0
- package/nitrogen/generated/ios/PdfViewer-Swift-Cxx-Umbrella.hpp +64 -0
- package/nitrogen/generated/ios/PdfViewerAutolinking.mm +33 -0
- package/nitrogen/generated/ios/PdfViewerAutolinking.swift +25 -0
- package/nitrogen/generated/ios/c++/HybridPdfViewerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridPdfViewerSpecSwift.hpp +205 -0
- package/nitrogen/generated/ios/c++/views/HybridPdfViewerComponent.mm +161 -0
- package/nitrogen/generated/ios/swift/ErrorEvent.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_ErrorEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_LoadCompleteEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_LoadingChangeEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_PageChangeEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_ScaleChangeEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_ThumbnailGeneratedEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridPdfViewerSpec.swift +65 -0
- package/nitrogen/generated/ios/swift/HybridPdfViewerSpec_cxx.swift +500 -0
- package/nitrogen/generated/ios/swift/LoadCompleteEvent.swift +57 -0
- package/nitrogen/generated/ios/swift/LoadingChangeEvent.swift +35 -0
- package/nitrogen/generated/ios/swift/PageChangeEvent.swift +46 -0
- package/nitrogen/generated/ios/swift/ScaleChangeEvent.swift +35 -0
- package/nitrogen/generated/ios/swift/ThumbnailGeneratedEvent.swift +46 -0
- package/nitrogen/generated/shared/c++/ErrorEvent.hpp +71 -0
- package/nitrogen/generated/shared/c++/HybridPdfViewerSpec.cpp +52 -0
- package/nitrogen/generated/shared/c++/HybridPdfViewerSpec.hpp +111 -0
- package/nitrogen/generated/shared/c++/LoadCompleteEvent.hpp +75 -0
- package/nitrogen/generated/shared/c++/LoadingChangeEvent.hpp +67 -0
- package/nitrogen/generated/shared/c++/PageChangeEvent.hpp +71 -0
- package/nitrogen/generated/shared/c++/ScaleChangeEvent.hpp +67 -0
- package/nitrogen/generated/shared/c++/ThumbnailGeneratedEvent.hpp +71 -0
- package/nitrogen/generated/shared/c++/views/HybridPdfViewerComponent.cpp +243 -0
- package/nitrogen/generated/shared/c++/views/HybridPdfViewerComponent.hpp +127 -0
- package/nitrogen/generated/shared/json/PdfViewerConfig.json +23 -0
- package/package.json +175 -0
- package/src/PdfViewer.nitro.ts +97 -0
- package/src/index.tsx +27 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 thatkid02
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "PdfViewer"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/thatkid02/react-native-pdf-viewer.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = [
|
|
17
|
+
"ios/**/*.{h,m,mm,swift}",
|
|
18
|
+
"cpp/**/*.{hpp,cpp}",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
s.dependency 'React-jsi'
|
|
22
|
+
s.dependency 'React-callinvoker'
|
|
23
|
+
|
|
24
|
+
load 'nitrogen/generated/ios/PdfViewer+autolinking.rb'
|
|
25
|
+
add_nitrogen_files(s)
|
|
26
|
+
|
|
27
|
+
install_modules_dependencies(s)
|
|
28
|
+
end
|
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# react-native-pdf-viewer
|
|
2
|
+
|
|
3
|
+
High-performance PDF viewer for React Native, built with [Nitro Modules](https://nitro.margelo.com/) for native rendering performance.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
✅ **High-performance PDF rendering** with native APIs (PDFKit on iOS, PdfRenderer on Android)
|
|
8
|
+
✅ **Pinch-to-zoom and pan gestures** - Smooth, native gesture handling
|
|
9
|
+
✅ **Thumbnail generation** for page previews
|
|
10
|
+
✅ **Local and remote PDF support** (file://, http://, https://)
|
|
11
|
+
✅ **Page navigation** with events
|
|
12
|
+
✅ **TypeScript support** - Full type safety
|
|
13
|
+
✅ **Memory efficient** - Virtualizes pages, only renders visible content
|
|
14
|
+
✅ **iOS-specific layouts** - Horizontal scroll and paging modes
|
|
15
|
+
|
|
16
|
+
## Platform Support
|
|
17
|
+
|
|
18
|
+
| Feature | Android | iOS | Notes |
|
|
19
|
+
|---------|---------|-----|-------|
|
|
20
|
+
| Local Files (file://) | ✅ | ✅ | |
|
|
21
|
+
| Remote URLs (http/https) | ✅ | ✅ | Prefer HTTPS in production |
|
|
22
|
+
| Zoom Controls | ✅(2 scale) | ✅ | |
|
|
23
|
+
| Page Navigation | ✅ | ✅ | |
|
|
24
|
+
| Horizontal Scroll | ❌ | ✅ | iOS only |
|
|
25
|
+
| Paging Mode | ❌ | ✅ | iOS only |
|
|
26
|
+
| Thumbnail Generation | ✅ | ✅ | Async on both platforms |
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm install react-native-pdf-viewer react-native-nitro-modules
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> `react-native-nitro-modules` is required as this library relies on [Nitro Modules](https://nitro.margelo.com/).
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Basic Example
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { PdfViewerView } from 'react-native-pdf-viewer';
|
|
42
|
+
|
|
43
|
+
export default function App() {
|
|
44
|
+
return (
|
|
45
|
+
<PdfViewerView
|
|
46
|
+
source="https://example.com/document.pdf"
|
|
47
|
+
style={{ flex: 1 }}
|
|
48
|
+
onLoadComplete={(event) => {
|
|
49
|
+
console.log('PDF loaded:', event.pageCount, 'pages');
|
|
50
|
+
}}
|
|
51
|
+
onError={(event) => {
|
|
52
|
+
console.error('PDF error:', event.message);
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Advanced Example with All Features
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import React, { useRef } from 'react';
|
|
63
|
+
import { View, Button, StyleSheet } from 'react-native';
|
|
64
|
+
import { PdfViewerView, type PdfViewer } from 'react-native-pdf-viewer';
|
|
65
|
+
|
|
66
|
+
export default function AdvancedPdfViewer() {
|
|
67
|
+
const pdfRef = useRef<PdfViewer>(null);
|
|
68
|
+
|
|
69
|
+
const handleLoadComplete = (event) => {
|
|
70
|
+
console.log(`PDF loaded: ${event.pageCount} pages`);
|
|
71
|
+
console.log(`Page size: ${event.pageWidth}x${event.pageHeight}`);
|
|
72
|
+
|
|
73
|
+
// Generate thumbnails for all pages
|
|
74
|
+
pdfRef.current?.generateAllThumbnails();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const handlePageChange = (event) => {
|
|
78
|
+
console.log(`Current page: ${event.page + 1} of ${event.pageCount}`);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const handleScaleChange = (event) => {
|
|
82
|
+
console.log(`Zoom scale: ${event.scale}`);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const handleThumbnailGenerated = (event) => {
|
|
86
|
+
console.log(`Thumbnail for page ${event.page}: ${event.uri}`);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const goToPage = (page: number) => {
|
|
90
|
+
pdfRef.current?.goToPage(page);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const zoomIn = () => {
|
|
94
|
+
pdfRef.current?.setScale(2.0);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<View style={styles.container}>
|
|
99
|
+
<PdfViewerView
|
|
100
|
+
ref={pdfRef}
|
|
101
|
+
source="https://example.com/document.pdf"
|
|
102
|
+
style={styles.pdf}
|
|
103
|
+
// Layout options
|
|
104
|
+
spacing={16}
|
|
105
|
+
enableZoom={true}
|
|
106
|
+
minScale={0.5}
|
|
107
|
+
maxScale={5.0}
|
|
108
|
+
// iOS-only options
|
|
109
|
+
horizontal={false} // iOS only
|
|
110
|
+
enablePaging={false} // iOS only
|
|
111
|
+
// Loading indicator
|
|
112
|
+
showsActivityIndicator={true}
|
|
113
|
+
// Event handlers
|
|
114
|
+
onLoadComplete={handleLoadComplete}
|
|
115
|
+
onPageChange={handlePageChange}
|
|
116
|
+
onScaleChange={handleScaleChange}
|
|
117
|
+
onThumbnailGenerated={handleThumbnailGenerated}
|
|
118
|
+
onError={(event) => console.error(event.message)}
|
|
119
|
+
onLoadingChange={(event) => console.log('Loading:', event.isLoading)}
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<View style={styles.controls}>
|
|
123
|
+
<Button title="Go to Page 5" onPress={() => goToPage(4)} />
|
|
124
|
+
<Button title="Zoom In" onPress={zoomIn} />
|
|
125
|
+
</View>
|
|
126
|
+
</View>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const styles = StyleSheet.create({
|
|
131
|
+
container: {
|
|
132
|
+
flex: 1,
|
|
133
|
+
},
|
|
134
|
+
pdf: {
|
|
135
|
+
flex: 1,
|
|
136
|
+
},
|
|
137
|
+
controls: {
|
|
138
|
+
flexDirection: 'row',
|
|
139
|
+
justifyContent: 'space-around',
|
|
140
|
+
padding: 16,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Loading Local Files
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<PdfViewerView
|
|
149
|
+
source="file:///path/to/document.pdf"
|
|
150
|
+
style={{ flex: 1 }}
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### iOS-Specific Features
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
// Horizontal scrolling (iOS only)
|
|
158
|
+
<PdfViewerView
|
|
159
|
+
source="https://example.com/document.pdf"
|
|
160
|
+
horizontal={true}
|
|
161
|
+
style={{ flex: 1 }}
|
|
162
|
+
/>
|
|
163
|
+
|
|
164
|
+
// Paging mode with swipe transitions (iOS only)
|
|
165
|
+
<PdfViewerView
|
|
166
|
+
source="https://example.com/document.pdf"
|
|
167
|
+
enablePaging={true}
|
|
168
|
+
style={{ flex: 1 }}
|
|
169
|
+
/>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## API Reference
|
|
173
|
+
|
|
174
|
+
### Props
|
|
175
|
+
|
|
176
|
+
| Prop | Type | Default | Description |
|
|
177
|
+
|------|------|---------|-------------|
|
|
178
|
+
| `source` | `string` | - | PDF source URI (file://, http://, https://) |
|
|
179
|
+
| `spacing` | `number` | `8` | Space between pages in pixels |
|
|
180
|
+
| `enableZoom` | `boolean` | `true` | Enable pinch-to-zoom gestures |
|
|
181
|
+
| `minScale` | `number` | `0.5` | Minimum zoom scale |
|
|
182
|
+
| `maxScale` | `number` | `4.0` | Maximum zoom scale |
|
|
183
|
+
| `showsActivityIndicator` | `boolean` | `true` | Show loading indicator |
|
|
184
|
+
| `horizontal` | `boolean` | `false` | Horizontal scroll (iOS only) |
|
|
185
|
+
| `enablePaging` | `boolean` | `false` | Enable paging mode (iOS only) |
|
|
186
|
+
| `onLoadComplete` | `(event) => void` | - | Called when PDF loads |
|
|
187
|
+
| `onPageChange` | `(event) => void` | - | Called when page changes |
|
|
188
|
+
| `onScaleChange` | `(event) => void` | - | Called when zoom changes |
|
|
189
|
+
| `onError` | `(event) => void` | - | Called on error |
|
|
190
|
+
| `onThumbnailGenerated` | `(event) => void` | - | Called when thumbnail is ready |
|
|
191
|
+
| `onLoadingChange` | `(event) => void` | - | Called when loading state changes |
|
|
192
|
+
|
|
193
|
+
### Methods
|
|
194
|
+
|
|
195
|
+
Access methods via ref:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
const pdfRef = useRef<PdfViewer>(null);
|
|
199
|
+
|
|
200
|
+
// Navigate to specific page (0-indexed)
|
|
201
|
+
pdfRef.current?.goToPage(pageIndex);
|
|
202
|
+
|
|
203
|
+
// Set zoom scale
|
|
204
|
+
pdfRef.current?.setScale(2.0);
|
|
205
|
+
|
|
206
|
+
// Generate thumbnail for specific page
|
|
207
|
+
pdfRef.current?.generateThumbnail(pageIndex);
|
|
208
|
+
|
|
209
|
+
// Generate thumbnails for all pages
|
|
210
|
+
pdfRef.current?.generateAllThumbnails();
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Events
|
|
214
|
+
|
|
215
|
+
#### LoadCompleteEvent
|
|
216
|
+
```typescript
|
|
217
|
+
{
|
|
218
|
+
pageCount: number; // Total number of pages
|
|
219
|
+
pageWidth: number; // Width of first page
|
|
220
|
+
pageHeight: number; // Height of first page
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### PageChangeEvent
|
|
225
|
+
```typescript
|
|
226
|
+
{
|
|
227
|
+
page: number; // Current page index (0-based)
|
|
228
|
+
pageCount: number; // Total pages
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### ScaleChangeEvent
|
|
233
|
+
```typescript
|
|
234
|
+
{
|
|
235
|
+
scale: number; // Current zoom scale
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### ErrorEvent
|
|
240
|
+
```typescript
|
|
241
|
+
{
|
|
242
|
+
message: string; // Error description
|
|
243
|
+
code: string; // Error code (e.g., "FILE_NOT_FOUND")
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### ThumbnailGeneratedEvent
|
|
248
|
+
```typescript
|
|
249
|
+
{
|
|
250
|
+
page: number; // Page index
|
|
251
|
+
uri: string; // File URI of thumbnail image
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Performance
|
|
256
|
+
|
|
257
|
+
- **Memory efficient**: Virtualizes pages, only renders visible content
|
|
258
|
+
- **Smooth scrolling**: 60 FPS on most devices
|
|
259
|
+
- **Large PDFs**: Tested with 500+ page documents
|
|
260
|
+
- **Smart caching**: Automatic memory management with LRU cache
|
|
261
|
+
|
|
262
|
+
## Troubleshooting
|
|
263
|
+
|
|
264
|
+
### PDF fails to load from URL
|
|
265
|
+
|
|
266
|
+
Ensure the URL is accessible and returns a valid PDF. For production apps, always use HTTPS URLs for security.
|
|
267
|
+
|
|
268
|
+
### Out of memory errors on large PDFs
|
|
269
|
+
|
|
270
|
+
The library automatically manages memory, but for very large documents at high zoom levels:
|
|
271
|
+
- Limit `maxScale` to reduce memory usage
|
|
272
|
+
- The Android implementation dynamically reduces quality at high zoom levels
|
|
273
|
+
|
|
274
|
+
### iOS horizontal/paging mode not working
|
|
275
|
+
|
|
276
|
+
These features are iOS-only. On Android, the viewer always uses vertical scrolling.
|
|
277
|
+
|
|
278
|
+
## Contributing
|
|
279
|
+
|
|
280
|
+
- [Development workflow](CONTRIBUTING.md#development-workflow)
|
|
281
|
+
- [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
|
|
282
|
+
- [Code of conduct](CODE_OF_CONDUCT.md)
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
MIT
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
project(pdfviewer)
|
|
2
|
+
cmake_minimum_required(VERSION 3.9.0)
|
|
3
|
+
|
|
4
|
+
set(PACKAGE_NAME pdfviewer)
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
6
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
7
|
+
|
|
8
|
+
# Define C++ library and add all sources
|
|
9
|
+
add_library(${PACKAGE_NAME} SHARED src/main/cpp/cpp-adapter.cpp)
|
|
10
|
+
|
|
11
|
+
# Add Nitrogen specs :)
|
|
12
|
+
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/pdfviewer+autolinking.cmake)
|
|
13
|
+
|
|
14
|
+
# Set up local includes
|
|
15
|
+
include_directories("src/main/cpp" "../cpp")
|
|
16
|
+
|
|
17
|
+
find_library(LOG_LIB log)
|
|
18
|
+
|
|
19
|
+
# Link all libraries together
|
|
20
|
+
target_link_libraries(
|
|
21
|
+
${PACKAGE_NAME}
|
|
22
|
+
${LOG_LIB}
|
|
23
|
+
android # <-- Android core
|
|
24
|
+
)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.PdfViewer = [
|
|
3
|
+
kotlinVersion: "2.0.21",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36,
|
|
6
|
+
targetSdkVersion: 36,
|
|
7
|
+
ndkVersion: "27.1.12297006"
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
ext.getExtOrDefault = { prop ->
|
|
11
|
+
if (rootProject.ext.has(prop)) {
|
|
12
|
+
return rootProject.ext.get(prop)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return PdfViewer[prop]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
repositories {
|
|
19
|
+
google()
|
|
20
|
+
mavenCentral()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
dependencies {
|
|
24
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
25
|
+
// noinspection DifferentKotlinGradleVersion
|
|
26
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def reactNativeArchitectures() {
|
|
31
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
32
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
apply plugin: "com.android.library"
|
|
36
|
+
apply plugin: "kotlin-android"
|
|
37
|
+
apply from: '../nitrogen/generated/android/pdfviewer+autolinking.gradle'
|
|
38
|
+
|
|
39
|
+
apply plugin: "com.facebook.react"
|
|
40
|
+
|
|
41
|
+
android {
|
|
42
|
+
namespace "com.margelo.nitro.pdfviewer"
|
|
43
|
+
|
|
44
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
45
|
+
ndkVersion getExtOrDefault("ndkVersion")
|
|
46
|
+
|
|
47
|
+
defaultConfig {
|
|
48
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
49
|
+
targetSdkVersion getExtOrDefault("targetSdkVersion")
|
|
50
|
+
|
|
51
|
+
externalNativeBuild {
|
|
52
|
+
cmake {
|
|
53
|
+
cppFlags "-frtti -fexceptions -Wall -fstack-protector-all"
|
|
54
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
55
|
+
abiFilters (*reactNativeArchitectures())
|
|
56
|
+
|
|
57
|
+
buildTypes {
|
|
58
|
+
debug {
|
|
59
|
+
cppFlags "-O1 -g"
|
|
60
|
+
}
|
|
61
|
+
release {
|
|
62
|
+
cppFlags "-O2"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
externalNativeBuild {
|
|
70
|
+
cmake {
|
|
71
|
+
path "CMakeLists.txt"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
packagingOptions {
|
|
76
|
+
excludes = [
|
|
77
|
+
"META-INF",
|
|
78
|
+
"META-INF/**",
|
|
79
|
+
"**/libc++_shared.so",
|
|
80
|
+
"**/libfbjni.so",
|
|
81
|
+
"**/libjsi.so",
|
|
82
|
+
"**/libfolly_json.so",
|
|
83
|
+
"**/libfolly_runtime.so",
|
|
84
|
+
"**/libglog.so",
|
|
85
|
+
"**/libhermes.so",
|
|
86
|
+
"**/libhermes-executor-debug.so",
|
|
87
|
+
"**/libhermes_executor.so",
|
|
88
|
+
"**/libreactnative.so",
|
|
89
|
+
"**/libreactnativejni.so",
|
|
90
|
+
"**/libturbomodulejsijni.so",
|
|
91
|
+
"**/libreact_nativemodule_core.so",
|
|
92
|
+
"**/libjscexecutor.so"
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
buildFeatures {
|
|
97
|
+
buildConfig true
|
|
98
|
+
prefab true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
buildTypes {
|
|
102
|
+
release {
|
|
103
|
+
minifyEnabled false
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
lint {
|
|
108
|
+
disable "GradleCompatible"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
compileOptions {
|
|
112
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
113
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
dependencies {
|
|
118
|
+
implementation "com.facebook.react:react-android"
|
|
119
|
+
implementation project(":react-native-nitro-modules")
|
|
120
|
+
implementation "androidx.recyclerview:recyclerview:1.3.2"
|
|
121
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
package com.margelo.nitro.pdfviewer
|
|
2
|
+
|
|
3
|
+
import android.view.View
|
|
4
|
+
import androidx.annotation.Keep
|
|
5
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
6
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* HybridPdfViewer that extends the Nitrogen-generated HybridPdfViewerSpec.
|
|
10
|
+
* This wraps DocumentViewer and connects it to the Nitro interface.
|
|
11
|
+
*/
|
|
12
|
+
@DoNotStrip
|
|
13
|
+
@Keep
|
|
14
|
+
class HybridPdfViewer(private val reactContext: ThemedReactContext) : HybridPdfViewerSpec() {
|
|
15
|
+
|
|
16
|
+
companion object {
|
|
17
|
+
private const val TAG = "HybridPdfViewer"
|
|
18
|
+
|
|
19
|
+
init {
|
|
20
|
+
if (BuildConfig.DEBUG) {
|
|
21
|
+
android.util.Log.d(TAG, "HybridPdfViewer class initializing...")
|
|
22
|
+
}
|
|
23
|
+
// Load native library before HybridPdfViewerSpec is initialized
|
|
24
|
+
pdfviewerOnLoad.initializeNative()
|
|
25
|
+
if (BuildConfig.DEBUG) {
|
|
26
|
+
android.util.Log.d(TAG, "Native library loaded successfully")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private val documentViewer = PdfViewer(reactContext)
|
|
32
|
+
|
|
33
|
+
init {
|
|
34
|
+
if (BuildConfig.DEBUG) {
|
|
35
|
+
android.util.Log.d(TAG, "HybridPdfViewer instance created")
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override val view: View
|
|
40
|
+
get() = documentViewer
|
|
41
|
+
|
|
42
|
+
// Connect DocumentViewer callbacks to Nitro callbacks
|
|
43
|
+
init {
|
|
44
|
+
documentViewer.onLoadCompleteCallback = { pageCount, pageWidth, pageHeight ->
|
|
45
|
+
onLoadComplete?.invoke(LoadCompleteEvent(
|
|
46
|
+
pageCount = pageCount.toDouble(),
|
|
47
|
+
pageWidth = pageWidth.toDouble(),
|
|
48
|
+
pageHeight = pageHeight.toDouble()
|
|
49
|
+
))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
documentViewer.onPageChangeCallback = { page, pageCount ->
|
|
53
|
+
onPageChange?.invoke(PageChangeEvent(
|
|
54
|
+
page = page.toDouble(),
|
|
55
|
+
pageCount = pageCount.toDouble()
|
|
56
|
+
))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
documentViewer.onScaleChangeCallback = { scale ->
|
|
60
|
+
onScaleChange?.invoke(ScaleChangeEvent(
|
|
61
|
+
scale = scale.toDouble()
|
|
62
|
+
))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
documentViewer.onErrorCallback = { message, code ->
|
|
66
|
+
onError?.invoke(ErrorEvent(
|
|
67
|
+
message = message,
|
|
68
|
+
code = code
|
|
69
|
+
))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
documentViewer.onThumbnailGeneratedCallback = { page, uri ->
|
|
73
|
+
onThumbnailGenerated?.invoke(ThumbnailGeneratedEvent(
|
|
74
|
+
page = page.toDouble(),
|
|
75
|
+
uri = uri
|
|
76
|
+
))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
documentViewer.onLoadingChangeCallback = { isLoading ->
|
|
80
|
+
onLoadingChange?.invoke(LoadingChangeEvent(
|
|
81
|
+
isLoading = isLoading
|
|
82
|
+
))
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// MARK: - Nitro Properties
|
|
87
|
+
|
|
88
|
+
override var source: String? = null
|
|
89
|
+
set(value) {
|
|
90
|
+
if (BuildConfig.DEBUG) {
|
|
91
|
+
android.util.Log.d("HybridPdfViewer", "Setting source: $value")
|
|
92
|
+
}
|
|
93
|
+
field = value
|
|
94
|
+
documentViewer.sourceUri = value
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Note: horizontal and enablePaging are iOS-only features, but we keep them
|
|
98
|
+
// here to match the interface. They have no effect on Android.
|
|
99
|
+
override var horizontal: Boolean? = null
|
|
100
|
+
set(value) {
|
|
101
|
+
field = value
|
|
102
|
+
// No-op on Android - always uses vertical scroll
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
override var enablePaging: Boolean? = null
|
|
106
|
+
set(value) {
|
|
107
|
+
field = value
|
|
108
|
+
// No-op on Android - paging not supported
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
override var spacing: Double? = null
|
|
112
|
+
set(value) {
|
|
113
|
+
field = value
|
|
114
|
+
value?.let { documentViewer.spacing = it.toFloat() }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
override var enableZoom: Boolean? = null
|
|
118
|
+
set(value) {
|
|
119
|
+
field = value
|
|
120
|
+
value?.let { documentViewer.enableZoom = it }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
override var minScale: Double? = null
|
|
124
|
+
set(value) {
|
|
125
|
+
field = value
|
|
126
|
+
value?.let { documentViewer.minScale = it.toFloat() }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
override var maxScale: Double? = null
|
|
130
|
+
set(value) {
|
|
131
|
+
field = value
|
|
132
|
+
value?.let { documentViewer.maxScale = it.toFloat() }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
override var showsActivityIndicator: Boolean? = null
|
|
136
|
+
set(value) {
|
|
137
|
+
field = value
|
|
138
|
+
value?.let { documentViewer.showsActivityIndicator = it }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Event callbacks - these are set by Nitro
|
|
142
|
+
override var onLoadComplete: ((event: LoadCompleteEvent) -> Unit)? = null
|
|
143
|
+
override var onPageChange: ((event: PageChangeEvent) -> Unit)? = null
|
|
144
|
+
override var onScaleChange: ((event: ScaleChangeEvent) -> Unit)? = null
|
|
145
|
+
override var onError: ((event: ErrorEvent) -> Unit)? = null
|
|
146
|
+
override var onThumbnailGenerated: ((event: ThumbnailGeneratedEvent) -> Unit)? = null
|
|
147
|
+
override var onLoadingChange: ((event: LoadingChangeEvent) -> Unit)? = null
|
|
148
|
+
|
|
149
|
+
// MARK: - Nitro Methods
|
|
150
|
+
|
|
151
|
+
override fun goToPage(page: Double) {
|
|
152
|
+
documentViewer.goToPage(page.toInt())
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
override fun setScale(scale: Double) {
|
|
156
|
+
documentViewer.setScale(scale.toFloat())
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
override fun generateThumbnail(page: Double) {
|
|
160
|
+
if (BuildConfig.DEBUG) {
|
|
161
|
+
android.util.Log.d("HybridPdfViewer", "generateThumbnail called for page: $page")
|
|
162
|
+
}
|
|
163
|
+
documentViewer.generateThumbnail(page.toInt())
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
override fun generateAllThumbnails() {
|
|
167
|
+
documentViewer.generateAllThumbnails()
|
|
168
|
+
}
|
|
169
|
+
}
|