Teknik Makale

Düzenlemeden Sonra Bayat Metin: PDFium FPDF_TEXTPAGE Önbelleği

PDFiumPas ile bir PDF sayfasına bir satır damgalamak için AddText'i çağırırsınız, ardından damganın indiğini doğrulamak için hemen FindFirst'ı çağırırsınız ve arama boş döner. Metin sayfadadır -Acrobat onu gösterir- ama PDFiumPas'ın TPdf bileşeni, sayfanın içerik akışından bir kez ayrıştırılmış ayrı bir önbelleğe alınmış FPDF_TEXTPAGE yapısı tutar ve bir düzenleme o yapıyı kendiliğinden geriye dönük olarak güncellemez. Yenilenmeden önce sorgularsanız, sayfayı değişikliğinizden sonra değil önce göründüğü tam haliyle okursunuz

PDFium bir düzenlemeden hemen sonra neden bayat metin döndürür?

PDFiumPas, Delphi ve C++Builder için Google'ın PDFium render motorunu sarmalar ve metin ile düzenleme çağrıları o motorun içindeki iki farklı alt sisteme ulaşır. FPDF_TEXTPAGE okuma tarafına aittir: FPDFText_LoadPage, sayfanın içerik akışını bir kez dolaşır ve metin sayfasını -karakter kodları, konumlar, yazı tipi ölçütleri, kelime sınırları- kurar ve PDFiumPas o yapıyı sayfa yüklü kaldığı sürece önbelleğe alır. FPDFPage_InsertObject veya FPDFPage_GenerateContent gibi düzenleme çağrıları tamamen farklı bir temsil üzerinde çalışır, sayfanın nesne ve içerik akışı grafiği, ve PDFium bu değişiklikleri kendiliğinden zaten açık olan bir metin sayfasına itmez. Her düzenlemede yeniden kurmak toplu düzenlemeyi kabul edilemez şekilde yavaşlatırdı; bu yüzden tasarım o maliyeti bir kural karşılığında takas eder -tutamacı elinde tutan her kimse, içerik değiştiren bir düzenlemeden sonra onu kapatır ve bir sonraki okuma taze bir tane kurar

TPdf'in metin önbelleğinin içinde: FTextPage, LoadTextPage ve UnloadTextPage

TPdf, önbelleğe alınmış tutamacı tek bir özel alanda, FTextPage'de takip eder ve yaşam döngüsünü iki metotta sarmalar. LoadTextPage, FTextPage'in nil olup olmadığını kontrol eder ve yalnızca o durumda, mevcut sayfaya karşı FPDFText_LoadPage'i çağırır; bir tutamaç zaten varsa, LoadTextPage sayfa onu kurduğundan beri değişip değişmediğini sormadan onu yeniden kullanır. UnloadTextPage diğer yarısıdır: yerel tutamacı FPDFText_ClosePage ile kapatır, FTextPage'i geri nil'e ayarlar ve ayrıca önbelleğe alınmış web bağlantısı listesini ve devam eden herhangi bir bulma oturumunu da düşürür, çünkü ikisi de aynı metin sayfasından türetilmiştir ve aynı nedenle bayatlarlar

LoadTextPage'in kontrolsüz yeniden kullanma davranışı, sıralamanın neden önemli olduğunun tam nedenidir. TPdf üzerindeki her metin sorgusu -Text, FindFirst, GetWebLinks- önce LoadTextPage'den geçer; bu yüzden FTextPage hâlâ düzenleme öncesi tutamacı tutuyor olduğu sürece, bu çağrıların hiçbirinin bir değişikliğin gerçekleştiğini bilmesinin bir yolu yoktur. Sayfa gezinmesi burada hiçbir zaman risk değildi: sayfa geçişlerinde, yeniden yüklemelerde ve belge kapanışında çalışan UnloadPage, her zaman metin sayfasını sayfanın kendisiyle birlikte kapattı. Açık soru her zaman hâlâ üzerinde oturduğunuz sayfaya uygulanan düzenlemeler hakkındaydı

Hangi PDFiumPas metotları önbelleği otomatik olarak yeniler?

TPdf'in kendi sayfa düzenleme metotları -AddText, SetText, SetTextPositions, AddPath, RemoveObject ve InsertFormObjectFromXObject- değişikliği içerik akışına serileştirmek için UpdatePage'i (PDFium'un FPDFPage_GenerateContent'i) çağırmadan önce her biri UnloadTextPage'i çağırır. Bunlardan herhangi birini çağırın ve bir sonraki Text, FindFirst veya GetWebLinks çağrısı, sizin tarafınızdan gereken hiçbir ekstra çağrı olmadan, içeriği şu anda durduğu haliyle metin sayfasını yeniden kurar

var
  Pdf: TPdf;
  Index: Integer;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'invoice.pdf';
    Pdf.Active := True;
    Pdf.PageNumber := 1;

    Pdf.AddText('Reviewed by J. Alvarez', 'Helvetica', 10, 72, 40, clBlack, 255, 0);
    // AddText already closed the cached text page, so this FindFirst
    // call rebuilds it fresh before it searches
    Index := Pdf.FindFirst('Reviewed by J. Alvarez');
    if Index >= 0 then
      ShowMessage('Stamp confirmed at character ' + IntToStr(Index));
  finally
    Pdf.Free;
  end;
end;

Hâlâ bozulan örüntü: ham TextPage tutamacını önbelleğe almak

