Introduction Features Installation Quick Start API Reference Configuration License

PDF.js Viewer for Qt (PySide6 / PyQt6)

Production-ready, embeddable PDF viewer widgets for PySide6 and PyQt6 applications, powered by Mozilla's PDF.js with basic annotation support.

Executive Summary

The PDF.js Viewer packages provides extensive PDF viewing and basic annotation capabilities for Qt-based Python applications. Available in two variants—pdfjs-viewer-pyside6 (LGPL) and pdfjs-viewer-pyqt6 (GPL)—these packages offer identical APIs with different licensing to accommodate both open-source and proprietary use cases.

The packages are lightweight wrappers around the PDF.js reference implementation. With extensive configuration options, individual features of the viewer can be enabled or disabled according to your needs. In addition, the packages provide a connection between the viewer and Qt, allowing it to be controlled through function calls. They also expose signals that enable the main application to react to events occurring within the viewer.

The quick‑start information on this page is complemented comprehensive example implementations, which are available in the ‘examples’ directories of our GitHub repositories for pdfjs-viewer-pyside6 and pdfjs-viewer-pyqt6.

Key Benefits:
  • Full-featured PDF viewing with zoom, rotation, and navigation
  • Basic annotation tools (highlight, text, ink, stamps)
  • Save PDFs with annotations
  • Flexible print request handling with multiple modes
  • Automatic light/dark mode support
  • PyInstaller-ready for distribution
  • 100% API compatibility between PySide6 and PyQt6 versions

Which Package Should You Use?

Package Qt Binding License Proprietary Use
pdfjs-viewer-pyside6 PySide6 LGPL v3 ✅ Yes
pdfjs-viewer-pyqt6 PyQt6 GPL v3 ❌ No
Licensing Note: If you're building a proprietary application, use pdfjs-viewer-pyside6. The PyQt6 version (GPL) requires your application to be open-source under GPL v3.

Features

🖼️ PDF.js Integration

View, zoom, rotate, and navigate PDFs with reference implementation's capabilities.

✏️ Rich Annotations

Highlight, draw, add text and stamps to PDFs.

💾 Save with Annotations

Export PDFs with annotations.

🖨️ Flexible Printing

Three print modes: system viewer, Qt dialog for printing, or custom handling.

🎨 Theme Support

Automatic light/dark mode following system preferences.

⚙️ Viewer Options

Control page, zoom, and sidebar when loading PDFs.

🔒 Security

Basic checks added, unused features disabled, suppress external links.

📦 PyInstaller Ready

Automatic bundling for frozen applications.

🎛️ Feature Control

Enable/disable specific UI features via configuration.

🔧 7 Configuration Presets

Pre-configured settings for common use cases.

🌐 Cross-Platform

Works on Windows, macOS, and Linux.

📡 Signal-Based API

Qt signals for all major events and state changes.

Installation

PySide6 Version (LGPL)

pip install pdfjs-viewer-pyside6

Optional dependencies for Qt print dialog:

pip install pdfjs-viewer-pyside6[qt-print]

PyQt6 Version (GPL)

pip install pdfjs-viewer-pyqt6

Optional dependencies for Qt print dialog:

pip install pdfjs-viewer-pyqt6[qt-print]
Requirements:
  • Python ≥ 3.8
  • PySide6 ≥ 6.10.0 or PyQt6 ≥ 6.10.0
  • PySide6-WebEngine or PyQt6-WebEngine ≥ 6.10.0
Compatibility:
This module uses QWebEngineView. Please note that bugs in this component can lead to critical errors that may cause the main application to crash. Before reporting an issue to us, please check whether downgrading or upgrading the component by one version resolves the problem, and consult our GitHub repository for notes on known versions with issues related to the integration of PDF.js

Quick Start

Basic Viewer (PySide6)

from PySide6.QtWidgets import QApplication, QMainWindow
from pdfjs_viewer import PDFViewerWidget

app = QApplication([])
window = QMainWindow()
window.resize(1024, 768)

# Create viewer
viewer = PDFViewerWidget()
viewer.load_pdf("document.pdf")
# or show blank viewer 
# viewer.show_blank_page()
# Note: PDF.js loads a demo file if you don't load a PDF or show a blank page!

# Connect signals
viewer.pdf_loaded.connect(lambda meta: print(f"Loaded: {meta['filename']}"))
viewer.pdf_saved.connect(lambda data, path: print(f"Saved to {path}"))

window.setCentralWidget(viewer)
window.show()
app.exec()

Basic Viewer (PyQt6)

from PyQt6.QtWidgets import QApplication, QMainWindow
from pdfjs_viewer import PDFViewerWidget

app = QApplication([])
window = QMainWindow()
window.resize(1024, 768)

# Create viewer
viewer = PDFViewerWidget()
viewer.load_pdf("document.pdf")
# or show blank viewer 
# viewer.show_blank_page()
# Note: PDF.js loads a demo file if you don't load a PDF or show a blank page!

