Bir PDF'i bölün ve sayfa aralıklarını çıkarın
Bir bakışta
“Bir bakışta” başlıklı bölümTek bir PDF’iniz var ve birden fazlasına ihtiyacınız var. Bu tarif, Core bölme
yüzeyi olan NextPDF\Document\PdfSplitter ile tek bir belgeyi birden çok dosyaya
ayırır. Kaynağı ham bir PDF bayt dizesi olarak geçirir ve hangi sayfaları
istediğinizi tarif edersiniz. Bölücü, kaynağı nesne grafiği üzerinden ayrıştırır,
istenen her aralığın erişilebilir nesnelerini kendi sayfa ağacına ve çapraz
referans tablosuna sahip taze, yeniden numaralandırılmış bir belgeye kopyalar ve
uyumlu bir okuyucuda açılan, yapısal olarak eksiksiz PDF’leri geri verir.
Bu, birleştirme tarifinin tersidir: birleştirme birçok belgeyi tek bir belgede oluşturur, bölme bir belgeyi birçok belgeye ayrıştırır. Aynı yüzey, en sık ihtiyaç duyduğunuz üç görevi kapsar:
- Aralıklara göre bölme — adlandırdığınız her sayfa aralığı için bir çıktı belgesi üretin.
- Her N sayfada bir bölme — uzun bir dosyayı sabit boyutlu parçalara kesin.
- Bir aralığı çıkarma — tek bir bitişik sayfa aralığını tek bir belgeye alın.
Bölme işlemi süreç içinde, başsız tarayıcı veya ağ çağrısı olmadan çalışır.
Core’un kurulu olması (composer require nextpdf/core:^3) ve okunabilir bir PDF
gerekir.
Kurulum
“Kurulum” başlıklı bölümcomposer require nextpdf/core:^3Kavramsal genel bakış
“Kavramsal genel bakış” başlıklı bölümBir PDF, sayfalarına bir /Pages düğümünde köklenen bir sayfa ağacı üzerinden
ulaşır ve her dolaylı nesneye çapraz referans verisi (bir tablo veya bir akış)
aracılığıyla erişir. Sayfaları bayt dilimleyerek çıkaramazsınız: tek bir sayfa,
dosyanın başka bir yerinde bulunan paylaşılan yazı tiplerine, görüntülere ve
kaynak sözlüklerine atıfta bulunur ve çapraz referans konumları artık geçerli
olmaz.
PdfSplitter asıl işi yapar. Her aralık için, istenen sayfa nesnelerinden nesne
grafiğini gezer, erişilebilir nesne kapanışını toplar, bu nesneleri taze bir
adres alanında yeniden numaralandırır, tek sayfa ağaçlı bir belge yeniden inşa
eder ve PDF 2.0 yapısına uygun gerçek bir çapraz referans tablosu yayar
(ISO 32000-2:2020, çapraz referans tablosu §7.5.4, sayfa ağacı §7.7.3). Her çıktı,
bir parça değil, kendi kendine yeten bir belgedir.
Sayfa numaraları 1 tabanlı ve dâhildir. Bir aralık, bir
NextPDF\Document\PageRange değer nesnesidir: new PageRange(2, 5), 2’den 5’e
kadarki sayfalar anlamına gelir. Yapıcı, kendi değişmezlerini doğrular —
NextPDF\Exception\PageLayoutException fırlatarak 1’in altındaki bir başlangıcı
veya başlangıçtan önce gelen bir sonu reddeder — böylece imkânsız bir aralık,
bölücünün derinlerinde değil, oluşturma anında başarısız olur. PageRange::parse()
ve PageRange::all(), hatalı biçimlendirilmiş bir belirtimde veya pozitif olmayan
bir sayfa toplamında aynı PageLayoutException’ı fırlatır.
API yüzeyi
“API yüzeyi” başlıklı bölümnew NextPDF\Document\PdfSplitter() üç yöntem sunar. Hepsi kaynağı, asla bir yol
değil, ham bir PDF bayt dizesi olarak alır.
split(string $pdfData, array $ranges, int $maxBytes = 100_000_000, int $maxRanges = 1000): SplitResult$rangesiçindeki herPageRangeiçin sırayla bir çıktı belgesi üretir. İki sınır parametresi, girdi boyutunu ve aralık sayısını sınırlandırır.splitEvery(string $pdfData, int $pagesPerSegment): SplitResultbelgeyi her biri$pagesPerSegmentsayfa olan sabit boyutlu parçalara keser; son parça kalanı tutar.extractPages(string $pdfData, PageRange $range): SplitDocumenttek bir aralığı çıkarır ve doğrudan o tek belgeyi döndürür.
split() ve splitEvery(), bir readonly nesne olan ve $documents (parçaların
bir listesi), $totalPages (kaynaktaki sayfalar) ve $sourceSize taşıyan bir
NextPDF\Document\SplitResult döndürür. count(), sıfır tabanlı bir dizine göre
bir parça getirmek için document(int $index) ve totalOutputSize() sunar.
Her parça ve extractPages()’in dönüş değeri, bir NextPDF\Document\SplitDocument
nesnesidir: $pdfData (parça baytları), $range, $pageCount, $sizeBytes ve
isValid() yardımcısını sunan bir readonly nesne. isValid() dar bir
%PDF-başlık akıl sağlığı denetimidir — parça baytları %PDF ile başladığında
true döndürür — bir belge yapısı veya uyumluluk doğrulaması değildir; bölücünün
bir PDF ürettiğini onaylar, dosyanın tam olarak uyumlu olduğunu değil.
Bir PageRange’i doğrudan new PageRange($start, $end) ile oluşturur ya da
insan tarafından okunabilir bir belirtimi PageRange::parse('1-3,5,7-10') ile
ayrıştırırsınız; bu, split()’e iletmeye hazır bir list<PageRange> döndürür.
PageRange::all($totalPages), belgenin tamamını kapsayan tek bir aralık döndürür.
Kod örneği — Hızlı başlangıç
“Kod örneği — Hızlı başlangıç” başlıklı bölümBu örnek tek bir dosyayı okur ve onu iki belgeye böler: 1’den 3’e kadarki sayfalar ve 4’ten 6’ya kadarki sayfalar. Çağrı şeklini göstermek için hata işlemeyi atlar; aşağıdaki üretim örneği tüm korumaları ekler.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Document\PageRange;use NextPDF\Document\PdfSplitter;
$splitter = new PdfSplitter();
$result = $splitter->split( file_get_contents(__DIR__ . '/report.pdf'), [ new PageRange(1, 3), new PageRange(4, 6), ],);
foreach ($result->documents as $i => $segment) { file_put_contents(__DIR__ . sprintf('/part-%d.pdf', $i + 1), $segment->pdfData);}
printf("Split %d-page source into %d document(s).\n", $result->totalPages, $result->count());Kod örneği — Üretim
“Kod örneği — Üretim” başlıklı bölümBu kendi kendine yeten program, bellekte küçük bir çok sayfalı belge oluşturur,
böylece harici bir dosya olmadan çalışır. Üç işlemin tümünü gösterir — aralıklara
göre bölme, her N sayfada bir bölme ve tek bir aralığı çıkarma. Aralığa göre
parçaları ve çıkarılan kuyruğu doğrulayıp yazar ve boyuta göre sonucu bir sayım
olarak bildirir, böylece neredeyse aynı üç yazma döngüsü olmadan her çağrı şeklini
görürsünüz. Bölme yüzeyinin fırlattığı istisnaları yakalar ve her birini yutmak
yerine bağlamla yeniden fırlatır. Bellek içi kaynağı kendi file_get_contents()
okumanız veya nesne deposu getirmeniz ile değiştirin.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use InvalidArgumentException;use NextPDF\Core\Document;use NextPDF\Document\Merge\UnsupportedSourceDocumentException;use NextPDF\Document\PageRange;use NextPDF\Document\PdfSplitter;use NextPDF\Document\SplitDocument;use NextPDF\Exception\PageLayoutException;
/** * Build a tiny labelled multi-page PDF so the program is self-contained. * * In your own code, replace this with a read of the PDF you want to split, * for example file_get_contents($path). */function buildSample(int $pages): string{ $doc = Document::createStandalone(); $doc->setTitle('Split sample');
for ($page = 1; $page <= $pages; $page++) { $doc->addPage(); $doc->setFont('helvetica', '', 12); $doc->cell(0, 10, sprintf('Source page %d', $page), newLine: true); }
return $doc->getPdfData();}
$source = buildSample(7);
$splitter = new PdfSplitter();
try { // 1. Split into named ranges: one output per PageRange, in order. $byRange = $splitter->split( $source, PageRange::parse('1-3,4-6'), maxBytes: 50_000_000, maxRanges: 100, );
// 2. Split every 2 pages: segments of [1-2], [3-4], [5-6], [7] (remainder). $bySize = $splitter->splitEvery($source, 2);
// 3. Extract a single range as one document. $tail = $splitter->extractPages($source, new PageRange(7, 7));} catch (InvalidArgumentException $e) { // Raised on an oversized input, an empty range list, or too many ranges. throw new RuntimeException('Split rejected its input: ' . $e->getMessage(), previous: $e);} catch (PageLayoutException $e) { // Raised when a range exceeds the source page count, and also by the // PageRange constructor / PageRange::parse() on an invalid or malformed range. throw new RuntimeException( sprintf('Range out of bounds (page %d): %s', $e->getPageNumber(), $e->getConstraint()), previous: $e, );} catch (UnsupportedSourceDocumentException $e) { // Raised fail-closed on an encrypted, signed, or form-bearing source. throw new RuntimeException('Source cannot be split: ' . $e->getMessage(), previous: $e);}
printf( "Source has %d page(s). By-range produced %d doc(s); by-size produced %d doc(s).\n", $byRange->totalPages, $byRange->count(), $bySize->count(),);
foreach ($byRange->documents as $i => $segment) { emitSegment(sprintf('range-%d', $i + 1), $segment);}
emitSegment('tail', $tail);
/** * Validate a segment and write it to the cookbook side-channel directory, * or to the script directory by default. */function emitSegment(string $name, SplitDocument $segment): void{ if (!$segment->isValid()) { throw new RuntimeException(sprintf('Segment "%s" failed its %%PDF header check.', $name)); }
$dir = getenv('NEXTPDF_COOKBOOK_OUTPUT'); $dir = $dir !== false && $dir !== '' ? $dir : __DIR__; $path = sprintf('%s/%s.pdf', rtrim($dir, '/'), $name);
if (file_put_contents($path, $segment->pdfData) === false) { throw new RuntimeException(sprintf('Could not write segment to "%s".', $path)); }
printf("Wrote %s: pages %d-%d, %d bytes.\n", $name, $segment->range->start, $segment->range->end, $segment->sizeBytes);}Beklenen standart çıktı (bayt boyutları derlemeye bağlıdır):
Source has 7 page(s). By-range produced 2 doc(s); by-size produced 4 doc(s).Wrote range-1: pages 1-3, <n> bytes.Wrote range-2: pages 4-6, <n> bytes.Wrote tail: pages 7-7, <n> bytes.Sınır durumları ve püf noktaları
“Sınır durumları ve püf noktaları” başlıklı bölüm- Kaynak bir yol değil, bayttır. Her yöntem ham bir PDF dizesi alır. Dosyayı
önce
file_get_contents()ile okuyun ya da baytları nesne deposundan çekin. Bir yol geçirmek, kaynağın ayrıştırılamamasına yol açar. - Sayfa numaraları 1 tabanlı ve dâhildir.
new PageRange(1, 3), 1, 2 ve 3. sayfaları kapsar — üç sayfa. 1’in altındaki bir başlangıç veya başlangıçtan önce gelen bir son,PageRangeyapıcısının kendisindenPageLayoutExceptionfırlatır. - Sonun ötesindeki bir aralık bir hatadır, bir kırpma değil. Bir aralığın
sonu kaynak sayfa sayısını aşarsa,
split()PageLayoutExceptionfırlatır; aralığı asla sessizce son sayfaya kırpmaz. Aralıklarınız çağıran tarafından sağlanıyorsa önce sayfa sayısını inceleyin. splitEvery()kalanı tutar. Son parça geriye kalan sayfaları tutar; bu nedenle her 2 sayfada bir bölünen 7 sayfalık bir belge dört parça verir: üçü 2 sayfalık ve biri 1 sayfalık.$pagesPerSegmenten az 1 olmalıdır, aksi takdirde birInvalidArgumentExceptionalırsınız.- Boş bir aralık listesi reddedilir.
$ranges === []ilesplit()birInvalidArgumentExceptionfırlatır. Çağırmadan önce en az bir aralık oluşturun. - Sınırlar kesmek yerine fırlatır.
maxBytesveyamaxRangesdeğerinin aşılması birInvalidArgumentExceptionfırlatır. Bölücü, aşırı boyutlu bir girdiyi asla kısmen işlemez; bu nedenle her iki sınırı da iş yükünüze göre ayarlayın. - Şifrelenmiş, imzalanmış ve form taşıyan kaynaklar kapalı arıza verir.
Şifrelenmiş bir kaynak (anahtar olmadan kopyalanamaz), dijital olarak
imzalanmış bir kaynak (yeniden sayfalandırma, imzanın bayt aralığını geçersiz
kılar) ya da etkileşimli bir form taşıyan bir kaynak (bir alanın araç birimleri
düşürülen sayfalarda yer alıp öksüz kalabilir)
UnsupportedSourceDocumentExceptionfırlatır. Bölücü, bozuk veya tehlikeye girmiş bir belge yaymaktansa reddeder. Bir form belgesini bölmek, bu sürümün bilinen bir kısıtlamasıdır. UnsupportedSourceDocumentException,Mergead alanı altında bulunur. Tam nitelenmiş adıNextPDF\Document\Merge\UnsupportedSourceDocumentException’dır. Bir bölme sayfasındaki oMergeyolu bir kopyala/yapıştır hatası değildir: bir kaynak güvenle kopyalanamadığında hem birleştirme hem de bölme yüzeylerinin fırlattığı tek, paylaşılan kaynak-belge reddetme istisnasıdır. Onu o ad alanından içe aktarın.- Çıktı yapısal olarak tazedir, bayt kararlı değildir. Her parça kendi
kataloğuna, sayfa ağacına ve fragmanına sahip yeni bir belgedir. Aynı girdi
üzerinde yapılan iki çalıştırma yapısal olarak eşittir, ancak baytça aynı olacağı
garanti edilmez — bu nedenle
structuralyeniden üretilebilirlik profili.
Başarım
“Başarım” başlıklı bölümBölme, tüm aralıklarda kopyalanan sayfa sayısıyla doğrusaldır. İşin baskın kısmı,
bölücünün kendi defter tutması değil, kaynağın ayrıştırılması ve her aralığın nesne
kapanışının kopyalanmasıdır. Kaynak bellekte bir dize olarak tutulur ve her
parçanın baytları siz yazana kadar tutulur; bu nedenle tepe bellek, kaynak boyutu
artı ürettiğiniz en büyük aralık kadar olur. maxBytes koruması, bu tepenin kaynak
tarafını sınırlı tutar. Yüksek hacimli işlem hatları için, hatalı biçimlendirilmiş
veya aşırı boyutlu bir girdinin belleği tüketmek yerine hızlıca başarısız olması
için maxBytes ve maxRanges değerlerini iş yükünüzün ihtiyaç duyduğu en küçük
değerlere ayarlayın.
Güvenlik notları
“Güvenlik notları” başlıklı bölümBölme süreç içinde çalışır; hiçbir belge baytı ana makineden ayrılmaz ve hiçbir ağ çağrısı yapılmaz. Her kaynak PDF’i güvenilmeyen girdi olarak ele alın:
- Sınırları sıkı tutun.
maxBytesvemaxRanges, hizmet reddi girdisine karşı ilk savunma hattınızdır. Yüklemeleri kabul eden herhangi bir yüzey için bunları cömert varsayılanlara değil, gerçek tavanınıza ayarlayın. - Bölmeden önce ayıklayın. Şifrelenmiş veya imzalanmış bir kaynak kapalı arıza verir, ancak bu koşulları daha erken saptayabilirsiniz. Güvenilmeyen girdileri önce Core denetçisinden geçirin. Daha ağır işlemden önce şifrelemeyi, imzaları ve risk işaretlerini bayraklayan sınırlı bir tarama için Bir PDF’i ayrıştırın ve inceleyin sayfasına bakın.
- Kullanıcı girdisini asla bir yola eklemeyin. Bu tarif, sabit bir dizine veya cookbook yan kanalına yazar. Yol geçişini önlemek için çıktı yollarını ve parça adlarını bir istek alanından değil, sunucu tarafından denetlenen değerlerden türetin.
- Çıktıda gizli bilgi yok. Parça dosyalarını, görmemesi gereken bir istemciye iç tanımlayıcıları ortaya çıkaran bir konuma ya da adla yazmayın.
Uyumluluk
“Uyumluluk” başlıklı bölümBu tarif, kendine ait normatif bir standart iddiasında bulunmaz. Tek bir belgeyi
Core bölme yüzeyi üzerinden ayrıştırır ve her parçayı SplitDocument::isValid()
%PDF-başlık denetimiyle akıl sağlığı açısından denetler — bölücünün bir PDF
yaydığına dair bir varlık denetimi, bir uyumluluk veya belge yapısı doğrulaması
değil. PdfSplitter’ın her parça için yeniden inşa ettiği sayfa ağacı ve çapraz
referans yapıları, /modules/core/document/ referansında tarif edilen PDF 2.0
yapılarıdır (ISO 32000-2:2020, çapraz referans tablosu §7.5.4, sayfa ağacı §7.7.3).
Sürüm, sayfa sayısı, şifreleme ve imza bayrakları dâhil olmak üzere herhangi bir
girdi veya çıktı belgesinin yapısal okuması için
Bir PDF’i ayrıştırın ve inceleyin
sayfasında belgelenen Core denetçisini kullanın.
Ayrıca bakınız
“Ayrıca bakınız” başlıklı bölüm- Document modülü referansı — tam bölme, birleştirme ve belge-parça yüzeyi.
- Harici PDF’leri birleştirin — ters tarif: birçok belgeyi tek bir belgede oluşturun.
- Bir PDF’i ayrıştırın ve inceleyin — bölmeden önce güvenilmeyen girdileri ayıklayın.
- İstisna farkındalıklı hata işleme
—
PageLayoutExceptionveUnsupportedSourceDocumentExceptionarkasındaki NextPDF istisna hiyerarşisi.