Всеки геометричен текстов извличач познава; Той чете глифовете, които една страница рисува, сортира ги по базова линия и хоризонтална позиция и се надява визуалното подреждане да съвпада с реда, който човек би прочел; В едноколонен отчет тази догадка е права; В двуколонна статия в списание, формуляр със странична лента или таблица, чиито клетки са излъчени колона по колона, тя е грешна по начини, които са трудни за забелязване и скъпи за откриване надолу по веригата; HotPDF отговаря на това с ExtractLoadedPageStructureText, който игнорира геометрията изцяло: той обхожда дървото на структурата на документа в авторски ред, както е дефиниран в ISO 32000-1 §14.8.4, после препаскира глифовете на страницата по техния marked-content идентификатор; За tagged PDF това не е евристика, това е редът, който произвеждащото приложение е декларирало
Функцията връща False, когато страницата няма използваемо дърво на структурата, което е сигналът да се падне обратно към геометричния извличач, а не да се провали; Този двупътен дизайн има повече значение от алгоритъма: реален документен прием вижда tagged правителствени формуляри и сканерен изход в една и съща папка и конвейер, който обработва само едното, не е конвейер
Защо геометричното извличане бърка реда на четене?
Защото PDF content поток не носи изобщо ред на четене; Той е последователност от оператори за рисуване и производител е свободен да ги излъчи в каквата последователност му пасва на собствения му layout engine; Текстови процесори обикновено излъчват в потоков ред и геометричното сортиране изглежда добре; Layout инструменти, дизайнери на формуляри и генератори на отчети често не: долен колонтитул може да бъде излъчен преди тялото, таблица може да бъде запълнена колона-главно, а двуколонна страница може да преплита редове от двете колони, защото composer-ът ги е разрешил заедно
Режимът на отказ е тих; Геометричен извличач никога не докладва грешка, той просто връща проза, чиито изречения са сплескани от две колони; Всичко, което консумира този текст — индекс за търсене, field mapper за е-фактури, retrieval конвейер, хранещ езиков модел — унаследява щетите без предупреждение; HotPDF също доставя геометричните извличачи за заредени документи и те остават правилният инструмент за неетикетирани файлове; смисълът на структура-ред пътя е да спре догадките, когато документът вече носи отговора
Какво дървото на структурата действително съхранява
Tagged PDF държи второ, паралелно описание на страницата; Catalog-ът сочи към /StructTreeRoot, чийто /K деца образуват дърво от структурни елементи: /Document, /Sect, /P, /Table, /TR, /TD и така нататък; Листата на това дърво са marked-content референции, цели числа, които назовават отсечка от content потока на страницата; От страната на съдържанието тези отсечки се отварят с оператор BDC, носещ /MCID, и се затварят с EMC; Всеки структурен елемент носи и запис /Pg, назоваващ страницата, на която принадлежи, което прави обхождането на страница възможно в документ, чийто структура-ред обхваща стотици страници
HotPDF обхожда това дърво с таван на дълбочина 128 нива и филтрира по /Pg, така че само текущата страница допринася; Изходът на обхода не е текст, а подреден списък от MCID стойности: авторският ред на marked-content отсечките на тази страница; Препаскирането на текста тогава е въпрос на възпроизвеждане на глифовете в този ред
MCID се записва по време на глиф извличането, не се търси после
Това е детайлът на имплементацията, който прави функцията евтина; HotPDF вече записва активния marked-content идентификатор на всеки глиф, който извлича, в полето MCID на THPDFGlyphRecord, защото content-stream интерпретаторът знае кой BDC scope е отворен в момента, в който обработва всеки оператор Tj или TJ; Извличането в структура-ред следователно не се нуждае от втори проход през content потока; То събира MCID последователността от дървото на структурата, после разпределя вече извлечените глифи по кофи по MCID и ги излъчва в тази последователност
var
Pdf: THotPDF;
PageCount, I, Untagged: Integer;
PageText, AllText: UnicodeString;
Report: TStrings; // diagnostics приемник, собственост на извикващия
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
// Авторски ред направо от дървото на структурата
if Untagged > 0 then
Report.Add(Format('page %d: %d glyphs outside the structure tree',
[I, Untagged]));
end
else
// Няма използваемо дърво на структурата на тази страница: геометричен fallback
Pdf.ExtractLoadedPageText(I, PageText);
AllText := AllText + PageText + #13#10;
end;
finally
Pdf.Free;
end;
end;
Неетикетирани глифи се броят, никога не се изхвърлят мълшиво
Страница може да бъде частично етикетирана; Производители добавят декоративна линия, номер на страница или късен воден знак извън всеки BDC scope и тези глифи не принадлежат на никой MCID; Изхвърлянето им би било подредената имплементация и грешната, защото същата дупка се появява и когато производител етикетира тялото, но забрави таблицата и вие щяхте да загубите таблицата, без да забележите
HotPDF добавя непоисковите глифи като геометрична опашка след текста в структура-ред и докладва броя им чрез output параметъра UntaggedGlyphCount; Това число е сигнал за качество, върху който можете да действате; Шепа глифи на страница от две хиляди е страница-мебел и може да бъде игнориран; Четиридесет процента от страницата извън дървото на структурата означава, че етикетирането е декоративно и геометричният извличач е по-честният отговор за този файл
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);
// Вярвайте на дървото на структурата само когато претендира по-голямата част от страницата
if (TotalGlyphs = 0) or (Untagged * 4 <= TotalGlyphs) then
begin
UsedStructure := True;
Result := True;
Exit;
end;
end;
Result := Pdf.ExtractLoadedPageText(PageIndex, AText);
end;
Какво кара функцията да върне False
Три случая и си заслужава да се разграничат, защото само един от тях е дефект в документа; Първият е обикновен неетикетиран PDF: без /StructTreeRoot, нищо за обхождане и False е просто истината; Вторият е сканерирана страница, чиито текст идва от OCR слой, който никога не е бил етикетиран; Третият е интересният: съдържание, което носи BDC оператори със /MCID стойности, но чиято страница няма запис /StructParents и чието дърво на структурата никога не реферира тези идентификатори; Marked content-ът съществува, структурната страна не и няма ред за възстановяване; HotPDF докладва False, вместо да измисли такъв
Този последен случай се появява в ръчно редактирани файлове и в изход от инструменти, които излъчват marked content за optional-content или artifact цели, без да строят дърво на структурата; Ако сами произвеждате tagged PDF, същата асиметрия е това, което PDF/UA валидацията проверява, а страната на writer-а е разгледана в layout DOM-а, който излъчва tagged, страницан изход
Къде структура-редът се изплаща сам
Аудит на достъпността е очевидният: ако сертифицирате документ спрямо PDF/UA, редът на четене, който screen reader ще обяви, е точно структура-редът, така че извличането му е начинът да го прегледате без screen reader; Прихващането на данни е по-големият търговски случай; Tagged правителствени формуляри, регулирани оповестявания и прикачени е-фактури носят field етикети и стойности в деклариран ред и четенето им в този ред премахва цял клас mapping дефекти, които геометричното извличане създава върху многоколонни оформления
Най-новият консуматор е retrieval за езикови модели; Чанкингът на документ за embedding е толкова добър, колкото текстовият ред и част, която сплеска две колони, произвежда изречения, които никога не са съществували; Извличането в структура-ред е най-евтиният наличен ремонт за това, защото за tagged документи правилният ред вече е във файла и само трябва да бъде прочетен
HotPDF е нативен VCL компонент за Delphi и C++Builder, така че обходът на дървото на структурата и глиф възпроизвеждането тичат и двете in-process върху зареден документ без външен renderer; Пълни API детайли за семейството извличачи на заредени документи са на продуктовата страница HotPDF Delphi PDF component