İçeriğe geç
getnextpdf.com

Her seferinde aynı byte'lar: yeniden üretilebilir PDF'ler

Spec: ISO 32000-2, §14.4Spec: ISO 32000-2, §14.3.3

Aynı girdilerden bir PDF’yi iki kez kurun ve aynı dosyayı beklersiniz. Çoğu PDF kütüphanesi bunu vaat edemez — yeniden kurun ve diff’leyin, byte’lar sürüklenir. NextPDF, hareket eden iki şeyi sabitleyebilir; böylece aynı girdiler her seferinde aynı byte’ları üretir.

Byte-özdeş çıktı bir gösteriş metriği değildir. Ekiplerin gerçekten istediği üç şeyin altındaki temeldir.

Birincisi önbelleğe almadır. Bir kurulum, girdilerinin saf bir işleviyse, çıktısının özeti bir önbellek anahtarıdır. Aynı girdiler, aynı özet, işi atla ve saklanan dosyayı sun. Byte’lar gezindiğinde, özet de gezinir ve önbellek asla isabet etmez.

İkincisi kurcalama-kanıtıdır. Gönderdiği dosyayı tam olarak yeniden üretebilen bir hat, sonradan, arşivlenmiş bir belgenin değiştirilmediğini kanıtlayabilir: yeniden kur, ikisinin de özetini al, karşılaştır. Gömülü bir saat yüzünden tek bir byte bile farklıysa, kanıt gitmiştir ve “bana güven”e geri dönmüşsünüzdür.

Üçüncüsü güvenilir CI’dır. Bir golden-file testi, bilinen-iyi bir çıktıyı kaydeder ve bir değişiklik onu değiştirdiğinde başarısız olur. O sinyal yalnızca, değişmemiş bir motor değişmemiş bir dosyayı yeniden ürettiğinde anlamlıdır. Her çalıştırma bir zaman damgasında farklılaşırsa, golden file gürültüdür ve ekip kırmızı bir kurulumu yok saymayı öğrenir — testteki en pahalı alışkanlık.

NextPDF’nin deterministik profilinde, aynı kurulumlar arasında aksi takdirde sürüklenecek olan, motor tarafından kontrol edilen iki alan, tarihler ve /ID’dir. Bu, hattın geri kalanının zaten kararlı olduğunu varsayar — aynı girdiler ve kendiliğinden değişmeyen bir serileştirme (bununla ilgili daha fazlası aşağıda):

  • Gömülü tarihler. Document information sözlüğü CreationDate ve ModDate’i taşır (Spec: ISO 32000-2, §14.3.3) ve XMP meta verisi onları yansıtır. Kurulum zamanında “şimdi”yi yakalayın ve her yeniden kurulum farklılaşır.
  • Dosya tanımlayıcısı. /ID dizisi, dosyayı tanımlayan bir çift byte dizgesidir (Spec: ISO 32000-2, §14.4), trailer sözlüğünde saklanır (Spec: ISO 32000-2, §7.5.5). Kütüphaneler onu genellikle güncel zamandan artı rastgele byte’lardan türetir; bu yüzden tasarım gereği her çalıştırmada farklıdır.

İkisini de sabitleyin — sabit bir zaman damgası ve /ID için sabit bir tohum — ve çıktı, içeriğinin deterministik bir işlevi hâline gelir. İçeriği rahat bırakın ve dosya byte byte özdeş olur. Bu, Reproducible Builds projesinin derlenmiş yazılım için kurduğu, belge katmanına uygulanan aynı disiplindir.

NextPDF’de determinizm bir test hilesi değil, bir yapılandırma nesnesidir. Motor, NextPDF\Core ad alanında bir DeterministicSettings değer nesnesi sunar. final readonly, değişmezdir ve yukarıda adı geçen, saat ve rastgeleden türeyen tam iki sürüklenme kaynağını sabitler: tarihler ve /ID. Onları sabitlemek, en yaygın iki sürüklenme kaynağını ortadan kaldırır, ama tek başına byte-özdeş çıktıyı garanti etmez. Motorun diğer serileştirme davranışı — nesne sıralaması, font alt kümeleme ve sıkıştırma ayarları — de çıktının yeniden üretilebilmesi için deterministik olmalıdır ve NextPDF bunları tasarım gereği kararlı tutar.

