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
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:/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
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: 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
See also PDF Multipage Viewer Example.