Ko stran PDF nima TrimBox, je njen učinkoviti TrimBox CropBox strani, kadar pa manjka tudi CropBox, je to MediaBox. BleedBox in ArtBox sledita istemu pravilu. PDFlibPas, knjižnica PDF za Delphi, uveljavi to privzeto verigo dosledno v GetPageBox, HasPageBox in CapturePageEx od v3.539.44, okvirje za produkcijo, postavljene na vozel /Pages, pa ignorira, ker jih ISO 32000-1 ne pusti podedovati
To zveni kot opomba pod črto, dokler ne sestavite posla. Predstavljajte si notranjost knjige z MediaBox 6,25 × 9,25 palca, CropBox nastavljenim na obrez 6 × 9 palcev in brez TrimBox, ker tisti, ki jo je izvozil, ni pomislil, da bi ga zapisal. Zahtevate obrezni okvir, dobite medijskega, in vsaka celica na vašem tiskovnem listu vleče osmino palca prelive in odpada v sosedo. PDFlibPas je imel napake točno na tem območju, popravljene v v3.539.42 in v3.539.44, način njihovega popravila pa nekaj pove o tem, kako bi semantiko okvirjev strani morala implementirati katera koli knjižnica PDF
Kateri okvir velja, kadar stran nima TrimBox?
Odgovor je fiksna privzeta veriga iz ISO 32000-1 §14.11.2: CropBox se privzame na MediaBox, BleedBox, TrimBox in ArtBox pa se vsak privzame na CropBox. Ničesar, razen CropBox, se ne privzame naravnost na MediaBox. Stran, ki definira samo MediaBox, ima torej pet identičnih okvirjev, stran, ki definira MediaBox plus CropBox, pa štiri okvirje, enake CropBox
| Okvir | BoxType v PDFlibPas | Privzeto, kadar manjka | Podedovalno s /Pages |
|---|---|---|---|
| MediaBox | 1 | Brez, vnos je obvezen | Da |
| CropBox | 2 | MediaBox | Da |
| BleedBox | 3 | CropBox | Ne |
| TrimBox | 4 | CropBox | Ne |
| ArtBox | 5 | CropBox | Ne |
Dvostopenjska veriga šteje, ker je lahko CropBox sam podedovan. Učinkoviti TrimBox strani, ki nima ne TrimBox ne lastnega CropBox, je CropBox najbližjega prednika, ki ga ima, če pa tega ni, podedovani MediaBox. Specifikacija doda še eno pravilo, ki ga je lahko pozabiti: okvirji obreza, prelive, trim in umetnosti ne smejo segati čez medijski okvir, če pa segajo, so učinkovito zmanjšani na svoje presečišče z njim. PDFlibPas poroča vsak okvir, kakor je shranjen v datoteki, zato naj validator, ki obravnava nezaupljiv vnos, sam stisne proti MediaBox
Katere atribute strani lahko vozel /Pages prenese naprej?
Točno štiri: Resources, MediaBox, CropBox in Rotate. ISO 32000-1 §7.7.3.4 definira podedovanje atributov, tabela 30 pa označuje kot podedovalne samo tiste štiri vnose objekta strani. BleedBox, TrimBox in ArtBox pripadajo listni strani. TrimBox, zapisan vozlu /Pages, ni podedovana vrednost; to je nestandardni ključ, ki ga skladen bralec ignorira
Nestandardne datoteke, take kot ta, obstajajo, običajno z enim samim TrimBox na korenskem vozlu stranskega drevesa kot okrajšavo za "vsaka stran ima ta obrez". Okrajšava je videti pravilna v vsakem orodju, ki slede /Parent za vsak ključ, in to je težava: datoteka zdaj pomeni dve stvari, odvisno od tega, kdo jo bere. Bralec, ki sledi specifikaciji, ne vidi TrimBox in uporabi CropBox, bralec, ki podeduje vse, pa vidi starševsko vrednost. V predtiskovnem cevovodu ta dvoumnost pristane na tiskovnem listu
Delovni tokovi PDF/X (ISO 15930) so od TrimBox odvisni za končno velikost, profili PDF/X pa zahtevajo, da vsaka stran deklarira TrimBox ali ArtBox. Okvir, parkiran na vozlu /Pages, tiste zahteve ne izpolnjuje, ker ključ nikoli ne doseže objekta strani. Preflight naj take datoteke označi, namesto da bi jih tiho bral na en ali drug način
Kaj je PDFlibPas naredil narobe pred v3.539.44?
PDFlibPas je imel tri ločene napake, vse v reki med tem, kar pravi specifikacija, in tem, kar sta počela dva neodvisna kodna predora. Prva je bila popravljena v v3.539.42, drugi dve pa v v3.539.44
Produktni okvirji so se med zajemom privzeli na MediaBox
Pred v3.539.42 je notranja rutina, ki pripravi stran za zajem (kopira podedovane vnose na stran in dopolni manjkajoče okvirje), BleedBox, TrimBox in ArtBox dala vrednosti MediaBox, kadar so manjkali. CapturePageEx z opcijami 2 do 4 bere svoj obdajajoč pravokotnik točno iz teh dopolnjenih vnosov, tako da na strani, ki definira samo CropBox, zahteva po obreznem okvirju zajela celoten medijski okvir. GetPageBox je privzeti CropBox že uveljavljal in sklic CapturePageEx je ves čas pravil, da se uporabi obrezni okvir, kadar zahtevani okvir manjka; koda zajema se je z obema skorala. Od v3.539.42 se trije produktni okvirji privzamejo na CropBox strani, ki je do takrat že na strani (njen lasten, kopiran od prednika ali dopolnjen iz MediaBox), samo CropBox sam pa pade nazaj na MediaBox
Dve poti podedovanja, eno semantično pravilo
Druga napaka je bilo samo nestandardno podedovanje, tanko pa je bilo to, da je PDFlibPas razreševal okvirje po dveh neodvisnih predorih. Poizvedbe okvirjev (GetPageBox in HasPageBox) so slede verigi /Parent skozi enega pomočnika, zajem pa skozi ločenega lokalnega pomočnika. Oba sta podedovala vsak ključ, produktni okvirji niso bili izvzeti. Popravek samo enega od njiju bi znotraj enega dokumenta izdelal protislovje: s TrimBox, širokim 180 točk, na vozlu /Pages in CropBox, širokim 380 točk, na strani bi GetPageBox še naprej poročal širino obreza 180, medtem ko bi CapturePageEx zgradil formo, široko 380. V v3.539.44 obe poti omejita hojo po /Parent na štiri podedovalne ključe, produktni okvirji se berejo samo z lista, tuj starševski vnos pa ostane v datoteki nedotaknjen — ne izbrisan in ne prepisan
HasPageBox je spregledal neposredne starševske sezname
HasPageBox vrne 0, kadar stran nima okvirja zahtevanega tipa, 1, kadar ima stran lastnega (shranjenega neposredno ali skozi indirekten sklic), in 2, kadar je MediaBox ali CropBox podedovan od prednika. Stara koda je vrnila 2 samo, kadar je bila podedovana vrednost indirekten sklic, tako da je podedovani neposreden seznam vrnil 0. Popravek loči razreševanje sklica od preskusa seznama in obe representaciji zdaj vrneta 2. Od v3.539.44 HasPageBox za BleedBox, TrimBox ali ArtBox lahko vrne samo 0 ali 1
Lekcija se posploši daleč prek okvirjev strani. Ko en kos semantike specifikacije ima v knjižnici dve implementacijski vstopni točki, ju popravite skupaj in ju preskusite kot matriko, ne z eno samo srečno-potno datoteko. Regresijska množica PDFlibPas prekriža dve representaciji starševskega okvirja (neposredni in indirektni seznam) s tremi stanji lista (manjka, neposredni seznam, indirektni seznam) in tremi opcijami zajema (bleed, trim, art), kar da 18 scenarijev, vsak pa preveri rezultat poizvedbe, zajete meje, legitemirno podedovanje MediaBox in CropBox ter nedotaknjen starševski vnos
Kako v Delphiju preberem učinkoviti TrimBox?
Kličite GetPageBox(4, Dimension) na izbrani strani. PDFlibPas vam uveljavi privzeto verigo, tako da je rezultat učinkoviti TrimBox, ne glede na to, ali ga stran ima. Parirajte ga s HasPageBox, kadar morate vedeti, od kod vrednost prihaja, kar poročilo preflighta ponavadi hoče
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 = lastna, 2 = podedovana
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 in SetPageBox delujeta v trenutnih koordinatnih nastavitvah dokumenta. Primeri tukaj tečejo s privzetimi: izvor 0 (spodaj levo, skladen s uporabniškim prostorom PDF) in točke kot merska enota, tako da je dimenzija Top zgornji rob, merjen navzgor od spodnjega roba strani. Po SetOrigin(1) sta dimenziji Top in Bottom merjeni navzdol od zgornjega roba strani, po SetMeasurementUnits(1) pa vsaka vrednost pride nazaj v milimetrih. Širina in višina nista odvisna od izvora
Iskanje produktnih okvirjev, nasedlih na vozlih /Pages
Od v3.539.44 API okvirjev TrimBox na vozlu /Pages ne vidi več, kar je prav, orodje preflighta pa običajno hoče tako datoteko poročati, namesto da bi jo tiho bralo po specifikaciji. Vozli stranskega drevesa so navadni objekti, zato jih lahko najde nizkonivojski objektni API: hodite po številkah objektov do GetMaxObjectNumber, berite vsakega z GetObjectToString in iščite slovar /Pages, ki nosi produktni okvirni ključ. Druga polovica preizkusa je preizkus po posameznih straneh, ki zanima PDF/X, HasPageBox pa nanj zdaj odgovarja, kakor bi validator PDF/X, ker starševski TrimBox ne šteje več
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. Produktni okvirji na vozlih stranskega drevesa: nestandardni in ignorirani
for ObjNum := 1 to Lib.GetMaxObjectNumber do
begin
Src := ''; // prosta številka ne vrne besedila
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: vsaka stran potrebuje svoj TrimBox ali 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. Neobvezen popravek: obrez 6 x 9 palcev znotraj medijskega okvirja 6.25 x 9.25 palca
// (točke, izhodišče spodaj levo: Left, Top, Width, Height)
if Missing > 0 then
Log.Add(Format('TrimBox written on %d pages',
[Lib.SetPageBoxRange('', 4, 9, 657, 432, 648)]));
end;
Tekstovno ujemanje je pragmatičen preizkus, ne razčlenjevalnik. Zanaša se na to, da PDFlibPas serializira vsak vnos slovarja kot ključ, en presledek in vrednost, kar velja za objekte, prebrane nazaj skozi GetObjectToString. Korak popravila zasluži odločitev, ne refleksa: tuj starševski vnos je lahko kar to, kar je nameraval avtor, a ga potrdite proti delovnemu nalogu, preden ga povzdignete v uradnega. SetPageBoxRange s praznim razponom uveljavi okvir na vsako stran in vrne število posodobljenih strani. Ko je obstoječi okvir strani indirekten seznam, ki si ga lahko deli druga stran ali vozel /Pages, SetPageBox da tej strani nov neposreden seznam, namesto da bi prepisal deljeni objekt. Nastavitev BleedBox, TrimBox ali ArtBox tudi dvigne odklenjen dokument na PDF 1.3, različico, ki je uvedla te vnose
Impozicija strani na TrimBox s CapturePageEx
CapturePageEx(Page, 3) spremeni stran v Form XObject, katerega obdajajoči okvir je učinkoviti TrimBox strani, DrawCapturedPage pa to formo postavi na drugo stran v kateri koli velikosti. Od v3.539.42 vam opcija 3 na strani brez TrimBox da CropBox, kakor pravi sklic, namesto MediaBox z vsem odpadom
Dve lastnosti zajema oblikujeta kodo. Zajem je destruktiven: zajeta stran je odstranjena iz dokumenta in dokument ne more nikoli pasti na nič strani, zato dodajte prvi izhodni list, preden zajamete karkoli. Zajem dela tudi samo znotraj enega dokumenta, zato povlecite vsak vhod najprej v en sam dokument; tehnike za sestavljanje in prepletanje virov PDF v enem prehodu veljajo neposredno
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;
// Učinkovita velikost obreza strani 1 (ta postavitev predpostavlja enoten obrez)
Lib.SelectPage(1);
TrimW := Lib.GetPageBox(4, 2);
TrimH := Lib.GetPageBox(4, 3);
// Dodajte in dimenzionirajte prvi list; NewPage izbere novo stran
Lib.NewPage;
Lib.SetPageDimensions(2 * TrimW, TrimH);
// Vsak zajem odstrani stran 1, zato se naslednja izvorna stran premakne gor
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;
// Ostane samo list: dve obrezani strani na list, druga ob drugi
Lib.SelectPage(1);
for I := 0 to SourceCount - 1 do
begin
if (I > 0) and (I mod 2 = 0) then
Lib.NewPage; // iste velikosti kot trenutni list
// Privzet izvor: Top je zgornji rob, merjen od spodaj
Lib.DrawCapturedPage(Captures[I], (I mod 2) * TrimW, TrimH, TrimW, TrimH);
end;
Lib.SaveToFile(OutFile);
finally
Lib.Free;
end;
end;
Zajem na osnovi obreza odreže vse izven TrimBox, kar je to, kar hočete za digitalni izvod ali postavitev reži-in-sklada. Za tiskovni list, obrezan po tiskanju, zajemite z opcijo 2, da preživi preliv, in razmaknite celice za širino prelive. Ker zajem odstrani izvorne strani, zaznamki in povezave, ki so kazale nanje, izgubijo svoje cilje, zato sestavite v ločeno izhodno datoteko, namesto da bi urejali dokument, katerega navigacijo še potrebujete; zamenjava strani brez lomljenja zaznamkov pokriva to stran stranske kirurgije
Kadar mora izvirnik ostati nedotaknjen, ImportPageAsFormXObject(SourceDocumentID, SourcePage, Options) vzame iste vrednosti opcij od 0 do 4 (podajte Lib.SelectedDocument za trenutni dokument), pusti stransko drevo izvirnika nespremenjeno, normalizira podedovani zasuk strani v matriko forme in vrne ročaj, ki ga DrawCapturedPage sprejme. CapturePageEx ne razveljavi /Rotate, zato zasukan vhod potrebuje ta korak najprej, izravnava zasuka strani brez lomljenja okvirjev strani pa pokaže, kaj se zgodi z vsakim okvirjem, ko to storite. Ena previdnost za vhode, ki lahko nosijo produktni okvirje na vozlih /Pages: pot uvoza razreši svoj okvir skozi svoje lastno iskanje prednikov, ločeno od dveh predorov, usklajenih v v3.539.44, zato najprej preverite HasPageBox(4) na izvorni strani in podajte opcijo 1 (CropBox), kadar vrne 0. To obdrži rezultat vezanega na specifikacijo, ne na to, kako je bila datoteka po naključju zapisana
Hiter pregled okvirjev strani
- Učinkoviti CropBox: lasten CropBox strani, sicer najbližji podedovani CropBox, sicer učinkoviti MediaBox (ISO 32000-1 §14.11.2)
- Učinkoviti BleedBox, TrimBox in ArtBox: lasten vnos listne strani, sicer učinkoviti CropBox
- Samo
Resources,MediaBox,CropBoxinRotatese podedujejo z vozlov/Pages(§7.7.3.4, tabela 30); produktni okvirji na vozlih/Pagesso ignorirani 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 brez okvirja, 1 lasten okvir strani (neposreden ali indirekten), 2 podedovani MediaBox ali CropBox (neposreden ali indirekten)CapturePageEx(Page, Options): 0 MediaBox, 1 CropBox z rezervo na MediaBox, 2 do 4 BleedBox, TrimBox ali ArtBox z rezervo na CropBox- Nadgradite na v3.539.44 ali novejši za dosledne privzete vrednosti in podedovanje čez poizvedbe okvirjev in zajem
Okvirji strani so mesto, kjer se tihe privzete vrednosti PDF srečajo s predtiskovnimi tolerancami, merjenimi v delih milimetra, in knjižnica bodisi uveljavi te privzete vrednosti povsod enako bodisi vam da dva odgovora na eno vprašanje. Celoten API okvirjev, zajema in Form XObject je dokumentiran na strani izdelka PDFlibPas PDF Library for Delphi