On Tue, Jan 11, 2005 at 12:15:15PM +0100, Aur�lien Jarno wrote:
> >The attached patch adds a new generate_fdf action to generate a FDF
> >out of a PDF file. (As the format is to ugly to write it directly
> >by hand). As I'm neighter fluently in java nor in C++, I guess it
> >could use some review.
> There is no attached file. Could you please send it again?
Ups, sorry. Someday I have to configure mutt to warn me if I talk
about attached patches and do not attach them....
So, this time it should be attached.
Hochachtungsvoll,
Bernhard R. Link
diff -r -u pdftk-1.12/debian/pdftk.1 pdftk-new/debian/pdftk.1
--- pdftk-1.12/debian/pdftk.1 2004-11-09 00:11:40.000000000 +0100
+++ pdftk-new/debian/pdftk.1 2005-01-11 11:50:18.000000000 +0100
@@ -44,7 +44,7 @@
.br
[\fBcat\fR | \fBattach_files\fR | \fBunpack_files\fR | \fBburst\fR |
.br
- \fBfill_form\fR | \fBbackground\fR |
+ \fBfill_form\fR | \fBbackground\fR | \fBgenerate_fdf\fR
.br
\fBdump_data\fR | \fBdump_data_fields\fR | \fBupdate_info\fR]
.br
@@ -65,6 +65,8 @@
.br
* Fill PDF Forms with FDF Data and/or Flatten Forms
.br
+* Generate FDF Data Stencil from PDF Forms
+.br
* Apply a Background Watermark
.br
* Report PDF Metrics such as Metadata and Bookmarks
@@ -122,7 +124,7 @@
applying all of the output options, like encryption and compression.
Available operations are: \fBcat\fR, \fBattach_files\fR, \fBunpack_files\fR,
\fBburst\fR, \fBfill_form\fR,
-\fBbackground\fR, \fBdump_data\fR, \fBdump_data_fields\fR, \fBupdate_info\fR.
Some operations
+\fBbackground\fR, \fBdump_data\fR, \fBdump_data_fields\fR, \fBgenerate_fdf\fR,
\fBupdate_info\fR. Some operations
takes additional arguments, described below.
.RS 3
.TP
@@ -243,6 +245,11 @@
Reads a single, input PDF file and reports form field statistics to the given
output
filename or (if no output is given) to stdout. Does not create a new PDF.
.TP
+.B generate_fdf
+Reads a single, input PDF file and generates a FDF file suitable for
\fBfill_form\fR
+out of it to the given output
+filename or (if no output is given) to stdout. Does not create a new PDF.
+.TP
.B update_info <info data filename | - | PROMPT>
Changes the metadata stored in a single PDF's Info dictionary to match
the input data file. The input data file uses the same syntax as the
diff -r -u pdftk-1.12/java_libs/com/lowagie/text/pdf/FdfWriter.java
pdftk-new/java_libs/com/lowagie/text/pdf/FdfWriter.java
--- pdftk-1.12/java_libs/com/lowagie/text/pdf/FdfWriter.java 2004-07-20
04:38:28.000000000 +0200
+++ pdftk-new/java_libs/com/lowagie/text/pdf/FdfWriter.java 2005-01-10
23:06:07.000000000 +0100
@@ -62,6 +62,7 @@
public class FdfWriter {
static byte[] HEADER_FDF =
DocWriter.getISOBytes("%FDF-1.2\n%\u00e2\u00e3\u00cf\u00d3\n");
HashMap fields = new HashMap();
+ PdfArray verbfields = null;
/** The PDF file associated with the FDF. */
private String file;
@@ -80,6 +81,10 @@
wrt.writeTo();
}
+ void setVerbField(PdfArray array) {
+ verbfields = array;
+ }
+
boolean setField(String field, PdfObject value) {
HashMap map = fields;
StringTokenizer tk = new StringTokenizer(field, ".");
@@ -300,7 +305,10 @@
void writeTo() throws DocumentException, IOException {
PdfDictionary dic = new PdfDictionary();
- dic.put(PdfName.FIELDS, calculate(fdf.fields));
+ if( fdf.verbfields != null )
+ dic.put(PdfName.FIELDS, fdf.verbfields);
+ else
+ dic.put(PdfName.FIELDS, calculate(fdf.fields));
if (fdf.file != null)
dic.put(PdfName.F, new PdfString(fdf.file,
PdfObject.TEXT_UNICODE));
PdfDictionary fd = new PdfDictionary();
diff -r -u pdftk-1.12/pdftk/pdftk.cc pdftk-new/pdftk/pdftk.cc
--- pdftk-1.12/pdftk/pdftk.cc 2004-10-27 02:56:18.000000000 +0200
+++ pdftk-new/pdftk/pdftk.cc 2005-01-10 21:25:18.000000000 +0100
@@ -62,6 +62,7 @@
#include "com/lowagie/text/pdf/PdfEncryptor.h"
#include "com/lowagie/text/pdf/PdfNameTree.h"
#include "com/lowagie/text/pdf/FdfReader.h"
+#include "com/lowagie/text/pdf/FdfWriter.h"
#include "com/lowagie/text/pdf/AcroFields.h"
#include "com/lowagie/text/pdf/PdfIndirectReference.h"
#include "com/lowagie/text/pdf/PdfIndirectObject.h"
@@ -294,6 +295,11 @@
strcmp( ss_copy, "datadump" )== 0 ) {
return dump_data_k;
}
+ else if( strcmp( ss_copy, "generate_fdf" )== 0 ||
+ strcmp( ss_copy, "fdfgen" )== 0 ||
+ strcmp( ss_copy, "fdfdump" )== 0 ) {
+ return generate_fdf_k;
+ }
else if( strcmp( ss_copy, "dump_data_fields" )== 0 ) {
return dump_data_fields_k;
}
@@ -438,7 +444,7 @@
TK_Session::is_valid() const
{
return( m_valid_b &&
- ( m_operation== dump_data_k ||
m_operation== dump_data_fields_k || m_authorized_b ) &&
+ ( m_operation== dump_data_k ||
m_operation== dump_data_fields_k || generate_fdf_k || m_authorized_b ) &&
!m_input_pdf.empty() &&
m_input_pdf_readers_opened_b &&
@@ -454,6 +460,7 @@
( m_operation== burst_k ||
m_operation== dump_data_k ||
m_operation==
dump_data_fields_k ||
+ m_operation== generate_fdf_k ||
m_operation== unpack_files_k ||
!m_output_filename.empty() ) );
}
@@ -520,6 +527,9 @@
case dump_data_fields_k:
cout << " dump_data_fields - Report form field data on a
single, input PDF." << endl;
break;
+ case generate_fdf_k:
+ cout << " generate_fdf_k - Generate a dummy FDF file from a
PDF." << endl;
+ break;
case unpack_files_k:
cout << " unpack_files - Copy PDF file attachments into given
directory." << endl;
break;
@@ -805,6 +815,10 @@
m_operation= dump_data_fields_k;
arg_state= output_e;
}
+ else if( arg_keyword== generate_fdf_k ) {
+ m_operation= generate_fdf_k;
+ arg_state= output_e;
+ }
else if( arg_keyword== fill_form_k ) {
m_operation= filter_k;
arg_state= form_data_filename_e; // look for an
FDF filename
@@ -2181,6 +2195,7 @@
}
break;
+ case generate_fdf_k :
case dump_data_fields_k :
case dump_data_k: { // report on input document
@@ -2194,6 +2209,26 @@
itext::PdfReader* input_reader_p=
m_input_pdf.begin()->m_readers.front().second;
+ if( m_operation== generate_fdf_k ) {
+ java::OutputStream* ofs_p= 0;
+
+ if( m_output_filename.empty() ||
m_output_filename== "-" ) {
+ ofs_p =
+ ofs_p = get_output_stream( "-",
+
m_ask_about_warnings_b );
+ } else {
+ ofs_p = get_output_stream(
m_output_filename,
+
m_ask_about_warnings_b );
+ }
+
+ if( ofs_p ) {
+ ReportGenerateFDF( ofs_p,
input_reader_p );
+ }
+ else { // error
+ cerr << "Error: unable to open
file for output: " << m_output_filename << endl;
+ }
+ }
+ else
if( m_output_filename.empty() ||
m_output_filename== "-" ) {
if( m_operation== dump_data_k ) {
ReportOnPdf( cout,
input_reader_p );
@@ -2336,7 +2371,7 @@
<operation> may be empty, or:\n\
[cat | attach_files | unpack_files | burst |\n\
fill_form | background |\n\
- dump_data | dump_data_fields | update_info]\n\
+ dump_data | dump_data_fields | generate_fdf | update_info]\n\
\n\
For Complete Help: pdftk --help\n";
}
@@ -2418,8 +2453,8 @@
sion.\n\
\n\
Available operations are: cat, attach_files,\n\
- unpack_files, burst, fill_form, background,\n\
- dump_data, dump_data_fields, update_info. Some\n\
+ unpack_files, generata_fdf, fill_form, background,\n\
+ dump_data, burst, dump_data_fields, update_info. Some\n\
operations takes additional arguments, described\n\
below.\n\
\n\
@@ -2571,6 +2606,10 @@
(if no output is given) to stdout. Does not\n\
create a new PDF.\n\
\n\
+ generate_fdf\n\
+ Reads a single, input PDF file and generate\n\
+ and generate a FDF file from it.\n\
+\n\
update_info <info data filename | - | PROMPT>\n\
Changes the metadata stored in a single PDF's\n\
Info dictionary to match the input data file.\n\
diff -r -u pdftk-1.12/pdftk/pdftk.h pdftk-new/pdftk/pdftk.h
--- pdftk-1.12/pdftk/pdftk.h 2004-10-26 22:15:42.000000000 +0200
+++ pdftk-new/pdftk/pdftk.h 2005-01-10 20:36:53.000000000 +0100
@@ -69,6 +69,7 @@
filter_k, // apply 'filters' to a single, input PDF based on
output args
dump_data_k, // no PDF output
dump_data_fields_k,
+ generate_fdf_k,
unpack_files_k, // unpack files from input; no PDF output
//
first_operation_k= cat_k,
diff -r -u pdftk-1.12/pdftk/report.cc pdftk-new/pdftk/report.cc
--- pdftk-1.12/pdftk/report.cc 2004-11-03 22:56:00.000000000 +0100
+++ pdftk-new/pdftk/report.cc 2005-01-11 11:42:39.000000000 +0100
@@ -65,6 +65,7 @@
#include "com/lowagie/text/pdf/PdfEncryptor.h"
#include "com/lowagie/text/pdf/PdfNameTree.h"
#include "com/lowagie/text/pdf/FdfReader.h"
+#include "com/lowagie/text/pdf/FdfWriter.h"
#include "com/lowagie/text/pdf/AcroFields.h"
#include "com/lowagie/text/pdf/PdfIndirectReference.h"
#include "com/lowagie/text/pdf/PdfIndirectObject.h"
@@ -909,6 +910,222 @@
return ret_val_b;
}
+static void
+ReportGenerateFDF( itext::PdfArray* array, itext::PdfArray* kids_array_p,
itext::PdfReader* reader_p )
+{
+ java::ArrayList* kids_p= kids_array_p->getArrayList();
+ if( kids_p ) {
+ for( jint kids_ii= 0; kids_ii< kids_p->size(); ++kids_ii ) {
+
+ itext::PdfDictionary* kid_p= (itext::PdfDictionary*)
+ reader_p->getPdfObject(
(itext::PdfDictionary*)(kids_p->get(kids_ii)) );
+ if( kid_p && kid_p->isDictionary() ) {
+ itext::PdfDictionary* dict = new
itext::PdfDictionary();
+
+ // field type
+ if( kid_p->contains( itext::PdfName::FT ) ) {
+ itext::PdfName* ft_p= (itext::PdfName*)
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::FT ) );
+ if( ft_p && ft_p->isName() ) {
+
+
+ if( ft_p->equals(
itext::PdfName::TX ) ) { // text
+ }
+ else if( ft_p->equals(
itext::PdfName::CH ) ) { // choice
+ }
+ else if( ft_p->equals(
itext::PdfName::SIG ) ) { // signature
+ } else
+ continue;
+ }
+ }
+
+ // field name; special inheritance rule:
prepend parent name
+ if( kid_p->contains( itext::PdfName::T ) ) {
+ itext::PdfString* pdfs_p=
(itext::PdfString*)
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::T ) );
+ if( pdfs_p ) {
+ dict->put( itext::PdfName::T,
pdfs_p);
+ }
+ }
+
+ // field alt. name
+ if( kid_p->contains( itext::PdfName::TU ) ) {
+ itext::PdfString* pdfs_p=
(itext::PdfString*)
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::TU ) );
+ if( pdfs_p ) {
+ dict->put( itext::PdfName::T,
pdfs_p);
+ }
+ }
+
+ // field value; inheritable; may be string or
name
+ if( kid_p->contains( itext::PdfName::V ) ) {
+ itext::PdfObject* pdfs_p=
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::V ) );
+ if( pdfs_p ) {
+ dict->put( itext::PdfName::V,
pdfs_p);
+ }
+ } else
+ if( kid_p->contains( itext::PdfName::DV ) ) {
+ itext::PdfObject* pdfs_p=
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::DV ) );
+ if( pdfs_p ) {
+ dict->put( itext::PdfName::V,
pdfs_p);
+ }
+ } else {
+ dict->put( itext::PdfName::V, new
itext::PdfString(JvNewStringLatin1("\n%%EOF\n")));
+ }
+
+ // rich text value; may be a string or a stream
+ if( kid_p->contains( itext::PdfName::RV ) ) {
+ itext::PdfObject* pdfo_p=
(itext::PdfObject*)
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::RV ) );
+ if( pdfo_p ) {
+ dict->put( itext::PdfName::RV,
pdfo_p);
+ }
+ }
+
+/* IT would be nice to also process those somehow...
+ // available states
+ if( kid_p->contains( itext::PdfName::AP ) ) {
+ itext::PdfDictionary* ap_p=
(itext::PdfDictionary*)
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::AP ) );
+ if( ap_p && ap_p->isDictionary() ) {
+
+ // this is one way to cull
button option names: iterate over
+ // appearance state names
+
+ // N
+ if( ap_p->contains(
itext::PdfName::N ) ) {
+ itext::PdfObject* n_p=
+
reader_p->getPdfObject( ap_p->get( itext::PdfName::N ) );
+ if( n_p &&
n_p->isDictionary() ) {
+ java::Set*
n_set_p= ((itext::PdfDictionary*)n_p)->getKeys();
+ for(
java::Iterator* it= n_set_p->iterator(); it->hasNext(); ) {
+
itext::PdfName* key_p= (itext::PdfName*)it->next();
+
+
ostringstream oss;
+
OutputPdfName( oss, key_p );
+
acc_state.m_states.insert( oss.str() );
+ }
+ }
+ }
+
+ // D
+ if( ap_p->contains(
itext::PdfName::D ) ) {
+ itext::PdfObject* n_p=
+
reader_p->getPdfObject( ap_p->get( itext::PdfName::D ) );
+ if( n_p &&
n_p->isDictionary() ) {
+ java::Set*
n_set_p= ((itext::PdfDictionary*)n_p)->getKeys();
+ for(
java::Iterator* it= n_set_p->iterator(); it->hasNext(); ) {
+
itext::PdfName* key_p= (itext::PdfName*)it->next();
+
+
ostringstream oss;
+
OutputPdfName( oss, key_p );
+
acc_state.m_states.insert( oss.str() );
+ }
+ }
+ }
+
+ // R
+ if( ap_p->contains(
itext::PdfName::R ) ) {
+ itext::PdfObject* n_p=
+
reader_p->getPdfObject( ap_p->get( itext::PdfName::N ) );
+ if( n_p &&
n_p->isDictionary() ) {
+ java::Set*
n_set_p= ((itext::PdfDictionary*)n_p)->getKeys();
+ for(
java::Iterator* it= n_set_p->iterator(); it->hasNext(); ) {
+
itext::PdfName* key_p= (itext::PdfName*)it->next();
+
+
ostringstream oss;
+
OutputPdfName( oss, key_p );
+
acc_state.m_states.insert( oss.str() );
+ }
+ }
+ }
+
+ }
+ }
+
+ // list-box / combo-box possible states
+ if( kid_p->contains( itext::PdfName::OPT ) ) {
+ itext::PdfArray* kid_opts_p=
(itext::PdfArray*)
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::OPT ) );
+ if( kid_opts_p && kid_opts_p->isArray()
) {
+ java::ArrayList* opts_p=
kid_opts_p->getArrayList();
+ for( jint opts_ii= 0; opts_ii<
opts_p->size(); ++opts_ii ) {
+ itext::PdfString*
opt_p= (itext::PdfString*)
+
reader_p->getPdfObject( (itext::PdfObject*)(opts_p->get(opts_ii)) );
+ if( opt_p &&
opt_p->isString() ) {
+ ostringstream
name_oss;
+
OutputPdfString( name_oss, opt_p );
+
acc_state.m_states.insert( name_oss.str() );
+ }
+ }
+ }
+ }
+*/
+
+ if( kid_p->contains( itext::PdfName::KIDS ) ) {
// recurse
+ itext::PdfArray* kid_kids_p=
(itext::PdfArray*)
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::KIDS ) );
+ if( kid_kids_p && kid_kids_p->isArray()
) {
+ itext::PdfArray* kida = new
itext::PdfArray();
+
+ ReportGenerateFDF(kida,
+
kid_kids_p,
+
reader_p );
+
+ dict->put(
itext::PdfName::KIDS, kida);
+
+ }
+ else { // error
+ }
+ }
+ if( kid_p->contains( itext::PdfName::T ) ) {
+ itext::PdfObject* pdfo_p=
(itext::PdfObject*)
+ reader_p->getPdfObject(
kid_p->get( itext::PdfName::T ) );
+ if( pdfo_p ) {
+ dict->put( itext::PdfName::T,
pdfo_p);
+ }
+
+ }
+
+ array->add(dict);
+ }
+ }
+ }
+ else { // error
+ cerr << "Internal Error: unable to get ArrayList in
ReportAcroFormFields()" << endl;
+ }
+}
+
+void
+ReportGenerateFDF(java::OutputStream* ofs_p, itext::PdfReader* reader_p )
+{
+ itext::PdfDictionary* catalog_p= reader_p->catalog;
+ if( catalog_p && catalog_p->isDictionary() ) {
+ itext::PdfArray* array = new itext::PdfArray();
+
+ itext::PdfDictionary* acro_form_p= (itext::PdfDictionary*)
+ reader_p->getPdfObject( catalog_p->get(
itext::PdfName::ACROFORM ) );
+ if( acro_form_p && acro_form_p->isDictionary() ) {
+
+ itext::PdfArray* fields_p= (itext::PdfArray*)
+ reader_p->getPdfObject( acro_form_p->get(
itext::PdfName::FIELDS ) );
+ if( fields_p && fields_p->isArray() ) {
+
+ // enter recursion
+ ReportGenerateFDF( array, fields_p, reader_p );
+ }
+ }
+ itext::FdfWriter* writer = new itext::FdfWriter();
+ writer->setVerbField(array);
+ writer->writeTo(ofs_p);
+ }
+ else { // error
+ cerr << "Internal Error: unable to access PDF catalog from
ReportAcroFormFields()" << endl;
+ }
+}
+
void
ReportAcroFormFields( ostream& ofs,
itext::PdfReader* reader_p )
diff -r -u pdftk-1.12/pdftk/report.h pdftk-new/pdftk/report.h
--- pdftk-1.12/pdftk/report.h 2004-10-27 02:00:48.000000000 +0200
+++ pdftk-new/pdftk/report.h 2005-01-10 21:04:40.000000000 +0100
@@ -27,6 +27,9 @@
itext::PdfReader* reader_p );
void
+ReportGenerateFDF(java::OutputStream* ofs_p, itext::PdfReader* reader_p );
+
+void
ReportOnPdf( ostream& ofs,
itext::PdfReader* reader_p );