Teknik Makale

PDFlibPas TrimBox, BleedBox ve CropBox varsayılanları

Bir PDF sayfasında TrimBox yoksa etkin TrimBox, sayfanın CropBox'ıdır; CropBox da yoksa MediaBox'tır. BleedBox ile ArtBox aynı kuralı izler. Delphi için PDF kütüphanesi olan PDFlibPas bu varsayılan zincirini v3.539.44'ten beri GetPageBox, HasPageBox ve CapturePageEx'de tutarlı uygular ve bir /Pages düğümüne konan production kutularını yok sayar, çünkü ISO 32000-1 onların kalıtılmasına izin vermez

Bu bir dipnot gibi okunur, ta ki bir iş (job) dayayana dek. 6.25 × 9.25 inçlik bir MediaBox'ı, trim olan 6 × 9 inçlik bir CropBox'ı ve hiç TrimBox'ı olmayan — onu yazmayı ihracatçının aklına gelmemiş — bir kitap içi düşünün. Trim kutusunu isteyin, media kutusunu alın; ve baskı tabakanızdaki her hücre komşusuna bir sekizinci inçlik bleed ile slug sürükler. PDFlibPas'ın tam da bu alandaki kusurları v3.539.42 ile v3.539.44'te düzeltildi ve düzeltme biçimleri, page-box semantiğinin herhangi bir PDF kütüphanesinde nasıl uygulanması gerektiği hakkında bir şey söylüyor

Bir sayfada TrimBox yokken hangi kutu geçerlidir?

Yanıt, ISO 32000-1 §14.11.2'den gelen sabit bir varsayılan zinciridir: CropBox MediaBox'a, BleedBox, TrimBox ve ArtBox ise her biri CropBox'a varsayılanlaşır. MediaBox'a doğrudan varsayılanlaşan tek kutu CropBox'tır. Yalnızca MediaBox tanımlayan bir sayfanın bu yüzden beş özdeş kutusu, MediaBox artı CropBox tanımlayan bir sayfanın ise dört kutusu CropBox'a eşittir

KutuPDFlibPas BoxTypeYokken varsayılan/Pages'ten kalıtılır mı
MediaBox1Yok, girdi zorunludurEvet
CropBox2MediaBoxEvet
BleedBox3CropBoxHayır
TrimBox4CropBoxHayır
ArtBox5CropBoxHayır

İki adımlı zincir önemlidir, çünkü CropBox'ın kendisi de kalıtılabilir olabilir. Ne kendi TrimBox'ı ne de kendi CropBox'ı olan bir sayfanın etkin TrimBox'ı, CropBox'ı olan en yakın atanın CropBox'ıdır; o da yoksa kalıtılan MediaBox'tır. Belirtimin unutması kolay bir kuralı daha vardır: crop, bleed, trim ve art kutuları media kutusunun ötesine uzanmamalıdır; uzanırlarsa fiilen onunla kesişimlerine indirgenirler. PDFlibPas her kutuyu dosyada saklandığı hâliyle bildirir; dolayısıyla güvenilmeyen girdiyi işleyen bir validator, MediaBox'a göre kendi kendini kısmalıdır

PDFlibPas page box varsayılan zinciri: CropBox MediaBox'a, BleedBox, TrimBox ve ArtBox ise her biri CropBox'a varsayılanlaşır; 450'e 666 pointlik MediaBox'ı ve TrimBox bulunmayınca etkin trim olan 432'ye 648 pointlik CropBox'ı taşıyan bir kitap içinin yanında çizilir
MediaBox'a doğrudan varsayılanlaşan tek kutu CropBox'tır; dolayısıyla yalnızca MediaBox taşıyan bir sayfanın beş özdeş kutusu vardır

Bir /Pages düğümü hangi sayfa attribute'larını aktarabilir?

