Search code examples
c++qtqmakeqpdf

linking qPDF in Qt


I need to fill pdf form. To do this I found and use qPDF c++ library.

I added libqpdf.dll.a on fillPdf.pro using :

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/qpdf/lib/ -llibqpdf.dll

INCLUDEPATH += $$PWD/qpdf
DEPENDPATH += $$PWD/qpdf

I also added all includes .h and .hh headers from qpdf library to my project.

But I still have bugs that look like linkage problems :

.o:-1: In function `ZN8reporter8fillTestEPKcS1_S1_':
.cpp:19: erreur : undefined reference to `QPDF::QPDF()'
.cpp:20: erreur : undefined reference to `QPDF::processFile(char const*, char const*)'                                                     ^
.cpp:19: erreur : undefined reference to `QPDF::~QPDF()'
collect2.exe:-1: erreur : error: ld returned 1 exit status

summarised code all symbols are recognised by qt creator and i have access to theire classes by clicking on them :

#include <qpdf/QPDF.hh>
#include <qpdf/QPDFWriter.hh>
#include <qpdf/QUtil.hh>
#include <iostream>

int main(int argc, char* argv[])
{
    char* whoami = QUtil::getWhoami(argv[0]);
    if (argc != 3)
    {
        std::cerr << "Usage: " << whoami << " infile outfile" << std::endl;
        return 2;
    }
    char const* infile = argv[1];
    char const* outfile = argv[2];
    QPDF in;
    in.processFile(infile);
    int pageno = 0;
    std::vector<QPDFObjectHandle> const& pages = in.getAllPages();
    for (std::vector<QPDFObjectHandle>::const_iterator iter = pages.begin();
         iter != pages.end(); ++iter)
    {
        std::cout << ++pageno << " ";
        QPDFObjectHandle page = *iter;
        page.replaceKey(
            "/Contents",
            QPDFObjectHandle::newStream(&in, "q Q"));
    }
    QPDFWriter out(in, outfile);
    out.write();
    return 0;
}

More information :

QPDF WINDOWS Lib : https://sourceforge.net/projects/qpdf/files/qpdf/8.4.0/
includes : https://github.com/qpdf/qpdf/tree/master/include/qpdf
exemple  : https://github.com/qpdf/qpdf/blob/master/examples/pdf-set-form-values.cc

I am using qt creator 4.4.0 based on qt 5.9.1 (MSVC 2015, 32bit)

Can someone tell me where am i wrong?

My .pro

TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG += qt
QT       += core gui
QT       += axcontainer
QT      += serialport

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

SOURCES += main.cpp

HEADERS += \
    qpdf/Buffer.hh \
    qpdf/BufferInputSource.hh \
    qpdf/ClosedFileInputSource.hh \
    qpdf/Constants.h \
    qpdf/DLL.h \
    qpdf/FileInputSource.hh \
    qpdf/InputSource.hh \
    qpdf/JSON.hh \
    qpdf/Pipeline.hh \
    qpdf/Pl_Buffer.hh \
    qpdf/Pl_Concatenate.hh \
    qpdf/Pl_Count.hh \
    qpdf/Pl_DCT.hh \
    qpdf/Pl_Discard.hh \
    qpdf/Pl_Flate.hh \
    qpdf/Pl_QPDFTokenizer.hh \
    qpdf/Pl_RunLength.hh \
    qpdf/Pl_StdioFile.hh \
    qpdf/PointerHolder.hh \
    qpdf/qpdf-c.h \
    qpdf/QPDF.hh \
    qpdf/QPDFAcroFormDocumentHelper.hh \
    qpdf/QPDFAnnotationObjectHelper.hh \
    qpdf/QPDFDocumentHelper.hh \
    qpdf/QPDFExc.hh \
    qpdf/QPDFFormFieldObjectHelper.hh \
    qpdf/QPDFNameTreeObjectHelper.hh \
    qpdf/QPDFNumberTreeObjectHelper.hh \
    qpdf/QPDFObject.hh \
    qpdf/QPDFObjectHandle.hh \
    qpdf/QPDFObjectHelper.hh \
    qpdf/QPDFObjGen.hh \
    qpdf/QPDFOutlineDocumentHelper.hh \
    qpdf/QPDFOutlineObjectHelper.hh \
    qpdf/QPDFPageDocumentHelper.hh \
    qpdf/QPDFPageLabelDocumentHelper.hh \
    qpdf/QPDFPageObjectHelper.hh \
    qpdf/QPDFSystemError.hh \
    qpdf/QPDFTokenizer.hh \
    qpdf/QPDFWriter.hh \
    qpdf/QPDFXRefEntry.hh \
    qpdf/QTC.hh \
    qpdf/QUtil.hh \
    qpdf/RandomDataProvider.hh \
    qpdf/Types.h


