Bài viết Kỹ thuật

Xuất và Nhập XFDF trong Delphi với PDFium Component

PDFium Component cho Delphi và Lazarus trao đổi dữ liệu biểu mẫu và các chú thích thông qua XFDF, định dạng trao đổi XML được định nghĩa bởi ISO 19444-1. TPdf.ExportXFDF tuần tự hóa mọi giá trị trường biểu mẫu và mọi chú thích được hỗ trợ trong tài liệu đã tải sang một file XFDF hoặc TStream; TPdf.ImportXFDF đọc ngược tài liệu XFDF lại, tạo lại các chú thích trên các trang của chúng và áp dụng các giá trị trường. Quy trình round-trip duy nhất đó bao quát hai quy trình công việc mà mọi ứng dụng tài liệu cuối cùng đều cần: một người đánh giá đánh dấu một file PDF trong Acrobat và gửi cho bạn các bình luận để gộp, hoặc một quy tắc tuân thủ yêu cầu dữ liệu biểu mẫu phải nằm trong một file riêng biệt, có thể so sánh (diff) và kiểm toán được thay vì được nhúng cứng vào chính file PDF

PDFium có hỗ trợ xuất và nhập XFDF không?

Bản thân thư viện PDFium thì không. API C gốc hoàn toàn không có hàm XFDF hoặc FDF nào — không có gì trong các file header công khai của nó đọc hoặc ghi một trong hai định dạng, do đó không có mức độ wrap nào giúp bạn đạt được điều đó. Do đó, PDFium Component triển khai toàn bộ engine XFDF trong Pascal, trong một unit chuyên dụng xây dựng và phân tích cú pháp XML trực tiếp trên mô hình dữ liệu chú thích và trường biểu mẫu TPdf hiện có. Trình viết phát ra XML thủ công và trình đọc là một trình phân tích cú pháp máy trạng thái viết tay, vì vậy tính năng này không thêm bất kỳ phụ thuộc nào vào Delphi XMLDoc hoặc các unit FPC DOM và hoạt động giống hệt nhau trên Delphi và Lazarus

Lựa chọn thiết kế đó rất quan trọng khi bạn đánh giá các giải pháp thay thế. Nếu bạn đang gọi các hàm FPDF_* thô từ Pascal, XFDF là một bức tường: khả năng này đơn giản là không có trong file DLL. Component vượt qua bức tường đó bằng cách coi XFDF như một bài toán tuần tự hóa thuần túy — thu thập các giá trị trường và bản ghi chú thích mà wrapper đã biết cách đọc, ghi chúng ra dưới dạng XML tiêu chuẩn, và đảo ngược quy trình khi nhập

File XFDF chứa những gì?

ISO 19444-1 định nghĩa XFDF là biểu diễn XML của FDF, và dữ liệu của nó được chia thành hai khối cấp cao nhất. Phần tử <fields> mang các tên trường biểu mẫu phân cấp và các giá trị của chúng — mọi thứ người dùng đã nhập, chọn hoặc tích trong một AcroForm. Phần tử <annots> mang các chú thích: TPdf.ExportXFDF phát ra 18 loại phụ — text, highlight, underline, strikeout, squiggly, line, circle, square, caret, polygon, polyline, stamp, ink, freetext, fileattachment, sound, link, và redact. Các chú thích Widget cố tình vắng mặt trong <annots> vì dữ liệu của chúng đi trong <fields>, và các loại phụ nội bộ của trình xem như Popup không nằm trong từ vựng XFDF

Để đảm bảo quá trình round-trip diễn ra chuẩn xác, bản ghi TPdfAnnotation đã được mở rộng với siêu dữ liệu mà XFDF yêu cầu: Name (mã định danh duy nhất NM), Subject, ModificationDate, CreationDate, Icon, Opacity, các điểm đầu cuối của dòng, các đỉnh polygon và polyline, và các đường dẫn vẽ ink. Mỗi trường mới đi đôi với một sentinel boolean Has*, vì vậy code được xây dựng trên các phiên bản trước đó tiếp tục biên dịch và tiếp tục tạo ra các từ điển tương tự — một trường chưa thiết lập sẽ không bao giờ được ghi. Nếu bạn đã tạo các chú thích bằng lập trình, cùng một bản ghi bạn sử dụng cho chú thích đánh dấu văn bản với quad points giờ đây mang theo mọi thứ XFDF cần

Làm thế nào để xuất dữ liệu biểu mẫu và các chú thích sang XFDF?

Một lệnh gọi xử lý toàn bộ tài liệu. Tải PDF với tính năng điền biểu mẫu được kích hoạt, gọi ExportXFDF, và component sẽ duyệt qua từng trang, thu thập các giá trị trường và chú thích, rồi ghi XML. Giá trị trả về là số lượng byte UTF-8 được ghi, đóng vai trò như một kiểm tra độ tin cậy nhanh chóng trong nhật ký

var
  Pdf: TPdf;
  Bytes: Integer;
begin
  Pdf := TPdf.Create(Self);
  Pdf.FormFill := True;            // needed so field values are live
  Pdf.FileName := 'expense-report.pdf';
  Pdf.Active := True;
  Bytes := Pdf.ExportXFDF('expense-report.xfdf');
  ShowMessage(Format('%d bytes of XFDF written', [Bytes]));
end;