Tam olarak dördünü: Resources, MediaBox, CropBox ve Rotate. ISO 32000-1 §7.7.3.4 attribute kalıtımını tanımlar ve Table 30 yalnızca o dört page-object girdisini kalıtılabilir işaretler. BleedBox, TrimBox ve ArtBox yaprak sayfaya aittir. Bir /Pages düğümüne yazılmış TrimBox kalıtılan bir değer değildir; standartlara uygun bir okuyucunun yok saydığı standart dışı bir anahtardır

Böyle standart dışı dosyalar vardır; genelde "her sayfada bu trim var" kısayolu olarak kök page tree düğümünde tek bir TrimBox ile. Kısayol, her anahtar için /Parent'ı gezen her araçta doğru görünür ve sorun da budur: dosya artık kimin okuduğuna göre iki anlam taşır. Belirtimi izleyen bir okuyucu TrimBox görmez ve CropBox'ı kullanır; her şeyi kalıtan bir okuyucu ise ebeveyn değerini görür. Prepress hattında o belirsizlik baskı tabakasına kadar iner

PDFlibPas page tree kalıtımı: yalnızca Resources, MediaBox, CropBox ve Rotate bir Pages düğümünden aşağı iner; dolayısıyla köke park edilmiş bir TrimBox, standartlara uygun okuyucuların yok saydığı standart dışı bir anahtardır. v3.539.44 öncesinde iki bağımsız kod yolu onu kalıtıyor ve tek belge için farklı trim boyutları bildiriyordu
Dosya, kimin okuduğuna göre iki anlam taşır ve prepress hattında o belirsizlik baskı tabakasına düşer

PDF/X (ISO 15930) iş akışları bitmiş boyut için TrimBox'a dayanır ve PDF/X profilleri her sayfanın bir TrimBox ya da ArtBox bildirmesini ister. Bir /Pages düğümüne park edilmiş kutu o gereksinimi karşılamaz, çünkü anahtar sayfa object'ine hiç ulaşmaz. Preflight böyle dosyaları bir türlü sessizce okumak yerine işaretlemelidir

PDFlibPas v3.539.44'ten önce neyi yanlış yapıyordu?

PDFlibPas'ın üç ayrı kusuru vardı; hepsi belirtimin söylediği ile iki bağımsız kod yolunun yaptığı arasındaki boşluktaydı. İlki v3.539.42'de, öteki ikisi v3.539.44'te düzeltildi

Production kutuları capture sırasında MediaBox'a varsayılanlaşıyordu

v3.539.42 öncesinde bir sayfayı capture'a hazırlayan dâhilî yordam (kalıtılan girdileri sayfanın üzerine kopyalar ve eksik kutuları doldurur), BleedBox, TrimBox ve ArtBox yokken onlara MediaBox değerlerini veriyordu. 2 ile 4 optionlarıyla CapturePageEx, sınırlayıcı dikdörtgenini tam olarak o doldurulmuş girdilerden okur; dolayısıyla yalnızca CropBox tanımlayan bir sayfada trim kutusu istemek bütün media kutusunu capture ediyordu. GetPageBox CropBox varsayılanını çoktan uyguluyordu ve CapturePageEx referansı da hep, istenen kutu eksikken crop kutusunun kullanıldığını söylüyordu; capture kodu ikisine de ters düşüyordu. v3.539.42'den beri üç production kutusu sayfanın CropBox'ına varsayılanlaşır — o ana kadar CropBox sayfanın üzerindedir (kendi, bir atadan kopyalanmış ya da MediaBox'tan doldurulmuş) — ve yalnızca CropBox'ın kendisi MediaBox'a döner

İki kalıtım yolu, bir semantik kural

