Teknik Makale

PDF Fast Web View: Doğrusallaştırma Neyi Değiştirir

Bağlantıdan 200 sayfalık bir PDF açın; tarayıcı, dosyanın büyük bölümü gelene kadar çoğu zaman boş kalır. Fast Web View, doğrusallaştırılmış bir PDF'nin Adobe Acrobat'taki adıdır (ISO 32000-1 Ek F): birinci sayfa ve küçük bir indeks öndedir; böylece görüntüleyici birinci sayfayı erken çizer ve sonraki sayfaları HTTP Range isteğiyle getirir. HotPDF de PDFium Component da Delphi'den doğrusallaştırılmış dosyalar yazabilir

Bunların hiçbiri ayrı bir dosya biçimi değildir. Doğrusallaştırılmış bir PDF, uyumlu herhangi bir okuyucunun özel işlem yapmadan açtığı sıradan bir PDF'dir; fark, bayt sırasında ve iki ek yapıda, yani doğrusallaştırma parametre sözlüğü ile ipucu akışındadır (hint stream). Bir okuyucunun bu yararı hiç hissedip hissetmeyeceğine iki koşul karar verir. Dosya doğrusallaştırılmış olmalı ve öyle kalmalıdır; sonradan eklenen bir düzenleme bunu sessizce bozar. Web sunucusu da bayt aralığı isteklerini 206 Partial Content ile yanıtlamalıdır. Dosyanın sonuna aramanın bedava olduğu yerel bir diskte doğrusallaştırılmış bir PDF tam olarak ötekiler gibi davranır; dolayısıyla Fast Web View, doğrudan bir URL'den açılan büyük belgelerde karşılığını veren salt bir ağ optimizasyonudur

Doğrusallaştırılmış bir PDF neyi yeniden düzenler?

Doğrusallaştırılmış bir PDF, birinci sayfanın ihtiyaç duyduğu her şeyi dosyanın başına yakın bitişik bir blokta, öbür tüm sayfaların önünde toplar. Sıradan bir PDF'de nesneler herhangi bir sırada durabilir; çünkü sondaki çapraz başvuru tablosu her nesne numarasını bayt ofsetine eşler: okuyucu kuyruğa gider, startxref işaretçisini okur, tabloyu yükler ve ardından her şeyi bulabilir (PDF dosya yapısına genel bakış o başlık, gövde, xref ve trailer düzenini tek tek anlatır). Tasarım diskte mükemmel, ağda zayıftır; çünkü ağda tam da en son gelen kısım kuyruktur. Birinci sayfayı çizmek için geleneksel bir okuyucu sayfa nesnesine, içerik akışına ve kullandığı fontlarla resimlere ihtiyaç duyar ve düzensiz bir dosyada bunların herhangi biri son megabaytta durabilir

Doğrusallaştırma sırayı düzeltir. Başlığın ardından doğrusallaştırma sözlüğü ile küçük bir birinci sayfa çapraz başvuru bölümü, sonra katalog gibi belge düzeyi nesneler, birincil ipucu akışı ve tüm kaynaklarıyla birinci sayfa gelir. Kalan sayfalar teker teker izler; ardından sonraki sayfalar arasında paylaşılan nesneler ve en sonda ana çapraz başvuru tablosu gelir. Optimizasyonu yok sayan bir okuyucu hâlâ sondan başlar ve her nesneyi ana tablo üzerinden bulur; doğrusallaştırılmış bir dosya bu yüzden eski yazılımlarla tam uyumluluğunu korur. Bunun sonucunda nesne numaralandırması bir hex düzenleyicide ters görünür: öndeki blok en yüksek numaraları taşır, arkasındaki her şey 1'den itibaren numaralanır

Fast Web View sağlayan doğrusallaştırılmış bir PDF ile sıradan bir PDF'nin bayt düzeni karşılaştırması
Doğrusallaştırılmış bir dosya, doğrusallaştırma sözlüğünü, birinci sayfa xref'ini ve ipucu akışını her şeyin önüne taşır; sıradan bir PDF ise tek indeksini dosyanın kuyruğunda tutar

Doğrusallaştırma sözlüğü ve ipucu tabloları

%PDF başlığından sonraki ilk nesne, doğrusallaştırma parametre sözlüğüdür ve Ek F, onun ilk 1024 bayt içinde oturmasını gerektirir; böylece akışla gelen okuyucu açılış paketinde onu bulur. Tablo F.1 girdilerini listeler: /L bütün dosyanın bayt cinsinden uzunluğu, /H birincil ipucu akışının ofseti ve uzunluğu, /O birinci sayfanın nesne numarası, /E birinci sayfanın bittiği ofset, /N sayfa sayısı ve /T ana çapraz başvuru tablosundaki ilk girdinin ofsetidir. /L girdisi göründüğünden daha ağırdır. Gerçek dosya boyutuyla uyuşmadığında Tablo F.1, okuyucuya belgeye sıradan bir PDF muamelesi göstermesini ve içindeki her ipucunu yok saymasını söyler

