Importing data to AX from a fillable PDF form

Recently, a requirement came up for importing data from a fillable PDF form into Dynamics AX 2012.  With the right tools, this is actually quite straightforward.

PDF Labs have a product called PDFTK Server, which is a free download.  However, if you need commercial support from them that’s a paid service.  Likewise, if you want to redistribute the software and your software is not licensed under the GPL (Hint, Dynamics AX is most certainly not GPL-licensed…), you’ll need a redistribution license.  The link to the license is here, and the product download is here.

After installing PDFTK Server, you can use it to extract values from the fields in a fillable PDF.  Just pass the file name and the command to dump data fields:  (It’s a 32-bit program, and I’m assuming that the operating system is 64-bit, adjust the file path below as suitable for your situation)

C:\Program Files (x86)\PDFTk Server\bin\pdftk.exe <Path to PDF file> dump_data_fields

It will return a list of all the fillable fields in the PDF file, with information about the field type, name, label, value, etc.  The format is easy to understand, so I’m not going to go into details here.  Instead, I’m going to show how to read this information into AX.  This has been developed and tested with AX2012R3, but it should work more or less unchanged on any version of AX that supports CLR interop.

The method below can be added to your import class, or to a new class.  The naming of the class is up to you and your naming conventions.

First, declare local variables and assert CAS permissions.

public Map getFormData( FilenameOpen _PDFFileName )
{
  Map fieldMap = new Map( Types::String, Types::String );
  System.Diagnostics.Process process;
  System.Diagnostics.ProcessStartInfo processStartInfo;
  System.String processOutput;
  System.IO.StreamReader processReader;
  System.Exception clrException;
  str strOutput;
  container conOutput;
  int i;
  str fieldName;
  str fieldValue;

  new InteropPermission( InteropKind::ClrInterop ).assert();

Next, create a .NET Process object, along with associated start info.  The path to the PDFTK executable should not be hardcoded here, I have done so for the sake of readability.  Stuff that one into a parameters table somewhere and make it configurable.

try
{
  process = new System.Diagnostics.Process();
  processStartInfo = new System.Diagnostics.ProcessStartInfo();
  processStartInfo.set_FileName( "C:\\Program Files (x86)\\PDFtk Server\\bin\\pdftk.exe" );
  processStartInfo.set_Arguments( strFmt( "\"%1\" dump_data_fields", _PDFFileName ) );
  processStartInfo.set_RedirectStandardOutput( true );
  processStartInfo.set_UseShellExecute( false );
  process.set_StartInfo( processStartInfo );

Next, start the process, grab all its output and wait for it to exit.

  process.Start();

  processReader = process.get_StandardOutput();
  processOutput = processReader.ReadToEnd();

  process.WaitForExit();

  conOutput = str2con( processOutput, "\r\n", false );
}

The CLR code was wrapped in a try-block, so make sure we catch any CLR errors.

catch( Exception::CLRError )
{
  clrException = CLRInterop::getLastException();
  if( clrException != null )
  {
    throw error( clrException.ToString() );
  }
}

We’re done with the CLR stuff, so revert the assert.

CodeAccessPermission::revertAssert();

Now, time to get that output into a format that’s easier for AX to work with.  I’ll iterate over the input and put the results into a FieldName=>FieldValue Map object.  In the output from PDFTk, the field name is sent before the value, so we can treat the appearance of FieldName to flag the start of a new field and set the fieldName variable.  Then, then the FieldValue line comes through, we add the name and value to the map.

for( i = 1; i <= conLen( conOutput ); i++ )
{
  strOutput = conPeek( conOutput, i );
  info( strOutput );
  if( subStr( strOutput, 1, 10 ) == "FieldName:" ) // Need to include the colon, as there are two lines with the same prefix
  {
    fieldName = subStr( strOutput, 12, strLen( strOutput ) - 11 );
  }
  else if( subStr( strOutput, 1, 10 ) == "FieldValue" )
  {
    fieldValue = subStr( strOutput, 13, strLen( strOutput ) - 12 );

    // We can take this shortcut because pdftk will send us the field name before the value.
    fieldMap.insert( fieldName, fieldValue );
  }
}

Finally, return the map.

  return fieldMap;
}

It’s worth noting that this method returns all values as strings.  If you need to use them as dates, numbers, enum values, etc. convert them as needed in your code.  However, make sure you validate the data format properly before converting, as there may not be sufficient validations happening on the PDF side.  I have seen stuff to make me wake up at night and my eyes bleed, all come through from PDFs that were supposedly validated already…

As usual, xpo file can be found at: Class_EB_PDFImport.zip

One thought on “Importing data to AX from a fillable PDF form”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.