İkinci kusur standart dışı kalıtımın kendisiydi ve ince kısım, PDFlibPas'ın kutuları iki bağımsız yol boyunca çözmesiydi. Kutu sorguları (GetPageBox ile HasPageBox) /Parent zincirini bir yardımcı üzerinden geziyordu, capture ise ayrı bir yerel yardımcı üzerinden. İkisi de her anahtarı kalıtıyordu, production kutuları dahil. Yalnızca birini düzeltmek tek belgenin içinde çelişki üretirdi: /Pages düğümünde 180 point genişliğinde bir TrimBox ve sayfada 380 point genişliğinde bir CropBox varken GetPageBox hâlâ 180lik trim genişliği bildirirken CapturePageEx 380lik form kuruyordu. v3.539.44'te her iki yol da /Parent gezinimini dört kalıtılabilir anahtarla sınırlandırır, production kutuları yalnızca yapraktan okunur ve başıboş ebeveyn girdisi ne silinir ne yeniden yazılır; dosyada dokunulmadan kalır

PDFlibPas HasPageBox dönüş kodları sıfır, bir ve iki; v3.539.44'ten beri direkt ile indirect diziler ikisi de kalıtsal sayılır. Yanında CapturePageEx sıfırdan dörde optionlar: v3.539.42'den beri BleedBox, TrimBox ve ArtBox, MediaBox yerine CropBox'a döner
Tek belirtim kuralının iki uygulama giriş noktası birlikte düzeltilir ve 18 senaryoluk bir matrisle sınanır; sorgu ile capture her dosyada hemfikirdir

HasPageBox direkt ebeveyn dizilerini kaçırıyordu

HasPageBox, sayfada istenen tipte kutu yokken 0, sayfanın kendi kutusu varken (doğrudan ya da bir indirect referans üzerinden saklanmış) 1 ve bir MediaBox ya da CropBox bir atadan kalıtılmışken 2 döndürür. Eski kod 2'yi yalnızca kalıtılan değer indirect bir referansken döndürüyordu; dolayısıyla kalıtılan direkt bir dizi 0 veriyordu. Düzeltme, referans çözümünü dizi sınamasından ayırır ve her iki gösterim de artık 2 döndürür. v3.539.44'ten beri BleedBox, TrimBox ya da ArtBox için HasPageBox yalnızca 0 ya da 1 döndürebilir

Ders, page box'ların çok ötesine geneller. Bir belirtim semantiğinin bir kütüphanede iki uygulama giriş noktası olduğunda onları birlikte düzeltin ve tek bir mutlu-yol dosyasıyla değil bir matris olarak sının. PDFlibPas regresyon seti, iki ebeveyn-kutu gösterimini (direkt ve indirect dizi) üç yaprak durumu (yok, direkt dizi, indirect dizi) ile üç capture optionıyla (bleed, trim, art) çaprazlar; 18 senaryo çıkar ve her biri sorgu sonucunu, capture edilen sınırları, meşru MediaBox ve CropBox kalıtımını ve dokunulmamış ebeveyn girdisini denetler

Delphi'de etkin TrimBox'ı nasıl okurum?

Seçili sayfada GetPageBox(4, Dimension) çağırın. PDFlibPas varsayılan zincirini sizin için uygular; dolayısıyla sonuç, sayfada olsun ya da olmasın etkin TrimBox'tır. Değerin nereden geldiğini bilmeniz gerektiğinde — bir preflight raporu genelde ister — yanına HasPageBox koyun

uses
  System.SysUtils, PDFlibrary;

const
  BOX_CROP   = 2;
  BOX_TRIM   = 4;
  DIM_LEFT   = 0;
  DIM_WIDTH  = 2;
  DIM_HEIGHT = 3;
  DIM_BOTTOM = 5;

function DescribeTrim(Lib: TPDFlib; Page: Integer): string;
var
  Source: string;
begin
  Lib.SelectPage(Page);
  if Lib.HasPageBox(BOX_TRIM) = 1 then
    Source := 'own TrimBox'
  else if Lib.HasPageBox(BOX_CROP) <> 0 then   // 1 = kendi, 2 = kalıtsal
    Source := 'defaulted to the CropBox'
  else
    Source := 'defaulted to the MediaBox';
  Result := Format('page %d: trim %.2f x %.2f pt at (%.2f, %.2f), %s',
    [Page,
     Lib.GetPageBox(BOX_TRIM, DIM_WIDTH),
     Lib.GetPageBox(BOX_TRIM, DIM_HEIGHT),
     Lib.GetPageBox(BOX_TRIM, DIM_LEFT),
     Lib.GetPageBox(BOX_TRIM, DIM_BOTTOM),
     Source]);
