PDFiumPas, sayfa metnini bir dize yerine bir yapı olarak döndürür. GetStructuredText, bloklar içeren bir TPdfStructuredTextPage üretir; her blok satırlar içerir, her satır biçimli parçalar içerir; her düzeyde sayfa uzayı sınırları vardır ve kaynak karakter indeksleri korunur, böylece herhangi bir parça altındaki metin sayfasına geri eşlenebilir
Çoğu kodun başladığı düz dize çıkarma hâlâ oradadır ve kendi amacı için hâlâ doğrudur. Hangi kelimelerin bir başlık olduğunu, hangilerinin sol sütuna ait olduğunu ya da bir eşleşmenin sayfada gerçekte nerede durduğunu bilmeniz gerektiği anda yetersiz kalmaya başlar
Çoğu iş için düz bir dize neden yanlış çıktıdır?
Çünkü insanların çıkarılmış metinden sorduğu sorular neredeyse hiçbir zaman "bu sayfada hangi karakterler var" değildir. Sorular "başlık ne", "bu bir tablo mu", "bu paragraf bölüm 4'e mi ait", "vurguyu nereye çizeyim" şeklindedir. Tek bir dize bunların hiçbirini yanıtlamaz ve ondan yeniden inşa ettiğiniz her cevap artık sizin sahiplendiğiniz bir sezgiseldir
İki sütunlu düzenler bunu somutlaştırır. İki sütunlu bir makaleyi bir dize olarak çıkarın; üreticinin içerik akışını nasıl yazdığına bağlı olarak, birinci sütunun ardından ikinci sütunu alabilir ya da birinci sütunun birinci satırını, ikinci sütunun birinci satırını, birinci sütunun ikinci satırını, ve böyle devam edebilirsiniz. İkisi de uyumlu bir PDF'ten çıkabilir. Hiçbiri biçim düzeyinde yanlış değildir, çünkü PDF bir sayfadaki işaretleri tanımlar, bir belge taslağını değil. Blok tabanlı bir model, çıkarıcının sıralama kararını açıkça vermesine ve hangi kararı verdiğini size söylemesine izin verir
İçerik sırası mı fiziksel düzen mi?
TPdfStructuredTextOptions.ReadingOrder, roContentOrder ile roPhysicalLayout arasında seçim yapar ve doğru cevap neye daha çok güvendiğinize bağlıdır: üreticiye mi, geometriye mi
İçerik sırası, metni içerik akışının çizdiği sırada döndürür. Bu hızlıdır ve iyi davranan bir üretici tarafından üretilen belgeler için genellikle amaçlanan okuma sırasıdır. Fiziksel düzen ise akış sırasını yok sayar ve sırayı karakterlerin gerçekte nerede durduğundan yeniden inşa eder, önce satırlara sonra sütunlara kümeler. Bu, taranıp OCR'lenmiş sayfalar için, metni okuma sırası yerine yazı tipi sırasında yayan araçların çıktısı için ve görsel sonucun güvenebileceğiniz tek şey olduğu her durum için istediğiniz şeydir
uses
PDFium;
var
Pdf: TPdf;
Options: TPdfStructuredTextOptions;
Page: TPdfStructuredTextPage;
B, L: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'article.pdf';
Pdf.LoadDocument;
Pdf.PageNumber := 1; // 1 tabanlı
Options := TPdfStructuredTextOptions.Default;
Options.ReadingOrder := roPhysicalLayout;
Options.IncludeFontInfo := True;
Options.IncludeSemantics := True;
Options.MaxCharacters := 200000; // kapalı-yönde başarısız olan bütçe
Page := Pdf.GetStructuredText(Options);
for B := 0 to High(Page.Blocks) do
begin
if Page.Blocks[B].Kind = cfHeading then
Emit(Format('H%d: %s',
[Page.Blocks[B].HeadingLevel, Page.Blocks[B].Text]))
else
for L := 0 to High(Page.Blocks[B].Lines) do
Emit(Page.Blocks[B].Lines[L].Text);
end;
finally
Pdf.Free;
end;
end;
Etiketleme geometrinin veremeyeceği neyi ekler?
Niyeti. IncludeSemantics etkinken, etiketlenmiş bir PDF'ten gelen bloklar yapı ağacından çekilen bir Kind taşır; bu yüzden bir başlık, yazı tipi ortalamadan büyük olduğu için değil üreticinin öyle söylediği için başlıktır. Türler, yeniden kullanım için önemli olan biçimleri kapsar: cfParagraph, bir HeadingLevel'a sahip cfHeading, cfListItem, cfTableCell, cfCaption, cfFigure ve etiketlenmemiş yedek cfPlain
Source alanı her sınıflandırmanın nereden geldiğini kaydeder; yapı ağacı için rosStructure, çıkarım için rosHeuristic; bir çıkarım hattına bir belge kümesi boyunca ne kadar güveneceğinize karar verirken günlüğe kaydedilecek alan da budur. Şekiller bilinmeye değer özel bir durumdur: bir cfFigure bloğu için metin herhangi bir glif yerine alternatif açıklamadan gelir, çünkü bir şeklin kendine ait karakteri yoktur. Eşleşmeyen alternatif metin, düşürülmek yerine yine de temsil edilir; bu, bir erişilebilirlik denetiminin, sayfada hiçbir şey çizmese bile bir açıklamanın var olduğunu görmesini sağlayan şeydir. Etiketleme modelinin kendisi PDF/UA yapı ağacı doğrulamasında ele alınmıştır
Parçalar biçimlendirmeyi ve kökeni taşır
Her TPdfStructuredTextSpan, metnini, sayfa uzayı sınırlarını, FontName, FontSize, FontWeight ve Angle'ı, ayrıca SourceStartIndex ve SourceCharacterCount'u taşır. Parçalar biçimlendirme değiştiğinde kırılır; bu yüzden üç kalın kelimeli bir cümle üç parça haline gelir ve HTML ya da Markdown'da vurguyu yeniden inşa etmek, yazı tipi adlarından tahmin etmek yerine özellikleri okuma meselesidir
İki kaynak indeks alanı, çıkarımı bir rapordan çok bir özelliğe dönüştürenlerdir. Sayfanın karakter dizisine geri işaret ederler; bu, bir aramada eşleştirdiğiniz bir bloğun, metin üzerinde ikinci, farklı sıralı bir geçiş olmadan karakter düzeyinde seçim geometrisine ya da bir vurgu dikdörtgenine dönüştürülebileceği anlamına gelir; mekanizma karakter kutularıyla görsel metin satırı seçiminde anlatılmıştır. Angle alanı göründüğünden daha önemlidir: bir damgadaki ya da bir filigrandaki döndürülmüş metin, gövde metniyle aynı koordinat uzayına iner ve açıyı yok sayan bir hat, çapraz bir "DRAFT"ı memnuniyetle bir paragrafın ortasına birleştirir
Bütçe ve iki kalite sayacı
MaxCharacters, bir kırpma ayarı değil kapalı-yönde başarısız olan bir bütçedir: onu aşan bir sayfa içeriğin bir kısmını sessizce döndürmek yerine durur. Güvenilmeyen bir alım yolunda istediğiniz davranış budur, çünkü bir milyon karakterli bir sayfa ya makine tarafından üretilmiş bir canavardır ya da çıkarıcınızı sistemin en yavaş parçası haline getirme girişimidir
Döndürülen sayfadaki iki sayaç, çıkarım kalitesini doğrudan tanımlar. UnmappedCharacterCount, kullanılabilir bir Unicode eşlemesi olmayan karakterleri sayar; bu, bir /ToUnicode CMap'i olmadan gömülü bir alt küme yazı tipinin klasik belirtisidir; böyle bir metin mükemmel görünür ve kullanışsız bir şey olarak çıkarılır. GeometryFailureCount, sınırlayıcı kutusu belirlenemeyen karakterleri sayar; bu, fiziksel düzen sıralamasını bozar. İkisini de günlüğe kaydedin. Bu sayıların sürekli olarak sıfıra yakın olduğu bir belge kümesi güvenle dizinlenebilir; olmadığı bir kümede ise, herhangi bir alt akış sonucu güvenilir olmadan önce hattınızdaki bazı üreticilerin ilgiye ihtiyacı olduğunu size söylüyordur
var
Page: TPdfStructuredTextPage;
B, S, L: Integer;
Emphasised: Boolean;
begin
Page := Pdf.GetStructuredText(Options);
if Page.UnmappedCharacterCount > 0 then
Log(Format('page %d: %d characters without a Unicode mapping',
[Page.PageNumber, Page.UnmappedCharacterCount]));
if Page.GeometryFailureCount > 0 then
Log(Format('page %d: %d characters without geometry',
[Page.PageNumber, Page.GeometryFailureCount]));
for B := 0 to High(Page.Blocks) do
for L := 0 to High(Page.Blocks[B].Lines) do
for S := 0 to High(Page.Blocks[B].Lines[L].Spans) do
begin
Emphasised := Page.Blocks[B].Lines[L].Spans[S].FontWeight >= 600;
AppendRun(Page.Blocks[B].Lines[L].Spans[S].Text, Emphasised,
Page.Blocks[B].Lines[L].Spans[S].SourceStartIndex);
end;
end;
Gerçek sayfalarda performans
Fiziksel düzen çıkarımı pahalı moddur ve uygulama gerçekten büyük sayfalar için inşa edilmiştir: karakter sıralaması tekrarlanan taramalar yerine O(n log n) çalışır, satır ve parça arabellekleri karakter başına yeniden ayırmak yerine geometrik olarak büyür, Unicode metni dize birleştirme yerine arabelleklerde inşa edilir ve komşu metin nesneleri için yazı tipi aramaları önbelleğe alınır. Bu birleşim, yoğun 5.000 karakterlik bir sayfayı ikinci dereceden yerine öngörülebilir tutan şeydir
Sayfa sayısı ağırlıklı bir iş için, mümkün olan yerde daha ucuz modu seçmeye yine de değer. Güvendiğiniz etiketlenmiş belgeler için semantik etkinken roContentOrder'ı kullanın ve roPhysicalLayout'u geometrinin tek sinyal olduğu taranmış ve eski materyal için ayırın. Tek ihtiyacınız düz bir dizeyse, PDF belgelerinden metin çıkarmada anlatılan daha basit API daha hızlı yol olarak kalır ve metni işaretlenmiş içerik tanımlayıcılarına kadar izlemeniz gerektiğinde, BDC ve MCID işaretlenmiş içeriği okuma ve yazma o katmanı kapsar
Blok modeli, alım hatlarının istediği şeyle de temiz bir şekilde eşleşir: başlıklı paragraflar bir başlığa sahip bir parçadır ve sınırlar, bir alıntının bir belgeye değil sayfadaki bir konuma işaret etmesini sağlar. PDFiumPas, PDFium motoru etrafında bir Delphi ve Lazarus bileşenidir; örneklerle PDFium Delphi bileşeni sayfasında belgelenmiştir