Tekninen artikkeli

TrimBox-, BleedBox- ja CropBox-oletukset PDFlibPasissa

Kun PDF-sivulla ei ole TrimBoxia, sen tehollinen TrimBox on sivun CropBox, ja kun myös CropBox puuttuu, se on MediaBox. BleedBox ja ArtBox noudattavat samaa sääntöä. PDFlibPas, PDF Library for Delphi, soveltaa tätä oletusketjua yhdenmukaisesti funktioissa GetPageBox, HasPageBox ja CapturePageEx versiosta v3.539.44 alkaen, ja se ohittaa /Pages-solmulle sijoitetut tuotantolaatikot, koska ISO 32000-1 ei anna niiden periä

Kyse kuulostaa alaviitteeltä, kunnes impositoit työn. Kuvittele kirjan sisus, jossa on 6.25 × 9.25 tuuman MediaBox, 6 × 9 tuuman trimmiin asetettu CropBox eikä TrimBoxia lainkaan, koska sen vientiä tehnyt ei koskaan tullut ajatelleeksi kirjoittaa sellaista. Pyydä trim-laatikkoa, saat media-laatikon sijaan, ja jokainen painoarkin solu raahaa kahdeksasosan tuumaa bleedia ja slugia naapuriinsa. PDFlibPasissa oli vikoja täsmälleen tällä alueella, korjattu versioissa v3.539.42 ja v3.539.44, ja tapa, jolla ne korjattiin, kertoo jotain siitä, miten sivulaatikoiden semantiikan pitäisi toteutua missä tahansa PDF-kirjastossa

Mikä laatikko pätee, kun sivulla ei ole TrimBoxia?

Vastaus on kiinteä oletusketju lähteestä ISO 32000-1 §14.11.2: CropBoxin oletus on MediaBox, ja BleedBoxin, TrimBoxin ja ArtBoxin oletus on kukin CropBox. Mikään muu kuin CropBox ei mene oletuksena suoraan MediaBoxiin. Sivulla, joka määrittelee vain MediaBoxin, on siksi viisi identtistä laatikkoa, ja sivulla, joka määrittelee MediaBoxin plus CropBoxin, on neljä CropBoxin suuruista laatikkoa

LaatikkoPDFlibPas BoxTypeOletus, kun puuttuuPeriytyvä /Pages-solmulta
MediaBox1Ei mitään, merkintä on pakollinenKyllä
CropBox2MediaBoxKyllä
BleedBox3CropBoxEi
TrimBox4CropBoxEi
ArtBox5CropBoxEi

Kaksivaiheinen ketju merkitsee paljon, koska CropBox voi itse olla peritty. Sivun tehollinen TrimBox, jolla ei ole omaa TrimBoxia eikä CropBoxia, on lähimmän sellaisen esi-isän CropBox, ja jos sellaista ei ole, peritty MediaBox. Määrittely lisää yhden säännön, jonka unohtaa helposti: crop-, bleed-, trim- ja art-laatikoiden ei pidä ulottua media-laatikon yli, ja jos ne ulottuvat, ne kaventuvat tehollisesti leikkaukseensa sen kanssa. PDFlibPas raportoi jokaisen laatikon sellaisena kuin se on talletettu tiedostoon, joten epäluotettavaa syötettä käsittelevän validaattorin kannattaa puristaa MediaBoxia itseään vasten

PDFlibPasin sivulaatikon oletusketju, jossa CropBoxin oletus on MediaBox ja BleedBoxin, TrimBoxin ja ArtBoxin oletus kukin CropBox, piirrettynä kirjan sisuksen viereen, jossa on 450 kertaa 666 pisteen MediaBox ja 432 kertaa 648 pisteen CropBox, josta tulee tehollinen trim, kun TrimBoxia ei ole
Muuta kuin CropBox ei mene oletuksena suoraan MediaBoxiin, joten sivulla, jossa on vain MediaBox, on viisi identtistä laatikkoa

Mitä sivuattribuutteja /Pages-solmu voi välittää alaspäin?

Tasan neljä: Resources, MediaBox, CropBox ja Rotate. ISO 32000-1 §7.7.3.4 määrittelee attribuuttien perinnän, ja taulukko 30 merkitsee vain ne neljä sivuobjektin merkintää perityviksi. BleedBox, TrimBox ja ArtBox kuuluvat lehtisivulle. /Pages-solmuun kirjoitettu TrimBox ei ole peritty arvo; se on epästandardi avain, jonka määritelmän mukainen lukija ohittaa

