I have a PDF File filled with text, arrays, images etc.. but also with textfields.
I would like to know if and how is it possible to import a correctly formatted XML File in order to fill these textfields such as Name, Surname, Address...
I simply want to do something like this action from the menu in Acrobat Reader : Edition- Form options - Import data but using Delphi programming.
I presume that I will need to open the PDF and use a function to parse the XML file in order to fill the form but for now I didn't find any good advice on the web explaining how to do such thing.
I hope my question is correct and it will be possible for you to answer me.
Best regards.
Here is some example code which uses the form-filling COM objects of Acrobat to fill in Acrobat fields from XML data parsed using the XML Partner library from Turbopower. It's not compilable as is because of some external dependencies, and in need of a bit of refactoring, but it should give you the general idea. The Calibrate
routine adds some markings to assist field placement.
unit AcrobatXMLu;
interface
uses
[...]
Acrobat_tlb, // main Acrobat COM wrapper
AFORMAUTLib_TLB; // Acrobat Forms COM objects
type
TGenerateFlag = (gfShowAcrobat, gfUseDefaultValues, gfGenerateTestData, gfAddCalibration, gfAlwaysFillCheckboxes);
TGenerateFlags = set of TGenerateFlag;
function CreateAcrobatFieldsInner(eStartNode: TxpElement;
const Path: String; Flags : TGenerateFlags; const OutputFile : String): Boolean;
implementation
function CreateAcrobatFieldsInner(eStartNode: TxpElement;
const Path: String; Flags : TGenerateFlags; const OutputFile : String): Boolean;
var
List : TxpNodeList;
X : TxpNode;
N,
Max : Integer;
E : TxpElement;
Align,
S : String;
AddedFields : TStringlist;
procedure CreateField(E : TxpElement);
var
FieldName,
FieldType,
MappedFieldType : String;
PageNo : Integer;
ALeft,
ARight,
ATop,
ABottom,
AWidth,
AHeight : Single;
S : String;
Field : IField; // Acrobat form field
IsMultiLine : Boolean;
begin
FieldName := E.GetAttribute('Name');
if CompareText(FieldName, 'Name1') = 0 then
NoOp;
FieldType := LowerCase(E.GetAttribute('PdfFieldType'));
MappedFieldType := FieldType;
if CompareText(MappedFieldType, 'checkbox') = 0 then
MappedFieldType := 'text';
IsMultiLine := CompareText(MappedFieldType, 'memo') = 0;
if (CompareText(MappedFieldType, 'Text') = 0) or (CompareText(MappedFieldType, 'Memo') = 0) then
MappedFieldType := 'text';
Align := LowerCase(E.GetAttribute('Align'));
S := E.GetAttribute('Page');
if S = '' then
S := TxpElement(E.ParentNode).GetAttribute('Page');
if S <> '' then
PageNo := StrToInt(S) - 1
else
PageNo := 0;
S := GetHeritableAttribute(E, 'XPos', 'FieldXPos');
ALeft := StrToInt(S);
ATop := E.GetAttributeInt('YPos');
S := GetHeritableAttribute(E, 'Width', 'FieldWidth');
if S <> '' then
AWidth := StrToInt(S)
else
AWidth := 60;
S := GetHeritableAttribute(E, 'Height', 'FieldHeight');
if S <> '' then
AHeight := StrToInt(S)
else
AHeight := 20;
ARight := ALeft + AWidth;
ABottom := ATop - AHeight;
try
Field := Acrobat.Fields.Add(FieldName, MappedFieldType, PageNo, ALeft, ATop, ARight, ABottom) as IField;
if True or (AddedFields.IndexOf(FieldName) < 0) then begin
if CompareText(MappedFieldType, 'text') = 0 then begin
S := GetHeritableAttribute(E, 'TextFont', 'FieldTextFont');
if S <> '' then
Field.Set_TextFont(S);
S := GetHeritableAttribute(E, 'TextSize', 'FieldTextSize');
if S <> '' then
if not (CompareText(S, 'Auto') = 0) then
Field.Set_TextSize(StrToInt(S));
if IsMultiLine then
Field.Set_IsMultiline(True);
S := E.GetAttribute('DefaultValue');
S := StringReplace(S, #10, #10#13, [rfReplaceAll]);
if S <> '' then begin
Field.Set_Value(S);
end
else begin
if CompareText(FieldType, 'checkbox') = 0 then begin
if gfAlwaysFillCheckboxes in Flags then
Field.Set_Value('X')
end
else begin
if gfGenerateTestData in Flags then
Field.Set_Value(Format('(%s)', [FieldName]));
end;
end;
if Align <> '' then
Field.Set_Alignment(Align);
end
else begin
if CompareText(MappedFieldType, 'checkbox') = 0 then begin
end;
end;
end;
if AddedFields.IndexOf(FieldName) < 0 then
AddedFields.Add(FieldName)
except
ShowMessage('Error adding field ' + FieldName);
end;
end;
procedure Calibrate;
var
X,
Y,
N,
M,
Page : Integer;
Field : IField; // Acrobat form field
procedure AddField(X, Y : Integer);
var
S : String;
FieldName : String;
begin
if X < 40 then
S := Format('Y:%d', [Y])
else
S := Format('X:%d', [X]);
FieldName := Format('X%dY%d', [X, Y]);
Field := Acrobat.Fields.Add(FieldName, 'text', Page, X, Y, X + 40, Y - 15) as IField;
Field.Set_TextFont('Courier');
Field.Set_TextSize(10);
Field.Set_Value(S);
end;
begin
for Page := 0 to ((Acrobat.AcroApp.GetActiveDoc as CAcroAVDoc).GetPDDoc as CAcroPDDoc).GetNumPages - 1 do begin
for N := 1 to 15 do
AddField(40 * N, 0);
N := 15;
for M := 0 to 60 do
AddField(0, N * M);
end;
end;
begin
AddedFields := TStringlist.Create;
AddedFields.Sorted := True;
Result := False;
try
List := eStartNode.SelectNodes(Path);
try
Max := List.Length - 1;
for N := 0 to Max do begin
X := List.Item(N);
if not (X is TxpElement) then Continue;
E := TxpElement(X);
S := E.GetAttribute('YPos');
if S <> '' then
CreateField(E);
end;
Result := True;
finally
List.Free;
end;
if gfAddCalibration in Flags then
Calibrate;
Acrobat.DocV.GetPDDoc.Save(PDSaveFull, OutputFile);
finally
AddedFields.Free;
end;
end;
end.
As written, this code adds the fields defined in the XML and fills them in as it goes along. Obvious;ly it is a trivial matter to fill in existing fields instead.
Some of the 3rd party PDF libraries for Delphi may also be able to fill in Acrobat fields. Also the command-line library PDFtk can also fill in Acrobat fields and do other things the Acrobat COM objects cannot, like "flattening" a PDF form, which effectively merges the text in the fields into the host document and so no longer editable as a form. See https://www.pdflabs.com/docs/pdftk-man-page/ for more info.