Teknik Makale

PDF Çıkarımında Sahte Bold Kaynaklı Mükerrer Metni Düzeltin

HotPDF Delphi Component, v2.768.15'ten itibaren, sahte bold ile üstüne basılmış logoların çıkarılan PDF metninde bıraktığı mükerrer karakterleri kaldırır. Bir üretici aynı glifi bir noktanın kesri kadar arayla iki kez boyadığında ExtractLoadedPageText, ExtractLoadedPageTextLayout ve ExtractLoadedPageTextColumns o karakteri artık bir kez okur; böylece "Bold words" çıktısı artık "Bold wordsBold words" olmaz

Bu, çözücü arızası gibi görünüp olmayan o hatalardan biri. Her glif doğru çözülür; metin gerçekten sayfada iki kez duruyor. Düzeltme, tamamen geçerli iki gliften hangisinin kopya olduğuna karar vermek ve bunu "fill" içindeki çift l ya da bir tablonun özdeş hücreleri gibi meşru tekrarlı harfleri yemeden yapmak zorunda. Bu yazının gerisi HotPDF'in kullandığı kuralı, arkasındaki eşik değerleri, diğer çıkarma araçlarından bilinçli olarak nerede ayrıldığını ve aynı sürüm treninden gelen, satır sonlarından da metin kesen ikinci, ilgisiz bir çıkarma hatasını anlatır

Sahte bold PDF çıkarmasında neden çift metin üretir?

Sahte bold, çıkarılan metni çiftler; çünkü üretici metni iki kez çizer ve her iki kopya da gerçek metin gösterme işlemidir. Bir font ailesinin bold yüzü yoksa ya da yazım aracı sadece zahmet etmiyorsa birçok üretici bold'u, aynı metni Tj ya da TJ ile gösterip metin konumunu onda birler mertebesinde sağa kaydırıp yeniden göstererek taklit eder. Ekranda iki kopya daha kalın bir kontura birleşir. Content stream'de ise karakter kodlarının iki eksiksiz turu vardır ve ISO 32000-1 §9.4, "bu dekorasyondur" diyen bir bayrak çıkarcıya vermez

Üstüne basılmış logolar aynı soruna başka bir açıdan çarpar. Bir logotype, aynı fontu ve aynı karakterleri kullanarak bir kez dolgu (fill) olarak bir kez de kontur (stroke) ya da hafif kaydırılmış bir gölge olarak boyanabilir. Content stream'i boyama sırasında gezen naif bir çıkarıcı önce ilk turun tamamını, sonra ikinci turun tamamını üretir; sonucun "BBoolldd" değil "Bold wordsBold words" olarak okunmasının nedeni budur. v2.768.15 öncesi HotPDF metin birleştirmesi her iki kopyayı da tutuyordu ve kopya, çağıranların asıl önemsediği şeyi bozuyordu: sayfa metninde "Bold words" araması, iki tur arasındaki kelime sınırı kaybolduğu için başarısız olabiliyordu

Gerçek metin rendering modu 2 (önce dolgu sonra kontur, ISO 32000-1 §9.3.6) bu soruna sahip değildir; çünkü tek bir metin gösterme işlemi, tek bir glif kümesi üretir. Kopyalar yalnızca üretici metni iki kez verdiğinde görünür

HotPDF hangi glifin kopya olduğuna nasıl karar verir?