Tällaisia epästandardia tiedostoja on olemassa, tyypillisesti yhdellä TrimBoxilla juurisivupuusolmulla lyhenteenä lauseelle "jokaisella sivulla on tämä trim". Lyhenteen näyttää oikealta mikä tahansa työkalu, joka kulkee /Parentia jokaista avainta varten, ja se on ongelma: tiedosto tarkoittaa nyt kahta asiaa riippuen siitä, kuka sen lukee. Määritelmää seuraava lukija ei näe TrimBoxia ja käyttää CropBoxia, kun taas kaiken perivä lukija näkee vanhemman arvon. Esipainoprosessissa kyseinen monimerkityksisyys päätyy painoarkille

PDFlibPasin sivupuun perintä, jossa vain Resources, MediaBox, CropBox ja Rotate välittyvät Pages-solmun yli, joten juurelle pysäköitu TrimBox on epästandardi avain, jonka määritelmän mukaiset lukijat ohittavat; ennen v3.539.44 kaksi itsenäistä koodipolkua peri sen ja raportoi eri trim-koot yhdelle asiakirjalle
Tiedosto tarkoittaa kahta asiaa riippuen siitä, kuka sen lukee, ja esipainoprosessissa kyseinen monimerkityksisyys laskeutuu painoarkille

PDF/X-työnkulut (ISO 15930) riippuvat TrimBoxista valmiin koon kannalta, ja PDF/X-profiilit vaativat, että jokainen sivu ilmoittaa TrimBoxin tai ArtBoxin. /Pages-solmulle pysäköity laatikko ei täytä kyseistä vaatimusta, koska avain ei koskaan yletä sivuobjektiin. Preflightin kannattaa liputtaa tällaiset tiedostot sen sijaan, että lukee ne hiljaa jommalla kummalla tavalla

Mitä PDFlibPas sai väärin ennen v3.539.44:ää?

PDFlibPasissa oli kolme erillistä vikaa, kaikki siinä välissä, mitä määrittely sanoo ja mitä kaksi itsenäistä koodipolkua teki. Ensimmäinen korjattiin versiossa v3.539.42, muut kaksi versiossa v3.539.44

Tuotantolaatikot ottivat oletuksena MediaBoxin kaappauksen yhteydessä

Ennen v3.539.42:sta sisäinen rutiini, joka valmistelee sivun kaappausta varten (se kopioi perityt merkinnät sivulle ja täyttää puuttuvat laatikot), antoi BleedBoxille, TrimBoxille ja ArtBoxille MediaBoxin arvot, kun ne olivat poissa. CapturePageEx valinnoilla 2–4 lukee rajalaatikkonsa täsmälleen kyseisistä täytetyistä merkinnöistä, joten sivulla, joka määrittelee vain CropBoxin, trim-laatikon pyytäminen kaappasi koko media-laatikon. GetPageBox sovelsi jo CropBox-oletusta, ja CapturePageEx-referenssi oli aina sanonut, että crop-laatikkoa käytetään, kun pyydetty laatikko puuttuu; kaappauskoodi oli eri mieltä kummastakin. Versiosta v3.539.42 alkaen kolmen tuotantolaatikon oletus on sivun CropBox, joka siihen mennessä on jo sivulla (sen oma, esi-isästä kopioitu tai MediaBoxista täytetty), ja vain CropBox itse palaa MediaBoxiin

Kaksi perintäpolkua, yksi semanttinen sääntö

Toinen vika oli epästandardi perintä itse, ja hienovarainen osa oli siinä, että PDFlibPas ratkaisi laatikot pitkin kahta itsenäistä polkua. Laatikkokyselyt (GetPageBox ja HasPageBox) kulkivat /Parent-ketjun yhden avustajan kautta, ja kaappaus kulki sen erillisen paikallisen avustajan kautta. Molemmat perivät jokaisen avaimen, tuotantolaatikot mukaan lukien. Vain toisen korjaaminen olisi tuottanut ristiriidan yhden asiakirjan sisään: kun /Pages-solmulla on 180 pisteen levyinen TrimBox ja sivulla 380 pisteen levyinen CropBox, GetPageBox olisi yhä raportoinut trim-leveydeksi 180, kun CapturePageEx rakensi 380 levyisen formin. Versiossa v3.539.44 molemmat polut rajaavat /Parent-kulun neljään perittyvään avaimeen, tuotantolaatikot luetaan vain lehdestä, ja eksynyt vanhemman merkintä jää tiedostoon koskemattomaksi, ei poistettuna eikä uudelleenkirjoitettuna

