PDFium Component, Delphi'den ApplyOcrSearchLayer üzerinden taranmış PDF sayfalarına aranabilir bir metin katmanı ekler. Seçilen her sayfayı render eder, pikselleri sizin sağladığınız bir OCR sağlayıcısına verir ve tanınan sözcükleri, taramadaki sözcüklerin üzerine konumlandırılmış görünmez metin nesneleri olarak geri yazar. Orijinal sayfa görüntüsü hiçbir zaman çözülmez (decode), yeniden kodlanmaz veya değiştirilmez, bu yüzden görsel sonuç, başladığınız sayfayla bayt bayt aynıdır
Tanıma motoru kasıtlı olarak kütüphanenin parçası değildir. PDFium; sayfa render etmeyi, koordinat eşlemeyi, yazı tipi yüklemeyi, metin nesnesi oluşturmayı ve görünmez render modlarını sunar, ama hiçbir OCR motoru içermez ve aksini iddia etmek, birinin tanıma ürününü bir PDF bileşenine paketlemek anlamına gelirdi. Bunun yerine tanıma, IPdfOcrProvider arayüzünün arkasında yaşar: kütüphane sabit düzenli, üst kökenli BGRA pikselleri geçirir ve sağlayıcı Unicode metin, güven değerleri ve sözcük dörtgenleri döndürür
Aranabilir bir metin katmanı tam olarak nedir?
Taranmış bir PDF, bir belgenin resmidir. Sayfa içeriği tek büyük bir görüntüdür ve seçilecek, aranacak, kopyalanacak veya dizinlenecek hiçbir şey yoktur. Aranabilir bir metin katmanı, render modu görünmez olarak ayarlanmış gerçek metin nesnelerini o görüntünün üzerine ekler, bu yüzden görüntüleyiciler hiçbir şey çizmez ama seçim, arama ve çıkarma sözcükleri tam olarak göründükleri yerde bulur
Konumlandırma her şeydir. Görünmez metin birkaç punto kaymışsa, seçim vurguları sözcüklerin üzerine değil yanına iner ve bir paragrafı kopyalamak yanlış sırada metin üretir. Geometrinin, oransal bir tahminden değil PDFium'un sayfayı render etmek için kullandığı aynı dönüşümlerden gelmesi gerekmesinin nedeni budur
Sağlayıcıyı uygulamak
Sağlayıcı sözleşmesi tek bir yöntemdir. Boyutları, satır adımını (stride), DPI'yi, piksel biçimini ve piksel baytlarının kendisini taşıyan bir sayfa görüntüsü kaydı, artı bir iptal belirteci (token) alır ve sözcükleri veya bir hata mesajı döndürür:
uses
PDFium;
type
TMyOcrProvider = class(TInterfacedObject, IPdfOcrProvider)
public
function RecognizePage(const Image: TPdfOcrImage;
const CancellationToken: IPdfCancellationToken;
out Words: TPdfOcrWords; out ErrorMessage: string): Boolean;
end;
function TMyOcrProvider.RecognizePage(const Image: TPdfOcrImage;
const CancellationToken: IPdfCancellationToken;
out Words: TPdfOcrWords; out ErrorMessage: string): Boolean;
var
I: Integer;
begin
// Image.Pixels, Image.Stride baytlık üst kökenli BGRA satırları tutar.
// Bunları motorunuza verin, ardından tanınan her sözcük için bir girdi doldurun
SetLength(Words, RecognisedCount);
for I := 0 to RecognisedCount - 1 do
begin
Words[I].Text := EngineWordText(I);
Words[I].Confidence := EngineWordConfidence(I); // 0..1
Words[I].Quad := TPdfOcrQuad.FromRectangle(
EngineLeft(I), EngineTop(I), EngineRight(I), EngineBottom(I));
end;
ErrorMessage := '';
Result := True;
end;
Dikdörtgenler yerine dörtgenler, çünkü bir tarama nadiren sayfaya tam dik açılıdır. Hafifçe döndürülmüş bir sayfadaki bir sözcük bir paralelkenar kaplar ve TPdfOcrQuad, eğik ve döndürülmüş sözcüklerin doğru bir seçim bölgesi tutması için dört köşe noktası taşır. Yalnızca eksene hizalı kutular bildiren motorlar, yozlaşmış (degenerate) dörtgeni oluşturan FromRectangle'ı kullanabilir
Sözcük konumları neden oransal olarak ölçeklenemez?
Bir piksel koordinatını, render genişliğine bölüp sayfa genişliğiyle çarparak bir sayfa koordinatına dönüştürmek cazip gelir. Bu, yalnızca döndürmesi olmayan, CropBox'ı MediaBox ile özdeş olan ve orijini sıfırda olan sayfalar için işe yarar; birçok taranmış belge ise bu koşullardan en az birinde başarısız olur
PDFium Component, dört dörtgen köşesinin her birini render edicinin pikselleri üretmek için kullandığı aynı eşleme olan FPDF_DeviceToPage üzerinden ayrı ayrı eşler, bu yüzden /Rotate girdileri ve ofsetli kırpma kutuları yapı gereği ele alınır. Metin nesnesi için afin matris, ardından eşlenmiş noktaların üçünden — sol alt, sağ alt ve sol üst köşelerden — oluşturulur; bu, konumu, ölçeği, döndürmeyi ve eğimi (shear) ifade etmek için tam olarak yeterlidir
Metin nesnesinin kendisi, gerçek yazı tipi sınırları ölçülebilsin diye birim yazı tipi boyutunda oluşturulur ve ölçülen nesne sınırları ardından hedef dörtgene eşlenir. Tahmin edilen bir punto boyutuyla boyutlandırıp taranmış sözcükle eşleşmesini ummak, her yazı tipi değişiminde kayardı; önce ölçmek, uyumu katmanın hangi yazı tipini kullandığından bağımsız kılar
Bir belge üzerinde çalıştırmak
Seçenekler kaydı; çözünürlüğü, filtrelemeyi ve her bütçeyi denetler. Güven filtrelemesi göründüğünden daha önemlidir: düşük güvenli çöp sözcükler arama sonuçlarını kalıcı olarak kirletir ve yanlış bir render'ın aksine, bir arama saçmalık döndürene kadar kimse fark etmez:
var
Pdf: TPdf;
Options: TPdfOcrOptions;
Report: TPdfOcrReport;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'scanned-contract.pdf';
Pdf.LoadDocument;
Options := TPdfOcrOptions.Default;
Options.Dpi := 300; // tanıma çözünürlüğü
Options.MinConfidence := 0.60; // belirsiz sözcükleri at
Options.SkipPagesWithText := True; // doğuştan dijital sayfalara dokunma
Options.ContinueOnError := True; // tek bir kötü sayfa işi durdurmamalı
Options.MaxPixelsPerPage := 40 * 1000 * 1000;
if Pdf.ApplyOcrSearchLayer(TMyOcrProvider.Create, Options, Report) then
Pdf.SaveAs('scanned-contract-searchable.pdf');
for I := 0 to High(Report.Pages) do
if Report.Pages[I].Status = popsFailed then
Writeln(Format('page %d failed: %s',
[Report.Pages[I].PageNumber, Report.Pages[I].ErrorMessage]));
Writeln(Format('%d word(s) inserted, %d rejected, %d page(s) skipped',
[Report.InsertedWordCount, Report.RejectedWordCount,
Report.SkippedPageCount]));
finally
Pdf.Free;
end;
end;
SkipPagesWithText, karışık arşivlerde vurgulanmaya değer. Zaten gerçek metin taşıyan bir PDF — ister doğuştan dijital ister daha önce işlenmiş olsun — üzerinden körü körüne OCR çalıştırırsanız ikinci bir metin katmanı alır ve bu yinelenme, çıkarmanın her sözcüğü iki kez döndürmesine neden olur. Sayfa başına popsSkippedExistingText durumu, hangi sayfaların dokunulmadan bırakıldığını tam olarak söyler
Bütçeler, iptal ve hata kapsamı
Kötü niyetli veya yalnızca devasa bir belgenin şişirebileceği her miktarın bir tavanı vardır: sayfa başına ve toplamda piksel, sayfa başına ve toplamda sözcük ve sözcük başına karakter. Bunların hepsi, sayfa yazıldıktan sonra değil önce denetlenir ve piksel tahmini, herhangi bir bitmap ayrılmadan önce sayfa boyutlarından ve DPI'den hesaplanır. DPI'yi 150'den 300'e çıkarmak sayfa başına belleği dört katına çıkarır, bu yüzden bir toplu iş büyük biçimlerde başarısız olmaya başladığında ilk ayarlanacak parametre sayfa başına tavandır
İptal belirteci, tüm yol boyunca geçer: aşamalı render, sağlayıcı çağrısı ve sözcük başına ekleme döngüsü. Bu, 400 sayfalık bir dosyanın tanınması sırasında iptal eden bir kullanıcının belgenin sonunda değil bir sayfa içinde durması anlamına gelir ve bileşenin başka yerlerinde kullanılan, iptal edilebilir aşamalı render yazısında anlatılan aynı belirteç kalıbı burada değişmeden geçerlidir
Hata kapsamı sayfa başınadır. Kütüphane, bir sayfaya eklediği nesne tanıtıcılarını toplar ve tüm sözcükler yerleştirildikten sonra FPDFPage_GenerateContent'i bir kez çağırır. Ortada bir şey — bir sağlayıcı hatası veya bir yazı tipi sorunu — başarısız olursa, o sayfaya eklenen nesneler tersten kaldırılır ve sayfa içeriği yeniden oluşturulur, bu yüzden başarısız bir sayfa yarım bir metin katmanı tutmak yerine orijinal durumuna döner. Belge döngüsü ardından ContinueOnError'a göre devam eder veya durur ve etkin sayfa her zaman geri yüklenir
Görüntünün gerçekten dokunulmadığını doğrulamak
Mevcut en güçlü kontrol aynı zamanda en basit olanıdır: sayfayı katmanı uygulamadan önce ve sonra aynı boyutta render edin ve bitmap'leri karşılaştırın. Bunlar bayt bayt özdeş olmalıdır, çünkü görünmez metin hiçbir şey çizmez ve görüntü akışı hiçbir zaman çözülmemiştir (decode). Herhangi bir fark, metin katmanı dışında bir şeyin sayfayı değiştirdiği anlamına gelir
Bundan sonra, işlenmiş dosyadan çıkararak ve sözcük konumlarının tarama üzerine indiğini doğrulayarak metin tarafını doğrulayın. Çıkarma yolu, PDF belgelerinden metin çıkarma yazısında anlatılanla aynıdır ve hizalamanın hızlı bir görsel kontrolü için, PDF sayfalarını JPEG'e dönüştürme yazısındaki gibi sayfaları görüntülere render etmek, tarama üzerine sözcük kutularını bindirmenize olanak tanır
OCR katmanlama, render etme, çıkarma ve düzenlemenin hepsi Delphi, C++Builder ve Lazarus'ta aynı belge nesnesine karşı çalışır; tam API yüzeyi Delphi için PDFium Component sayfasında anlatılmıştır