Hi there,
I'm trying to embeed the last version of PDF.js viewer in my custom application using Qt webkit. I added the build version of PDF.js to my qrc file, and succeed to load the viewer.html.
The issue I have is when loading the pdf file which is using "file" scheme, it complains about cross origin problem:
XMLHttpRequest cannot load file:///H:/Downloads/8R23987019162_18.pdf. Cross origin requests are only supported for HTTP.
If I do load the PDF.js viewer.html from my file system and not from the resources (qrc), it works fine. Of course for my customer, it's necessary to embeed to the PDF viewer in my executable.
According to the Qt webkit documentation (http://qt-project.org/doc/qt-5/qwebsecurityorigin.html) it should simply works : "By default local schemes like file:// and qrc:// are concidered to be in the same security origin, and can access each other's resources. ". It was also working on Qt4 with an older version of PDF.js (that's why I'm updating it).
I tried to add some addAccessWhitelistEntry() without any success.
Here is my constructor that inherits from QWebView :
page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
page()->settings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true);
page()->settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
page()->settings()->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
page()->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
// Object that contains path
page()->mainFrame()->addToJavaScriptWindowObject("customPdfJsObject",m_loader,QWebFrame::QtOwnership);
page()->mainFrame()->load( QUrl("qrc:/pdf.js/web/viewer.html"));
The only change I made to viewer .js is :
var DEFAULT_URL = customPdfJsObject.myPath;
What is also strange is that if I use the Open Menu inside the viewer and select the same file on disk, it open correctly.
Thanks for your help.
I now use the url parameter directly but still doesn't work...
page()->mainFrame()->load(QUrl("qrc:/pdf.js/web/viewer.html?file=" + path));
What will be the difference between a file open with the URL and using the open button and file chooser (where it works)?
AFAIK both use PDFView.open(file, 0);
, so there should be no difference. I know nothing about the QRC protocol, but since it complains about cross origin requests, might it be possible to disable that check somehow?
After investigating a bit more, I'm not sure it's related to QRC protocol.
If I open viewser.html on my Windows 7 OS, it use my default browser (Google Chrome) and is not able to load the relative default document:
PDF.js v1.0.473 (build: 1694cd8)
Message: Unexpected server response (0) while retrieving PDF "file:///C:/Projets/DematStore/src/plugin/basicui/pdf.js/web/compressed.tracemonkey-pldi-09.pdf".
I have indeed similar errors in the console
XMLHttpRequest cannot load file:///C:/Projets/DematStore/src/plugin/basicui/pdf.js/web/compressed.tracemonkey-pldi-09.pdf. Cross origin requests are only supported for HTTP. viewer.html:1
Uncaught TypeError: Cannot read property 'xhr' of undefined
Then if I use the open button, and fetch the same file from File Open Directory dialog, it load properly from the same origin 'file://'.
I would also like to disable the check but can it be done in PDF.js or is it a browser setting?
Hm, strange. We should look into this more. I'm also cc'ing @yurydelendik, perhaps he knows more about what can cause this.
Thanks for your help.
I checked the function pdfViewOpen(url, ...)
and the url is different from default opening and using open button.
On Google Chrome:
Default: compressed.tracemonkey-pldi-09.pdf
Open Button: blob:null/713e5690-ed36-454e-8733-ae49394899c7
With QtWebkit and qrc protocol
Default: file:///H:/Downloads/LISEZ-MOI_8.pdf
Open Button: blob:qrc:///53e62ab0-0478-4dbc-9f74-2839e52506a6
The link was somehow converted from local file to qrc protocol using blob (?) which is why it works the second time. Any idea what is doing this?
I did more testing, I have the problem with Google Chrome on both Windows and Mac (Version 36.0.1985.125).
But It's fine on Firefox 31 and Safari 6.1.5, when I open viewer.html, it open the default document just fine.
In addition in Chrome I have this error, that I don't have with QtWebkit. But this is probably another issue: Uncaught TypeError: Cannot read property 'xhr' of undefined
/cc @Rob--W
Any idea what may be the issue with Chrome here?
@timvandermeij Is there a way to use blob to transform the url when using ?file= like the open button does?
Is there a way to use blob to transform the url when using ?file= like the open button does?
Yes, see http://jsbin.com/heqaqeya/1/edit?js (wfm in Firefox)
Actually, maybe. Crossorigin restrictions apply in this case as well http://jsbin.com/husamoji/1/edit
@yurydelendik Indeed I obtain: XMLHttpRequest cannot load blob:http://run.jsbin.com/c292c5b1-5aee-4783-98ed-412dcb408cad. Cross origin requests are only supported for HTTP.
?file=
, then the contents of that file is fetched via XMLHttpRequest
.The second method always works, because access to the file content in that way is not restricted. The first method could fail if PDF.js does not get cross-origin / file:-access permissions.
In the Chrome extension, file:-access is disabled by default for security reasons. Visit chrome://extensions/?id=oemmndcbldboiebfnladdacbdfmadadm
and put a check before "Allow access to file URLs" as suggested at https://chrome.google.com/webstore/detail/pdf-viewer/oemmndcbldboiebfnladdacbdfmadadm.
If you use PDF.js at the file://
scheme in Chrome, then you need to pass the --allow-file-access-from-files
flag to the command. The equivalent in QtWebkit seems to be QWebSettings::LocalContentCanAccessFileUrls
.
If you provide the source code of a minimal example that uses PDF.js to embed a PDF file in a Qt application, then I can take a look at your issue in the evening. If you prefer to keep the source private, just mail it to me at [email protected]. If it is Windows-specific project, make sure that it is compatible with VS 2010.
@Rob--W Thank you for your explanations.
I created a small Qt project with just one widget being my PDFViewer (derived from QWebView). It's very basic Qt so it shouldn't be any problem with MSVC 2010. You can right click in the webview and click "Inspect" to see console. You will need to change the QUrl in main.cpp to match a file in your disk.
http://jcourtois.fr/files/PDFViewer_Issue5057.zip
I personnaly use Qt 5.3.1, just be sure to use Qt 5 as there was no issue in Qt 4 at the time. I embedded v1.0.473 in qrc file.
If you have any question, please ask. Thanks again.
Great, no need for Windows, I can just compile it on my Linux box with qmake :)
The PDF fails to load because QtWebKit does apparently not support cross-origin XMLHttpRequest in Web Workers. This can be "fixed" by appending #disableWorker=true
to the URL. This work-around is a suboptimal solution, because all (CPU-intensive) work will be done on the main thread instead of a worker thread, causing the UI to not be as smooth as when Web workers are enabled.
I'll look into a way to gracefully fall back to fetching the resource in the main thread and passing the data to the web worker. With transferables, this operation should be relatively cheap. Even if transferable messages are not supported, then the extra memory copy operation is not worse than seeing no PDF at all.
#include "pdfjswidget.h"
#include <QtWebKitWidgets/QWebPage>
#include <QtWebKitWidgets/QWebFrame>
PdfJsWidget::PdfJsWidget(const QUrl& res, QWidget *parent) :
QWebView(parent)
{
const QString path = res.toString();
setRenderHint(QPainter::Antialiasing);
setRenderHint(QPainter::TextAntialiasing);
setRenderHint(QPainter::SmoothPixmapTransform);
setRenderHint(QPainter::HighQualityAntialiasing);
QWebSettings* settings = QWebSettings::globalSettings();
settings->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
settings->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true);
settings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
settings->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
settings->setAttribute(QWebSettings::LocalStorageEnabled, true); // Note: Added as well to allow PDF history to be kept.
settings->setAttribute(QWebSettings::JavascriptEnabled, true);
page()->mainFrame()->load(QUrl("qrc:/pdfjs/web/viewer.html?file=" + path + "#disableWorker=true"));
}
@Rob--W Thank you very much for the work around, even is not optimal.
I will keep me posted on the new issue you opened.
Cheers
@johnlamericain By the way, you should URL-encode the file before concatenating it with the URL. Otherwise files with percentages in the name may not open correctly. QUrl::toPercentEncoding
seems to do the job: http://qt-project.org/doc/qt-5/qurl.html#toPercentEncoding
const QString path = QUrl::toPercentEncoding(res.toString());
@Rob--W Thanks I will add this as well.
@Rob--W Thanks, it works like a charm.
Using Xamarin forms for UWP (windows phone 10)
I'm facing same issue in loadind the pdf file form local folder
eg: ms-appx-web:///Assets/pdfjs/web/viewer.html?file=C:\Users\username\Desktop\pdf-sample.pdf.
error : PDF.js v1.8.186 (build: 32e01cda)
Message: Missing PDF "file:///C:/Users/username/Desktop/pdf-sample.pdf".
Actually I need to display the pdf file that is downloaded in local folder.
Path is taken from "string filepath = ApplicationData.Current.LocalFolder.Path + "//Prajesh.pdf";
can anyone share your idea
Thanks in advance
Hi @sivashankararumugam ,
Exaclty same situation here
Did you acheived that (loading pdf file from local folder)? Please can you share the idea.
Thanks in advance
@BoobalanK @sivashankararumugam this issue a bit old, but it seems to be still an open question. So here is my solution:
In the viewer.js there is a function webViewerOpenFileViaURL
, that checks, if the file starts with file://
and then loads the content via XHR and uses the blob stuff from the open
method to load the file.
You could make this function somehow public and directly call it, or you use the idea to write your own function like this:
function openXHRBlob(file) {
PDFViewerApplication.setTitleUsingUrl(file);
var xhr = new XMLHttpRequest();
xhr.onload = function () {
PDFViewerApplication.open(new Uint8Array(xhr.response));
};
try {
xhr.open('GET', file);
xhr.responseType = 'arraybuffer';
xhr.send();
} catch (ex) {
throw ex;
}
}
You can call this function from anywhere to open local PDFs without restrictions. Also tested under WebEngineView QML Item.
@BoobalanK @sivashankararumugam this issue a bit old, but it seems to be still an open question. So here is my solution:
In the viewer.js there is a function
webViewerOpenFileViaURL
, that checks, if the file starts withfile://
and then loads the content via XHR and uses the blob stuff from theopen
method to load the file.You could make this function somehow public and directly call it, or you use the idea to write your own function like this:
function openXHRBlob(file) { PDFViewerApplication.setTitleUsingUrl(file); var xhr = new XMLHttpRequest(); xhr.onload = function () { PDFViewerApplication.open(new Uint8Array(xhr.response)); }; try { xhr.open('GET', file); xhr.responseType = 'arraybuffer'; xhr.send(); } catch (ex) { throw ex; } }
You can call this function from anywhere to open local PDFs without restrictions. Also tested under WebEngineView QML Item.
Any possibility to share your working configuration to understand correct syntax to load a (example: c:\test.pdf) from a local machine?