PDFlibPasin HasPageBoxin paluukoodit nolla, yksi ja kaksi, joissa suorat ja epäsuorat taulukot lasketaan kumpikin perityiksi versiosta v3.539.44 alkaen, CapturePageExin valintojen nollan ja neljän vieressä, joissa BleedBox, TrimBox ja ArtBox palaavat CropBoxiin MediaBoxin sijaan versiosta v3.539.42 alkaen
Yhden määrittelysäännön kaksi toteutuksen sisääntulopistettä korjataan yhdessä ja testataan 18 skenaarion matriisina, kysely ja kaappaus sopivat yhteen jokaisella tiedostolla

HasPageBox ohitti suorat vanhemman taulukot

HasPageBox palauttaa arvon 0, kun sivulla ei ole pyydettyä tyyppiä olevaa laatikkoa, arvon 1, kun sivulla on oma laatikko (talletettu suoraan tai epäsuoran viittauksen kautta), ja arvon 2, kun MediaBox tai CropBox on peritty esi-isältä. Vanha koodi palautti arvon 2 vain, kun peritty arvo oli epäsuora viittaus, joten peritty suora taulukko palautti arvon 0. Korjaus erottaa viittausten purkamisen taulukkotestistä, ja molemmat esitystavat palauttavat nyt arvon 2. Versiosta v3.539.44 alkaen HasPageBox voi BleedBoxin, TrimBoxin tai ArtBoxin kohdalla palauttaa vain arvon 0 tai 1

Oppi yleistyy pitkälle sivulaatikoiden ulkopuolelle. Kun yksi määritelmän semantiikan palanen saa kirjastossa kaksi toteutuksen sisääntulopistettä, korjaa ne yhdessä ja testaa ne matriisina yhden onnistumispolun tiedoston sijaan. PDFlibPasin regressiojoukko risteyttää kaksi vanhemman laatikon esitystapaa (suora ja epäsuora taulukko) kolmen lehtitilan (poissa, suora taulukko, epäsuora taulukko) ja kolmen kaappausvalinnan (bleed, trim, art) kanssa, mikä antaa 18 skenaariota, ja jokainen tarkistaa kyselyn tuloksen, kaapatut rajat, laillisen MediaBox- ja CropBox-perinnän sekä koskemattoman vanhemman merkinnän

Miten luen tehollisen TrimBoxin Delphissä?

Kutsu GetPageBox(4, Dimension)ia valitulla sivulla. PDFlibPas soveltaa oletusketjun puolestasi, joten tulos on tehollinen TrimBox riippumatta siitä, onko sivulla sellaista. Yhdistä se funktioon HasPageBox, kun sinun tarvitsee tietää, mistä arvo tuli, mitä preflight-raportti yleensä tekee

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 = oma, 2 = peritty
    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.

Sekä GetPageBox että SetPageBox toimivat asiakirjan nykyisillä koordinaattiasetuksilla. Esimerkit täällä ajetaan oletuksilla: alkukohta 0 (vasen alakulma, vastaa PDF:n käyttäjätilaa) ja pisteet mittayksikkönä, joten Top-dimensio on yläreuna, mitattuna ylöspäin sivun alareunasta. Kutsun SetOrigin(1) jälkeen dimensiot Top ja Bottom mitataan sen sijaan alaspäin sivun yläreunasta, ja kutsun SetMeasurementUnits(1) jälkeen jokainen arvo palautuu millimetreinä. Leveys ja korkeus eivät riipu alkukohdasta

Jumiin jääneiden tuotantolaatikoiden löytäminen /Pages-solmuilta