Kurucusu iki bağımsız değişken alır:

public function __construct(
public DateTimeImmutable $timestamp,
public string $fileIdSeed,
) {
// ...
}

$timestamp, her tarih alanına yazılan tek sabit andır — CreationDate, ModDate ve onların XMP yansıması. Tek bir DateTimeImmutable geçirin ve belge, duvar saatine kaçta olduğunu sormayı bırakır. $fileIdSeed, trailer /ID’sini sabitleyen girdidir: 32 karakterlik bir on altılık (hexadecimal) dizge. Aynı tohumu verin ve motor, saati ve bir rastgele kaynağı örneklemek yerine aynı dosya tanımlayıcısını türetir.

Nesne kendi girdisini doğrular. Tohum tam olarak 32 on altılık karakter olmalıdır; başka herhangi bir şey, farklı görünen bir /ID sessizce üretmek yerine kurulumda bir InvalidConfigException ile reddedilir. Bu, motorun geri kalanının benimsediği aynı tahmin-etmeyi-reddet duruşudur — belirsiz bir girdi, byte’ları sessizce değiştirmek yerine yüksek sesle başarısız olur.

İkisi de sabitlenmiş hâlde, tarif, Reproducible Builds projesinin tanıdık kıldığıdır: yeniden kur, diff’le ve diff boştur.

  1. Fix the inputsThe same content, fonts, and settings that produced the original document.
  2. Pin the timestampOne DateTimeImmutable feeds CreationDate, ModDate, and the XMP dates — no wall clock.
  3. Pin the /ID seedA 32-character hex seed derives the trailer /ID instead of a clock-plus-random value.
  4. BuildThe output is now a pure function of content; the two moving parts are held still.
  5. Rebuild and diffRegenerate from the same inputs and compare bytes — an empty diff is the proof.
Reproducible build: identical inputs plus a pinned timestamp and a pinned /ID seed produce the same bytes, which a rebuild-and-diff step confirms.

Küçük, eksiksiz bir şekil. Ayarlar bir kez kurulur ve yeniden kullanılır; böylece aynı programın iki çalıştırması aynı dosyayı üretir.

<?php
declare(strict_types=1);
use NextPDF\Core\DeterministicSettings;
use NextPDF\Exception\InvalidConfigException;
// One fixed instant for every date field — never the wall clock.
$timestamp = new DateTimeImmutable('2026-01-01T00:00:00+00:00');
// A 32-character hex seed pins the trailer /ID. Same seed, same /ID.
$fileIdSeed = '0123456789abcdef0123456789abcdef';
try {
$deterministic = new DeterministicSettings(
timestamp: $timestamp,
fileIdSeed: $fileIdSeed,
);
} catch (InvalidConfigException $e) {
// A malformed seed (not exactly 32 hex chars) is refused here,
// before any document is built — not silently coerced.
error_log($e->getMessage());
throw $e;
}
// Hand $deterministic to the document configuration. With both moving
// parts pinned, building the same content twice yields identical bytes:
//
// sha256(build_one) === sha256(build_two)

Tohum, kontrol ettiğiniz bir kurulum girdisidir, bir sır değil. Onu kurulum yapılandırmanızın geri kalanının yanında saklayın. Asıl mesele, onun sabit olmasıdır; böylece ürettiği dosya tanımlayıcısı da sabittir.

