- Qt 6.12
- Qt PDF
- Qt PDF Examples
- PDF Single Page Viewer Example
- Qt 6.12.0 Reference Documentation

# 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

You can 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.

- Qt Extension for Visual Studio Code

  Run the Qt: Open Qt examples command from the Command Palette, and select the example from the list. For more information, see Qt Extension for Visual Studio Code: 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: pdfDocument.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:/qt/qml/SinglePageModule/resources/document-open.svg"
                         onTriggered: fileDialog.open()
                     }
                 }
                 ToolButton {
                     action: Action {
                         shortcut: StandardKey.ZoomIn
                         enabled: view.sourceSize.width < 10000
                         icon.source: "qrc:/qt/qml/SinglePageModule/resources/zoom-in.svg"
                         onTriggered: view.renderScale *= root.scaleStep
                     }
                 }
                 ToolButton {
                     action: Action {
                         shortcut: StandardKey.ZoomOut
         ...
                 SpinBox {
                     id: currentPageSB
                     from: 1
                     to: pdfDocument.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: pdfDocument.password = passwordField.text
         }

         Dialog {
             id: errorDialog
             title: "Error loading " + pdfDocument.source
             standardButtons: Dialog.Close
             modal: true
             closePolicy: Popup.CloseOnEscape
             anchors.centerIn: parent
             width: 300
             visible: pdfDocument.status === PdfDocument.Error

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

Add the main component, PdfScrollablePageView:

         PdfScrollablePageView {
             id: view
             anchors.fill: parent
             anchors.leftMargin: searchDrawer.position * searchDrawer.width
             document: PdfDocument {
                 id: pdfDocument
                 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
                             Layout.preferredWidth: 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:/qt/qml/SinglePageModule/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 {
                     id: searchField
                     placeholderText: "search"
                     Layout.minimumWidth: 150
                     Layout.maximumWidth: 300
                     Layout.fillWidth: true
                     onAccepted: searchDrawer.open()
                     Image {
                         visible: searchField.text !== ""
                         source: "qrc:/qt/qml/SinglePageModule/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:/qt/qml/SinglePageModule/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 {
                     Layout.fillWidth: true
                     property size implicitPointSize: pdfDocument.pagePointSize(view.currentPage)
                     text: "page " + (view.currentPage + 1) + " of " + pdfDocument.pageCount +
                           " scale " + view.renderScale.toFixed(2) +
                           " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + "pts"
                     visible: pdfDocument.status === PdfDocument.Ready
                 }
             }
         }
     }

#### Files and Attributions

Example project @ code.qt.io

See also PDF Multipage Viewer Example.