HotPDF iki glifi, aynı Unicode değerini taşıyorlarsa, yazım yönleri aynıysa, boyutları birbirinin %5'i içindeyse ve neredeyse üst üste duruyorlarsa — baseline boyunca aralarındaki fark glif boyutunun en fazla 0,1'i ve baseline'a dik yönde en fazla 0,2'si ise — tek boyanmış karakter sayar. Böyle her çiftte önce boyanan glif tutulur, sonraki atılır. Bunlar v2.768.15 uygulamasındaki değerlerdir ve her biri bir nedenden oradadır

  • Aynı Unicode, aynı yön. Yalnızca özdeş karakterler kopya olabilir ve yatay bir glif asla dikey (WMode 1) bir glifle karşılaştırılmaz
  • Boyut %5 içinde. Tesadüfen üst üste binen büyük bir başlık harfi ile küçük bir altyazı harfi aynı metin değildir; bu yüzden boyut uyuşmazlığı çifti eler
  • Baseline boyunca boyutun 0,1'i. Sahte bold kaydırmaları bir noktanın onda birleri mertebesinde, normal metin boyutunun yüzde birkaçı kadardır. İki gerçek bitişik harf, sıkı kern'lenmiş bir "ll" bile, kabaca tam bir advance genişliği kadar ayrılır; bu toleranstan bir büyüklük mertebesi daha fazla
  • Baseline'a dik yönde boyutun 0,2'si. Gölgeler ve kontur turları çoğu zaman sağa olduğu kadar aşağıya da kayar; bu yüzden dikey tolerans daha gevşektir ama yine de sonraki satıra olan uzaklığın çok altındadır
  • İlk boyanan kazanır. Daha erken glifi tutmak ilk turun okuma sırasını korur; böylece hayatta kalan metin, üreticinin yazdığı yönde akmaya devam eder

Uzaklıklar ham sayfa X ve Y'si yerine glifin kendi baseline çerçevesinde ölçülür; böylece döndürülmüş metin ve eğik bir baseline üzerindeki metin, sıradan yatay satırlarla aynı toleransları kullanır. Kullanılan boyut, glifin sayfada boyandığı haliyle, quad'sından türetilen boyutudur; Tf'ye verilen boyut operandı değil. Bu, Tf'yi 1'e ayarlayıp her şeyi metin matrisiyle ölçekleyen belgeler için önemlidir; ham bir font boyutu orada her toleransı işe yaramaz kılardı

Boşluklar asla aday değildir. U+0020'ye kadar olan kod noktaları, bölünemez boşluk U+00A0 ve ideografik boşluk U+3000 atlanır; çünkü boş gliflerde çoğaltılacak görünür mürekkep yoktur ve birini düşürmek kelimeleri birleştirirdi

Denetimi yoğun sayfalarda hızlı tutmak

HotPDF'deki kopya denetimi kabaca n log n zamanda çalışır, n karede değil; çünkü önce sıralar ve yalnızca küçük bir pencere içindeki glifleri karşılaştırır. Her glifi her glifle karşılaştırmak doğru ama kullanılamaz olurdu: küçük puntolu yoğun bir sayfa kolayca on binlerce glif taşıyabilir ve denetim her metin çıkarma çağrısında çalışır

İç rutin, boşluk olmayan glifleri stabil bir merge sort ile önce Unicode değerine, sonra origin X'ine, sonra boyama sırasına göre sıralar. Sıralamadan sonra bir glifin her olası kopyası, sıralamada hemen ardından gelir; bu yüzden tarama yalnızca Unicode değeri eşleştiği ve X farkı glif boyutunun 0,25'i içinde kaldığı sürece ileri yürür. 0,25'lik pencere, baseline boyunca 0,1 toleranstan bilerek daha geniştir; böylece hafif eğik bir baseline üzerindeki çift, kesin baseline-çerçevesi testi çalışmadan önce içine düşer. İnce bir durum: A glifinden başlayan tarama, A'dan önce boyanmış eşleşen bir glifle karşılaşırsa kopya A'dır; A atılır ve tarama durur, daha erken glif daha ilerideki her şeyle karşılaştırmayı sürdürür. "İlk boyanan kazanır" kuralını çiftlerin yanında üçlü vuruşlar için de geçerli kılan bu kuraldır

Karşılaştırma font ya da karakter kodu üzerinden değil, Unicode değeri üzerinden yapılır. Farklı fontlar üzerinden aynı Unicode'a çıkan iki glif, örneğin bir gömülü subset'te dolgu turu ve bir başkasında kontur turu, yine de tek karakter olarak tanınır. Madalyonun öbür yüzü: kodu Unicode 0'a çözülen bir glifin güvenilir karşılaştırılacağı bir şey yoktur; bu da HotPDF ile yüklenmiş PDF'lerden metin çıkarma yazısında ele alınan çözülemeyen sayısına göz kulak olmanın bir nedeni daha