end;

var
  Lib: TPDFlib;
  Page: Integer;
begin
  Lib := TPDFlib.Create;
  try
    if Lib.LoadFromFile('interior.pdf', '') = 1 then
      for Page := 1 to Lib.PageCount do
        Writeln(DescribeTrim(Lib, Page));
  finally
    Lib.Free;
  end;
end.

GetPageBox ile SetPageBox belgenin geçerli koordinat ayarlarında çalışır. Buradaki örnekler varsayılanlarla koşar: köken 0 (PDF user space ile aynı olan sol-alt) ve ölçü birimi point; dolayısıyla Top boyutu, sayfanın altından yukarı ölçülen üst kenardır. SetOrigin(1) sonrasında Top ile Bottom boyutları sayfanın üstünden aşağıya ölçülür ve SetMeasurementUnits(1) sonrasında her değer milimetre olarak döner. Genişlik ile yükseklik kökene bağlı değildir

/Pages düğümlerinde mahsur kalmış production kutularını bulmak

v3.539.44'ten beri kutu API'si bir /Pages düğümündeki TrimBox'ı görmüyor; bu doğrudur. Ama bir preflight aracı çoğu zaman böyle bir dosyayı sessizce belirtim yöntemiyle okumak yerine raporlamak ister. Page tree düğümleri sıradan object'lerdir; dolayısıyla düşük düzeyli object API'si onları bulabilir: GetMaxObjectNumber'a kadar object numaralarını gezin, her birini GetObjectToString ile okuyun ve production kutu anahtarı taşıyan bir /Pages sözlüğü arayın. Denetimin ikinci yarısı, PDF/X'in önemsediği sayfa-başı testtir ve HasPageBox artık onu bir PDF/X validator'ının yapacağı gibi yanıtlar; çünkü ebeveyn TrimBox artık sayılmıyor

procedure PreflightTrim(Lib: TPDFlib; Log: TStrings);
const
  ProductionKeys: array[0..2] of string = ('/BleedBox', '/TrimBox', '/ArtBox');
var
  ObjNum, K, Page, Missing: Integer;
  Src: string;
begin
  // 1. Page tree düğümlerindeki production kutular: standart dışı ve yok sayılır
  for ObjNum := 1 to Lib.GetMaxObjectNumber do
  begin
    Src := '';                                // boş numaralar metin döndürmez
    Src := string(Lib.GetObjectToString(ObjNum));
    if Pos('/Type /Pages', Src) = 0 then
      Continue;
    for K := Low(ProductionKeys) to High(ProductionKeys) do
      if Pos(ProductionKeys[K] + ' ', Src) > 0 then
        Log.Add(Format('object %d: %s on a /Pages node is not inheritable',
          [ObjNum, ProductionKeys[K]]));
  end;

  // 2. PDF/X: her sayfanın kendi TrimBox'ı ya da ArtBox'ı olmalı
  Missing := 0;
  for Page := 1 to Lib.PageCount do
  begin
    Lib.SelectPage(Page);
    if (Lib.HasPageBox(4) = 0) and (Lib.HasPageBox(5) = 0) then
    begin
      Inc(Missing);
      Log.Add(Format('page %d: no TrimBox or ArtBox', [Page]));
    end;
  end;

  // 3. İsteğe bağlı onarım: 6.25 x 9.25 inçlik media kutusu içinde 6 x 9 inçlik trim
  //    (point, sol-alt köken: Left, Top, Width, Height)
  if Missing > 0 then
    Log.Add(Format('TrimBox written on %d pages',
      [Lib.SetPageBoxRange('', 4, 9, 657, 432, 648)]));
