บทความเทคนิค

TrimBox, BleedBox และ CropBox ใน PDFlibPas: โซ่ค่าเริ่มต้น

เมื่อหน้า PDF ไม่มี TrimBox TrimBox ที่มีผลจริงของมันคือ CropBox ของหน้า และเมื่อ CropBox ก็หายไปด้วย มันคือ MediaBox BleedBox กับ ArtBox ยึดกฎเดียวกัน PDFlibPas ซึ่งเป็น PDF Library for Delphi ใช้ห่วงโซ่ค่าเริ่มต้นนี้สม่ำเสมอใน GetPageBox, HasPageBox และ CapturePageEx ตั้งแต่ v3.539.44 และมันมองข้ามกล่องสายการผลิตที่ถูกวางบน node /Pages เพราะ ISO 32000-1 ไม่อนุญาตให้พวกมันสืบทอด

ฟังดูเหมือนเชิงอรรถจนกว่าคุณจะจัดหน้าแบบ impose ภาพหนังสือขนาด MediaBox 6.25 คูณ 9.25 นิ้ว CropBox เซ็ตไว้ที่ trim 6 คูณ 9 นิ้ว และไม่มี TrimBox เพราะคน export ไม่เคยคิดจะเขียนมัน ขอ trim box ไปแล้วได้ media box กลับมา และทุก cell บนแผ่น press sheet ลากขอบ bleed กับ slug เข้าไปติดเพื่อนบ้านหนึ่งในแปดนิ้ว PDFlibPas เคยมีข้อบกพร่องอยู่ในแถวนี้พอดี แก้แล้วใน v3.539.42 กับ v3.539.44 และวิธีแก้ก็บอกอะไรบางอย่างว่า page-box semantics ควรถูก implement อย่างไรใน PDF library ตัวไหนก็ตาม

กล่องไหนมีผลเมื่อหน้าไม่มี TrimBox

คำตอบคือห่วงโซ่ค่าเริ่มต้นตายตัวจาก ISO 32000-1 §14.11.2: CropBox เริ่มต้นที่ MediaBox และ BleedBox, TrimBox กับ ArtBox แต่ละตัวเริ่มต้นที่ CropBox ไม่มีอะไรเริ่มต้นตรงไปที่ MediaBox นอกจาก CropBox หน้าที่นิยามแค่ MediaBox จึงมีกล่องห้าตัวเหมือนกันหมด และหน้าที่นิยาม MediaBox กับ CropBox จะมีกล่องสี่ตัวเท่ากับ CropBox

กล่องBoxType ของ PDFlibPasค่าเริ่มต้นเมื่อหายสืบทอดจาก /Pages ได้
MediaBox1ไม่มี entry นี้ต้องมีเสมอได้
CropBox2MediaBoxได้
BleedBox3CropBoxไม่ได้
TrimBox4CropBoxไม่ได้
ArtBox5CropBoxไม่ได้

ห่วงโซ่สองขั้นนี้สำคัญเพราะ CropBox เองก็สืบทอดได้ TrimBox ที่มีผลจริงของหน้าที่ไม่มีทั้ง TrimBox และ CropBox ของตัวเอง คือ CropBox ของบรรพบุรุษที่ใกล้ที่สุดที่มีมัน และถ้าไม่มีเลยก็คือ MediaBox ที่สืบทอดมา สเปกยังเติมกฎอีกข้อที่ลืมง่าย: กล่อง crop, bleed, trim และ art ไม่ควรยื่นเลย media box และถ้ายื่น มันถูกหดให้เหลือส่วนตัดกับ media box โดยปริยาย PDFlibPas รายงานแต่ละกล่องตามที่เก็บไว้ในไฟล์ validator ที่รับ input ไม่น่าเชื่อถือจึงควรหนีบเทียบกับ MediaBox ด้วยตัวเอง