# Connect signals
viewer.pdf_loaded.connect(lambda meta: print(f"Loaded: {meta['filename']}"))
viewer.pdf_saved.connect(lambda data, path: print(f"Saved to {path}"))

window.setCentralWidget(viewer)
window.show()
app.exec()

Viewer Options

Control how PDFs are displayed when loaded:

# Open at specific page with custom zoom
viewer.load_pdf("document.pdf", page=5, zoom="page-width")

# Open with bookmarks sidebar visible
viewer.load_pdf("document.pdf", pagemode="bookmarks")

# Combine multiple options
viewer.load_pdf(
    "document.pdf",
    page=10,
    zoom=150,          # 150% zoom
    pagemode="thumbs"  # Show thumbnails
)
# Open at specific page with custom zoom
viewer.load_pdf("document.pdf", page=5, zoom="page-width")

# Open with bookmarks sidebar visible
viewer.load_pdf("document.pdf", pagemode="bookmarks")

# Combine multiple options
viewer.load_pdf(
    "document.pdf",
    page=10,
    zoom=150,          # 150% zoom
    pagemode="thumbs"  # Show thumbnails
)

Supported options:

  • page: Page number to open (1-indexed)
  • zoom: Named ("page-width", "page-height", "page-fit", "auto") or numeric (10-1000)
  • pagemode: Sidebar state - "none", "thumbs", "bookmarks", "attachments"
  • nameddest: Named destination to navigate to

API Reference

PDFViewerWidget

Main widget class for viewing PDFs.

Methods

Method Description
load_pdf(source, page, zoom, pagemode, nameddest) Load PDF from file path or bytes with optional viewer options
show_blank_page() Show empty viewer (respects current theme)
save_pdf(output_path=None) Save PDF with annotations (returns bytes)
print_pdf() Trigger print dialog (behavior depends on print_handler config)
get_pdf_data() Get current PDF data with annotations as bytes
has_annotations() Check if PDF has been annotated (returns bool)
goto_page(page) Navigate to specific page
get_page_count() Get total page count (returns int)
get_current_page() Get current page number (returns int)
set_features_enabled(features) Update feature flags dynamically

Signals

All signals developers can listen to:

Signal Parameters Description
pdf_loaded metadata: dict Emitted when PDF successfully loads. Metadata includes filename, page count, and PDF information.
pdf_saved data: bytes, path: str Emitted when PDF is saved. Provides PDF data with annotations and save path.
print_requested data: bytes Emitted when print is triggered using SYSTEM or QT_DIALOG handlers.
print_data_ready data: bytes, filename: str Emitted when using EMIT_SIGNAL print handler for custom print handling.
annotation_modified none Emitted when annotations are added, changed, or removed.
page_changed current: int, total: int Emitted when current page changes. Provides page number and total count.
error_occurred message: str Emitted when errors occur during PDF operations.
external_link_blocked url: str Emitted when an external link is blocked by security settings.

Configuration

Print Handling

The viewer supports three print handling modes:

Mode Signal Requires pypdfium2 Use Case
SYSTEM print_requested(bytes) No OS-based viewing / printing (default)
QT_DIALOG print_requested(bytes) Yes Embedded Qt print dialog
EMIT_SIGNAL print_data_ready(bytes, str) No Custom print handling

Configuration Presets

7 pre-configured presets for common use cases:

Preset (click for details) Features Best For
readonly No printing, saving, or annotations Kiosk displays, untrusted PDFs
simple Print, save, basic annotations General PDF viewing
annotation All annotation tools enabled PDF review, collaboration
form Text input, signatures Form filling, contracts
kiosk Print only, maximum stability 24/7 public terminals
safer Minimal features, all stability options Embedded systems, older Qt
unrestricted Everything enabled (default) Development, testing
from pdfjs_viewer import PDFViewerWidget, ConfigPresets, PrintHandler

# Simple preset usage
viewer = PDFViewerWidget(preset="readonly")

# Customize a preset
viewer = PDFViewerWidget(
    preset="readonly",
    customize={
        "features": {
            "save_enabled": True,
            "freetext_enabled": True,
            "ink_enabled": True,
        },
        "security": {
            "allow_external_links": True,
            "block_remote_content": False,
        },
        "stability": {
            "safer_mode": True,
            "disable_webgl": False,
        }
    }
)

# Hybrid approach (recommended)
config = ConfigPresets.annotation()
config.print_handler = PrintHandler.EMIT_SIGNAL
config.features.stamp_enabled = False
viewer = PDFViewerWidget(config=config)
from pdfjs_viewer import PDFViewerWidget, ConfigPresets, PrintHandler

# Simple preset usage
viewer = PDFViewerWidget(preset="readonly")

# Customize a preset
viewer = PDFViewerWidget(
    preset="readonly",
    customize={"features": {"save_enabled": True}}
)

