I use the qpdf it works when I use /Tx for filling text field. But it doesn't work for checking a checkBox :
Lib header : https://github.com/qpdf/qpdf/blob/aa602fd107a622a3f12e6530220bb0303b95b520/include/qpdf/QPDFFormFieldObjectHelper.hh
My code : 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 ;
}
Who understand why it doesn't work for Btn although it does work for /Tx
Logs for /Tx and i see it on the outputPDF
OK6 OkeyMenIfillAutomaticallyYourSheet ffh.getValueAsString() : OkeyMenIfillAutomaticallyYourSheet
Logs for Btn : Nothing on the outputPDF
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
I understand that the qpdf don't see /Yes or /Off in my pdf but there is no /Tx neither and in the qpdf exemple ther is no /Yes or /Off fileds name
Need help to get the good idea, thanks you
intersting part of the lib source code :
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);
}
SourceCode of qpdf .dll :
I added some clarification elements on the answare
Thank you in advance for help!!!!
SOLVED : EVERY THING IS WORKING WELL WITH MY CODE BELLOW : HAPPINESSE
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;
}
If Using pdf acrobat reader you will have to change the exportation value to "Yes" and not "On" any more exportation value is a checkbox proprety accessible from acrobate reader