ห่วงโซ่ค่าเริ่มต้นของ page box ใน PDFlibPas ที่ CropBox เริ่มต้นที่ MediaBox และ BleedBox, TrimBox กับ ArtBox ต่างเริ่มต้นที่ CropBox วาดเคียงกับหนังสือขนาด MediaBox 450 คูณ 666 พอยต์ และ CropBox 432 คูณ 648 พอยต์ที่กลายเป็น trim ที่มีผลจริงเมื่อไม่มี TrimBox อยู่
ไม่มีอะไรเริ่มต้นตรงไปที่ MediaBox นอกจาก CropBox หน้าที่มีแต่ MediaBox จึงมีกล่องห้าตัวเหมือนกันหมด

attribute ไหนที่ node /Pages ส่งต่อลงไปได้

เป๊ะสี่ตัว: Resources, MediaBox, CropBox และ Rotate ISO 32000-1 §7.7.3.4 นิยามการสืบทอด attribute และ Table 30 mark entry ของ page object แค่สี่ตัวนี้เป็น inheritable BleedBox, TrimBox กับ ArtBox เป็นของหน้า leaf ตัวเอง TrimBox ที่เขียนลง node /Pages ไม่ใช่ค่าที่สืบทอด มันคือ key ที่ไม่เป็นมาตรฐานซึ่ง reader ที่เป็นไปตามสเปกจะมองข้าม

ไฟล์ไม่เป็นมาตรฐานแบบนี้มีอยู่จริง มักมี TrimBox ตัวเดียววางบน node รากของ page tree เป็นชวเลขว่า "ทุกหน้ามี trim นี้" ชวเลขแบบนี้ดูถูกในเครื่องมือที่เดิน /Parent หาทุก key และนั่นแหละคือปัญหา ไฟล์กลายเป็นมีความหมายสองแบบแล้วแต่ใครอ่าน reader ที่ยึดสเปกเห็นว่าไม่มี TrimBox แล้วใช้ CropBox ขณะที่ reader ที่สืบทอดทุกอย่างเห็นค่าของ parent ใน pipeline prepress ความกำกวมนี้ไปจบลงบนแผ่นพิมพ์

การสืบทอดของ page tree ใน PDFlibPas ที่มีแค่ Resources, MediaBox, CropBox กับ Rotate ส่งต่อลงผ่าน node Pages TrimBox ที่จอดบนรากจึงเป็น key ที่ไม่เป็นมาตรฐานซึ่ง reader ที่เป็นไปตามสเปกมองข้าม ก่อน v3.539.44 เส้นทางโค้ดอิสระสองทางสืบทอดมันและรายงานขนาด trim ต่างกันในเอกสารฉบับเดียว
ไฟล์มีความหมายสองแบบแล้วแต่ใครอ่าน และใน pipeline prepress ความกำกวมนั้นตกลงบนแผ่นพิมพ์

workflow แบบ PDF/X (ISO 15930) พึ่ง TrimBox ในเรื่องขนาดเสร็จสมบูรณ์ และโปรไฟล์ PDF/X บังคับให้ทุกหน้าประกาศ TrimBox หรือ ArtBox กล่องที่จอดบน node /Pages ไม่ผ่านเงื่อนไขนี้ เพราะ key ไม่เคยไปถึง page object preflight ควร flag ไฟล์แบบนี้ ไม่ใช่อ่านเอาแบบใดแบบหนึ่งเงียบ ๆ

PDFlibPas พลาดอะไรไว้ก่อน v3.539.44

PDFlibPas มีข้อบกพร่องแยกกันสามจุด ทั้งหมดอยู่ในช่องว่างระหว่างสิ่งที่สเปกพูดกับสิ่งที่เส้นทางโค้ดอิสระสองทางทำ จุดแรกถูกแก้ใน v3.539.42 อีกสองจุดใน v3.539.44

กล่องสายการผลิตเริ่มต้นที่ MediaBox ตอน capture

