Một công cụ preflight hàng loạt là một chương trình console không có cửa sổ, được trỏ vào một thư mục PDF, xác nhận từng tệp theo các tiêu chuẩn tuân thủ bạn chỉ định và để lại bằng chứng đọc được bằng máy về những gì nó tìm thấy. Không ai ngồi xem nó. Nó chạy vào lúc 2 giờ sáng theo cron hoặc Windows Task Scheduler, hoặc như một gate trong CI pipeline, và người tiếp theo quan tâm đến đầu ra của nó là một scheduler đọc exit code hoặc một kiểm toán viên mở báo cáo nhiều tuần sau. Điều đó thay đổi ý nghĩa của "đúng". Engine preflight của PDFium Component, một thư viện PDF mã nguồn cho Delphi, C++Builder và Lazarus, làm cho các lời gọi xác nhận bản thân gần như tầm thường. Công việc quyết định liệu công cụ có xứng đáng không nằm xung quanh những lời gọi đó: profile nào bạn đã kiểm tra, exit code đã nói gì với scheduler, và liệu báo cáo mà lẽ ra đã phát hiện lỗi còn tồn tại khi ai đó đi tìm nó
Hợp đồng: những gì scheduler thực sự có thể thấy
Một CI runner hoặc Windows Task Scheduler chỉ thấy hai thứ từ công cụ của bạn: exit code và bất kỳ tệp nào nó để lại. Dòng log, màu sắc console, đầu ra tiến trình: tất cả những thứ đó dành cho con người xem trực tiếp, và lúc 2 giờ sáng không ai đang xem. Vì vậy hãy cố định từ vựng exit code trước khi bạn chạm vào API, và giữ nó nhàm chán:
0: mọi tệp tuân thủ mọi profile được yêu cầu1: ít nhất một tệp tạo ra phát hiện xác nhận2: công cụ bản thân thất bại trên ít nhất một tệp (đầu vào bị hỏng, khóa, sự cố)
Sự phân biệt giữa code 1 và 2 là thứ các nhóm bỏ qua và sau này hối hận. Một PDF bị hỏng không thể mở không phải là lỗi xác nhận. Gộp nó vào code 1 và một xe tải đầy ắp bản quét hỏng hóc xuất hiện trong dashboard của bạn như một sự sụp đổ tuân thủ đột ngột, khiến ai đó đuổi theo một hồi quy tiêu chuẩn chưa từng xảy ra, trong khi câu chuyện thực là máy quét hỏng ở thượng nguồn
Hai mục nữa thuộc về hợp đồng. Thứ nhất là timeout mỗi tệp. Một PDF bệnh lý, hàng nghìn trang với cấu trúc đối tượng lồng nhau sâu, có thể giữ một lần validation pass trong nhiều phút, và cửa sổ ban đêm không có sự kiên nhẫn cho nó. Kill job của tệp đó tại thời hạn, tính nó là tool failure, và tiếp tục batch. Thứ hai là thư mục kiểm dịch: di chuyển mọi đầu vào timeout hoặc không mở được sang đó thay vì để tại chỗ. Qua vài tháng thư mục đó âm thầm tích lũy các tài liệu tệ nhất mà khách hàng thực sự của bạn gửi, và corpus đó có giá trị hơn bất kỳ mẫu tổng hợp nào bạn có thể tự viết
Chọn tiêu chuẩn, và tại sao cấp độ tuân thủ quan trọng
Enum TPdfPreflightStandard bao gồm các họ tiêu chuẩn xuất hiện trong thực tế: ppsPdfA cho tuân thủ lưu trữ ISO 19005, ppsPdfUa cho khả năng tiếp cận ISO 14289, ppsPdfX cho trao đổi in ấn, cùng ppsPdfE, ppsPdfR và ppsPdfVT cho công việc kỹ thuật, raster và dữ liệu biến đổi. Trong một họ, engine đọc cấp độ tuân thủ mà tài liệu tuyên bố và báo cáo nó mỗi tiêu chuẩn trong ConformanceName của kết quả. Chỉ đặt tên họ hiếm khi đủ, vì cấp độ mới là nơi sự khác biệt thực sự tồn tại. PDF/A-2b hứa hẹn tái tạo hình ảnh và không hơn. PDF/A-3a thêm yêu cầu gắn thẻ cấu trúc logic và cho phép nhúng tệp nguồn, đó là một tiêu chuẩn khó hơn nhiều để đạt được đối với tài liệu quét không có cây thẻ nào cả. Nhầm điều này theo cả hai hướng và batch nói dối bạn. Nếu chính sách lưu giữ của bạn thực sự muốn PDF/A-2b nhưng bạn không pass các tệp vì thiếu thẻ cấu trúc, báo cáo đầy phát hiện mà không ai sẽ sửa. Chấp nhận bất kỳ nhãn PDF/A nào mà không kiểm tra cấp độ và bạn ký duyệt các tài liệu đáp ứng tiêu chuẩn yếu hơn những gì bạn hứa. Các quy định về khả năng tiếp cận từ người mua chính phủ ngày càng xếp PDF/UA lên trên tất cả điều này, điều không thêm chi phí cho lần chạy vì BuildPdfPreflightReport (từ unit FPdfPreflightReport) nhận một tập hợp tiêu chuẩn:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Một lời gọi đánh giá cả hai tiêu chuẩn và trả về một bản ghi báo cáo hợp nhất duy nhất
Tại sao danh sách phát hiện rỗng không phải là pass
Báo cáo liệt kê các phát hiện theo từng tiêu chuẩn, và danh sách vấn đề rỗng chỉ có nghĩa là "không tìm thấy vấn đề trong các tiêu chuẩn thực sự đã chạy." Đó là tuyên bố hẹp hơn "tệp tuân thủ tiêu chuẩn bạn quan tâm," và khoảng cách giữa hai điều đó là nơi preflight hàng loạt âm thầm thối rữa. Một lỗi cấu hình bỏ ppsPdfA khỏi tập hợp tạo ra chính xác cùng một danh sách vấn đề rỗng như một tệp thực sự sạch. Vì vậy hãy coi sự im lặng là đáng ngờ. Duyệt Report.Results và khẳng định hai điều cho mọi tiêu chuẩn bạn định kiểm tra: rằng một mục nhập kết quả cho nó tồn tại, và rằng cờ IsCompliant của nó, được hỗ trợ bởi Status = pfsPass, là true. Một công việc hàng đêm coi "không có phát hiện" là "sẵn sàng lưu trữ" mà không bao giờ xác nhận tiêu chuẩn nào đã được đánh giá là cách cổ điển để một thư mục các tệp không tuân thủ trôi qua trong nhiều tháng, cho đến khi một kiểm toán viên bên ngoài mở một tệp bằng veraPDF và toàn bộ lưu trữ bị nghi ngờ
Một cạm bẫy thứ hai ẩn trong việc một phát hiện là gì. Mỗi TPdfPreflightIssue mang một Code, một Category, một Description, và một Recommendation, và nó đặt tên quy tắc bị vi phạm, không phải một trang hay một đối tượng. Đó là một lựa chọn thiết kế có hệ quả cho vòng phản hồi. Báo cáo cho nhóm sản xuất biết loại khiếm khuyết nào tồn tại, một font chưa nhúng hoặc một định danh XMP bị thiếu, và việc tìm đối tượng vi phạm cụ thể là công việc của công cụ khắc phục ở hạ nguồn, không phải của validator. Xây dựng consumers báo cáo của bạn dựa trên các giá trị Code ổn định, không bao giờ dựa trên văn bản mô tả đọc được bằng người, có thể được viết lại giữa các phiên bản mà không cảnh báo
Tệp báo cáo cho máy và cho người trực
Bản ghi báo cáo viết cùng các phát hiện theo năm định dạng: SaveJsonToFile, SaveCsvToFile, SaveHtmlToFile, SaveTextToFile, và SaveMarkdownToFile, mỗi cái có hàm kiểu ToJson tương ứng khi bạn muốn chuỗi trong bộ nhớ thay vì trên đĩa. Hãy cưỡng lại sự thôi thúc chọn một. Viết JSON cho pipeline, để CI có thể đính kèm nó vào bản ghi công việc và phân tích mã vấn đề và trạng thái mỗi tiêu chuẩn mà không cần scrape văn bản. Viết HTML cho người được gọi đến, vì nó mở trong bất kỳ trình duyệt nào mà không cần tooling. Cả hai cùng nhau tốn một dòng thêm mỗi tệp và tránh cho kỹ sư trực của bạn nhiệm vụ tệ nhất trong xử lý hàng loạt, đó là giải mã ngược một blob JSON thô lúc 2 giờ sáng để tìm hiểu tệp nào bị lỗi. Một kỷ luật quan trọng hơn lựa chọn định dạng: lấy tên mỗi báo cáo từ tên tệp đầu vào, không bao giờ từ timestamp, hoặc hai lần chạy song song sẽ xen kẽ báo cáo mà bạn không thể khớp lại với đầu vào của chúng
Ngưỡng mức độ nghiêm trọng thuộc về cấu hình thay vì code. Một chú thích không có mô tả thay thế là lỗi cứng cho cổng nộp PDF/UA và là ghi chú có thể bỏ qua cho kho lưu trữ nội bộ, tuy nhiên nó là cùng một phát hiện trong cả hai. Expose một mức độ fail-on mỗi profile để chính sách có thể thay đổi mà không cần biên dịch lại, và đóng dấu mức độ đang có hiệu lực vào tóm tắt công việc bản thân. Quý sau không ai nhớ ngưỡng nào mà batch tháng 10 năm ngoái đã chạy, và tóm tắt là nơi duy nhất bộ nhớ đó tồn tại
Cô lập các tệp để một PDF xấu không thể làm chìm cả batch
procedure RunPreflightBatch(const InputDir, ReportDir: string;
out FilesWithFindings, ToolFailures: Integer);
var
SR: TSearchRec;
Pdf: TPdf;
Report: TPdfPreflightReport;
begin
FilesWithFindings := 0;
ToolFailures := 0;
if FindFirst(InputDir + '*.pdf', faAnyFile, SR) = 0 then
try
repeat
Pdf := TPdf.Create(nil); // fresh instance per file: no state bleed
try
try
Pdf.FileName := InputDir + SR.Name;
Pdf.Active := True;
if not Pdf.Active then // load failures are silent, not raised
raise EPdfError.Create('Cannot open ' + SR.Name);
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Report.SaveJsonToFile(ReportDir + ChangeFileExt(SR.Name, '.json'));
Report.SaveHtmlToFile(ReportDir + ChangeFileExt(SR.Name, '.html'));
if Report.TotalIssueCount > 0 then
Inc(FilesWithFindings);
except
on E: Exception do
begin
Inc(ToolFailures); // exit-code-2 territory, not a validation verdict
WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
Ba lựa chọn có chủ đích tồn tại trong vòng lặp đó. Một TPdf mới cho mỗi tệp đảm bảo rằng một tài liệu làm hỏng trạng thái engine không thể làm nhiễm độc các tệp theo sau nó. Kiểm tra Active tường minh xứng đáng có vị trí vì Active := True nuốt các lỗi tải thay vì raise chúng; bỏ guard và một tệp bị cắt ngắn trôi vào lời gọi xác nhận trước khi thất bại ở đâu đó ở hạ nguồn với thông điệp gây hiểu nhầm. try..except bên trong tồn tại bên trong phạm vi mỗi tệp một cách có mục đích, vì vậy một exception duy nhất tăng counter failure và vòng lặp tiếp tục. Bạn muốn các báo cáo sạch sẽ cho 4.999 tệp tốt ngay cả khi tệp thứ 5.000 bị hỏng. Và cả hai định dạng báo cáo được ghi ra đĩa trước khi phán quyết được tính, có nghĩa là bằng chứng tồn tại ngay cả khi một lỗi sau đó trong logic tóm tắt đếm sai
Ánh xạ exit code sau đó thu gọn thành một vài dòng trong file project:
begin
RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
if Failures > 0 then
Halt(2)
else if Findings > 0 then
Halt(1);
// falling through exits with 0: every file conformed
end.
Những gì preflight sẽ không làm cho bạn
Engine phát hiện; nó không sửa chữa. Một phát hiện về font chưa nhúng hoặc không gian màu phụ thuộc thiết bị là một lệnh công việc cho bất kỳ ai tạo ra các tệp, và validator không có cách nào vá nó tại chỗ. Vì vậy hãy lập kế hoạch vòng phản hồi một cách cố ý. Báo cáo phải đến nơi nhóm sản xuất thực sự đọc chúng, hoặc cùng một phát hiện sẽ xuất hiện lại mỗi đêm cho đến khi ai đó cuối cùng hỏi tại sao tỷ lệ tuân thủ không bao giờ cải thiện. Cũng có lợi khi kiểm tra chéo một mẫu phán quyết với một validator độc lập, veraPDF cho PDF/A hoặc preflight của Acrobat cho PDF/X, trước khi một kiểm toán viên bên ngoài kiểm tra chéo cho bạn. Khi hai engine không đồng ý về một tệp khách hàng thực, tài liệu đó không phải là phiền toái; nó chính xác là trường hợp hồi quy mà kiểm tra phát hành của bạn đang thiếu. Giữ nó, đặt tên nó, và chạy nó trên mỗi bản build
Thêm một cặp đáng biết. Cùng engine xác nhận điều khiển các kiểm tra tương tác trong UI đánh giá, vì vậy CLI headless này và bàn làm việc đánh giá nhập PDF dành cho nhà phân tích có thể chia sẻ một từ vựng xác nhận duy nhất thay vì dần dần phân kỳ theo thời gian. Và vì [ppsPdfA, ppsPdfUa] đánh giá khả năng tiếp cận trong cùng một pass, phía PDF/UA của batch phù hợp sạch sẽ với công việc phía trình xem như xây dựng trình đọc PDF có khả năng tiếp cận trong Delphi. Profiles, định dạng báo cáo và API preflight đầy đủ được ghi chép trên trang sản phẩm của PDFium Component