@compdfkit_pdf_sdk/react_native
Version:
ComPDFKit for React Native is a comprehensive SDK that allows you to quickly add PDF functionality to Android, iOS, and React Native applications.
1,408 lines (1,332 loc) • 77.3 kB
text/typescript
/**
* Copyright © 2014-2026 PDF Technologies, Inc. All Rights Reserved.
*
* THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
* AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
* UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
* This notice may not be removed from this file.
*/
import { NativeModules, findNodeHandle, Platform } from "react-native";
import {
CPDFAnnotationRenderOptions,
CPDFDocumentEncryptAlgo,
CPDFDocumentPermissions,
CPDFPageCompression,
HexColor,
} from "../configuration/CPDFOptions";
import { CPDFPage, CPDFPageSize } from "../page/CPDFPage";
import { CPDFAnnotation } from "../annotation/CPDFAnnotation";
import {
CPDFAnnotationMarkState,
CPDFAnnotationReviewState,
CPDFReplyAnnotation,
} from "../annotation/CPDFReplyAnnotation";
import { CPDFImageAnnotation } from "../annotation/CPDFImageAnnotation";
import { CPDFWidget } from "../annotation/form/CPDFWidget";
import { CPDFTextSearcher } from "../page/CPDFTextSearcher";
import { normalizeColorsInAnnotation, normalizeColorsInWidget, normalizeColorToARGB } from "../util/CPDFEnumUtils";
import { CPDFImageData, CPDFImageType } from "../util/CPDFImageData";
import { CPDFInfo } from "./CPDFInfo";
import { CPDFDocumentPermissionInfo } from "./CPDFDocumentPermissionInfo";
import { CPDFOutline } from "./CPDFOutline";
import { CPDFBookmark } from "./CPDFBookmark";
import { CPDFEditorTextAttr } from "../configuration/config/CPDFContentEditorConfig";
import { CPDFEditArea } from "../edit/CPDFEditArea";
import { CPDFSignatureWidget } from "../annotation/form/CPDFSignatureWidget";
import {
CPDFWatermark,
toNativeWatermark,
} from "./CPDFWatermark";
const { CPDFViewManager } = NativeModules;
const DEFAULT_ANNOTATION_RENDER_SCALE = 3.0;
const DEFAULT_ANNOTATION_RENDER_QUALITY = 100;
export type CPDFExtractImageResult = {
success: boolean;
count: number;
directoryPath: string;
imagePaths: string[];
};
type NativeExtractImageResult = {
success?: unknown;
count?: unknown;
directory_path?: unknown;
image_paths?: unknown;
};
type NativeReplyPayload = Record<string, unknown>;
type ReplyIdentity = {
nativeId?: string;
replyKey?: string;
parentUuid?: string;
};
function stringOrUndefined(value: unknown): string | undefined {
return typeof value === "string" && value.length > 0 ? value : undefined;
}
function normalizeExtractImageResult(
result: unknown,
fallbackDirectoryPath: string
): CPDFExtractImageResult {
const nativeResult = (result && typeof result === "object")
? result as NativeExtractImageResult
: {};
const imagePaths = Array.isArray(nativeResult.image_paths)
? nativeResult.image_paths.filter((path): path is string => typeof path === "string")
: [];
const count = typeof nativeResult.count === "number" && Number.isFinite(nativeResult.count)
? nativeResult.count
: imagePaths.length;
const directoryPath = typeof nativeResult.directory_path === "string"
? nativeResult.directory_path
: fallbackDirectoryPath;
return {
success: typeof nativeResult.success === "boolean"
? nativeResult.success
: imagePaths.length > 0,
count,
directoryPath,
imagePaths,
};
}
function createAnnotationRenderOptions(
options: CPDFAnnotationRenderOptions = {}
) {
const scale = options.scale ?? DEFAULT_ANNOTATION_RENDER_SCALE;
const targetWidth = options.targetWidth;
const targetHeight = options.targetHeight;
const compression = options.compression ?? CPDFPageCompression.PNG;
const quality = options.quality ?? DEFAULT_ANNOTATION_RENDER_QUALITY;
if (scale <= 0) {
throw new Error("renderAnnotationAppearance(): scale must be greater than 0.");
}
if (targetWidth != null && targetWidth <= 0) {
throw new Error("renderAnnotationAppearance(): targetWidth must be greater than 0.");
}
if (targetHeight != null && targetHeight <= 0) {
throw new Error("renderAnnotationAppearance(): targetHeight must be greater than 0.");
}
if (quality < 1 || quality > 100) {
throw new Error("renderAnnotationAppearance(): quality must be between 1 and 100.");
}
return {
scale,
target_width: targetWidth,
target_height: targetHeight,
compression,
quality,
};
}
function normalizeNativeDate(value: unknown): Date | undefined {
if (value == null) {
return undefined;
}
const date = value instanceof Date ? value : new Date(value as string | number);
if (Number.isNaN(date.getTime())) {
return undefined;
}
return date;
}
function normalizeBookmark(bookmark: CPDFBookmark): CPDFBookmark {
return {
...bookmark,
date: normalizeNativeDate(bookmark.date),
};
}
function normalizeDocumentInfo(info: CPDFInfo): CPDFInfo {
return {
...info,
creationDate: normalizeNativeDate(info.creationDate),
modificationDate: normalizeNativeDate(info.modificationDate),
};
}
export class CPDFDocument {
private _viewerRef: any;
private _textSearcher: CPDFTextSearcher;
private _replyIdentities = new WeakMap<CPDFReplyAnnotation, ReplyIdentity>();
constructor(viewerRef: any) {
this._viewerRef = viewerRef;
this._textSearcher = new CPDFTextSearcher(this._viewerRef);
}
/**
* Get the text searcher for the current document.
* @returns The text searcher instance for the current document.
*/
get textSearcher() {
return this._textSearcher;
}
private _replyFromNative(payload: NativeReplyPayload): CPDFReplyAnnotation {
const reply = CPDFReplyAnnotation.fromJson(payload);
this._replyIdentities.set(reply, {
nativeId: stringOrUndefined(payload.nativeId ?? payload.native_id),
replyKey: stringOrUndefined(payload.replyKey ?? payload.reply_key),
parentUuid: stringOrUndefined(payload.parentUuid ?? payload.parent_uuid),
});
return reply;
}
private _replyIdentity(reply: CPDFReplyAnnotation): ReplyIdentity {
return this._replyIdentities.get(reply) ?? {};
}
private _replyIdentityPayload(reply: CPDFReplyAnnotation) {
const identity = this._replyIdentity(reply);
return {
native_id: identity.nativeId ?? null,
reply_key: identity.replyKey ?? null,
parent_uuid: identity.parentUuid ?? null,
};
}
/**
* Get the page object at the specified index
* @param pageIndex The index of the page to retrieve
* @returns The page object at the specified index
*/
pageAtIndex = (pageIndex: number): CPDFPage => {
return new CPDFPage(this._viewerRef, pageIndex);
};
/**
* Reopens a specified document in the current `CPDFReaderView` component.
*
* @example
* await pdfReaderRef.current?._pdfDocument.open(document, 'password');
*
* @param document The file path of the PDF document.
* * (Android) For a local storage file path:
* ```tsx
* document = 'file:///storage/emulated/0/Download/sample.pdf'
* ```
* * (Android) For a content URI:
* ```tsx
* document = 'content://...'
* ```
* * (Android) For an asset file path:
* ```tsx
* document = "file:///android_asset/..."
* ```
* @param password The password for the document, which can be null or empty.
* @returns A promise that resolves to `true` if the document is successfully opened, otherwise `false`.
*/
open = (
document: string,
password: string | null = null,
pageIndex: number = 0
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.open(tag, document, password, pageIndex);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Gets the file name of the PDF document.
* @example
* const fileName = await pdfReaderRef.current?._pdfDocument.getFileName();
* @returns
*/
getFileName = (): Promise<string> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.getFileName(tag);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Checks if the PDF document is encrypted.
* @example
* const isEncrypted = await pdfReaderRef.current?._pdfDocument.isEncrypted();
* @returns
*/
isEncrypted = (): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.isEncrypted(tag);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Checks if the PDF document is an image document.
* This is a time-consuming operation that depends on the document size.
* @example
* const isImageDoc = await pdfReaderRef.current?._pdfDocument.isImageDoc();
* @returns
*/
isImageDoc = (): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.isImageDoc(tag);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Gets the current document's permissions. There are three types of permissions:
* No restrictions: [CPDFDocumentPermissions.NONE]
* If the document has an open password and an owner password,
* using the open password will grant [CPDFDocumentPermissions.USER] permissions,
* and using the owner password will grant [CPDFDocumentPermissions.OWNER] permissions.
*
* @example
* const permissions = await pdfReaderRef.current?._pdfDocument.getPermissions();
* @returns
*/
getPermissions = async (): Promise<CPDFDocumentPermissions> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
const id = await CPDFViewManager.getPermissions(tag);
if (id == 0) {
return Promise.resolve(CPDFDocumentPermissions.NONE);
} else if (id == 1) {
return Promise.resolve(CPDFDocumentPermissions.USER);
} else if (id == 2) {
return Promise.resolve(CPDFDocumentPermissions.OWNER);
}
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Check if owner permissions are unlocked
*
* @example
* const unlocked = await pdfReaderRef.current?._pdfDocument.checkOwnerUnlocked();
* @returns
*/
checkOwnerUnlocked = (): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.checkOwnerUnlocked(tag);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Whether the owner password is correct.
* If the password is correct, the document will be unlocked with full owner permissions.
*
* @example
* const check = await pdfReaderRef.current?._pdfDocument.checkOwnerPassword('ownerPassword');
*
* @param password password The owner password to be verified.
* @returns A promise that resolves to `true` if the owner password is correct, otherwise `false`.
*/
checkOwnerPassword = (password: string): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.checkOwnerPassword(tag, password);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Get the total number of pages in the current document
*
* @example
* const pageCount = await pdfReaderRef.current?._pdfDocument.getPageCount();
*
* @returns
*/
getPageCount = (): Promise<number> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.getPageCount(tag);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Creates a document watermark. This changes the current document in memory
* and does not save it to disk automatically.
*
* @example
* await pdfReaderRef.current?._pdfDocument.createWatermark(
* createTextWatermark({
* textContent: 'Confidential',
* pages: [0, 1],
* textColor: '#FF0000',
* fontSize: 28,
* opacity: 0.75,
* })
* );
*/
createWatermark = (watermark: CPDFWatermark): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.createWatermark(tag, toNativeWatermark(watermark));
}
return Promise.reject("Unable to find the native view reference");
};
// /**
// * Returns the number of watermarks in the current document.
// *
// * @example
// * const count = await pdfReaderRef.current?._pdfDocument.getWatermarkCount();
// */
// getWatermarkCount = (): Promise<number> => {
// const tag = findNodeHandle(this._viewerRef);
// if (tag != null) {
// return CPDFViewManager.getWatermarkCount(tag);
// }
// return Promise.reject("Unable to find the native view reference");
// };
// /**
// * Returns a watermark at the given 0-based index, or null when not found.
// *
// * @example
// * const watermark = await pdfReaderRef.current?._pdfDocument.getWatermark(0, {
// * exportImage: true,
// * });
// */
// getWatermark = async (
// index: number,
// options: { exportImage?: boolean } = {}
// ): Promise<CPDFWatermark | null> => {
// const tag = findNodeHandle(this._viewerRef);
// if (tag != null) {
// const result = await CPDFViewManager.getWatermark(tag, index, {
// export_image: options.exportImage ?? false,
// });
// return result == null ? null : fromNativeWatermark(result as NativeWatermarkPayload);
// }
// return Promise.reject("Unable to find the native view reference");
// };
// /**
// * Returns all watermarks in the current document.
// *
// * @example
// * const watermarks = await pdfReaderRef.current?._pdfDocument.getWatermarks({
// * exportImages: false,
// * });
// */
// getWatermarks = async (
// options: { exportImages?: boolean } = {}
// ): Promise<CPDFWatermark[]> => {
// const tag = findNodeHandle(this._viewerRef);
// if (tag != null) {
// const result = await CPDFViewManager.getWatermarks(tag, {
// export_images: options.exportImages ?? false,
// });
// return Array.isArray(result)
// ? result.map((item) => fromNativeWatermark(item as NativeWatermarkPayload))
// : [];
// }
// return Promise.reject("Unable to find the native view reference");
// };
// /**
// * Updates the watermark at the given 0-based index.
// *
// * @example
// * const watermark = await pdfReaderRef.current?._pdfDocument.getWatermark(0);
// * if (watermark) {
// * await pdfReaderRef.current?._pdfDocument.updateWatermark(
// * watermark.index,
// * copyWatermark(watermark, { opacity: 0.45 })
// * );
// * }
// */
// updateWatermark = (
// index: number,
// watermark: CPDFWatermark
// ): Promise<boolean> => {
// const tag = findNodeHandle(this._viewerRef);
// if (tag != null) {
// const payload = toNativeWatermark(watermark, {
// allowEmptyImagePath: true,
// });
// return CPDFViewManager.updateWatermark(tag, index, payload);
// }
// return Promise.reject("Unable to find the native view reference");
// };
// /**
// * Removes one watermark at the given 0-based index.
// *
// * @example
// * const removed = await pdfReaderRef.current?._pdfDocument.removeWatermark(0);
// */
// removeWatermark = (index: number): Promise<boolean> => {
// const tag = findNodeHandle(this._viewerRef);
// if (tag != null) {
// return CPDFViewManager.removeWatermark(tag, index);
// }
// return Promise.reject("Unable to find the native view reference");
// };
/**
* Removes all watermarks in the current document.
*
* @example
* const removedAll = await pdfReaderRef.current?._pdfDocument.removeAllWatermarks();
*/
removeAllWatermarks = (): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.removeAllWatermarks(tag);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Remove the user password and owner permission password
* set in the document, and perform an incremental save.
*
* @example
* const result = await pdfReaderRef.current?._pdfDocument.removePassword();
* @returns
*/
removePassword = (): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.removePassword(tag);
}
return Promise.reject("Unable to find the native view reference");
};
/**
*
* This method sets the document password, including the user password for access restrictions
* and the owner password for granting permissions.
*
* - To enable permissions like printing or copying, the owner password must be set; otherwise,
* the settings will not take effect.
*
* @example
* const success = await pdfReaderRef.current?._pdfDocument.setPassword(
* 'user_password',
* 'owner_password',
* false,
* false,
* CPDFDocumentEncryptAlgo.rc4
* );
*
* @param userPassword The user password for document access restrictions.
* @param ownerPassword The owner password to grant permissions (e.g., printing, copying).
* @param allowsPrinting Whether printing is allowed (true or false).
* @param allowsCopying Whether copying is allowed (true or false).
* @param encryptAlgo The encryption algorithm to use (e.g., `CPDFDocumentEncryptAlgo.rc4`).
* @returns A promise that resolves to `true` if the password is successfully set, otherwise `false`.
*/
setPassword = (
userPassword: string,
ownerPassword: string,
allowsPrinting: boolean,
allowsCopying: boolean,
encryptAlgo: CPDFDocumentEncryptAlgo
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.setPassword(tag, {
user_password: userPassword,
owner_password: ownerPassword,
allows_printing: allowsPrinting,
allows_copying: allowsCopying,
encrypt_algo: encryptAlgo,
});
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Get the encryption algorithm of the current document
*
* @example
* const encryptAlgo = await pdfReaderRef.current?._pdfDocument.getEncryptAlgo();
* @returns
*/
getEncryptAlgo = async (): Promise<CPDFDocumentEncryptAlgo> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
const encryptAlgo = await CPDFViewManager.getEncryptAlgo(tag);
if (encryptAlgo == "noEncryptAlgo") {
return Promise.resolve(CPDFDocumentEncryptAlgo.NO_ENCRYPT_ALGO);
} else if (encryptAlgo == "rc4") {
return Promise.resolve(CPDFDocumentEncryptAlgo.RC4);
} else if (encryptAlgo == "aes128") {
return Promise.resolve(CPDFDocumentEncryptAlgo.AES128);
} else if (encryptAlgo == "aes256") {
return Promise.resolve(CPDFDocumentEncryptAlgo.AES256);
}
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Checks whether the document has been modified
*
* @example
* const hasChange = await pdfReaderRef.current?._pdfDocument.hasChange();
*
* @returns {Promise<boolean>} Returns a Promise indicating if the document has been modified.
* `true`: The document has been modified,
* `false`: The document has not been modified.
* If the native view reference cannot be found, a rejected Promise will be returned.
*/
hasChange = (): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.hasChange(tag);
}
return Promise.resolve(false);
};
/**
* Exports annotations from the current PDF document to an XFDF file.
*
* @example
* const exportXfdfFilePath = await pdfReaderRef.current?._pdfDocument.exportAnnotations();
*
* @returns The path of the XFDF file if export is successful; an empty string if the export fails.
*/
exportAnnotations = (): Promise<string> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.exportAnnotations(tag);
}
return Promise.reject("Unable to find the native view reference");
};
/**
*
* Delete all comments in the current document
* @example
* const removeResult = await pdfReaderRef.current?._pdfDocument.removeAllAnnotations();
*
* @returns
*/
removeAllAnnotations = (): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.removeAllAnnotations(tag);
}
return Promise.resolve(false);
};
/**
* Imports annotations from the specified XFDF file into the current PDF document.
* @example
* // Android - assets file
* const testXfdf = 'file:///android_asset/test.xfdf';
* const importResult = await pdfReaderRef.current?._pdfDocument.importAnnotations(testXfdf);
*
* // Android - file path
* const testXfdf = '/data/user/0/com.compdfkit.reactnative.example/xxx/xxx.xfdf';
* const importResult = await pdfReaderRef.current?._pdfDocument.importAnnotations(testXfdf);
*
* // Android - Uri
* const xfdfUri = 'content://xxxx'
* const importResult = await pdfReaderRef.current?._pdfDocument.importAnnotations(xfdfUri);
*
* // iOS
*
* @param xfdfFile Path of the XFDF file to be imported.
* @returns true if the import is successful; otherwise, false.
*/
importAnnotations = (xfdfFile: string): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.importAnnotations(tag, xfdfFile);
}
return Promise.resolve(false);
};
/**
* Imports the form data from the specified XFDF file into the current PDF document.
* @example
* const xfdfFile = '/data/user/0/com.compdfkit.reactnative.example/xxx/xxx.xfdf';
* // or use Uri on the Android Platform.
* const xfdfFile = 'content://xxxx';
* const importResult = await pdfReaderRef.current?._pdfDocument.importWidgets(xfdfFile);
*
* @param xfdfFile Path of the XFDF file to be imported.
* @returns true if the import is successful; otherwise, false.
*/
importWidgets = (xfdfFile: string): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.importWidgets(tag, xfdfFile);
}
return Promise.resolve(false);
};
/**
* exports the form data from the current PDF document to an XFDF file.
* @example
* const exportXfdfFilePath = await pdfReaderRef.current?._pdfDocument.exportWidgets();
* @returns The path of the XFDF file if export is successful; an empty string if the export fails.
*/
exportWidgets = (): Promise<string> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.exportWidgets(tag);
}
return Promise.reject(
new Error("Unable to find the native view reference")
);
};
/**
* Invokes the system's print service to print the current document.
* @example
* await pdfReaderRef.current?._pdfDocument.printDocument();
* @returns
*/
printDocument = (): Promise<void> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.printDocument(tag);
}
return Promise.reject(
new Error("Unable to find the native view reference")
);
};
/**
* Flatten all pages of the current document
* @param savePath The path to save the flattened document. On Android, you can pass a Uri.
* @param fontSubset Whether to include the font subset when saving.
* @example
* const savePath = 'file:///storage/emulated/0/Download/flatten.pdf';
* // or use Uri on the Android Platform.
* const savePath = await ComPDFKit.createUri('flatten_test.pdf', 'compdfkit', 'application/pdf');
* const fontSubset = true;
* const result = await pdfReaderRef.current?._pdfDocument.flattenAllPages(savePath, fontSubset);
* await pdfReaderRef.current?.reloadPagesPreservingPosition();
* @returns Returns 'true' if the flattened document is saved successfully, otherwise 'false'.
*/
flattenAllPages = (
savePath: string,
fontSubset: boolean
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.flattenAllPages(tag, savePath, fontSubset);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Saves the document to the specified directory.
* @example
* const savePath = 'file:///storage/emulated/0/Download/save.pdf';
* const removeSecurity = false;
* const fontSubset = true;
* const result = await pdfReaderRef.current?._pdfDocument.saveAs(savePath, removeSecurity, fontSubset);
* @param savePath Specifies the path where the document should be saved.<br>
*
* On Android, both file paths and URIs are supported. For example:
* - File path: `/data/user/0/com.compdfkit.flutter.example/cache/temp/PDF_Document.pdf`
* - URI: `content://media/external/file/1000045118`
* @param removeSecurity Whether to remove the document's password.
* @param fontSubset Whether to embed font subsets into PDF. Defaults to true.
* @returns Returns 'true' if the document is saved successfully, otherwise 'false'.
*/
saveAs = (
savePath: string,
removeSecurity: boolean,
fontSubset: boolean
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.saveAs(tag, savePath, removeSecurity, fontSubset);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Imports another PDF document and inserts it at a specified position in the current document.
*
* This method imports an external PDF document into the current document, allowing you to choose which pages to import and where to insert the document.
*
* @example
* const filePath = '/data/user/0/com.compdfkit.flutter.example/cache/temp/PDF_Document.pdf';
* const pages = [0]; // The pages to import from the document
* const insertPosition = 0; // The position to insert, 0 means insert at the beginning of the document
* const password = ''; // The password for the document, if encrypted
* const importResult = await pdfReaderRef.current?._pdfDocument.importDocument(filePath, pages, insertPosition, password);
*
* @param filePath The path of the PDF document to import. Must be a valid, accessible path on the device.
* @param pages The collection of pages to import, represented as an array of integers. If `null` or an empty array is passed, the entire document will be imported.
* @param insertPosition The position to insert the external document into the current document. This value must be provided. If not specified, the document will be inserted at the end of the current document.
* @param password The password for the document, if it is encrypted. If the document is not encrypted, an empty string `''` can be passed.
*
* @returns Returns a `Promise<boolean>` indicating whether the document import was successful.
* - `true` indicates success
* - `false` or an error indicates failure
*
* @throws If the native view reference cannot be found, the promise will be rejected with an error.
*/
importDocument = (
filePath: string,
pages: Array<number> | null = [],
insertPosition: number = -1,
password: string | null = ""
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.importDocument(tag, filePath, {
password: password,
pages: pages,
insert_position: insertPosition,
});
}
return Promise.reject(
new Error("Unable to find the native view reference")
);
};
/**
* Splits the specified pages from the current document and saves them as a new document.
*
* This function extracts the given pages from the current PDF document and saves them as a
* new document at the provided save path.
*
* @example
* const savePath = '/data/user/0/com.compdfkit.flutter.example/cache/temp/PDF_Document.pdf';
* const pages = [0, 2, 4]; // Pages to extract from the current document
* const result = await pdfReaderRef.current?.splitDocumentPages(savePath, pages);
*
* @param savePath The path where the new document will be saved.
* @param pages The array of page numbers to be extracted and saved in the new document.
*
* @returns A Promise that resolves to `true` if the operation is successful, or `false` if it fails.
*
* @throws If the native view reference is not found, the promise will be rejected with an error message.
*/
splitDocumentPages = (
savePath: string,
pages: Array<number> = []
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.splitDocumentPages(tag, savePath, pages);
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Extracts images from the current document into the specified output directory.
*
* The output path is a directory, not a single file path. The SDK writes images
* directly into this directory, creates it when needed, and does not clear
* existing files or create an extra child directory for each call.
*
* @example
* const result = await pdfReaderRef.current?._pdfDocument.extractImages(
* '/data/user/0/com.example/files/extracted-images',
* [0]
* );
*
* @param directoryPath The actual output directory where extracted images are saved.
* @param pages Zero-based page indexes. Empty or null means all pages.
* @returns A structured result containing success, count, directoryPath, and imagePaths.
*/
extractImages = (
directoryPath: string,
pages: Array<number> | null = []
): Promise<CPDFExtractImageResult> => {
if (directoryPath.trim().length === 0) {
return Promise.reject(new Error("extractImages(): directoryPath is required."));
}
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.extractImages(tag, directoryPath, pages ?? [])
.then((result: unknown) => normalizeExtractImageResult(result, directoryPath));
}
return Promise.reject("Unable to find the native view reference");
};
/**
* Retrieves the path of the current document.
* On Android, if the document was opened via a URI, the URI will be returned.
*
* This function returns the path of the document being viewed. If the document was opened
* through a file URI on Android, the URI string will be returned instead of a file path.
*
* @example
* const documentPath = await pdfReaderRef.current?._pdfDocument.getDocumentPath();
*
* @returns A promise that resolves to the path (or URI) of the current document.
* If the native view reference is not found, the promise will be rejected with an error.
*
* @throws Will reject with an error message if the native view reference cannot be found.
*/
getDocumentPath = (): Promise<string> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.getDocumentPath(tag);
}
return Promise.reject(
new Error("Unable to find the native view reference")
);
};
/**
* Inserts a blank page at the specified index in the document.
*
* This method allows adding a blank page of a specified size at a specific index within the PDF document.
* It is useful for document editing scenarios where page insertion is needed.
*
* @param pageIndex - The index position where the blank page will be inserted. Must be a valid index within the document.
* @param pageSize - The size of the blank page to insert. Defaults to A4 size if not specified.
* Custom page sizes can be used by creating an instance of `CPDFPageSize` with custom dimensions.
* @example
* const pageSize = CPDFPageSize.a4;
* // Custom page size
* // const pageSize = new CPDFPageSize(500, 800);
* const result = await pdfReaderRef.current?._pdfDocument.insertBlankPage(0, pageSize);
* @returns A Promise that resolves to a boolean value indicating the success or failure of the blank page insertion.
* Resolves to `true` if the insertion was successful, `false` otherwise.
*/
insertBlankPage(
pageIndex: number,
pageSize: CPDFPageSize = CPDFPageSize.a4
): Promise<boolean> {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.insertBlankPage(
tag,
pageIndex,
pageSize.width,
pageSize.height
);
}
return Promise.reject("Unable to find the native view reference");
}
/**
* Inserts an image as a new page into the current document at the specified index.
*
* The image will be placed on a blank page with the given dimensions. The imagePath
* should point to a valid image resource accessible by the native platform (for example,
* a file path, content URI on Android, or bundled asset path).
*
* @param pageIndex - Zero-based index at which the new image page will be inserted. Must be a valid index within the document (inserting at 0 places the page at the beginning).
* @param imagePath - Path or URI to the image to be inserted (platform-dependent). The image format should be supported by the underlying platform (e.g., PNG, JPEG).
* @param pageSize - The size of the page to create for the image. Defaults to A4 size if not provided.
* @returns A Promise that resolves to `true` if the image page was successfully inserted, or `false` if the operation failed.
*
* @example
* // Android file path:
* const imagePath = 'file:///storage/emulated/0/Download/photo.jpg';
*
* // Android content URI:
* const imagePath = 'content://media/external/images/media/12345';
*
* // Android asset path:
* const imagePath = 'file:///assets/photo.jpg';
*
* // iOS file path:
* const imagePath = 'var/mobile/Containers/Data/Application/.../Documents/photo.jpg';
*
* // insert image page at index 2
* const success = await pdfReaderRef.current?._pdfDocument.insertImagePage(2, imagePath, CPDFPageSize.custom(imageWidth, imageHeight));
*
* // need reload pages after inserting image page.
* if (success) {
* await pdfReaderRef.current?.reloadPagesPreservingPosition();
* }
*/
insertImagePage(
pageIndex: number,
imagePath: string,
pageSize: CPDFPageSize = CPDFPageSize.a4
): Promise<boolean> {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.insertImagePage(
tag,
pageIndex,
imagePath,
pageSize.width,
pageSize.height
);
}
return Promise.reject("Unable to find the native view reference");
}
/**
* Removes the pages at the specified indices from the current document.
*
* The provided array should contain zero-based page indices to remove. Behavior for duplicate
* indices or out-of-range indices depends on the native implementation; callers should ensure
* indices are valid and unique where possible.
*
* @param pageIndices - An array of zero-based page indices identifying the pages to remove.
* @returns A Promise that resolves to `true` if the pages were successfully removed, or `false` on failure.
*
* @example
* const result = await pdfReaderRef.current?._pdfDocument.removePages([0, 2, 5]);
* // need reload pages after removing pages.
* if (result){
* await pdfReaderRef.current?.reloadPagesPreservingPosition();
* }
*/
removePages(pageIndices: Array<number>): Promise<boolean> {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.removePages(tag, pageIndices);
}
return Promise.reject("Unable to find the native view reference");
}
/**
* Copies a page and inserts the duplicated page at the target index.
*
* Both indexes are zero-based. `pageIndex` must point to an existing page.
* `insertIndex` accepts `0..pageCount`, and `-1` appends the copied page to
* the end of the current document.
*
* @param pageIndex - The zero-based index of the source page to duplicate.
* @param insertIndex - The zero-based insertion index for the copied page, or `-1` to append.
* @returns A Promise that resolves to `true` if the page was copied successfully, or `false` otherwise.
*
* @example
* const copied = await pdfReaderRef.current?._pdfDocument.copyPage(0, -1);
* if (copied) {
* await pdfReaderRef.current?.reloadPagesPreservingPosition();
* }
*/
copyPage(pageIndex: number, insertIndex: number): Promise<boolean> {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.copyPage(tag, pageIndex, insertIndex);
}
return Promise.reject("Unable to find the native view reference");
}
/**
* Moves a page from one index to another within the current document.
*
* This operation reorders pages so that the page originally at `fromIndex` will be placed
* at `toIndex`. Both indices are zero-based. If `toIndex` is greater than the current page
* count or either index is invalid, the operation may fail.
*
* @param fromIndex - The zero-based index of the page to move.
* @param toIndex - The zero-based target index where the page should be inserted.
* @returns A Promise that resolves to `true` if the page was moved successfully, or `false` if the operation failed.
*
* @example
* const moved = await pdfReaderRef.current?._pdfDocument.movePage(4, 1);
* // need reload pages after moving page.
* if (moved){
* await pdfReaderRef.current?.reloadPagesPreservingPosition();
* }
*/
movePage(fromIndex: number, toIndex: number): Promise<boolean> {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.movePage(tag, fromIndex, toIndex);
}
return Promise.reject("Unable to find the native view reference");
}
/**
* Removes an annotation from the current page.
* @param annotation The annotation to be removed.
* @example
* await pdfReaderRef?.current?._pdfDocument.removeAnnotation(annotation);
*/
removeAnnotation(annotation: CPDFAnnotation): Promise<boolean> {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.removeAnnotation(
tag,
annotation.page,
annotation.uuid
);
}
return Promise.reject(
new Error("Unable to find the native view reference")
);
}
/**
* Adds a plain reply to an annotation.
*
* Mark and review state replies are managed by dedicated state APIs and are
* not created by this method.
*
* @example
* const annotations = await pdfReaderRef.current?._pdfDocument
* .pageAtIndex(0)
* .getAnnotations();
* const reply = await pdfReaderRef.current?._pdfDocument.addAnnotationReply(
* annotations[0],
* { content: 'Please review this highlight.', title: 'ComPDFKit' }
* );
*
* @param annotation The parent annotation to reply to.
* @param options Reply content and optional author/title.
* @returns The created plain reply annotation, or null if creation failed.
*/
addAnnotationReply = async (
annotation: CPDFAnnotation,
options: { content: string; title?: string }
): Promise<CPDFReplyAnnotation | null> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
const reply = await CPDFViewManager.addAnnotationReply(
tag,
annotation.page,
annotation.uuid,
options.content,
options.title ?? ""
);
return reply?.uuid ? this._replyFromNative(reply) : null;
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Gets all replies attached to an annotation.
*
* Returned reply objects include their content, markState, and reviewState.
* Native mark/review state replies are implementation details and are not
* exposed through a public replyType field.
*
* @example
* const replies = await pdfReaderRef.current?._pdfDocument
* .getAnnotationReplies(annotation);
*
* @param annotation The parent annotation.
* @returns Replies attached to the annotation.
*/
getAnnotationReplies = async (
annotation: CPDFAnnotation
): Promise<CPDFReplyAnnotation[]> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
const replies = await CPDFViewManager.getAnnotationReplies(
tag,
annotation.page,
annotation.uuid
);
return Array.isArray(replies)
? replies.map((item) => this._replyFromNative(item))
: [];
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Updates a plain annotation reply.
*
* @example
* const replies = await pdfReaderRef.current?._pdfDocument
* .getAnnotationReplies(annotation);
* await pdfReaderRef.current?._pdfDocument.updateAnnotationReply(replies[0], {
* content: 'Updated reply content.',
* });
*
* @param reply The plain reply annotation to update.
* @param options Updated reply content and optional author/title.
* @returns `true` when the reply was updated.
*/
updateAnnotationReply = (
reply: CPDFReplyAnnotation,
options: { content: string; title?: string }
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.updateAnnotationReply(
tag,
reply.page,
reply.uuid,
options.content,
options.title ?? "",
this._replyIdentityPayload(reply)
);
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Removes a plain annotation reply.
*
* @example
* const replies = await pdfReaderRef.current?._pdfDocument
* .getAnnotationReplies(annotation);
* await pdfReaderRef.current?._pdfDocument.removeAnnotationReply(replies[0]);
*
* @param reply The plain reply annotation to remove.
* @returns `true` when the reply was removed.
*/
removeAnnotationReply = (reply: CPDFReplyAnnotation): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.removeAnnotationReply(
tag,
reply.page,
reply.uuid,
this._replyIdentityPayload(reply)
);
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Removes all plain replies attached to an annotation.
*
* Mark and review state replies are preserved.
*
* @example
* await pdfReaderRef.current?._pdfDocument
* .removeAllAnnotationReplies(annotation);
*
* @param annotation The parent annotation.
* @returns `true` when all plain replies were removed.
*/
removeAllAnnotationReplies = (
annotation: CPDFAnnotation
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.removeAllAnnotationReplies(
tag,
annotation.page,
annotation.uuid
);
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Sets the mark state of an annotation or annotation reply.
*
* @example
* await pdfReaderRef.current?._pdfDocument.setAnnotationMarkState(
* annotation,
* CPDFAnnotationMarkState.MARKED
* );
*
* const replies = await pdfReaderRef.current?._pdfDocument
* .getAnnotationReplies(annotation);
* if (replies?.length) {
* await pdfReaderRef.current?._pdfDocument.setAnnotationMarkState(
* replies[0],
* CPDFAnnotationMarkState.UNMARKED
* );
* }
*
* @param annotation The annotation or reply annotation to update.
* @param state The mark state.
* @returns `true` when the state was updated.
*/
setAnnotationMarkState = (
annotation: CPDFAnnotation,
state: CPDFAnnotationMarkState
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.setAnnotationMarkState(
tag,
annotation.page,
annotation.uuid,
state,
annotation instanceof CPDFReplyAnnotation
? this._replyIdentityPayload(annotation)
: null
);
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Gets the mark state of an annotation or annotation reply.
*
* @example
* const state = await pdfReaderRef.current?._pdfDocument
* .getAnnotationMarkState(annotation);
*
* @param annotation The annotation or reply annotation to query.
* @returns The current mark state.
*/
getAnnotationMarkState = async (
annotation: CPDFAnnotation
): Promise<CPDFAnnotationMarkState> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
const state = await CPDFViewManager.getAnnotationMarkState(
tag,
annotation.page,
annotation.uuid,
annotation instanceof CPDFReplyAnnotation
? this._replyIdentityPayload(annotation)
: null
);
return state === CPDFAnnotationMarkState.MARKED
? CPDFAnnotationMarkState.MARKED
: CPDFAnnotationMarkState.UNMARKED;
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Sets the review state of an annotation or annotation reply.
*
* @example
* await pdfReaderRef.current?._pdfDocument.setAnnotationReviewState(
* annotation,
* CPDFAnnotationReviewState.ACCEPTED
* );
*
* const replies = await pdfReaderRef.current?._pdfDocument
* .getAnnotationReplies(annotation);
* if (replies?.length) {
* await pdfReaderRef.current?._pdfDocument.setAnnotationReviewState(
* replies[0],
* CPDFAnnotationReviewState.COMPLETED
* );
* }
*
* @param annotation The annotation or reply annotation to update.
* @param state The review state.
* @returns `true` when the state was updated.
*/
setAnnotationReviewState = (
annotation: CPDFAnnotation,
state: CPDFAnnotationReviewState
): Promise<boolean> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.setAnnotationReviewState(
tag,
annotation.page,
annotation.uuid,
state,
annotation instanceof CPDFReplyAnnotation
? this._replyIdentityPayload(annotation)
: null
);
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Gets the review state of an annotation or annotation reply.
*
* @example
* const state = await pdfReaderRef.current?._pdfDocument
* .getAnnotationReviewState(annotation);
*
* @param annotation The annotation or reply annotation to query.
* @returns The current review state.
*/
getAnnotationReviewState = async (
annotation: CPDFAnnotation
): Promise<CPDFAnnotationReviewState> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
const state = await CPDFViewManager.getAnnotationReviewState(
tag,
annotation.page,
annotation.uuid,
annotation instanceof CPDFReplyAnnotation
? this._replyIdentityPayload(annotation)
: null
);
return Object.values(CPDFAnnotationReviewState).includes(state)
? state
: CPDFAnnotationReviewState.NONE;
}
return Promise.reject(new Error("Unable to find the native view reference"));
};
/**
* Removes a form widget from the current page.
* @example
* await pdfReaderRef?.current?._pdfDocument.removeWidget(widget);
* @see CPDFWidget - Base class for all form widgets
* @param widget The widget to be removed.
* @returns
*/
removeWidget(widget: CPDFWidget): Promise<boolean> {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
return CPDFViewManager.removeWidget(tag, widget.page, widget.uuid);
}
return Promise.reject(
new Error("Unable to find the native view reference")
);
}
/**
* Gets the size of the specified page.
* @example
* const size = await pdfReaderRef?.current?._pdfDocument.getPageSize(pageIndex);
* @param pageIndex The index of the page (0-based).
* @returns The size of the specified page as a `CPDFPageSize` object.
* @throws Error If the native view reference cannot be found.
* @remarks This method retrieves the dimensions of a specific page in the PDF document.
* @since 2.5.0
*/
getPageSize = async (pageIndex: number): Promise<CPDFPageSize> => {
const tag = findNodeHandle(this._viewerRef);
if (tag != null) {
const size = await CPDFViewManager.getPageSize(tag, pageIndex);
return Promise.resolve(CPDFPageSize.custom(size.width, size.height));
}
return Promise.reject(
new Error("Unable to find the native view reference")
);
};
/**
* Renders a PDF page into a base64-encoded image string.
*
* Converts the specified page of the currently loaded PDF document into an image
* with the given dimensions, background color, and optional