PDF Library for Delphi gộp hai tài liệu AcroForm với một chính sách tường minh cho các trường có cùng tên. MergeDocumentEx nhận định danh tài liệu nguồn và một trong ba chiến lược: dfsReject từ chối việc gộp, dfsMerge giữ tên chung và đồng bộ giá trị, còn dfsAutoNumber đổi tên các trường đến một cách có thể dự đoán được. Việc quét tên diễn ra trước khi bất kỳ số đối tượng nào dịch chuyển, nên một lần gộp bị từ chối để lại cả hai tài liệu vẫn sử dụng được đầy đủ
Bất kỳ ai từng lắp ráp một bộ hồ sơ ứng dụng PDF đều đã gặp phải điều này. Ba form, mỗi form có một trường tên là Signature hoặc Date hoặc Total, được gộp vào một file. Trong một AcroForm, tên trường đầy đủ định danh chính là danh tính của trường, nên hai trường cùng tên hoàn toàn không phải hai trường: điền vào một trường sẽ điền luôn trường kia, và một chữ ký áp dụng lên một trường sẽ bao phủ một phạm vi mà không ai có ý định như vậy
Vì sao xung đột tên được quyết định trước khi gộp?
MergeDocument cũ hơn nối mảng trường gốc AcroForm của hai tài liệu và không cung cấp lựa chọn nào. Tệ hơn, khi kết quả không dùng được, việc phát hiện diễn ra sau khi số đối tượng đã được đánh số lại và cây trang đã được khâu nối, để lại cho bên gọi một tài liệu ở trạng thái mà không bản gốc nào từng có
MergeDocumentEx đảo ngược thứ tự đó. Nó thu thập tên các trường cấp cao nhất từ cả hai tài liệu, so sánh chúng, và áp dụng chiến lược trước khi bất cứ thứ gì di chuyển. Do đó một lần từ chối là một thao tác sạch sẽ không làm gì cả: tài liệu đích không bị chạm, tài liệu nguồn không bị chạm, và cả hai vẫn mở và dùng được, điều mà bài kiểm thử gộp xác minh bằng cách đọc lại giá trị một trường từ tài liệu nguồn sau một lần gộp bị từ chối
Phép so sánh dùng một tập tên có thứ tự, phân biệt hoa thường, nên chi phí tỷ lệ với tổng số trường của cả hai nhân với một hệ số logarit thay vì với tích của hai số đếm. Phân biệt hoa thường là lựa chọn đúng ở đây vì tên trường PDF phân biệt hoa thường; gấp chúng lại sẽ gộp những trường mà đặc tả coi là khác biệt
Ba chiến lược, và khi nào mỗi chiến lược là đúng
dfsReject là chiến lược cho các pipeline tự động không được phép tạo ra tài liệu mơ hồ. Lần gộp trả về không và LastErrorCode báo cáo 705, một mã riêng để có thể phân biệt tên trùng lặp với mọi thất bại gộp khác và định tuyến đến một biện pháp khắc phục cụ thể, thường là đổi tên trường ở phía thượng nguồn
dfsMerge cố tình giữ tên chung và đồng bộ giá trị đích cùng giá trị mặc định vào trường nguồn, nên một trình xem tuân thủ chuẩn coi nhiều widget đó như một trường được đặt tên logic duy nhất, đây là hành vi AcroForm chuẩn cho một trường có nhiều chú thích widget. Điều nó không làm là gộp các dictionary trường khác nhau thành một đối tượng duy nhất. Mỗi trường giữ liên kết trang, hình thức hiển thị và hành động riêng của nó, vì gộp chúng lại sẽ âm thầm loại bỏ định dạng và hành vi thuộc về tài liệu đến
dfsAutoNumber đổi tên các trường trùng lặp đến bằng cách thêm hậu tố số bắt đầu từ _2 và lấy số tự do đầu tiên. Kết quả có thể tái lập: nó chỉ phụ thuộc vào những tên hiện có, không bao giờ phụ thuộc vào số đối tượng trường, nên gộp cùng một cặp tài liệu hai lần cho ra cùng tên cả hai lần. Thuộc tính đó quan trọng khi mã ở tầng sau, một lượt nhập FDF hay một ánh xạ cơ sở dữ liệu tham chiếu đến trường theo tên
uses
PDFlibrary;
var
Lib: TPDFlib;
TargetDoc, SourceDoc: Integer;
begin
Lib := TPDFlib.Create;
try
TargetDoc := Lib.SelectedDocument;
Lib.LoadFromFile('application-part1.pdf', '');
SourceDoc := Lib.NewDocument;
Lib.LoadFromFile('application-part2.pdf', '');
Lib.SelectDocument(TargetDoc);
if Lib.MergeDocumentEx(SourceDoc, dfsReject) = 0 then
begin
if Lib.LastErrorCode = 705 then
begin
// Cả hai tài liệu vẫn còn nguyên vẹn - thử lại với một chính sách
Log('duplicate field names; retrying with auto-numbering');
Lib.MergeDocumentEx(SourceDoc, dfsAutoNumber);
end;
end;
Lib.SaveToFile('application-complete.pdf');
finally
Lib.Free;
end;
end;
Hãy chú ý mẫu hai bước trong đoạn mã đó, điều chỉ khả thi vì việc từ chối không phá hủy gì. Thử chính sách nghiêm ngặt trước, kiểm tra lỗi, rồi mới quyết định. Với một lần gộp thất bại giữa chừng, phương án dự phòng sẽ phải bắt đầu lại từ đầu bằng cách tải lại cả hai file
Form đã gộp trông như thế nào sau đó
Dưới dfsMerge, một trường đích tên Shared mang "Target value" và một trường nguồn cùng tên tạo ra hai trường, cả hai đều tên Shared, cả hai đều báo cáo giá trị đích, vì giá trị đích và giá trị mặc định được đồng bộ vào trường đến. Đó chính là ngữ nghĩa dự kiến cho một tên chung: một trường logic, nhiều widget, một giá trị
Dưới dfsAutoNumber, cùng đầu vào tạo ra Shared và Shared_2 là các trường riêng biệt với giá trị độc lập. Hãy chọn giữa hai cách này bằng cách hỏi một câu duy nhất: liệu điền vào một trường có nên điền luôn trường kia hay không? Đối với tên người ký lặp lại trên mỗi phần của một bộ hồ sơ, câu trả lời là có, và dfsMerge là đúng. Đối với một tổng số mang ý nghĩa khác nhau trên mỗi form, câu trả lời là không, và đánh số tự động là đúng
// Sau khi gộp, liệt kê những gì bạn thực sự nhận được
for I := 1 to Lib.FormFieldCount do
Log(Format('%d: %s = %s',
[I, Lib.GetFormFieldTitle(I), Lib.GetFormFieldValue(I)]));
Lưu ý thực tế cho việc lắp ráp bộ hồ sơ form
Một lần gộp thành công tiêu thụ tài liệu nguồn: nó bị loại khỏi danh sách tài liệu của thư viện, đó là lý do DocumentCount giảm từ hai xuống một. Đừng tiếp tục dùng định danh nguồn sau đó. Phiên bản tài liệu được nâng lên giá trị cao hơn trong hai giá trị, nên gộp một form PDF 2.0 vào một tài liệu 1.7 cho ra một file 2.0
Thứ tự quan trọng đối với tên. Gộp A vào B và gộp B vào A cho ra các kết quả đánh số tự động khác nhau, vì tài liệu đóng vai trò gộp giữ nguyên tên của nó không đổi. Khi một bộ hồ sơ có một form chính chuẩn tắc, hãy đặt form đó làm đích
Trường chữ ký đáng được cân nhắc riêng. Một chữ ký được áp dụng trước khi gộp chỉ bao phủ bản sửa đổi mà nó đã ký, nên gộp làm nó vô hiệu theo nghĩa thực tế rằng file đã thay đổi kể từ khi ký. Hãy lắp ráp trước rồi ký tài liệu đã lắp ráp, thay vì gộp các phần đã ký. Khi việc gộp là về nội dung trang thay vì về form, đường dẫn nhanh hơn được mô tả trong gộp PDF nhanh bằng dịch chuyển tham chiếu byte là công cụ tốt hơn
Cuối cùng, hãy lập kế hoạch cho phần dữ liệu của bộ hồ sơ cùng với việc gộp. Nếu giá trị trường đến từ một hệ thống bên ngoài, hãy quyết định liệu hệ thống đó có định vị trường theo tên trước khi chọn đánh số tự động hay không, vì Shared_2 sẽ không khớp với một ánh xạ đang mong đợi Shared. Các định dạng nhập và xuất được đề cập trong trao đổi dữ liệu form FDF, XFDF và XFA, và hành vi kịch bản hóa ở cấp trường có thể cũng bị ảnh hưởng bởi việc đổi tên được đề cập trong hành động form tương tác và JavaScript
Gộp form, trao đổi dữ liệu và ký đều chạy trong cùng một thư viện cho Delphi, C++Builder và Free Pascal; danh sách tính năng đầy đủ có trên trang PDF Library for Delphi