end;

Metin eşleşmesi pragmatik bir denetimdir, ayrıştırıcı değil. PDFlibPas'ın her sözlük girdisini bir anahtar, bir boşluk ve bir değer olarak serileştirdiğine dayanır; GetObjectToString üzerinden geri okunan object'ler için bu geçerlidir. Onarım adımı refleks değil karar ister: başıboş ebeveyn değeri, yazarın kastettiği şeyin kendisi olabilir; ama onu resmîleştirmeden önce iş fişiyle doğrulayın. Boş aralıkla SetPageBoxRange, kutuyu her sayfaya uygular ve güncellenen sayfa sayısını döndürür. Bir sayfanın mevcut kutusu — başka bir sayfanın ya da bir /Pages düğümünün paylaşabileceği — indirect bir dizi olduğunda SetPageBox, paylaşılan objecti yeniden yazmak yerine o sayfaya yeni bir direkt dizi verir. BleedBox, TrimBox ya da ArtBox set etmek ayrıca kilidi açık bir belgeyi, o girdileri getiren sürüm olan PDF 1.3'e yükseltir

CapturePageEx ile sayfaları TrimBox'a dayamak

CapturePageEx(Page, 3), bir sayfayı sınırlayıcı kutusu sayfanın etkin TrimBox'ı olan bir Form XObject'e çevirir ve DrawCapturedPage o formu başka bir sayfaya istenen boyutta yerleştirir. v3.539.42'den beri TrimBox'sız bir sayfada option 3, referansın anlattığı gibi MediaBox'ı ve bütün slug'ını değil, CropBox'ı verir

Capture'ın iki özelliği kodu biçimlendirir. Capture yıkıcıdır: capture edilen sayfa belgeden kaldırılır ve belge asla sıfır sayfaya düşemez; dolayısıyla herhangi bir şey capture etmeden önce ilk çıktı tabakasını ekleyin. Capture ayrıca yalnızca tek bir belge içinde çalışır; dolayısıyla önce bütün girdileri tek belgeye toplayın — PDF kaynaklarını tek geçişte harmanlamak ve serpiştirmek teknikleri doğrudan uygulanır

procedure ImposeTwoUp(const InFile, OutFile: string);
var
  Lib: TPDFlib;
  Captures: array of Integer;
  SourceCount, I: Integer;
  TrimW, TrimH: Double;
begin
  Lib := TPDFlib.Create;
  try
    if Lib.LoadFromFile(InFile, '') <> 1 then
      raise Exception.Create('Cannot open ' + InFile);
    SourceCount := Lib.PageCount;

    // 1. sayfanın etkin trim boyutu (bu düzen tek tip trim varsayar)
    Lib.SelectPage(1);
    TrimW := Lib.GetPageBox(4, 2);
    TrimH := Lib.GetPageBox(4, 3);

    // İlk tabakayı ekleyip boyutlandır; NewPage yeni sayfayı seçer
    Lib.NewPage;
    Lib.SetPageDimensions(2 * TrimW, TrimH);

    // Her capture 1. sayfayı kaldırır; sonraki kaynak sayfa yukarı kayar
    SetLength(Captures, SourceCount);
    for I := 0 to SourceCount - 1 do
    begin
      Captures[I] := Lib.CapturePageEx(1, 3);   // 3 = TrimBox
      if Captures[I] = 0 then
        raise Exception.CreateFmt('Capture of source page %d failed', [I + 1]);
    end;

    // Geriye yalnızca tabaka kaldı: tabaka başına iki kırpılmış sayfa, yan yana
    Lib.SelectPage(1);
    for I := 0 to SourceCount - 1 do
    begin
      if (I > 0) and (I mod 2 = 0) then
        Lib.NewPage;                            // geçerli tabakayla aynı boyut
      // Varsayılan köken: Top üst kenardır, alttan ölçülür
      Lib.DrawCapturedPage(Captures[I], (I mod 2) * TrimW, TrimH, TrimW, TrimH);
    end;
    Lib.SaveToFile(OutFile);
  finally
    Lib.Free;
  end;