# Hybrid approach (recommended)
config = ConfigPresets.annotation()
config.print_handler = PrintHandler.EMIT_SIGNAL
config.features.stamp_enabled = False
config.features.save_enabled = False
viewer = PDFViewerWidget(config=config)

All Configurable Options

Complete reference of all configuration options available:

Click to expand all configurable options

PDFFeatures (features)

Option Type Default Description
print_enabled bool True Enable print button in toolbar
save_enabled bool True Enable save button in toolbar
load_enabled bool False Enable load PDF button in toolbar
presentation_mode bool False Enable fullscreen presentation mode button (not working in Qt)
highlight_enabled bool True Enable highlight annotation tool
freetext_enabled bool True Enable text annotation tool
ink_enabled bool True Enable drawing/ink annotation tool
stamp_enabled bool True Enable stamp annotation tool
stamp_alttext_enabled bool True Enable alt-text dialog for stamps
bookmark_enabled bool False Show URL of loaded document button (not working in Qt)
scroll_mode_buttons bool False Show scroll mode buttons (vertical/horizontal/wrapped)
spread_mode_buttons bool False Show spread mode buttons (single/two-page)

PDFSecurityConfig (security)

Option Type Default Description
allow_external_links bool False Allow clicking external links in PDFs
block_remote_content bool True Block loading remote images and resources
allowed_protocols list[str] ["http", "https"] Allowed URL protocols for links
custom_csp str None Custom Content Security Policy

PDFStabilityConfig (stability)

Option Type Default Description
safer_mode bool True Enable recommended stability settings
use_isolated_profile bool True Each viewer gets its own profile
profile_name str None Custom profile name (auto-generated if None)
disable_webgl bool True Disable WebGL rendering
disable_gpu bool True Disable GPU acceleration
disable_gpu_compositing bool True Disable GPU compositing
disable_cache bool True Disable disk cache
disable_local_storage bool True Disable localStorage API
disable_session_storage bool True Disable sessionStorage API
disable_databases bool True Disable Web SQL and IndexedDB
disable_service_workers bool True Disable service workers
disable_background_networking bool True Disable background network requests
disable_software_rasterizer bool False Disable software rendering (keep enabled)
force_prefers_reduced_motion bool False Reduce animations
max_cache_size_mb int 0 Maximum cache size (0 = minimal)

PDFViewerConfig (main)

Option Type Default Description
auto_open_folder_on_save bool True Open folder after saving PDF
confirm_before_external_link bool True Show confirmation dialog for external links
disable_context_menu bool True Disable Qt's native context menu
print_handler PrintHandler SYSTEM Print mode: SYSTEM, QT_DIALOG, or EMIT_SIGNAL
print_dpi int 300 DPI for Qt print dialog rendering
print_fit_to_page bool True Scale to fit page vs actual size
print_parallel_pages int 4 Number of pages to render in parallel (Qt dialog)
default_zoom str "auto" Default zoom: "auto", "page-fit", "page-width", or percentage
sidebar_visible bool False Show sidebar by default
spread_mode str "none" Page spread mode: "none", "odd", "even"

License & Dependencies

Package Licensing

Package License Qt Binding Qt Binding License Proprietary Use
pdfjs-viewer-pyside6 LGPL v3 PySide6 LGPL v3 ✅ Yes
pdfjs-viewer-pyqt6 GPL v3 PyQt6 GPL v3 ❌ No
PySide6 (LGPL) Notice:
This module uses PySide6 (Qt for Python), licensed under the LGPL v3.
You may replace the PySide6 and Qt shared libraries in the application directory.
PyQt6 (GPL) Notice:
This module uses PyQt6, licensed under the GPL v3.
Applications using this module must be licensed under GPL v3 or compatible license and provide source code to users.

Bundled Dependencies

Component Version License Homepage Repository
PDF.js 5.4.530 Apache 2.0 mozilla.github.io/pdf.js github.com/mozilla/pdf.js
PySide6 ≥ 6.10.0 LGPL v3 qt.io/qt-for-python code.qt.io/pyside
PyQt6 ≥ 6.10.0 GPL v3 riverbankcomputing.com/pyqt github.com/PyQt
pypdfium2 (optional) ≥ 4.0.0 Apache 2.0 github.com/pypdfium2 github.com/pypdfium2

License Comparison: PySide6 vs PyQt6

PySide6 (LGPL):

  • ✅ Can be used in proprietary applications
  • ✅ No need to open-source your application
  • ⚠️ Must keep Qt libraries as external shared libraries (not statically linked)
  • ⚠️ Users must be able to replace PySide6/Qt libraries

PyQt6 (GPL):

  • ⚠️ Must license your application under GPL v3
  • ⚠️ Must provide source code to users
  • ⚠️ Must include GPL v3 license and copyright notices
  • ✅ No library replacement requirements
Legal Disclaimer:
This license information is provided to the best of our knowledge but is NOT legal advice. The information may be incomplete or incorrect. If you are unsure which license to use for your project, please consult with a professional lawyer or licensing expert who can provide guidance specific to your situation.

Additional Resources