Kai PDF puslapis neturi TrimBox, jo efektyvusis TrimBox yra puslapio CropBox, o kai nėra ir CropBox – MediaBox. BleedBox ir ArtBox laikosi tos pačios taisyklės. PDFlibPas, PDF Library for Delphi, šią numatytąją grandinę taiko nuosekliai GetPageBox, HasPageBox ir CapturePageEx nuo v3.539.44, o gamybines dėzes, padėtas ant /Pages mazgo, ignoruoja, nes ISO 32000-1 neleidžia joms paveldėti
Tai atrodo kaip išnaša, kol nepradedate skaidyti darbo. Įsivaizduokite knygos vidų su 6,25 × 9,25 col MediaBox, CropBox, nustatytu į 6 × 9 col apipjovimą, ir be TrimBox, nes kas jį eksportavo, apie tokį rašymą nėra pagalvojęs. Prašote trim dėzės – gaunate media dėzę, ir kiekviena spaudos lakšto ląstelė tempia aštuntadalį colio bleed ir slug į kaimynę. PDFlibPas turėjo defektų būtent šioje srityje, pataisytų v3.539.42 ir v3.539.44, o taisymo būdas ką nors pasako apie tai, kaip puslapių dėžių semantiką derėtų įgyvendinti bet kurioje PDF bibliotekoje
Kuri dėžė galioja, kai puslapis neturi TrimBox?
Atsakymas – fiksuota numatytųjų reikšmių grandinė iš ISO 32000-1 §14.11.2: CropBox numatytoji yra MediaBox, o BleedBox, TrimBox ir ArtBox kiekviena numatytoji – CropBox. Niekas, išskyrus CropBox, nėra tiesiogiai numatytoji MediaBox. Puslapis, apibrėžiantis vien MediaBox, todėl turi penkias identiškas dėzes, o puslapis su MediaBox ir CropBox turi keturias dėžes, lygias CropBox
| Dėžė | PDFlibPas BoxType | Numatytoji, kai nėra | Paveldima iš /Pages |
|---|---|---|---|
| MediaBox | 1 | Nėra, įrašas privalomas | Taip |
| CropBox | 2 | MediaBox | Taip |
| BleedBox | 3 | CropBox | Ne |
| TrimBox | 4 | CropBox | Ne |
| ArtBox | 5 | CropBox | Ne |
Dviejų žingsnių grandinė svarbi, nes pats CropBox gali būti paveldėtas. Efektyvusis puslapio, neturinčio nei TrimBox, nei savo CropBox, TrimBox yra artimiausio turinčiojo protėvio CropBox, o nesant tokio – paveldėtasis MediaBox. Specifikacija prideda dar vieną lengvai užmirštamą taisyklę: crop, bleed, trim ir art dėžės neturėtų išsiplėsti už media dėžės, o jei išsiplečia, jos efektyviai sutraukiamos iki savo sankirtos su ja. PDFlibPas kiekvieną dėzę praneša taip, kaip ji saugoma faile, tad validatorius, apdorojantis nepatikimą įvedimą, turėtų pats kirpti pagal MediaBox
Kuriuos puslapio atributus /Pages mazgas gali perduoti žemyn?
Lygiai keturis: Resources, MediaBox, CropBox ir Rotate. ISO 32000-1 §7.7.3.4 apibrėžia atributų paveldėjimą, o 30 lentelė tik tas keturias puslapio objekto reikšmes pažymi paveldimomis. BleedBox, TrimBox ir ArtBox priklauso lapiniam puslapiui. TrimBox, įrašytas į /Pages mazgą, nėra paveldima reikšmė; tai nestandartinis raktas, kurį atitinkantis standartui skaitytuvas ignoruoja
Tokie nestandartiniai failai egzistuoja – paprastai su vienu TrimBox ant šakninio puslapių medžio mazgo kaip trumpinis „kiekvienas puslapis turi šitą trim“. Trumpinis atrodo teisingas bet kuriame įrankyje, einančiame /Parent kiekvienam raktui, ir tai yra problema: failas dabar reiškia du dalykus, priklausomai nuo to, kas jį skaito. Standartą atsekantis skaitytuvas TrimBox nemato ir naudoja CropBox, o viską paveldintis skaitytuvas mato tėvo reikšmę. Išspaudos paruošimo grandinėje ta dviprasmybė nukeliauja ant spaudos lakšto
PDF/X (ISO 15930) darbo eigos nuo TrimBox priklauso dėl galutinio dydžio, o PDF/X profiliai reikalauja, kad kiekvienas puslapis deklaruotų TrimBox arba ArtBox. Dėzę, pastatytą ant /Pages mazgo, tas reikalavimas nepatenkina, nes raktas niekada nepasiekia puslapio objekto. Preflight tokius failus turėtų pažymėti, o ne tylioje skaityti juos vienaip ar kitaip
Ką PDFlibPas darė negerai iki v3.539.44?
PDFlibPas turėjo tris atskirus defektus, visus – tarpelyje tarp to, ką sako specifikacija, ir to, ką darė du nepriklausomi kodo keliai. Pirmasis pataisytas v3.539.42, kiti du – v3.539.44
Gamybinės dėžės fiksacijos metu gaudavo MediaBox numatytąją
Iki v3.539.42 vidinė rutina, ruošianti puslapį fiksacijai (ji nukopijuoja paveldėtas reikšmes ant puslapio ir užpildo dingusias dėzes), BleedBox, TrimBox ir ArtBox teikdavo MediaBox reikšmes, kai tų nėra. CapturePageEx su 2–4 parinktimis savo aprėpiantįjį stačiakampį skaito būtent iš tų užpildytų reikšmių, tad puslapyje, apibrėžiančiame vien CropBox, prašymas trim dėzės fiksavo visą media dėzę. GetPageBox CropBox numatytąją jau taikydavo, ir CapturePageEx dokumentacija visada sakydavo, kad nesant prašytos dėzės naudojama crop dėzė; fiksacijos kodas su abiem nesutapo. Nuo v3.539.42 trys gamybinės dėžės numatytąja turi puslapio CropBox, kuris tuo momentu jau būna ant puslapio (savas, nukopijuotas iš protėvio arba užpildytas iš MediaBox), o vien pats CropBox grįžta prie MediaBox
Du paveldėjimo keliai, viena semantinė taisyklė
Antras defektas buvo pats nestandartinis paveldėjimas, o subtili dalis ta, kad PDFlibPas dėzes išspręndavo dviem nepriklausomais keliais. Dėžių užklausos (GetPageBox ir HasPageBox) /Parent grandinę einavo per vieną pagalbinę funkciją, o fiksacija – per atskirą vietinę. Abu paveldėdavo kiekvieną raktą, įskaitant gamybines dėzes. Vieno jų taisymas sukurtų prieštaravimą vieno dokumento viduje: su 180 taškų pločio TrimBox ant /Pages mazgo ir 380 taškų pločio CropBox ant puslapio GetPageBox vis dar praneštų 180 trim plotį, kol CapturePageEx sukurtų 380 pločio formą. v3.539.44 abu keliai /Parent ėjimą apriboja keturiais paveldimaisiais raktais, gamybinės dėžės skaitomos tik iš lapo, o atsilikęs tėvo įrašas lieka faile nepaliestas – nei ištrintas, nei perrašytas
HasPageBox praleisdavo tiesioginius tėvo masyvus
HasPageBox grąžina 0, kai puslapis neturi prašyto tipo dėzės, 1 – kai puslapis turi savąją (saugomą tiesiogiai arba per netiesioginę nuorodą), ir 2 – kai MediaBox arba CropBox paveldėti iš protėvio. Senasis kodas 2 grąžindavo tik tada, kai paveldėta reikšmė buvo netiesioginė nuoroda, tad paveldėtas tiesioginis masyvas grąžindavo 0. Taisymas atskiria nuorodos išskleidimą nuo masyvo patikros, ir abu atvaizdai dabar grąžina 2. Nuo v3.539.44 HasPageBox BleedBox, TrimBox arba ArtBox gali grąžinti tik 0 arba 1
Pamoka apibendrinama gerokai už puslapių dėžių. Kai viena specifikacijos semantikos dalis bibliotekoje turi du įgyvendinimo įėjimo taškus, taisykite juos kartu ir testuokite matrica, o ne vienu viskas-gerai failu. PDFlibPas regresijos rinkinys susikerta dviejų tėvo dėžių atvaizdų (tiesioginis ir netiesioginis masyvas) su trimis lapo būsenomis (nėra, tiesioginis masyvas, netiesioginis masyvas) ir trimis fiksacijos parinktimis (bleed, trim, art) – išeina 18 scenarijų, ir kiekvienas tikrina užklausos rezultatą, fikuotas ribas, teisėtą MediaBox ir CropBox paveldėjimą bei nepaliestą tėvo įrašą
Kaip Delphi perskaityti efektyvųjį TrimBox?
Kvieskite GetPageBox(4, Dimension) pažymėtame puslapyje. PDFlibPas numatytąją grandinę taiko už jus, tad rezultatas – efektyvusis TrimBox, ar puslapis turi savąjį, ar ne. Poruokite su HasPageBox, kai reikia žinoti, iš kur atkeliavo reikšmė – preflight ataskaita to paprastai ir nori
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 = savas, 2 = paveldėtas
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 ir SetPageBox abu dirba dokumento dabartiniuose koordinčių nustatymuose. Čia esantys pavyzdžiai sukasi su numatytosiomis: kilmė 0 (apačioje kairėje, kaip PDF naudotojo erdvė) ir taškai kaip matavimo vienetas, tad Top matmuo – viršutinė kraštinė, skaičiuojama aukštyn nuo puslapio apačios. Po SetOrigin(1) Top ir Bottom matmenys skaičiuojami žemyn nuo puslapio viršaus, o po SetMeasurementUnits(1) kiekviena reikšmė grįžta milimetrais. Plotis ir aukštis nuo kilmės nepriklauso
Gamybinių dėžių, įstringusių ant /Pages mazgų, paieška
Nuo v3.539.44 dėžių API TrimBox ant /Pages mazgo daugiau nemato – teisingai – bet preflight įrankis paprastai nori tokius failus pranešti, o ne tylioje skaityti pagal standartą. Puslapių medžio mazgai – paprasti objektai, tad žemo lygio objektų API gali juos rasti: apeikite objektų numerius iki GetMaxObjectNumber, kiekvieną perskaitykite su GetObjectToString ir ieškokite /Pages žodyno, nešančio gamybinės dėžės raktą. Antroji patikros pusė – puslapiui skirtas testas, kurį rūpinasi PDF/X, ir HasPageBox dabar į jį atsako taip, kaip atsaktų PDF/X validatorius, nes tėvo TrimBox daugiau nesiskaito
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. Gamybinės dėžės puslapių medžio mazguose: nestandartinės ir ignoruojamos
for ObjNum := 1 to Lib.GetMaxObjectNumber do
begin
Src := ''; // laisvi numeriai negrąžina teksto
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: kiekvienam puslapiui reikia savo TrimBox arba 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. Pasirinktinis taisymas: 6 x 9 col trim 6.25 x 9.25 col media dėžėje
// (taškai, apatinės kairės kilmė: Left, Top, Width, Height)
if Missing > 0 then
Log.Add(Format('TrimBox written on %d pages',
[Lib.SetPageBoxRange('', 4, 9, 657, 432, 648)]));
end;
Teksto atitikimas – pragmatinė patikra, ne analizatorius. Ji remiasi tuo, kad PDFlibPas kiekvieną žodyno įrašą užrašo kaip raktą, vieną tarpą ir reikšmę – kas laikosi objektams, perskaitytiems atgal pro GetObjectToString. Taisymo žingsnis nusipelno sprendimo, o ne reflekso: atsilikusi tėvo reikšmė gali būti būtent tai, ką ketino autorius, bet patvirtinkite ją pagal darbo užsakymą prieš padarydami oficialia. SetPageBoxRange su tuščiu intervalu dėzę taiko kiekvienam puslapiui ir grąžina atnaujintų puslapių skaičių. Kai puslapio esama dėzė yra netiesioginis masyvas, kurį gali dalintis kitas puslapis arba /Pages mazgas, SetPageBox tam puslapiui duoda naują tiesioginį masyvą vietoj bendrojo objekto perrašymo. BleedBox, TrimBox arba ArtBox nustatymas dar ir pakelia neatrakintą dokumentą iki PDF 1.3 – versijos, įvedusios tas reikšmes
Puslapių skaidymas pagal TrimBox su CapturePageEx
CapturePageEx(Page, 3) puslapį paverčia Form XObject, kurio aprėpianti dėzė – puslapio efektyvusis TrimBox, o DrawCapturedPage tą formą įstatinėja į kitą puslapį bet kokiu dydžiu. Nuo v3.539.42 3 parinktis puslapyje be TrimBox duoda CropBox, kaip ir sako dokumentacija, vietoj MediaBox su visu jo slugu
Dvi fiksacijos savybės formuoja kodą. Fiksacija naikinanti: fiksuotas puslapis iš dokumento pašalinamas, o dokumentas niekada negali nukristi iki nulio puslapių, tad pirmąjį išvesties lakštą prijunkite dar prieš fiksuodami ką nors. Fiksacija veikia ir tik vieno dokumento viduje, tad visus įvedimus pirmiausia sutraukite į vieną dokumentą; technikos iš PDF šaltinių sugrupavimo ir perpynimo vienu ėjimu tinka tiesiogiai
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 puslapio efektyvusis trim dydis (šis išdėstymas mano vienodą trim)
Lib.SelectPage(1);
TrimW := Lib.GetPageBox(4, 2);
TrimH := Lib.GetPageBox(4, 3);
// Pridėkite ir išmatuokite pirmąjį lakštą; NewPage pažymi naująjį puslapį
Lib.NewPage;
Lib.SetPageDimensions(2 * TrimW, TrimH);
// Kiekviena fiksacija pašalina 1 puslapį, tad kita šaltinio puslapio eina aukštyn
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;
// Liko vienas lakštas: du apkarpyti puslapiai lakštui, vienas šalia kito
Lib.SelectPage(1);
for I := 0 to SourceCount - 1 do
begin
if (I > 0) and (I mod 2 = 0) then
Lib.NewPage; // toks pat dydis kaip dabartinis lakštas
// Numatytoji kilmė: Top – viršutinė kraštinė, matuojama nuo apačios
Lib.DrawCapturedPage(Captures[I], (I mod 2) * TrimW, TrimH, TrimW, TrimH);
end;
Lib.SaveToFile(OutFile);
finally
Lib.Free;
end;
end;
Trim pagrįsta fiksacija nukerpa viską už TrimBox ribų – būtent to norite skaitmeniniam maketui arba cut-and-stack išdėstymui. Spaudos lakštui, kuris apipjaustomas po spaudos, fiksuokite su 2 parinktimi, kad bleed išgyventų, ir išdėstykite ląsteles bleed pločiu. Kadangi fiksacija pašalina šaltinio puslapius, į juos rodančios žymės ir nuorodos praranda tikslus, tad skaidykite į atskirą išvesties failą, o ne redaguodami dokumentą, kurio naršymas jums dar reikalingas; puslapių keitimas nesugadinus žymių dengia tą puslapių chirurgijos pusę
Kai šaltinis turi likti nepaliestas, ImportPageAsFormXObject(SourceDocumentID, SourcePage, Options) priima tas pačias 0–4 parinktis (dabartiniam dokumentui perduokite Lib.SelectedDocument), palieka šaltinio puslapių medį nepakeistą, paveldėtą puslapio pasukimą suvienodina į formos matricą ir grąžina rankeną, kurią priima DrawCapturedPage. CapturePageEx /Rotate neatšaukia, tad pasuktas įvedimas pirmiausia reikalauja to žingsnio, o puslapio pasukimo išlyginimas nesugadinus puslapių dėžių rodo, kas nutinka kiekvienai dėzei, kai tai darote. Vienas įspėjimas dėl įvedimų, galinčių nešti gamybines dėzes ant /Pages mazgų: importo kelias savo dėzę išspręndžia per savą protėvių paiešką, atskirą nuo dviejų v3.539.44 sulygintų kelių, tad pirmiausia patikrinkite HasPageBox(4) šaltinio puslapyje ir perduokite 1 parinktį (CropBox), kai ji grąžina 0. Taip rezultatas lieka susietas su standartu, o ne su tuo, kaip failas atsitiktinai parašytas
Puslapių dėžių trumpa atmintinė
- Efektyvusis CropBox: puslapio savasis CropBox, kitu atveju artimiausias paveldėtasis CropBox, dar kitu – efektyvusis MediaBox (ISO 32000-1 §14.11.2)
- Efektyviosios BleedBox, TrimBox ir ArtBox: lapinio puslapio savas įrašas, kitu atveju – efektyvusis CropBox
- Tik
Resources,MediaBox,CropBoxirRotatepaveldimi iš/Pagesmazgų (§7.7.3.4, 30 lentelė); gamybinės dėžės ant/Pagesmazgų ignoruojamos 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 – dėzės nėra, 1 – puslapio savoji dėzė (tiesioginė ar netiesioginė), 2 – paveldėta MediaBox arba CropBox (tiesioginė ar netiesioginė)CapturePageEx(Page, Options): 0 MediaBox, 1 CropBox su MediaBox atsarginiu, 2–4 BleedBox, TrimBox arba ArtBox su CropBox atsarginiu- Atnaujinkite iki v3.539.44 ar vėlesnės, kad numatytosios reikšmės ir paveldėjimas būtų nuoseklūs tarp dėžių užklausų ir fiksacijos
Puslapių dėžės – vieta, kur PDF tyliosios numatytosios reikšmės susitinka su išspaudos paruošimo leistinais nuokrypiais, matuojamais milimetro dalimis, ir biblioteka arba tas numatytąsias taiko visur vienodai, arba duoda jums du atsakymus į vieną klausimą. Pilna dėžių, fiksacijos ir Form XObject API dokumentuota PDFlibPas PDF Library for Delphi produkto puslapyje