0
votes

I have a simple pdf generator in my QML app, which uses C++ to receive a signal and write my PDF document to the device - when using a sample TestHtml.html document in the resources prints as intended, but I'm struggling to be able to send over custom HTML formatted text directly from the QML page.

What I want to do

My reportPage.qml displays varying data/information, when I click the 'download' button, it sends this formatted data to the C++ to print to PDF, (C++ is not my strong point).

What I have tried

  • Using QIODevice to write to the .html file, effectively deleting/re-writing the data as needed
  • Using QString to pass my text from reportPage.qml as a string
  • Using QVariant to store my html as an array and pass as this from reportPage.qml

My only issue is, where c++ is not my strong point, all I get is errors in my pdf.cpp such as undeclared identifiers, no matching member function and so on so fourth

My Question is

What is the best way to pass formatted html text using signals/slots form QML to C++ to print as a pdf using my pdf printer shown in the code below?

At Current a simplified part of my code is:

reportPage.qml

import Felgo 3.0
import com.allbookd.pdf 1.0
import QtQuick.Controls 1.2

Page {
    id: reportPage

    MyPdf {
        id: pdf
    }
    AppButton {
        text: "download"
        var htmlData = "<b>Hello</b> World 
                        <ul>
                            <li>Coffee</li>
                            <li>Tea</li>
                            <li>Milk</li>
                        </ul>"
        onClicked: { pdf.saveInvoice(htmlData); }
    }
}

pdf.h

#ifndef PDF_H
#define PDF_H

#include <QObject>
#include <QDebug>
#include <QPainter>
#include <QTextDocument>
#include <QPdfWriter>
#include <QDate>
#include <QStandardPaths>
#include <QPrinter>
#include <QFile>

class pdf : public QObject
{
    Q_OBJECT
public:
    explicit pdf(QObject *parent = nullptr);

    void setCurrentDate();
    QString getCurrentDate() const;

signals:

    void invoiceSaved(int error);

public slots:
    QString getPdfPath() const;
    void saveInvoice();

private:
    QString currentDate;
    QString pdfPath;
};

#endif // PDF_H

pdf.cpp


pdf::pdf(QObject *parent) : QObject(parent)
{
    setCurrentDate();
    QString path = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
    pdfPath = path + "/invoice_" + getCurrentDate() + ".pdf";
}

void pdf::setCurrentDate()
{
    QDate date(QDate::currentDate());
    int day = date.day();
    int month = date.month();
    int year =  date.year();

    currentDate = QString::number(day) + "_" + QString::number(month) + "_" + QString::number(year);
}

QString pdf::getCurrentDate() const
{
    return currentDate;
}

QString pdf::getPdfPath() const
{
    return pdfPath;
}


void pdf::saveInvoice(QString htmlData)
{
    int error = 0;
    QPdfWriter pdfWriter(getPdfPath());

    pdfWriter.setPageSize(QPageSize(QPageSize::A4));

    QPainter painter(&pdfWriter);

    painter.scale(15.0, 15.0);

    QFile file(":/htmlcode.html");    //the HTML file from resources

    if(file.open(QIODevice::ReadWrite)) {
        QByteArray temp = file.readAll();   //read it all

        QString html = temp;                //convert it to QString
        QTextDocument doc;
        doc.setHtml(html);                  //set it as HTML

        doc.drawContents(&painter);         

    } else {
        qDebug() << "error: " << file.error();
        error = 1;
    }
    emit invoiceSaved(error);

}

1
1) The qresources are read-only so it changes to if(file.open(QIODevice::ReadOnly)) { :, also add painter.end() after doc.drawContents(&painter); - eyllanesc
On the other hand, according to what I understand, you want to send information from QML to C++ but I don't see any code about it. - eyllanesc
Hey @eyllanesc I've just updated my question with some basic html text as an example of what I would be passing over to the printer, you are correct in what you say - I want to send information (formatted text) from QML, to C++. - Ldweller
I don't see where you use htmlData - eyllanesc
@eyllanesc I have tried passing it through to cpp by declaring in my pdf.h and pdf.cpp QString htmlData but only error hence left out of the code - Ldweller

1 Answers

1
votes

You can expose your pdf class to QML. Make functions Q_INVOKABLE to be able to call them directly from QML or even better use Q_PROPERTY

Docs for Q_INVOKABLE Docs for Q_PROPERTY

Here is an example:

Register/expose your pdf class to qml in main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "pdf.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    qmlRegisterType<pdf>("com.allbookd.pdf", 1, 0, "PdfPrinter");
    QQmlApplicationEngine engine;    
    ............

    return app.exec();
}

Make saveInvoice function invokable in pdf.h:

#include <QObject>

class pdf : public QObject
{
    Q_OBJECT
public:
    explicit pdf(QObject *parent = nullptr);

    void setCurrentDate();
    QString getCurrentDate() const;

    Q_INVOKABLE void saveInvoice(QString htmlData);

    ..............

And save htmlData to pdf using QPdfWriter:

void pdf::saveInvoice(QString htmlData)
{
    int error = 0;
    QPdfWriter pdfWriter(getPdfPath());

    pdfWriter.setPageSize(QPageSize(QPageSize::A4));

    QPainter painter(&pdfWriter);
    painter.scale(15.0, 15.0);

    QTextDocument doc;
    doc.setHtml(htmlData);
    doc.drawContents(&painter);

    emit invoiceSaved(error);
}

Your QML could then look like that:

import Felgo 3.0
import com.allbookd.pdf 1.0
import QtQuick.Controls 1.2

Page {
    id: reportPage
    property PdfPrinter pdf: PdfPrinter{}

    AppButton {
        text: "download"
        var htmlData = "<b>Hello</b> World 
                        <ul>
                            <li>Coffee</li>
                            <li>Tea</li>
                            <li>Milk</li>
                        </ul>"
        onClicked: { pdf.saveInvoice(htmlData); }
    }
}