ก่อน v3.539.42 routine ภายในที่เตรียมหน้าเพื่อ capture (มันก๊อป entry ที่สืบทอดมาลงหน้าแล้วเติมกล่องที่หายไป) ให้ MediaBox แก่ BleedBox, TrimBox กับ ArtBox เมื่อพวกมันไม่อยู่ CapturePageEx ตัวเลือก 2 ถึง 4 อ่านสี่เหลี่ยมครอบของมันจาก entry ที่เติมแล้วพวกนี้พอดี บนหน้าที่นิยามแค่ CropBox การขอ trim box จึง capture ได้ media box ทั้งก้อน GetPageBox ใช้ค่าเริ่มต้น CropBox มาตลอด และเอกสารอ้างอิงของ CapturePageEx ก็พูดมาตลอดว่าถ้ากล่องที่ขอไม่มีจะใช้ crop box โค้ด capture เถียงทั้งคู่ ตั้งแต่ v3.539.42 กล่องสายการผลิตสามตัวเริ่มต้นที่ CropBox ของหน้า ซึ่ง ณ จุดนั้นอยู่บนหน้าแล้ว (ของตัวเอง ก๊อปมาจากบรรพบุรุษ หรือเติมจาก MediaBox) และมีแต่ CropBox เองที่ย้อนกลับไปที่ MediaBox

สองเส้นทางสืบทอด กฎ semantic เดียว

ข้อบกพร่องที่สองคือการสืบทอดไม่เป็นมาตรฐานนั้นเอง และจุดละเอียดคือ PDFlibPas resolve กล่องผ่านสองเส้นทางอิสระ การ query กล่อง (GetPageBox กับ HasPageBox) เดินห่วงโซ่ /Parent ผ่าน helper หนึ่งตัว ส่วน capture เดินผ่าน helper ท้องถิ่นอีกตัว ทั้งคู่สืบทอดทุก key รวมถึงกล่องสายการผลิต แก้แค่ตัวใดตัวหนึ่งจะได้ความขัดแย้งในเอกสารฉบับเดียว: ด้วย TrimBox กว้าง 180 พอยต์บน node /Pages และ CropBox กว้าง 380 พอยต์บนหน้า GetPageBox จะยังรายงานความกว้าง trim เป็น 180 ขณะที่ CapturePageEx สร้าง form กว้าง 380 ออกมา ใน v3.539.44 ทั้งสองเส้นทางจำกัดการเดิน /Parent ไว้แค่สี่ key ที่สืบทอดได้ กล่องสายการผลิตถูกอ่านจาก leaf เท่านั้น และ entry หลงทางบน parent ถูกทิ้งไว้ในไฟล์เหมือนเดิม ไม่ลบ ไม่เขียนทับ

รหัสคืนค่า 0, 1 และ 2 ของ HasPageBox ใน PDFlibPas ที่ array ทั้งแบบ direct กับ indirect นับเป็นแบบสืบทอดได้ตั้งแต่ v3.539.44 เคียงกับตัวเลือก 0 ถึง 4 ของ CapturePageEx ที่ BleedBox, TrimBox กับ ArtBox ย้อนกลับไปที่ CropBox แทน MediaBox ตั้งแต่ v3.539.42
entry point การ implement สองตัวสำหรับกฎสเปกข้อเดียวถูกแก้พร้อมกันและทดสอบเป็นเมทริกซ์ 18 สถานการณ์ โดย query กับ capture เห็นตรงกันทุกไฟล์

HasPageBox พลาด array แบบ direct ที่อยู่บน parent

HasPageBox คืน 0 เมื่อหน้าไม่มีกล่องชนิดที่ขอ คืน 1 เมื่อหน้ามีกล่องของตัวเอง (เก็บแบบ direct หรือผ่าน indirect reference) และคืน 2 เมื่อ MediaBox หรือ CropBox ถูกสืบทอดมาจากบรรพบุรุษ โค้ดเดิมคืน 2 เมื่อค่าที่สืบทอดมาเป็น indirect reference เท่านั้น array ที่สืบทอดมาแบบ direct จึงคืน 0 การแก้แยกการ dereference ออกจากการทดสอบ array และทั้งสองรูปแบบตอนนี้คืน 2 ตั้งแต่ v3.539.44 HasPageBox สำหรับ BleedBox, TrimBox หรือ ArtBox คืนได้แค่ 0 หรือ 1

