使用 ffh.getFieldType() == "/Btn" 和 qpdf lib 检查复选框

check checkBox using ffh.getFieldType() == "/Btn" with qpdf lib

我使用 qpdf,它在我使用 /Tx 填充文本字段时有效。 但它不适用于检查复选框:

库 header : https://github.com/qpdf/qpdf/blob/aa602fd107a622a3f12e6530220bb0303b95b520/include/qpdf/QPDFFormFieldObjectHelper.hh

我的代码:if (ffh.getFieldType() == "/Btn")

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <qpdf/QPDF.hh>
#include <qpdf/QPDFPageDocumentHelper.hh>
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
#include <qpdf/QPDFWriter.hh>
#include <qpdf/QUtil.hh>
#include <QFileDialog>
#include <QDebug>
#include "qtfillpdf.h"
#include "ui_qtfillpdf.h"

qtfillpdf::qtfillpdf(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::qtfillpdf)
{
    ui->setupUi(this);
}

qtfillpdf::~qtfillpdf()
{
    delete ui;
}

int  qtfillpdf::qtfillTESTREPORTpdf( char const* infilename  , char const* outfilename,char const* value )
{

    qDebug() << "qtfillTESTREPORTpdf : 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() << "qtfillTESTREPORTpdf : 1.1 " << value;
    qpdf.processFile(infilename);
    std::string nm;
    nm= qpdf.getFilename();
    qDebug() << "qtfillTESTREPORTpdf : 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() << "qtfillTESTREPORTpdf : 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);

        char const* checkPASS="true";
        char const* checkFAIL="false";

        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() << "qtfillTESTREPORTpdf : 4 "  << annot_iter->getAppearanceState().c_str() ;


            QPDFFormFieldObjectHelper ffh =
                    afdh.getFieldForAnnotation(*annot_iter);

            if (ffh.getFieldType() == "/Tx")
            {
                //qDebug() << "qtfillTESTREPORTpdf : 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();
            }

            else if (ffh.getFieldType() == "/Btn")
            {
                //qDebug() << "qtfillTESTREPORTpdf : 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(checkPASS);
                qDebug() << "OK7" << checkPASS << "  ffh.getValueAsString() : " << ffh.getValueAsString().c_str() << endl;
                qDebug() << "OK7- 2 " << checkPASS << "  ffh.getValue().getName() : " <<ffh.getValue().getName().c_str() << endl;
            }

        }
    }

    //  Write out a new file
    QPDFWriter w(qpdf, outfilename);
    w.setStaticID(true); // for testing only
    w.write();
    return 3;
}

void qtfillpdf::on_Browse_clicked()
{

    QString s_configFileName;
    QString outfilename = "OUTTESTREPORTWithCheckedBoxes.pdf";
    QString value="OkeyMenIfillAutomaticallyYourSheet";

    s_configFileName = QFileDialog::getOpenFileName(
                this,
                tr("Selectionner le fichier de configuration [xlsm]"),
                "",
                tr("pdf Files (*.pdf)")
                );

    qDebug() << "File Name : " << s_configFileName ;


    qtfillTESTREPORTpdf(s_configFileName.toStdString().c_str(), outfilename.toStdString().c_str(), value.toStdString().c_str());

    qDebug()<< " END_BROWSE :" << s_configFileName ;
}

谁知道为什么它对 Btn 不起作用,尽管它对 /Tx 有效

/Tx 的日志,我在输出 PDF 上看到它

OK6 OkeyMenIfillAutomaticallyYourSheet   ffh.getValueAsString() :  OkeyMenIfillAutomaticallyYourSheet

Btn 的日志:输出 PDF 上没有任何内容

WARNING:  object stream 1979, object 1129 0 at offset 41539 -> dictionary key /V: operation for name attempted on object of type null: returning dummy name
OK7- 2  true   ffh.getValue().getName() :  /QPDFFakeName

我知道 qpdf 在我的 pdf 中看不到 /Yes 或 /Off 但也没有 /Tx 并且在 qpdf 示例中没有 /Yes 或 /Off 文件名

需要帮助来获得好主意,谢谢

lib 源代码的有趣部分:

