PDF belgeleri ilk bakışta basit görünebilir ancak iç yapıları şaşırtıcı derecede karmaşık olabilir. Geliştiricileri sıklıkla şaşırtan alanlardan biri, PDF sayfa sıralamasının gerçekte nasıl çalıştığını anlamaktır. PDF sayfa kopyalama örnek programımızı düzeltip geliştirirken HotPDF Delphi PDF Bileşenigibi zorlu sorunlarla karşılaştık. Bu kapsamlı kılavuz, temel nesne yapısından gelişmiş ağaç gezinme tekniklerine kadar her PDF geliştiricisinin bilmesi gereken temel kavramları açıklayacaktır.
PDF Belge Mimarisi
Temel Kavramlar
Özünde, bir PDF belgesi bir nesne veritabanı gibi oluşturulmuştur. Her nesnenin benzersiz bir tanımlayıcısı vardır ve diğer nesnelere referans verebilir. Bu, belge kataloğunun (kök) belgenin çeşitli bölümlerine giriş noktası görevi gördüğü, birbirine bağlı veri yapılarından oluşan karmaşık bir ağ oluşturur.
PDF'yi bir buzdağı gibi düşünün; belgeyi görüntülerken gördüğünüz yalnızca yüzeydir; altında ise belgenin görünümünü ve davranışını her açıdan tanımlayan karmaşık nesneler, referanslar ve meta veriler yapısı bulunur.
Nesne Referans Sistemi
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 |
1 0 obj <- Object 1 << /Type /Page /Parent 3 0 R /Contents 4 0 R /MediaBox [0 0 612 792] /Resources 5 0 R >> endobj |
Her PDF nesnesi şu modeli izler: ObjectNumber Generation obj.
Aşağıdaki gibi referanslarda R son eki 3 0 R "nesne 3, nesil 0'a referans" anlamına gelir.
Nesil Sayılarını Anlamak
Oluşturma numarası (modern PDF'lerde genellikle 0) önemli bir amaca hizmet eder:
- Nesil 0: Orijinal nesne
- 1. Nesil+: Güncellenmiş sürümler (artımlı güncellemelerde kullanılır)
- Nesil 65535: Silinen nesne işaretçisi
|
1 2 3 4 5 6 7 8 9 |
% Original object 5 0 obj << /Type /Page /Contents 6 0 R >> endobj % Updated version (incremental update) 5 1 obj << /Type /Page /Contents 6 0 R /Rotate 90 >> endobj |
PDF Dosya Yapısına Genel Bakış
Bir PDF dosyası dört ana bölümden oluşur:
- Başlık: Sürüm bilgisi (
%PDF-1.7) - Gövde: Nesne tanımları ve veriler
- Çapraz Referans Tablosu: Nesne konum dizini
- Fragman: Kök referansı ve dosya meta verileri
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
%PDF-1.7 <- Header 1 0 obj << /Type /Catalog ... >> <- Body (objects) 2 0 obj << /Type /Pages ... >> ... xref <- Cross-reference table 0 10 0000000000 65535 f 0000000009 00000 n ... trailer <- Trailer << /Size 10 /Root 1 0 R >> startxref 1234 %%EOF |
Sayfa Ağacı Yapısı
Sayfa Ağacı Konsepti
PDF, sayfaları düzenlemek için, dosya sisteminin dizinleri düzenlemesine benzer şekilde hiyerarşik bir ağaç yapısı kullanır. Bu tasarım birden fazla amaca hizmet eder:
- Verimli Gezinme: Belgenin tamamını ayrıştırmadan herhangi bir sayfaya hızlı erişim
- Sayfa Devri: Ortak özellikler üst düğümlerden devralınabilir
- Ölçeklenebilirlik: Binlerce sayfalık belgeleri verimli bir şekilde işler
- Esneklik: Karmaşık belge yapılarını ve iç içe geçmiş bölümleri destekler
|
1 2 3 4 5 6 7 |
Root Catalog ↓ Pages Tree Root (/Type /Pages) ↓ Kids Array → [Page1, Page2, Page3, ...] ↓ ↓ ↓ /Type /Page /Type /Page /Type /Page |
Gerçek Örnek: Basit Sayfa Ağacı
PDF dosyasındaki tipik bir sayfa ağacı şöyle görünür:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
16 0 obj (Pages Tree Root) << /Type /Pages /Count 3 /Kids [ 20 0 R <- Reference to first page 1 0 R <- Reference to second page 4 0 R <- Reference to third page ] /MediaBox [0 0 612 792] <- Inherited by all pages >> endobj 20 0 obj (First Page) << /Type /Page /Parent 16 0 R /Contents 21 0 R /Resources 22 0 R >> endobj 1 0 obj (Second Page) << /Type /Page /Parent 16 0 R /Contents 2 0 R /Resources 3 0 R /Rotate 90 >> endobj 4 0 obj (Third Page) << /Type /Page /Parent 16 0 R /Contents 5 0 R /Resources 6 0 R >> endobj |
Kritik Nokta: Kids dizisi şunları tanımlar: mantıksal sayfa sırası, dosyadaki nesnelerin fiziksel sırası değil.
qpdf Çıktısından Gerçek Dünya Örneği
İşte gerçek çıktı:
Sorunlu bir PDF'de qpdf --show-pages :
|
1 2 3 4 5 6 |
page 1: 20 0 R content: 192 0 R page 2: 1 0 R content: 190 0 R page 3: 4 0 R content: 188 0 R |
Şuna dikkat edin:
- Mantıksal Sayfa 1 şurada saklanır Nesne 20 Çocuklar dizisinde ilk olarak (en yüksek nesne numarası)
- Mantıksal Sayfa 2 şurada saklanır Nesne 1 (en düşük nesne numarası)
- Mantıksal Sayfa 3 şurada saklanır Nesne 4 (ortadaki nesne numarası)
Kod işlenen nesneler sayısal sırayla (1, 4, 20) ayrıştırılırsa, doğru mantıksal sıra (1, 2, 3) yerine yanlış sayfa sırası (2, 3, 1) elde edilir.
Karmaşık Örnek: İç İçe Yerleştirilmiş Sayfa Ağacı
Büyük belgeler daha iyi organizasyon için genellikle iç içe sayfa ağaçlarını kullanır:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
1 0 obj (Document Catalog) << /Type /Catalog /Pages 2 0 R >> endobj 2 0 obj (Root Pages Node) << /Type /Pages /Count 8 /Kids [3 0 R 4 0 R] <- Two intermediate nodes >> endobj 3 0 obj (Chapter 1 Pages) << /Type /Pages /Parent 2 0 R /Count 5 /Kids [10 0 R 11 0 R 12 0 R 13 0 R 14 0 R] /MediaBox [0 0 612 792] >> endobj 4 0 obj (Chapter 2 Pages) << /Type /Pages /Parent 2 0 R /Count 3 /Kids [20 0 R 21 0 R 22 0 R] /MediaBox [0 0 612 792] >> endobj % Individual page objects follow... 10 0 obj << /Type /Page /Parent 3 0 R ... >> 11 0 obj << /Type /Page /Parent 3 0 R ... >> ... |
Bu bir ağaç yapısı oluşturur:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 |
Root (8 pages) ├── Chapter 1 (5 pages) │ ├── Page 1 (10 0 R) │ ├── Page 2 (11 0 R) │ ├── Page 3 (12 0 R) │ ├── Page 4 (13 0 R) │ └── Page 5 (14 0 R) └── Chapter 2 (3 pages) ├── Page 6 (20 0 R) ├── Page 7 (21 0 R) └── Page 8 (22 0 R) |
Sayfa Ağacı Özellikleri
Gerekli Özellikler:
/Type: Olmalı ara düğümler için/Pagesveya Yaprak düğümler için/Page/Kids: Alt sayfa referansları dizisi (yalnızca ara düğümler)/Count: Alt sayfaların toplam sayısı/Parent: Üst düğüme referans (kök hariç)
İsteğe Bağlı Devralınabilen Özellikler:
/MediaBox: Sayfa boyutları/CropBox: Görünür sayfa alanı/BleedBox: Taşma payı alanını yazdırıyor/TrimBox: Son kırpılmış sayfa boyutu/ArtBox: Anlamlı içerik alanı/Resources: Yazı tipleri, resimler, grafik durumları/Rotate: Sayfa döndürme (0, 90, 180, 270 derece)
Yaygın Yanlış Kanılar
Hata #1: Sıralı Nesne Numaralarını Varsaymak = Sayfa Sırası
Birçok geliştirici, PDF'de nesne 1, 2 ve 3 olarak depolanan sayfalar varsa, nesne 1'in sayfa 1 olduğunu varsayar. Bu temelde yanlıştır ve ince hatalara yol açar.
Bu Varsayım Neden Başarısız:
- Nesne numaraları, sayfa sırasına göre değil, PDF oluşturma sırasında atanır
- PDF düzenleyicileri optimizasyon sırasında nesneleri yeniden numaralandırabilir
- Artımlı güncellemeler daha yüksek sayılara sahip yeni nesneler ekler
- Nesne akışları numaralandırma düzenlerini değiştirebilir
Gerçeklik: Nesne numaraları yalnızca tanımlayıcılardır. Gerçek sayfa sırası, Sayfa ağacındaki Kids dizisi tarafından belirlenir.
Gerçek Dünya Örneği:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 |
% These pages were created in order: Page 1, Page 2, Page 3 % But stored in PDF with these object numbers: 150 0 obj << /Type /Page ... >> % Actually page 1 23 0 obj << /Type /Page ... >> % Actually page 2 8 0 obj << /Type /Page ... >> % Actually page 3 % The Pages tree defines the correct order: 16 0 obj << /Type /Pages /Kids [150 0 R 23 0 R 8 0 R] % Logical order >> |
Hata #2: Sayfaları Fiziksel Dosya Sırasında İşleme
Nesneleri PDF dosyasından sırayla okumak, sayfaları doğru sırada göstermez.
Örnek Sorun:
- Dosya nesneleri fiziksel sırayla içeriyor: 1, 4, 16, 20
- Sayfa ağacı Çocuk dizisi: [20 0 R, 1 0 R, 4 0 R]
- Doğru mantıksal sayfa sırası: Nesne 20 (sayfa 1), Nesne 1 (sayfa 2), Nesne 4 (sayfa 3)
- Yanlış fiziksel dosya sırası: Nesne 1 (sayfa 2), Nesne 4 (sayfa 3), Nesne 16 (sayfa değil), Nesne 20 (sayfa 1)
Bu Neden Olur:
- PDF yazarları sayfa sırasını değil dosya boyutunu optimize eder
- Nesne akışları içeriği yeniden düzenleyebilir
- Doğrusallaştırma, web görüntüleme için nesne sıralamasını değiştirir
- Çoklu düzenleme araçları katman değişikliklerine neden olabilir
Hata #3: Belge Kataloğunu Göz Ardı Etmek
Bazı ayrıştırma kodları sayfaları uygun zinciri izlemeden doğrudan bulmaya çalışır: Kök → Sayfalar → Çocuklar.
Sorunlu Yaklaşım:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 |
// Wrong: Direct page search for i := 0 to Objects.Count - 1 do begin if Objects[i].GetValue('/Type') = '/Page' then AddToPageList(Objects[i]); // Wrong order! end; |
Doğru Yaklaşım:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 |
// Right: Follow the document structure CatalogObj := FindObjectByReference(TrailerRoot); PagesObj := FindObjectByReference(CatalogObj.GetValue('/Pages')); KidsArray := PagesObj.GetValue('/Kids'); for i := 0 to KidsArray.Count - 1 do begin PageRef := KidsArray.GetReference(i); PageObj := FindObjectByReference(PageRef); AddToPageList(PageObj); // Correct order! end; |
Hata 4: İç İçe Yerleştirilmiş Sayfa Ağaçlarını İşlememek
Tüm sayfa ağaçlarının düz (tek düzey) olduğunu varsaymak, karmaşık belge yapılarını gözden kaçırır.
Basit Ağaç (Genellikle Varsayılır):
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 |
Pages Root ├── Page 1 ├── Page 2 └── Page 3 |
Gerçek Karmaşık Ağaç:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 |
Pages Root ├── Part 1 Pages │ ├── Chapter 1 Pages │ │ ├── Page 1 │ │ └── Page 2 │ └── Chapter 2 Pages │ ├── Page 3 │ └── Page 4 └── Part 2 Pages └── Page 5 |
Özyinelemeli Yapıyı Kullanma:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
procedure ProcessPageNode(Node: TPDFObject; var PageList: TPageList); begin if Node.GetValue('/Type') = '/Pages' then begin // Intermediate node - process all kids KidsArray := Node.GetValue('/Kids'); for i := 0 to KidsArray.Count - 1 do begin ChildRef := KidsArray.GetReference(i); ChildObj := FindObjectByReference(ChildRef); ProcessPageNode(ChildObj, PageList); // Recursive call end; end else if Node.GetValue('/Type') = '/Page' then begin // Leaf node - actual page PageList.Add(Node); end; end; |
Hata 5: Sayfayı Devralmayı Yoksaymak
Devralınan özelliklerin dikkate alınmaması sayfanın hatalı oluşturulmasına yol açar.
Kalıtım Zinciri Örneği:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 |
Root Pages (/MediaBox [0 0 612 792], /Resources 10 0 R) ├── Chapter Pages (/Rotate 90) │ └── Page 1 (/Contents 20 0 R) └── Page 2 (/Contents 21 0 R, /MediaBox [0 0 595 842]) |
Etkili Özellikler:
- Sayfa 1: MediaBox=[0,0,612,792] (devralındı), Döndür=90 (devraldı), Kaynaklar=10 0 R (devraldı), İçerik=20 0 R
- Sayfa 2: MediaBox=[0,0,595,842] (geçersiz kılındı), Döndür=0 (devralınmadı), Kaynaklar=10 0 R (devralındı), İçerik=21 0 R
Uygulama (HotPDF Bileşeni):
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
function GetEffectivePageProperties(PageObj: TPDFDictionary): TPDFDictionary; var EffectiveProps: TPDFDictionary; CurrentNode: TPDFDictionary; begin EffectiveProps := TPDFDictionary.Create; CurrentNode := PageObj; // Walk up the tree collecting inherited properties while CurrentNode <> nil do begin // Add properties not already set (inheritance chain) if not EffectiveProps.HasKey('/MediaBox') and CurrentNode.HasKey('/MediaBox') then EffectiveProps.SetValue('/MediaBox', CurrentNode.GetValue('/MediaBox')); if not EffectiveProps.HasKey('/Resources') and CurrentNode.HasKey('/Resources') then EffectiveProps.SetValue('/Resources', CurrentNode.GetValue('/Resources')); // ... other inheritable properties // Move to parent if CurrentNode.HasKey('/Parent') then CurrentNode := FindObjectByReference(CurrentNode.GetValue('/Parent')) else CurrentNode := nil; end; Result := EffectiveProps; end; |
Hata #6: Sayım Değerlerinin Doğru Olduğunu Varsaymak
Bazen
Sayfa ağacı düğümlerindeki /Count değerleri gerçek sayfa sayısıyla eşleşmiyor.
Sorun:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 |
Pages Root << /Count 5 <- Claims 5 pages /Kids [A B C] <- But only 3 direct children >> Node A: /Count 2, /Kids [Page1, Page2] Node B: /Count 1, /Kids [Page3] Node C: /Count 3, /Kids [Page4, Page5, Page6] <- 3 pages, not matching parent count |
Savunma Programlaması:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// HotPDF VCL Component code snippet function CountActualPages(PagesNode: TPDFDictionary): Integer; var ActualCount: Integer; KidsArray: TPDFArray; i: Integer; ChildObj: TPDFDictionary; begin ActualCount := 0; KidsArray := PagesNode.GetValue('/Kids'); for i := 0 to KidsArray.Count - 1 do begin ChildObj := FindObjectByReference(KidsArray.GetReference(i)); if ChildObj.GetValue('/Type') = '/Page' then Inc(ActualCount) else if ChildObj.GetValue('/Type') = '/Pages' then Inc(ActualCount, CountActualPages(ChildObj)); end; // Verify against claimed count ClaimedCount := PagesNode.GetValue('/Count'); if ClaimedCount <> ActualCount then WriteLn('Warning: Count mismatch - claimed: ', ClaimedCount, ', actual: ', ActualCount); Result := ActualCount; end; |
Sayfaları Doğru Şekilde Ayrıştırma
Adım 1: Belge Kökünü Bulun
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 |
// Find trailer and get Root reference RootRef := GetTrailerRootReference(); RootObject := FindObject(RootRef); |
Adım 2: Sayfa Ağacına gidin
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 |
// Get Pages reference from Root catalog PagesRef := RootObject.GetValue('/Pages'); PagesObject := FindObject(PagesRef); |
Adım 3: Çocuk Dizisini Sırayla İşleyin
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 |
// Extract Kids array - this defines page order KidsArray := PagesObject.GetValue('/Kids'); // Process each page in the order specified by Kids for i := 0 to KidsArray.Count - 1 do begin PageRef := KidsArray[i]; PageObject := FindObject(PageRef); // Now you have the actual page i+1 end; |
Gelişmiş Konseptler
İç İçe Yerleştirilmiş Sayfa Ağaçları
Daha iyi organizasyon için büyük belgelerde iç içe sayfa ağaçları bulunabilir:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 |
Root Pages ├── Chapter 1 Pages │ ├── Page 1 │ ├── Page 2 │ └── Page 3 └── Chapter 2 Pages ├── Page 4 └── Page 5 |
Sayfa Devri
Sayfalar, üst sayfalarının ağaç düğümünden aşağıdaki gibi özellikleri devralabilir:
- MediaBox (sayfa boyutu)
- CropBox (görünür alan)
- Kaynaklar (yazı tipleri, resimler)
- Döndürme
Pratik Uygulama İpuçları
1. Her Zaman Ağaç Yapısını Takip Edin
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 |
// Wrong: Assumes sequential object order PageObject := GetObject(PageNumber); // Right: Follows Pages tree structure PageObject := GetPageFromKidsArray(PageNumber - 1); |
2. Özyinelemeli Sayfa Ağaçlarını İşleyin
Bazı PDF'lerde birden fazla düzeyde sayfa ağacı düğümü bulunur. Kodunuz yinelemeli olarak ağaçta dolaşmalıdır:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
procedure ProcessPageNode(Node: TPDFObject); begin if Node.Type = 'Pages' then begin // Intermediate node - process Kids for each Kid in Node.Kids do ProcessPageNode(Kid); end else if Node.Type = 'Page' then begin // Leaf node - actual page AddPageToArray(Node); end; end; |
3. Sayfa Sayılarını Doğrulayın
Her zaman şunu doğrulayın:
Pages nesnelerindeki /Count değeri, bulunan gerçek sayfa sayısıyla eşleşiyor:
|
1 2 3 4 |
ExpectedCount := PagesObject.GetValue('/Count'); ActualCount := CountPagesInTree(PagesObject); if ExpectedCount <> ActualCount then RaiseError('Page count mismatch'); |
PDF Sayfası Sorunlarında Hata Ayıklama
Yaygın Belirtiler
- Yanlış sayfa çıkarıldı: Genellikle Kids dizi sırasının göz ardı edildiğini gösterir
- Eksik sayfalar: Genellikle iç içe sayfa ağaçlarının işlenmemesinden kaynaklanır
- Yinelenen sayfalar: Hem ara hem de yaprak düğümler işlenirken meydana gelebilir
Hata Ayıklama Teknikleri
- Sayfa ağacı yapısını günlüğe kaydedin:
|
1 2 |
WriteLn('Pages tree Kids: [', KidsArrayToString(Kids), ']'); WriteLn('Processing page object: ', PageObjectNumber); |
-
Sayfa içeriğini doğrulayın: Küçük bir örnek çıkarın ve bunun beklenen içerikle eşleştiğini doğrulayın
-
Harici araçları kullan: Gibi araçlar
qpdfveyapdftkPDF yapısını analiz etmeye yardımcı olabilir
En İyi Uygulamalar
1. Doğru Veri Yapıları Oluşturun
Dahili sayfa dizinizi PDF'nin mantıksal sayfa sırası ile aynı sırada oluşturun:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 |
// Build PageArray following Kids order SetLength(PageArray, PageCount); for i := 0 to KidsArray.Count - 1 do begin PageRef := KidsArray[i]; PageArray[i] := FindObject(PageRef); end; |
2. Ayrıştırma ile İşlemeyi Ayırın
Önce tam sayfa yapısını ayrıştırın, ardından işlemleri gerçekleştirin. Belge yapısını ayrıştırırken sayfaları işlemeye çalışmayın.
3. Kenar Durumlarını İşleyin
- Boş belgeler (0 sayfa)
- Tek sayfalı belgeler
- Karışık sayfa yönlendirmelerine sahip belgeler
- Devralınan özelliklere sahip belgeler
Gelişmiş PDF Nesne Türleri
PDF Nesne Hiyerarşisini Anlamak
Temel sayfa nesnelerinin ötesinde PDF'ler, belgenin tamamını oluşturmak için birlikte çalışan çok sayıda özel nesne türü içerir:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Document Catalog (Root) ├── Pages Tree ├── Outlines (Bookmarks) ├── Names Dictionary ├── Dests (Named Destinations) ├── ViewerPreferences ├── PageLabels ├── Metadata ├── StructTreeRoot (Tagged PDF) ├── MarkInfo ├── Lang ├── SpiderInfo ├── OutputIntents ├── PieceInfo ├── AcroForm (Interactive Forms) ├── Encrypt (Security) └── Extensions |
İçerik Akışı Nesneleri
Sayfa içeriği, çizim komutlarını içeren akış nesnelerinde depolanır:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
5 0 obj (Content Stream) << /Length 1274 /Filter /FlateDecode >> stream BT % Begin text /F1 12 Tf % Set font (F1) and size (12) 100 700 Td % Move to position (100, 700) (Hello World) Tj % Show text "Hello World" ET % End text Q % Save graphics state q % Restore graphics state endstream endobj |
Kaynak Nesneleri
Kaynaklar, içerik akışları tarafından kullanılan yazı tiplerini, görüntüleri ve grafik durumlarını tanımlar:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
6 0 obj (Resources) << /Font << /F1 7 0 R % Font resource /F2 8 0 R >> /XObject << /Im1 9 0 R % Image resource >> /ExtGState << /GS1 10 0 R % Graphics state >> /ColorSpace << /CS1 11 0 R % Color space >> >> endobj |
Yazı Tipi Nesneleri
Yazı tipleri birden fazla alt türe sahip karmaşık nesnelerdir:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
7 0 obj (Type 1 Font) << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj 8 0 obj (TrueType Font) << /Type /Font /Subtype /TrueType /BaseFont /ArialMT /FirstChar 32 /LastChar 126 /Widths [278 278 355 ...] /FontDescriptor 12 0 R >> endobj |
Profesyonel PDF Analiz Araçları
Komut Satırı Araçları
QPDF – PDF'ler için İsviçre Çakısı:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# Show page tree structure and page order qpdf --show-pages input.pdf # Show detailed page information in JSON format qpdf --json=latest --json-key=pages input.pdf # Validate PDF structure qpdf --check input.pdf # Show cross-reference table qpdf --show-xref input.pdf # Show specific object (e.g., pages tree root) qpdf --show-object="16 0 R" input.pdf # Show encryption details qpdf --show-encryption input.pdf # Show filtered stream data qpdf --filtered-stream-data input.pdf # Show complete document structure in JSON qpdf --json input.pdf |
CPDF – Tutarlı PDF Komut Satırı Araçları:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# Get comprehensive PDF information in JSON format cpdf -info-json input.pdf # Get detailed page information with boxes and rotation cpdf -page-info-json input.pdf # List all fonts with encoding and type information cpdf -list-fonts-json input.pdf # List images with dimensions, color space, and compression cpdf -list-images-json input.pdf # View specific PDF objects (great for debugging) cpdf -obj 16 input.pdf # Output: <</Count 3/Kids[20 0 R 1 0 R 4 0 R]/Type/Pages>> # Analyze document composition and size breakdown cpdf -composition-json input.pdf # Shows percentage of images, fonts, content streams, etc. # List bookmarks in JSON format cpdf -list-bookmarks-json input.pdf # Export complete PDF structure as JSON for detailed analysis cpdf -output-json input.pdf -o structure.json |
PDFtk – PDF Araç Takımı:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 |
# Dump document metadata pdftk input.pdf dump_data # Show bookmarks pdftk input.pdf dump_data | grep -A 5 "Bookmark" # Extract specific pages pdftk input.pdf cat 1-3 output pages_1_to_3.pdf # Rotate pages pdftk input.pdf cat 1-endright output rotated.pdf |
MuPDF Araçları:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 |
# Show PDF structure mutool show input.pdf # Extract text with positioning mutool draw -F txt input.pdf # Convert to HTML (preserves structure) mutool convert -F html input.pdf output.html # Show object details mutool show input.pdf 1 0 R |
Masaüstü Analiz Araçları
PDF Gezgini (Ticari):
- Belge yapısının görsel ağaç görünümü
- Nesne özelliklerinin gerçek zamanlı düzenlenmesi
- Çapraz referans doğrulaması
- Akış kod çözme ve görüntüleme
PDF Hata Ayıklayıcı (Adobe):
- Adım adım PDF oluşturma
- Söz dizimi vurgulamalı nesne denetçisi
- İçerik akışı analizi
- Hata tespiti ve raporlama
Analiz için Programlama Kitaplıkları
Python:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import PyPDF2 import fitz # PyMuPDF # PyPDF2 analysis with open('input.pdf', 'rb') as file: reader = PyPDF2.PdfFileReader(file) # Show page tree structure pages_obj = reader.trailer['/Root']['/Pages'] print(f"Pages object: {pages_obj}") # Show each page's properties for i in range(reader.numPages): page = reader.getPage(i) print(f"Page {i+1}: {page}") # PyMuPDF detailed analysis doc = fitz.open('input.pdf') for page_num in range(doc.page_count): page = doc[page_num] # Get page dictionary page_dict = page.get_contents() print(f"Page {page_num + 1} contents: {len(page_dict)} bytes") # Get text with positioning blocks = page.get_text("dict") for block in blocks["blocks"]: if "lines" in block: for line in block["lines"]: for span in line["spans"]: print(f"Text: '{span['text']}' at {span['bbox']}") |
JavaScript (PDF.js):
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Load and analyze PDF pdfjsLib.getDocument('input.pdf').promise.then(function(pdf) { // Get page count console.log('Page count:', pdf.numPages); // Analyze each page for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) { pdf.getPage(pageNum).then(function(page) { // Get page annotations page.getAnnotations().then(function(annotations) { console.log(`Page ${pageNum} annotations:`, annotations); }); // Get text content page.getTextContent().then(function(textContent) { console.log(`Page ${pageNum} text items:`, textContent.items.length); }); }); } }); |
Performansla İlgili Hususlar
Verimli Sayfa Ağacı Geçişi
Büyük belgelerle uğraşırken verimli geçiş kritik hale gelir:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// HotPDF Component code snippet // Optimized page tree traversal with caching type TPageCache = class private FPageObjects: TDictionary<Integer, TPDFPageObject>; FPageTree: TPDFPagesTree; public function GetPage(PageNumber: Integer): TPDFPageObject; procedure PreloadPageRange(StartPage, EndPage: Integer); procedure ClearCache; end; function TPageCache.GetPage(PageNumber: Integer): TPDFPageObject; begin // Check cache first if FPageObjects.ContainsKey(PageNumber) then Exit(FPageObjects[PageNumber]); // Load on demand Result := FPageTree.LoadPage(PageNumber); FPageObjects.Add(PageNumber, Result); end; procedure TPageCache.PreloadPageRange(StartPage, EndPage: Integer); var I: Integer; PageObj: TPDFPageObject; begin // Batch load for better performance for I := StartPage to EndPage do begin if not FPageObjects.ContainsKey(I) then begin PageObj := FPageTree.LoadPage(I); FPageObjects.Add(I, PageObj); end; end; end; |
Bellek Yönetimi
Büyük PDF'ler dikkatli bellek yönetimi gerektirir:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
// losLab HotPDF Component code snippet // Memory-efficient PDF processing type TPDFProcessor = class private FMemoryLimit: Int64; FCurrentMemoryUsage: Int64; procedure CheckMemoryUsage; procedure FlushCaches; public procedure ProcessPagesInBatches(PDF: TPDFDocument; BatchSize: Integer); end; procedure TPDFProcessor.ProcessPagesInBatches(PDF: TPDFDocument; BatchSize: Integer); var I, StartPage, EndPage: Integer; PageCount: Integer; Batch: TList<TPDFPageObject>; begin PageCount := PDF.GetPageCount; StartPage := 1; while StartPage <= PageCount do begin EndPage := Min(StartPage + BatchSize - 1, PageCount); Batch := TList<TPDFPageObject>.Create; try // Load batch of pages for I := StartPage to EndPage do begin Batch.Add(PDF.GetPage(I)); CheckMemoryUsage; end; // Process batch ProcessPageBatch(Batch); finally // Clean up batch Batch.Free; FlushCaches; end; StartPage := EndPage + 1; end; end; |
Geç Yükleme Stratejileri
Büyük belgeler için yavaş yükleme uygulayın:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// Lazy-loaded page tree type TLazyPDFPage = class private FPageReference: TPDFReference; FPageObject: TPDFPageObject; FLoaded: Boolean; function GetPageObject: TPDFPageObject; public constructor Create(PageRef: TPDFReference); property PageObject: TPDFPageObject read GetPageObject; property IsLoaded: Boolean read FLoaded; procedure Unload; // Free memory when not needed end; function TLazyPDFPage.GetPageObject: TPDFPageObject; begin if not FLoaded then begin WriteLn('[DEBUG] Loading page from reference ', FPageReference.ObjectNumber); FPageObject := LoadObjectFromReference(FPageReference); FLoaded := True; end; Result := FPageObject; end; procedure TLazyPDFPage.Unload; begin if FLoaded then begin WriteLn('[DEBUG] Unloading page ', FPageReference.ObjectNumber); FPageObject.Free; FPageObject := nil; FLoaded := False; end; end; |
Hata İşleme ve Doğrulama
Güçlü PDF Ayrıştırma
Hatalı biçimlendirilmiş veya bozuk PDF'leri incelikle işleyin:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
// losLab Software Development code snippet // Defensive PDF parsing with error recovery type TPDFParseResult = (prSuccess, prWarning, prError, prCriticalError); function ParsePDFWithRecovery(FileName: string): TPDFParseResult; var PDF: TPDFDocument; ErrorCount: Integer; WarningCount: Integer; begin Result := prSuccess; ErrorCount := 0; WarningCount := 0; try PDF := TPDFDocument.Create; try // Basic file validation if not ValidatePDFHeader(FileName) then begin WriteLn('[ERROR] Invalid PDF header'); Inc(ErrorCount); end; // Load with error recovery if not PDF.LoadFromFileWithRecovery(FileName) then begin WriteLn('[ERROR] Failed to load PDF structure'); Inc(ErrorCount); end; // Validate page tree case ValidatePageTree(PDF) of vtValid: WriteLn('[INFO] Page tree is valid'); vtWarning: begin WriteLn('[WARN] Page tree has minor issues'); Inc(WarningCount); end; vtError: begin WriteLn('[ERROR] Page tree is corrupted'); Inc(ErrorCount); end; end; // Validate cross-references if not ValidateXRefTable(PDF) then begin WriteLn('[WARN] Cross-reference table has issues, attempting repair'); if RepairXRefTable(PDF) then Inc(WarningCount) else Inc(ErrorCount); end; // Determine result based on error counts if ErrorCount > 0 then Result := prError else if WarningCount > 0 then Result := prWarning else Result := prSuccess; finally PDF.Free; end; except on E: Exception do begin WriteLn('[CRITICAL] Exception during PDF parsing: ', E.Message); Result := prCriticalError; end; end; end; |
Doğrulama Kontrol Listeleri
Kapsamlı doğrulamayı uygulayın:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
// losLab Software code snippet // PDF validation checklist source codes type TValidationCheck = record Name: string; Passed: Boolean; Message: string; end; function ValidatePDFDocument(PDF: TPDFDocument): TArray<TValidationCheck>; var Checks: TArray<TValidationCheck>; begin SetLength(Checks, 10); // Check 1: File header Checks[0].Name := 'PDF Header'; Checks[0].Passed := ValidatePDFVersion(PDF.Version); Checks[0].Message := 'PDF version: ' + PDF.Version; // Check 2: Document catalog Checks[1].Name := 'Document Catalog'; Checks[1].Passed := PDF.Catalog <> nil; Checks[1].Message := 'Root catalog ' + IfThen(Checks[1].Passed, 'found', 'missing'); // Check 3: Page tree structure Checks[2].Name := 'Page Tree'; Checks[2].Passed := ValidatePageTreeStructure(PDF); Checks[2].Message := Format('Page tree contains %d pages', [PDF.PageCount]); // Check 4: Cross-reference table Checks[3].Name := 'Cross-Reference Table'; Checks[3].Passed := ValidateXRefConsistency(PDF); Checks[3].Message := 'XRef table consistency check'; // Check 5: Object integrity Checks[4].Name := 'Object Integrity'; Checks[4].Passed := ValidateObjectIntegrity(PDF); Checks[4].Message := 'All referenced objects exist'; // Check 6: Page content streams Checks[5].Name := 'Content Streams'; Checks[5].Passed := ValidateContentStreams(PDF); Checks[5].Message := 'All pages have valid content'; // Check 7: Font resources Checks[6].Name := 'Font Resources'; Checks[6].Passed := ValidateFontResources(PDF); Checks[6].Message := 'Font resources are complete'; // Check 8: Image resources Checks[7].Name := 'Image Resources'; Checks[7].Passed := ValidateImageResources(PDF); Checks[7].Message := 'Image resources are accessible'; // Check 9: Encryption Checks[8].Name := 'Encryption'; Checks[8].Passed := ValidateEncryption(PDF); Checks[8].Message := 'Encryption settings are valid'; // Check 10: Metadata Checks[9].Name := 'Metadata'; Checks[9].Passed := ValidateMetadata(PDF); Checks[9].Message := 'Document metadata is well-formed'; Result := Checks; end; |
Pratik Doğrulama: Gerçek PDF Analizi
Bu makaledeki kavramları doğrulamak için sorunlu bir PDF dosyasında qpdf kullanarak gerçek analiz gerçekleştirdik. Sonuçlar sayfa sıralama sorununu mükemmel bir şekilde ortaya koydu:
Gerçek qpdf Çıktı Analizi
Komut: qpdf --show-pages input-all.pdf
Sonuçlar:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 |
page 1: 20 0 R content: 192 0 R page 2: 1 0 R content: 190 0 R page 3: 4 0 R content: 188 0 R |
Analiz:
- Mantıksal Sayfa 1 → Nesne 20 (en yüksek sayı)
- Mantıksal Sayfa 2 → Nesne 1 (en düşük sayı)
- Mantıksal Sayfa 3 → Nesne 4 (ortadaki sayı)
Bu gerçek dünya örneği, nesne sırası ayrıştırmasının neden başarısız olduğunu kanıtlıyor: nesnelerin sayısal olarak (1, 4, 20) işlenmesi, doğru mantıksal sıra (1, 2, 3) yerine sayfaların (2, 3, 1) oluşmasına neden olur.
Doğrulama Komutları
Bu qpdf komutları belge yapısını başarıyla doğruladı:
Urvanov Sözdizimi Vurgulayıcı v2.9.1|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Show page structure - WORKS qpdf --show-pages input-all.pdf # Show detailed page info in JSON - WORKS qpdf --json=latest --json-key=pages input-all.pdf # Validate PDF structure - WORKS qpdf --check input-all.pdf # Output: "No syntax or stream encoding errors found" # Show cross-reference table - WORKS qpdf --show-xref input-all.pdf # Show specific object (e.g., pages tree root) qpdf --json=latest --json-key=qpdf input-all.pdf | findstr "Pages" # Output: "/Pages": "16 0 R" |
Gerçek Etki
Bu analiz, tamamlayıcı makalemizde açıklanan hata ayıklama yaklaşımını doğruladı. Düzeltme uygulamayı içeriyordu ReorderPageArrByPagesTree sayfaları nesne sırası yerine mantıksal sırayla işlemek ve gösterilen sorunu doğrudan ele almak için.
Sonuç
PDF sayfa ağaçlarını anlamak, güvenilir PDF işleme için çok önemlidir, ancak bu, PDF belge yapısına hakim olmanın yalnızca başlangıcıdır. Bu kapsamlı analiz şunları kapsamıştır:
Teknik Ustalık Puanı
- Belge Mimarisi: PDF'ler karmaşık referans sistemlerine sahip karmaşık nesne veritabanlarıdır
- Sayfa Ağacı Gezinmesi: Mantıksal sıra (Kids dizileri) ile fiziksel sıranın dikkatli ele alınması gerekir
- Nesne İlişkileri: Nesnelerin birbirine nasıl başvurduğunu anlamak, ayrıştırma hatalarını önler
- Kalıtım Kalıpları: Sayfa özellikleri ağaç hiyerarşisindeki üst düğümlerden devralınır
- Hata Kurtarma: Sağlam ayrıştırma, hatalı biçimlendirilmiş belgeleri zarif bir şekilde işler
Gelişmiş Kavramlar Kapsanıyor
- İç İçe Yapılar: Gerçek dünyadaki PDF'lerde genellikle çok düzeyli sayfa ağaçları bulunur
- Nesne Türleri: PDF'ler sayfaların ötesinde yazı tipleri, resimler, formlar ve meta veriler içerir
- Performans Optimizasyonu: Büyük belgeler yavaş yükleme ve bellek yönetimi gerektirir
- Doğrulama Stratejileri: Kapsamlı kontrol, ince hataları önler
- Araç Entegrasyonu: Profesyonel araçlar hata ayıklama ve analiz yeteneklerini geliştirir
Geliştirme İçin En İyi Uygulamalar
- Şartnameyi Takip Edin: ISO 32000 yetkili PDF yapısını tanımlar
- Savunma Amaçlı Programlamayı Uygulayın: Belge yapısına ilişkin varsayımları her zaman doğrulayın
- Uygun Araçları Kullanın: Hata ayıklama için mevcut PDF analiz araçlarından yararlanın
- Kapsamlı Bir Şekilde Test Edin: Farklı PDF oluşturucular farklı yapılar üretir
- Akıllıca Önbelleğe Alın: Bellek kullanımını performans gereksinimleriyle dengeleyin
Gerçek Dünya Uygulaması
Bu kılavuzdaki kavramlar aşağıdakiler için geçerlidir:
- PDF Görüntüleyiciler: Sayfa sıralamasını ve oluşturulmasını düzeltin
- Belge İşlemciler: Sayfa çıkarma, birleştirme ve değiştirme
- Erişilebilirlik Araçları: Ekran okuyucuların yapısını anlama
- Arşiv Sistemleri: Uzun süreli belge koruma
- Güvenlik Analizi: Adli analizin yapısını anlamak
Temel Çıkarımlar
PDF sayfa sıralaması küçük bir teknik detay gibi görünebilir, ancak yanlış yapılması, takip edilmesi zor olan ince hatalara neden olabilir. Temel prensip basittir: dosyadaki nesnelerin fiziksel düzenine değil, her zaman PDF spesifikasyonunda tanımlanan mantıksal yapıya saygı gösterir.
Bu kavramları anlayarak ve bunları doğru şekilde uygulayarak, gerçek dünyadaki belgelerin tüm karmaşıklığının üstesinden gelebilecek PDF işleme uygulamaları oluşturabilirsiniz. İster basit bir sayfa çıkarıcı ister karmaşık bir belge yönetim sistemi oluşturuyor olun, bu temel size iyi hizmet edecektir.
Unutmayın: PDF'ler belirli kuralları olan yapılandırılmış belgelerdir. Kodunuzda bu kurallara uymak, daha iyi uyumluluk, daha az kullanıcı şikayeti ve daha sağlam uygulamalar sağlar. PDF yapısının anlaşılmasına yapılan yatırım, hata ayıklama süresinin azalması ve kullanıcı memnuniyetinin artmasıyla karşılığını verir.