QImage+QPainter+QPdfWriter + newPage() = working code
-
Hi!
I ask someone - who already has on his hands a fully working (production) code, which behaves like that:
We have a list of QImages - each of then must presented as A4 PDF page.
So we must somehow to combine them into one PDF. new image = new page.Can you share it here?
According to library description this task should be possible for the trio "QImage+QPainter+QPdfWriter".
Main magic probably is done by QPdfWriter - because it has newPage() function. -
Break your problem into smaller problems or tasks:
list of QImages
Doesn't matter. It can be done repeatedly.
If it works with oneQImage
on one page, it will work also with x images on x pages.Main magic probably is done by QPdfWriter - because it has newPage() function.
You'll need
QPdfWriter
to manage your PDF (pages and meta data) and as "device" to write yourQImage
fromQPainter
to file.
It's not that hard. -
I have some old Python code that uses QPainter to make multi page PDF's. This is basically what it does. Should be straightforward to translate into C++
# Make PDF writer first w = gui.QPdfWriter(fn) w.setTitle("My Cool PDF") w.setResolution(90) w.setPageSize(w.Letter) # Make QPainter on it **after** setting stuff like resolution and paper size p = gui.QPainter(w) # there's always a first page to start with, so draw that before calling newPage() draw_first_page(p) for page in pages: w.newPage() draw_page(p, page) # Make sure you don't accidentally add a blank newPage() after your last actual page.
Then if your draw_page function, it sounds like you just want QPainter::drawImage so that should be simple to implement, and not particularly special for being on a PDF surface vs painting to a widget or anything else.
-
Unfortunately, but it is precisely the presence of such answers in almost all - more or less significant forums, chats/ etc... after using it in my own code that shows that the advice is given from the off.docs, but they were not applied by themselves on practice. And of course they are fragmented filled. That's why I wrote about the big request to provide the entire related code - which works in production! And of course in C++. Yes. I have seen a bunch of python code that works. But as soon as you start translating it into C++, some nuances emerge that are encoded in the bowels of the Python engine, or in the bowels of the related py.system libraries, and which cannot be brought into C++ 1-in-1. And in the end nothing works...
-
@Pl45m4 said in QImage+QPainter+QPdfWriter + newPage() = working code:
it is easy doable
QImage image = QImage::fromData(imageData);// imageData - from class func's param if (image.isNull()) { errorMsg = QStringLiteral("Cannot load image"); return false; } QPainter painter(m_writer); //from ctor: m_writer = new QPdfWriter(filePtr); // filePtr = new QFile(fileName); if (!m_writer->newPage()) { errorMsg = QStringLiteral("Cannot add new page at PDF file for further work."); return false; } painter.drawImage(QPointF(0, 0), image);
Also for m_writer was set 3 options:
m_writer->setPageSize(QPageSize(QPageSize::A4));
m_writer->setResolution(72);
m_writer->setPageOrientation(QPageLayout::Orientation::Landscape);after that:
filePtr->close();
delete m_writer;for the first sight - pretty good? But it's not working...
I can save 'image' as PNG file (not JPG coz input image goes with alpha-channel)
So I can prof myself that image is fine. BUT PDF file is get corrupted/not filled with image/bla-bla .. another errors...
So that's why I want see from somewhere - a real, production - full C++ code but which definitely works in same manner:
one picture comes from somewhere and is added as a whole new page in the PDF file at the end of the document.
The picture covers the entire area of the page. A4 size. Sheet orientation - landscape. -
@RazerMind said in QImage+QPainter+QPdfWriter + newPage() = working code:
QByteArray image = imageData; // from class func's param
...
painter.drawImage(QPointF(0, 0), image);
This does not even compile.
-
@RazerMind said in QImage+QPainter+QPdfWriter + newPage() = working code:
Unfortunately, but it is precisely the presence of such answers in almost all - more or less significant forums, chats/ etc... after using it in my own code that shows that the advice is given from the off.docs, but they were not applied by themselves on practice.
To be clear, I copied and pasted from my actual working code to make that example code for you. It works in practice. I commented to specific places where order of operations had tripped me up when I originally wrote it.
If you are expecting somebody to literally write exactly the software you want, that's not a reasonable expectation from a discussion forum where random people are taking time out of their day to help you for free.
BUT PDF file is get corrupted/not filled with image/bla-bla .. another errors...
If you want help with specific problems, you'll need to do the leg work to break down the specific problems you are having and ask more specific questions. From this, I couldn't begin to guess what to suggest or help you with if I wanted to spend more time helping you.
-
@Christian-Ehrlicher said in QImage+QPainter+QPdfWriter + newPage() = working code:
This does not even compile.
sorry - fixed. I did not copy as single piece all code. coz it's slightly scattered throughout the class functions.
-
@wrosecrans said in QImage+QPainter+QPdfWriter + newPage() = working code:
If you want help with specific problems
I don't quite understand what specific problem I could single out to get help on solving it.
There is only one problem - there is no final PDF file with a new page and a new image on the last page.
And so - everything is compiled, linked, launched, works ... BUT there is no desired result.
According to the documentation and a mini-example of working python code, it is clear that for C ++ code, too, should not be required to be complex, long, etc. BUT the practice of running this mini-code shows that there is some kind of a bunch of problems that SOMETHING needs to be solved. For that garbage in the PDF file, which is nevertheless generated, cannot be the desired result. Those it turns out that the C++ code needs to add some kind of checks, corrections, clarifications or PDF properties, or images. Maybe additional scaling? Changing proportions? As you can see, I can't even figure out what's wrong here - because the simplest code from the documentation simply turned out to be unable to do what was written about it. Although this was not expected. -
@RazerMind said in QImage+QPainter+QPdfWriter + newPage() = working code:
because the simplest code from the documentation simply turned out to be unable to do what was written about it.
Doubt.
According to the documentation and a mini-example of working python code, it is clear that for C ++ code, too, should not be required to be complex, long, etc. BUT the practice of running this mini-code shows that there is some kind of a bunch of problems that SOMETHING needs to be solved. For that garbage in the PDF file, which is nevertheless generated
I also doubt that you properly convert Qt code written in Python to C++ and it suddenly produces garbage, while the Python way seem to work.
Debug your code, set breakpoints, check expected results and check WHY it outputs garbage in your case.
Check if you file is valid, if your image data is valid and everything was correctly initialized.
You are not the first one, who uses this Qt C++ module/class and tries to write content to a new PDF page.
If you still need help, you should post what you've done exactly and not snippets grabbed from here and there out of your classes. -
@Pl45m4
Ok, feel free to reuse next code for your own tests:class PdfDocument { public: PdfDocument(const QString& fileName); virtual ~PdfDocument(); bool isReady() const { return m_file != nullptr && m_writer != nullptr; } const QString& get_errorMsg() const { return errorMsg; } bool addImageToPdf(const QByteArray& imageData); private: QFile* createFile(const QString& fileName, QString& error) QPdfWriter* createPdfWriter() const; private: QString outputFileName; QFile* m_file; QPdfWriter* m_writer; QString errorMsg; }; PdfDocument::PdfDocument(const QString& fileName) : outputFileName(fileName), m_file(nullptr), m_writer(nullptr), errorMsg("impossible error") { m_file = createFile(outputFileName, errorMsg); if (m_file) m_writer = createPdfWriter(); } PdfDocument::~PdfDocument() { m_file->close(); delete m_writer; delete m_file; } QFile* PdfDocument::createFile(const QString& fileName, QString& error) { QFile* file = new QFile(fileName); if (file->exists()) { if (!file->open(QIODevice::Append)) { error = QStringLiteral("Cannot open file for appending: %0").arg(file->errorString()); delete file; return nullptr; } } else { if (!file->open(QIODevice::WriteOnly)) { error = QStringLiteral("Cannot create file: %0").arg(file->errorString()); delete file; return nullptr; } } return file; } QPdfWriter* PdfDocument::createPdfWriter() const { QPdfWriter* writer = new QPdfWriter(m_file); writer->setPageSize(QPageSize(QPageSize::A4)); writer->setResolution(72); writer->setPageOrientation(QPageLayout::Orientation::Landscape); return writer; } bool PdfDocument::addImageToPdf(const QByteArray& imageData) { if (!m_writer) return false; // impossible case, but... QImage image = QImage::fromData(imageData); if (image.isNull()) { errorMsg = QStringLiteral("Cannot load image from data."); return false; } QPainter painter(m_writer); if (!m_writer->newPage()) { errorMsg = QStringLiteral("Cannot add new page at PDF file for further work."); return false; } painter.drawImage(QPointF(0, 0), image); return true; } // somewhere in the code: PdfDocument pdf("full_PDF_pathName_onDisk"); if (!pdf.isReady()) { //produce general error message, use also and pdf.get_errorMsg() //about this bad case and return false; }else{ QByteArray singlePage = //get it from response of 3rd component = webclient; return pdf.addImageToPdf(singlePage); }
-
@RazerMind said in QImage+QPainter+QPdfWriter + newPage() = working code:
QByteArray singlePage = //get it from response of 3rd component = webclient;
return pdf.addImageToPdf(singlePage);Without testing (I will later), are you sure that the data (i.e. either your web response or your bytearray) you receive is 100% valid and you are not getting your "garbage" already there?
Something like this in
addImageToPdf
QImage image = QImage::fromData(imageData); if (image.isNull()) { errorMsg = QStringLiteral("Cannot load image from data."); return false; } QLabel lbl; lbl.setPixmap(QPixmap::fromImage(image)); lbl.show(); // QPainter stuff // ...
-
@RazerMind said in QImage+QPainter+QPdfWriter + newPage() = working code:
I don't quite understand what specific problem I could single out to get help on solving it.
There is only one problem - there is no final PDF file with a new page and a new image on the last page.Part of programming is learning to break problems down into smaller problems. The QPainter API's work on any surface, like a QIMage that you can save to a PNG, or a QWidget. So, are you drawing what you want to draw correctly? If you can replicate the issue on something other than a PDF, then the problem has nothing to do with a PDF.
Does drawing on everything but a PDF work? Then the problem is specific to making a PDF.
Break the problem down into smaller pieces like that. Are you saying there is no PDF file at all? In your sentence you lump "final PDF file with a new page and a new image on the last page" into a single concept like it has irreducible complexity, so I can't even tell if you made an empty PDF or a PDF with multiple pages but the wrong content, or no file, etc.
-
@wrosecrans
ok for the very beginning - you can try to reuse my code from above. What you will get?
Of course here will be small problem - how to obtain bytearray of input image in your case....
....................Aha! one idea I have: the best variant in this ase should be: open any small pdf you have with
help of class QPdfDocument and render it into QImage class))) {https://doc.qt.io/qt-5/qpdfdocument.html#render}
So We can kill two birds with one stone this way.- We will be sure that we get a valid image. It will not depend on what and where the photo/image you can find && download. With what resolution? What size? Who knows. And so everything is already predetermined.
- Because If you take some ready-made PDF file (with 1/2/3 pages max!), then trying to add an Image to the new one being created, you will, as it were, have to completely repeat your original PDF file! Of course, I'm not talking about bit similarity, but about the same content when viewing. But the essence does not change from this. You should have two PDF files on your disk after the code runs. One initial, let's say with 3 original pages, and one final, created as a result of running the C++ code, which was supposed to take the first page from the original PDF file and repeat it. Accordingly, when you open these two files in AdobeAcrobat for ex., the first two pages must absolutely match, because one page will be just the source, and the second one will be just a literal copy, because it is made on the basis of the complete image from this first page.
-
@RazerMind said in QImage+QPainter+QPdfWriter + newPage() = working code:
100000% sure.
So how does the image look? If the
QLabel
is shown correctly, we can be sure that it's a PDFWriter issue -
I made some tests and played around a bit.
My conclusion:
This works flawless. Generates a
PDF.pdf
with three pages, on each page the image, starting at (0,0).QImage img("Path/to/Image.png"); QPdfWriter* writer = new QPdfWriter("Path/to/Pdf.pdf"); writer->setPageSize(QPageSize(QPageSize::A4)); writer->setResolution(72); writer->setPageOrientation(QPageLayout::Orientation::Landscape); QPainter painter(writer); for (int i=0; i<3; ++i) { painter.drawImage(QPoint(0, 0), img); writer->newPage(); }
Then I used your class / code, using the same image.
It works, BUT only as long as the PDF file doesn't exists.Maybe somebody can correct me, if I'm wrong, but
QPdfWriter
is a generator for PDF files. For invoices, small documents and other stuff. AFAIK it's not designed to continue to write to already existing PDF files or append any content "out of the box" (which you tried to do).
TheQPainter
onQPdfWriter
didn't care much aboutQFile(QIODevice::Append))
, since it resets and starts from the beginning of the file.Also,
QPdfWriter::newPage()
creates a new page and then the painter jumps there. Since append doesn't work, it starts at page 1, creates a new one and paints the image on page 2, leaving the first (starting) page blank.
Doing the same on the same file again, doesn't append either. It creates the same file again (blank first page, then image on second).There's nothing wrong with your code and probably not with your
QByteArray
, it's unfortunately not how it works withQPdfWriter
/QPainter
:(
I guess, you have to find another way or use external pdf libs. -
@Pl45m4 said in QImage+QPainter+QPdfWriter + newPage() = working code:
AFAIK it's not designed to continue to write to already existing PDF files or append any content "out of the box" (which you tried to do).
Yep, looks like you are right. And I should stop using this flawless library at all((((((
But huge thx for all this "testing" stuff... -
Have a look at LibHaru, esp. the Graphics part