İlk tuzak, “Zaman damgasını kaldırdım, yani kurulumum artık yeniden üretilebilir”dir. Genellikle değildir; çünkü /ID dizisi, iki kaynağın daha sessiz olanıdır. Tarihler bir meta veri panelinde görünür ve hatırlaması kolaydır; trailer /ID ise çoğu okuyucuya görünmezdir ve her çalıştırmada saatten ve bir rastgele kaynaktan yeniden üretilir. Yalnızca tarihleri sabitleyen bir kurulum, yine de her seferinde farklı bir dosya üretir. İkisini birden sabit tutmalısınız.

İkinci tuzak, determinizmi tek başına bir güvenlik özelliği olarak ele almaktır. Sabitlenmiş bir /ID, bir dosyayı yeniden üretilebilir kılar; onu imzalı kılmaz ve tek başına iki kurulumun eşleştiğini kanıtlamaz. Byte byte bir karşılaştırma ya da bir özet, kurulumların eşleştiğini kanıtlar; /ID’yi sabitlemek yalnızca yanıltıcı bir fark kaynağını ortadan kaldırır. Ve bunların hiçbiri, bir üçüncü tarafın dosyaya kefil olduğunu kanıtlamaz. Yeniden üretilebilirlik ve imzalama, ikamesi olmayan, tamamlayıcı katmanlardır.

Determinizm, motorun kendi hareket eden parçalarını sabitler. Sizin girdilerinizi sabitlemez. İçeriğiniz canlı bir zaman damgası gömüyorsa, diskte değişmiş bir font çekiyorsa ya da güncel tarihe bağlı bir değer oluşturuyorsa, çıktı değişir; çünkü girdi değişti — ve bu doğrudur. DeterministicSettings, motorun belirlenimsizliğini kaldırır, sizinkini değil. Yeniden üretilebilir bir kurulum, yine de yeniden üretilebilir girdiler gerektirir.

Deterministic byte-identical output — edition availability
EditionAvailability
Core

Full support. DeterministicSettings ships in the open-source core: pin the timestamp and the /ID seed and the same content rebuilds to the same bytes — no edition gate.

ProNot in this edition
EnterpriseNot in this edition
  • Golden-file testi — byte-özdeş çıktıya bağlı olan CI tekniği ve deterministik bir motorun neden onun ön koşulu olduğu.
  • Artımlı güncellemeler — bir PDF’nin ekleyerek nasıl büyüdüğü; bir dosyayı önceki sürümleriyle ilişkilendirmek için /ID dizisinin yeniden önem kazandığı yer.
  • Meta veri ve XMP paketi — gömülü tarihlerin nerede yaşadığı ve XMP paketinin document information sözlüğünü nasıl yansıttığı.
  • Bir PDF dosyasının anatomisi — trailer, cross-reference table ve /ID dizisinin dosya yapısında nerede oturduğu.
  • Byte-özdeş (byte-identical) — tam olarak, byte byte eşleşen iki dosya. “Aynı”nın en güçlü biçimi ve bir özetin ya da bir diff’in doğrulayabileceği biçim.
  • /ID (dosya tanımlayıcısı) — bir PDF’yi ve sürümlerini tanımlayan iki byte dizgesinden oluşan dizi (ISO 32000-2 §14.4), trailer sözlüğünde saklanır (§7.5.5). Genellikle saatten artı rastgele byte’lardan türetilir; bu yüzden her sabitlenmemiş kurulumda değişir.
  • Document information sözlüğüCreationDate ve ModDate’i taşıyan yapı (ISO 32000-2 §14.3.3). Deterministik bir kurulumun sabitlemesi gereken iki belirlenimsizlik kaynağından biri.
  • Golden file — bir testin karşı karşılaştırdığı, kaydedilmiş, bilinen-iyi bir çıktı; yalnızca değişmemiş bir motor değişmemiş bir dosyayı yeniden ürettiğinde anlamlıdır.
  • Yeniden üretilebilir kurulum (reproducible build) — çıktısı, girdilerinin deterministik bir işlevi olan bir kurulum; böylece aynı girdilerden yeniden kurmak aynı byte’ları verir. Terim, derlenmiş yazılım için Reproducible Builds projesinden gelir.