40. sayfaya atlamayı ucuzlatan şey ipucu akışıdır. İçinde, her sayfa için nesnelerinin nerede başladığını ve kaç bayt kapladığını kaydeden bir sayfa ofset ipucu tablosu ile bunu birden çok sayfada kullanılan fontlar, logolar ve öbür kaynaklar için yapan bir paylaşılan nesne ipucu tablosu taşır. 40. sayfayı isteyen okuyucu bayt aralığına bakar, tam o dilimi ister ve dilim gelir gelmez çizer; hâlâ elinde olmayan paylaşılan nesneleri de aynı yolla çeker. Ev yapımı doğrusallaştırıcılar iki ayrıntıda takılır. İpucu ofsetleri mantıksaldır; ipucu akışının kendisi yokmuş gibi hesaplanır, dolayısıyla fiziksel ofsetleri yazan bir yazıcı sonraki her aralık isteğini ipucu nesnesinin boyutu kadar saptırır. Ve kaynaklarını toplarken bir sayfanın /Parent bağını izleyen yazıcı, sayfa ağacında geriye doğru yürür ve bütün belgeyi birinci sayfa bölümüne sürükler. Her iki dosya da hâlâ doğrusallaştırılmış olduğunu iddia eder. Ne biri daha hızlı yüklenir ne öteki

Fast Web View neden sunucunun yardımına ihtiyaç duyar?

Fast Web View yalnızca sunucu, dosyanın rastgele dilimlerini istek üzerine teslim ediyorken çalışır. Okuyucu Range başlıklı HTTP istekleri gönderir ve karşılığında 206 Partial Content bekler; sunucu hiç Accept-Ranges: bytes duyurmadıysa veya önündeki bir proxy ya da CDN aralık isteklerini tam aktarımlara dönüştürüyorsa, okuyucu 40. sayfayı kendi başına getiremez ve bütün dosyayı indirir. PDF'nin içindeki yapı o an doğrudur ve tamamen boşa gitmiştir; en sık doğrusallaştırma bozuk diye yanlış teşhis edilen arıza da budur. Bir belgeyi yeniden oluşturmadan önce, görüntüleyicinin açtığı tam URL'nin ilk kilobaytını isteyin, örneğin curl -r 0-1023 ile, ve durumun 206 olduğunu doğrulayın. Statik dosya sunucuları bunu genellikle varsayılan olarak doğru yapar; PDF'leri kendi işleyicilerinden aktaran uygulama uç noktaları ise sık sık yapmaz

Doğrusallaştırma ipucu tablolarının, Fast Web View PDF'nin bir sayfası için 206 Partial Content döndüren bir HTTP aralık isteğini sürmesi
İpucu tabloları okuyucuya herhangi bir sayfanın bayt aralığını verir; böylece aralık yeteneği olan bir ana makine 40. sayfayı tek kısmi yanıtla sunar. Bayt servisi olmadan bütün dosya yeniden indirilir

Bir PDF'nin Fast Web View'i olup olmadığını nasıl anlarsınız?

Dosyayı üreten araca güvenmek yerine baytları denetleyin ve koşulun iki yarısını da denetleyin: dosya kendini doğrusallaştırılmış ilan eder ve o ilan diskteki dosyayla hâlâ örtüşür. Masaüstünde hızlı bir bakımdan yayımlama hattındaki otomatik bir kapıya kadar hemen her durumu üç denetim kapsar:

  • Adobe Acrobat veya Reader: File > Properties'i açın; Description sekmesi sağ alt köşede Fast Web View: Yes ya da No gösterir. Acrobat Yes'i yalnızca yapı hem mevcut hem güncelse bildirir; dolayısıyla doğrusallaştırmadan sonra düzenlenmiş bir dosya No gösterir
  • Komut satırında qpdf: qpdf --check-linearization ipucu tablolarını gerçek nesne konumlarına karşı doğrular, qpdf --show-linearization ise sözlüğü ve her ipucu tablosu girdisini döker
  • Dosyanın ilk kilobaytı: dosyayı bir metin ya da hex düzenleyicide açın ve ilk 1024 bayt içinde /Linearized 1 taşıyan bir nesne arayın; oradaysa /L değerini işletim sisteminin bildirdiği dosya boyutuyla karşılaştırın
# Doğrusallaştırma sözlüğünü ve ipucu tablolarını düzene karşı doğrular
qpdf --check-linearization report-web.pdf

