Configure callback functions to respond to editor events and customize file operations.
Monitor loading state changes to show/hide loading indicators.
Type signature:
onLoadingStateChange?: (isLoading: boolean) => void
Example:
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onLoadingStateChange: (isLoading: boolean) => {
const loader = document.getElementById('loading-spinner');
if (loader) {
loader.style.display = isLoading ? 'block' : 'none';
}
},
});
Respond to configuration changes. Triggered when layout, theme, language, branding, default font, or units & guides settings change. See index.IConfigChangeData.
Type signature:
onConfigChange?: (config: IConfigChangeData) => void
IConfigChangeData interface:
interface IConfigChangeData {
themeConfig?: IThemeConfig;
defaultFont?: IDefaultFontConfig;
unitsAndGuides?: IUnitsAndGuidesConfig;
languageConfig?: ILanguageConfig;
brandingConfig?: IBrandingConfig;
}
Example:
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onConfigChange: (config) => {
console.log('Configuration changed:', config);
// Save user preferences to localStorage
localStorage.setItem('pdfUserPreferences', JSON.stringify(config));
// Or sync to backend
await fetch('/api/user/preferences', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
});
},
});
Handle errors that occur during PDF operations. This callback receives standard JavaScript Error objects.
Type signature:
onError?: (error: Error) => void
Example:
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onError: (error: Error) => {
console.error('PDF Editor Error:', error);
// Show user-friendly error message
showNotification({
type: 'error',
title: 'PDF Error',
message: error.message,
});
},
});
Integrate with error tracking services like Sentry, LogRocket, or custom logging:
import { PdfEditor } from '@avanquest/pdf-web-viewer';
import * as Sentry from '@sentry/browser';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onError: (error: Error) => {
// Log to Sentry
Sentry.captureException(error, {
tags: {
component: 'pdf-editor',
action: 'pdf-operation',
},
extra: {
timestamp: new Date().toISOString(),
userId: currentUser?.id,
},
});
// Display user-friendly message
showToast({
type: 'error',
message: 'An error occurred while processing the PDF. Please try again.',
});
},
});
Handle password-protected files with a custom UI. The callback receives the worker error details and a setPassword helper. You can return a password immediately or call setPassword later (e.g., after a modal submit) to resolve the pending request.
Type signature:
onPasswordRequired?: IPasswordRequiredCallback
Exports:
import type { IPasswordRequiredCallback, ISetPasswordFn, IPasswordRequiredError } from '@avanquest/pdf-web-viewer';
See also: the "Password Callback Types" section in Configuration Options for the full type shapes.
Example: Custom password modal with error message
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onPasswordRequired: async (error: IPasswordRequiredError, setPassword: ISetPasswordFn) => {
// Show your modal with error.message or error.data.message
const password = await showPasswordModal({
title: 'Password required',
message: error.message,
});
// Option A: return password directly
if (password) return password;
// Option B: resolve later via service (leave Promise pending)
// setPassword(password);
},
});
When onPasswordRequired is provided, the built-in password dialog is suppressed. Returning an empty string or rejecting the promise cancels the attempt; to retry, call setPassword or editor.ui.pdfWebService.setDocumentPassword with a new password.
Replace the default file picker with a custom implementation. When provided, this callback is invoked instead of the default file input behavior.
Type signature:
onOpenFile?: () => Promise<void>
Example: Custom File Picker
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onOpenFile: async () => {
// Show custom file picker
const file = await customFilePicker({
accept: '.pdf',
multiple: false,
});
if (file) {
// Open the selected file using the service
await editor.ui.pdfWebService.openDocument({ file });
}
},
});
Example: Cloud Storage Integration
Integrate with Google Drive, Dropbox, OneDrive, or other cloud storage:
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onOpenFile: async () => {
try {
// Show cloud file picker (e.g., Google Drive Picker)
const fileUrl = await googleDrivePicker.pick({
mimeTypes: ['application/pdf'],
});
if (fileUrl) {
// Fetch file from cloud storage
const response = await fetch(fileUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const blob = await response.blob();
const file = new File([blob], 'document.pdf', { type: 'application/pdf' });
// Open the file
await editor.ui.pdfWebService.openDocument({ file });
}
} catch (error) {
console.error('Failed to open file from cloud:', error);
showToast({ type: 'error', message: 'Failed to open file from cloud storage' });
}
},
});
Customize how files are downloaded. The callback receives a File object with the filename already set.
Type signature:
onDownloadFile?: (file: File) => void
Example: Custom Download with Timestamp
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onDownloadFile: (file: File) => {
// Add timestamp to filename
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const newFilename = `${file.name.replace('.pdf', '')}-${timestamp}.pdf`;
// Create download link
const url = URL.createObjectURL(file);
const a = document.createElement('a');
a.href = url;
a.download = newFilename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
},
});
Example: Cloud Storage Upload
Save files to cloud storage instead of downloading to the user's device:
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onDownloadFile: async (file: File) => {
try {
// Show upload progress
showToast({ type: 'info', message: 'Uploading to cloud...' });
// Upload to cloud storage
const formData = new FormData();
formData.append('file', file);
formData.append('userId', currentUser.id);
const response = await fetch('/api/documents/upload', {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
},
body: formData,
});
if (!response.ok) {
throw new Error('Upload failed');
}
const { url, documentId } = await response.json();
// Show success message
showToast({
type: 'success',
message: `File saved to cloud`,
action: {
label: 'View',
onClick: () => window.open(url, '_blank'),
},
});
// Update UI or navigate
console.log('Document ID:', documentId);
} catch (error) {
console.error('Upload failed:', error);
showToast({ type: 'error', message: 'Failed to upload file to cloud' });
}
},
});
Customize the print behavior. The callback receives the PDF file and an optional flag indicating whether to open in a new tab.
Type signature:
onPrint?: (file: File, newTab?: boolean) => void
Example: Custom Print Handler
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onPrint: (file: File, newTab?: boolean) => {
const printUrl = URL.createObjectURL(file);
if (newTab) {
// Open in new tab for printing
window.open(printUrl, '_blank');
} else {
// Print directly using hidden iframe
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.style.position = 'absolute';
iframe.src = printUrl;
document.body.appendChild(iframe);
iframe.onload = () => {
iframe.contentWindow?.print();
// Clean up after printing
setTimeout(() => {
document.body.removeChild(iframe);
URL.revokeObjectURL(printUrl);
}, 1000);
};
}
},
});
Example: Print with Analytics
Track print operations:
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onPrint: (file: File, newTab?: boolean) => {
// Track print event
analytics.track('pdf_print', {
fileName: file.name,
fileSize: file.size,
printMode: newTab ? 'new-tab' : 'direct',
timestamp: Date.now(),
});
// Perform print
const printUrl = URL.createObjectURL(file);
if (newTab) {
window.open(printUrl, '_blank');
} else {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = printUrl;
document.body.appendChild(iframe);
iframe.onload = () => {
iframe.contentWindow?.print();
setTimeout(() => {
document.body.removeChild(iframe);
URL.revokeObjectURL(printUrl);
}, 1000);
};
}
},
});
Customize how files are sent by email. The callback receives a File object and allows you to implement custom email integration logic.
Type signature:
onSendByEmailFile?: (file: File) => void
Example: Email Integration
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onSendByEmailFile: async (file: File) => {
try {
// Upload file to backend for email sending
const formData = new FormData();
formData.append('file', file);
formData.append('recipient', currentUser.email);
const response = await fetch('/api/email/send-pdf', {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
},
body: formData,
});
if (!response.ok) {
throw new Error('Failed to send email');
}
showToast({
type: 'success',
message: 'PDF sent via email successfully',
});
} catch (error) {
console.error('Failed to send email:', error);
showToast({
type: 'error',
message: 'Failed to send PDF via email',
});
}
},
});
Example: Open Email Client with Attachment
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onSendByEmailFile: async (file: File) => {
// Convert file to base64 for mailto link
const reader = new FileReader();
reader.onload = () => {
const mailtoLink = `mailto:?subject=PDF Document: ${encodeURIComponent(file.name)}&body=Please find the attached PDF document.`;
// Open email client
window.location.href = mailtoLink;
showToast({
type: 'info',
message: 'Please attach the PDF manually in your email client',
});
};
reader.readAsDataURL(file);
},
});
Handle snapshot area captures with custom logic. When this callback is provided, the default snapshot preview dialog is bypassed, and the callback receives a Blob containing the PNG image data.
Type signature:
onSnapshotArea?: (blob: Blob) => void
Example: Automatic Download
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onSnapshotArea: (blob: Blob) => {
// Automatically download the snapshot
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `snapshot-${Date.now()}.png`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showToast({
type: 'success',
message: 'Snapshot downloaded successfully',
});
},
});
Example: Upload to Server
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onSnapshotArea: async (blob: Blob) => {
try {
console.log('Snapshot captured:', blob.size, 'bytes');
// Upload to backend
const formData = new FormData();
formData.append('snapshot', blob, `snapshot-${Date.now()}.png`);
formData.append('userId', currentUser.id);
const response = await fetch('/api/snapshots/upload', {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
},
body: formData,
});
if (!response.ok) {
throw new Error('Upload failed');
}
const { snapshotId, url } = await response.json();
showToast({
type: 'success',
message: 'Snapshot uploaded successfully',
action: {
label: 'View',
onClick: () => window.open(url, '_blank'),
},
});
console.log('Snapshot ID:', snapshotId);
} catch (error) {
console.error('Failed to upload snapshot:', error);
showToast({
type: 'error',
message: 'Failed to upload snapshot',
});
}
},
});
Example: Copy to Clipboard
import { PdfEditor } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onSnapshotArea: async (blob: Blob) => {
try {
// Copy image to clipboard
await navigator.clipboard.write([
new ClipboardItem({
'image/png': blob,
}),
]);
showToast({
type: 'success',
message: 'Snapshot copied to clipboard',
});
} catch (error) {
console.error('Failed to copy snapshot:', error);
showToast({
type: 'error',
message: 'Failed to copy snapshot to clipboard',
});
}
},
});
Behavior:
Replace built-in dialogs with custom implementations for specific dialog types.
Type signature:
dialogs?: CustomDialogs
type CustomDialogs = Array<ICustomDialog> | null;
interface ICustomDialog {
type: EDialogWorkerType | EDialogDocumentType;
callback: DialogCallback;
}
type DialogCallback = (
event: TDialogTemplateConfig,
close?: () => void,
submit?: (value: string) => void
) => Promise<void>;
Example: Custom Dialog Implementation
import { PdfEditor, EDialogDocumentType } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
dialogs: [
{
type: EDialogDocumentType.SAVE_CHANGES,
callback: async (event, close, submit) => {
// Show custom confirmation dialog
const result = await showCustomDialog({
title: 'Save Changes?',
message: 'Do you want to save your changes before closing?',
buttons: [
{ label: 'Save', value: 'save', primary: true },
{ label: "Don't Save", value: 'discard' },
{ label: 'Cancel', value: 'cancel' },
],
});
if (result === 'save') {
submit?.('save');
} else if (result === 'discard') {
close?.();
}
// Cancel does nothing (keeps dialog open)
},
},
],
});
Note: The dialogs feature allows fine-grained control over specific internal dialogs. For most use cases, the file operation callbacks (onOpenFile, onDownloadFile, onPrint) provide simpler customization options.
Comprehensive example showing all callbacks working together:
import { PdfEditor, IConfigChangeData } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
// State monitoring
onLoadingStateChange: (isLoading: boolean) => {
const spinner = document.getElementById('loading-spinner');
if (spinner) {
spinner.style.display = isLoading ? 'flex' : 'none';
}
},
onConfigChange: (config: IConfigChangeData) => {
// Persist user preferences
localStorage.setItem('pdfUserPreferences', JSON.stringify(config));
// Optionally sync to backend
fetch('/api/user/preferences', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
}).catch((err) => console.error('Failed to sync preferences:', err));
},
// Error handling
onError: (error: Error) => {
console.error('PDF Editor Error:', error);
// Log to error tracking
if (window.Sentry) {
window.Sentry.captureException(error);
}
// Show user notification
showToast({
type: 'error',
message: error.message || 'An error occurred',
});
},
// File operations
onOpenFile: async () => {
try {
const file = await showCustomFilePicker({
accept: '.pdf',
multiple: false,
});
if (file) {
await editor.ui.pdfWebService.openDocument({ file });
showToast({ type: 'success', message: 'File opened successfully' });
}
} catch (error) {
console.error('Failed to open file:', error);
showToast({ type: 'error', message: 'Failed to open file' });
}
},
onDownloadFile: async (file: File) => {
try {
// Upload to cloud storage
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/documents/save', {
method: 'POST',
body: formData,
});
if (response.ok) {
const { documentId } = await response.json();
showToast({
type: 'success',
message: 'File saved to cloud',
action: {
label: 'View',
onClick: () => window.open(`/documents/${documentId}`, '_blank'),
},
});
}
} catch (error) {
console.error('Failed to save file:', error);
showToast({ type: 'error', message: 'Failed to save file' });
}
},
onPrint: (file: File, newTab?: boolean) => {
const printUrl = URL.createObjectURL(file);
if (newTab) {
window.open(printUrl, '_blank');
} else {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = printUrl;
document.body.appendChild(iframe);
iframe.onload = () => {
iframe.contentWindow?.print();
setTimeout(() => {
document.body.removeChild(iframe);
URL.revokeObjectURL(printUrl);
}, 1000);
};
}
},
onSendByEmailFile: async (file: File) => {
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/email/send-pdf', {
method: 'POST',
body: formData,
});
if (response.ok) {
showToast({ type: 'success', message: 'PDF sent via email' });
}
} catch (error) {
console.error('Failed to send email:', error);
showToast({ type: 'error', message: 'Failed to send PDF via email' });
}
},
onSnapshotArea: async (blob: Blob) => {
try {
// Upload snapshot to server
const formData = new FormData();
formData.append('snapshot', blob, `snapshot-${Date.now()}.png`);
const response = await fetch('/api/snapshots/upload', {
method: 'POST',
body: formData,
});
if (response.ok) {
const { url } = await response.json();
showToast({
type: 'success',
message: 'Snapshot saved',
action: {
label: 'View',
onClick: () => window.open(url, '_blank'),
},
});
}
} catch (error) {
console.error('Failed to save snapshot:', error);
showToast({ type: 'error', message: 'Failed to save snapshot' });
}
},
});
Track user interactions and usage patterns:
import { PdfEditor, IConfigChangeData } from '@avanquest/pdf-web-viewer';
const editor = await PdfEditor({
license: 'YOUR_LICENSE_KEY',
container: document.getElementById('pdf-container'),
onLoadingStateChange: (isLoading: boolean) => {
if (isLoading) {
analytics.track('pdf_loading_started');
} else {
analytics.track('pdf_loading_completed');
}
},
onConfigChange: (config: IConfigChangeData) => {
analytics.track('pdf_config_changed', {
hasTheme: !!config.themeConfig,
hasLanguage: !!config.languageConfig,
hasBranding: !!config.brandingConfig,
timestamp: Date.now(),
});
},
onOpenFile: async () => {
analytics.track('pdf_open_initiated');
try {
const file = await showFilePicker();
if (file) {
analytics.track('pdf_file_selected', {
fileName: file.name,
fileSize: file.size,
fileType: file.type,
});
await editor.ui.pdfWebService.openDocument({ file });
}
} catch (error) {
analytics.track('pdf_open_failed', { error: error.message });
}
},
onDownloadFile: (file: File) => {
analytics.track('pdf_downloaded', {
fileName: file.name,
fileSize: file.size,
timestamp: Date.now(),
});
// Proceed with download
const url = URL.createObjectURL(file);
const a = document.createElement('a');
a.href = url;
a.download = file.name;
a.click();
URL.revokeObjectURL(url);
},
onPrint: (file: File, newTab?: boolean) => {
analytics.track('pdf_printed', {
fileName: file.name,
fileSize: file.size,
printMode: newTab ? 'new-tab' : 'direct',
timestamp: Date.now(),
});
// Proceed with print
const url = URL.createObjectURL(file);
if (newTab) {
window.open(url, '_blank');
} else {
// ... iframe print logic
}
},
onSendByEmailFile: async (file: File) => {
analytics.track('pdf_email_sent', {
fileName: file.name,
fileSize: file.size,
timestamp: Date.now(),
});
// Send email logic
try {
const formData = new FormData();
formData.append('file', file);
await fetch('/api/email/send-pdf', { method: 'POST', body: formData });
} catch (error) {
analytics.track('pdf_email_failed', { error: error.message });
}
},
onSnapshotArea: async (blob: Blob) => {
analytics.track('pdf_snapshot_captured', {
blobSize: blob.size,
blobType: blob.type,
timestamp: Date.now(),
});
// Handle snapshot
try {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `snapshot-${Date.now()}.png`;
a.click();
URL.revokeObjectURL(url);
} catch (error) {
analytics.track('pdf_snapshot_failed', { error: error.message });
}
},
onError: (error: Error) => {
analytics.track('pdf_error_occurred', {
errorMessage: error.message,
errorName: error.name,
errorStack: error.stack,
timestamp: Date.now(),
});
// Show error to user
console.error('PDF Error:', error);
},
});