HotPDF, Acrobat'ın Fast Web View olarak etiketlediği düzen olan linearize edilmiş PDF dosyalarını, THotPDF üzerindeki LinearizeOutput özelliği aracılığıyla yazar. Bunu BeginDoc'tan önce ayarlamak, HotPDF'in tamamlanmış nesne grafiğini yeniden sıralamasını sağlar, böylece byte aralığından haberdar bir okuyucu, tüm belgeyi önce indirmek yerine, yalnızca dosyanın baş kısmını getirdikten sonra ilk sayfayı görüntüleyebilir. Mekanizma ISO 32000-1 Ek F'dir
Bunun önemli olmasının nedeni gösterişsizdir. Normal bir PDF, çapraz referans tablosunu sonuna koyar, dolayısıyla bir görüntüleyici herhangi bir şeyin nerede olduğunu bilmeden önce son bayta ulaşmalıdır. Bir tarayıcıya 200 sayfalık taranmış bir rapor verin, kullanıcı tek istediği sayfa 1 olsa bile tüm aktarım boyunca bir yükleme animasyonuna bakar. Linearizasyon bunu yazma anında bir maliyet ödeyerek çözer. Bu makale özellikle o yazma yolunu, bölümlemeyi, ölçüm döngüsünü ve sert sınırları ele alır; Fast Web View'ın size ne kazandırdığına dair kavramsal arka plan için daha önceki PDF linearizasyonu ve Fast Web View açıklaması o zemini kapsar
Linearize edilmiş düzen gerçekte neyi garanti eder?
Linearize edilmiş bir dosya, son derece belirli bir fiziksel sıralamaya sahip sıradan bir PDF'dir ve sunduğu her garanti, yeni bir nesne türünden değil bu sıralamadan gelir. HotPDF, parçaları Ek F'nin öngördüğü sırayla yayar: ilk 1024 bayt içinde linearizasyon parametre sözlüğü, erken bir çapraz referans tablosu, belge düzeyi nesneler, birincil hint akışı, ilk sayfa ve onun özel nesneleri, ardından kalan sayfalar, ardından paylaşılan nesneler, ardından geri kalan her şey ve son olarak ana çapraz referans tablosu
Bölümleme türetilmiştir, bildirilmiş değildir. HotPDF, referans grafiğini her sayfa nesnesinden dolaşır ve her dolaylı nesne için kaç sayfanın ona eriştiğini ve hangi sayfanın ona ilk eriştiğini kaydeder. Tam olarak bir sayfa tarafından kullanılan bir nesne o sayfaya özel olur. Birden fazla sayfa tarafından erişilen bir nesne paylaşılan olur. Katalog, artı /ViewerPreferences, /OpenAction, /Threads ve /AcroForm altında referans verdiği her şey, artı koruma etkinken şifreleme sözlüğü, her şeyden önce gelmesi gereken belge düzeyi grubu oluşturur. Sayfa ağacı düğümleri, ilk sayfa bölümünü kirletmesinler diye kasıtlı olarak geride tutulur
Parametre sözlüğü, bir okuyucunun başka hiçbir şeyi okumadan önce ihtiyaç duyduğu sayıları taşır: toplam dosya uzunluğu için /L, hint akışının uzaklığı ve uzunluğu için /H, ilk sayfanın nesne numarası için /O, ilk sayfa bölümünün bittiği byte için /E, sayfa sayısı için /N ve ana çapraz referans tablosu girdisinin uzaklığı için /T. Bunların her biri, onları yazmanız gerektiği anda henüz var olmayan bir dosyaya byte uzaklığıdır
Hint tablosu uzaklıkları neden yakınsamalı?
Çünkü parametre sözlüğündeki sayılar, onları içeren dosyayı tanımlar ve bunlardan herhangi birini değiştirmek dosyayı değiştirir. Bu, linearize edilmiş bir yazıcının merkezi zorluğudur ve HotPDF'in bir kez yazmak yerine tekrar tekrar ölçmesinin nedeni budur. /T'yi 6 haneden 7 haneye genişletin, parametre sözlüğü bir bayt büyür; başlık büyür; her nesne kayar; ana çapraz referans tablosu taşınır; /T artık farklı bir değere ihtiyaç duyar. Düzen, gerçek çıktının tek bir baytı taahhüt edilmeden önce sabit bir noktaya ulaşmalıdır
HotPDF bunu sınırlı bir yinelemeyle ele alır. Önce her nesneyi, baytları tutmadan uzunluğu kaydeden sayan bir akışa serileştirir, böylece her nesnenin bilinen bir serileştirilmiş boyutu olur. Ardından belge düzeyi gruba, hint akışına, ilk sayfa grubuna, sonraki sayfa gruplarına, paylaşılan gruba ve geri kalanına uzaklıklar atayan bir düzen geçişi çalıştırır ve ana çapraz referans tablosunun nereye ineceğini bildirir. O sonuç, bir sonraki geçişe girdi olarak geri beslenir. Döngü sekiz denemeyle sınırlıdır ve yakınsamama, makul görünen yanlış uzaklıklara sahip bir dosya üretmek yerine bir istisna fırlatır
CandidateMainOffset := 0;
for Attempt := 0 to 7 do
begin
CalculateLayout(CandidateMainOffset, FirstXRefData,
HintOffset, EndFirstPage, NewMainOffset);
if NewMainOffset = CandidateMainOffset then
Break;
CandidateMainOffset := NewMainOffset;
end;
if NewMainOffset <> CandidateMainOffset then
raise Exception.Create('Linearization layout did not converge');
İki ayrıntı döngüyü çırpınmaktan alıkoyar. Parametre sözlüğü, boşluklarla doldurulmuş sabit 384 baytlık bir yuvaya yazılır, dolayısıyla kendi büyümesi düzeni asla dengesizleştiremez; sözlük metni bu rezervasyonu aşarsa HotPDF her şeyi sessizce kaydırmak yerine bir istisna fırlatır. Ve yakınsamadan sonra HotPDF bir doğrulayıcı düzen geçişi daha çalıştırır ve hint akışı uzunluğunu yeniden kontrol eder, çünkü hint akışının kendisi düzen sabitlendiğinde ancak bilinen uzaklıkları kodlar. Tüm bu ölçümün getirisi, HotPDF'in belgenin ikinci bir kopyasını asla arabelleğe almamasıdır: uzaklıklar sabitlendiğinde, nesneler doğrudan hedef akışa serileştirilir, her bölüm sınırında yazılan baytların vaat edilen uzaklıkla eşleştiğine dair bir doğrulamayla
Delphi'den etkinleştirmek
API yüzeyi tek bir Boolean'dır ve tek gereksinimi üretim başlamadan önce ayarlamanızdır. LinearizeOutput varsayılan olarak False'dır ve düzen geçişi belge yazıldığında çalışır, dolayısıyla onu EndDoc'tan sonra atamak hiçbir şey başarmaz
var
PDF: THotPDF;
begin
PDF := THotPDF.Create(nil);
try
PDF.FileName := 'fast-view.pdf';
PDF.Version := pdf17;
PDF.LinearizeOutput := True; // must precede BeginDoc
PDF.BeginDoc;
PDF.Canvas.TextOut(72, 72, 'First page');
PDF.EndDoc;
finally
PDF.Free;
end;
end;
Bir dağıtım uyarısı, kod tarafındaki her şeyi geride bırakır. Linearizasyon yalnızca taşıma HTTP aralık isteklerini desteklediğinde kazanç sağlar. Aynı dosyayı onu bütün olarak akıtan bir uç noktadan, ya da Range'i yok sayan bir CDN yapılandırmasından sunun, ve kullanıcıya görünür hiçbir kazanç olmadan kendinize daha yavaş bir yazma yolu ve daha büyük bir dosya satın almış olursunuz. Kodu kontrol etmeden önce sunucuyu kontrol edin
Linearizasyon UseXRefStream ve UseObjectStreams'i neden geçersiz kılar?
Çünkü linearize edilmiş yazıcının her nesnenin kendi doğrudan adreslenebilir byte uzaklığına sahip olmasına ihtiyacı vardır ve bu iki özellik de bunu ortadan kaldırır. HotPDF bu yüzden, çağıran UseXRefStream veya UseObjectStreams'i de ayarlamış olsa bile, LinearizeOutput etkinleştirildiğinde geleneksel metin çapraz referans tablolarını ve paketlenmemiş dolaylı nesneleri yayar. Bu kasıtlı bir geçersiz kılmadır, kendinizin çözmesi gereken bir çakışma değildir
Gerekçe hint tablolarından gelir. Bir hint tablosu, bir sayfa bölümünün nerede başladığını ve ne kadar uzun olduğunu tanımlar, böylece bir okuyucu tam olarak o aralığı isteyebilir. Bir /ObjStm konteynerine paketlenmiş bir nesnenin hiç bağımsız bir uzaklığı yoktur; yalnızca bir birim olarak getirilip açılması gereken başka bir sıkıştırılmış akışın içinde bir dilim olarak var olur. Dosya boyutu için nesne akışlarına güveniyorsanız, linearizasyon ve sıkıştırmanın burada zıt yönlere çektiğini anlayın ve HotPDF'de nesne akışları ve artımlı güncellemeler yazısındaki değiş tokuşu okuyun. Aynı gerilim, tam olarak eski okuyucuları akış tabanlı tablolarla birlikte çalışır tutmak için var olan melez referanslı dosyaları şekillendirir, Office tarafından üretilen PDF'lerdeki melez çapraz referans akışları üzerine makalede ele alındığı gibi
Bir sürüm alt sınırı da vardır. Linearizasyon PDF 1.2 veya daha yenisini gerektirir. Seçilen sürüm daha eskiyse, HotPDF onu otomatik olarak yükseltir, ancak StrictVersionLock ayarlanmışsa bu durumda yazma, kasıtlı olarak sabitlediğiniz bir belgeyi sessizce yükseltmek yerine bir istisna fırlatır
4 GiB duvarı ve HotPDF'in kesmek yerine neden reddettiği
Linearizasyon hint tabloları uzaklıkları 32 bit değerler olarak saklar, dolayısıyla linearize edilmiş bir dosya 4 GiB'de veya ötesinde hiçbir şeyi adresleyemez ve HotPDF, sarılmış uzaklıklara sahip bir dosya yazmak yerine böyle bir çıktıyı açık bir istisnayla reddeder. Sınır bir HotPDF uygulama tercihi değildir; Ek F'nin tanımladığı alanların genişliğidir
Kontrol üç yerde uygulanır ve üçü de önemlidir. HotPDF, serileştirilmiş uzunluğu bilindiğinde her nesneyi doğrular, hint girdilerini oluştururken her sayfa bölümü uzunluğunu doğrular ve ana çapraz referans tablosu boyutlandırıldıktan sonra son dosya uzunluğunu doğrular. Erken başarısız olmak tüm amaçtır: sessizce kesilmiş bir uzaklığa sahip bir hint tablosu, onu bütün olarak indiren bir görüntüleyicide doğru şekilde açılan ve yalnızca linearizasyonun var olma nedeni olan byte-aralığı istemcisi için başarısız olan bir dosya üretir, ki bu en kötü olası hata modudur çünkü test görüntüleyiciniz onu asla yeniden üretmez. Çok gigabaytlık çıktı üretiyorsanız linearizasyon doğru araç değildir ve büyük PDF iş akışları için Direct File API üzerine notlarda anlatılan akış yaklaşımı bakılacak yöndür
Yüklediğiniz bir dosyada linearizasyonu tespit etmek
THotPDF.IsLoadedLinearized, şu anda yüklü belgenin zaten linearize edilmiş biçimde yazılıp yazılmadığını bildirir ve canlı akıştan değil, ayrıştırmadan önce alınan bir anlık görüntüden yanıt verir. HotPDF, kaynak akışın sıfır konumundan ilk 1024 baytı okur, bunları ilk obj anahtar kelimesi için ve ardından değeri 1 olan bir /Linearized girdisi için tarar ve boolean sonucu önbelleğe alır
var
PDF: THotPDF;
PageCount: Integer;
begin
PDF := THotPDF.Create(nil);
try
PageCount := PDF.LoadFromFile('incoming.pdf');
if (PageCount > 0) and (not PDF.IsLoadedLinearized) then
Writeln('Source is not Fast Web View ready');
finally
PDF.Free;
end;
end;
Bu açıklamadaki iki kısıt yük taşıyıcıdır. Tespit, akış konumuna güvenemez, çünkü uygulama kodu soruyu sorduğunda ayrıştırıcı onu zaten hareket ettirmiştir ve talep üzerine yeniden okuyamaz çünkü LoadFromFile, yükleme bittiğinde dahili kaynak akışını serbest bırakır. Bu yüzden ayrıştırmadan-önce-yakala-ve-önbelleğe-al tasarımı vardır. Tarama ayrıca değer konusunda kasıtlı olarak literaldir: yalnızca /Linearized 1 veya tamamen sıfır kesirli sayısal olarak eşdeğer bir form kabul edilir, çünkü parametre sözlüğü başka bir şey söyleyen bir dosya Ek F vaadini vermiyordur
Çalmaya değer bir Delphi kayıt tuzağı
Dinamik diziler içeren yerel kayıtlar, yönetilen alanlarını başlatır ve başka hiçbir şeyi başlatmaz, ve dizinin yanında düz bir Count alanı tutuyorsanız onu kendiniz temizlemeniz gerekir. Bu, geliştirme sırasında linearizasyon bölümlemesini ısırdı ve tam olarak bir platformun onu gizlemesi nedeniyle bir güne mal olan türden bir hatadır
type
THPDFLinearIndexList = record
Values: THPDFIntegerArray; // managed field: cleared for you
Count: Integer; // plain field: whatever was on the stack
end;
// Required, not cosmetic:
Part4 := Default(THPDFLinearIndexList);
Part6 := Default(THPDFLinearIndexList);
Part8 := Default(THPDFLinearIndexList);
Part9 := Default(THPDFLinearIndexList);
Dinamik dizi alanı referans sayımlıdır, dolayısıyla derleyici onu sıfırlar. Yanındaki Count, böyle bir garantisi olmayan sıradan bir tamsayıdır ve başlatılmamış bir Count, ilk eklemeyi rastgele bir indekse gönderir. Win32 altında yığın yuvası tesadüfen sıfır tutuyordu, ekleme indeks 0'a indi ve her test geçti. Win64 altında aynı kod dizinin sonunun ötesine yazdı. Ders linearizasyonun çok ötesine genellenir: bir kayıt, yönetilen ve yönetilmeyen alanları karıştırdığında, Default(TRecord) atayın ve derleyicinin hangi alanları kapsadığını düşünmeyi bırakın ve yeşil bir Win32 çalıştırmasını asla başlatmanın doğru olduğuna dair kanıt olarak ele almayın
Burada anlatılan LinearizeOutput ve IsLoadedLinearized üyeleri, Delphi ve C++Builder için standart HotPDF Component ile birlikte gönderilir; ürün sayfası, çapraz referans akışları, nesne akışları ve sürüm kilitleme ile etkileşim kurallarını içeren tam özellik referansını taşır