# /L, /H, /O, /E, /N, /T ve her ipucu tablosu girdisini döker
qpdf --show-linearization report-web.pdf

qpdf denetimi, ipucu tablolarını gerçek nesne konumlarıyla uzlaştırdığı için bir hatta bağlamaya değer olandır; yalnızca sözlüğün varlığını teyit eden bir denetim, ofsetleri yanlış baytlara işaret eden bir dosyayı da onaylar. Delphi tarafında HotPDF'deki THotPDF.IsLoadedLinearized, yüklenmiş bir belgenin kendini doğrusallaştırılmış ilan edip etmediğini bildirir; ayrıştırmadan önce ilk 1024 baytı /Linearized için tarar. PDFium Component aynı soruyu TPdf.InspectAvailability sınıf işleviyle yanıtlar; Linearization alanı belgeyi hiç yüklemeden plnLinearized, plnNotLinearized ya da plnUnknown döndürür. İki bayrak da dosyanın ne ilan ettiğini bildirir; o ilanın diskteki baytlarla hâlâ örtüşüp örtüşmediğini /L karşılaştırması söyler:

uses
  System.SysUtils, System.Classes, System.Math, System.StrUtils, PDFium;

type
  TFastWebView = (fwvNo, fwvStale, fwvYes);

// Kendi yardımcınız: PDFium, dosyanın doğrusallaştırılmış olup olmadığına
// karar verir; /L karşılaştırması sonradan eklenen düzenlemeleri yakalar
function FastWebViewState(const FileName: string): TFastWebView;
var
  Stream: TFileStream;
  Head: AnsiString;
  Text: string;
  P, Q: Integer;
begin
  Result := fwvNo;
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    if TPdf.InspectAvailability(Stream).Linearization <> plnLinearized then
      Exit;
    SetLength(Head, Min(Stream.Size, 1024));   // Ek F: ilk 1024 bayt
    Stream.Position := 0;
    if Length(Head) > 0 then
      Stream.ReadBuffer(Head[1], Length(Head));
    Text := string(Head);
    P := Pos('/Linearized', Text);
    if P = 0 then
      Exit;
    P := PosEx('/L ', Text, P + Length('/Linearized'));
    if P = 0 then
      Exit;
    Inc(P, 3);
    Q := P;
    while (Q <= Length(Text)) and CharInSet(Text[Q], ['0'..'9']) do
      Inc(Q);
    if StrToInt64Def(Copy(Text, P, Q - P), -1) = Stream.Size then
      Result := fwvYes
    else
      Result := fwvStale;   // bir kez doğrusallaştırılmış, sonra düzenlenmiş
  finally
    Stream.Free;
  end;
end;

Fast Web View'i Acrobat'ta, qpdf'de ve Delphi'de açmak

Ek F sırasında bütün dosyayı yeniden yazan her araç Fast Web View'i açabilir ve asıl önemli olan, onun en son koşmasıdır. Acrobat'ta Preferences > Documents altında Save As optimizes for Fast Web View seçeneğini açın ve sonra Kaydet yerine File > Save As kullanın; çünkü düz bir Kaydet, artımlı bir güncelleme ekleyebilir. Sunucuda ya da bir derleme betiğinde qpdf --linearize in.pdf out.pdf, herhangi bir PDF'i doğrusallaştırılmış biçimde yeniden yazar ve girdiye dokunmaz. Delphi kodunun, belgeyi üretmenize mi yoksa hazır almanıza bağlı olarak iki yolu vardır. HotPDF, THotPDF.LinearizeOutput True iken doğrusallaştırılmış çıktıyı doğrudan yazar; diğer çıktı seçenekleriyle birlikte BeginDoc öncesinde ayarlayın ve Ek F düzeni EndDoc içinde hesaplanıp yazılır:

qpdf --linearize report-final.pdf report-web.pdf
var
  PDF: THotPDF;
begin
  PDF := THotPDF.Create(nil);
  try
    PDF.AutoLaunch := False;       // EndDoc sonrası bir görüntüleyici açma
    PDF.FileName := 'catalog-web.pdf';
    PDF.LinearizeOutput := True;   // BeginDoc öncesinde ayarlanır
    PDF.BeginDoc;                  // BeginDoc sonrası 1. sayfa mevcuttur
    PDF.CurrentPage.TextOut(72, 72, 0, 'Spring catalog');
    PDF.AddPage;
    PDF.CurrentPage.TextOut(72, 72, 0, 'Price list');
    PDF.EndDoc;                    // doğrusallaştırılmış düzen burada yazılır
  finally
    PDF.Free;
  end;
end;

