
{"id":52,"date":"2017-04-04T19:32:27","date_gmt":"2017-04-04T08:32:27","guid":{"rendered":"http:\/\/bakke.online\/?p=52"},"modified":"2017-04-04T19:32:27","modified_gmt":"2017-04-04T08:32:27","slug":"importing-data-to-ax-from-a-fillable-pdf-form","status":"publish","type":"post","link":"https:\/\/www.bakke.online\/index.php\/2017\/04\/04\/importing-data-to-ax-from-a-fillable-pdf-form\/","title":{"rendered":"Importing data to AX from a fillable PDF form"},"content":{"rendered":"<p>Recently, a requirement came up for importing data from a fillable PDF form into Dynamics AX 2012. \u00a0With the right tools, this is actually quite straightforward.<\/p>\n<p>PDF Labs have a product called PDFTK Server, which is a free download. \u00a0However,\u00a0if you need commercial support from them that&#8217;s a paid service. \u00a0Likewise, 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&#8230;), you&#8217;ll need a redistribution license. \u00a0The link to the license is <a href=\"https:\/\/www.pdflabs.com\/docs\/pdftk-license\/\">here<\/a>, and the product download is <a href=\"https:\/\/www.pdflabs.com\/tools\/pdftk-server\/\">here<\/a>.<\/p>\n<p><!--more-->After installing PDFTK Server, you can use it to extract values from the fields in a fillable PDF. \u00a0Just pass the file name and the command to dump data fields: \u00a0(It&#8217;s a 32-bit program, and I&#8217;m assuming that the operating system is 64-bit, adjust the file path below as suitable for your situation)<\/p>\n<pre>C:\\Program Files (x86)\\PDFTk Server\\bin\\pdftk.exe &lt;Path to PDF file&gt; dump_data_fields<\/pre>\n<p>It will return a list of all the fillable fields in the PDF file, with information about the field type, name, label, value, etc. \u00a0The format is easy to understand, so I&#8217;m not going to go into details here. \u00a0Instead, I&#8217;m going to show how to read this information into AX. \u00a0This has been developed and tested with AX2012R3, but it should work more or less unchanged on any version of AX that supports CLR interop.<\/p>\n<p>The method below can be added to your import class, or to a new class. \u00a0The naming of the class is up to you and your naming conventions.<\/p>\n<p>First, declare local variables and assert CAS permissions.<\/p>\n<pre>public Map getFormData( FilenameOpen _PDFFileName )\n{\n  Map fieldMap = new Map( Types::String, Types::String );\n  System.Diagnostics.Process process;\n  System.Diagnostics.ProcessStartInfo processStartInfo;\n  System.String processOutput;\n  System.IO.StreamReader processReader;\n  System.Exception clrException;\n  <span style=\"color: #0000ff;\"><strong>str<\/strong> <\/span>strOutput;\n  <span style=\"color: #0000ff;\"><strong>container<\/strong> <\/span>conOutput;\n  <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>i;\n  <span style=\"color: #0000ff;\"><strong>str<\/strong> <\/span>fieldName;\n  <span style=\"color: #0000ff;\"><strong>str<\/strong> <\/span>fieldValue;\n<span style=\"color: #0000ff;\"><strong>\n  new<\/strong> <\/span>InteropPermission( InteropKind::ClrInterop ).assert();<\/pre>\n<p>Next, create a .NET Process object, along with associated start info. \u00a0The path to the PDFTK executable should not be hardcoded here, I have done so for the sake of readability. \u00a0Stuff that one into a parameters table somewhere and make it configurable.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>try<\/strong><\/span>\n{\n  process = <span style=\"color: #0000ff;\"><strong>new<\/strong> <\/span>System.Diagnostics.Process();\n  processStartInfo = new System.Diagnostics.ProcessStartInfo();\n  processStartInfo.set_FileName( <span style=\"color: #993300;\">\"C:\\\\Program Files (x86)\\\\PDFtk Server\\\\bin\\\\pdftk.exe\"<\/span> );\n  processStartInfo.set_Arguments( <span style=\"color: #0000ff;\"><strong>strFmt<\/strong><\/span>( <span style=\"color: #993300;\">\"\\\"%1\\\" dump_data_fields\"<\/span>, _PDFFileName ) );\n  processStartInfo.set_RedirectStandardOutput( <span style=\"color: #0000ff;\"><strong>true<\/strong> <\/span>);\n  processStartInfo.set_UseShellExecute( <span style=\"color: #0000ff;\"><strong>false<\/strong> <\/span>);\n  process.set_StartInfo( processStartInfo );<\/pre>\n<p>Next, start the process, grab all its output and wait for it to exit.<\/p>\n<pre>  process.Start();\n\n  processReader = process.get_StandardOutput();\n  processOutput = processReader.ReadToEnd();\n\n  process.WaitForExit();\n\n  conOutput = <span style=\"color: #0000ff;\"><strong>str2con<\/strong><\/span>( processOutput, <span style=\"color: #993300;\">\"\\r\\n\"<\/span>, <span style=\"color: #0000ff;\"><strong>false<\/strong> <\/span>);\n}<\/pre>\n<p>The CLR code was wrapped in a try-block, so make sure we catch any CLR errors.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>catch<\/strong><\/span>( Exception::CLRError )\n{\n  clrException = CLRInterop::getLastException();\n  <span style=\"color: #0000ff;\"><strong>if<\/strong><\/span>( clrException != <span style=\"color: #0000ff;\"><strong>null<\/strong> <\/span>)\n  {\n    <span style=\"color: #0000ff;\"><strong>throw<\/strong> <\/span>error( clrException.ToString() );\n  }\n}<\/pre>\n<p>We&#8217;re done with the CLR stuff, so revert the assert.<\/p>\n<pre>CodeAccessPermission::revertAssert();<\/pre>\n<p>Now, time to get that output into a format that&#8217;s easier for AX to work with. \u00a0I&#8217;ll iterate over the input and put the results into a FieldName=&gt;FieldValue Map object. \u00a0In 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. \u00a0Then, then the FieldValue line comes through, we add the name and value to the map.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>for<\/strong><\/span>( i = <span style=\"color: #ff0000;\"><strong>1<\/strong><\/span>; i &lt;= <span style=\"color: #0000ff;\"><strong>conLen<\/strong><\/span>( conOutput ); i++ )\n{\n  strOutput = <span style=\"color: #0000ff;\"><strong>conPeek<\/strong><\/span>( conOutput, i );\n  info( strOutput );\n  <span style=\"color: #0000ff;\"><strong>if<\/strong><\/span>( <span style=\"color: #0000ff;\"><strong>subStr<\/strong><\/span>( strOutput, <span style=\"color: #ff0000;\"><strong>1<\/strong><\/span>, <span style=\"color: #ff0000;\"><strong>10<\/strong> <\/span>) == <span style=\"color: #993300;\">\"FieldName:\"<\/span> ) <span style=\"color: #008000;\"><em>\/\/ Need to include the colon, as there are two lines with the same prefix<\/em><\/span>\n  {\n    fieldName = <span style=\"color: #0000ff;\"><strong>subStr<\/strong><\/span>( strOutput, <span style=\"color: #ff0000;\"><strong>12<\/strong><\/span>, <span style=\"color: #0000ff;\"><strong>strLen<\/strong><\/span>( strOutput ) - <span style=\"color: #ff0000;\"><strong>11<\/strong> <\/span>);\n  }\n  <span style=\"color: #0000ff;\"><strong>else<\/strong> <strong>if<\/strong><\/span>( <span style=\"color: #0000ff;\"><strong>subStr<\/strong><\/span>( strOutput, <span style=\"color: #ff0000;\"><strong>1<\/strong><\/span>, <span style=\"color: #ff0000;\"><strong>10<\/strong> <\/span>) == <span style=\"color: #993300;\">\"FieldValue\"<\/span> )\n  {\n    fieldValue = <span style=\"color: #0000ff;\"><strong>subStr<\/strong><\/span>( strOutput, <span style=\"color: #ff0000;\"><strong>13<\/strong><\/span>, strLen( strOutput ) - <span style=\"color: #ff0000;\"><strong>12<\/strong> <\/span>);\n\n    <span style=\"color: #008000;\"><em>\/\/ We can take this shortcut because pdftk will send us the field name before the value.<\/em><\/span>\n    fieldMap.insert( fieldName, fieldValue );\n  }\n}<\/pre>\n<p>Finally, return the map.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>  return<\/strong><\/span> fieldMap;\n}<\/pre>\n<p>It&#8217;s worth noting that this method returns all values as strings. \u00a0If you need to use them as dates, numbers, enum values, etc. convert them as needed in your code. \u00a0However, make sure you validate the data format properly before converting, as there may not be sufficient validations happening on the PDF side. \u00a0I have seen stuff to make me wake up at night and my eyes bleed, all come through from PDFs that were supposedly validated already&#8230;<\/p>\n<p>As usual, xpo file can be found at:\u00a0<a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/Class_EB_PDFImport.zip\">Class_EB_PDFImport.zip<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently, a requirement came up for importing data from a fillable PDF form into Dynamics AX 2012. \u00a0With the right tools, this is actually quite straightforward. PDF Labs have a product called PDFTK Server, which is a free download. \u00a0However,\u00a0if you need commercial support from them that&#8217;s a paid service. \u00a0Likewise, if you want to &hellip; <a href=\"https:\/\/www.bakke.online\/index.php\/2017\/04\/04\/importing-data-to-ax-from-a-fillable-pdf-form\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Importing data to AX from a fillable PDF form&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,1],"tags":[2,3,11,12],"class_list":["post-52","post","type-post","status-publish","format-standard","hentry","category-dynamics-ax","category-uncategorized","tag-ax2012r3","tag-dynamics-ax","tag-imports","tag-pdf"],"_links":{"self":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts\/52","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/comments?post=52"}],"version-history":[{"count":0,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts\/52\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/media?parent=52"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/categories?post=52"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/tags?post=52"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}