end;

Trim tabanlı bir capture, TrimBox dışındaki her şeyi kırpar; dijital bir prova ya da kes-ve-yığ düzeni için istediğiniz de budur. Baskıdan sonra kırpılacak bir baskı tabakası için option 2 ile capture edin ki bleed hayatta kalsın ve hücreleri bleed genişliğiyle aralayın. Capture kaynak sayfaları kaldırdığı için onlara işaret eden bookmark ve linkler hedeflerini kaybeder; dolayısıyla gezinmesine hâlâ ihtiyacınız olan bir belgeyi düzenlemek yerine ayrı bir çıktı dosyasına dayayın — bookmark'ları bozmadan sayfa değiştirme sayfa cerrahisinin o tarafını kapsar

Kaynak sapasağlam kalmak zorundayken ImportPageAsFormXObject(SourceDocumentID, SourcePage, Options) aynı 0-4 option değerlerini alır (geçerli belge için Lib.SelectedDocument geçirin), kaynak page tree'ye dokunmaz, kalıtılan sayfa rotasyonunu form matrisine normalize eder ve DrawCapturedPage'in kabul ettiği bir handle döndürür. CapturePageEx /Rotate'i geri almaz; dolayısıyla döndürülmüş girdi önce o adımı ister ve page box'ları bozmadan sayfa rotasyonunu düzleştirmek yaptığınızda her kutuya ne olduğunu gösterir. /Pages düğümlerinde production kutuları taşıyabilecek girdiler için bir uyarı: import yolu kutusunu kendi ata aramasıyla çözer — v3.539.44'te hizalanan iki yoldan ayrı olarak — dolayısıyla önce kaynak sayfada HasPageBox(4)'ü denetleyin ve 0 döndüğünde option 1 (CropBox) geçirin. Sonuç, dosyanın tesadüfen nasıl yazıldığına değil belirtime bağlanmış kalır

Page box hızlı başvurusu

  • Etkin CropBox: sayfanın kendi CropBox'ı, yoksa en yakın kalıtılmış CropBox, o da yoksa etkin MediaBox (ISO 32000-1 §14.11.2)
  • Etkin BleedBox, TrimBox ve ArtBox: yaprak sayfanın kendi girdisi, yoksa etkin CropBox
  • Yalnızca Resources, MediaBox, CropBox ve Rotate /Pages düğümlerinden kalıtılır (§7.7.3.4, Table 30); /Pages düğümlerindeki production kutuları yok sayılır
  • GetPageBox(BoxType, Dimension): BoxType 1 MediaBox, 2 CropBox, 3 BleedBox, 4 TrimBox, 5 ArtBox; Dimension 0 Left, 1 Top, 2 Width, 3 Height, 4 Right, 5 Bottom
  • HasPageBox(BoxType): 0 kutu yok, 1 sayfanın kendi kutusu (direkt ya da indirect), 2 kalıtılmış bir MediaBox ya da CropBox (direkt ya da indirect)
  • CapturePageEx(Page, Options): 0 MediaBox, 1 MediaBox yedekli CropBox, 2-4 CropBox yedekli BleedBox, TrimBox ya da ArtBox
  • Kutu sorguları ile capture arasında tutarlı varsayılanlar ve kalıtım için v3.539.44 ve sonrasına yükseltin

Page box'lar, PDF'in sessiz varsayılanlarının milimetrenin kesirleriyle ölçülen prepress toleranslarıyla buluştuğu yerdir; bir kütüphane ya o varsayılanları her yerde aynı biçimde uygular ya da tek soruya iki yanıt verir. Tam kutu, capture ve Form XObject API'si PDFlibPas PDF Library for Delphi ürün sayfasında belgelenmiştir