void
QPDFFormFieldObjectHelper::setV(
    QPDFObjectHandle value, bool need_appearances)
{
    if (getFieldType() == "/Btn")
    {
        if (isCheckbox())
        {
            bool okay = false;
            if (value.isName())
            {
                std::string name = value.getName();
                if ((name == "/Yes") || (name == "/Off"))
                {
                    okay = true;
                    setCheckBoxValue((name == "/Yes"));
                }
            }
            if (! okay)
            {
                this->oh.warnIfPossible(
                    "ignoring attempt to set a checkbox field to a"
                    " value of other than /Yes or /Off");
            }
        }
        else if (isRadioButton())
        {
            if (value.isName())
            {
                setRadioButtonValue(value);
            }
            else
            {
                this->oh.warnIfPossible(
                    "ignoring attempt to set a radio button field to"
                    " an object that is not a name");
            }
        }
        else if (isPushbutton())
        {
            this->oh.warnIfPossible(
                "ignoring attempt set the value of a pushbutton field");
        }
        return;
    }
    if (value.isString())
    {
        setFieldAttribute(
            "/V", QPDFObjectHandle::newUnicodeString(value.getUTF8Value()));
    }
    else
    {
        setFieldAttribute("/V", value);
    }
    if (need_appearances)
    {
        QPDF* qpdf = this->oh.getOwningQPDF();
        if (! qpdf)
        {
            throw std::logic_error(
                "QPDFFormFieldObjectHelper::setV called with"
                " need_appearances = true on an object that is"
                " not associated with an owning QPDF");
        }
        QPDFAcroFormDocumentHelper(*qpdf).setNeedAppearances(true);
    }
}

void
QPDFFormFieldObjectHelper::setV(
    std::string const& utf8_value, bool need_appearances)
{
    setV(QPDFObjectHandle::newUnicodeString(utf8_value),
         need_appearances);
}

qpdf .dll 的源代码:

https://github.com/qpdf/qpdf/blob/2d32f4db8fd125f2481ecf767d9f6506e80481f6/libqpdf/QPDFFormFieldObjectHelper.cc

我在 answare 上添加了一些说明元素

提前感谢您的帮助!!!!

已解决:一切正常,我的代码如下:幸福

int  qtfillpdf::qtfillTESTpdf( char const* infilename  , char const* outfilename,char const* value )
{

    qDebug() << "qtfillTESTREPORTpdf : 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() << "qtfillTESTREPORTpdf : 1.1 " << value;
    qpdf.processFile(infilename);
    std::string nm;
    nm= qpdf.getFilename();
    qDebug() << "qtfillTESTREPORTpdf : 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);

    QPDFObjectHandle checkPASS  = QPDFObjectHandle::newName("/Yes");


    std::vector<QPDFPageObjectHelper> pages = pdh.getAllPages();
    qDebug() << "qtfillTESTREPORTpdf : 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);

        //char const* checkPASS="/Yes";
        char const* checkFAIL="/Off";
        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() << "qtfillTESTREPORTpdf : 4 "  << annot_iter->getAppearanceState().c_str() ;


            QPDFFormFieldObjectHelper ffh = afdh.getFieldForAnnotation(*annot_iter);

            //qDebug() << " OK 5 qtfillTESTREPORTpdf : "  <<ffh.getFieldType().c_str() ;

            qDebug() << " OK 5-1 ffh.getFieldType().c_str(): "  <<ffh.getFieldType().c_str() << " ffh.getFullyQualifiedName() : "  <<ffh.getFullyQualifiedName().c_str() << endl;
            bool isname = checkPASS.isName();

            qDebug()<< " OK 5-2 isName() : " <<isname <<  " getName() :"<<  checkPASS.getName().c_str();

            //qDebug() << ffh.setFieldAttribute();

            if (ffh.getFieldType() == "/Tx")
            {
                // 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, true);
                qDebug() << "OK 6 : " << value ;
            }
            else if (ffh.getFieldType() == "/Btn")
            {
                //qDebug() << "qtfillTESTREPORTpdf : 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(checkPASS,true);
                qDebug() << "OK7-1" << checkPASS.isNumber() << "ffh.getValueAsString() : " << ffh.getValueAsString().c_str() << endl;
                qDebug() << "OK7-2 " << checkPASS.isString() << "ffh.getValue().getName() : " <<ffh.getValue().getName().c_str() << endl;


                //                ffh.setV(checkPASS);
                //                qDebug() << "OK7- 1" << checkPASS << "  ffh.getValueAsString() : " << ffh.getValueAsString().c_str() << endl;
                //                qDebug() << "OK7- 2 " << checkPASS << "  ffh.getValue().getName() : " <<ffh.getValue().getName().c_str() << endl;
                //                                qDebug() << "OK7- 2 " << checkPASS << "  ffh.getValue().getName() : " <<ffh << endl;

                //                ffh.setV(checkFAIL);
                //                qDebug() << "OK7- 1" << checkFAIL << "  ffh.getValueAsString() : " << ffh.getValueAsString().c_str() << endl;
                //                qDebug() << "OK7- 2 " << checkFAIL << "  ffh.getValue().getName() : " <<ffh.getValue().getName().c_str() << endl;
            }
        }
    }

    //  Write out a new file
    QPDFWriter w(qpdf, outfilename);
    w.setStaticID(true); // for testing only
    w.write();
    return 3;
}

如果使用 pdf acrobat reader,您必须将导出值更改为 "Yes" 而不是 "On" 导出值是可从 acrobate reader

访问的复选框属性