PDF/A và PDF/UA trả lời hai câu hỏi chẳng liên quan gì tới nhau, và coi chúng như một ô tích duy nhất gộp chung khả năng tiếp cận với lưu trữ chính là cách những tệp hỏng lọt vào kho lưu trữ với nhãn tuân thủ dán bên ngoài. PDF/A hỏi liệu hai mươi năm nữa tệp có còn hiển thị trung thực không. PDF/UA hỏi liệu công nghệ hỗ trợ hôm nay có đọc được nó không. Một tài liệu có thể qua chuẩn này ngon lành mà trượt chuẩn kia, nên phán quyết trung thực duy nhất đến từ việc chạy cả hai, và chạy trước khi tệp được ghi ra chứ không phải sau khi một hệ thống hạ nguồn đã tin vào định danh tuân thủ nướng sẵn trong metadata của nó. Định danh ấy là một lời tự khai. Không có gì trong định dạng bắt nó phải đúng, và một ứng dụng ghi "PDF/A-1b" vào XMP mà không thẩm định theo tiêu chuẩn sẽ tạo ra một tệp trông có vẻ tuân thủ với mọi bên tiêu thụ chỉ đọc mỗi cái nhãn. losLab PDF Library (PDF Library for Delphi) khép khoảng trống đó cho Delphi và C++Builder bằng cách dựng cả hai bộ thẩm định vào thẳng thư viện, nhờ vậy phép kiểm chạy ngay trong tiến trình mà không phải dựng dịch vụ ngoài nào
Hai tiêu chuẩn đánh trượt tệp vì những lý do trái ngược
ISO 19005 (PDF/A) là một khế ước về tái hiện. Một tệp tuân thủ phải hiển thị y hệt sau nhiều thập niên trên phần mềm chưa từng thấy hệ thống đã tạo ra nó, nên các quy tắc nhắm vào những phụ thuộc bên ngoài: mọi font đều nhúng, màu neo vào một ICC OutputIntent nhúng sẵn hoặc biểu diễn trong không gian không phụ thuộc thiết bị, không mã hóa trong PDF/A-1, không JavaScript, metadata XMP phải nhất quán với document information dictionary. ISO 14289 (PDF/UA) thì lại là khế ước về ngữ nghĩa. Công nghệ hỗ trợ phải duyệt được tài liệu và rút ra ý nghĩa, thứ nằm ở một tầng hoàn toàn khác: một cây cấu trúc đầy đủ, văn bản thay thế cho hình, tiêu đề tài liệu được đặt để hiển thị, các cấp đề mục không nhảy cóc, quan hệ tiêu đề bảng còn sống sót khi trang đã rời khỏi màn hình
Vì hai tiêu chuẩn canh gác hai tầng khác nhau, những tệp cắn bạn chính là những tệp nằm giữa chúng. Một tài liệu hoàn hảo cho lưu trữ có thể câm lặng trước trình đọc màn hình. Một tài liệu gắn thẻ đẹp đẽ có thể tham chiếu tới một font máy để bàn sẽ không tồn tại sau mười năm. Xuất bản trong khu vực công là nơi thường thấy cả hai yêu cầu ập tới cùng lúc, và pipeline ở đó không thể gộp chúng vào một cửa kiểm duy nhất. Các phát hiện đi tới những người khác nhau. Font không nhúng là khiếm khuyết trong mã sinh ra tệp PDF, còn thiếu văn bản thay thế thuộc về người sở hữu các mẫu nội dung, và một báo cáo trộn lẫn hai thứ thì chỉ tổ bị chuyển tiếp thêm lần nữa
Bạn nhắm tới phần nào của PDF/A cũng quan trọng ngang việc bạn có đạt được nó hay không. PDF/A-1 đóng băng ở PDF 1.4 và từ chối độ trong suốt lẫn JPEG2000, hai thứ mà đầu ra báo cáo hiện đại với tay tới mà chẳng nghĩ ngợi gì. PDF/A-2 (ISO 19005-2, dựng trên ISO 32000-1) chấp nhận cả hai và là mặc định tỉnh táo cho một kho lưu trữ mới. PDF/A-3 đi xa hơn và cho phép tệp nhúng thuộc bất kỳ kiểu nào, và đó là chỗ dựa của các định dạng hóa đơn điện tử chịu quản lý. Một đội tới năm 2026 vẫn chuẩn hóa trên PDF/A-1b thường là đang mang theo một yêu cầu ai đó viết ra mười lăm năm trước, và thương lượng lại phần tiêu chuẩn cần nhắm tới thường rẻ hơn việc bóc độ trong suốt ra khỏi từng biểu đồ mà hệ thống phát ra
Phát hiện có cấu trúc ngay lúc tiếp nhận
Điểm vào ở API phẳng là CheckFileCompliance, với bộ chọn phép kiểm 1 cho PDF/A và 2 cho PDF/UA. Nó trả về một handle string-list mà mỗi mục là một phát hiện riêng, mỗi dòng một cái, đúng hình hài mà một cửa kiểm tự động muốn duyệt qua:
function GateArchiveUpload(Pdf: TPDFlib; const FileName: string): Boolean;
var
ListId, I: Integer;
begin
ListId := Pdf.CheckFileCompliance(FileName, '', 1, 0); // 1 = PDF/A
if ListId = 0 then
begin
// 0 nghĩa là "không có phát hiện" HOẶC "không đọc được tệp" -- phân định trước khi cho qua
Result := Pdf.LastErrorCode = 0;
Exit;
end;
for I := 0 to Pdf.GetStringListCount(ListId) - 1 do
LogFinding(FileName, Pdf.GetStringListItem(ListId, I));
Pdf.ReleaseStringList(ListId);
Result := False;
end;
Hai chi tiết quyết định liệu đoạn này có chạy được không người trông. Chi tiết thứ nhất là một giá trị trả về mang hai nghĩa trái ngược. CheckFileCompliance trả về 0 khi tệp tuân thủ hoàn toàn, và cũng trả về 0 khi hoàn toàn không mở được tệp, vì bên trong thì một danh sách kết quả rỗng đều thu về 0 trong cả hai trường hợp. Một cửa kiểm đọc số 0 là đạt sẽ phất tay cho các tệp tải lên hỏng đi thẳng vào kho lưu trữ, nên hãy phân định bằng LastErrorCode trước khi tin con số không đó, đúng như cửa kiểm ở trên làm. Chi tiết thứ hai liên quan tới việc tệp đang ở giai đoạn nào trong vòng đời của nó. Bộ kiểm tra chạy trên bộ đọc streaming của thư viện thay vì mô hình tài liệu đầy đủ, mở tệp trực tiếp với chế độ chia sẻ đọc và không bao giờ gọi LoadFromFile, và đó là lý do nó nhai được đầu vào nhiều gigabyte mà không dựng cây đối tượng. Chính lượt mở streaming ấy lại hỏng khi một tiến trình khác còn giữ tệp để ghi, và một lượt tải lên đang dở dang chính là trạng thái đó. Hãy đặt cửa kiểm sau khi quá trình truyền kết thúc
Thiết kế streaming lại sinh lời một lần nữa khi tải nặng. Mỗi phép kiểm mở đầu vào ở chế độ chỉ đọc và chia sẻ cho việc đọc, nên một cuộc kiểm toán cả kho tài liệu mở rộng được ra nhiều luồng hay nhiều tiến trình với một instance TPDFlib cho mỗi worker mà không tranh chấp nhau. Tài nguyên cần kỷ luật chính là cái handle. Mọi kết quả khác 0 từ CheckFileCompliance vẫn được cấp phát cho tới khi bạn gọi ReleaseStringList, và một cửa kiểm chạy dài mà quên giải phóng chúng thì không sập, nó chỉ rỉ máu bộ nhớ từ từ cho tới khi ai đó đi tìm nguyên nhân
Báo cáo cho người, bản khác biệt cho cửa kiểm build
Một danh sách phát hiện là hình hài đúng cho cửa kiểm và là hình hài sai cho một email gửi đội làm mẫu nội dung. CreatePreflightReport kết xuất cùng phân tích đó thành văn xuôi dễ đọc, CreatePreflightReportEx thêm bộ chọn định dạng báo cáo, còn SavePreflightReport ghi nó xuống đĩa để báo cáo có thể đi kèm trong gói tài liệu bàn giao. Khối hợp đồng lưu trữ coi bản báo cáo đó là một hạng mục bàn giao đúng nghĩa, chứ không chỉ là tạo tác nội bộ
Thành viên lặng lẽ xứng đáng chỗ đứng nhất trong họ này là ComparePreflightReports. Tuân thủ là một bề mặt hồi quy như mọi mảng hành vi khác. Một chỉnh sửa nhỏ trong mẫu, một font doanh nghiệp vừa mua bản quyền, hay một lượt nâng cấp thư viện đều có thể mang tới một phát hiện chưa từng có ở bản phát hành trước, và chẳng cái nào tự lên tiếng. Hãy giữ các báo cáo chuẩn cho một tập tài liệu đại diện dưới quyền kiểm soát phiên bản, tạo lại chúng sau mỗi thay đổi, rồi chạy ComparePreflightReports để tính phần chênh lệch. Một bản khác biệt rỗng là tạo tác phát hành đáng giữ. Một phát hiện bất ngờ làm hỏng bản build, và đó là chỗ phát hiện ra nó rẻ hơn nhiều so với lúc kiểm toán
Tạo ra đầu ra đạt chuẩn ngay lượt đầu
Preflight kiếm cơm trên những tệp đến từ nơi khác. Với tài liệu do chính mã của bạn tạo ra, tìm vi phạm sau khi đã sinh xong rồi vá ngược lại là con đường vòng chậm chạp. PDF Library for Delphi mang sẵn một chế độ ở phía sinh tệp cho mỗi tiêu chuẩn, và bạn có thể bật cả hai cho cùng một tài liệu:
var
Pdf: TPDFlib;
Diag: WideString;
begin
Pdf := TPDFlib.Create;
try
Pdf.NewDocument;
Pdf.SetPDFAMode(1);
Pdf.LoadOutputIntentProfile('sRGB-IEC61966-2.1.icc', 'RGB');
Pdf.SetPDFUAMode('en-US');
Pdf.SetInformation(1, 'Quarterly Statement'); // /Title: bắt buộc với PDF/UA
// ... vẽ nội dung đã gắn thẻ ở đây ...
Diag := Pdf.GetPDFUADiagnostics;
if Diag <> '' then
Writeln('fix before shipping: ', Diag);
Pdf.SaveToFile('statement.pdf');
// lượt preflight có giá trị là lượt chạy trên tệp đã lưu:
Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));
finally
Pdf.Free;
end;
end;
Cái bẫy nấp ở thời điểm lưu. Vài sửa chữa tuân thủ diễn ra trong lúc tài liệu được tuần tự hóa chứ không phải lúc bạn bật chế độ: ép cờ in trên annotation, ghi giá trị AFRelationship mặc định cho tệp nhúng của PDF/A-3, chuẩn hóa thứ tự tab và phần mô tả trường form cho PDF/UA. Tài liệu nằm trong bộ nhớ không giống hệt từng byte với tài liệu đáp xuống đĩa, nên phán quyết preflight duy nhất có ý nghĩa là phán quyết tính từ tệp đã lưu. Hãy thẩm định chính statement.pdf. Đừng suy ra mức tuân thủ từ đối tượng còn trong bộ nhớ, bởi tập byte bạn đem ra phán xét không phải tập byte bạn đã phát hành
Các kịch bản hóa đơn mang theo XML máy đọc được bên cạnh tài liệu trực quan đi theo mẫu hình ZUGFeRD và Factur-X, vốn dựng trên PDF/A-3. Những trường hợp đó nên đặt quan hệ của tệp đính kèm một cách tường minh bằng SetPDFA3DefaultAFRelationship, vì ISO 19005-3 đòi mọi tệp nhúng phải khai báo vai trò của nó so với tài liệu. Bỏ trống thì phần XML nhúng chỉ là một khối dữ liệu không nêu mục đích, và bộ thẩm định sẽ để ý thấy
Trọng tài độc lập: veraPDF và Acrobat
Bên sản xuất không nên là quan tòa duy nhất phán xử đầu ra của chính mình. Các bộ kiểm tra của PDF Library for Delphi cho bạn phán quyết nhanh, có cấu trúc, ngay trong tiến trình, đúng thứ bạn cần trên đường chạy nóng, nhưng cửa kiểm phát hành cho một lô lưu trữ vẫn nên đẩy đầu ra qua một bộ thẩm định không ai trong đội bạn viết ra. veraPDF là bản cài tham chiếu do cộng đồng duy trì cho PDF/A và là công cụ mà phần lớn kho lưu trữ nêu tên trong tiêu chí nghiệm thu, nên đó là bộ cần khớp. Các hồ sơ preflight của Acrobat làm trọng tài phá thế hòa hữu ích khi veraPDF và phép kiểm trong tiến trình bất đồng. Hãy ghi tên bộ thẩm định cùng phiên bản của nó bên cạnh mọi báo cáo lưu lại. Một lời khẳng định rằng tệp đã qua veraPDF nói lên rất ít nếu thiếu số hiệu bản build đã cho nó qua, vì công cụ này siết chặt luật lệ theo từng bản phát hành
Các bộ thẩm định quả thật có bất đồng ở rìa của các tiêu chuẩn, và khi đó câu trả lời không phải là chọn công cụ mình ưa. Hãy thu tệp lại thành một mẫu tối giản nhưng vẫn kích hoạt được bất đồng đó rồi đọc nó đối chiếu với văn bản tiêu chuẩn. Một giờ làm vậy thường phơi ra một trong hai điều: một lỗi công cụ thật sự đáng báo lên thượng nguồn, hoặc một điều khoản mà đội bạn lâu nay đọc sai và nên viết lại vào ghi chú tuân thủ để người sau khỏi mở lại vụ kiện
Đầu vào đã mã hóa có lối tắt. Cả hai bộ kiểm tra đều nhận đối số mật khẩu, nhưng một tệp PDF/A-1 có encryption dictionary thì đã không tuân thủ sẵn rồi, vì ISO 19005-1 cấm mã hóa hoàn toàn, nên một hồ sơ nộp đã mã hóa có thể bị trả lại trước khi bất kỳ phân tích sâu nào kịp chạy. Việc tìm hiểu một encryption dictionary thực sự cho phép những gì lại là công việc riêng, được bàn trong kiểm toán mã hóa và quyền hạn PDF
Các phát hiện PDF/UA gần như luôn truy ngược về cách cây cấu trúc được soạn ra ngay từ đầu, và những kỹ thuật gắn thẻ đằng sau chuyện đó nằm trong dựng cây cấu trúc tagged PDF trong Delphi. Những kho lưu trữ còn đòi hỏi chữ ký số nên ghép cửa kiểm này với quy trình trong ký và thẩm định PAdES. Tài liệu API preflight đầy đủ nằm trên trang sản phẩm losLab PDF Library for Delphi