PDF Web Viewer
    Preparing search index...

    Event Callbacks

    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:

    • Without callback: Snapshot displays a preview dialog with options to "Save As" or "Open as PDF"
    • With callback: The callback is invoked with the PNG blob, and the preview dialog is skipped
    • Error handling: If the callback throws an error, the system falls back to showing the default dialog

    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);
    },
    });