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:
- multipage/CMakeLists.txt
- multipage/main.cpp
- multipage/multipage.pro
- multipage/resources/document-open.svg
- multipage/resources/edit-clear.svg
- multipage/resources/edit-copy.svg
- multipage/resources/edit-select-all.svg
- multipage/resources/go-down-search.svg
- multipage/resources/go-next-view-page.svg
- multipage/resources/go-previous-view-page.svg
- multipage/resources/go-up-search.svg
- multipage/resources/rotate-left.svg
- multipage/resources/rotate-right.svg
- multipage/resources/zoom-fit-best.svg
- multipage/resources/zoom-fit-width.svg
- multipage/resources/zoom-in.svg
- multipage/resources/zoom-original.svg
- multipage/resources/zoom-out.svg
- multipage/viewer.qml
- multipage/viewer.qrc