İçeriğe geç

Document: DPart'lar, bölme / birleştirme ve satıcı uzantıları

Document modülü, sayfa içeriğiyle değil, Portable Document Format (PDF) dosyalarının bütünüyle çalışır. Düzenlemeye tabi iş akışlarının meta veri eklemek için kullandığı Document Part hiyerarşisini oluşturur. Bir PDF’yi sayfa aralıklarına göre parçalara ayırır, birkaç PDF’yi tek bir dosyada birleştirir ve geliştirici uzantılarını belge kataloğuna kaydeder.

Terminal window
composer require nextpdf/core:^3

Bu modül, sayfa içeriği katmanının üzerinde yer alır. Graphics ve Content operatör yayarken, Document yapısal düzeyde çalışır: sayfa ağaçları, belge kataloğu ve Document Part ağacı.

Bir Document Part (DPart), bir PDF’nin mantıksal bölümüdür. ISO 32000-2, düğümleri Document Part Metadata (DPM) taşıyan bir DPart hiyerarşisi tanımlar. İlaç, hukuk veya arşivleme iş akışı gibi düzenlemeye tabi bir iş akışı, meta veriyi dosyanın tamamı yerine sayfaların bir alt aralığıyla ilişkilendirebilir — §14.12. DPart, değişmez bir readonly düğümdür: yaprak düğüm, ardışık sayfa dizinlerinden oluşan bir diziye başvurur; ara düğüm ise alt DPart düğümlerini bir ağaçta gruplar. DPartRoot, Writer’ın serileştirdiği ağaç köküdür. Yaprak düğümün /Start ve /End girişleri, sayfa dizini tam sayıları değil, sayfa nesnelerine dolaylı başvurulardır — §14.12. DPart::resolveWithPageObjects(), bu girişleri writer tarafından sağlanan sayfa dizini→nesne numarası eşlemesini kullanarak çözer ve /Start (ve isteğe bağlı /End) başvuru biçimini döndürür. Yalnızca eşlemenin kullanılamadığı test yollarında tam sayı biçimine geri döner.

PdfMerger ve PdfSplitter, belge oluşturma yüzeyidir. PdfMerger, birden çok girdi PDF’sindeki sayfa nesnelerini birleştirir, çakışmaları önlemek için nesneleri yeniden numaralandırır ve tek bir sayfa ağacı ile çapraz başvuru tablosunu yeniden oluşturur. Ürettiği sayfa ağacı, dengeli bir Pages düğümüdür; Kids ile Count girişlerini ve PDF’nin sayfa ağacı düğümleri için tanımladığı kalıtsal nitelik modelini taşır — §7.7.3. PdfSplitter bunun tersini yapar: sayfa aralıklarını bağımsız SplitDocument nesnelerine çıkarır. PageRange, her iki sınıfın da kullandığı değer nesnesidir. 1 tabanlıdır, sınırlarını doğrular ve contains(), count() ile toArray() yanıtlarını verir.

VendorExtensionRegistry, ExtensionsDictionary ve DeveloperExtensionEntry, belge kataloğundaki geliştirici uzantıları sözlüğünü modeller. Bir motor, temel belirtimin ötesinde bir satıcı uzantı düzeyi bildirmek için bu sözlüğü kullanır. Kayıt defteri, aynı satıcı önekinin çakışacak biçimde yeniden kaydedilmesini VendorExtensionRegistryConflictException ile reddeder. CollectionDictionary ve CollectionSort, bir PDF koleksiyonu (taşınabilir koleksiyon veya portföy) katalog girişini modeller.

SınıfTemel yöntemlerRol
DPartisLeaf(), hasMetadata(), resolveWithPageObjects(), write()Değişmez Document Part düğümü (@since 1.12.0)
DPartRootisEmpty(), write()Writer’ın serileştirdiği DPart ağaç kökü (@since 1.12.0)
PdfMergermerge(array $pdfFiles, int $maxFiles = 100, int $maxTotalBytes = 200_000_000), append()Nesne yeniden numaralandırmalı çoklu PDF birleştirme (@since 1.9.0)
PdfSplittersplit(), splitEvery(), extractPages()Sayfa aralığını SplitDocument öğelerine bölme (@since 1.9.0)
PageRangecontains(int $page), count(), toArray()1 tabanlı sayfa aralığı değer nesnesi
MergeResult / SplitResultisValid(), count(), document(), totalOutputSize()Oluşturma sonucu nesneleri
VendorExtensionRegistryuzantı kaydıGeliştirici uzantıları kayıt defteri (@since 2.2.0)
ExtensionsDictionarywithEntry(), entries(), isEmpty(), toPdfDictionary()Değişmez uzantılar sözlüğü oluşturucusu (@since 2.0.0)
CollectionDictionarytoPdfDictionary()Taşınabilir koleksiyon katalog girişi (@since 2.0.0)

Tam PHPDoc tablosunu oluşturmak için composer docs:generate-api-php -- --module=Document komutunu çalıştırın.

