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 );
 

Reply via email to