PDF Single Page Viewer Example | Qt PDF (original) (raw)

A Qt Quick PDF viewer that views one page at a time.

PDF Single Page Viewer Example demonstrates how to use the PdfScrollablePageView component to render PDF documents and search for text in them.

Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, see Qt Creator: Tutorial: Build and run.

Creating the Main Window

Instantiate an ApplicationWindow, bind its title to the title of the PDF document, and create a toolbar:

ApplicationWindow { id: root width: 800 height: 1024 color: "lightgrey" title: document.title visible: true required property url source // for main.cpp property real scaleStep: Math.sqrt(2)

header: ToolBar {
    [RowLayout](qml-qtquick-layouts-rowlayout.html) {
        anchors.fill: parent
        anchors.rightMargin: 6

The toolbar has buttons for most of the common actions, plus a SpinBox to show and control the current page number:

        [ToolButton](qml-qtquick-controls-toolbutton.html) {
            action: Action {
                shortcut: StandardKey.Open
                icon.source: "qrc:/singlepage/resources/document-open.svg"
                onTriggered: fileDialog.open()
            }
        }
        [ToolButton](qml-qtquick-controls-toolbutton.html) {
            action: Action {
                shortcut: StandardKey.ZoomIn
                enabled: view.sourceSize.width < 10000
                icon.source: "qrc:/singlepage/resources/zoom-in.svg"
                onTriggered: view.renderScale *= root.scaleStep
            }
        }
        [ToolButton](qml-qtquick-controls-toolbutton.html) {
            action: Action {
                shortcut: StandardKey.ZoomOut
...
        [SpinBox](qml-qtquick-controls-spinbox.html) {
            id: currentPageSB
            from: 1
            to: document.pageCount
            editable: true
            value: view.currentPage + 1
...

Add dialogs to inform the user when an error occurs and to prompt for a password if required:

[Dialog](qml-qtquick-controls-dialog.html) {
    id: passwordDialog
    title: "Password"
    standardButtons: Dialog.Ok | Dialog.Cancel
    modal: true
    closePolicy: Popup.CloseOnEscape
    anchors.centerIn: parent
    width: 300

    contentItem: TextField {
        id: passwordField
        placeholderText: qsTr("Please provide the password")
        echoMode: TextInput.Password
        width: parent.width
        onAccepted: passwordDialog.accept()
    }
    onOpened: function() { passwordField.forceActiveFocus() }
    onAccepted: document.password = passwordField.text
}

[Dialog](qml-qtquick-controls-dialog.html) {
    id: errorDialog
    title: "Error loading " + document.source
    standardButtons: Dialog.Close
    modal: true
    closePolicy: Popup.CloseOnEscape
    anchors.centerIn: parent
    width: 300
    visible: document.status === PdfDocument.Error

    contentItem: Label {
        id: errorField
        text: document.error
    }
}

Add the main component, PdfScrollablePageView:

[PdfScrollablePageView](qml-qtquick-pdf-pdfscrollablepageview.html) {
    id: view
    anchors.fill: parent
    anchors.leftMargin: searchDrawer.position * searchDrawer.width
    document: PdfDocument {
        id: document
        source: Qt.resolvedUrl(root.source)
        onPasswordRequired: passwordDialog.open()
    }
    searchString: searchField.text
}

A Drawer holds a ListView to show search results from the searchModel:

[Drawer](qml-qtquick-controls-drawer.html) {
    id: searchDrawer
    edge: Qt.LeftEdge

// modal: false // dim: false // commented out as workaround for QTBUG-83859 width: 300 y: root.header.height height: view.height clip: true ListView { id: searchResultsList anchors.fill: parent anchors.margins: 2 model: view.searchModel currentIndex: view.searchModel.currentResult ScrollBar.vertical: ScrollBar { } delegate: ItemDelegate { id: resultDelegate required property int index required property int page required property string contextBefore required property string contextAfter width: parent ? parent.width : 0 RowLayout { anchors.fill: parent spacing: 0 Label { text: "Page " + (resultDelegate.page + 1) + ": " } Label { text: resultDelegate.contextBefore elide: Text.ElideLeft horizontalAlignment: Text.AlignRight Layout.fillWidth: true Layout.preferredWidth: parent.width / 2 } Label { font.bold: true text: view.searchString width: implicitWidth } Label { text: resultDelegate.contextAfter elide: Text.ElideRight Layout.fillWidth: true Layout.preferredWidth: parent.width / 2 } } highlighted: ListView.isCurrentItem onClicked: view.searchModel.currentResult = resultDelegate.index } } }

Finally, add a second toolbar as a footer, to hold the search field, search up/down buttons and some status information:

footer: ToolBar {
    height: footerRow.implicitHeight
    [RowLayout](qml-qtquick-layouts-rowlayout.html) {
        id: footerRow
        anchors.fill: parent
        [ToolButton](qml-qtquick-controls-toolbutton.html) {
            action: Action {
                icon.source: "qrc:/singlepage/resources/go-up-search.svg"
                shortcut: StandardKey.FindPrevious
                enabled: view.searchModel.count > 0
                onTriggered: view.searchBack()
            }
            ToolTip.visible: enabled && hovered
            ToolTip.delay: 2000
            ToolTip.text: "find previous"
        }
        [TextField](qml-qtquick-controls-textfield.html) {
            id: searchField
            placeholderText: "search"
            Layout.minimumWidth: 150
            Layout.maximumWidth: 300
            Layout.fillWidth: true
            onAccepted: searchDrawer.open()
            [Image](qml-qtquick-image.html) {
                visible: searchField.text !== ""
                source: "qrc:/singlepage/resources/edit-clear.svg"
                anchors {
                    right: parent.right
                    top: parent.top
                    bottom: parent.bottom
                    margins: 3
                    rightMargin: 5
                }
                [TapHandler](qml-qtquick-taphandler.html) {
                    onTapped: searchField.clear()
                }
            }
        }
        [ToolButton](qml-qtquick-controls-toolbutton.html) {
            action: Action {
                icon.source: "qrc:/singlepage/resources/go-down-search.svg"
                shortcut: StandardKey.FindNext
                enabled: view.searchModel.count > 0
                onTriggered: view.searchForward()
            }
            ToolTip.visible: enabled && hovered
            ToolTip.delay: 2000
            ToolTip.text: "find next"
        }
        [Label](qml-qtquick-controls-label.html) {
            Layout.fillWidth: true
            property size implicitPointSize: document.pagePointSize(view.currentPage)
            text: "page " + (view.currentPage + 1) + " of " + document.pageCount +
                  " scale " + view.renderScale.toFixed(2) +
                  " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + "pts"
            visible: document.status === PdfDocument.Ready
        }
    }
}

}

Files and Attributions

Example project @ code.qt.io

See also PDF Multipage Viewer Example.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.