PDFium Component phát hiện các bảng trên một trang PDF và trả về chúng dưới dạng một lưới ô với span dòng và cột, dòng tiêu đề và một giá trị độ tin cậy, thông qua ExtractTables cho một trang và ExtractDocumentTables cho cả tài liệu. Mỗi bảng chuyển sang CSV hoặc JSON chỉ bằng một lệnh gọi, và các bảng tiếp diễn qua một điểm ngắt trang có thể được liên kết thành một chuỗi tiếp diễn
PDF không có object bảng. Một bảng trong PDF là một tập các đoạn văn bản được đặt vị trí sao cho một con người đọc chúng thành một lưới, đôi khi có kẻ đường bao quanh và thường thì không. Khôi phục lưới đó nghĩa là dựng lại một ý định mà tệp chưa bao giờ ghi lại, đó là lý do mọi công cụ trích xuất tạo ra kết quả hơi khác nhau, và lý do một công cụ cho bạn biết độ tin cậy của nó hữu ích hơn một công cụ không cho biết
Hai chế độ phát hiện cho hai loại bảng
Phát hiện có kẻ đường dùng các đường đã vẽ. Mỗi đoạn path đã stroke được biến đổi sang tọa độ trang qua ma trận của page object, các đường ngang và dọc được giao nhau, và các giao điểm hình thành các thành phần liên thông. Mỗi thành phần trở thành lưới các vị trí X và Y đã sắp xếp riêng của chính nó, đây chính là điều giữ hai bảng riêng biệt trên cùng một trang không bị gộp thành một lưới vô nghĩa
Phát hiện khoảng trắng xử lý các bảng được vẽ bằng căn chỉnh thay vì kẻ đường. Các hộp từ được nhóm thành các dòng thị giác, khoảng trống trong một dòng chia nó thành các cột ứng viên, và một bảng chỉ được chấp nhận khi ít nhất MinRows dòng lặp lại ít nhất MinColumns anchor căn trái trong phạm vi AlignmentTolerance. Hệ số khoảng cách dòng mặc định là 3, bao trùm khoảng cách baseline khoảng 30 điểm điển hình của văn bản cỡ chữ 12 mà không để một dòng đơn chứa nhiều đoạn văn bản giả dạng thành một bảng
uses
PDFium;
var
Pdf: TPdf;
Options: TPdfTableExtractionOptions;
Tables: TPdfTables;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'annual-report.pdf';
Pdf.LoadDocument;
Pdf.PageNumber := 12; // đánh số từ 1
Options := TPdfTableExtractionOptions.Default;
Options.DetectRuledTables := True;
Options.DetectWhitespaceTables := True;
Options.MinConfidence := 0.6; // mặc định là 0.5
Options.HeaderRowCount := 1;
Tables := Pdf.ExtractTables(Options);
for I := 0 to High(Tables) do
Writeln(Format('table %d: %dx%d cells, confidence %.2f, mode %d',
[I, Tables[I].RowCount, Tables[I].ColumnCount,
Tables[I].Confidence, Ord(Tables[I].DetectionMode)]));
if Length(Tables) > 0 then
SaveText('page12-table0.csv', Tables[0].ToCsv);
finally
Pdf.Free;
end;
end;
Ô hợp nhất được khôi phục như thế nào?
Đây là phần các bộ trích xuất ngây thơ làm sai. Một ô hợp nhất không thể xác định được chỉ từ lưới toàn cục, vì lưới được suy ra từ mọi đường trên trang, và một vùng hợp nhất đơn giản là thiếu mất đường nội bộ vốn dĩ đã tách chúng ra
Quy tắc được dùng ở đây mang tính cục bộ: hai ô cơ sở liền kề được hợp nhất khi không có đường ranh giới nào bao phủ khoảng cách giữa chúng. Union-find nối chúng lại, các thành phần hình chữ nhật kết quả trở thành giá trị RowSpan và ColumnSpan, và văn bản được gán cho một ô cơ sở theo điểm trung tâm của nó rồi đi theo ô đó tới gốc hợp nhất của nó. Làm theo cách này cũng giữ chi phí tuyến tính theo số từ cộng số ô, thay vì phép quét bậc hai bạn sẽ gặp phải khi kiểm tra từng từ với từng ô
Hiệu ứng thực tế là một bảng tài chính với một tiêu đề "Total" hợp nhất trải ba cột sẽ ra đúng một ô có span bằng ba, thay vì một ô có nội dung và hai ô trống bí ẩn
Tiếp diễn qua các trang
Các bảng dài ngắt qua nhiều trang, và coi mỗi mảnh trên mỗi trang là một bảng độc lập buộc bên gọi phải tự ghép nối chúng. ExtractDocumentTables có thể liên kết chúng thay vào đó, nhưng chỉ dưới các điều kiện nghiêm ngặt: mảnh phải là bảng thấp nhất trên trang trước đó, mảnh kế tiếp phải là bảng trên cùng của trang sau đó, số trang phải liền kề, và ranh giới cột phải khớp nhau
Cả bốn điều kiện cùng nhau chính là thứ ngăn lỗi hiển nhiên, đó là nối mọi bảng bốn cột trong một tài liệu thành một siêu bảng tưởng tượng chỉ vì chúng tình cờ có cùng số cột. Khi các điều kiện được thỏa mãn, các bảng dùng chung một định danh nhóm tiếp diễn và mang theo metadata tiếp diễn; khi không, bạn nhận được các bảng riêng biệt và có thể tự quyết định
Việc trích xuất ở cấp tài liệu dùng chung các ngân sách MaxCells và MaxTables qua các trang thay vì đặt lại theo từng trang, và nó khôi phục trang đang hoạt động trong một khối finally, nên một lần trích xuất chạy trong một trình xem sẽ để người dùng đứng lại đúng trang họ đang xem
Xuất mà không làm hỏng dữ liệu
Cả hai bộ xuất đều cẩn trọng về việc escape. CSV luôn đặt trong ngoặc kép các trường và nhân đôi các dấu ngoặc kép nội bộ, tránh thất bại kinh điển khi một ô chứa dấu phẩy âm thầm biến thành hai cột. Với các ô hợp nhất, nội dung chỉ được phát ra tại anchor trên-trái, nên một vòng round-trip CSV không nhân đôi một tiêu đề trải rộng khắp các cột nó bao phủ
JSON bảo toàn Unicode thay vì escape nó thành ASCII, escape các ký tự điều khiển, và bao gồm metadata mà một bên tiêu thụ cần để đánh giá chất lượng: chế độ phát hiện, độ tin cậy, ranh giới, giá trị span, cờ tiêu đề và thông tin tiếp diễn. Nếu bạn đang đưa các bảng đã trích xuất vào một hệ thống hạ nguồn, hãy ưu tiên JSON, vì một dòng CSV không thể cho bạn biết bảng nó đến từ đâu đạt độ tin cậy 0,51:
// Trích xuất toàn tài liệu, chỉ giữ lại các bảng đáng tin
Tables := Pdf.ExtractDocumentTables(Options);
for I := 0 to High(Tables) do
begin
if Tables[I].Confidence < 0.75 then
begin
Log(Format('page %d table needs review (%.2f)',
[Tables[I].PageNumber, Tables[I].Confidence]));
Continue;
end;
if Tables[I].ContinuationGroup > 0 then
AppendToGroup(Tables[I].ContinuationGroup, Tables[I].ToJson)
else
EmitStandalone(Tables[I].ToJson);
end;
Điều chỉnh, và biết khi nào nên dừng
Ba thiết lập quan trọng hơn phần còn lại. MinConfidence là cổng chất lượng, và 0,5 là mức khoan dung có chủ đích; hãy nâng nó lên cho việc nạp dữ liệu tự động và hạ nó xuống cho một giao diện review nơi con người xác nhận từng kết quả. MinColumnGap quyết định điều gì được tính là ranh giới cột ở chế độ khoảng trắng, và các bảng được canh sát nhau trong các báo cáo dày đặc có thể cần giảm nó xuống từ mức mặc định 12 điểm. MaxRowGapFactor quyết định khi nào khoảng cách dọc kết thúc một bảng, điều này quan trọng với các bảng có dòng trống thỉnh thoảng
Hãy trung thực về giới hạn. Các bảng có kẻ đường trích xuất đáng tin cậy. Các bảng khoảng trắng được căn sạch sẽ trích xuất tốt. Các bảng có văn bản xoay, bảng lồng nhau, hoặc các ô có nội dung ngắt dòng trông giống một dòng khác sẽ cần review dù các tham số được đặt thế nào đi nữa. Với những trường hợp đó, mô hình văn bản có cấu trúc cho bạn nguyên liệu thô để dựng một bộ đọc theo lĩnh vực riêng, được mô tả trong khối văn bản có cấu trúc và thứ tự đọc
Một sự kết hợp hữu ích: khi một tài liệu đã scan không có văn bản nào cả, việc phát hiện bảng không có gì để làm việc cho tới khi tồn tại một lớp văn bản. Hãy thêm một lớp trước, như được mô tả trong thêm lớp văn bản có thể tìm kiếm cho PDF đã scan, rồi mới trích xuất. Các hộp từ mà một nhà cung cấp OCR trả về chính xác là đầu vào mà phát hiện khoảng trắng cần
Trích xuất bảng, văn bản có cấu trúc và reflow đều đọc từ cùng một mô hình trang trong Delphi, C++Builder và Lazarus; API đầy đủ được mô tả tại trang PDFium Component cho Delphi