บทเรียนนี้ใช้กว้างกว่า page box มาก เมื่อ semantics หนึ่งข้อของสเปกมี entry point การ implement สองตัวใน library ให้แก้พร้อมกันและทดสอบเป็นเมทริกซ์ ไม่ใช่ด้วยไฟล์ happy path ไฟล์เดียว ชุด regression ของ PDFlibPas ไขว้รูปแบบ parent box สองแบบ (array direct กับ indirect) กับสถานะ leaf สามแบบ (หาย, array direct, array indirect) กับตัวเลือก capture สามแบบ (bleed, trim, art) ได้ 18 สถานการณ์ และแต่ละเคสเช็คผล query, ขอบเขตที่ capture ได้, การสืบทอด MediaBox กับ CropBox ที่ถูกกฎหมาย และ entry บน parent ที่ถูกทิ้งไว้ไม่แตะต้อง

อ่าน TrimBox ที่มีผลจริงใน Delphi อย่างไร

เรียก GetPageBox(4, Dimension) บนหน้าที่เลือกไว้ PDFlibPas ใช้ห่วงโซ่ค่าเริ่มต้นให้คุณแล้ว ผลลัพธ์จึงเป็น TrimBox ที่มีผลจริงไม่ว่าหน้าจะมีหรือไม่ จับคู่กับ HasPageBox เมื่อต้องรู้ว่าค่ามาจากไหน ซึ่งรายงาน preflight มักต้องการ

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 = own, 2 = inherited
    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 และ SetPageBox ทำงานตามการตั้งค่าพิกัดปัจจุบันของเอกสาร ตัวอย่างในนี้รันด้วยค่าเริ่มต้น: origin 0 (มุมล่างซ้าย ตรงกับ PDF user space) และหน่วยเป็นพอยต์ dimension Top จึงคือขอบบนที่วัดจากล่างของหน้าขึ้นไป หลัง SetOrigin(1) dimension Top กับ Bottom จะถูกวัดจากบนของหน้าลงมาแทน และหลัง SetMeasurementUnits(1) ทุกค่าคืนมาเป็นมิลลิเมตร ความกว้างกับความสูงไม่ขึ้นกับ origin

หากล่องสายการผลิตที่ติดค้างบน node /Pages

ตั้งแต่ v3.539.44 box API ไม่เห็น TrimBox บน node /Pages อีก ซึ่งถูกต้อง แต่เครื่องมือ preflight มักอยากรายงานไฟล์แบบนี้ออกมา ไม่ใช่อ่านเงียบ ๆ ตามสเปก node ของ page tree เป็น object ธรรมดา low-level object API จึงหาพวกมันได้: เดินหมายเลข object ถึง GetMaxObjectNumber อ่านทีละตัวด้วย GetObjectToString แล้วมองหา dictionary /Pages ที่ถือ key ของกล่องสายการผลิต ครึ่งหลังของการเช็คคือ test ต่อหน้าที่ PDF/X ใส่ใจ และ HasPageBox ตอนนี้ตอบมันแบบเดียวกับที่ validator ของ PDF/X จะตอบ เพราะ TrimBox บน parent ไม่ถูกนับอีกต่อไป

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. กล่องสายการผลิตบน node ของ page tree: ไม่เป็นมาตรฐานและถูกมองข้าม
  for ObjNum := 1 to Lib.GetMaxObjectNumber do
  begin
    Src := '';                                // เลขที่ว่างคืนข้อความไม่มา
    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: ทุกหน้าต้องมี TrimBox หรือ ArtBox ของตัวเอง
  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. ซ่อมแบบเลือกได้: trim 6 x 9 นิ้วข้างใน media box 6.25 x 9.25 นิ้ว
  //    (หน่วย point, origin มุมล่างซ้าย: Left, Top, Width, Height)
  if Missing > 0 then
    Log.Add(Format('TrimBox written on %d pages',
      [Lib.SetPageBoxRange('', 4, 9, 657, 432, 648)]));
end;

