react-native-optimized-pdf 1.1.0 → 1.3.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/CHANGELOG.md +36 -19
- package/CODE_OF_CONDUCT.md +128 -0
- package/LICENSE +21 -0
- package/README.md +50 -35
- 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 +1 -1
- package/ios/Core/PDFDocumentLoader.swift +7 -4
- package/ios/Core/PDFError.swift +0 -9
- package/ios/ReactNative/OptimizedPDFViewManager.swift +0 -28
- package/ios/ReactNative/OptimizedPdfViewManager.m +0 -11
- package/ios/Views/OptimizedPDFView.swift +31 -54
- package/ios/Views/TiledPDFPageView.swift +188 -49
- package/package.json +27 -14
- package/src/OptimizedPdfView.tsx +21 -19
- package/src/components/PdfNavigationControls.tsx +1 -1
- package/src/components/PdfOverlays.tsx +3 -3
- package/src/constants.ts +0 -2
- package/src/services/pdfCache.ts +9 -3
- package/src/types/index.ts +25 -2
- package/tsconfig.json +17 -10
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
|
|
|
@@ -135,15 +141,11 @@ export default function OptimizedPdfView({
|
|
|
135
141
|
return <PdfErrorOverlay error={error} style={style} />;
|
|
136
142
|
}
|
|
137
143
|
|
|
138
|
-
if (!localPath) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
144
|
return (
|
|
143
145
|
<View style={[{ flex: 1 }, style]}>
|
|
144
146
|
<NativeOptimizedPdfView
|
|
145
|
-
source={localPath}
|
|
146
|
-
page={page}
|
|
147
|
+
source={localPath as string}
|
|
148
|
+
page={showNavigationControls ? internalPage : page || 0}
|
|
147
149
|
enableAntialiasing={enableAntialiasing}
|
|
148
150
|
maximumZoom={maximumZoom}
|
|
149
151
|
password={password || ''}
|
|
@@ -155,7 +157,7 @@ export default function OptimizedPdfView({
|
|
|
155
157
|
/>
|
|
156
158
|
{showNavigationControls && (
|
|
157
159
|
<PdfNavigationControls
|
|
158
|
-
currentPage={
|
|
160
|
+
currentPage={internalPage}
|
|
159
161
|
totalPages={totalPages}
|
|
160
162
|
onNextPage={handleNextPage}
|
|
161
163
|
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/constants.ts
CHANGED
package/src/services/pdfCache.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import RNFS from 'react-native-fs';
|
|
1
|
+
import * as RNFS from '@dr.pogodin/react-native-fs';
|
|
2
2
|
import md5 from 'crypto-js/md5';
|
|
3
3
|
import type { PdfSource } from '../types';
|
|
4
4
|
|
|
@@ -33,11 +33,12 @@ export class PdfCacheService {
|
|
|
33
33
|
// Check if cache has expired
|
|
34
34
|
const stat = await RNFS.stat(localPath);
|
|
35
35
|
const now = Date.now() / 1000;
|
|
36
|
-
|
|
36
|
+
const modifiedAt = stat.mtime.getTime() / 1000;
|
|
37
|
+
return now - modifiedAt < source.expiration;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
|
-
* Download a PDF file and cache it locally
|
|
41
|
+
* Download a PDF file and cache it locally, or return a local file directly
|
|
41
42
|
* @param source PDF source configuration
|
|
42
43
|
* @param onProgress Optional callback for download progress
|
|
43
44
|
* @returns Local file path
|
|
@@ -46,6 +47,11 @@ export class PdfCacheService {
|
|
|
46
47
|
source: PdfSource,
|
|
47
48
|
onProgress?: (percent: number) => void,
|
|
48
49
|
): Promise<string> {
|
|
50
|
+
// Local files are already readable by the native viewer.
|
|
51
|
+
if (source.uri.startsWith('file://') || source.uri.startsWith('/')) {
|
|
52
|
+
return source.uri;
|
|
53
|
+
}
|
|
54
|
+
|
|
49
55
|
const localPath = this.getCacheFilePath(source);
|
|
50
56
|
|
|
51
57
|
// Check if we can use cached version
|
package/src/types/index.ts
CHANGED
|
@@ -12,8 +12,6 @@ export interface PdfSource {
|
|
|
12
12
|
cacheFileName?: string;
|
|
13
13
|
/** Cache expiration time in seconds. If not set, cache never expires */
|
|
14
14
|
expiration?: number;
|
|
15
|
-
/** HTTP method for download. Default: 'GET' */
|
|
16
|
-
method?: string;
|
|
17
15
|
/** HTTP headers for download request */
|
|
18
16
|
headers?: Record<string, string>;
|
|
19
17
|
}
|
|
@@ -41,6 +39,8 @@ export interface PdfErrorEvent {
|
|
|
41
39
|
export interface OptimizedPdfViewProps {
|
|
42
40
|
/** PDF source configuration */
|
|
43
41
|
source: PdfSource;
|
|
42
|
+
/** Current page index (0-based) */
|
|
43
|
+
page?: number;
|
|
44
44
|
/** Password for encrypted PDF files */
|
|
45
45
|
password?: string;
|
|
46
46
|
/** Maximum zoom level. Default: 3 */
|
|
@@ -110,3 +110,26 @@ export interface PdfErrorOverlayProps {
|
|
|
110
110
|
error: string;
|
|
111
111
|
style?: ViewStyle;
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
export interface NativeOptimizedPdfViewProps {
|
|
115
|
+
/** PDF source configuration */
|
|
116
|
+
source: string;
|
|
117
|
+
/** Current page index (0-based) */
|
|
118
|
+
page: number;
|
|
119
|
+
/** Password for encrypted PDF files */
|
|
120
|
+
password?: string;
|
|
121
|
+
/** Maximum zoom level. Default: 3 */
|
|
122
|
+
maximumZoom?: number;
|
|
123
|
+
/** Enable antialiasing for better rendering quality. Default: true */
|
|
124
|
+
enableAntialiasing?: boolean;
|
|
125
|
+
/** Callback when PDF is loaded successfully */
|
|
126
|
+
onLoadComplete?: (event: NativeLoadCompleteEvent) => void;
|
|
127
|
+
/** Callback when an error occurs */
|
|
128
|
+
onError?: (error: PdfErrorEvent) => void;
|
|
129
|
+
/** Callback when page count is available */
|
|
130
|
+
onPageCount?: (event: NativePageCountEvent) => void;
|
|
131
|
+
/** Callback when page changes */
|
|
132
|
+
onPasswordRequired?: () => void;
|
|
133
|
+
/** Custom style for the container */
|
|
134
|
+
style?: ViewStyle;
|
|
135
|
+
}
|
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
|
}
|