qtwebkitqwebviewqwebengineviewqresource

How to display dynamically created images in QWebView?


I'm working with QtWebKit where I'm displaying dynamically created HTML content, and I need to display the images that I retrieve from the database. For example, if I need to display an image from the resource, I add this line to the content I use in QWebView::setHtml:

<img src="qrc:/images/image.jpg"/>

That works great, WebView automatically finds the resource and displays it. Now I need to substitute this image with the content that I retrieve from the database, and that doesn't have a file in the filesystem. How to do that?

Can I manage the qrc namespace adding the content dynamically? Is that possible to add my own QRC handler that would receive and serve the requests from WebView? If not QRC, is there any other protocol that I can use to provide the content to the images in WebView?

I have full control over what I'm adding to the WebView using the setHtml method.

Update: I would like to solve the same problem for QWebEngineView as well.


Solution

  • QtWebkit

    In the case of QtWebkit you must intercept the request using a QNetworkAccessManager and resend a custom QNetworkReply.

    #include <QtWebKitWidgets>
    
    class CustomReply : public QNetworkReply{
    public:
        explicit CustomReply(const QByteArray & content, const QByteArray & contentType, const QUrl & url):
            QNetworkReply(), m_content(content){
            offset = 0;
            setUrl(url);
            open(ReadOnly | Unbuffered);
            setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType));
            setHeader(QNetworkRequest::ContentLengthHeader, QVariant(m_content.size()));
            QTimer::singleShot(0, this, &CustomReply::dispatch);
        }
        bool isSequential() const{
            return true;
        }
        qint64 bytesAvailable() const{
            return m_content.size() - offset + QIODevice::bytesAvailable();
        }
    public slots:
        void abort(){
        }
    protected:
        qint64 readData(char *data, qint64 maxSize){
            if (offset < m_content.size()) {
                qint64 number = qMin(maxSize, m_content.size() - offset);
                ::memcpy(data, m_content.constData() + offset, number);
                offset += number;
                return number;
            } else
                return -1;
        }
    private:
        void dispatch(){
            emit readyRead();
            emit finished();
        }
        QByteArray m_content;
        qint64 offset;
    };
    
    class NetworkAccessManager: public QNetworkAccessManager{
    public:
        using QNetworkAccessManager::QNetworkAccessManager;
    protected:
        QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData){
            qDebug() << request.url();
            if (request.url() == QUrl("qrc:/images/image.jpg")){
    
                QImage image(150, 150, QImage::Format_RGB32);
                image.fill(QColor("salmon"));
    
                QByteArray ba;
                QBuffer buffer(&ba);
                buffer.open(QIODevice::WriteOnly);
                image.save(&buffer, "JPEG");
    
                return new CustomReply(ba, "image/jpeg", request.url());
            }
            return QNetworkAccessManager::createRequest(op, request, outgoingData);
        }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QWebView view;
        view.resize(640, 480);
        view.show();
    
        view.page()->setNetworkAccessManager(new NetworkAccessManager);
    
        QString html = R"(<img src="qrc:/images/image.jpg">)";
        view.setHtml(html);
        return a.exec();
    }
    

    QtWebEngine

    In QtWebEngine you must implement a QWebEngineUrlSchemeHandler but you cannot use the qrc, http or https schemas:

    #include <QtWebEngineWidgets>
    
    #define SCHEMENAME "so"
    
    class Handler : public QWebEngineUrlSchemeHandler{
    public:
        void requestStarted(QWebEngineUrlRequestJob *job){
            if(job->requestUrl() == QUrl("so:/images/image.jpg")){
                QImage image(150, 150, QImage::Format_RGB32);
                image.fill(QColor("salmon"));
    
                QBuffer *buffer = new QBuffer;
                buffer->open(QIODevice::WriteOnly);
                image.save(buffer, "JPEG");
                buffer->seek(0);
                buffer->close();
    
                job->reply("image/jpeg", buffer);
            }
        }
        static void registerUrlScheme(){
            QWebEngineUrlScheme webUiScheme(SCHEMENAME);
            webUiScheme.setFlags(QWebEngineUrlScheme::SecureScheme |
                                 QWebEngineUrlScheme::LocalScheme |
                                 QWebEngineUrlScheme::LocalAccessAllowed);
            QWebEngineUrlScheme::registerScheme(webUiScheme);
        }
    
    };
    
    
    int main(int argc, char *argv[])
    {
        Handler::registerUrlScheme();
    
        QApplication a(argc, argv);
        QWebEngineView view;
    
        Handler handler;
        view.page()->profile()->installUrlSchemeHandler(SCHEMENAME, &handler);
        view.resize(640, 480);
        view.show();
    
        QString html = R"(<img src="so:/images/image.jpg">)";
        view.setHtml(html);
    
        return a.exec();
    }