Hangi HotPDF çıkarma çağrıları mükerrer glifleri kaldırır?

HotPDF'de metin üreten bütün çağrılar kopyaları kaldırır ve ham glif çağrısı kaldırmaz. Tekilleştirme, sayfanın görünür kutusu dışındaki glifleri de atan adım olan THotPDF.ExtractVisibleLoadedPageGlyphs içinde yaşar ve düz metin, sütun, yerleşim ile semantik metin çıkarması gliflerini hep ondan okur. THotPDF.ExtractLoadedPageGlyphs ise content stream'in boyadığı her glifi, üst baskılar dâhil, döndürmeye devam eder; çünkü vurgulama, sansürleme ya da adli araç kuran çağıranların boyanan her şeyin eksiksiz kaydına ihtiyacı vardır

Sıradan metin işlerinde çağıran kodda hiçbir şey değişmez. v2.768.15 öncesi çift metin çıkaran aynı döngü artık her kelimeyi bir kez çıkarır

var
  Pdf: THotPDF;
  PageCount, I: Integer;
  PageText: UnicodeString;
begin
  Pdf := THotPDF.Create(nil);
  try
    PageCount := Pdf.LoadFromFile('brochure.pdf');
    for I := 0 to PageCount - 1 do
      if Pdf.ExtractLoadedPageText(I, PageText) then
        if Pos('Bold words', PageText) > 0 then
          Writeln('Found on page ', I + 1);
  finally
    Pdf.Free;
  end;
end;

Yerleşim ve sütun çıktısı da aynı ölçüde faydalanır; çünkü çoğaltılmış bir tur, bir satırın geri kalanını karakter ızgarasında yana itiyordu. ExtractLoadedPageTextColumns, isteğe bağlı boşluk çarpanını korur; kopya filtresi sütun bölmesinden önce çalışır, bu yüzden o ayarla etkileşmez

var
  Grid, Columns: UnicodeString;
begin
  if Pdf.ExtractLoadedPageTextLayout(0, Grid) then
    TFile.WriteAllText('page1-layout.txt', Grid, TEncoding.UTF8);
  // Sütunlar, bir boşluk medyan glif advance'inin 3 katını aştığında bölünür
  if Pdf.ExtractLoadedPageTextColumns(0, Columns, 3.0) then
    TFile.WriteAllText('page1-columns.txt', Columns, TEncoding.UTF8);
end;

Bir sayfanın hiç üst baskılı metin içerip içermediğini öğrenmek isterseniz iki glif çağrısını karşılaştırın. Farkın crop kutusunun dışına düşen glifleri de saydığını unutmayın; onu tam bir sayı değil, kopyalar için bir üst sınır gibi ele alın

var
  AllGlyphs, Visible: THPDFGlyphArray;
begin
  if Pdf.ExtractLoadedPageGlyphs(0, AllGlyphs) and
     Pdf.ExtractVisibleLoadedPageGlyphs(0, Visible) then
    Writeln(Format('%d painted, %d read as text (%d overprinted or off-page)',
      [Length(AllGlyphs), Length(Visible),
       Length(AllGlyphs) - Length(Visible)]));
end;

Bu glifleri metne çeviren kelime aralığı ve satır sonu kuralları filtrede etkilenmez; ayrıntıları HotPDF'in sayfa metninde kelime boşluklarını ve satır sonlarını nereye koyduğu yazısında ele alınır

Kuralın diğer araçlarla bilinçli olarak ayrıştığı yer

HotPDF, bazı diğer çıkarma araçlarının tuttuğu tam kopyaları atar ve bu fark kasıtlıdır. v2.768.15 değişikliği bir referans derlemine karşı denetlenirken, tam olarak aynı konumda iki kez boyanmış bir sayfa alt bilgisi HotPDF'de bir kez, bir referans çıkarıcıda iki kez çıkmıştı. Özdeş bir glifin üstüne boyanmış glif ayrı bir karakter olarak görünmez; bu yüzden onu bir kez okumak, sayfanın gerçekte gösterdiği şeydir