Cả hai hướng đều có một nạp chồng TStream, vì vậy không có gì bắt buộc phải sử dụng một file tạm thời trên đĩa. Xuất sang một TMemoryStream là dạng tự nhiên khi XFDF được chuyển hướng đến một phản hồi HTTP, một blob cơ sở dữ liệu, hoặc một mục lưu trữ được ký

var
  Buffer: TMemoryStream;
begin
  Buffer := TMemoryStream.Create;
  try
    Pdf.ExportXFDF(Buffer);        // same XML, no file involved
    Buffer.Position := 0;
    // hand the stream to a web response, blob column, or zip entry
  finally
    Buffer.Free;
  end;
end;

ImportXFDF gộp các bình luận trở lại tài liệu bằng cách nào?

Phía nhập là nơi quy trình công việc của người đánh giá kết thúc. Một đồng nghiệp chú thích hợp đồng trong Acrobat, xuất các bình luận dưới dạng XFDF, và gửi cho bạn vài kilobyte XML thay vì bản sao thứ hai của file PDF. TPdf.ImportXFDF phân tích cú pháp file đó, tạo từng chú thích trên trang mà thuộc tính page của nó chỉ định, và ghi các giá trị trường vào các chú thích widget khớp tương ứng. Hàm trả về tổng số trường cộng với chú thích được áp dụng, nhờ đó UI có thể xác nhận chính xác lượng dữ liệu đã đến

var
  Applied: Integer;
begin
  Applied := Pdf.ImportXFDF('review-comments.xfdf');
  StatusBar.SimpleText :=
    Format('%d fields and annotations merged', [Applied]);
end;

Trình phân tích cú pháp là một trình quét thẻ viết tay được xây dựng để có độ dung sai cao thay vì quá nghiêm ngặt: các phần tử không xác định sẽ bị bỏ qua, các lệnh xử lý, khai báo DOCTYPE và bình luận bị bỏ qua, các tên cờ không được nhận dạng bị loại bỏ, và các giá trị thuộc tính được giải mã (unescaped) qua bộ đầy đủ các thực thể XML và tham chiếu ký tự số, bao gồm cả các cặp surrogate UTF-16. XFDF được tạo ra bởi Acrobat, bởi một trình xem web, hoặc bởi một công cụ khác đều có thể được gộp lại mà không gặp trở ngại nào. Khi các chú thích đã nằm trong tài liệu, chu kỳ đánh giá tiếp tục với cơ chế trả lời và trạng thái được mô tả trong xây dựng quy trình đánh giá chú thích với PDFium Component, và các giá trị trường được nhập tham gia vào logic thứ tự tab thông thường được đề cập trong điều hướng trường biểu mẫu

Tại sao các ký tự phân tách thập phân lại quan trọng đối với XFDF?

Mọi chú thích trong XFDF được định vị bởi các chuỗi tọa độ — rect="70.5,540,200,560" và tương tự — và ISO 19444-1 yêu cầu dấu chấm làm ký tự phân tách thập phân. Định dạng float mặc định của Delphi tuân theo locale của Windows, vì vậy trên một hệ thống tiếng Đức hoặc tiếng Pháp, một hàm FloatToStr ngây thơ sẽ biến 70.5 thành 70,5, làm hỏng danh sách tọa độ phân tách bằng dấu phẩy thành dữ liệu rác mà các bộ xử lý khác từ chối hoặc đọc sai. PDFium Component chuẩn hóa mọi số mà nó ghi: ký tự phân tách thập phân của locale được viết lại thành dấu chấm và các số không ở cuối được cắt bỏ, do đó file được xuất ra giống hệt từng byte cho dù nó được tạo ra trên máy en-US hay de-DE. Nếu bạn đã từng debug một công cụ PDF hoạt động tốt tại văn phòng nhưng lại thất bại tại trang khách hàng Châu Âu, bạn đã gặp chính xác nhóm lỗi này — thật tốt khi biết component tự động xử lý nó cho bạn

Các giới hạn của quy trình round-trip là gì?

Có hai giới hạn đáng được nêu rõ ràng. Thứ nhất, XFDF vận chuyển dữ liệu chú thích chứ không phải diện mạo dựng hình — các stream diện mạo (appearance streams) không phải là một phần của định dạng, vì vậy trình xem tiếp nhận sẽ tạo lại diện mạo của từng chú thích từ các thuộc tính của nó. Một highlight hoặc square sẽ dựng hình chính xác ở mọi nơi; một stamp với diện mạo tùy chỉnh sẽ quay về những gì trình xem đích vẽ cho tên stamp đó. Thứ hai, API PDFium cơ bản có các setter cho chuỗi nhưng không có setter cho số lượng chú thích hoặc hình học đường dẫn, vì vậy Opacity, các điểm đầu cuối dòng, các đỉnh và các nét vẽ ink chỉ hỗ trợ xuất: component bảo toàn chúng một cách trung thực khi ghi XFDF, nhưng không thể ghi ngược chúng vào một file PDF khi nhập. Các giá trị trường, nội dung, màu sắc, rect, quad points, ngày tháng và siêu dữ liệu danh tính đều round-trip hoàn hảo theo cả hai hướng

Để bắt đầu thực hành, mẫu XfdfLab đi kèm trong bộ demo dành cho Delphi, C++Builder và Lazarus, liên kết cả hai lệnh gọi với các nút bấm trên bất kỳ file PDF nào bạn mở. Hỗ trợ XFDF được bao gồm trong phiên bản hiện tại của PDFium Component, cùng với các API chú thích và biểu mẫu mà nó xây dựng trên đó