Versiosta v3.539.44 alkaen laatikko-API ei enää näe TrimBoxia /Pages-solmulla, mikä on oikein, mutta preflight-työkalu haluaa yleensä raportoida tällaisen tiedoston sen sijaan, että lukee sen hiljaa määritelmän tavalla. Sivupuusolmut ovat tavallisia objekteja, joten matalan tason objekti-API pystyy löytämään ne: kulje objektinumerot arvoon GetMaxObjectNumber asti, lue jokainen funktiolla GetObjectToString ja etsi /Pages-sanakirja, joka kantaa tuotantolaatikkoavainta. Tarkistuksen toinen puoli on sivukohtainen testi, josta PDF/X välittää, ja HasPageBox vastaa siihen nykyään niin kuin PDF/X-validaattori vastaisi, koska vanhemman TrimBox ei enää laske

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. Tuotantolaatikot sivupuusolmuilla: epästandardi ja ohitetaan
  for ObjNum := 1 to Lib.GetMaxObjectNumber do
  begin
    Src := '';                                // vapaat numerot eivät palauta tekstiä
    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: jokainen sivu tarvitsee oman TrimBoxin tai ArtBoxin
  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. Valinnainen korjaus: 6 x 9 tuuman trim 6.25 x 9.25 tuuman medialaatikon sisällä
  //    (pisteet, vasemman alakulman alkukohta: Left, Top, Width, Height)
  if Missing > 0 then
    Log.Add(Format('TrimBox written on %d pages',
      [Lib.SetPageBoxRange('', 4, 9, 657, 432, 648)]));
end;

Tekstivertailu on pragmaattinen tarkistus, ei jäsentin. Se nojaa siihen, että PDFlibPas serialisoi jokaisen sanakirjamerkinnän avaimeksi, yhdeksi välilyönniksi ja arvoksi, mikä pätee objekteille, jotka luetaan takaisin funktiolla GetObjectToString. Korjausvaihe ansaitsee päätöksen refleksin sijaan: eksynyt vanhemman arvo saattaa hyvinkin olla se, mitä tekijä aikoi, mutta vahvista se työkorttia vasten ennen kuin teet siitä virallisen. SetPageBoxRange tyhjällä välillä soveltaa laatikon jokaiselle sivulle ja palauttaa päivitettyjen sivujen määrän. Kun sivun olemassa oleva laatikko on epäsuora taulukko, jota toinen sivu tai /Pages-solmu saattaa jakaa, SetPageBox antaa kyseiselle sivulle uuden suoran taulukon jaetun objektin uudelleenkirjoittamisen sijaan. BleedBoxin, TrimBoxin tai ArtBoxin asettaminen nostaa myös lukitsemattoman asiakirjan versioon PDF 1.3, joka esitteli kyseiset merkinnät

Sivujen impositointi TrimBoxille CapturePageExilla

CapturePageEx(Page, 3) muuttaa sivun Form XObjectiksi, jonka rajalaatikko on sivun tehollinen TrimBox, ja DrawCapturedPage sijoittaa kyseisen formin toiselle sivulle millä tahansa koolla. Versiosta v3.539.42 alkaen valinta 3 sivulla ilman TrimBoxia antaa sinulle CropBoxin, kuten referenssi kuvaa, MediaBoxin sijaan kaikine slugineen

Kaappauksen kaksi ominaisuutta muovaavat koodia. Kaappaus on tuhoava: kaapattu sivu poistetaan asiakirjasta, eikä asiakirja voi koskaan pudota nollaan sivuun, joten liitä ensimmäinen tulostusarkki ennen kuin kaappaat mitään. Kaappaus toimii myös vain yhden asiakirjan sisällä, joten nosta jokainen syöte yhteen asiakirjaan ensin; artikkelin PDF-lähteiden kokoaminen ja lomittaminen yhdellä ajolla tekniikat pätevät suoraan

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;

    // Sivun 1 tehollinen trim-koko (tämä asettelu olettaa yhtenäisen trimin)
    Lib.SelectPage(1);
    TrimW := Lib.GetPageBox(4, 2);
    TrimH := Lib.GetPageBox(4, 3);

    // Liitä ja mitoita ensimmäinen arkki; NewPage valitsee uuden sivun
    Lib.NewPage;
    Lib.SetPageDimensions(2 * TrimW, TrimH);

    // Jokainen kaappaus poistaa sivun 1, joten seuraava lähdesivu siirtyy ylös
    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;

    // Vain arkki on jäljellä: kaksi trimmattua sivua arkilla rinnakkain
    Lib.SelectPage(1);
    for I := 0 to SourceCount - 1 do
    begin
      if (I > 0) and (I mod 2 = 0) then
        Lib.NewPage;                            // sama koko kuin nykyinen arkki
      // Oletusalkukohta: Top on yläreuna, mitattuna alareunasta
      Lib.DrawCapturedPage(Captures[I], (I mod 2) * TrimW, TrimH, TrimW, TrimH);
    end;
    Lib.SaveToFile(OutFile);
  finally
    Lib.Free;
  end;
