I have an email which contains perfectly formatted html with the single exception that images are linked differently: <img width=456 height=384 id="_x0000_i1026" src="cid:X.MA2.1374935634#aol.com" alt="cid:X.MA4.1372453963#aol.com"> the email has other parts including the image with this content id. The problem is that I dont know how to point the QWebview to the data (which I have). Is there a way to add the image to its cache?
It's possible but not easy.
Basically you need to:
1- provide your own QNetworkAccessManager-inherited class, overriding createRequest() to catch these links refering to "cid":
QNetworkReply*
MyManager::createRequest (Operation op,
const QNetworkRequest & req,
QIODevice * outgoingData = 0)
{
if (op==GetOperation && req.url().scheme() == "cid")
return MyNetworkReply(req.url().path());
else
return QNetworkAccessManager::createRequest(op, req, outgoingData);
}
2- Connect it to the webview with:
MyManager* manager = new MyManager;
view->page()->setNetworkAccessManager(manager);
3- Provide an implementation of MyNetworkReply which inherits from QNetworkReply, a QIODevice-class. And this is the complicated part. You need to provide at least readData(), bytesAvailable(), a constructor that sets up the reply in terms of HTTP headers, and launches the actual asynchronous read with QTimer::singleShot()
4- Decode the attachment (probably from base64 if it's a picture) into a QByteArray for your MyNetworkReply::readData() to read from that.
There's a complete example on qt.gitorious.org written by Qt Labs developers in the Qt 4.6 days. They display an internally generated PNG, not an external mail attachment, but the general steps are as described above. See:
http://qt.gitorious.org/qt-labs/graphics-dojo/blobs/master/url-rendering/main.cpp
However this code has a flaw with Qt-4.8. in the constructor for RendererReply, when it does:
open(ReadOnly|Unbuffered);
this should be:
open(ReadOnly);
otherwise webkit never reads the entire data and displays the broken picture icon.
Related
We want to develop a widget to upload images to containers. This is a very well documented task:
1.- Object Storage Tutorial
2.- Fireware-Wiki
3.- OpenStack Object Storage Docs (Swift)
With all this you can manage to get (download), upload, delete files in a container. This is relatively clear.
On the other hand, we want to develop another widget to display images stored in a container. I think in something like this to show them:
<img src="public_object_url"/>
But I do not know how to do that. Where I get this public URL? Is there a public URL? Is it get in some step during the uploading process?
I am a bit lost how to do that. Any help would be highly appreciated.
Thanks in advance.
EDIT 1
We get blocked displaying images once they are downloaded.
A look inside "img" tags shows this:
what is the string returned by URL.createObjectURL(). If we look inside this link, the browser displays this:
We have decoded the string coming in the property "value" and the image is there!
To get the image from the object storage server we used a very similar code that the one used in the operator Álvaro recommended.
objectstorage.getFile( containerName,
reports[i].urlImagen,{
token: token,
onSuccess: onGetFileSuccess.bind(null, i),
onFailure: onGetFileFailure
});
function onGetFileSuccess(index, picture){
downloadedPicsCont--;
reports[index].urlImagen = URL.createObjectURL(picture);
if(!(downloadedPicsCont > 0)){
MashupPlatform.wiring.pushEvent('reports_output', JSON.stringify(reports));
}
}
The picture variable has the following structure, which seems to be ok too.
What is it happening?
EDIT 2
Finally, we found the reason. We were downloading images that were created directly from the cloud and not with objectStorageAPI. In you upload images from the cloud, when you download them you get them inside cdmi objects so the URL.createObjectURL doesn't not work as expected. In the other hand, if you upload them using objectStorageAPI, when downloading them, they come in raw format, so the method works correctly.
As far as I know, FIWARE Object Storage needs authentication, so there are no such public URL. But... you can download the image using your credentials and then use the URL.createObjectURL method for getting an URL usable in the src attribute of the img element.
It's a bit old, but you can use this operator as reference.
I have tried the following code to send an email from an Universal Windows Platform app. It works fine when I use EmailMessageBodyKind::PlainText. However, as indicated in the code below, EmailMessageBodyKind::Html seems to launch the email client with no content. Does anyone know what else needs to be set to get this to work - the documentation is sparse 8 (
using namespace Windows::Storage::Streams;
using namespace Windows::ApplicationModel::Email;
using namespace Windows::Security::Cryptography;
auto bin = CryptographicBuffer::ConvertStringToBinary(
L"<html><body>this <b>is</b> text</body></html>",
BinaryStringEncoding::Utf16LE);
auto memStream = ref new InMemoryRandomAccessStream();
concurrency::create_task(memStream->WriteAsync(bin)).then(
[memStream](unsigned)
{
auto email = ref new EmailMessage();
email->To->Append(ref new EmailRecipient(L"test#gmail.com"));
email->Subject = L"Email Report";
auto randomAccessStreamReference = RandomAccessStreamReference::CreateFromStream(memStream);
email->SetBodyStream(EmailMessageBodyKind::Html, randomAccessStreamReference);
EmailManager::ShowComposeNewEmailAsync(email);
}
);
Well, I got some bad news for you.
It is not possible to do so using EmailManager.ShowComposeNewEmailAsync
Regarding using SetBodyStream with EmailMessageBodyKind.Html, we have this from MSDN forum:
Currently, the EmailMessageBodyKind.Html won't work for create a new
HTML e-mail and there is no other way as a workaround, I've checked
the internal resource, this API is used for populating messages from
App server and save e-mail message into local folder.
The thing is: EmailManager.ShowComposeNewEmailAsync uses mailto to send the message and, as stated in some other question already answered here:
Section 2 of RFC 2368 says that the body field is supposed to be in
text/plain format, so you can't do HTML.
However even if you use plain text it's possible that some modern mail
clients would render the resulting link as a clickable link anyway,
though.
That being said, you're relying on the mail client to render that HTML for you.
I've tested this using Windows 10 Mail Client, Gmail and Outlook (both the later on a web browser), and all of them failed to render a simple HTML <b> tag on the mail body, showing it as plain text instead.
Now, for the alternatives (from that same MSDN forum thread):
Note that if I use the ShareDataContract (DataTransferManager), I am
able to set the HTML in the request and it will appear in the email
body if the user chooses to share via Mail. However I would like to
skip the Share UI and go directly with composing an email with
recipient already populated, HTML body, and image attachments.
One alternative is to persist the HTML body to a file and then include
that file as an additional attachment, however that is not ideal
The DataTransferManager successfully formatted the HTML message. Here's a small sample of how your sample code would look like, adapted from MSDN:
void YourView::ShareHtml()
{
DataTransferManager^ dataTransferManager = DataTransferManager::GetForCurrentView();
auto dataRequestedToken = dataTransferManager->DataRequested +=
ref new TypedEventHandler<DataTransferManager^, DataRequestedEventArgs^>(
this, &YourView::OnShareHtml);
DataTransferManager::ShowShareUI();
}
void YourView::OnShareHtml(DataTransferManager^ sender, DataRequestedEventArgs^ e)
{
DataRequest^ request = e->Request;
request->Data->Properties->Title = "Email Report";
String^ html = L"<html><body>this <b>is</b> text</body></html>";
String^ htmlFormat = HtmlFormatHelper::CreateHtmlFormat(html);
request->Data->SetHtmlFormat(htmlFormat);
}
The limitations of this approach are:
You cannot force the user to select e-mail as the sharing option
You cannot previously specify the mail recipient.
So the Mongoose.c library is pretty straight-forward. I've been able to use their event system, URL recognition, multi-form example, and their connection system to build a simple login-system. I've used C++ minGW, the mongoose.c&.h, and my browser. Now I'd like to implement images.
But there's a fundamental issue I can't get around. I can transfer EITHER an html document, OR an image. The jpg, alone, will display happily, as will the html document, so long as either is alone. My code is relatively simple, for html:
--pretend std::string HTMLAsString holds all html for the document.
mg_send_data(conn,HTMLAsString,strlen(HTMLAsString));
When I want to send an image, its quite similar:
while ((fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_send_data(conn,buf,n);
}
mg_send_data(conn,"\r\n",2);
Both of these work (I've cut out the irrelevant parts like how the string is composed, or how the buffer is filled, suffice to say those aspects work). I can have HTML formatting, with a 'missing image space,' or I can have an image shown, but no HTML.
How do I send BOTH an image and HTML?
Mr. Andersen should get credit for this, but I can't mark a comment as an answer, and I want to close the question.
He was dead on. First the client-browser requests the page. The server sends it. When the client-browser receives the HTML document, it then sends requests to the server for all images/files as specified in the HTML.
I was checking all requests from clients for the addresses, using conn->uri. This allowed me to simply run string comparisons to figure out what page I was receiving data from. However, I wasn't checking for any OTHER strings apart from those I had pages for.
As soon as I put a simple:
std::cout << "REQUESTED:" << conn->uri << std::endl;
I saw the requests clear as day (in my case /image.jpg). So I put the aforementioned image code together with just another string comparison in the reply function, and presto-magico, images embedded within HTML, all playing nice and happy together.
Thank you for answering my question.
P.S. The send file code is a little different:
char buf[1024];
int n;
FILE *fp;
fp = fopen(cstrpath, "rb");
if(fp==NULL){printf("ERROR, NO %s found.",cstrpath);}
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_send_data(conn,buf,n);
}
fclose(fp);
mg_send_data(conn,"\r\n",2);
I have a class in my application that displays HTML-based documentation that is stored as a set of HTML files on the user's hard drive.
My documentation module has a feature that allows it to remember the most recently-viewed page. I'm currently using QWebView::url() to get the URL of the current page so I can store it in the config. The next time the documentation viewer is activated, the URL is pulled from the config, processed appropriately, and is then sent back to the QWebView. That way, the user can pick up where he/she left off.
QWebView::url is a good way to get the current URL, except it isn't as precise as I need it to be. It only captures the base URL without any of the extras. For instance, every heading in my documentation has an id attribute attached to it, and I used this to make a browsable table of contents. For instance, the user can click any item in the TOC to jump to the appropriate heading.
However, QWebKit::url only returns something like this: file:///Z:/doc/foo.html when I need file:///Z:/doc/foo.html#heading, where #heading is the last item clicked in the TOC. How can I get it to include those internal links (not quite sure what the proper name for them is) in the URL string?
In a perfect world, QWebView would automatically know when the user scrolls past each heading in the current document and would update the URL string automatically. This would allow for nearly seamless reading between sessions. I don't expect it to work that way, but is this even possible?
This works like a charm for me.
main.cpp
#include <QWebView>
#include <QDebug>
#include <QApplication>
int main(int argc, char **argv)
{
QApplication application(argc, argv);
QWebView view;
view.load(QUrl("http://qt-project.org/doc/qt-5/QWebView.html"));
view.show();
QObject::connect(&view, &QWebView::urlChanged, [&view]() {
qDebug() << "Url being viewed:" << view.url().toString();
});
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
QT += widgets webkit webkitwidgets
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && main
Then try to click for instance on the details of the right side.
Output
Changed url being viewed: "http://qt-project.org/doc/qt-5/QWebView.html"
Changed url being viewed: "http://qt-project.org/doc/qt-5/QWebView.html#details"
Changed url being viewed: "http://qt-project.org/doc/qt-5/QWebView.html#details"
Even though the signal is emitted twice, you can see that the url accessor method returns the correct url.
I am able to generate public URLs for iCloud files. e.g. https://www.icloud.com/documents/dl/?p=3&t=BAKsXkcDP-p8sdTS8NgBLWRQxE281oe4hogA
Accessing such a URL from a browser, I see a landing page, and shorty afterwards the file downloads automatically. Fine.
However, I want to be able to download this file from my iOS app (with NSURLConnection). How can I do this? Maybe...
a) process the html headers to somehow determine the direct URL?
b) intercept the redirect/refresh that triggers the download on a browser?
c) somehow imitate a browser in order to trigger a download?
Thanks
PS. please give me the idiot's answer- I'm clueless about html etc.
Here is the html response I'm getting for the indirect URL above:
var SC_benchmarkPreloadEvents={headStart:new Date().getTime()}; -->iCloud - Loading ...window.SC=window.SC||{MODULE_INFO:{},LAZY_INSTANTIATION:{}};SC.buildMode="production";
SC.buildNumber="1FCS22.32292";SC.buildLocale="en-us";String.preferredLanguage="en-us";window.SC=window.SC||{MODULE_INFO:{},LAZY_INSTANTIATION:{}};SC._detectBrowser=function(userAgent,language){var version,webkitVersion,browser={};
userAgent=(userAgent||navigator.userAgent).toLowerCase();language=language||navigator.language||navigator.browserLanguage;
version=browser.version=(userAgent.match(/.*(?:rv|chrome|webkit|opera|ie)/: ([ );]|$)/)||[])[1];
webkitVersion=(userAgent.match(/webkit/(.+?) /)||[])[1];browser.windows=browser.isWindows=!!/windows/.test(userAgent);
browser.mac=browser.isMac=!!/macintosh/.test(userAgent)||(/mac os x/.test(userAgent)&&!/like mac os x/.test(userAgent));
browser.lion=browser.isLion=!!(/mac os x 10_7/.test(userAgent)&&!/like mac os x 10_7/.test(userAgent));
browser.iPhone=browser.isiPhone=!!/iphone/.test(userAgent);browser.iPod=browser.isiPod=!!/ipod/.test(userAgent);
browser.iPad=browser.isiPad=!!/ipad/.test(userAgent);browser.iOS=browser.isiOS=browser.iPhone||browser.iPod||browser.iPad;
browser.android=browser.isAndroid=!!/android/.test(userAgent);browser.opera=/opera/.test(userAgent)?version:0;
browser.isOpera=!!browser.opera;browser.msie=/msie/.test(userAgent)&&!browser.opera?version:0;
browser.isIE=!!browser.msie;browser.isIE8OrLower=!!(browser.msie&&parseInt(browser.msie,10)<=8);
browser.mozilla=/mozilla/.test(userAgent)&&!/(compatible|webkit|msie)/.test(userAgent)?version:0;
browser.isMozilla=!!browser.mozilla;browser.webkit=/webkit/.test(userAgent)?webkitVersion:0;
browser.isWebkit=!!browser.webkit;browser.chrome=/chrome/.test(userAgent)?version:0;
browser.isChrome=!!browser.chrome;browser.mobileSafari=/apple.*mobile/.test(userAgent)&&browser.iOS?webkitVersion:0;
browser.isMobileSafari=!!browser.mobileSafari;browser.iPadSafari=browser.iPad&&browser.isMobileSafari?webkitVersion:0;
browser.isiPadSafari=!!browser.iPadSafari;browser.iPhoneSafari=browser.iPhone&&browser.isMobileSafari?webkitVersion:0;
browser.isiPhoneSafari=!!browser.iphoneSafari;browser.iPodSafari=browser.iPod&&browser.isMobileSafari?webkitVersion:0;
browser.isiPodSafari=!!browser.iPodSafari;browser.isiOSHomeScreen=browser.isMobileSafari&&!/apple.*mobile.*safari/.test(userAgent);
browser.safari=browser.webkit&&!browser.chrome&&!browser.iOS&&!browser.android?webkitVersion:0;
browser.isSafari=!!browser.safari;browser.language=language.split("-",1)[0];browser.current=browser.msie?"msie":browser.mozilla?"mozilla":browser.chrome?"chrome":browser.safari?"safari":browser.opera?"opera":browser.mobileSafari?"mobile-safari":browser.android?"android":"unknown";
return browser};SC.browser=SC._detectBrowser();if(typeof SC_benchmarkPreloadEvents!=="undefined"){SC.benchmarkPreloadEvents=SC_benchmarkPreloadEvents;
SC_benchmarkPreloadEvents=undefined}else{SC.benchmarkPreloadEvents={headStart:new Date().getTime()}
}SC.setupBodyClassNames=function(){var el=document.body;if(!el){return}var browser,platform,shadows,borderRad,classNames,style;
browser=SC.browser.current;platform=SC.browser.windows?"windows":SC.browser.mac?"mac":"other-platform";
style=document.documentElement.style;shadows=(style.MozBoxShadow!==undefined)||(style.webkitBoxShadow!==undefined)||(style.oBoxShadow!==undefined)||(style.boxShadow!==undefined);
borderRad=(style.MozBorderRadius!==undefined)||(style.webkitBorderRadius!==undefined)||(style.oBorderRadius!==undefined)||(style.borderRadius!==undefined);
classNames=el.className?el.className.split(" "):[];if(shadows){classNames.push("box-shadow")
}if(borderRad){classNames.push("border-rad")}classNames.push(browser);if(browser==="chrome"){classNames.push("safari")
}classNames.push(platform);var ieVersion=parseInt(SC.browser.msie,10);if(ieVersion){if(ieVersion===7){classNames.push("ie7")
}else{if(ieVersion===8){classNames.push("ie8")}else{if(ieVersion===9){classNames.push("ie9")
}}}}if(SC.browser.mobileSafari){classNames.push("mobile-safari")}if("createTouch" in document){classNames.push("touch")
}el.className=classNames.join(" ")};(function(){var styles=[];if(window.devicePixelRatio==2||window.location.search.indexOf("2x")>-1){styles=["/applications/documents/download/en-us/1FCS22.32292/stylesheet#2x-packed.css"];
SC.APP_IMAGE_ASSETS=["/applications/documents/sproutcore/desktop/en-us/1FCS22.32292/stylesheet-no-repeat#2x.png","/applications/documents/coreweb/views/en-us/1FCS22.32292/stylesheet-no-repeat#2x.png","/applications/documents/sproutcore/ace/en-us/1FCS22.32292/stylesheet-no-repeat#2x.png","/applications/documents/sproutcore/ace/en-us/1FCS22.32292/stylesheet-repeat-x#2x.png","/applications/documents/sproutcore/ace/en-us/1FCS22.32292/stylesheet-repeat-y#2x.png","/applications/documents/download/en-us/1FCS22.32292/stylesheet-no-repeat#2x.png","/applications/documents/download/en-us/1FCS22.32292/stylesheet-repeat-x#2x.png"]
}else{styles=["/applications/documents/download/en-us/1FCS22.32292/stylesheet-packed.css"];
SC.APP_IMAGE_ASSETS=["/applications/documents/sproutcore/desktop/en-us/1FCS22.32292/stylesheet-no-repeat.png","/applications/documents/coreweb/views/en-us/1FCS22.32292/stylesheet-no-repeat.png","/applications/documents/sproutcore/ace/en-us/1FCS22.32292/stylesheet-no-repeat.png","/applications/documents/sproutcore/ace/en-us/1FCS22.32292/stylesheet-repeat-x.png","/applications/documents/sproutcore/ace/en-us/1FCS22.32292/stylesheet-repeat-y.png","/applications/documents/download/en-us/1FCS22.32292/stylesheet-no-repeat.png","/applications/documents/download/en-us/1FCS22.32292/stylesheet-repeat-x.png"]
}var head=document.getElementsByTagName("head")[0],len=styles.length,idx,css;for(idx=0;
idxSC.benchmarkPreloadEvents.headEnd=new Date().getTime();SC.benchmarkPreloadEvents.bodyStart=new Date().getTime();if(SC.setupBodyClassNames){SC.setupBodyClassNames()};SC.benchmarkPreloadEvents.bodyEnd=new Date().getTime();
As of July 2012, the following seems to work. But there's no guarantee that apple won't change their scheme for generating these, and it's possible that they would regard this as a private API and reject your app. So use at your own risk.
The URL has two important parameters, p and t. The first seems to identify a server, while the second identifies the actual file. The direct download link is made by plugging these values into this URL:
https://p[p]-ubiquityws.icloud.com/ws/file/[t]
Looking at your example:
https://www.icloud.com/documents/dl/?p=3&t=BAKsXkcDP-p8sdTS8NgBLWRQxE281oe4hogA
p is 3, and t is BAKsXkcDP-p8sdTS8NgBLWRQxE281oe4hogA. So your direct download link would be
https://p3-ubiquityws.icloud.com/ws/file/BAKsXkcDP-p8sdTS8NgBLWRQxE281oe4hogA
Whenever I've published a link to iCloud, p has been 01; so it's possible that you might need to zero-pad your value in which case your URL would be
https://p03-ubiquityws.icloud.com/ws/file/BAKsXkcDP-p8sdTS8NgBLWRQxE281oe4hogA
It would be great to know whether that's necessary.
In iCloud Drive / iOS8 the links are different, but you can still get a direct link to the files.
Original link:
https://www.icloud.com/attachment?u=https%3A%2F%2Fms-eu-ams-103-prod.digitalhub.com%2FB%2FATmkKK8ju8SRwQqDoEFKJzbRsxiuAXQ3PBcJBXw1Qot9jz68TkqjiiNu%2F%24%7Bf%7D%3Fo%3DAtenENR8OcvlNq6JMa331mr-8gCreXxwcfgQ26B5gFKo%26v%3D1%26x%3D3%26a%3DBclucinSeKmFAy2GJg%26e%3D1413787013%26k%3D%24%7Buk%7D%26r%3D567CC38A-FD1B-4DE6-B11B-4166A5669E1B-1%26z%3Dhttps%253A%252F%252Fp03-content.icloud.com%253A443%26s%3DlO5SolOouS9qhYz1oIxKDoGtMpo%26hs%3DovfPXj3b9XXz9lWKChBmyNq_cug&uk=OXDCcLTETbvUcOKdJ-vTdQ&f=Testdatei.vrphoto&sz=1212622
URL decoded to be more readable:
https://www.icloud.com/attachment?u=https://ms-eu-ams-103-prod.digitalhub.com/B/ATmkKK8ju8SRwQqDoEFKJzbRsxiuAXQ3PBcJBXw1Qot9jz68TkqjiiNu/${f}?o=AtenENR8OcvlNq6JMa331mr-8gCreXxwcfgQ26B5gFKo&v=1&x=3&a=BclucinSeKmFAy2GJg&e=1413787013&k=${uk}&r=567CC38A-FD1B-4DE6-B11B-4166A5669E1B-1&z=https%3A%2F%2Fp03-content.icloud.com%3A443&s=lO5SolOouS9qhYz1oIxKDoGtMpo&hs=ovfPXj3b9XXz9lWKChBmyNq_cug&uk=OXDCcLTETbvUcOKdJ-vTdQ&f=Testdatei.vrphoto&sz=1212622
Save the text between '?u=' and '&uk=' as a NSMutableString
Save the information after 'uk=' and 'f=' as NSStrings
In the first string replace the text '${f}' with the 'f=' string and replace the text '${uk}' whith the 'uk=' string
If you need the files size for any reason, it's the number after 'sz=', but this is not needed for the final link
Voila, here is your direct link to the file:
https://ms-eu-ams-103-prod.digitalhub.com/B/ATmkKK8ju8SRwQqDoEFKJzbRsxiuAXQ3PBcJBXw1Qot9jz68TkqjiiNu/Testdatei.vrphoto?o=AtenENR8OcvlNq6JMa331mr-8gCreXxwcfgQ26B5gFKo&v=1&x=3&a=BclucinSeKmFAy2GJg&e=1413787013&k=OXDCcLTETbvUcOKdJ-vTdQ&r=567CC38A-FD1B-4DE6-B11B-4166A5669E1B-1&z=https%3A%2F%2Fp03-content.icloud.com%3A443&s=lO5SolOouS9qhYz1oIxKDoGtMpo&hs=ovfPXj3b9XXz9lWKChBmyNq_cug
It looks like the heavy lifting is done by the file referenced there:
https://www.icloud.com/applications/documents/download/en-us/1FCS22.32292/javascript-packed.js
I'd start there looking for the file name etc.