win32:CONFIG(release, debug|release): LIBS += -L$$PWD/qpdf/lib/ -llibqpdf.dll
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/qpdf/lib/ -llibqpdf.dllINCLUDEPATH += $$PWD/qpdf
DEPENDPATH += $$PWD/qpdf


Solution

  • I get it working using the configuration bellow :

    My.pro :

    
    #-------------------------------------------------
    #
    # Project created by QtCreator 2019-04-01T16:52:28
    #
    #-------------------------------------------------
    
    QT       += core gui
    QT       += axcontainer
    QT      += serialport
    
    CONFIG += c++11 console
    CONFIG -= app_bundle
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    TARGET = QTfillpdftest
    TEMPLATE = app
    
    # The following define makes your compiler emit warnings if you use
    # any feature of Qt which has been marked as deprecated (the exact warnings
    # depend on your compiler). Please consult the documentation of the
    # deprecated API in order to know how to port your code away from it.
    DEFINES += QT_DEPRECATED_WARNINGS
    
    # You can also make your code fail to compile if you use deprecated APIs.
    # In order to do so, uncomment the following line.
    # You can also select to disable deprecated APIs only up to a certain version of Qt.
    #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
    
    
    SOURCES += \
            main.cpp \
            qtfillpdf.cpp
    
    HEADERS += \
            qtfillpdf.h\
            qpdf/Buffer.hh \
            qpdf/BufferInputSource.hh \
            qpdf/ClosedFileInputSource.hh \
            qpdf/Constants.h \
            qpdf/DLL.h \
            qpdf/FileInputSource.hh \
            qpdf/InputSource.hh \
            qpdf/JSON.hh \
            qpdf/Pipeline.hh \
            qpdf/Pl_Buffer.hh \
            qpdf/Pl_Concatenate.hh \
            qpdf/Pl_Count.hh \
            qpdf/Pl_DCT.hh \
            qpdf/Pl_Discard.hh \
            qpdf/Pl_Flate.hh \
            qpdf/Pl_QPDFTokenizer.hh \
            qpdf/Pl_RunLength.hh \
            qpdf/Pl_StdioFile.hh \
            qpdf/PointerHolder.hh \
            qpdf/qpdf-c.h \
            qpdf/QPDF.hh \
            qpdf/QPDFAcroFormDocumentHelper.hh \
            qpdf/QPDFAnnotationObjectHelper.hh \
            qpdf/QPDFDocumentHelper.hh \
            qpdf/QPDFExc.hh \
            qpdf/QPDFFormFieldObjectHelper.hh \
            qpdf/QPDFNameTreeObjectHelper.hh \
            qpdf/QPDFNumberTreeObjectHelper.hh \
            qpdf/QPDFObject.hh \
            qpdf/QPDFObjectHandle.hh \
            qpdf/QPDFObjectHelper.hh \
            qpdf/QPDFObjGen.hh \
            qpdf/QPDFOutlineDocumentHelper.hh \
            qpdf/QPDFOutlineObjectHelper.hh \
            qpdf/QPDFPageDocumentHelper.hh \
            qpdf/QPDFPageLabelDocumentHelper.hh \
            qpdf/QPDFPageObjectHelper.hh \
            qpdf/QPDFSystemError.hh \
            qpdf/QPDFTokenizer.hh \
            qpdf/QPDFWriter.hh \
            qpdf/QPDFXRefEntry.hh \
            qpdf/QTC.hh \
            qpdf/QUtil.hh \
            qpdf/RandomDataProvider.hh \
            qpdf/Types.h
    
    FORMS += \
            qtfillpdf.ui
    
    
    win32:CONFIG(release, debug|release): LIBS += -L$$PWD/qpdf/lib/ -llibqpdf.dll
    else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/qpdf/lib/ -llibqpdf.dll
    else:unix: LIBS += -L$$PWD/qpdf/lib/ -llibqpdf.dll
    
    INCLUDEPATH += $$PWD/qpdf
    DEPENDPATH += $$PWD/qpdf
    

    Function :

    int  qtfillpdf::qtfillpdfFile( char const* infilename  , char const* outfilename,char const* value )
    {
    
        qDebug() << "qtfillpdf : 1 " << value;
        // This is a contrived example that just goes through a file page
        // by page and sets the value of any text fields it finds to a
        // fixed value as given on the command line. The purpose here is
        // to illustrate use of the helper classes around interactive
        // forms.
    
        QPDF qpdf;
        qDebug() << "qtfillpdfFile : 1.1 " << value;
        qpdf.processFile(infilename);
        std::string nm;
        nm= qpdf.getFilename();
        qDebug() << "qtfillpdf : 2 " << nm.c_str() ;
        // We will iterate through form fields by starting at the page
        // level and looking at each field for each page. We could
        // also called QPDFAcroFormDocumentHelper::getFormFields to
        // iterate at the field level, but doing it as below
        // illustrates how we can map from annotations to fields.
    
        QPDFAcroFormDocumentHelper afdh(qpdf);
        QPDFPageDocumentHelper pdh(qpdf);
        std::vector<QPDFPageObjectHelper> pages = pdh.getAllPages();
        qDebug() << "qtfillpdf : 3 "  ;
        for (std::vector<QPDFPageObjectHelper>::iterator page_iter =
             pages.begin();
             page_iter != pages.end(); ++page_iter)
        {
            // Get all widget annotations for each page. Widget
            // annotations are the ones that contain the details of
            // what's in a form field.
            std::vector<QPDFAnnotationObjectHelper> annotations =
                    afdh.getWidgetAnnotationsForPage(*page_iter);
    
            for (std::vector<QPDFAnnotationObjectHelper>::iterator annot_iter = annotations.begin();
                 annot_iter != annotations.end(); ++annot_iter)
            {
                // For each annotation, find its associated field. If
                // it's a text field, set its value. This will
                // automatically update the document to indicate that
                // appearance streams need to be regenerated. At the
                // time of this writing, qpdf doesn't have any helper
                // code to assist with appearance stream generation,
                // though there's nothing that prevents it from being
                // possible.
                //qDebug() << "qtfillpdf : 4 "  << annot_iter->getAppearanceState().c_str() ;
    
                QPDFFormFieldObjectHelper ffh =
                        afdh.getFieldForAnnotation(*annot_iter);
                if (ffh.getFieldType() == "/Tx")
                {
                    //qDebug() << "qtfillpdf : 5 "  <<ffh.getChoices().front().c_str();
                    // Set the value. This will automatically set
                    // /NeedAppearances to true. If you don't want to
                    // do that, pass false as the second argument. For
                    // details see comments in
                    // QPDFFormFieldObjectHelper.hh.
                    ffh.setV(value);
    
                    qDebug() << "OK6" << value << "  ffh.getValueAsString() : " <<ffh.getValueAsString().c_str();
                }
    
            }
        }
    
        //  Write out a new file
        QPDFWriter w(qpdf, outfilename);
        w.setStaticID(true); // for testing only
        w.write();
        return 3;
    }
    void qtfillpdf::on_Browse_clicked()
    {
        ///TODO : mettre le liens 
        QString s_configFileName;
        QString outfilename = "OUT.pdf";
        QString value="qfiller";
    
    
        s_configFileName = QFileDialog::getOpenFileName(
                    this,
                    tr("Selectfile"),
                    "", tr("pdf Files (*.pdf)")
                    );
    
        qDebug() << "File Name : " << s_configFileName ;
    
    
        qtfillpdfFile(s_configFileName.toStdString().c_str(), outfilename.toStdString().c_str(), value.toStdString().c_str());
    
        qDebug()<< " END_BROWSE :" << s_configFileName ;
    }