Dürüst sınır öbür yöne işler. İkinci turu baseline boyunca glif boyutunun 0,1'inden fazla kaydıran sahte bold yakalanmaz ve göze batan biçimde farklı bir boyutta çizilen gölge turu da yakalanmaz. O yerleşimler pratikte nadirdir; çünkü kaydırma o kadar büyüdüğünde görsel etki bold gibi görünmeyi bırakır. Ama dekoratif bir başlıktan çift metin görürseniz filtrenin bozuk olduğunu varsaymadan önce ExtractLoadedPageGlyphs'ten gelen glif X değerlerini karşılaştırın. Filtre ayrıca geometrik olarak aynı yerde olmayan kopyalara hiçbir şey yapmaz; sayfanın başka bir yerindeki gizli bir katmanda tekrarlanan akan bir başlık gibi. O bir içerik sorunudur, üst baskı değil

CID fontlu metinde satır sonları neden kesilmişti?

HotPDF v2.768.12'de düzeltilen ayrı bir hata, /W genişlik dizisi CID'e göre sıralanmamış CID anahtarlı (Type 0) fontlarla dizilmiş metinde satır sonlarını düşürüyordu. ISO 32000-1 §9.7.4.3, /W dizi biçimini tanımlar ama girişlerinin artan CID sırasında olmasını şart koşmaz ve bazı üreticiler onları gliflerin belgede ilk kullanım sırasına göre yazar

Metin çıkarma çözücüsü genişliklere ikili aramayla bakıyordu; o da girdinin sıralı olduğunu sessizce varsayar. Sıralanmamış bir dizide aramaların çoğu ıskalıyor ve varsayılan genişlik /DW'ye dönüyordu; o da 1000 birim, tam bir em. Böyle bir fonttaki Latin metin, sanki her harf tam genişlikteymiş gibi diziliyordu, satırlar sayfanın sağ kenarını epey aşıyordu ve görünür kutunun dışına düşen glifler atılıyordu; böylece satır sonları çıkarılan metinden ve aramadan kayboluyordu. Hiçbir şey çökmüyor ve hiçbir şey raporlanmıyordu; onu zor fark edilir yapan da buydu

HotPDF artık /W girişlerini okuduktan hemen sonra stabil bir merge sort ile sıralar; iki giriş çakıştığında önce yazılan kazanmaya devam eder ve dizi, dosya sırasındaki okunuşuyla uyumlu kalır. Sayfa render motoru hiç etkilenmedi; çünkü genişlik verisinin kendi kopyasını tutuyor ve onu doğrusal tarıyor, dikey genişlik dizisi /W2 de doğrusal taranıyor. Bir sayfa mükemmel görünürken kötü çıktığında bu ayrımı hatırlamaya değer: bir PDF kütüphanesinde rendering ile çıkarma, beklediğinizden çok daha az kod paylaşır

FAQ

HotPDF, "fill" içindeki çift l gibi tekrarlı harfleri kaldırır mı?

Hayır. İki gerçek bitişik harf kabaca bir advance genişliği kadardır; HotPDF ise yalnızca baseline boyunca glif boyutunun 0,1'i, dik yönde 0,2'si içindeki özdeş karakterleri birleştirir, bu yüzden meşru tekrarlar korunur

Üst baskılı kopyalar dâhil her glifi nasıl alırım?

THotPDF.ExtractLoadedPageGlyphs çağırın; sayfanın boyadığı her glifi boyama sırasıyla döndürür. THotPDF.ExtractVisibleLoadedPageGlyphs ve bütün metin çıkarma çağrıları, tekilleştirilmiş görünür kümeyi döndürür

Hangi HotPDF sürümü sahte bold kaynaklı çift metni düzeltir?

HotPDF v2.768.15, metin, yerleşim, sütun ve semantik metin çıkarmasına mükerrer glif kaldırmayı ekledi; v2.768.12 ise sıralanmamış /W dizili CID fontlarında kaybolan satır sonlarını düzeltti

İki düzeltme de güncel HotPDF Delphi PDF component ile gelir; o, PDF oluşturma, düzenleme ve rendering özelliklerinin yanında Delphi ve C++Builder'dan yüklenmiş PDF'lerden metin çıkarır