İki gigabaytlık bir PDF'i bariz yoldan birleştirmek veya bölmek, size aynı anda iki şeye mal olur: duvar saati süresi ve adres uzayı. Bariz yol, her girdiyi yüklemek, işi yapmak, çıktıyı yazmaktır. Kırılan yer yüklemedir. 300'den 600 DPI'ye geçen bir tarama arşivi, doğrusal çözünürlüğünü ikiye katlar ve diskte kabaca dört katına çıkar; bu yüzden tüm yıl boyunca 400 MB'lik dosyaları idare eden aynı montaj işi, bir girdi bir gigabaytı geçer geçmez, çoğu zaman yalnızca sayfa saymaktan başka bir şey yapmıyorken bile debelenmeye başlar. Görev hiç zorlaşmadı. Aç, say, aralık seç, birleştir, işin tamamı budur. Tam ağaç yükleme, bu boyutta basitçe akla yatkın bir varsayılan olmaktan çıktı. Delphi ve C++Builder için losLab'ın PDF kütüphanesi olan PDF Library for Delphi, buna Direct Access katmanıyla cevap verir: tüm belgeyi bellekte kurmak yerine çapraz referans tablosunu yerinde gezen bir akış okuyucusuyla desteklenen, DA önekli bir fonksiyon ailesi
Tam bir yüklemede bellek nereye gider
Bir PDF'i "normal şekilde" yüklemek, xref'i ayrıştırmak, her dolaylı nesneyi bellek içi bir ağaca çözmek, nesne akışlarını çözmek ve sayfa ağacını, yazı tiplerini ve açıklamaları üzerinde çalışabileceğiniz nesnelere bağlamak anlamına gelir. Düzenleme iş akışları için bu doğru takastır. Birleştirme, bölme ve inceleme işi için ise çoğunlukla israftır. 30.000 sayfalık bir tarama arşivi milyonlarca dolaylı nesne barındırabilir ve bir bölme işi bunlardan yalnızca birkaç yüzünü okumak zorundadır: istenen aralıktaki sayfa düğümleri, artı o düğümlerin referans verdiği her şey
Direct Access katmanı modeli tersine çevirir. DAOpenFile ve DAOpenFileReadOnly, dosyanın kuyruğundaki birkaç kilobaytlık trailer ve xref'i ayrıştırır ve bir dosya tanıtıcısı döndürür. Nesneler, bir çağrı onlara ihtiyaç duyduğunda tembelce getirilir. Pratik sonuç, çok gigabaytlık bir dosyayı açmanın küçük bir dosyayı açmakla hemen hemen aynı sürede olmasıdır ve bellek, dosyanın içerdiği şeyi değil, dokunduğunuz şeyi izler
Devasa bir dosyayı yüklemeden yoklamak
Aşağıdaki örüntü, kütüphanenin kendi büyük dosya kıyaslamasından gelir: salt okunur açın, sorular sorun, kapatın. Hiçbir belge ağacı hiç var olmaz
var
Lib: TPDFlib;
Handle, Pages: Integer;
begin
Lib := TPDFlib.Create;
try
Handle := Lib.DAOpenFileReadOnly('archive-2025.pdf', '');
if Handle = 0 then
raise Exception.Create('Direct access open failed');
Pages := Lib.DAGetPageCount(Handle);
Writeln('pages : ', Pages);
Writeln('title : ', Lib.DAGetInformation(Handle, 'Title'));
Lib.DACloseFile(Handle);
finally
Lib.Free;
end;
end;
Salt okunur mod, mümkün olduğunda tercih etmeye değer: alım aşamasının, başka süreçler dosyayı tutarken çalışmasına izin verir ve niyeti belgeler. Yanlışlıkla değiştiren bir fonksiyonu çağıran bir yoklama aşaması, arşivi bozmak yerine hızlıca başarısız olur
PageRef bir sayfa numarası değil, bir nesne tanıtıcısıdır
DA API'siyle en yaygın tek hata, bir fonksiyon bir PageRef beklerken bir sayfa numarası geçirmektir. Sayfa başına DA çağrılarının neredeyse tamamı, bir sayfa numarası yerine sayfa nesnesine bir referans tanıtıcısı alır: DAExtractPageText, DARenderPageToFile, DARotatePage ve DACapturePage'in hepsi bir ref bekler. İnsana yönelik numarayı DAFindPage üzerinden çevirerek bir tane elde edersiniz:
PageRef := Lib.DAFindPage(Handle, 250); // sayfa numarası -> nesne tanıtıcısı
if PageRef <> 0 then
begin
Text := Lib.DAExtractPageText(Handle, PageRef, 0);
Lib.DARenderPageToFile(Handle, PageRef, 5, 150, 'page250.png');
end;
Bunun yerine ham 250 sayısını geçirmek bir hata fırlatmaz. O tanıtıcı değerinin arkasında hangi nesne bulunuyorsa ona hitap eder; bu, iyi bir günde görünür biçimde başarısız olur, kötü bir günde ise yanlış sayfadan metni bir müşteriye dönük belgeye çıkarır. DA katmanını kendi servis kodunuza sararsanız, çeviriyi atlamayı imkansız kılın: sınırda sayfa numaralarını kabul edin, hemen DAFindPage'i çağırın ve dahili olarak yalnızca ref'leri geçirin
Adlandırılmış bir listeyle yüzlerce dosyayı birleştirmek
İki dosya için MergeFiles(First, Second, Output) yeterlidir. Toplu montaj, dosya listeleri üzerinden daha iyi ölçeklenir: girdileri bir liste adı altında kaydedin, ardından listeyi tek bir geçişte birleştirin
Lib.AddToFileList('Statements', 'jan.pdf');
Lib.AddToFileList('Statements', 'feb.pdf');
Lib.AddToFileList('Statements', 'mar.pdf');
Lib.MergeFileList('Statements', 'q1-statements.pdf');
// Sonucu ucuz yoldan doğrulayın: yine direct access
Handle := Lib.DAOpenFileReadOnly('q1-statements.pdf', '');
Writeln('merged pages: ', Lib.DAGetPageCount(Handle));
Lib.DACloseFile(Handle);
Birleştirme ailesinin üç varyantı vardır ve fark yalnızca hız değildir. MergeFileListFast, yapı ağacının korunmasını atlar; MergeFileListStrict sıkı modu zorunlu kılar; sonek almayan sürüm dengeli varsayılandır. Ortaya çıkan işletimsel kural şudur: herhangi bir girdi, erişilebilirlik yapısının hayatta kalması gereken Etiketli bir PDF ise, PDF/UA için üretilen her şey bunun bariz örneği olmak üzere, varsayılan veya Strict varyantına başvurun, çünkü Fast, yapı ağacını sessizce düşürür. Etiketleme olmayan düz tarama arşivleri için Fast, bedava performanstır. İşlem hattı başına karar verin, geliştirici ruh haline göre değil ve iş günlüğüne kullanılan varyantı kaydedin
Yüklemeden bölme: aralık çıkarma
Bölme, aynı yükleme-yok felsefesini izler. ExtractFilePages(InputFileName, Password, OutputFileName, RangeList), '1-500', '501-1000' gibi bir aralık listesi veya virgülle ayrılmış seçimlerle bir sayfa aralığını doğrudan dosyadan dosyaya çeker ve kaynak asla bir belge ağacına dönüşmez. Bir belge başka nedenlerle zaten yüklüyse, ExtractPageRanges mevcut olandan yeni bir bellek içi belge üretir ve CopyPageRanges, aralıkları ID'ye göre başka bir yüklü belgeden aktarır. Konsolide edilmiş yazdırma akışlarının ekstre başına bölünmesi için, dosyadan-dosyaya biçimi, 4 GB'lık bir girdinin hiç RAM'e şişmesini engelleyen biçimdir
Geometrileri hakkında yalan söyleyen dosyalar
Büyük dosya işlem hatları, sadece girdiler daha çok sistemden geçtiği için, küçük dosya işlem hatlarının hiç görmediği bir oranda hasarlı dosyalarla karşılaşır. İki başarısızlık biçimi açık bir ele alışı hak eder
Birincisi, kaymış başlıklar. Posta ağ geçitleri ve yazdırma kuyruklayıcıları bazen bir PDF'in başına bayt ekler; bu yüzden %PDF işaretçisi artık ofset 0'da oturmaz ve dosyadaki her xref ofseti aynı miktarda yanlıştır. Akış okuyucusu bunu algılar ve açığa çıkarır (düz düzeyde DAShiftedHeader, TSmartPDFReader üzerinde ShiftedHeader), ardından okumalar sırasında bunu telafi eder. Ev yapımı ofset aritmetiği tipik olarak bunu yapmaz; bu yüzden "ürettiğimiz her dosyada çalışıyor, X müşterisinin dosyalarında başarısız oluyor" klasik belirtidir
İkincisi, bozuk çapraz referans tabloları. DACopyFile(InputFileName, OutputFileName, PageCount), xref'i yeniden kurarken tüm dosyayı yeni bir kopyaya akıtır ve yan ürün olarak sayfa sayısını döndürür. Onu titiz bir aşağı akış tüketicisinin önünde bir normalleştirme aşaması olarak çalıştırmak, bir sınıf aralıklı ayrıştırma hatasını tek bir öngörülebilir onarım adımına dönüştürür. Ve kendi düzenlemelerinizin kaydedilmesi gerektiğinde, DAAppendFile onları, gigabaytları yeniden yazmak yerine yeni bir revizyon ekleyerek artımlı bir güncelleme olarak yazar; bu da kayıt maliyetini dosyayla değil, değişiklikle orantılı tutar
Teslimat ayrıntıları: doğrusallaştırma ve kompozisyon
İki bitişik yetenek, büyük dosya işlem hattını tamamlar. Kurulan çıktı, tarayıcı içi görüntüleme için HTTP üzerinden sunulduğunda, LinearizeFile onu bayt aralığı akışı için yeniden düzenler; böylece ilk sayfa, 500 MB'lik bir paketin geri kalanı indirmeyi bitirmeden önce görüntülenir. Onu son aşama olarak, tüm birleştirmeden sonra çalıştırın, çünkü sonraki herhangi bir değişiklik dosyayı yeniden doğrusalsızlaştırır. Ve paketlerin düz birleştirme yerine kompozisyona ihtiyaç duyduğu durumlarda, örneğin her ekstrenin arkasına damgalanmış bir kapak sayfası veya bir çıktı sayfasına bindirilmiş iki kaynak sayfası, DACapturePage herhangi bir sayfayı, DADrawCapturedPage'in bir hedef sayfaya keyfi bir dikdörtgende yerleştirdiği yeniden kullanılabilir bir şablona dönüştürür; bu da çok gigabaytlık kaynak üzerinde yine tam bir belge yüklemesi olmadan gerçekleşir
Sınırlar ve salt okunur kalan şeyler
Biçimin kendisi, Direct Access'ten çok önce yer sıkıntısı çeker. Ofsetler, DA katmanı boyunca baştan sona Int64'tür, bu yüzden gerçek tavanlar, kullanılabilir disk ve klasik (akış olmayan) çapraz referans tablolarının 10 basamaklı xref ofset alanıdır. Çok gigabaytlık tarama arşivleri pratikte sıradandır ve nesneler yalnızca bir çağrı onları istediğinde okunduğu için bellek, dosya boyutundan bağımsız olarak sınırlı kalır
İki soru, doğrudan yanıtlanmayı hak edecek kadar sık gelir. Varsayılan yol üzerinden birleştirme, belge yapısını karşıya taşır, bu yüzden yer imleri ve bağlantılar hayatta kalır; Fast varyantı, yapı ağacını hız karşılığında takas eden varyanttır ve bunu etiketlenmemiş girdiler için ayırmanın tüm nedeni de budur. Güvenli alışkanlık, birleştirilmiş çıktıyı açmak, ana hatlarını gezmek ve göndermeden önce birkaç iç bağlantıyı yerinde kontrol etmektir. Düzenlemeye gelince: salt okunur yoklama ile tam yükleme arasında yararlı bir orta zemin vardır. Sayfa düzeyi işlemler, aralarında DARotatePage, DAMovePage ve DAHidePage'in de bulunduğu şekilde, form alanı okumalarıyla birlikte doğrudan tanıtıcı üzerinde çalışır ve DAAppendFile bu düzenlemeleri artımlı bir revizyon olarak kalıcı kılar. İçerik düzeyi düzenleme, bir sayfanın içindeki işaretleme operatörlerini yeniden yazan her şey, hâlâ tam belge katmanına aittir
İlgili makaleler
Birleştirilmiş çıktınızın erişilebilir kalması gerekiyorsa, yapı ağacı arka planı, Fast birleştirme varyantının tam olarak neyi atacağını açıklayan Etiketli PDF erişilebilirlik makalesinde ele alınır. Böldüğünüz aralıklardan içerik çekmek için metin, görüntü ve yazı tipi çıkarma kılavuzuna bakın
Tam Direct Access fonksiyon listesi kütüphaneyle birlikte gelir; sürümler ve deneme indirmeleri PDF Library for Delphi ürün sayfasındadır