TPdf, PDFiumPas'ın sarmalamadığı bir FPDFText_* fonksiyonunu çağırmanız gereken nadir durum için, canlı tutamacı salt okunur bir TextPage özelliği üzerinden sunar. Bu çıkış kapağı, otomatik geçersizleştirmenin yardımcı olamadığı tek yerdir de: FPDF_TEXTPAGE değerini özellikten yerel bir değişkene bir kez kopyaladığınızda, PDFiumPas'ın onu hâlâ tuttuğunuzu bilmesinin hiçbir yolu yoktur ve kodunuzda başka bir yerde UnloadTextPage çalıştığında kopyanızı güncellemesinin de hiçbir yolu yoktur

var
  Pdf: TPdf;
  RawHandle: FPDF_TEXTPAGE;
  StaleCount: Integer;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'contract.pdf';
    Pdf.Active := True;
    Pdf.PageNumber := 1;

    RawHandle := Pdf.TextPage;    // FPDFText_LoadPage handle, cached in FTextPage
    Pdf.SetText(0, 'Amended Clause 4.2');
    // SetText already closed RawHandle and set Pdf.TextPage back to nil.
    // Calling any FPDFText_* function against the old value now touches a
    // handle PDFium has already freed — undefined behavior, not a bug you
    // can catch with a nil check
    StaleCount := FPDFText_CountChars(RawHandle);
  finally
    Pdf.Free;
  end;
end;

FPDFText_ClosePage onun üzerinde çalıştıktan sonra bir tutamacı kullanmak, göz ardı etmeyi seçebileceğiniz bir PDFiumPas kuralı değil, PDFium'un kendisinde tanımsız davranıştır -son bilinen veriyi döndürebilir, hiçbir şey döndürmeyebilir veya süreci çökertebilir ve belirli bir derlemede bunlardan hangisinin olacağı, uygulama kodunun bağımlı olması gereken bir şey değildir. Güvenli kural dardır: Pdf.TextPage'i, ihtiyaç duyan FPDFText_* çağrısından hemen önce taze okuyun ve sayfayı düzenleyebilecek bir ifadenin ötesinde asla bir kopyasını tutmayın

Düzenlemelerinizi toplulaştırın, sonra bir kez sorgulayın

Bunların hiçbiri her AddText veya RemoveObject çağrısının sonucu kontrol etmek için hemen ardından savunmacı bir metin sorgusuna ihtiyaç duyduğu anlamına gelmez. Her düzenleme metodu metin sayfasını bir kez kapatmanın maliyetini zaten öder; bir döngü içindeki her tek düzenlemeden sonra sorgulamak, FPDFText_LoadPage her çalıştığında tüm içerik akışını yeniden dolaştığından, hiçbir fayda için o maliyeti tekrar öder

var
  Pdf: TPdf;
  I: Integer;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'watermarked.pdf';
    Pdf.Active := True;
    Pdf.PageNumber := 1;

    // Strip every text object that looks like a draft watermark. Each
    // RemoveObject call already invalidates the cache on its own, so
    // nothing needs refreshing by hand between iterations
    for I := Pdf.ObjectCount - 1 downto 0 do
      if (Pdf.ObjectType[I] = otText) and (Pdf.ObjectBounds[I].Top > 700) then
        Pdf.RemoveObject(I, True);

    // Query once, after the whole batch is done, not once per removal
    if Pdf.FindFirst('DRAFT') < 0 then
      ShowMessage('Watermark cleared');
  finally
    Pdf.Free;
  end;
end;

Aynı toplulaştırma mantığı özellikle arama durumu için de geçerlidir. FindNext ve FindPrevious, FindFirst tarafından başlatılan bir oturuma devam eder ve o oturum, her şeyle birlikte UnloadTextPage tarafından yıkılır; bu yüzden bir düzenlemeden sonra tekrar FindFirst'ı çağırmak yerine tekrar FindNext'i çağırmak, artık var olmayan içeriğe karşı sessizce bir aramaya devam etmek yerine bir istisna fırlatır. Herhangi bir düzenlemeyi hem metin içeriği hem de arama konumu için sert bir sınır olarak ele alın ve düzenlemelerinizin öte tarafında taze bir FindFirst'ın aramayı yeniden almasına izin verin

Bu, çıkarma ve açıklama işiyle nereye oturuyor?

Düz metin çıkarma -hiçbir şeyi değiştirmeden bir sayfanın metnini okumak- bunların hiçbirine hiç çarpmaz, çünkü hiçbir düzenlemenin dokunmadığı bir tutamacı hiçbir şey geçersiz kılmaz. Text, karakter dikdörtgenleri ve kelime sınırlarının değiştirilmemiş bir sayfada nasıl çalıştığı için, PDFiumPas ile metin çıkarma üzerine tamamlayıcı makale, bu makalenin üzerine eklediği metin sayfası önbelleği yaşam döngüsü olmadan o zemini kapsar

Önbellek yaşam döngüsü en çok düzenleyip sonucu hemen üzerinde işlem yapan iş akışlarında önemlidir: bir düzeltmeyi damgalayıp onu aramak, bir paragrafı karartıp gittiğini doğrulamak veya yakınına metin eklendikten hemen sonra bir işaretleme açıklamasını çapalamak için bir ifadeyi bulmak. Bu son durum kendi başına işaretlenmeye değer -dörtgen-nokta işaretleme açıklamaları metin sayfasından okunan karakter dikdörtgenlerinden konumlandırılır; bu yüzden bir düzenlemeden önce yakalanan koordinatlardan kurulan bir açıklama, düzenleme indiğinde yanlış noktayı vurgulamış olur

TPdf'in düzenleme ve metin API'leri, Delphi ve C++Builder için PDFium Bileşeni'nin bir parçasıdır ve ürün sayfası, burada ele alınan düzenleme, çıkarma ve arama yüzeyleri için tam metot referansını taşır