A convenience wrapper for building react native apps with PDFTron mobile SDK
PDFTron React Native Wrapper
A convenience wrapper for building react native apps with PDFTron mobile SDK.
Prerequisites
- No license key is required for trial. However, a valid commercial license key is required after trial.
- npm or yarn
- PDFTron SDK >= 6.10.0
- react-native >= 0.60.0 (for versions before 0.60.0, use branch
rn553
)
Preview
Android | iOS |
---|---|
Legacy UI
Version 2.0.2
is the last stable release for the legacy UI.
The release can be found here: https://github.com/PDFTron/pdftron-react-native/releases/tag/legacy-ui.
Installation
-
If using yarn, do:
yarn global add react-native-cli
-
First, follow the official getting started guide on setting up the React Native environment, setting up the iOS and Android environment, and creating a React Native project, the following steps will assume your app is created through
react-native init MyApp
. -
In
MyApp
folder, installreact-native-pdftron
by calling:yarn add github:PDFTron/pdftron-react-native yarn add @react-native-community/cli --dev yarn add @react-native-community/cli-platform-android --dev yarn add @react-native-community/cli-platform-ios --dev yarn install
or
npm install github:PDFTron/pdftron-react-native --save npm install @react-native-community/cli --save-dev npm install @react-native-community/cli-platform-android --save-dev npm install @react-native-community/cli-platform-ios --save-dev
Android
-
Add the following in your
android/app/build.gradle
file:android { compileSdkVersion rootProject.ext.compileSdkVersion compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } defaultConfig { applicationId "com.reactnativesample" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" + multiDexEnabled true + manifestPlaceholders = [pdftronLicenseKey:PDFTRON_LICENSE_KEY] } dependencies { + implementation "androidx.multidex:multidex:2.0.1" } ... }
-
Add the following to your
android/build.gradle
file:buildscript { ext { buildToolsVersion = "28.0.3" + minSdkVersion = 21 compileSdkVersion = 28 targetSdkVersion = 28 } // ... }
-
In your
android/gradle.properties
file. Add the following line to it:# Add the PDFTRON_LICENSE_KEY variable here. # For trial purposes leave it blank. # For production add a valid commercial license key. PDFTRON_LICENSE_KEY=
-
Add the following to your
android/app/src/main/AndroidManifest.xml
file:<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.myapp"> <uses-permission android:name="android.permission.INTERNET" /> <!-- Required to read and write documents from device storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Required if you want to record audio annotations --> + <uses-permission android:name="android.permission.RECORD_AUDIO" /> <application ... + android:largeHeap="true" + android:usesCleartextTraffic="true"> <!-- Add license key in meta-data tag here. This should be inside the application tag. --> + <meta-data + android:name="pdftron_license_key" + android:value="${pdftronLicenseKey}"/> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" - android:windowSoftInputMode="adjustResize" + android:windowSoftInputMode="adjustPan" + android:theme="@style/CustomAppTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> </application> </manifest>
-
In your
android\app\src\main\java\com\myapp\MainApplication.java
file, changeApplication
toMultiDexApplication
:- import android.app.Application; + import androidx.multidex.MultiDexApplication; ... - public class MainApplication extends Application implements ReactApplication { + public class MainApplication extends MultiDexApplication implements ReactApplication {
-
Replace
App.js
with what is shown here -
Finally in the root project directory, run
react-native run-android
.
iOS
-
Open
Podfile
in theios
folder, add the followng line to thetarget 'MyApp' do ... end
block:target 'MyApp' do # ... pod 'PDFNet', podspec: 'https://www.pdftron.com/downloads/ios/cocoapods/pdfnet/latest.podspec' # ... end
-
In the
ios
folder, runpod install
. -
(Optional) If you need a close button icon, you will need to add the PNG resources to
MyApp
as well, i.e.ic_close_black_24px
. -
Replace
App.js
with what is shown here. -
Finally in the root project directory, run
react-native run-ios
.
Usage
Replace App.js
with the following:
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
PermissionsAndroid,
BackHandler,
NativeModules,
Alert
} from 'react-native';
import { DocumentView, RNPdftron } from 'react-native-pdftron';
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
permissionGranted: Platform.OS === 'ios' ? true : false
};
RNPdftron.initialize("Insert commercial license key here after purchase");
RNPdftron.enableJavaScript(true);
}
componentDidMount() {
if (Platform.OS === 'android') {
this.requestStoragePermission();
}
}
async requestStoragePermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
this.setState({
permissionGranted: true
});
console.log("Storage permission granted");
} else {
this.setState({
permissionGranted: false
});
console.log("Storage permission denied");
}
} catch (err) {
console.warn(err);
}
}
onLeadingNavButtonPressed = () => {
console.log('leading nav button pressed');
if (Platform.OS === 'ios') {
Alert.alert(
'App',
'onLeadingNavButtonPressed',
[
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{ cancelable: true }
)
} else {
BackHandler.exitApp();
}
}
render() {
if (!this.state.permissionGranted) {
return (
<View style={styles.container}>
<Text>
Storage permission required.
</Text>
</View>
)
}
const path = "https://pdftron.s3.amazonaws.com/downloads/pl/PDFTRON_mobile_about.pdf";
return (
<DocumentView
document={path}
showLeadingNavButton={true}
leadingNavButtonIcon={Platform.OS === 'ios' ? 'ic_close_black_24px.png' : 'ic_arrow_back_white_24dp'}
onLeadingNavButtonPressed={this.onLeadingNavButtonPressed}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
- (iOS) For app bundle file path:
document="sample"
- (Android) For local storage file path:
document="file:///storage/emulated/0/Download/sample.pdf"
- (Android) For raw resource path (include file extension):
document="android.resource://mypackagename/raw/sample.pdf"
- (Android) For content Uri:
document="content://..."
API
RNPdftron
initialize
initialize(string)
enableJavaScript
enableJavaScript(bool)
getVersion
getVersion()
Return a promise with the version of the PDFNet version used.
getPlatformVersion
getPlatformVersion()
Return a promise with the version of current platform (Android/iOS).
encryptDocument
encryptDocument(string, string, string)
This function does not lock around the document so be sure to not use it while the document is opened in the viewer.
Return a promise.
Example:
RNPdftron.encryptDocument("/sdcard/Download/new.pdf", "1111", "").then(() => {
console.log("done password");
});
Parameters:
Name | Type | Description |
---|---|---|
file path | string | the local file path to the file |
password | string | the password |
current password | string | the current password, use empty string if no password |
Components
DocumentView
A component for displaying documents of different types such as PDF, docx, pptx, xlsx and various image formats.
Props
document
string, required
password
string, optional
leadingNavButtonIcon
string, optional
onLeadingNavButtonPressed
function, optional
showLeadingNavButton
bool, optional
onDocumentLoaded
function, optional
onDocumentError
function, optional
disabledElements
array of string, optional
disabledTools
array of string, optional
customHeaders
object, optional
readOnly
bool, optional, default to false
thumbnailViewEditingEnabled
bool, optional, default to true
annotationAuthor
string, optional
continuousAnnotationEditing
bool, optional
selectAnnotationAfterCreation
bool, optional, default to true
fitMode
string, optional
layoutMode
string, optional
initialPageNumber
number, optional
pageNumber
number, optional
onPageChanged
function, optional
onZoomChanged
function, optional
Perameters:
Name | Type | Description |
---|---|---|
previousPageNumber | int | the previous page number |
pageNumber | int | the current page number |
topToolbarEnabled
Deprecated. Use hideTopAppNavBar
prop instead.
bool, optional
bottomToolbarEnabled
bool, optional
annotationToolbars
array of object, options
Defines custom toolbars. If passed in, default toolbars will no longer appear.
It is possible to mix and match with default toolbars. See example below:
const myToolbar = {
[Config.CustomToolbarKey.Id]: 'myToolbar',
[Config.CustomToolbarKey.Name]: 'myToolbar',
[Config.CustomToolbarKey.Icon]: Config.ToolbarIcons.FillAndSign,
[Config.CustomToolbarKey.Items]: [Config.Tools.annotationCreateArrow, Config.Tools.annotationCreateCallout, Config.Buttons.undo]
};
annotationToolbars={[Config.DefaultToolbars.Annotate, myToolbar]}
hideDefaultAnnotationToolbars
array of Config.DefaultToolbars
tags, optional
Defines which default toolbars should be hidden. Default to none.
hideAnnotationToolbarSwitcher
bool, optional
Defines whether to show the toolbar switcher in the top toolbar. Default to false.
hideTopToolbars
bool, optional
Defines whether to show both the top nav app bar and the annotation toolbar. Default to false.
hideTopAppNavBar
bool, optional
Defines whether to show the top nav app bar. Default to false.
hideToolbarsOnTap
bool, optional
Whether an unhandled tap in the viewer should toggle the visibility of the top and bottom toolbars. The default value is true
. When false
, the top and bottom toolbar visibility will not be toggled and the page content will fit between the bars, if any.
pageIndicatorEnabled
bool, optional
showSavedSignatures
bool, optional
isBase64String
bool, optional
If true, document
prop will be treated as a base64 string.
When viewing a document initialized with a base64 string (ie a memory buffer), a temporary file is created on Android, and no temporary path is created on iOS.
padStatusBar
bool, optional, android only
If true, the viewer will add padding to take account of status bar. Default to false.
autoSaveEnabled
bool, optional
hideAnnotationMenu
array of Config.Tools
string constants, optional
Defines annotation types that will not show the default annotation menu
annotationMenuItems
array of Config.AnnotationMenu
string constants, optional
Defines menu items that can show when an annotation is selected.
overrideAnnotationMenuBehavior
array of Config.AnnotationMenu
string constants, optional
Defines menu items that should skip default behavior.
onAnnotationMenuPress
function, optional
Defines what happens on annotation menu press if it is passed in to overrideAnnotationMenuBehavior
Parameters:
Name | Type | Description |
---|---|---|
annotationMenu | string | One of Config.AnnotationMenu string constants |
annotations | array | An array of {id, rect} objects, where id is the annotation identifier and rect={x1, y1, x2, y2} specifies the annotation's screen rect. |
longPressMenuEnabled
bool, optional, default to true
If true, the viewer will show the default menu on long press.
longPressMenuItems
array of Config.LongPressMenu
string constants, optional
Defines menu items that can show when long press on text or blank space.
overrideLongPressMenuBehavior
array of Config.LongPressMenu
string constants, optional
Defines menu items that should skip default behavior.
onLongPressMenuPress
function, optional
Defines what happens on long press menu press if it is passed in to overrideLongPressMenuBehavior
Parameters:
Name | Type | Description |
---|---|---|
longPressMenu | string | One of Config.LongPressMenu string constants |
longPressText | string | the selected text if pressed on text, empty otherwise |
overrideBehavior
array of Config.Actions
string constants, optional
Defines actions that should skip default behavior, such as external link click.
onBehaviorActivated
function, optional
Defines what happens on certain behavior if it is passed in to overrideBehavior
Parameters:
Name | Type | Description |
---|---|---|
action | string | One of Config.Actions string constants |
data | object | A JSON object that varies depending on the action |
data param table:
Action | Param |
---|---|
Config.Actions.linkPress |
key: url , value: the link pressed |
pageChangeOnTap
bool, optional, default to true
useStylusAsPen
bool, optional, default to true
If true, stylus will act as a pen in pan mode, otherwise it will act as finger
multiTabEnabled
bool, optional, default to false
If true, viewer will show multiple tabs for documents opened.
tabTitle
string, optional, default to file name, takes effect when multiTabEnabled is true.
signSignatureFieldsWithStamps
bool, optional, default to false
If true, signature field will be signed with image stamp.
This is useful if you are saving XFDF to remote source.
followSystemDarkMode
bool, optional, Android only, default to true
If true, UI will appear in dark color when System is dark mode. Otherwise it will use viewer setting instead.
collabEnabled
bool, optional, if set to true then currentUser
must be set as well for collaboration mode to work
currentUser
string, required if collabEnabled
is set to true
currentUserName
string, optional
onExportAnnotationCommand
function, optional, annotation command will be given on each edit
onAnnotationsSelected
function, optional
Parameters:
Name | Type | Description |
---|---|---|
annotations | array | array of annotation data in the format {id: string, pageNumber: number, rect: {x1: number, y1: number, x2: number, y2: number}} |
onAnnotationChanged
function, optional
Parameters:
Name | Type | Description |
---|---|---|
action | string | the action that occurred (add, delete, modify) |
annotations | array | array of annotation data in the format {id: string, pageNumber: number} |
annotationPermissionCheckEnabled
bool, optional, default to false
If true, annotation's flags will be taken into account when it is selected, for example, a locked annotation can not be resized or moved.
onFormFieldValueChanged
function, optional
Parameters:
Name | Type | Description |
---|---|---|
fields | array | array of field data in the format {fieldName: string, fieldValue: string} |
onBookmarkChanged
function, optional
Defines what happens if a change has been made to bookmarks
Parameters:
Name | Type | Description |
---|---|---|
bookmarkJson | string | the list of current bookmarks in JSON format |
Example:
import { DocumentView, Config } from 'react-native-pdftron';
<DocumentView
ref={(c) => this._viewer = c}
document={path}
showLeadingNavButton={true}
leadingNavButtonIcon={Platform.OS === 'ios' ? 'ic_close_black_24px.png' : 'ic_arrow_back_white_24dp'}
onLeadingNavButtonPressed={() => {}}
onDocumentLoaded={() => {}}
onDocumentError={() => {}}
disabledElements={[Config.Buttons.searchButton, Config.Buttons.shareButton]}
disabledTools={[Config.Tools.annotationCreateLine, Config.Tools.annotationCreateRectangle]}
customHeaders={{Foo: bar}}
initialPageNumber={11}
readOnly={false}
annotationAuthor={'PDFTron'}
continuousAnnotationEditing={true}
fitMode={Config.FitMode.FitPage}
layoutMode={Config.LayoutMode.Continuous}
onPageChanged={({previousPageNumber, pageNumber}) => { console.log('page changed'); }}
onAnnotationChanged={({action, annotations}) => { console.log('annotations changed'); }}
annotationPermissionCheckEnabled={false}
onBookmarkChanged={({bookmarkJson}) => { console.log('bookmark changed'); }}
/>
Methods
setToolMode
To set the current tool mode (Config.Tools
constants).
this._viewer.setToolMode(Config.Tools.annotationCreateFreeHand);
commitTool
Commits the current tool, only available for multi-stroke ink and poly-shape.
Returns a Promise.
this._viewer.commitTool().then((committed) => {
// committed: true if either ink or poly-shape tool is committed, false otherwise
});
getPageCount
To get the current page count of the document.
Returns a Promise.
this._viewer.getPageCount().then((pageCount) => {
console.log('pageCount', pageCount);
});
importAnnotations
To import XFDF string to the current document.
Returns a Promise.
const xfdf = '<?xml version="1.0" encoding="UTF-8"?>\n<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">...</xfdf>';
this._viewer.importAnnotations(xfdf);
exportAnnotations
To extract XFDF from the current document.
Perameters:
Name | Type | Description |
---|---|---|
options | object | key: annotList, type: array |
Returns a Promise.
this._viewer.exportAnnotations().then((xfdf) => {
console.log('xfdf', xfdf);
});
With options:
// annotList is an array of annotation data in the format {id: string, pageNumber: int}
this._viewer.exportAnnotations({annotList: annotations}).then((xfdf) => {
console.log('xfdf for annotations', xfdf);
});
flattenAnnotations
To flatten the forms and (optionally) annotations in the current document. The formsOnly
parameter controls whether only forms are flattened.
Returns a Promise.
// flatten forms and annotations in the current document.
this._viewer.flattenAnnotations(false);
deleteAnnotations
To delete the specified annotations in the current document.
Returns a Promise.
// delete annotations in the current document.
this._viewer.deleteAnnotations([
{
id: 'annotId1',
pageNumber: 1,
},
{
id: 'annotId2',
pageNumber: 2,
}
]);
saveDocument
To save the current document.
Returns a Promise.
this._viewer.saveDocument().then((filePath) => {
console.log('saveDocument:', filePath);
});
setFlagForFields
Set a field flag value on one or more form fields.
Parameters:
Name | Type | Description |
---|---|---|
fields | array | list of field names for which the flag should be set |
flag | integer | flag to be set (see https://www.pdftron.com/api/ios/Enums/PTFieldFlag.html) |
value | bool | value to set for flag |
Returns a Promise.
this._viewer.setFlagForFields(['First Name', 'Last Name'], Config.FieldFlags.ReadOnly, true);
setValuesForFields
Set field values on one or more form fields.
Note: the old function setValueForFields
is deprecated. Please use this one.
Parameters:
Name | Type | Description |
---|---|---|
fieldsMap | object | map of field names and values which should be set |
Returns a Promise.
this._viewer.setValuesForFields({
'textField1': 'Test',
'textField2': 1234,
'checkboxField1': true,
'checkboxField2': false,
'radioButton1': 'Yes',
'radioButton2': 'No'
});
importAnnotationCommand
Import remote annotation command to local document.
Parameters:
Name | Type | Description |
---|---|---|
xfdfCommand | string | the XFDF command string |
initialLoad | bool | whether this is for initial load |
Returns a Promise.
handleBackButton
Android only.
this._viewer.handleBackButton().then((handled) => {
if (!handled) {
BackHandler.exitApp();
}
});
selectAnnotation
To select the specified annotation in the current document.
Parameters:
Name | Type | Description |
---|---|---|
id | string | the id of the target annotation |
pageNumber | integer | the page number where the targe annotation is located. It is 1-indexed |
Return a Promise.
// select annotation in the current document.
this._viewer.selectAnnotation('annotId1', 1);
setFlagsForAnnotations
To set flags for specified annotations in the current document. The flagValue
controls whether a flag will be set to or removed from the annotation.
Note: the old function setFlagForAnnotations
is deprecated. Please use this one.
Parameters:
Name | Type | Description |
---|---|---|
annotationFlagList | array | A list of annotation flag operations |
Return a Promise.
// Set flag for annotations in the current document.
this._viewer.setFlagsForAnnotations([
{
id: 'annotId1',
pageNumber: 1,
flag: Config.AnnotationFlags.noView,
flagValue: true
},
{
id: 'annotId2',
pageNumber: 5,
flag: Config.AnnotationFlags.lockedContents,
flagValue: false
}
]);
setPropertiesForAnnotation
To set properties for specified annotation in the current document, if it is valid.
Note: the old function setPropertyForAnnotation
is deprecated. Please use this one.
Parameters:
Name | Type | Description |
---|---|---|
annotationId | string | the unique id of the annotation |
pageNumber | integer | the page number where annotation is located. It is 1-indexed |
propertyMap | object | an object containing properties to be set. Available properties are listed below |
Properties:
Name | Type | Markup exclusive | Example |
---|---|---|---|
rect | object | no | {x1: 1, y1: 2, x2: 3, y2: 4} |
contents | string | no | "contents" |
subject | string | yes | "subject" |
title | string | yes | "title" |
contentRect | object | yes | {x1: 1, y1: 2, x2: 3, y2: 4} |
Return a promise.
// Set properties for annotation in the current document.
this._viewer.setPropertiesForAnnotation('Pdftron', 1, {
rect: {
x1: 1.1, // left
y1: 3, // bottom
x2: 100.9, // right
y2: 99.8 // top
},
contents: 'Hello World',
subject: 'Sample',
title: 'set-prop-for-annot'
});
getPageCropBox
Return a JSON object with properties for position (x1
, y1
, x2
and y2
) and size (width
and height
) of the crop box for specified page.
Parameters:
Name | Type | Description |
---|---|---|
pageNumber | integer | the page number for the target crop box. It is 1-indexed |
Return a Promise.
this._viewer.getPageCropBox(1).then((cropBox) => {
console.log('bottom-left coordinate:', cropBox.x1, cropBox.y1);
console.log('top-right coordinate:', cropBox.x2, cropBox.y2);
console.log('width and height:', cropBox.width, cropBox.height);
});
importBookmarkJson
Imports user bookmarks to the document. The input needs to be a valid bookmark JSON format, for example {"0":"Page 1"}.
Parameters:
Name | Type | Description |
---|---|---|
bookmarkJson | String | needs to be in valid bookmark JSON format, for example {"0": "Page 1"}. The page numbers are 1-indexed |
Return a Promise.
this._viewer.importBookmarkJson("{\"0\": \"Page 1\", \"3\": \"Page 4\"}");
setCurrentPage
Set current page of the document.
Parameters:
Name | Type | Description |
---|---|---|
pageNumber | integer | the page number for the target crop box. It is 1-indexed |
Return a Promise (with a boolean that tells whether the setting process is successful).
this._viewer.setCurrentPage(4).then((success) => {
if (success) {
console.log("Current page is set to 4.");
}
});
getDocumentPath
Return the path of the current document.
Return a Promise.
this._viewer.getDocumentPath().then((path) => {
console.log('The path to current document is: ' + path);
});
closeAllTabs
Closes all tabs in multi-tab environment.