react-native-optimized-pdf 1.0.1 → 1.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 +12 -18
- package/CODE_OF_CONDUCT.md +128 -0
- package/LICENSE +21 -0
- package/README.md +23 -32
- package/ReactNativeOptimizedPdf.podspec +4 -4
- package/SECURITY.md +21 -0
- package/android/build.gradle +1 -1
- package/android/src/main/kotlin/com/reactnativeoptimizedpdf/OptimizedPdfView.kt +305 -143
- package/docs/android-setup.md +1 -1
- package/ios/Core/PDFConfiguration.swift +28 -0
- package/ios/Core/PDFDocumentLoader.swift +75 -0
- package/ios/Core/PDFError.swift +36 -0
- package/ios/ReactNative/OptimizedPDFViewManager.swift +48 -0
- package/ios/ReactNative/OptimizedPdfViewManager.m +54 -0
- package/ios/Views/OptimizedPDFView.swift +401 -0
- package/ios/Views/TiledPDFPageView.swift +312 -0
- package/package.json +19 -11
- package/src/OptimizedPdfView.tsx +20 -14
- package/src/components/PdfNavigationControls.tsx +1 -1
- package/src/components/PdfOverlays.tsx +3 -3
- package/src/types/index.ts +25 -0
- package/tsconfig.json +17 -10
- package/ios/OptimizedPdfView.swift +0 -256
- package/ios/OptimizedPdfViewManager.m +0 -22
- package/ios/OptimizedPdfViewManager.swift +0 -24
- package/ios/TiledPdfPageView.swift +0 -76
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import CoreGraphics
|
|
3
|
+
import PDFKit
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import UIKit
|
|
7
|
+
import CoreGraphics
|
|
8
|
+
|
|
9
|
+
/// A view that renders a PDF page using CATiledLayer for efficient tiled rendering.
|
|
10
|
+
///
|
|
11
|
+
/// CATiledLayer divides the content into tiles, allowing smooth zooming and scrolling
|
|
12
|
+
/// without rendering the entire PDF at once. This is essential for large PDF documents.
|
|
13
|
+
|
|
14
|
+
// MARK: - Global cache of flattened pages
|
|
15
|
+
final class FlattenedPageCache {
|
|
16
|
+
static let shared = FlattenedPageCache()
|
|
17
|
+
|
|
18
|
+
private let cache = NSCache<NSString, FlattenedPageEntry>()
|
|
19
|
+
|
|
20
|
+
private init() {
|
|
21
|
+
cache.countLimit = 10
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func entry(forKey key: String) -> FlattenedPageEntry? {
|
|
25
|
+
cache.object(forKey: key as NSString)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func setEntry(_ entry: FlattenedPageEntry, forKey key: String) {
|
|
29
|
+
cache.setObject(entry, forKey: key as NSString)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func removeEntry(forKey key: String) {
|
|
33
|
+
cache.removeObject(forKey: key as NSString)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func removeAll() {
|
|
37
|
+
cache.removeAllObjects()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
final class FlattenedPageEntry: NSObject {
|
|
42
|
+
let document: CGPDFDocument
|
|
43
|
+
let page: CGPDFPage
|
|
44
|
+
|
|
45
|
+
init(document: CGPDFDocument, page: CGPDFPage) {
|
|
46
|
+
self.document = document
|
|
47
|
+
self.page = page
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// MARK: - Atomic flag for thread-safe cancellation
|
|
52
|
+
|
|
53
|
+
final class OSAtomicFlag {
|
|
54
|
+
private var _value: Bool = false
|
|
55
|
+
private var _lock = os_unfair_lock()
|
|
56
|
+
|
|
57
|
+
func set() {
|
|
58
|
+
os_unfair_lock_lock(&_lock)
|
|
59
|
+
_value = true
|
|
60
|
+
os_unfair_lock_unlock(&_lock)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func isSet() -> Bool {
|
|
64
|
+
os_unfair_lock_lock(&_lock)
|
|
65
|
+
let v = _value
|
|
66
|
+
os_unfair_lock_unlock(&_lock)
|
|
67
|
+
return v
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// MARK: - TiledPDFPageView
|
|
72
|
+
|
|
73
|
+
final class TiledPDFPageView: UIView {
|
|
74
|
+
|
|
75
|
+
// MARK: - Properties
|
|
76
|
+
|
|
77
|
+
var pdfPage: CGPDFPage? {
|
|
78
|
+
didSet { setNeedsDisplay() }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
var pdfURL: URL?
|
|
82
|
+
var pageIndex: Int = 0
|
|
83
|
+
|
|
84
|
+
var enableAntialiasing: Bool = true {
|
|
85
|
+
didSet { setNeedsDisplay() }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
var configuration: PDFConfiguration = .default {
|
|
89
|
+
didSet { applyConfiguration() }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
override class var layerClass: AnyClass { CATiledLayer.self }
|
|
93
|
+
|
|
94
|
+
private var tiledLayer: CATiledLayer { layer as! CATiledLayer }
|
|
95
|
+
|
|
96
|
+
private var _activePage: CGPDFPage?
|
|
97
|
+
private var _pdfKitDocument: PDFDocument?
|
|
98
|
+
private var _generation: UInt = 0
|
|
99
|
+
private let _cancelled = OSAtomicFlag()
|
|
100
|
+
|
|
101
|
+
// MARK: - Initialization
|
|
102
|
+
|
|
103
|
+
override init(frame: CGRect) {
|
|
104
|
+
super.init(frame: frame)
|
|
105
|
+
commonInit()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
required init?(coder: NSCoder) {
|
|
109
|
+
super.init(coder: coder)
|
|
110
|
+
commonInit()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
convenience init(frame: CGRect, configuration: PDFConfiguration) {
|
|
114
|
+
self.init(frame: frame)
|
|
115
|
+
self.configuration = configuration
|
|
116
|
+
applyConfiguration()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private func commonInit() {
|
|
120
|
+
applyConfiguration()
|
|
121
|
+
contentScaleFactor = UIScreen.main.scale
|
|
122
|
+
backgroundColor = .white
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// MARK: - Configuration
|
|
126
|
+
|
|
127
|
+
private func applyConfiguration() {
|
|
128
|
+
tiledLayer.tileSize = configuration.tileSize
|
|
129
|
+
tiledLayer.levelsOfDetail = configuration.levelsOfDetail
|
|
130
|
+
tiledLayer.levelsOfDetailBias = configuration.levelsOfDetailBias
|
|
131
|
+
enableAntialiasing = configuration.enableAntialiasing
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// MARK: - Cancellation
|
|
135
|
+
|
|
136
|
+
func cancel() {
|
|
137
|
+
_cancelled.set()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private var currentCacheKey: String {
|
|
141
|
+
guard let url = pdfURL else { return "" }
|
|
142
|
+
return "\(url.absoluteString)_page\(pageIndex)"
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// MARK: - Prepare Page (Flatten Pipeline)
|
|
146
|
+
|
|
147
|
+
func preparePage() {
|
|
148
|
+
_generation &+= 1
|
|
149
|
+
let currentGen = _generation
|
|
150
|
+
|
|
151
|
+
let cacheKey = currentCacheKey
|
|
152
|
+
if let cached = FlattenedPageCache.shared.entry(forKey: cacheKey) {
|
|
153
|
+
_activePage = cached.page
|
|
154
|
+
setNeedsDisplay()
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
_activePage = pdfPage
|
|
159
|
+
|
|
160
|
+
guard let url = pdfURL,
|
|
161
|
+
let originalPage = pdfPage else { return }
|
|
162
|
+
|
|
163
|
+
let idx = pageIndex
|
|
164
|
+
let originalCropBox = originalPage.getBoxRect(.cropBox)
|
|
165
|
+
let rotation = originalPage.rotationAngle
|
|
166
|
+
|
|
167
|
+
let pdfKitDoc: PDFDocument
|
|
168
|
+
if let existing = _pdfKitDocument {
|
|
169
|
+
pdfKitDoc = existing
|
|
170
|
+
} else if let newDoc = PDFDocument(url: url) {
|
|
171
|
+
_pdfKitDocument = newDoc
|
|
172
|
+
pdfKitDoc = newDoc
|
|
173
|
+
} else {
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
guard let pdfKitPage = pdfKitDoc.page(at: idx) else { return }
|
|
178
|
+
let annotations = pdfKitPage.annotations
|
|
179
|
+
if annotations.isEmpty { return }
|
|
180
|
+
|
|
181
|
+
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
182
|
+
guard let self = self else { return }
|
|
183
|
+
|
|
184
|
+
let entry = Self.createFlattenedEntry(
|
|
185
|
+
pdfKitPage: pdfKitPage,
|
|
186
|
+
originalCropBox: originalCropBox,
|
|
187
|
+
rotation: Int(rotation)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
guard let entry = entry else { return }
|
|
191
|
+
FlattenedPageCache.shared.setEntry(entry, forKey: cacheKey)
|
|
192
|
+
|
|
193
|
+
DispatchQueue.main.async { [weak self] in
|
|
194
|
+
guard let self = self, self._generation == currentGen else { return }
|
|
195
|
+
self._activePage = entry.page
|
|
196
|
+
self.setNeedsDisplay()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// MARK: - Flattening Logic
|
|
202
|
+
private static func createFlattenedEntry(
|
|
203
|
+
pdfKitPage: PDFPage,
|
|
204
|
+
originalCropBox: CGRect,
|
|
205
|
+
rotation: Int
|
|
206
|
+
) -> FlattenedPageEntry? {
|
|
207
|
+
let finalSize: CGSize
|
|
208
|
+
if rotation == 90 || rotation == 270 {
|
|
209
|
+
finalSize = CGSize(width: originalCropBox.height, height: originalCropBox.width)
|
|
210
|
+
} else {
|
|
211
|
+
finalSize = originalCropBox.size
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
var mediaBox = CGRect(origin: .zero, size: finalSize)
|
|
215
|
+
|
|
216
|
+
let pdfData = NSMutableData()
|
|
217
|
+
guard let consumer = CGDataConsumer(data: pdfData as CFMutableData),
|
|
218
|
+
let pdfContext = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else {
|
|
219
|
+
return nil
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
pdfContext.beginPage(mediaBox: &mediaBox)
|
|
223
|
+
|
|
224
|
+
if originalCropBox.origin.x != 0 || originalCropBox.origin.y != 0 {
|
|
225
|
+
pdfContext.translateBy(x: -originalCropBox.origin.x, y: -originalCropBox.origin.y)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
pdfKitPage.draw(with: .cropBox, to: pdfContext)
|
|
229
|
+
|
|
230
|
+
pdfContext.endPage()
|
|
231
|
+
pdfContext.closePDF()
|
|
232
|
+
|
|
233
|
+
guard let provider = CGDataProvider(data: pdfData as CFData),
|
|
234
|
+
let flattenedDoc = CGPDFDocument(provider),
|
|
235
|
+
let flattenedCGPage = flattenedDoc.page(at: 1) else {
|
|
236
|
+
return nil
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return FlattenedPageEntry(document: flattenedDoc, page: flattenedCGPage)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// MARK: - Rendering
|
|
243
|
+
|
|
244
|
+
override func draw(_ rect: CGRect) {
|
|
245
|
+
guard !_cancelled.isSet() else { return }
|
|
246
|
+
|
|
247
|
+
guard let context = UIGraphicsGetCurrentContext(),
|
|
248
|
+
let page = _activePage else { return }
|
|
249
|
+
|
|
250
|
+
renderPage(page, in: context, rect: rect)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private func renderPage(_ page: CGPDFPage, in context: CGContext, rect: CGRect) {
|
|
254
|
+
context.saveGState()
|
|
255
|
+
defer { context.restoreGState() }
|
|
256
|
+
|
|
257
|
+
context.setShouldAntialias(enableAntialiasing)
|
|
258
|
+
context.interpolationQuality = determineInterpolationQuality()
|
|
259
|
+
|
|
260
|
+
context.setFillColor(UIColor.white.cgColor)
|
|
261
|
+
context.fill(rect)
|
|
262
|
+
|
|
263
|
+
context.translateBy(x: 0, y: bounds.height)
|
|
264
|
+
context.scaleBy(x: 1.0, y: -1.0)
|
|
265
|
+
|
|
266
|
+
let pdfTransform = page.getDrawingTransform(
|
|
267
|
+
.cropBox,
|
|
268
|
+
rect: bounds,
|
|
269
|
+
rotate: 0,
|
|
270
|
+
preserveAspectRatio: true
|
|
271
|
+
)
|
|
272
|
+
context.concatenate(pdfTransform)
|
|
273
|
+
// Draw the page
|
|
274
|
+
context.drawPDFPage(page)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private func determineInterpolationQuality() -> CGInterpolationQuality {
|
|
278
|
+
tiledLayer.levelsOfDetail == 1 ? .low : .high
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// MARK: - Public Methods
|
|
283
|
+
|
|
284
|
+
extension TiledPDFPageView {
|
|
285
|
+
|
|
286
|
+
func prepareForReuse() {
|
|
287
|
+
pdfPage = nil
|
|
288
|
+
_activePage = nil
|
|
289
|
+
layer.contents = nil
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/// Updates the view size to match the page dimensions.
|
|
293
|
+
func updateFrame(for page: CGPDFPage) {
|
|
294
|
+
let pageRect = page.getBoxRect(.cropBox)
|
|
295
|
+
frame = CGRect(origin: .zero, size: pageRect.size)
|
|
296
|
+
pdfPage = page
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
func invalidateCache() {
|
|
300
|
+
FlattenedPageCache.shared.removeEntry(forKey: currentCacheKey)
|
|
301
|
+
_activePage = pdfPage
|
|
302
|
+
_pdfKitDocument = nil
|
|
303
|
+
setNeedsDisplay()
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
func invalidateAllCache() {
|
|
307
|
+
FlattenedPageCache.shared.removeAll()
|
|
308
|
+
_pdfKitDocument = nil
|
|
309
|
+
_activePage = pdfPage
|
|
310
|
+
setNeedsDisplay()
|
|
311
|
+
}
|
|
312
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-optimized-pdf",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "React Native PDF viewer with high performance, zoom, and support for large PDF files. Fast, memory-efficient, and supports annotations (annots) on iOS and Android.",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -14,21 +14,27 @@
|
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"react-native",
|
|
17
|
+
"react-native-pdf",
|
|
17
18
|
"pdf",
|
|
18
19
|
"pdf-viewer",
|
|
19
|
-
"viewer",
|
|
20
|
+
"react-native-pdf-viewer",
|
|
21
|
+
"pdf-reader",
|
|
22
|
+
"pdf-renderer",
|
|
23
|
+
"pdf-display",
|
|
20
24
|
"ios",
|
|
21
25
|
"android",
|
|
22
|
-
"performance",
|
|
23
|
-
"high-performance",
|
|
24
26
|
"large-pdf",
|
|
27
|
+
"big-pdf",
|
|
28
|
+
"pdf-performance",
|
|
29
|
+
"fast-pdf",
|
|
25
30
|
"memory-efficient",
|
|
26
31
|
"tiled-rendering",
|
|
27
32
|
"zoom",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
33
|
+
"annotations",
|
|
34
|
+
"annots",
|
|
35
|
+
"pdfkit",
|
|
36
|
+
"pdfium",
|
|
37
|
+
"catiledlayer"
|
|
32
38
|
],
|
|
33
39
|
"author": "Your Name",
|
|
34
40
|
"license": "MIT",
|
|
@@ -39,8 +45,8 @@
|
|
|
39
45
|
"devDependencies": {
|
|
40
46
|
"@react-native-community/eslint-config": "^3.2.0",
|
|
41
47
|
"@types/crypto-js": "^4.2.2",
|
|
42
|
-
"@types/react": "^18.
|
|
43
|
-
"@types/react-native": "^0.
|
|
48
|
+
"@types/react": "^18.2.6",
|
|
49
|
+
"@types/react-native": "^0.64.19",
|
|
44
50
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
45
51
|
"@typescript-eslint/parser": "^6.0.0",
|
|
46
52
|
"eslint": "^8.0.0",
|
|
@@ -48,6 +54,8 @@
|
|
|
48
54
|
"eslint-plugin-jest": "^29.11.0",
|
|
49
55
|
"eslint-plugin-prettier": "^5.5.4",
|
|
50
56
|
"prettier": "2.8.8",
|
|
57
|
+
"react": "18.2.0",
|
|
58
|
+
"react-native": "0.73.11",
|
|
51
59
|
"typescript": "^5.0.0"
|
|
52
60
|
},
|
|
53
61
|
"repository": {
|
package/src/OptimizedPdfView.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
|
|
2
|
+
import { View, Platform, requireNativeComponent } from 'react-native';
|
|
3
3
|
import { PdfCacheService } from './services/pdfCache';
|
|
4
4
|
import { PdfNavigationControls } from './components/PdfNavigationControls';
|
|
5
5
|
import { PdfLoadingOverlay, PdfErrorOverlay } from './components/PdfOverlays';
|
|
@@ -9,11 +9,16 @@ import {
|
|
|
9
9
|
DEFAULT_SHOW_NAVIGATION_CONTROLS,
|
|
10
10
|
ERROR_MESSAGES,
|
|
11
11
|
} from './constants';
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
OptimizedPdfViewProps,
|
|
14
|
+
NativeLoadCompleteEvent,
|
|
15
|
+
NativePageCountEvent,
|
|
16
|
+
NativeOptimizedPdfViewProps,
|
|
17
|
+
} from './types';
|
|
13
18
|
|
|
14
19
|
const NativeOptimizedPdfView =
|
|
15
20
|
Platform.OS === 'ios' || Platform.OS === 'android'
|
|
16
|
-
? requireNativeComponent('OptimizedPdfView')
|
|
21
|
+
? requireNativeComponent<NativeOptimizedPdfViewProps>('OptimizedPdfView')
|
|
17
22
|
: () => null;
|
|
18
23
|
|
|
19
24
|
/**
|
|
@@ -37,6 +42,7 @@ const NativeOptimizedPdfView =
|
|
|
37
42
|
*/
|
|
38
43
|
export default function OptimizedPdfView({
|
|
39
44
|
source,
|
|
45
|
+
page,
|
|
40
46
|
password,
|
|
41
47
|
maximumZoom = DEFAULT_MAXIMUM_ZOOM,
|
|
42
48
|
enableAntialiasing = DEFAULT_ENABLE_ANTIALIASING,
|
|
@@ -52,7 +58,7 @@ export default function OptimizedPdfView({
|
|
|
52
58
|
const [loading, setLoading] = useState(true);
|
|
53
59
|
const [progress, setProgress] = useState(0);
|
|
54
60
|
const [error, setError] = useState<string | null>(null);
|
|
55
|
-
const [
|
|
61
|
+
const [internalPage, setInternalPage] = useState(0);
|
|
56
62
|
const [totalPages, setTotalPages] = useState(1);
|
|
57
63
|
const lastSource = useRef<string | null>(null);
|
|
58
64
|
|
|
@@ -64,7 +70,7 @@ export default function OptimizedPdfView({
|
|
|
64
70
|
setProgress(0);
|
|
65
71
|
setError(null);
|
|
66
72
|
setLocalPath(null);
|
|
67
|
-
|
|
73
|
+
setInternalPage(0);
|
|
68
74
|
setTotalPages(1);
|
|
69
75
|
lastSource.current = source.uri;
|
|
70
76
|
|
|
@@ -96,23 +102,23 @@ export default function OptimizedPdfView({
|
|
|
96
102
|
}, [source.uri, source.cache, source.cacheFileName, source.expiration, onError, source]);
|
|
97
103
|
|
|
98
104
|
const handleNextPage = () => {
|
|
99
|
-
if (
|
|
100
|
-
const newPage =
|
|
101
|
-
|
|
105
|
+
if (internalPage < totalPages - 1) {
|
|
106
|
+
const newPage = internalPage + 1;
|
|
107
|
+
setInternalPage(newPage);
|
|
102
108
|
onPageChange?.(newPage);
|
|
103
109
|
}
|
|
104
110
|
};
|
|
105
111
|
|
|
106
112
|
const handlePrevPage = () => {
|
|
107
|
-
if (
|
|
108
|
-
const newPage =
|
|
109
|
-
|
|
113
|
+
if (internalPage > 0) {
|
|
114
|
+
const newPage = internalPage - 1;
|
|
115
|
+
setInternalPage(newPage);
|
|
110
116
|
onPageChange?.(newPage);
|
|
111
117
|
}
|
|
112
118
|
};
|
|
113
119
|
|
|
114
120
|
const handlePageChange = (newPage: number) => {
|
|
115
|
-
|
|
121
|
+
setInternalPage(newPage);
|
|
116
122
|
onPageChange?.(newPage);
|
|
117
123
|
};
|
|
118
124
|
|
|
@@ -143,7 +149,7 @@ export default function OptimizedPdfView({
|
|
|
143
149
|
<View style={[{ flex: 1 }, style]}>
|
|
144
150
|
<NativeOptimizedPdfView
|
|
145
151
|
source={localPath}
|
|
146
|
-
page={page}
|
|
152
|
+
page={showNavigationControls ? internalPage : page || 0}
|
|
147
153
|
enableAntialiasing={enableAntialiasing}
|
|
148
154
|
maximumZoom={maximumZoom}
|
|
149
155
|
password={password || ''}
|
|
@@ -155,7 +161,7 @@ export default function OptimizedPdfView({
|
|
|
155
161
|
/>
|
|
156
162
|
{showNavigationControls && (
|
|
157
163
|
<PdfNavigationControls
|
|
158
|
-
currentPage={
|
|
164
|
+
currentPage={internalPage}
|
|
159
165
|
totalPages={totalPages}
|
|
160
166
|
onNextPage={handleNextPage}
|
|
161
167
|
onPrevPage={handlePrevPage}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
|
|
2
|
+
import { View, Text, TouchableOpacity, TextInput, StyleSheet } from 'react-native';
|
|
3
3
|
import type { PdfNavigationControlsProps } from '../types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';
|
|
3
3
|
import type { PdfLoadingOverlayProps, PdfErrorOverlayProps } from '../types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -8,7 +8,7 @@ import type { PdfLoadingOverlayProps, PdfErrorOverlayProps } from '../types';
|
|
|
8
8
|
export const PdfLoadingOverlay: React.FC<PdfLoadingOverlayProps> = ({ progress, style }) => {
|
|
9
9
|
return (
|
|
10
10
|
<View style={[styles.container, style]}>
|
|
11
|
-
<ActivityIndicator size="large" color="#
|
|
11
|
+
<ActivityIndicator size="large" color="#000000" />
|
|
12
12
|
<Text style={styles.progressText}>{progress}%</Text>
|
|
13
13
|
</View>
|
|
14
14
|
);
|
|
@@ -33,7 +33,7 @@ const styles = StyleSheet.create({
|
|
|
33
33
|
padding: 20,
|
|
34
34
|
},
|
|
35
35
|
progressText: {
|
|
36
|
-
color: '#
|
|
36
|
+
color: '#000000',
|
|
37
37
|
marginTop: 10,
|
|
38
38
|
fontSize: 16,
|
|
39
39
|
fontWeight: '600',
|
package/src/types/index.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface PdfErrorEvent {
|
|
|
41
41
|
export interface OptimizedPdfViewProps {
|
|
42
42
|
/** PDF source configuration */
|
|
43
43
|
source: PdfSource;
|
|
44
|
+
/** Current page index (0-based) */
|
|
45
|
+
page?: number;
|
|
44
46
|
/** Password for encrypted PDF files */
|
|
45
47
|
password?: string;
|
|
46
48
|
/** Maximum zoom level. Default: 3 */
|
|
@@ -110,3 +112,26 @@ export interface PdfErrorOverlayProps {
|
|
|
110
112
|
error: string;
|
|
111
113
|
style?: ViewStyle;
|
|
112
114
|
}
|
|
115
|
+
|
|
116
|
+
export interface NativeOptimizedPdfViewProps {
|
|
117
|
+
/** PDF source configuration */
|
|
118
|
+
source: string;
|
|
119
|
+
/** Current page index (0-based) */
|
|
120
|
+
page: number;
|
|
121
|
+
/** Password for encrypted PDF files */
|
|
122
|
+
password?: string;
|
|
123
|
+
/** Maximum zoom level. Default: 3 */
|
|
124
|
+
maximumZoom?: number;
|
|
125
|
+
/** Enable antialiasing for better rendering quality. Default: true */
|
|
126
|
+
enableAntialiasing?: boolean;
|
|
127
|
+
/** Callback when PDF is loaded successfully */
|
|
128
|
+
onLoadComplete?: (event: NativeLoadCompleteEvent) => void;
|
|
129
|
+
/** Callback when an error occurs */
|
|
130
|
+
onError?: (error: PdfErrorEvent) => void;
|
|
131
|
+
/** Callback when page count is available */
|
|
132
|
+
onPageCount?: (event: NativePageCountEvent) => void;
|
|
133
|
+
/** Callback when page changes */
|
|
134
|
+
onPasswordRequired?: () => void;
|
|
135
|
+
/** Custom style for the container */
|
|
136
|
+
style?: ViewStyle;
|
|
137
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
|
+
// "extends": "@react-native/typescript-config/tsconfig.json",
|
|
2
3
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"module": "
|
|
5
|
-
"lib": ["es6"],
|
|
6
|
-
"allowJs": true,
|
|
7
|
-
"jsx": "react-native",
|
|
8
|
-
"noEmit": true,
|
|
9
|
-
"isolatedModules": true,
|
|
10
|
-
"strict": true,
|
|
11
|
-
"moduleResolution": "node",
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "ESNext",
|
|
12
6
|
"esModuleInterop": true,
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"jsx": "react-native",
|
|
13
9
|
"skipLibCheck": true,
|
|
14
|
-
"
|
|
10
|
+
"strict": true,
|
|
11
|
+
"noUnusedLocals": true,
|
|
12
|
+
"noUnusedParameters": true,
|
|
13
|
+
"noEmit": false,
|
|
14
|
+
"checkJs": false,
|
|
15
|
+
"allowJs": false,
|
|
16
|
+
"noImplicitAny": false,
|
|
17
|
+
"declaration": true,
|
|
18
|
+
"paths": {
|
|
19
|
+
"react-native-calendars": ["./src/index.ts"]
|
|
20
|
+
},
|
|
15
21
|
},
|
|
22
|
+
"include": ["src/**/*", "example/App.tsx"],
|
|
16
23
|
"exclude": ["node_modules", "**/__tests__/**"]
|
|
17
24
|
}
|