Bir PDF’yi tek sayfalık belgelere ayırın, ardından sonucu inceleyin.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Document\PageRange;
use NextPDF\Document\PdfSplitter;
$splitter = new PdfSplitter();
$result = $splitter->splitEvery(file_get_contents('/srv/in/report.pdf'), 1);
foreach (range(0, $result->count() - 1) as $index) {
$segment = $result->document($index);
file_put_contents("/srv/out/page-{$index}.pdf", $segment->pdfData);
}
$singlePage = $splitter->extractPages(
file_get_contents('/srv/in/report.pdf'),
new PageRange(2, 4),
);

Birkaç PDF’yi tanımlı bir girdi bütçesiyle birleştirin, ardından birleştirilmiş çıktıyı yazmadan önce sonucu doğrulayın.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Document\PdfMerger;
use NextPDF\Exception\PageLayoutException;
/** @var list<string> $sources Raw PDF byte strings to combine. */
$sources = array_map(
static fn (string $path): string => file_get_contents($path),
glob('/srv/batch/*.pdf') ?: [],
);
$merger = new PdfMerger();
try {
// Bound the merge: at most 50 files, 100 MB total.
$merged = $merger->merge($sources, maxFiles: 50, maxTotalBytes: 100_000_000);
} catch (PageLayoutException $e) {
throw new \RuntimeException('Merge rejected: empty or invalid input set.', previous: $e);
}
if (!$merged->isValid()) {
throw new \RuntimeException('Merged document failed structural validation.');
}
file_put_contents('/srv/out/combined.pdf', $merged->pdfData);
  • PdfMerger::merge() ve PdfSplitter::split(), girdi sınırlarını ResourceGuard aracılığıyla uygular. Çok fazla dosya veya çok fazla bayt içeren girdiler, sessizce kırpmak yerine bir istisna oluşturur. maxFiles / maxTotalBytes değerlerini iş yükünüze göre bilinçli olarak ayarlayın.
  • Boş dosya listesi veya boş aralık listesi PageLayoutException oluşturur. Bunları boş sonuçlar olarak değil, yapılandırma hataları olarak değerlendirin.
  • PageRange 1 tabanlıdır ve uç değerleri kapsar. Yaprak DPart’ın pages listesi 0 tabanlı sayfa dizinleridir. İki soyutlama farklı dizin tabanları kullanır. Bunlar arasında geçiş yaparken açıkça dönüştürün.
  • DPart readonly türündedir. Farklı bir ağaç oluşturmak için, var olanı değiştirmek yerine yeni düğümler oluşturun. resolveWithPageObjects(), tam sayı dizinli geri dönüş biçimini yalnızca sayfa nesnesi eşlemesi boş olduğunda döndürür. Üretim çıktısında bu yola güvenmeyin.
  • VendorExtensionRegistry, yinelenen bir satıcı öneki için VendorExtensionRegistryConflictException oluşturur. Her öneki yalnızca bir kez kaydedin.

Bölme ve birleştirme, sayfa sayısıyla doğrusal olarak ölçeklenir; modülün kendi kayıt tutma yükünden çok, ayrıştırma ve nesne yeniden numaralandırması tarafından belirlenir. Varsayılan referans iş yükü, 1500 ms duvar saati / 64 MB tepe bütçesine sığar. Büyük birleştirmeler, esas olarak toplam girdi baytlarıyla sınırlanır. maxTotalBytes koruması, tepe bellek kullanımını sınırlı tutar. Yeniden üretilebilirlik profili structural’dir: birleştirilmiş veya bölünmüş bir PDF yeni bir fragman ve /ID taşır; bu nedenle iki çalıştırma yapısal olarak eşittir ancak bayt düzeyinde özdeş değildir.

PdfMerger::merge() ve PdfSplitter::split(), güvenilmeyen PDF baytlarını kullanır. Ayrıştırmadan önce, her ikisi de girdiyi ResourceGuard::assertSize() / assertCount() üzerinden geçirir; bu da sıkıştırma açma veya nesne sayısını artırma kaynaklı hizmet reddi saldırısını sınırlar. Varsayılanlara güvenmek yerine, dağıtım için maxFiles, maxTotalBytes ve maxBytes argümanlarını sıkı tutun. Her girdi PDF’sini potansiyel olarak kötü niyetli kabul edin. Kaynaklar kullanıcı tarafından sağlandığında, toplu oluşturmayı kısıtlanmış bir işçide çalıştırın. Güven sınırı için /modules/core/security/ bölümündeki motor tehdit modeline bakın.

Bu modülün oluşturduğu DPart ağacı, ISO 32000-2 §14.12’deki Document Part modelini izler; yaprak /Start ve /End girişleri aynı madde kapsamında sayfa nesnelerine dolaylı başvurular olarak yayımlanır. Birleştirilmiş çıktı, §7.7.3’te tanımlanan sayfa ağacı düğüm yapısını kullanır. Bunlar, src/Document/ tarafından üretilen ve tests/Unit/Document/ (DPartTest, DPartRootTest, DPartPageRefTest, DocumentPdfMergerDeepTest, DocumentPageRangeParseDeepTest) tarafından test edilen uygulama davranışlarıdır. Bunlar, uçtan uca PDF 2.0 uyumluluğunun bir beyanı değildir. Tam belge uyumluluğu, /modules/core/conformance/ bölümünde açıklanan oracle ve golden paketleri tarafından doğrulanır.