Bài viết kỹ thuật

Trích xuất văn bản PDF theo thứ tự cấu trúc với HotPDF

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

So sánh trang PDF hai cột cho thấy trích xuất hình học sắp theo baseline nối lệch hai cột đối với trích xuất theo thứ tự cấu trúc MCID trong HotPDF
Sắp glyph theo baseline đan hai cột thành vô nghĩa, trong khi cây cấu trúc phát lại thứ tự mà trình tạo đã tuyên bố

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

Giải phẫu cây cấu trúc PDF nối các phần tử StructTreeRoot như Sect, Table, TR và TD tới các đoạn BDC MCID trong luồng nội dung trang HotPDF
Các lá của cây là tham chiếu marked-content, và mỗi phần tử mang một mục Pg cho phép bước đi lọc về trang hiện tại

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 đó

Luồng quyết định cho trích xuất văn bản cấu trúc HotPDF với dự phòng hình học khi trang không có cây cấu trúc dùng được hoặc tag chỉ để trang trí
True nghĩa là thứ tự cấu trúc với đuôi không tag được nối, còn False đưa trang sang bộ trích xuất hình học thay vì thất bại
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