Keď PDF strana nemá TrimBox, jej efektívny TrimBox je CropBox strany a keď chýba aj CropBox, je to MediaBox. BleedBox a ArtBox sa držia toho istého pravidla. PDFlibPas, PDF Library for Delphi, aplikuje tento predvolený reťazec konzistentne v GetPageBox, HasPageBox a CapturePageEx od v3.539.44 a ignoruje produkčné boxy položené na uzle /Pages, lebo ISO 32000-1 nedovoľuje, aby sa dedili
Číta to ako poznámku pod čiarou, kým neimposujete zakázku. Predstavte si vnútro knihy s MediaBoxom 6,25 × 9,25 palca, CropBoxom nastaveným na 6 × 9 palcový orez a bez TrimBoxu, lebo ten, kto to exportoval, nikdy nepomyslel na to, že ho zapíše. Poprojte si trim box a dostanete media box a každá bunka na tlačovej forme ťahá osminku palca bleed a slugu do svojho suseda. PDFlibPas mal práve v tejto oblasti defekty, opravené vo v3.539.42 a v3.539.44, a spôsob ich opravy niečo vraví o tom, ako by sa page-box sémantika mala implementovať v akejkoľvek PDF knižnici
Ktorý box platí, keď strana nemá TrimBox?
Odpoveďou je fixný predvolený reťazec z ISO 32000-1 §14.11.2: CropBox sa predvolene berie z MediaBoxu a BleedBox, TrimBox a ArtBox sa každý predvolene berie z CropBoxu. Iba CropBox sa predvolene berie priamo z MediaBoxu. Strana definujúca len MediaBox má preto päť identických boxov a strana definujúca MediaBox plus CropBox má štyri boxy rovné CropBoxu
| Box | PDFlibPas BoxType | Predvolený pri neprítomnosti | Dediteľný z /Pages |
|---|---|---|---|
| MediaBox | 1 | Žiadny, položka je povinná | Áno |
| CropBox | 2 | MediaBox | Áno |
| BleedBox | 3 | CropBox | Nie |
| TrimBox | 4 | CropBox | Nie |
| ArtBox | 5 | CropBox | Nie |
Dvojkrokový reťazec má význam, lebo aj samotný CropBox môže byť zdedený. Efektívny TrimBox strany bez vlastného TrimBoxu aj CropBoxu je CropBox najbližšieho predka, ktorý nejaký má, a ak nie, zdedený MediaBox. Špecifikácia pridáva ešte jedno pravidlo, ktoré sa ľahko zabúda: crop, bleed, trim aj art boxy nemajú presahovať media box a ak presahujú, fakticky sa zmenšia na ich prienik s ním. PDFlibPas hlási každý box tak, ako je uložený v súbore, takže validátor obsluhujúci nedôveryhodný vstup by sa mal sám zastrapať proti MediaBoxu
Ktoré atribúty strany môže uzol /Pages odovzdať ďalej?
Presne štyri: Resources, MediaBox, CropBox a Rotate. ISO 32000-1 §7.7.3.4 definuje dedenie atribútov a Table 30 označuje ako dediteľné len tieto štyri položky page objektu. BleedBox, TrimBox a ArtBox patria listovej strane. TrimBox zapísaný do uzlu /Pages nie je zdedená hodnota; je to neštandardný kľúč, ktorý konformný čítač ignoruje
Takéto neštandardné súbory existujú, typicky s jediným TrimBoxom na koreňovom uzle page stromu ako skratka za „každá strana má tento orez“. Skratka vyzerá správne v každom nástroji, ktorý prechádza /Parent pre každý kľúč, a v tom je problém: súbor teraz znamená dve veci podľa toho, kto ho číta. Čítač nasledujúci špecifikáciu nevidí žiadny TrimBox a použije CropBox, zatiaľ čo čítač dediace všetko vidí rodičovskú hodnotu. V prepress pipeline tá dvojsmyselnosť skončí na tlačovej forme
Pracovné postupy PDF/X (ISO 15930) závisia od TrimBoxu pri hotovej veľkosti a profily PDF/X vyžadujú, aby každá strana deklarovala TrimBox alebo ArtBox. Box zaparkovaný na uzle /Pages tej požiadavke nevyhovie, lebo kľúč sa nikdy nedostane na page objekt. Preflight má také súbory označiť namiesto ich tichého čítania jedným alebo druhým spôsobom
Čo malo PDFlibPas pred v3.539.44 zle?
PDFlibPas mal tri oddelené defekty, všetky v medzere medzi tým, čo hovorí špecifikácia, a tým, čo robili dve nezávislé kódové cesty. Prvý sa opravil vo v3.539.42, ostatné dva vo v3.539.44
Produkčné boxy počas capture predvolené na MediaBox
Pred v3.539.42 dávala interná rutina pripravujúca stranu na capture (kopíruje zdedené položky na stranu a dopĺňa chýbajúce boxy) BleedBoxu, TrimBoxu a ArtBoxu hodnoty MediaBoxu, keď chýbali. CapturePageEx s options 2 až 4 číta svoj ohraničujúci obdĺžnik presne z týchto doplnených položiek, takže na strane definujúcej len CropBox vzala žiadosť o trim box celý media box. GetPageBox už predvolený reťazec z CropBoxu aplikoval a referencia CapturePageEx vždy vravela, že sa použije crop box, keď požadovaný box chýba; capture kód odporoval obojím. Od v3.539.42 sa tri produkčné boxy predvolene berú z CropBoxu strany, ktorý je v tom momente už na strane (vlastný, skopírovaný od predka alebo doplnený z MediaBoxu) a len samotný CropBox prepadne na MediaBox
Dve dedičné cesty, jedno sémantické pravidlo
Druhým defektom bolo samotné neštandardné dedenie a jemnou časťou bolo, že PDFlibPas riešil boxy po dvoch nezávislých cestách. Dotazy na boxy (GetPageBox a HasPageBox) prechádzali reťazec /Parent cez jedného pomocníka a capture ho prechádzal cez samostatného lokálneho pomocníka. Oba dedili každý kľúč, produkčné boxy vrátane. Opraviť len jeden by vyrobilo rozpor vnútri jediného dokumentu: s TrimBoxom širokým 180 bodov na uzle /Pages a CropBoxom širokým 380 bodov na strane by GetPageBox stále hlásil šírku orezu 180, kým CapturePageEx postavil formu širokú 380. Vo v3.539.44 obidve cesty obmedzujú prechod /Parent na štyri dediteľné kľúče, produkčné boxy sa čítajú len z listu a zatúlaná rodičovská položka ostáva v súbore nedotknutá, ani vymazaná, ani prepísaná
HasPageBox prehliadal priame rodičovské polia
HasPageBox vracia 0, keď strana nemá box požadovaného typu, 1, keď má vlastný box (uložený priamo alebo cez nepriamu referenciu) a 2, keď je MediaBox alebo CropBox zdedený od predka. Starý kód vracal 2 len vtedy, keď bola zdedená hodnota nepriamou referenciou, takže zdedené priame pole vrátilo 0. Oprava oddeluje dereferencovanie od testu poľa a obidve reprezentácie teraz vracajú 2. Od v3.539.44 môže HasPageBox pre BleedBox, TrimBox alebo ArtBox vrátiť len 0 alebo 1
Lekcia sa zobecňuje ďaleko za page boxy. Keď má jedna kus sémantiky špecifikácie v knižnici dva implementačné vstupné body, opravte ich spolu a testujte ich ako maticu, nie jedným happy-path súborom. Regresná sada PDFlibPas kríži dve reprezentácie rodičovského boxu (priame a nepriame pole) s tromi stavmi listu (chýbajúci, priame pole, nepriame pole) a tromi capture options (bleed, trim, art), čo dáva 18 scenárov a každý kontroluje výsledok dotazu, zachytené hranice, legitímne dedenie MediaBoxu a CropBoxu aj nedotknutú rodičovskú položku
Ako si v Delphi prečítam efektívny TrimBox?
Zavolajte GetPageBox(4, Dimension) na vybranej strane. PDFlibPas vám aplikuje predvolený reťazec, takže výsledok je efektívny TrimBox bez ohľadu na to, či ho strana má. Spárujte to s HasPageBox, keď potrebujete vedieť, odkiaľ hodnota pochádza, čo bežne chce preflight report
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 = vlastný, 2 = zdedený
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 aj SetPageBox pracujú v aktuálnych súradnicových nastaveniach dokumentu. Príklady tu bežia s predvolenými: pôvod 0 (ľavý dolný roh, sediaci s PDF user space) a body ako meracia jednotka, takže dimenzia Top je horná hrana meraná nahor od spodku strany. Po SetOrigin(1) sa dimenzie Top a Bottom merajú nadol od hornej hrany strany a po SetMeasurementUnits(1) prichádza každá hodnota v milimetroch. Šírka a výška od pôvodu nezávisia
Hľadanie produkčných boxov zabehnutých na uzly /Pages
Od v3.539.44 box API už nevidí TrimBox na uzle /Pages, čo je správne, ale preflight nástroj zvyčajne chce takýto súbor nahlásiť, nie ho potichu čítať po špecifikačne. Uzly page stromu sú obyčajné objekty, takže ich dokáže nájsť nízkoúrovňové objektové API: prechádzajte čísla objektov po GetMaxObjectNumber, čítajte každý cez GetObjectToString a hľadajte slovník /Pages nesúci kľúč produkčného boxu. Druhou polovicou kontroly je test na stranu, o ktorý sa stará PDF/X a HasPageBox na ňu teraz odpovedá tak, ako by odpovedal PDF/X validátor, lebo rodičovský TrimBox sa už nepočíta
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. Produkčné boxy na uzloch page stromu: neštandardné a ignorované
for ObjNum := 1 to Lib.GetMaxObjectNumber do
begin
Src := ''; // voľné čísla nevracajú žiadny text
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: každá strana potrebuje vlastný TrimBox alebo 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. Voliteľná oprava: orez 6 x 9 palcov vo vnútri media boxu 6,25 x 9,25 palca
// (body, pôvod v ľavom dolnom rohu: Left, Top, Width, Height)
if Missing > 0 then
Log.Add(Format('TrimBox written on %d pages',
[Lib.SetPageBoxRange('', 4, 9, 657, 432, 648)]));
end;
Textová zhoda je pragmatická kontrola, nie parser. Spolieha sa na to, že PDFlibPas serializuje každú položku slovníka ako kľúč, jednu medzeru a hodnotu, čo drží pre objekty prečítané cez GetObjectToString. Opravný krok si zaslúži rozhodnutie, nie reflex: zatúlaná rodičovská hodnota môže pokojne byť to, čo autor zamýšľal, ale overte to proti job ticketu skôr, než z toho urobíte oficiálnu vec. SetPageBoxRange s prázdnym rozsahom aplikuje box na každú stranu a vráti počet aktualizovaných strán. Keď je existujúci box strany nepriamym poľom, ktoré môže zdieľať ďalšia strana alebo uzol /Pages, SetPageBox dá tej strane nové priame pole namiesto prepisu zdieľaného objektu. Nastavenie BleedBoxu, TrimBoxu alebo ArtBoxu tiež pozdvihne odomknutý dokument na PDF 1.3, verziu, ktorá tieto položky zaviedla
Imposovanie strán na TrimBox cez CapturePageEx
CapturePageEx(Page, 3) zmení stranu na Form XObject, ktorého ohraničujúci box je efektívny TrimBox strany, a DrawCapturedPage umiestni tú formu na inú stranu v ľubovoľnej veľkosti. Od v3.539.42 dá option 3 na strane bez TrimBoxu CropBox, ako popisuje referencia, namiesto MediaBoxu so všetkým slugom
Dve vlastnosti capture tvarujú kód. Capture je deštruktívny: zachytená strana sa odstráni z dokumentu a dokument nikdy nemôže klesnúť na nula strán, takže pripojte prvý výstupný hárok skôr, než čokoľvek zachytíte. Capture navyše funguje len vnútri jedného dokumentu, takže najprv stiahnite každý vstup do jediného dokumentu; techniky z článku o kollátovaní a prelúdaní PDF zdrojov v jednom prechode sa aplikujú priamo
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;
// Efektívna veľkosť orezu strany 1 (toto rozloženie predpokladá uniformný orez)
Lib.SelectPage(1);
TrimW := Lib.GetPageBox(4, 2);
TrimH := Lib.GetPageBox(4, 3);
// Pripojte a nastavte veľkosť prvého hárka; NewPage vyberie novú stranu
Lib.NewPage;
Lib.SetPageDimensions(2 * TrimW, TrimH);
// Každý capture odstráni stranu 1, takže ďalšia zdrojová strana sa posunie nahor
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;
// Ostáva len hárok: dva orezané strany na hárok, vedľa seba
Lib.SelectPage(1);
for I := 0 to SourceCount - 1 do
begin
if (I > 0) and (I mod 2 = 0) then
Lib.NewPage; // rovnakej veľkosti ako aktuálny hárok
// Predvolený pôvod: Top je horná hrana, meraná od spodku
Lib.DrawCapturedPage(Captures[I], (I mod 2) * TrimW, TrimH, TrimW, TrimH);
end;
Lib.SaveToFile(OutFile);
finally
Lib.Free;
end;
end;
Capture založený na oreze ostripuje všetko mimo TrimBoxu, čo chcete pre digitálny náhľad alebo cut-and-stack rozloženie. Pre tlačovú formu orezávanú po tlači zachytávajte s option 2, aby bleed prežil, a rozmiestnite bunky so vzdialenosťou podľa šírky bleedu. Keďže capture odstraňuje zdrojové strany, bookmarky a linky ukazujúce na ne prídu o svoje ciele, takže imposujte do samostatného výstupného súboru namiesto editovania dokumentu, ktorého navigáciu ešte potrebujete; nahradzovanie strán bez rozbitia bookmarkov pokrýva tú stránku operácií na stranách
Keď zdroj musí ostať nedotknutý, ImportPageAsFormXObject(SourceDocumentID, SourcePage, Options) berie rovnaké hodnoty options 0 až 4 (podajte Lib.SelectedDocument pre aktuálny dokument), necháva page strom zdroja nezmenený, normalizuje zdedenú rotáciu strany do matice formy a vracia handle, ktorý DrawCapturedPage prijíma. CapturePageEx nerozviaže /Rotate, takže otočený vstup potrebuje ten krok najprv a zrovnanie rotácie strany bez rozbitia page boxov ukazuje, čo sa stane každému boxu, keď to urobíte. Jedna výhrada pre vstupy, ktoré môžu niesť produkčné boxy na uzloch /Pages: importná cesta rieši svoj box cez vlastné vyhľadávanie predkov, oddelené od dvoch ciest zosúladených vo v3.539.44, takže najprv skontrolujte HasPageBox(4) na zdrojovej strane a podajte option 1 (CropBox), keď vráti 0. Tým zostane výsledok viazaný na špecifikáciu, nie na to, ako bol súbor náhodou napísaný
Rýchla referencia page boxov
- Efektívny CropBox: vlastný CropBox strany, inak najbližší zdedený CropBox, inak efektívny MediaBox (ISO 32000-1 §14.11.2)
- Efektívny BleedBox, TrimBox a ArtBox: vlastná položka listovej strany, inak efektívny CropBox
- Z uzlov
/Pagesdedia lenResources,MediaBox,CropBoxaRotate(§7.7.3.4, Table 30); produkčné boxy na uzloch/Pagessa ignorujú 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 žiadny box, 1 vlastný box strany (priamy alebo nepriamy), 2 zdedený MediaBox alebo CropBox (priamy alebo nepriamy)CapturePageEx(Page, Options): 0 MediaBox, 1 CropBox s prepadom na MediaBox, 2 až 4 BleedBox, TrimBox alebo ArtBox s prepadom na CropBox- Upgradujte na v3.539.44 alebo novšiu pre konzistentné predvolené a dedenie cez box dotazy aj capture
Page boxy sú miesto, kde sa tiché predvolené PDF stretávajú s prepress toleranciami meranými v zlomkoch milimetra a knižnica buď aplikuje tie predvolené všade rovnako, alebo vám podá dve odpovede na jednu otázku. Plné box, capture a Form XObject API je zdokumentované na produktovej stránke PDFlibPas PDF Library for Delphi