Как импортировать данные из XML в форму PDF с помощью Delphi?

У меня есть файл PDF, заполненный текстом, массивами, изображениями и т. д., но также и текстовыми полями.

Я хотел бы знать, возможно ли и как импортировать правильно отформатированный файл XML для заполнения таких текстовых полей, как Имя, фамилия, адрес...

Я просто хочу сделать что-то вроде этого действия из меню в Acrobat Reader: Edition- Form options - Import data, но используя программирование Delphi.

Я предполагаю, что мне нужно будет открыть PDF-файл и использовать функцию для анализа XML-файла, чтобы заполнить форму, но пока я не нашел в Интернете хороших советов, объясняющих, как это сделать.

Я надеюсь, что мой вопрос правильный, и вы сможете ответить мне.

С наилучшими пожеланиями.


person VirussInside    schedule 07.11.2017    source источник
comment
Это тривиально сделать в полной версии Acrobat, импортировав COM-объекты в модуль интерфейса и затем создав их во время выполнения. Возможно, в последних версиях Reader есть аналогичная возможность.   -  person MartynA    schedule 07.11.2017
comment
@MartynA Спасибо за ответ, я вроде как понимаю, но не понимаю, как это сделать в коде Delphi.   -  person VirussInside    schedule 08.11.2017
comment
Что ж. одна из библиотек COM-объектов Acrobat — это заполнение форм. Вы создаете модуль импорта Delphi (something_tlb.pas) из библиотеки типов, а затем просто создаете экземпляры объектов в нем для заполнения форм.   -  person MartynA    schedule 08.11.2017


Ответы (1)


Вот пример кода, который использует COM-объекты Acrobat для заполнения форм для заполнения полей Acrobat из данных XML, проанализированных с использованием библиотеки XML Partner от Turbopower. Он не компилируется как есть из-за некоторых внешних зависимостей и нуждается в небольшом рефакторинге, но он должен дать вам общее представление. Процедура Calibrate добавляет некоторые метки для облегчения размещения поля.

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.

Как написано, этот код добавляет поля, определенные в XML, и заполняет их по мере продвижения. Очевидно, что заполнение существующих полей является тривиальной задачей.

Некоторые из сторонних библиотек PDF для Delphi также могут заполнять поля Acrobat. Кроме того, библиотека командной строки PDFtk также может заполнять поля Acrobat и делать другие вещи, которые COM-объекты Acrobat не могут, например «выравнивание» PDF-формы, что эффективно объединяет текст в полях с основным документом и, таким образом, больше не редактируется как форма. См. https://www.pdflabs.com/docs/pdftk-man-page/ для получения дополнительной информации.

person MartynA    schedule 08.11.2017
comment
Большое Вам спасибо. Я войду в него и попробую все. Кажется, это дает мне ответ или, по крайней мере, очень хорошую линию поведения. Еще раз спасибо - person VirussInside; 09.11.2017