Mọi bộ trích xuất văn bản hình học đều đang đoán. Nó đọc các glyph mà một trang vẽ, sắp chúng theo baseline và vị trí ngang, rồi hy vọng sự sắp xếp thị giác khớp với thứ tự một con người sẽ đọc. Trên một báo cáo một cột, phán đoán đó đúng. Trên một bài báo tạp chí hai cột, một biểu mẫu có thanh bên, hay một bảng mà các ô được phát theo cột, nó sai theo những cách khó nhận ra và đắt đỏ để phát hiện ở hạ nguồn. HotPDF trả lời điều đó bằng ExtractLoadedPageStructureText, hàm phớt lờ hình học hoàn toàn: nó đi cây cấu trúc tài liệu theo thứ tự tác giả như ISO 32000-1 §14.8.4 định nghĩa, rồi ghép lại các glyph của trang theo định danh marked-content của chúng. Với một PDF có tag, đó không phải suy nghiệm, đó là thứ tự ứng dụng tạo ra đã tuyên bố
Hàm trả về False khi trang không có cây cấu trúc dùng được, tín hiệu để quay về bộ trích xuất hình học thay vì thất bại. Thiết kế hai con đường đó quan trọng hơn thuật toán: nơi tiếp nhận tài liệu thật nhìn thấy biểu mẫu chính phủ có tag và đầu ra máy quét trong cùng một thư mục, và một pipeline chỉ xử lý được một trong hai thì không phải là pipeline
Vì sao trích xuất hình học lấy sai thứ tự đọc?
Vì luồng nội dung PDF chẳng mang thứ tự đọc nào cả. Nó là một chuỗi toán tử vẽ, và một trình tạo tự do phát chúng theo bất kỳ thứ tự nào hợp với engine dàn trang của chính nó. Các trình soạn thảo văn bản thường phát theo thứ tự dòng chảy và sắp hình học trông ổn. Các công cụ dàn trang, trình thiết kế biểu mẫu, và bộ sinh báo cáo thì thường không: chân trang có thể được phát trước phần thân, một bảng có thể được điền theo cột trước hàng, và một trang hai cột có thể đan các dòng của cả hai cột vì bộ dàn đã giải quyết chúng cùng lúc
Dạng thất bại rất yên tĩnh. Một bộ trích xuất hình học chưa bao giờ báo lỗi, nó chỉ trả về văn xuôi mà các câu bị nối lệch từ hai cột. Bất cứ gì tiêu thụ văn bản đó — một chỉ mục tìm kiếm, một bộ ánh xạ trường hóa đơn điện tử, một pipeline truy hồi nuôi mô hình ngôn ngữ — thừa hưởng thiệt hại mà không có cảnh báo. HotPDF cũng kèm các bộ trích xuất hình học cho tài liệu đã nạp, và chúng vẫn là công cụ đúng cho các tệp không tag; điểm của con đường theo thứ tự cấu trúc là ngừng đoán khi tài liệu đã mang sẵn câu trả lời
Cây cấu trúc thực sự lưu trữ điều gì
Một PDF có tag giữ một mô tả thứ hai, song song của trang. Catalog trỏ tới một /StructTreeRoot, mà các con /K của nó tạo thành một cây phần tử cấu trúc: /Document, /Sect, /P, /Table, /TR, /TD, v.v. Các lá của cây đó là các tham chiếu marked-content, những số nguyên đặt tên cho một đoạn của luồng nội dung trang. Phía nội dung, các đoạn đó được mở bằng một toán tử BDC mang một /MCID và đóng bằng EMC. Mỗi phần tử cấu trúc còn mang một mục /Pg nêu tên trang mà nó thuộc về, điều khiến việc duyệt theo trang khả thi trong một tài liệu mà cây cấu trúc trải qua hàng trăm trang
HotPDF duyệt cây đó với trần độ sâu 128 tầng và lọc trên /Pg để chỉ trang hiện tại đóng góp. Đầu ra của phép duyệt không phải văn bản, mà là một danh sách có thứ tự các giá trị MCID: thứ tự tác giả của các đoạn marked-content trên trang này. Việc ghép lại văn bản khi đó chỉ là phát lại các glyph theo thứ tự đó
MCID được ghi lại trong lúc trích xuất glyph, chứ không tra cứu sau
Đây là chi tiết hiện thực khiến tính năng này rẻ. HotPDF đã ghi định danh marked-content đang hoạt động trên mọi glyph nó trích xuất, trong trường MCID của THPDFGlyphRecord, vì trình thông dịch luồng nội dung biết phạm vi BDC nào đang mở tại thời điểm nó xử lý mỗi toán tử Tj hay TJ. Trích xuất theo thứ tự cấu trúc vì vậy không cần lượt thứ hai trên luồng nội dung. Nó thu tập hợp thứ tự MCID từ cây cấu trúc, rồi chia các glyph đã trích xuất vào các nhóm theo MCID và phát chúng theo thứ tự đó
var
Pdf: THotPDF;
PageCount, I, Untagged: Integer;
PageText, AllText: UnicodeString;
Report: TStrings; // nơi nhận chẩn đoán thuộc quyền caller
begin
Pdf := THotPDF.Create(nil);
try
PageCount := Pdf.LoadFromFile('accessible-form.pdf');
AllText := '';
for I := 0 to PageCount - 1 do
begin
if Pdf.ExtractLoadedPageStructureText(I, PageText, Untagged) then
begin
// Thứ tự tác giả thẳng từ cây cấu trúc
if Untagged > 0 then
Report.Add(Format('page %d: %d glyphs outside the structure tree',
[I, Untagged]));
end
else
// Không có cây cấu trúc dùng được trên trang này: dự phòng hình học
Pdf.ExtractLoadedPageText(I, PageText);
AllText := AllText + PageText + #13#10;
end;
finally
Pdf.Free;
end;
end;
Glyph không tag được đếm, chưa bao giờ bị bỏ âm thầm
Một trang có thể được tag một phần. Các trình tạo thêm một đường kẻ trang trí, một số trang, hay một watermark muộn ngoài mọi phạm vi BDC, và những glyph đó không thuộc MCID nào. Bỏ chúng đi sẽ là hiện thực gọn gàng và là hiện thực sai, vì cùng khoảng trống đó cũng xuất hiện khi một trình tạo tag phần thân nhưng quên bảng, và bạn sẽ mất bảng mà không hay biết
HotPDF nối các glyph không được nhận về như một đoạn đuôi hình học sau văn bản theo thứ tự cấu trúc và báo số của chúng qua tham số đầu ra UntaggedGlyphCount. Con số đó là tín hiệu chất lượng mà bạn có thể hành động. Vài glyph trên một trang hai nghìn là nội thất trang và có thể bỏ qua. Bốn mươi phần trăm trang nằm ngoài cây cấu trúc nghĩa là việc tag chỉ mang tính trang trí và bộ trích xuất hình học là câu trả lời trung thực hơn cho tệp đó
function ExtractPageBestEffort(Pdf: THotPDF; PageIndex: Integer;
out AText: UnicodeString; out UsedStructure: Boolean): Boolean;
var
Untagged, TotalGlyphs: Integer;
Glyphs: THPDFGlyphArray;
begin
UsedStructure := False;
if Pdf.ExtractLoadedPageStructureText(PageIndex, AText, Untagged) then
begin
TotalGlyphs := 0;
if Pdf.ExtractLoadedPageGlyphs(PageIndex, Glyphs) then
TotalGlyphs := Length(Glyphs);
// Chỉ tin cây cấu trúc khi nó tuyên bố phần lớn trang
if (TotalGlyphs = 0) or (Untagged * 4 <= TotalGlyphs) then
begin
UsedStructure := True;
Result := True;
Exit;
end;
end;
Result := Pdf.ExtractLoadedPageText(PageIndex, AText);
end;
Điều gì khiến hàm trả về False
Ba trường hợp, và chúng đáng phân biệt vì chỉ một trong số đó là khuyết tật của tài liệu. Thứ nhất là một PDF không tag thông thường: không /StructTreeRoot, chẳng có gì để đi, và False đơn giản là sự thật. Thứ hai là một trang quét mà văn bản đến từ một tầng OCR chưa từng được tag. Thứ ba là trường hợp thú vị: nội dung mang các toán tử BDC với các giá trị /MCID nhưng trang của nó không có mục /StructParents và cây cấu trúc của nó chưa từng tham chiếu những định danh đó. Marked content tồn tại, phía cấu trúc thì không, và chẳng có thứ tự nào để phục hồi. HotPDF báo False thay vì bịa ra một thứ tự
Trường hợp cuối xuất hiện trong các tệp bị sửa tay và trong đầu ra của những công cụ phát marked content cho mục đích optional-content hay artifact mà không dựng một cây cấu trúc. Nếu bạn đang tự tạo PDF có tag, chính sự bất đối xứng đó là điều xác thực PDF/UA kiểm tra, và đối tác phía trình ghi được đề cập trong layout DOM phát đầu ra có tag, có phân trang
Ở đâu thứ tự cấu trúc tự hoàn vốn
Kiểm toán khả năng truy cập là cái hiển nhiên: nếu bạn đang chứng nhận một tài liệu theo PDF/UA, thứ tự đọc mà một trình đọc màn hình sẽ thông báo chính là thứ tự cấu trúc, nên trích xuất nó là cách bạn rà soát nó mà không cần trình đọc màn hình. Thu thập dữ liệu là trường hợp thương mại lớn hơn. Các biểu mẫu chính phủ có tag, các công bố được quản lý, và tệp đính kèm hóa đơn điện tử mang nhãn trường và giá trị theo thứ tự được tuyên bố, và đọc chúng theo thứ tự đó loại bỏ cả một lớp bug ánh xạ mà trích xuất hình học tạo ra trên bố cục nhiều cột
Người tiêu dùng mới nhất là truy hồi cho mô hình ngôn ngữ. Chia nhỏ một tài liệu để nhúng chỉ tốt bằng thứ tự văn bản, và một khối nối lệch hai cột tạo ra những câu chưa từng tồn tại. Trích xuất theo thứ tự cấu trúc là bản sửa rẻ nhất hiện có cho điều đó, vì với tài liệu có tag, thứ tự đúng đã nằm trong tệp và chỉ cần được đọc
HotPDF là một thành phần VCL thuần cho Delphi và C++Builder, nên phép duyệt cây cấu trúc và việc phát lại glyph đều chạy trong tiến trình trên một tài liệu đã nạp mà không dính dáng renderer ngoài. Chi tiết API đầy đủ cho họ trích xuất trên tài liệu đã nạp nằm trên trang sản phẩm HotPDF Delphi PDF component