Acordefn table: formname formfield formorder 25 AGENCY.ADDR1 1 && 1st tag 25 AGENCY.NAME 2 && 2nd tagExample generated .xfdf file:
<?xml version="1.0" encoding="UTF-8"?> <xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve"> <f href="../finishedforms/130.pdf"/> <ids original="09C45E0011319E1D16B7987F78A4F5FB" modified="9CE596CE9498FF4DB24C5CA7F3594705"/> <fields> <field name="AGENCY"> <field name="ADDR1"> <value>sdffdfd</value> </field> <field name="NAME"> <value>dfsdfdf</value> </field> </field> </fields> </xfdf>Below is a program that we used in the past to generate the .xfdf file. We also had a table named acordict that mapped fillable form field tags to our variables. It is one of the original drafts that worked and it could be modified, streamlined, etc. For instance this example searches the registry for Adobe Reader, enters a password for passworded forms, and also launches Adobe Reader which in unecessary since the webbrowser is much more suitable for viewing the pdf form. The portion scanning the acordefn table and generating the .xfdf file may be useful though.
PARAMETERS xacordform IF TYPE('xacordform')<>"C" .OR. EMPTY(xacordform) RETURN .F. ENDIF IF TYPE('li_acord')<>"L" .OR. !li_acord && no acord license RETURN .F. ENDIF IF TYPE('li_acordview')<>"L" RETURN .F. ENDIF IF TYPE('dopass') ="U" dopass = .F. && do not process password on acord pdf forms ENDIF PRIVATE acordalias, tempfile, pdffile, lcsetsafety, xreturn acordalias = ALIAS() tempfile = hdir+xacordform+'.xfdf' pdffile = hdir+xacordform+'.pdf' lcsetsafety = SET('SAFETY') xreturn = .F. SET SAFETY OFF IF FILE(pdffile) && .pdf file DELETE FILE (pdffile) ENDIF IF FILE(tempfile) && .xfdf file DELETE FILE (tempfile) ENDIF IF UseFile("ACORDEFN",ddir) IF UseFile("ACORDICT",ddir) DO AcordGen DO AcordExe ENDIF ENDIF IF USED("ACORDICT") && holds acord/policy profiler data dictionary USE IN ACORDICT ENDIF IF USED("ACORDEFN") && holds field definitions USE IN ACORDEFN ENDIF IF !EMPTY(acordalias) .AND. ALIAS() <> acordalias SELECT (acordalias) ENDIF IF !EMPTY(lcsetsafety) SET SAFETY &lcsetsafety ENDIF IF FILE(pdffile) && .pdf file DELETE FILE (pdffile) ENDIF IF FILE(tempfile) && .xfdf file DELETE FILE (tempfile) ENDIF RETURN xreturn PROCEDURE AcordGen SELECT Acordefn.formname, Acordefn.formfield, Acordefn.formorder,; Acordict.profiler, 01 AS starttag, 00 AS endtag; FROM ; acordefn ; LEFT OUTER JOIN acordict ; ON Acordefn.formfield = Acordict.formfield; WHERE Acordefn.formname = ( xacordform ); ORDER BY Acordefn.formorder; INTO CURSOR Acordfields READWRITE SELECT Acordfields GOTO TOP PRIVATE m.output m.output = FCREATE(tempfile,0) IF m.output < 0 =MESSAGEBOX("Could not create '+tempfile+' Acord xml file.",0+16+4096,"ERROR") RETURN ENDIF PRIVATE ifields, mfield, mvalue ifields = 1 mfield = '' mvalue = '' DIMENSION fNames[1], fTagNames[1] fNames = '' fTagNames = '' PRIVATE i, iRecCount, iRecNo, iFieldCount, iFieldNo iRecCount = RECCOUNT() *---Remove duplicate tags FOR iRecNo = 1 TO iRecCount GOTO iRecNo *---Expand field name mfield = Acordfields.formfield ifields = OCCURS('.', mfield) + 1 DIMENSION fNames[ifields] fNames = '' FOR i = 1 TO ifields IF OCCURS('.', mfield) > 0 fNames[i] = SUBSTR(mfield, 1, AT('.', mfield)) mfield = SUBSTR(mfield, AT('.', mfield)+1) ELSE fNames[i] = mfield mfield = '' ENDIF NEXT *---Initialize EndTag REPLACE AcordFields.endtag WITH ifields *---Step through the field names. Skip the last one iFieldCount = ifields - 1 FOR iFieldNo = 1 TO iFieldCount IF fNames[iFieldNo] = '*.' LOOP ENDIF GOTO iRecNo SKIP SCAN REST *---Expand field name mfield = Acordfields.formfield ifields = OCCURS('.', mfield) + 1 DIMENSION fTagNames[ifields] fTagNames = '' FOR i = 1 TO ifields IF OCCURS('.', mfield) > 0 fTagNames[i] = SUBSTR(mfield, 1, AT('.', mfield)) mfield = SUBSTR(mfield, AT('.', mfield)+1) ELSE fTagNames[i] = mfield mfield = '' ENDIF NEXT *---Check for Duplicates IF TYPE('fTagNames[iFieldNo]') <> 'U' IF fNames[iFieldNo] == fTagNames[iFieldNo] IF iFieldNo = 1 fTagNames[iFieldNo] = '*.' REPLACE Acordfields.starttag WITH Acordfields.starttag + 1 ELSE IF fTagNames[iFieldNo-1] = '*.' fTagNames[iFieldNo] = '*.' REPLACE Acordfields.starttag WITH Acordfields.starttag + 1 ENDIF ENDIF ELSE EXIT ENDIF ELSE EXIT ENDIF *---Save Record mfield = '' FOR i = 1 TO ifields IF !EMPTY(fTagNames[i]) mfield = mfield + fTagNames[i] ENDIF NEXT REPLACE Acordfields.formfield WITH mfield ENDSCAN NEXT *---Set EndTag GOTO iRecNo SKIP IF !EOF('Acordfields') ifields = OCCURS('*', AcordFields.formfield) SKIP -1 REPLACE AcordFields.endtag WITH AcordFields.endtag - ifields ENDIF NEXT *---Write Header =FWRITE(m.output, '<?xml version="1.0" encoding="UTF-8"?>'+CHR(10)) =FWRITE(m.output, '<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">'+CHR(10)) =FWRITE(m.output, '<f href="'+xacordform+'.pdf"/>'+CHR(10)) =FWRITE(m.output, '<fields>'+CHR(10)) *---Loop through records GOTO TOP SCAN *---Expand field name mfield = Acordfields.formfield ifields = OCCURS('.', mfield) + 1 DIMENSION fNames[ifields] fNames = '' FOR i = 1 TO ifields IF OCCURS('.', mfield) > 0 fNames[i] = SUBSTR(mfield, 1, AT('.', mfield)-1) mfield = SUBSTR(mfield, AT('.', mfield)+1) ELSE fNames[i] = mfield mfield = '' ENDIF NEXT *---Write Tags FOR i = 1 TO ifields IF i >= Acordfields.starttag =FWRITE(m.output, '<field name="'+RTRIM(fNames[i])+'">'+CHR(10)) ENDIF NEXT *---Write Value mvalue = '' IF !ISNULL(AcordFields.profiler) .and. !EMPTY(ALLTRIM(Acordfields.profiler)) mvalue = EVALUATE(Acordfields.profiler) ENDIF mvalue = XmlEncode(mvalue) =FWRITE(m.output, '<value>'+RTRIM(mvalue)+'</value>'+CHR(10)) FOR i = 1 TO Acordfields.endtag =FWRITE(m.output, '</field>'+CHR(10)) NEXT ENDSCAN =FWRITE(m.output, '</fields>'+CHR(10)) =FWRITE(m.output, '</xfdf>'+CHR(10)) =FCLOSE(m.output) USE IN Acordfields RETURN PROCEDURE AcordExe PRIVATE cAppName, lcresult, lcline, llacordok, lcACORDdef PRIVATE lnpos, lcpath, lcshort cAppName = '' lcresult = '' lcline = '' llacordok = .T. lcACORDdef = SYS(5)+SYS(2003) lnpos = 0 lcshort = '' lcpath = '' IF FILE(tempfile) IF UseFile("ACORDFRM",ddir,"FORMNAME") IF INDEXSEEK(UPPER(ALLTRIM(xacordform)),.T.,'ACORDFRM','FORMNAME') =STRTOFILE(ACORDFRM.FORMDATA,pdffile) ELSE =MESSAGEBOX("Cannot locate form:"+UPPER(ALLTRIM(xacordform))+" in forms table.",0+16+4096,"ERROR - Form Missing in Form Table.") ENDIF USE IN ACORDFRM ENDIF IF FILE(pdffile) DO DECLexewait lcresult = Acrofind() IF EMPTY(lcresult) lcresult = readregistrykey() IF OCCURS('"',lcresult)>0 IF TYPE('lcresult')="C" .AND. !EMPTY(lcresult) m.cAppName=SUBSTR(lcresult,2,AT('"',lcresult,2)-2) ELSE m.cAppName = '' ENDIF ELSE m.cAppName = lcresult+"AcroRd32.Exe" ENDIF ELSE m.cAppName = lcresult ENDIF IF FILE(m.cAppName) && AcroRd32.exe lcline = " /p /h " IF li_acordview && preview in acrobat reader so user can select printer by ctrl-p lcline = "" ENDIF SET DEFAULT TO LEFT(hdir,RAT("\",hdir)-1) lnpos=(RAT("\",m.cAppName)+1) && one position to the right of lcshort='' lcpath='' IF lnpos > 0 lcshort = UPPER(SUBSTR(m.cAppName,lnpos)) && grab from lnpos to end lcpath = UPPER(LEFT(m.cAppName,RAT("\",m.cAppName)-1)) && strip off the extension ENDIF IF !EMPTY(lcshort) .AND. !EMPTY(lcpath) lcpath = "'"+lcpath+"'" SET DEFAULT TO &lcpath llacordok = ExeWait(lcshort,lcline+hdir+xacordform+'.xfdf',lcpath) ELSE llacordok = ExeWait( m.cAppName, lcline+xacordform+'.xfdf',hdir) ENDIF IF !llacordok =MESSAGEBOX("Unable to print Acord Form.",0+16+4096,"ERROR") ELSE WAIT WINDOW "Finished Printing" NOWAIT ENDIF SET DEFAULT TO &lcACORDdef ELSE =MESSAGEBOX('Unable to locate Acrobat Reader. Please install and try again',0+16+4096,'ERROR - Acrobat Reader Missing.') ENDIF DO RELexewait ELSE =MESSAGEBOX("Unable to create Acord Form.",0+16+4096,"ERROR - PDF or XFDF Missing.") ENDIF ELSE =MESSAGEBOX("Unable to create Acord Form.",0+16+4096,"ERROR - PDF or XFDF Missing.") ENDIF xreturn = .T. RETURN PROCEDURE DECLexewait && load required dlls for exewait function =ADLLS(dllarray) IF ASCAN(dllarray,'CREATEPROCESS') = 0 DECLARE INTEGER CreateProcess IN kernel32; INTEGER lpAppName, STRING lpCmdLine, INTEGER lpProcAttr,; INTEGER lpThrAttr, INTEGER bInhHandles, INTEGER dwCrFlags,; INTEGER lpEnvir, INTEGER lpCurDir, ; STRING @lpStInfo, STRING @lpProcInfo ENDIF IF ASCAN(dllarray,'GETLASTERROR') = 0 DECLARE INTEGER GetLastError IN kernel32 ENDIF IF ASCAN(dllarray,"CLOSEHANDLE") = 0 DECLARE INTEGER CloseHandle IN kernel32 INTEGER hObject ENDIF IF ASCAN(dllarray,"GETEXITCODEPROCESS") = 0 DECLARE INTEGER GetExitCodeProcess IN WIN32API INTEGER hProcess, INTEGER @lpExitCode ENDIF IF ASCAN(dllarray,"SLEEP") = 0 DECLARE Sleep IN kernel32 INTEGER dwMilliseconds ENDIF RETURN PROCEDURE RELexewait && release dlls for exewait function CLEAR DLLS CreateProcess CLEAR DLLS GetLastError CLEAR DLLS CloseHandle CLEAR DLLS GetExitCodeProcess CLEAR DLLS Sleep RETURN FUNCTION buf2dword(lcBuffer) RETURN ASC(SUBSTR(lcBuffer, 1,1)) + ; ASC(SUBSTR(lcBuffer, 2,1)) * 256 +; ASC(SUBSTR(lcBuffer, 3,1)) * 65536 +; ASC(SUBSTR(lcBuffer, 4,1)) * 16777216 FUNCTION readregistrykey #DEFINE HKEY_CLASSES_ROOT -2147483648 #DEFINE HKEY_CURRENT_USER -2147483647 #DEFINE HKEY_LOCAL_MACHINE -2147483646 #DEFINE HKEY_USERS -2147483645 #DEFINE REG_SZ 1 && String #DEFINE REG_BINARY 3 && Binary data #DEFINE REG_DWORD 4 && 32bits int #DEFINE ERROR_SUCCESS 0 && OK LOCAL nKey, cSubKey, cValue, cValueRead nKey = HKEY_LOCAL_MACHINE cSubKey = "Software\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe" cValue = "Path" cValueRead = ReadREG_SZ(nKey, cSubKey, cValue) IF EMPTY(cValueRead) cValueRead = '' ENDIF RETURN cValueRead FUNCTION ReadREG_SZ PARAMETERS nKey, cSubKey, cValue DECLARE INTEGER RegOpenKey IN Win32API ; INTEGER nHKey, STRING @cSubKey, INTEGER @nResult DECLARE INTEGER RegQueryValueEx IN Win32API ; INTEGER nHKey, STRING lpszValueName, INTEGER dwReserved,; INTEGER @lpdwType, STRING @lpbData, INTEGER @lpcbData DECLARE INTEGER RegCloseKey IN Win32API INTEGER nHKey LOCAL nErrCode && Error Code returned from Registry functions LOCAL nKeyHandle && Handle to Key that is opened in the Registry LOCAL lpdwValueType && Type of Value that we are looking for LOCAL lpbValue && The data stored in the value LOCAL lpcbValueSize && Size of the variable LOCAL lpdwReserved && Reserved Must be 0 nKeyHandle = 0 lpdwReserved = 0 lpdwValueType = REG_SZ lpbValue = "" nErrCode = RegOpenKey(nKey, cSubKey, @nKeyHandle) IF (nErrCode # 0) THEN RETURN "" ENDIF lpcbValueSize = 1 nErrCode=RegQueryValueEx(nKeyHandle, cValue, lpdwReserved, @lpdwValueType, @lpbValue, @lpcbValueSize) lpbValue = SPACE(lpcbValueSize) nErrCode=RegQueryValueEx(nKeyHandle, cValue, lpdwReserved, @lpdwValueType, @lpbValue, @lpcbValueSize) =RegCloseKey(nKeyHandle) CLEAR DLLS RegOpenKey CLEAR DLLS RegQueryValueEx CLEAR DLLS RegCloseKey IF (nErrCode # 0) THEN RETURN "" ENDIF lpbValue = LEFT(lpbValue, lpcbValueSize - 1) RETURN lpbValue FUNCTION Acrofind PRIVATE WNetGetConnection, lpszLocalname, lpszRemoteName, ; sLen, Lni, DRIVE, Dtype, llfound, Lni PRIVATE ARRAY afilesfound(1,3) DIMENSION afilesfound(1,3) afilesfound(1,1)=" " llfound = .F. Lni = 0 DECLARE INTEGER WNetGetConnection IN win32api ; STRING lpszLocalName,; STRING lpszRemoteName,; INTEGER @ lpchBuffer && Declare the external WNetGetConnection ; API FUNCTION slpRemoteName = SPACE(254) && Initialize variables sLen = LEN(slpRemoteName) && Initialize variables *--Populate the one dimensional array with valid drive letters FOR I = 1 TO 26 && Loop through drive letters A thru Z DRIVE = CHR(I + 64) Dtype = DRIVETYPE(DRIVE) && Determine drive type DO CASE CASE Dtype = 3 && Hard drives Lni=Lni+1 DIMENSION adrivelist(Lni,3) adrivelist(Lni,1)=DRIVE adrivelist(Lni,2)=.F. adrivelist(Lni,3)=.F. CASE Dtype = 4 && Removable or network drives iSuccess = WNetGetConnection(DRIVE + ; ":",@slpRemoteName,@sLen) IF iSuccess = 0 Lni=Lni+1 DIMENSION adrivelist(Lni,3) adrivelist(Lni,1)=DRIVE adrivelist(Lni,2)=.F. adrivelist(Lni,3)=.F. ENDIF ENDCASE ENDFOR llfound = .F. FOR Lni = 1 TO ALEN(adrivelist,1) FOR ib = 10 TO 1 STEP -1 lcacrofile = adrivelist(Lni,1)+":"+"\Program Files\Adobe\Acrobat "+ALLTRIM(STR(ib))+".0\Reader\AcroRd32.Exe" IF FILE(lcacrofile) llfound = .T. EXIT ENDIF ENDFOR IF llfound EXIT ENDIF ENDFOR CLEAR DLLS WNetGetConnection IF llfound RETURN lcacrofile ELSE RETURN '' ENDIF PROCEDURE ExeWait (lcApp, lcCmdLine, lcdir) PRIVATE lntimes lntimes = 0 IF _VFP.AUTOYIELD = .F. llsetback = .T. ELSE llsetback = .F. ENDIF _VFP.AUTOYIELD = .T. PRIVATE lnclosepass lnclosepass = 0 #DEFINE INFINITE 0xFFFFFFFF LOCAL lcStartupInfo, lcProcInfo, hProcess, ; lnPrio, lnIBelieve1 lnIBelieve1 = 1 && Don't remember what that was lnPrio = 32 && Priority of Process=Normal lcStartupInfo = CHR(68) + REPLI(CHR(0), 67) lcProcInfo = REPLI(CHR(0), 16) IF CreateProcess(0, m.lcApp+" "+m.lcCmdLine+CHR(0), 0,0,; m.lnIBelieve1, m.lnPrio,; 0, 0, @lcStartupInfo, @lcProcInfo) <> 0 hProcess = buf2dword(SUBSTR(lcProcInfo, 1,4)) hThread = buf2dword(SUBSTR(lcProcInfo, 5,4)) DOEVENTS IF !li_acordview && printing only DO stopadobe ELSE DO WHILE .T. exitcode = 0 && initialize return value to 0 = GetExitCodeProcess(hProcess, @exitcode) && try to obtain process exit code IF exitcode # 259 && not still busy EXIT && fall out of loop ELSE *--Attempt to close the password window up to 3 times IF dopass .and. lnclosepass < 3 .AND. PassWin('Password') = Sleep (1000) && wait 1 seconds lnclosepass = lnclosepass + 1 ENDIF ENDIF = Sleep (100) && wait .1 seconds ENDDO ENDIF = CloseHandle(hThread) = CloseHandle(hProcess) ELSE IF llsetback _VFP.AUTOYIELD = .F. ENDIF RETURN .F. ENDIF IF llsetback _VFP.AUTOYIELD = .F. ENDIF RETURN FUNCTION PassWin PARAMETER tcTitle DECLARE INTEGER FindWindow IN Win32API AS FindWindow STRING, STRING lnwindow = FindWindow(0,tcTitle) IF lnwindow > 0 #DEFINE WM_SETFOCUS 0x0007 #DEFINE KEYEVENTF_KEYUP 2 #DEFINE VK_SHIFT 0x10 DECLARE SHORT PostMessage IN user32; INTEGER HWND,; INTEGER Msg,; STRING wParam,; INTEGER LPARAM DECLARE INTEGER SetForegroundWindow IN Win32API; INTEGER HWND DECLARE INTEGER SetActiveWindow IN Win32API; INTEGER HWND DECLARE keybd_event IN Win32API; INTEGER bVk, INTEGER bScan,; INTEGER dwFlags, INTEGER dwExtraInfo HWND = lnwindow ReturnValue = PostMessage(HWND, WM_SETFOCUS, 0, 0) lcsentence = "~PASSWORD"+CHR(13) && change to valid form password = SetActiveWindow(HWND) = SetForegroundWindow(HWND) FOR Lni = 1 TO LEN(lcsentence) lcchar = SUBSTR(lcsentence, Lni, 1) IF lcchar <> "~" llshift = ISUPPER(lcchar) lnchar = ASC(UPPER(lcchar)) IF llshift = keybd_event(VK_SHIFT, 0, 0, 0) ENDIF = keybd_event(lnchar, 0, 0, 0) = keybd_event(lnchar, 0, KEYEVENTF_KEYUP, 0) IF llshift = keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0) ENDIF ELSE lnchar = 192 = keybd_event(VK_SHIFT, 0, 0, 0) = keybd_event(lnchar, 0, 0, 0) = keybd_event(lnchar, 0, KEYEVENTF_KEYUP, 0) = keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0) ENDIF NEXT CLEAR DLLS PostMessage CLEAR DLLS SetForegroundWindow CLEAR DLLS SetActiveWindow CLEAR DLLS keybd_event CLEAR DLLS FindWindow RETURN .T. ELSE CLEAR DLLS FindWindow RETURN .F. ENDIF PROCEDURE stopadobe PARAMETERS hThread DECLARE Sleep IN kernel32 INTEGER dwMilliseconds DO WHILE !Passwin('Password') .and. lnclosepass < 10 lnclosepass = lnclosepass + 1 =SLEEP(300) ENDDO =Sleep(5000) LOCAL awin_apps, vfp_handle, ln_current_window,ln_window_count DIMENSION awin_apps[1,2] vfp_handle=0 DECLARE INTEGER FindWindow ; IN win32api ; INTEGER nullpointer, ; STRING cwindow_name DECLARE INTEGER GetWindow ; IN win32api ; INTEGER ncurr_window_handle, ; INTEGER ndirection DECLARE INTEGER GetWindowText ; IN win32api ; INTEGER n_win_handle, ; STRING @ cwindow_title, ; INTEGER ntitle_length vfp_handle=FindWindow(0,_SCREEN.CAPTION) ln_current_window=vfp_handle ln_window_count=0 DO WHILE ln_current_window>0 lc_window_title=SPACE(255) ln_length=GetWindowText(ln_current_window, ; @lc_window_title,LEN(lc_window_title)) IF ln_length>0 lc_window_title=STRTRAN(TRIM(lc_window_title),CHR(0),"") ELSE lc_window_title="" ENDIF IF ln_current_window>0 .AND. !EMPTY(lc_window_title) ln_window_count=ln_window_count+1 DIMENSION awin_apps(ln_window_count,2) awin_apps[ln_Window_Count,1]=lc_window_title awin_apps[ln_Window_Count,2]=ln_current_window ENDIF ln_current_window=GetWindow(ln_current_window,2) ENDDO PRIVATE it FOR it = 1 TO ALEN(awin_apps,1) IF "Adobe" $ awin_apps(it,1) IF TYPE('awin_apps(it,2)')="N" =close2(awin_apps(it,2)) ENDIF ENDIF ENDFOR RETURN .T. PROCEDURE close2 PARAMETERS HWND #DEFINE WM_CLOSE 16 #DEFINE WM_SETFOCUS 0x0007 DECLARE SHORT PostMessage IN user32; INTEGER HWND,; INTEGER Msg,; STRING wParam,; INTEGER LPARAM DECLARE INTEGER SetForegroundWindow IN Win32API; INTEGER HWND DECLARE INTEGER SetActiveWindow IN Win32API; INTEGER HWND =PostMessage(HWND, WM_SETFOCUS, 0, 0) =PostMessage(HWND, WM_CLOSE, 0, 0) RETURN .T. FUNCTION XmlEncode LPARAMETERS teExpression LOCAL lcReturn lcReturn = TRAN(teExpression) IF VARTYPE(teExpression) = 'C' lcReturn = ALLTRIM(lcReturn) lcReturn = STRTRAN(lcReturn, '&', '&') lcReturn = STRTRAN(lcReturn, '<', '&lt;') lcReturn = STRTRAN(lcReturn, '>', '>') lcReturn = STRTRAN(lcReturn, '"', '"') lcReturn = STRTRAN(lcReturn, "'", ''') ENDIF RETURN lcReturn FUNCTION XMLDeCode LPARAMETERS cTXT LOCAL i,ch FOR i=1 TO 255 ch=ALLTRIM(STR(i)) cTXT=STRTRAN(cTXT,"&#"+ch+";",CHR(i)) ENDFOR RETURN cTXT