PDF Multipage Viewer Example

A PDF viewer that allows scrolling through the pages.

PDF Multipage Viewer demonstrates how to use the PdfMultiPageView 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, visit Building and Running an Example.

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
    property string source // for main.cpp

    header: ToolBar {
        RowLayout {
            anchors.fill: parent
            anchors.rightMargin: 6

The toolbar has buttons for most of the common actions:

            ToolButton {
                action: Action {
                    shortcut: StandardKey.Open
                    icon.source: "qrc:/pdfviewer/resources/document-open.svg"
                    onTriggered: fileDialog.open()
                }
            }
            ToolButton {
                action: Action {
                    shortcut: StandardKey.ZoomIn
                    enabled: view.renderScale < 10
                    icon.source: "qrc:/pdfviewer/resources/zoom-in.svg"
                    onTriggered: view.renderScale *= Math.sqrt(2)
                }
            }
            ToolButton {
                action: Action {
                    shortcut: StandardKey.ZoomOut

Declare a PdfDocument and bind the status property and passwordRequired signal to inform the user when an error occurs or a password is required:

    Dialog {
        id: passwordDialog
        title: "Password"
        standardButtons: Dialog.Ok | Dialog.Cancel
        modal: true
        closePolicy: Popup.CloseOnEscape
        anchors.centerIn: parent
        width: 300

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

    Dialog {
        id: errorDialog
        title: "Error loading " + document.source
        standardButtons: Dialog.Ok
        modal: true
        closePolicy: Popup.CloseOnEscape
        anchors.centerIn: parent
        width: 300

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

    PdfDocument {
        id: document
        source: Qt.resolvedUrl(root.source)
        onStatusChanged: {
            if (status === PdfDocument.Error) errorDialog.open()
            view.document = (status === PdfDocument.Ready ? document : undefined)
        }
        onPasswordRequired: {
            passwordDialog.open()
            passwordField.forceActiveFocus()
        }
    }

Add the main component, PdfMultiPageView:

    PdfMultiPageView {
        id: view
        anchors.fill: parent
        anchors.leftMargin: searchDrawer.position * searchDrawer.width
        document: root.document
        searchString: searchField.text
        onCurrentPageChanged: currentPageSB.value = view.currentPage + 1
    }

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

    Drawer {
        id: searchDrawer
        edge: Qt.LeftEdge
        modal: false
        width: 300
        y: root.header.height
        height: view.height
        dim: false
        clip: true
        ListView {
            id: searchResultsList
            anchors.fill: parent
            anchors.margins: 2
            model: view.searchModel
            ScrollBar.vertical: ScrollBar { }
            delegate: ItemDelegate {
                width: parent ? parent.width : 0
                RowLayout {
                    anchors.fill: parent
                    spacing: 0
                    Label {
                        text: "Page " + (page + 1) + ": "
                    }
                    Label {
                        text: 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: contextAfter
                        elide: Text.ElideRight
                        Layout.fillWidth: true
                        Layout.preferredWidth: parent.width / 2
                    }
                }
                highlighted: ListView.isCurrentItem
                onClicked: {
                    searchResultsList.currentIndex = index
                    view.goToLocation(page, location, 0)
                    view.searchModel.currentResult = indexOnPage
                }
            }
        }
    }

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 {
            id: footerRow
            anchors.fill: parent
            ToolButton {
                action: Action {
                    icon.source: "qrc:/pdfviewer/resources/go-up-search.svg"
                    shortcut: StandardKey.FindPrevious
                    onTriggered: view.searchBack()
                }
                ToolTip.visible: enabled && hovered
                ToolTip.delay: 2000
                ToolTip.text: "find previous"
            }
            TextField {
                id: searchField
                placeholderText: "search"
                Layout.minimumWidth: 150
                Layout.fillWidth: true
                onAccepted: searchDrawer.open()
                Image {
                    visible: searchField.text !== ""
                    source: "qrc:/pdfviewer/resources/edit-clear.svg"
                    anchors {
                        right: parent.right
                        top: parent.top
                        bottom: parent.bottom
                        margins: 3
                        rightMargin: 5
                    }
                    TapHandler {
                        onTapped: searchField.clear()
                    }
                }
            }
            ToolButton {
                action: Action {
                    icon.source: "qrc:/pdfviewer/resources/go-down-search.svg"
                    shortcut: StandardKey.FindNext
                    onTriggered: view.searchForward()
                }
                ToolTip.visible: enabled && hovered
                ToolTip.delay: 2000
                ToolTip.text: "find next"
            }
            Label {
                id: statusLabel
                property size implicitPointSize: document.pagePointSize(view.currentPage)
                text: "page " + (currentPageSB.value) + " of " + document.pageCount +
                      " scale " + view.renderScale.toFixed(2) +
                      " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + " pt"
                visible: document.pageCount > 0
            }
        }
    }
}

Files and Attributions

Files:

© 2022 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.