end;

Trim-pohjainen kaappaus leikkaa kaiken TrimBoxin ulkopuolisen pois, mikä on juuri se, mitä haluat digitaaliselle vedokselle tai cut-and-stack-asettelulle. Painoarkille, joka trimmataan painamisen jälkeen, kaappaa valinnalla 2, jotta bleed säilyy, ja rivitä solut bleed-leveyden välein. Koska kaappaus poistaa lähdesivut, kirjanmerkit ja linkit, jotka osoittivat niihin, menettävät kohteensa, joten impositoi erilliseen tulostustiedostoon sen sijaan, että muokkaat asiakirjaa, jonka navigaatiota tarvitset yhä; artikkeli sivujen korvaaminen rikkomatta kirjanmerkkejä kattaa sivukirurgian kyseisen puolen

Kun lähteen on pysyttävä ehjänä, ImportPageAsFormXObject(SourceDocumentID, SourcePage, Options) ottaa samat valinta-arvot 0–4 (anna Lib.SelectedDocument nykyiselle asiakirjalle), jättää lähteen sivupuun muuttumattomaksi, normalisoi perityn sivunkiertyman formimatriisiin ja palauttaa kahvan, jonka DrawCapturedPage hyväksyy. CapturePageEx ei kumoa /Rotatea, joten kiertynyt syöte tarvitsee kyseisen vaiheen ensin, ja artikkeli sivunkiertyman tasoittaminen rikkomatta sivulaatikoita näyttää, mitä kullekin laatikolle tapahtuu, kun teet niin. Yksi varoitus syötteille, jotka saattavat kantaa tuotantolaatikoita /Pages-solmuilla: tuontipolku ratkaisee laatikkonsa oman esi-isähaun kautta, erillään kahdesta v3.539.44:ssa tasatusta polusta, joten tarkista HasPageBox(4) lähdesivulla ensin ja anna valinta 1 (CropBox), kun se palauttaa arvon 0. Se pitää tuloksen kiinni määritelmässä, ei siinä, miten tiedosto sattui kirjoitettavan

Sivulaatikon pikamuistio

  • Tehollinen CropBox: sivun oma CropBox, muutoin lähin peritty CropBox, muutoin tehollinen MediaBox (ISO 32000-1 §14.11.2)
  • Tehollinen BleedBox, TrimBox ja ArtBox: lehtisivun oma merkintä, muutoin tehollinen CropBox
  • Vain Resources, MediaBox, CropBox ja Rotate periytyvät /Pages-solmuilta (§7.7.3.4, taulukko 30); tuotantolaatikot /Pages-solmuilla ohitetaan
  • 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 ei laatikkoa, 1 sivun oma laatikko (suora tai epäsuora), 2 peritty MediaBox tai CropBox (suora tai epäsuora)
  • CapturePageEx(Page, Options): 0 MediaBox, 1 CropBox MediaBox-varalla, 2–4 BleedBox, TrimBox tai ArtBox CropBox-varalla
  • Päivitys versioon v3.539.44 tai uudempaan yhdenmukaisten oletusten ja perinnän saamiseksi laatikkokyselyissä ja kaappauksessa

Sivulaatikot ovat paikka, jossa PDF:n hiljaiset oletukset kohtaavat murto-osamillimetreinä mitattuja esipainotoleransseja, ja kirjasto joko soveltaa kyseisiä oletuksia samalla tavalla kaikkialla tai ojentaa sinulle kaksi vastausta yhteen kysymykseen. Koko laatikko-, kaappaus- ja Form XObject-API on dokumentoitu PDFlibPas PDF Library for Delphi -tuotesivulla