การ match ข้อความนี้เป็นเช็คแบบประนีประนอม ไม่ใช่ parser มันอาศัยว่า PDFlibPas serialize entry ของ dictionary เป็น key หนึ่งช่องว่างและค่า ซึ่งคงอยู่กับ object ที่อ่านกลับผ่าน GetObjectToString ขั้นซ่อมควรเป็นการตัดสินใจ ไม่ใช่แค่สะท้อนกิริยา ค่าที่หลงไปอยู่บน parent อาจเป็นสิ่งที่ผู้เขียนตั้งใจจริง แต่ให้ยืนยันกับ job ticket ก่อนจะทำให้มันเป็นทางการ SetPageBoxRange ด้วย range ว่างจะใช้กล่องกับทุกหน้าและคืนจำนวนหน้าที่อัปเดต เมื่อกล่องเดิมของหน้าเป็น array แบบ indirect ซึ่งหน้าอื่นหรือ node /Pages อาจแชร์อยู่ SetPageBox จะให้หน้านั้น array direct ใหม่แทนการเขียนทับ object ที่ถูกแชร์ การตั้ง BleedBox, TrimBox หรือ ArtBox ยังยกเอกสารที่ยังไม่ล็อกเวอร์ชันขึ้นเป็น PDF 1.3 ซึ่งเป็นเวอร์ชันที่เริ่มมี entry เหล่านี้ด้วย

จัดหน้าแบบ impose บน TrimBox ด้วย CapturePageEx

CapturePageEx(Page, 3) เปลี่ยนหน้าหนึ่งเป็น Form XObject ที่ bounding box คือ TrimBox ที่มีผลจริงของหน้า และ DrawCapturedPage วาง form นั้นลงหน้าอื่นได้ทุกขนาด ตั้งแต่ v3.539.42 ตัวเลือก 3 บนหน้าที่ไม่มี TrimBox จะให้ CropBox กับคุณ ตรงตามที่เอกสารอ้างอิงพูดไว้ แทนที่จะเป็น MediaBox พร้อม slug ทั้งชุด

สมบัติสองข้อของ capture กำหนดรูปโค้ด capture เป็นแบบทำลาย: หน้าที่ถูก capture ถูกถอดออกจากเอกสาร และเอกสารห้ามเหลือศูนย์หน้า จึงต้องแปะแผ่น output แผ่นแรกก่อน capture อะไร capture ยังทำงานข้างในเอกสารเดียวเท่านั้น จึงต้องดึง input ทุกตัวเข้าเอกสารเดียวก่อน เทคนิคสำหรับการ collate และ interleave แหล่ง PDF รวมในรอบเดียวใช้ตรง ๆ ได้เลย

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;

    // ขนาด trim ที่มีผลจริงของหน้า 1 (layout นี้สมมติ trim เท่ากันทุกหน้า)
    Lib.SelectPage(1);
    TrimW := Lib.GetPageBox(4, 2);
    TrimH := Lib.GetPageBox(4, 3);

    // แปะและกำหนดขนาดแผ่นแรก NewPage เลือกหน้าใหม่ให้ด้วย
    Lib.NewPage;
    Lib.SetPageDimensions(2 * TrimW, TrimH);

    // แต่ละ capture ถอดหน้า 1 ทิ้ง หน้าต้นทางถัดไปจึงเลื่อนขึ้น
    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;

    // เหลือแต่แผ่นกระดาษ: สองหน้าที่ trim แล้วต่อแผ่น วางเคียงกัน
    Lib.SelectPage(1);
    for I := 0 to SourceCount - 1 do
    begin
      if (I > 0) and (I mod 2 = 0) then
        Lib.NewPage;                            // ขนาดเท่าแผ่นปัจจุบัน
      // origin เริ่มต้น: Top คือขอบบน วัดจากล่างขึ้นไป
      Lib.DrawCapturedPage(Captures[I], (I mod 2) * TrimW, TrimH, TrimW, TrimH);
    end;
    Lib.SaveToFile(OutFile);
  finally
    Lib.Free;
  end;
end;

