Qt 5.15 QML PDF viewer cannot open password protected PDFs
-
wrote on 20 Aug 2021, 09:27 last edited by
Hi,
I installed the latest Qt version (5.15.5) with Commercial license with (among other modules):- MSVC 2019 (64-bit)
- Sources
- Qt PDF
- Qt WebEngine
I am running on Windows 10, trying to run the Multipage PDF example (C:\Qt\Examples\Qt-5.15.5\pdf\multipage). It runs but I cannot open a password protected PDF. It gives the dialog to enter the password, but then says: "Error loading file:///C:/Temp/TestLongPDFOnline-protected.pdf file not found", but I can open the same PDF with other applications. In viewer.qml it also says Unknown component (M300) at PdfMultiPageView (line 243).
In the Application Output area I get these warnings/errors:
qrc:/qt-project.org/qtpdf/qml/PdfMultiPageView.qml:297:25: Unable to assign [undefined] to QQuickPdfDocument*
qrc:/qt-project.org/qtpdf/qml/PdfMultiPageView.qml:166: TypeError: Cannot read property 'source' of undefined
qrc:/qt-project.org/qtpdf/qml/PdfMultiPageView.qml:164:17: QML Image: Error decoding: file:///C:/Temp/TestLongPDFOnline-protected.pdf: Unable to read image dataWhen I try to view the file with the errors by clicking on "qrc:/qt-project.org/qtpdf/qml/PdfMultiPageView.qml" I get a popup with the following:
"Could not open "\qt-project.org\qtpdf\qml\PdfMultiPageView.qml" for reading. Either the file does not exist or you do not have the permission to open it". The file was installed in C:\Qt\5.15.5\msvc2019_64\qml\QtQuick\Pdf\qml\PdfMultiPageView.qmlI am required to add functionality to my own application for viewing password protected PDFs and therefore would like to use methods similar to this example. Any advice on how to get the example to work properly will be greatly appreciated.
-
Hi,
Maybe a silly question but did you call setPassword before loading the document ?
-
Hi,
Maybe a silly question but did you call setPassword before loading the document ?
wrote on 23 Aug 2021, 10:16 last edited by@SGaist see below for the code of viewer.qml in the example. The file is loaded (document.source = file), the signal is triggered that a password is required, the password is entered (and set accordingly with onAccepted: document.password = passwordField.text). But the status of PdfDocument doesn't change once the password has been set, it remains PdfDocument.Error. Must I somehow reload once the password has been set? Thank you for your help.
/**************************************************************************** ** ** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQml 2.14 // workaround for QTBUG-82873 import QtQuick 2.14 import QtQuick.Controls 2.14 import QtQuick.Layouts 1.14 import QtQuick.Pdf 5.15 import QtQuick.Shapes 1.14 import QtQuick.Window 2.14 import Qt.labs.platform 1.1 as Platform 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 ToolButton { action: Action { shortcut: StandardKey.Open icon.source: "resources/document-open.svg" onTriggered: fileDialog.open() } } ToolButton { action: Action { shortcut: StandardKey.ZoomIn enabled: view.renderScale < 10 icon.source: "resources/zoom-in.svg" onTriggered: view.renderScale *= Math.sqrt(2) } } ToolButton { action: Action { shortcut: StandardKey.ZoomOut enabled: view.renderScale > 0.1 icon.source: "resources/zoom-out.svg" onTriggered: view.renderScale /= Math.sqrt(2) } } ToolButton { action: Action { icon.source: "resources/zoom-fit-width.svg" onTriggered: view.scaleToWidth(root.contentItem.width, root.contentItem.height) } } ToolButton { action: Action { icon.source: "resources/zoom-fit-best.svg" onTriggered: view.scaleToPage(root.contentItem.width, root.contentItem.height) } } ToolButton { action: Action { shortcut: "Ctrl+0" icon.source: "resources/zoom-original.svg" onTriggered: view.resetScale() } } ToolButton { action: Action { shortcut: "Ctrl+L" icon.source: "resources/rotate-left.svg" onTriggered: view.pageRotation -= 90 } } ToolButton { action: Action { shortcut: "Ctrl+R" icon.source: "resources/rotate-right.svg" onTriggered: view.pageRotation += 90 } } ToolButton { action: Action { icon.source: "resources/go-previous-view-page.svg" enabled: view.backEnbled onTriggered: view.back() } ToolTip.visible: enabled && hovered ToolTip.delay: 2000 ToolTip.text: "go back" } SpinBox { id: currentPageSB from: 1 to: document.pageCount editable: true onValueModified: view.goToPage(value - 1) Shortcut { sequence: StandardKey.MoveToPreviousPage onActivated: view.goToPage(currentPageSB.value - 2) } Shortcut { sequence: StandardKey.MoveToNextPage onActivated: view.goToPage(currentPageSB.value) } } ToolButton { action: Action { icon.source: "resources/go-next-view-page.svg" enabled: view.forwardEnabled onTriggered: view.forward() } ToolTip.visible: enabled && hovered ToolTip.delay: 2000 ToolTip.text: "go forward" } ToolButton { action: Action { shortcut: StandardKey.SelectAll icon.source: "resources/edit-select-all.svg" onTriggered: view.selectAll() } } ToolButton { action: Action { shortcut: StandardKey.Copy icon.source: "resources/edit-copy.svg" enabled: view.selectedText !== "" onTriggered: view.copySelectionToClipboard() } } Shortcut { sequence: StandardKey.Find onActivated: searchField.forceActiveFocus() } Shortcut { sequence: StandardKey.Quit onActivated: Qt.quit() } } } Platform.FileDialog { id: fileDialog title: "Open a PDF file" nameFilters: [ "PDF files (*.pdf)" ] onAccepted: document.source = file } 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() } } PdfMultiPageView { id: view anchors.fill: parent anchors.leftMargin: searchDrawer.position * searchDrawer.width document: root.document searchString: searchField.text onCurrentPageChanged: currentPageSB.value = view.currentPage + 1 } 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 } } } } footer: ToolBar { height: footerRow.implicitHeight RowLayout { id: footerRow anchors.fill: parent ToolButton { action: Action { icon.source: "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: "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: "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 } } } }
-
@Vloois said in Qt 5.15 QML PDF viewer cannot open password protected PDFs:
onPasswordRequired
What if you set the password directly in the handler ?
-
@Vloois said in Qt 5.15 QML PDF viewer cannot open password protected PDFs:
onPasswordRequired
What if you set the password directly in the handler ?
wrote on 24 Aug 2021, 07:46 last edited by@SGaist Thank you for your response.
I have hard coded the password in the handler as below, just to see if it makes a difference, but still get the same problem. I've also tried another PDF document encrypted using another platform, but still get the same issue.
onPasswordRequired: { //passwordDialog.open() //passwordField.forceActiveFocus() document.password = "Owner" }
Can you replicate the problem on your side if you run the example?
-
@SGaist Thank you for your response.
I have hard coded the password in the handler as below, just to see if it makes a difference, but still get the same problem. I've also tried another PDF document encrypted using another platform, but still get the same issue.
onPasswordRequired: { //passwordDialog.open() //passwordField.forceActiveFocus() document.password = "Owner" }
Can you replicate the problem on your side if you run the example?
-
@Vloois Could you share a .pdf file protected with its respective password to be able to test?
wrote on 24 Aug 2021, 09:00 last edited by@eyllanesc said in Qt 5.15 QML PDF viewer cannot open password protected PDFs:
Could you share a .pdf file protected with its respective password to be able to test?
I'm not sure if there is an easier to way share files, when trying to upload it I got an error "You do not have enough privileges for this action.", but you can download it from Dropbox with the links below:
Long is a pdf I got online and password protected using this site https://www.adobe.com/acrobat/online/password-protect-pdf.html the password is "Test123".
Short is a pdf I generated with QTextDocument and QPrinter and encrypted with QPDF (http://qpdf.sourceforge.net/ unrelated to Qt), the passwords are "User" or "Owner".
Thank you.
-
wrote on 17 Sept 2021, 09:57 last edited by
Just an update to anyone struggling with the same issue - it seems it is a bug and has been logged (https://bugreports.qt.io/browse/QTBUG-96574) hopefully they will attend to it shortly.
-
wrote on 21 Oct 2021, 09:45 last edited by
If you enter the correct password, the viewer can open the PDF file. As for your question, I suggest you delete the password on the PDF first, so that you can open the PDF directly on the viewer.