Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

PDF Query



  • Hi All,

    Can anyone recommend the best way to take an existing PDF, embed that in the Qt resources (so it is compiled and unaccessible), then use QPainter to add calculated variables to that PDF, and save a copy of it in a specific location.

    For example, if this is my existing (template) PDF:
    1e0c80f8-1a76-49c9-b992-8f34fabcef71-image.png

    I'd then like to use variables calculated in my code to fill in the gaps like this:
    07e6ed72-f0ce-411f-8f9e-f4c5d1e1dff6-image.png

    I know I can use QPdfWriter and QPainter to make the PDF with code, but that would take me way too long for the level of detail in the PDFs I have in mind.

    Really appreciate any guidance.

    Thanks!


  • Lifetime Qt Champion

    Hi
    Editing PDF is not really easy. You need a library like
    http://podofo.sourceforge.net/
    or maybe https://poppler.freedesktop.org/

    So may i propose another way.

    Use SVG files to be the details of the pdf.
    It should not be too hard to convert existing pdf to svg using inkscape.

    Used named markers (ids) to know where to put the generated texts.
    and use
    https://doc.qt.io/qt-5/qsvgrenderer.html#boundsOnElement
    to get its location from within Qt.

    Then print the SVG to pdf and then print the values on top of it.
    Since SVG is vectors the output is crisp no matter page size etc.



  • Thanks for this!

    So I have used Inkscape to change each page of my PDF into an SVG which is simple and works well.

    In the text of my PDF (that I change to SVG) I have placed some markers like this:

    916e4cba-26a7-45a3-9173-9a63d667e079-image.png

    Here is the code I have used in a small function to test it works.

                    void SvgTest()
                    {
                        QSvgRenderer renderer(QString("C:/Temp/testInputSVG.svg"));
                        QPrinter printer;
                        printer.setOutputFormat(QPrinter::PdfFormat);
                        printer.setOutputFileName(QString("C:/Temp/testOuputPDF.pdf"));
            
                        QPainter painter(&printer);
                        QRectF testRect = renderer.boundsOnElement(QString("{0}"));
                        painter.drawText(testRect, "TEST PRINT - DID THIS WORK?");
                        renderer.render(&painter);
                        painter.end();
                    }
    

    This is the output I get:
    c93e93ef-835c-48f3-9b63-814f77dd06a4-image.png

    Maybe I have misunderstood the named markers (ids)?

    Thanks again for the help :)


  • Lifetime Qt Champion

    Hi,

    Why not just generate the PDF on the fly using QPdfWriter ?

    Do you even need that template for that document ?



  • @SGaist The example I have shown above is extremely basic. The one I am actually trying to use is significantly more complex with lots of diagrams and mathematical equations.

    I have already started using QPDFWriter to write it from scratch within Qt but it is highly impractical. Using this SVG method would be perfect.

    @mrjj I have found (by opening the SVG in notepad++) that each item has its own id. Is this what you were referring to as named markers (ids)?

    Also, the final PDF size seems to be VERY large. e.g. the SVG is 5KB but the produced PDF is 102KB which is 20x larger.

    2f88daf4-e700-4474-aa54-938d42ba70b1-image.png


  • Lifetime Qt Champion

    I have found (by opening the SVG in notepad++) that each item has its own id. Is this what you were referring to as named markers (ids)?

    Hi. yes. those. That ID can be set directly in Inkscape (via properties, i think) and given good names
    instead of those auto generated.
    And you can then use that ID,
    renderer.boundsOnElement(QString("GoodName"));
    to get the a rectangle for the area. perfect for drawing on top of the svg.
    Even if scaled. :)
    However, Inkscape has inverted y, so check the return value (the rect) to
    see what i mean. ( i recall it just worked but just in case )

    About the size. Yes i wondered that too. I think we rasterize the svg to the page
    so hence the size increase. However, they rendered fast and even much larger its still small enough. However, if you generate 1000 pages this way. It might get too heavy.



  • @mrjj @SGaist
    I have found two main problems with this approach.

    1. Inkscape misses many elements from the original PDF.
    2. The rendering size for a 15-20 page document is huge.

    I have tried to reconsider and change to writing out the PDF in Qt but using HTML with the help of: https://wordtohtml.net/ or https://wordhtml.com/. I have used the following code to get this into my PDF:

    //PDF
    QPdfWriter pdf(location);
    QPageLayout pdfLayout = QPageLayout(
                            QPageSize(QPageSize::A4),
                            QPageLayout::Portrait,
                            QMarginsF(10,10,10,10),
                            QPageLayout::Unit(QPageLayout::Unit::Millimeter));
    int pageWidth = pdf.width();
    int pageHeight = pdf.height();
    
    //PAINTER
    QPainter painter; 
    painter.begin(&pdf);
    
    //BOX FOR TEXT
    QRect mainBox = QRect(QPoint(0, 0 + pageHeight * 0.08 + 150), QPoint(pageWidth - 0, pageHeight - 0 - pageHeight * 0.04 - 150));
    
    //QTEXTDOCUMENT
    QTextDocument td;
    td.setPageSize(QSize(pageWidth, pageHeight));
    td.documentLayout()->setPaintDevice(&pdf); 
    
    //SET HTML TEXT
    td.setHtml("<p>INSERT TEXT AS HTML HERE</p>");
    
    //DRAW TD CONTENTS TO PDF
    td.drawContents(&painter, mainBox); 
    

    This works, however, when I am using my laptop, the text comes onto the PDF very small. When I hook it up to the monitor, the text comes out really big. Do you know why this is and how I can make the text size consistent regardless of the screen I am using?

    Thank you

    EDIT: I have found that the difference between the two screens is this setting:
    cbe2c39e-f0fd-40aa-a0e3-c1eaee5dffa0-image.png

    EDIT2: I have tried the following but it still does not work. Small text in the PDF with 150% scaling and Large text in the PDF with 100% scaling.

    QApplication::setAttribute(Qt::AA_Use96Dpi);
    QApplication a(argc, argv);            
    a.setAttribute(Qt::AA_Use96Dpi);            
    

    I even set Qt::AA_Use96Dpi before AND after instantiating the QApplication.

    Thanks again :)



  • I have now found a solution (can't see any issues with it yet)

    Initial - Set up the printer and painter

    //PDF
    QPdfWriter pdf(location);
    QPageLayout pdfLayout = QPageLayout(
                            QPageSize(QPageSize::A4),
                            QPageLayout::Portrait,
                            QMarginsF(10,10,10,10),
                            QPageLayout::Unit(QPageLayout::Unit::Millimeter));
    int pageWidth = pdf.width();
    int pageHeight = pdf.height();
    
    //PAINTER
    QPainter painter; 
    painter.begin(&pdf);
    

    1st - Get the screen resolution and determine a scale factor from the screenresolution to 96dpi

    //identify the resolution of the screen                                 
    QPrinter screenPrinter(QPrinter::ScreenResolution);                     
    int screenResolution = screenPrinter.resolution();                      
    //set a scale factor based on 96dpi                                     
    int defaultResolution = 96;                                             
    double scaleFactor = double(screenResolution)/double(defaultResolution);
    

    2nd - Initialise the QTextDocument, set the paint device and the page size. NOTE THE SCALE FACTOR.

    //set up the HTML writing QTextDocument                              
    QTextDocument td;                                                    
    td.documentLayout()->setPaintDevice(&pdf);                           
    td.setPageSize(QSize(pageWidth/scaleFactor, pageHeight/scaleFactor));
    

    3rd - Use the painter to draw text normally if needed e.g.

    painter.drawText(QPoint(500, 500), QString("You can insert text like this"));
    

    4th - Write text using QTextDocument - SCALE THE PAINTER FIRST AND SCALE THE RECTANGLE TO PAINT IN

    painter.scale(scaleFactor, scaleFactor);
    
    QString html = "<p> Enter HTML Text here </p>"
    td.setHtml(html);
    QRect mainBox = QRect(QPoint(0/scaleFactor, 0/scaleFactor), QPoint(pageWidth/scaleFactor, pageHeight/scaleFactor));
    td.drawContents(%painter, mainBox);
    
    painter.scale(-1.0 * scaleFactor, -1.0 * scaleFactor); //scale the painter back if needed
    
    

    I hope this helps someone and if anyone sees any potential problems with this approach, please let me know.

    Thanks



  • @mrjj @SGaist

    So I thought this solution worked, but it has some problems. When I disconnect my monitor, the layout of the text changes. When I reconnect it and then disconnect it again, the layout changes yet again. No changes in code whatsoever, only disconnecting and reconnecting my monitors.

    Is there no way to have QTextDocument write to a pdf and produce the same layout every time regardless of the screen resolution or scaling %??



  • @AhmedAlshawi
    Guess (not much point asking me about it): does your screenPrinter.resolution() return different result as your monitors disconnect/connect? If you want "regardless of the screen resolution or scaling %", why are you doing screen resolution/scaling?



  • @JonB said in PDF Query:

    @AhmedAlshawi
    Guess (not much point asking me about it): does your screenPrinter.resolution() return different result as your monitors disconnect/connect? If you want "regardless of the screen resolution or scaling %", why are you doing screen resolution/scaling?

    It returns a different result when I change the scaling in display settings from 100% to 150% or otherwise. E.g. at 100% scaling it returns 96, at 150% it returns 144, at 175% it returns 168 etc.

    I want my application to work regardless of which screen resolution or scaling % the user has on their machine. It seems that QTextDocument is really poor at maintaining consistency. I am now finding that even if I do not change any of my code and only reset my machine, the pdf comes out differently. If I do not do any scaling, then when the Microsoft windows scaling is changed, the text size and layout completely changes.

    painter.drawtext() works perfectly but I can't use html with it.


Log in to reply