PDF Single Page Viewer Example

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, 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
     required property url source // for main.cpp
     property real scaleStep: Math.sqrt(2)

     header: ToolBar {
         RowLayout {
             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 {
                 action: Action {
                     shortcut: StandardKey.Open
                     icon.source: "qrc:/singlepage/resources/document-open.svg"
                     onTriggered: fileDialog.open()
                 }
             }
             ToolButton {
                 action: Action {
                     shortcut: StandardKey.ZoomIn
                     enabled: view.sourceSize.width < 10000
                     icon.source: "qrc:/singlepage/resources/zoom-in.svg"
                     onTriggered: view.renderScale *= root.scaleStep
                 }
             }
             ToolButton {
                 action: Action {
                     shortcut: StandardKey.ZoomOut
     ...
             SpinBox {
                 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 {
         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 {
         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 {
         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 {
         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 {
             id: footerRow
             anchors.fill: parent
             ToolButton {
                 action: Action {
                     icon.source: "qrc:/singlepage/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.maximumWidth: 300
                 Layout.fillWidth: true
                 onAccepted: searchDrawer.open()
                 Image {
                     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 {
                         onTapped: searchField.clear()
                     }
                 }
             }
             ToolButton {
                 action: Action {
                     icon.source: "qrc:/singlepage/resources/go-down-search.svg"
                     shortcut: StandardKey.FindNext
                     onTriggered: view.searchForward()
                 }
                 ToolTip.visible: enabled && hovered
                 ToolTip.delay: 2000
                 ToolTip.text: "find next"
             }
             Label {
                 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.