Zaten var olan dosyalar için PDFium Component TPdf.SaveAsLinearized sağlar. Yöntem, sıradan PDFium kaydını bir ara depolamaya çalıştırır; böylece sayfa düzenlemeleri ve form değerleri herhangi bir başka kaydetmeyle aynı yoldan geçer, ardından hedefinizi yazmadan önce o çıktıyı Ek F'ye göre yeniden yapılandırır. Yöntem, SaveAs ile aynı TSaveOption ve TPdfVersion parametrelerini alır, saIncremental ile PDF 1.2 altındaki her sürümü reddeder ve dosya adı overload'u, kaydetme başarılı olana kadar hedefin yerine konmayan geçici bir dosyaya yazar. Yazıcı dosyayı güncel revizyondan yeniden kurduğu için bu, sonradan eklenmiş düzenlemeler kapmış bir dosyada Fast Web View'i geri yüklemenin de yoludur. Şifreli girdi saRemoveSecurity ister: nesneleri yeniden numaralandırmak, standart güvenlik işleyicisinin nesne başına anahtarlarını geçersiz kılar (ISO 32000-1 §7.6.2); dolayısıyla web kopyası şifreleme olmadan yazılır

var
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'manual-rev3.pdf';   // sonradan eklenmiş düzenlemeler taşır
    Pdf.Active := True;
    if not Pdf.SaveAsLinearized('manual-web.pdf', saNoIncremental) then
      raise Exception.Create('Linearized save failed');
  finally
    Pdf.Free;
  end;
end;

Artımlı güncellemeler Fast Web View'i sessizce kapatır

Artımlı güncelleme, doğru doğrusallaştırılmış bir dosyanın daha sonra Fast Web View'i kaybetmesinin en yaygın nedenidir. Bir araç, artımlı kaydetme yoluyla bir imza eklediğinde, bir form alanını doldurduğunda ya da bir not iliştirdiğinde orijinal baytlara dokunmaz; değişen nesneleri, yeni bir çapraz başvuru bölümünü ve yeni bir trailer'ı sona ekler. Öndeki doğrusallaştırma sözlüğü hâlâ eski düzeni tanımlar, /L artık dosya boyutuyla uyuşmaz ve uyumlu bir okuyucu, doğrusallaştırılmış yapı dosyanın ilk bölümünde sapasağlam dururken belgeye sıradan bir PDF gibi davranır. HotPDF tam da bu tür revizyonları BeginIncrementalUpdate ve SaveIncrementalUpdate üzerinden ekler; bunlar belge hâlâ değişirken doğru araç, bir web sunucusuna gidecek dosya için yanlış son adımdır:

// Bir revizyon ekler: hızlıdır, önceki baytları sağlam tutar
// ve sonucu Fast Web View'siz bırakır
PDF.BeginIncrementalUpdate('report.pdf');
PDF.AddPage;
PDF.CurrentPage.TextOut(72, 760, 0, 'Addendum');
PDF.SaveIncrementalUpdate('report-rev2.pdf');
Artımlı güncellemeler, son bir tam yeniden yazım Fast Web View'i geri yükleyene dek doğrusallaştırma sözlüğünü eşzaman dışı bırakan revizyonlar ekler
Her artımlı kaydetme, bayat ön sözlüğün ötesine bir revizyon daha dizer; tam bir yeniden yazımla bitirmek ve yeniden doğrusallaştırmayı sona bırakmak Fast Web View'i sağlam tutar

İşe yarar kural şudur: belge değişim halindeyken artımlı düzenleyin, doğrusallaştırmayı en son adım olarak bir kez yapın ve kimse açmadan e-postayla gönderilen ya da indirilen dosyalar için Fast Web View'i kapalı bırakın; onlar, daha büyük önden yüklenmiş indeksten bir şey kazanmaz. İmzalı belgeler gerçek bir seçim dayatır: tam bir yeniden yazım, her imzanın /ByteRange'inin kapsadığı baytları değiştirir ve sonradan imzalama, /L uyuşmazlığını olduğu gibi geri getiren bir güncelleme ekler; dolayısıyla sözleşmelerde imzayı koruyun ve tam indirmeyi kabul edin. Ek F'nin yazma tarafı — ilk bayt yazılmadan önce ofsetlerin nasıl yakınsadığı ve doğrusallaştırılmış çıktının neden nesne akışlarından vazgeçtiği dahil — HotPDF ipucu tablolarıyla doğrusallaştırılmış PDF yazma ve Delphi'de PDFium Component ile Fast Web View doğrusallaştırma makalelerinde kapsanır. LinearizeOutput ile IsLoadedLinearized HotPDF Delphi Component ile gelir; SaveAsLinearized ve InspectAvailability ise Delphi, C++Builder ve Lazarus için PDFium Component'in parçasıdır