Når en PDF-side ikke har TrimBox, er dens effektive TrimBox sidens CropBox, og når CropBox også mangler, er det MediaBox. BleedBox og ArtBox følger samme regel. PDFlibPas, PDF Library for Delphi, anvender denne standardkjeden konsekvent i GetPageBox, HasPageBox og CapturePageEx siden v3.539.44, og den ignorerer produksjonsbokser plassert på en /Pages-node, for ISO 32000-1 lar dem ikke arve
Det høres ut som en fotnote til du legger ut en jobb. Tenk deg en bokinnside med en 6,25 × 9,25 tommer MediaBox, en CropBox satt til 6 × 9 tommer trim, og ingen TrimBox, for den som eksporterte den, tenkte aldri på å skrive én. Be om trimboksen, få medieboksen i stedet, og hver celle på trykkarket sleper en åttendedel tomme med bleed og slug inn i naboen. PDFlibPas hadde defekter i nøyaktig dette området, fikset i v3.539.42 og v3.539.44, og måten de ble fikset på, sier noe om hvordan sideboks-semantikk bør implementeres i ethvert PDF-bibliotek
Hvilken boks gjelder når en side ikke har TrimBox?
Svaret er en fast standardkjede fra ISO 32000-1 §14.11.2: CropBox faller tilbake på MediaBox, og BleedBox, TrimBox og ArtBox faller hver tilbake på CropBox. Ingenting unntatt CropBox faller rett tilbake på MediaBox. En side som definerer bare en MediaBox, har dermed fem identiske bokser, og en side som definerer en MediaBox pluss en CropBox, har fire bokser lik CropBox
| Boks | PDFlibPas BoxType | Standard ved fravær | Arvbar fra /Pages |
|---|---|---|---|
| MediaBox | 1 | Ingen, oppføringen er påkrevd | Ja |
| CropBox | 2 | MediaBox | Ja |
| BleedBox | 3 | CropBox | Nei |
| TrimBox | 4 | CropBox | Nei |
| ArtBox | 5 | CropBox | Nei |
Totrinnskjeden betyr noe fordi CropBox selv kan være arvet. Den effektive TrimBox-en til en side utenkenken TrimBox eller CropBox av sin egen er CropBox-en til den nærmeste forfaren som har én, og i mangel av det, den arvede MediaBox-en. Spesifikasjonen legger til én regel til som er lett å glemme: crop-, bleed-, trim- og art-boksene skal ikke strekke seg forbi medieboksen, og gjør de det, reduseres de i praksis til sitt snitt med den. PDFlibPas rapporterer hver boks som lagret i filen, så en validator som håndterer utruelig input, bør klemme mot MediaBox-en selv
Hvilke sideattributter kan en /Pages-node gi videre?
Nøyaktig fire: Resources, MediaBox, CropBox og Rotate. ISO 32000-1 §7.7.3.4 definerer attributtarv, og Table 30 merker bare de fire sideobjekt-oppføringene som arvbare. BleedBox, TrimBox og ArtBox tilhører bladsiden. En TrimBox skrevet inn i en /Pages-node er ikke en arvet verdi; den er en ikke-standard-nøkkel som en spesifikasjonskonform leser ignorerer
Slike ikke-standard-filer finnes, typisk med en enkelt TrimBox på roten til page tree-en som stenografi for «hver side har denne trimmen». Stenografien ser riktig ut i ethvert verktøy som går /Parent for hver nøkkel, og det er problemet: filen betyr nå to ting avhengig av hvem som leser den. En leser som følger spesifikasjonen, ser ingen TrimBox og bruker CropBox, mens en leser som arver alt, ser forelderverdien. I en prepress-pipeline ender den tvetydigheten på trykkarket
PDF/X (ISO 15930)-arbeidsflyter er avhengige av TrimBox for ferdig størrelse, og PDF/X-profilene krever at hver side deklarerer en TrimBox eller en ArtBox. En boks parkert på en /Pages-node oppfyller ikke det kravet, for nøkkelen når aldri sideobjektet. Preflight bør flagge slike filer i stedet for å lese dem i stillhet den ene eller andre veien
Hva gjorde PDFlibPas galt før v3.539.44?
PDFlibPas hadde tre separate defekter, alle i gapet mellom det spesifikasjonen sier og det to uavhengige kode-stier gjorde. Den første ble fikset i v3.539.42, de to andre i v3.539.44
Produksjonsbokser falt tilbake på MediaBox under capture
Før v3.539.42 ga den interne rutinen som klargjør en side for capture (den kopierer arvede oppføringer på siden og fyller inn manglende bokser), BleedBox, TrimBox og ArtBox MediaBox-verdiene når de manglet. CapturePageEx med alternativene 2 til 4 leser sin bounding-rectangle fra nøyaktig de utfylte oppføringene, så på en side som definerer bare en CropBox, ga en forespørsel etter trimboksen hele medieboksen i capture. GetPageBox anvendte allerede CropBox-standardverdien, og CapturePageEx-referansen hadde alltid sagt at crop-boksen brukes når den etterspurte boksen mangler; capture-koden var uenig med begge. Siden v3.539.42 faller de tre produksjonsboksene tilbake på sidens CropBox, som på det punktet allerede ligger på siden (dens egen, kopiert fra en forfar, eller fylt inn fra MediaBox), og bare CropBox selv faller tilbake på MediaBox
To arve-stier, én semantisk regel
Den andre defekten var den ikke-standard-arven i seg selv, og det subtile var at PDFlibPas løste bokser langs to uavhengige stier. Boks-oppslag (GetPageBox og HasPageBox) gikk /Parent-kjeden gjennom én hjelper, og capture gikk den gjennom en separat lokal hjelper. Begge arvet hver nøkkel, produksjonsbokser inkludert. Å fikse bare én av dem ville ha produsert en motsigelse inne i ett enkelt dokument: med en 180-punkts-bred TrimBox på /Pages-noden og en 380-punkts-bred CropBox på siden, ville GetPageBox fortsatt rapportert en trimbredde på 180 mens CapturePageEx bygde et 380-bredt form. I v3.539.44 begrenser begge stier /Parent-gangen til de fire arvbare nøklene, produksjonsbokser leses bare fra bladet, og den hakende forelderoppføringen blir i filen urørt, verken slettet eller omskrevet
HasPageBox savnet direkte forelderarrayer
HasPageBox returnerer 0 når siden ikke har noen boks av den etterspurte typen, 1 når siden har sin egen boks (lagret direkte eller gjennom en indirekte referanse), og 2 når en MediaBox eller CropBox er arvet fra en forfar. Den gamle koden returnerte 2 bare når den arvede verdien var en indirekte referanse, så en arvet direkte array returnerte 0. Fiksen skiller dereferering fra array-testen, og begge representasjonene returnerer nå 2. Siden v3.539.44 kan HasPageBox for en BleedBox, TrimBox eller ArtBox bare returnere 0 eller 1
Lærdommen generaliserer langt utover sidebokser. Når én bit spesifikasjonssemantikk har to implementasjonsinnganger i et bibliotek, fiks dem sammen og test dem som en matrise i stedet for med én happy path-fil. PDFlibPas-regresjonssettet krysser to forelderboks-representasjoner (direkte og indirekte array) med tre bladtilstander (fraværende, direkte array, indirekte array) og tre capture-alternativer (bleed, trim, art), noe som gir 18 scenarier, og hvert enkelt sjekker oppslagsresultatet, de fangede grensene, legitim MediaBox- og CropBox-arv, og den urørte forelderoppføringen
Hvordan leser jeg den effektive TrimBox-en i Delphi?
Kall GetPageBox(4, Dimension) på den valgte siden. PDFlibPas anvender standardkjeden for deg, så resultatet er den effektive TrimBox-en enten siden har en eller ikke. Kombiner den med HasPageBox når du trenger å vite hvor verdien kom fra, noe en preflight-rapport som regel gjør
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 = egen, 2 = arvet
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.
Både GetPageBox og SetPageBox arbeider i dokumentets nåværende koordinatinnstillinger. Eksemplene her kjører med standardverdiene: origo 0 (nede til venstre, som matcher PDF user space) og punkter som måleenhet, så Top-dimensjonen er den øvre kanten målt opp fra sidens bunn. Etter SetOrigin(1) måles Top- og Bottom-dimensjonene ned fra sidens topp i stedet, og etter SetMeasurementUnits(1) kommer hver verdi tilbake i millimeter. Bredde og høyde er ikke avhengige av origo
Finne produksjonsbokser som står strandede på /Pages-noder
Siden v3.539.44 ser boks-API-en ikke lenger en TrimBox på en /Pages-node, noe som er korrekt, men et preflight-verktøy vil som regel rapportere en slik fil i stedet for å lese den i stillhet på spesifikasjonsmåten. Page tree-noder er ordinære objekter, så lavnivå objekt-API-et kan finne dem: gå objektnumre opp til GetMaxObjectNumber, les hvert med GetObjectToString, og let etter en /Pages-ordbok som bærer en produksjonsboks-nøkkel. Andre halvdel av sjekken er per-side-testen PDF/X bryr seg om, og HasPageBox svarer den nå slik en PDF/X-validator ville gjort, for en forelder-TrimBox teller ikke lenger
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. Produksjonsbokser på page tree-noder: ikke-standard og ignorert
for ObjNum := 1 to Lib.GetMaxObjectNumber do
begin
Src := ''; // ledige numre returnerer ingen tekst
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: hver side trenger sin egen TrimBox eller 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. Valgfri reparasjon: en 6 x 9 tommer trim inne i en 6.25 x 9.25 tommer mediaboks
// (punkter, origo nede til venstre: Left, Top, Width, Height)
if Missing > 0 then
Log.Add(Format('TrimBox written on %d pages',
[Lib.SetPageBoxRange('', 4, 9, 657, 432, 648)]));
end;
Tekstmatchen er en pragmatisk sjekk, ikke en parser. Den er avhengig av at PDFlibPas serialiserer hver ordbokoppføring som en nøkkel, ett mellomrom og en verdi, noe som holder for objekter lest tilbake gjennom GetObjectToString. Reparasjonssteget fortjener en beslutning snarere enn en refleks: den hakende forelderverdien kan godt være det forfatteren mente, men bekreft den mot jobbseddelen før du gjør den offisiell. SetPageBoxRange med et tomt område anvender boksen på hver side og returnerer antallet oppdaterte sider. Når en sides eksisterende boks er en indirekte array, som en annen side eller en /Pages-node kan dele, gir SetPageBox den siden en ny direkte array i stedet for å omskrive det delte objektet. Å sette en BleedBox, TrimBox eller ArtBox hever også et ulåst dokument til PDF 1.3, versjonen som innførte de oppføringene
Legge ut sider på TrimBox-en med CapturePageEx
CapturePageEx(Page, 3) gjør en side om til et Form XObject hvis bounding box er sidens effektive TrimBox, og DrawCapturedPage plasserer det formet på en annen side i hvilken som helst størrelse. Siden v3.539.42 gir alternativ 3 på en side uten TrimBox deg CropBox, slik referansen beskriver, i stedet for MediaBox med all sin slug
To egenskaper ved capture former koden. Capture er destruktiv: den fangede siden fjernes fra dokumentet, og dokumentet kan aldri falle til null sider, så legg til det første outputarket før du capturerer noe. Capture virker også bare innenfor ett dokument, så trekk alle input inn i ett enkelt dokument først; teknikkene for å sortere og flette PDF-kilder i én gjennomgang gjelder direkte
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;
// Effektiv trimstørrelse av side 1 (dette oppsettet antar en uniform trim)
Lib.SelectPage(1);
TrimW := Lib.GetPageBox(4, 2);
TrimH := Lib.GetPageBox(4, 3);
// Legg til og dimensjoner det første arket; NewPage velger den nye siden
Lib.NewPage;
Lib.SetPageDimensions(2 * TrimW, TrimH);
// Hvert capture fjerner side 1, så neste kildeside rykker opp
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;
// Bare arket er igjen: to beskårete sider per ark, side om side
Lib.SelectPage(1);
for I := 0 to SourceCount - 1 do
begin
if (I > 0) and (I mod 2 = 0) then
Lib.NewPage; // samme størrelse som det nåværende arket
// Standard origo: Top er øvre kant, målt fra bunnen
Lib.DrawCapturedPage(Captures[I], (I mod 2) * TrimW, TrimH, TrimW, TrimH);
end;
Lib.SaveToFile(OutFile);
finally
Lib.Free;
end;
end;
En trim-basert capture klipper alt utenfor TrimBox-en, som er det du vil ha for en digital proof eller et cut-and-stack-oppsett. For et trykkark som beskjæres etter trykking, capture med alternativ 2 slik at bleed-en overlever, og plasser cellene med bleed-bredden som avstand. Fordi capture fjerner kildesidene, mister bokmerker og lenker som pekte på dem målene sine, så legg ut i en separat outputfil i stedet for å redigere et dokument hvis navigasjon du fortsatt trenger; å erstatte sider uten å ødelegge bokmerker dekker den siden av sidekirurgi
Når kilden må forbli intakt, tar ImportPageAsFormXObject(SourceDocumentID, SourcePage, Options) de samme alternativverdiene 0 til 4 (send Lib.SelectedDocument for det nåværende dokumentet), lar kildens page tree være uendret, normaliserer arvet siderotasjon inn i form-matrisen, og returnerer et håndtak DrawCapturedPage godtar. CapturePageEx ugjør ikke /Rotate, så rotert input trenger det steget først, og å flate ut siderotasjon uten å ødelegge sidebokser viser hva som skjer med hver boks når du gjør det. Én advarsel for input som kan bære produksjonsbokser på /Pages-noder: importstien løser sin boks gjennom sitt eget forfar-oppslag, atskilt fra de to stiene tilrettelagt i v3.539.44, så sjekk HasPageBox(4) på kildesiden først og send alternativ 1 (CropBox) når den returnerer 0. Det holder resultatet knyttet til spesifikasjonen i stedet for til hvordan filen tilfeldigvis var skrevet
Sideboks-hurtigreferanse
- Effektiv CropBox: sidens egen CropBox, ellers nærmeste arvede CropBox, ellers den effektive MediaBox (ISO 32000-1 §14.11.2)
- Effektiv BleedBox, TrimBox og ArtBox: bladsidens egen oppføring, ellers den effektive CropBox
- Bare
Resources,MediaBox,CropBoxogRotatearver fra/Pages-noder (§7.7.3.4, Table 30); produksjonsbokser på/Pages-noder ignoreres 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 BottomHasPageBox(BoxType): 0 ingen boks, 1 sidens egen boks (direkte eller indirekte), 2 en arvet MediaBox eller CropBox (direkte eller indirekte)CapturePageEx(Page, Options): 0 MediaBox, 1 CropBox med MediaBox-fallback, 2 til 4 BleedBox, TrimBox eller ArtBox med CropBox-fallback- Oppgrader til v3.539.44 eller senere for konsekvente standardverdier og arv på tvers av boks-oppslag og capture
Sidebokser er der PDFs stille standardverdier møter prepress-toleranser målt i brøkdeler av en millimeter, og et bibliotek anvender enten de standardverdiene på samme måte overalt eller gir deg to svar på ett spørsmål. Hele boks-, capture- og Form XObject-API-et er dokumentert på PDFlibPas PDF Library for Delphi produktsiden