การ capture แบบอิง trim จะ clip ทุกอย่างที่อยู่นอก TrimBox ซึ่งเป็นสิ่งที่คุณต้องการกับ digital proof หรือ layout แบบ cut-and-stack ส่วน press sheet ที่จะถูก trim หลังพิมพ์ ให้ capture ด้วยตัวเลือก 2 เพื่อให้ bleed รอด แล้วเว้นช่องระหว่าง cell ตามความกว้างของ bleed เพราะ capture ถอดหน้าต้นทางทิ้ง bookmark กับลิงก์ที่ชี้มาหามันจะเสียเป้าหมาย จึงควร impose ลงไฟล์ output แยก ไม่ใช่แก้เอกสารที่ navigation ยังต้องใช้ การแทนที่หน้าโดยไม่ทำ bookmark พังเล่าฝั่งนั้นของการผ่าตัดหน้า

เมื่อต้นทางต้องคงสภาพ ImportPageAsFormXObject(SourceDocumentID, SourcePage, Options) รับค่าตัวเลือก 0 ถึง 4 ชุดเดียวกัน (ส่ง Lib.SelectedDocument สำหรับเอกสารปัจจุบัน) ปล่อย page tree ของต้นทางให้เหมือนเดิม normalize rotation ของหน้าที่สืบทอดมาเข้า matrix ของ form และคืน handle ที่ DrawCapturedPage รับได้ CapturePageEx ไม่ undo /Rotate input ที่หมุนมาจึงต้องผ่านขั้นนั้นก่อน และการ flatten page rotation โดยไม่ทำ page box พังโชว์ว่าแต่ละกล่องเป็นอย่างไรเมื่อคุณทำ ข้อควรระวังสำหรับ input ที่อาจพากล่องสายการผลิตบน node /Pages มาด้วย: เส้นทาง import resolve กล่องผ่านการไล่บรรพบุรุษของตัวเอง แยกจากสองเส้นทางที่จัดแนวกันใน v3.539.44 ให้เช็ค HasPageBox(4) บนหน้าต้นทางก่อน แล้วส่งตัวเลือก 1 (CropBox) เมื่อมันคืน 0 ผลลัพธ์จึงผูกอยู่กับสเปก ไม่ใช่กับวิธีที่ไฟล์บังเอิญถูกเขียน

สรุปด่วนของ page box

  • CropBox ที่มีผลจริง: CropBox ของหน้าเอง ไม่งั้น CropBox ที่สืบทอดที่ใกล้ที่สุด ไม่งั้น MediaBox ที่มีผลจริง (ISO 32000-1 §14.11.2)
  • BleedBox, TrimBox กับ ArtBox ที่มีผลจริง: entry ของหน้า leaf เอง ไม่งั้น CropBox ที่มีผลจริง
  • มีแค่ Resources, MediaBox, CropBox กับ Rotate ที่สืบทอดจาก node /Pages (§7.7.3.4, Table 30) กล่องสายการผลิตบน node /Pages ถูกมองข้าม
  • GetPageBox(BoxType, Dimension): BoxType 1 MediaBox, 2 CropBox, 3 BleedBox, 4 TrimBox, 5 ArtBox; Dimension 0 ซ้าย, 1 บน, 2 กว้าง, 3 สูง, 4 ขวา, 5 ล่าง
  • HasPageBox(BoxType): 0 ไม่มีกล่อง, 1 กล่องของหน้าเอง (direct หรือ indirect), 2 MediaBox หรือ CropBox ที่สืบทอดมา (direct หรือ indirect)
  • CapturePageEx(Page, Options): 0 MediaBox, 1 CropBox พร้อมย้อนไป MediaBox, 2 ถึง 4 BleedBox, TrimBox หรือ ArtBox พร้อมย้อนไป CropBox
  • อัปเกรดเป็น v3.539.44 ขึ้นไปเพื่อค่าเริ่มต้นและการสืบทอดที่สม่ำเสมอทั้ง query กล่องและ capture

page box คือจุดที่ค่าเริ่มต้นเงียบ ๆ ของ PDF มาเจอค่าคลาดเคลื่อนของ prepress ที่วัดกันเป็นเศษเสี้ยวมิลลิเมตร และ library หรือจะใช้ค่าเริ่มต้นเหล่านี้แบบเดียวกันทุกที่ หรือส่งคำตอบสองชุดให้คำถามข้อเดียว API เต็มชุดของกล่อง, capture และ Form XObject มีเอกสารอยู่ที่หน้า product ของ PDFlibPas PDF Library for Delphi