Amikor egy PDF oldalnak nincs TrimBoxja, a tényleges TrimBoxja az oldal CropBoxja, és ha a CropBox is hiányzik, akkor a MediaBox. A BleedBox és az ArtBox ugyanezt a szabályt követi. A PDFlibPas, a PDF Library for Delphi, ezt az alapértelmezési láncot v3.539.44 óta következetesen alkalmazza a GetPageBox-ban, a HasPageBox-ban és a CapturePageEx-ben, és ignorálja a /Pages csomópontra tett gyártódobozokat, mert az ISO 32000-1 nem engedi, hogy öröklődjenek
Ez lábjegyzetnek hangzik, amíg nem kényszerítesz nyomdai megrendelést. Képzelj el egy könyvbelst, aminek 6,25 × 9,25 inch a MediaBoxja, a CropBoxja a 6 × 9 inch vágóméretre van állítva, és nincs TrimBoxja, mert aki exportálta, eszébe sem jutott egyet írni. Kérsz a vágódobozt, a médiadobozt kapod helyette, és minden cella a nyomtatási íven egy nyolcad inch vérzést és szennyszélt húz a szomszédjára. A PDFlibPasnak pontosan ezen a területen voltak hibái, amiket a v3.539.42 és a v3.539.44 javított, és a javítás módja elárul valamit arról, hogyan kellene megvalósítani az oldaldoboz-szemantikát bármely PDF libraryben
Melyik doboz érvényes, ha az oldalnak nincs TrimBoxja?
A válasz az ISO 32000-1 §14.11.2-e szerinti rögzített alapértelmezési lánc: a CropBox a MediaBoxra esik vissza, a BleedBox, TrimBox és ArtBox pedig egyaránt a CropBoxra. Semmi, csak a CropBox esik vissza közvetlenül a MediaBoxra. Egy oldal, ami csak MediaBoxot definiál, ezért öt azonos dobozzal bír, egy pedig, ami MediaBoxot meg CropBoxot, négy, a CropBoxszal egyenlő dobozzal
| Doboz | PDFlibPas BoxType | Alapérték hiányában | Örökölhető /Pages-ből |
|---|---|---|---|
| MediaBox | 1 | Nincs, a bejegyzés kötelező | Igen |
| CropBox | 2 | MediaBox | Igen |
| BleedBox | 3 | CropBox | Nem |
| TrimBox | 4 | CropBox | Nem |
| ArtBox | 5 | CropBox | Nem |
A két lépcsős lánc azért számít, mert a CropBox maga is örökölt lehet. Egy olyan oldal tényleges TrimBoxja, aminek se TrimBoxja, se saját CropBoxja nincs, annak a legközelebbi ősnek a CropBoxja, akinek van, ennek hiányában az örökölt MediaBox. A spec még egy, könnyen elfelejtett szabályt ad: a crop, bleed, trim és art dobozok nem nyúljhatnak túl a media boxon, és ha mégis, gyakorlatilag az átfedésükre zsugorodnak. A PDFlibPas minden dobozt úgy jelent, ahogy a fájlban tárolják, így egy megbízhatatlan bemenetet kezelő validátornak magának kell a MediaBoxra szorítania
Mely oldalattribútumokat adhat át le egy /Pages csomópont?
Pontosan négyet: Resources, MediaBox, CropBox és Rotate. Az ISO 32000-1 §7.7.3.4-e definiálja az attribútumöröklést, és a 30. táblázat csak ezt a négy oldalobjektum-bejegyzést jelöli örökölhetőként. A BleedBox, TrimBox és ArtBox a levéloldalé. Egy /Pages csomópontba írt TrimBox nem örökölt érték; szabványidegen kulcs, amit egy szabványkövető olvasó ignorál
Ilyen nem szabványos fájlok léteznek, jellemzően egyetlen TrimBoxszal a gyökér oldalfa csomóponton, mint rövidítés arra, hogy „minden oldalnak ez a vágója". A rövidítés bármely eszközben jól néz ki, ami minden kulcsra rájár a /Parent-re, és ez a gond: a fájl most két dolgot jelent attól függően, ki olvassa. A szabványt követő olvasó nem lát TrimBoxot, és a CropBoxot használja, míg a mindent öröklő olvasó a szülői értéket látja. Egy prepress pipeline-ban ez a kétértelműség a nyomtatási íven landol
A PDF/X (ISO 15930) munkafolyamatok a TrimBoxra építenek a kész méret szempontjából, és a PDF/X profilok megkövetelik, hogy minden oldal TrimBoxot vagy ArtBoxot deklaráljon. Egy /Pages csomópontra parkolt doboz nem felel meg ennek a követelménynek, mert a kulcs sosem ér el az oldalobjektumig. A preflight inkább jelezze az ilyen fájlokat, minthogy csendben egyik vagy másik módon olvassa őket
Mit rontott el a PDFlibPas v3.539.44 előtt?
A PDFlibPasnak három külön hibája volt, mind azok közt a résben, ami azért van, amit a spec mond és amit két független kódút tett. Az elsőt a v3.539.42, a másik kettőt a v3.539.44 javította
A gyártódobozok capture közben a MediaBoxra estek vissza
v3.539.42 előtt a belső rutin, ami az oldalt capture-re készíti (felteszi az örökölt bejegyzéseket az oldalra és pótolja a hiányzó dobozokat), hiányban a BleedBoxnak, TrimBoxnak és ArtBoxnak a MediaBox értékeit adta. A CapturePageEx 2-től 4-ig terjedő opciókkal pontosan ezekből a bepótölt bejegyzésekből olvassa a határoló téglalapját, így egy csak CropBoxot definiáló oldalon a vágódoboz kérése a teljes media boxot kapta el. A GetPageBox már alkalmazta a CropBox-alapértéket, és a CapturePageEx referencia is mindig azt írta, hogy hiányzó kért doboznál a crop box szolgál; a capture kód mindkettővel szembement. v3.539.42 óta a három gyártódoboz az oldal CropBoxjára esik vissza, ami arra a pontra már ott van az oldalon (saját, ősől másolt vagy MediaBoxból pótolt), és csak maga a CropBox esik vissza a MediaBoxra
Két öröklési út, egy szemantikai szabály
A második hiba maga a nem szabványos öröklés volt, és az volt a trükkös benne, hogy a PDFlibPas két független úton oldotta fel a dobozokat. A dobozlekérdezések (GetPageBox és HasPageBox) egy helperen át járták a /Parent láncot, a capture pedig egy külön helyi helperen át. Mindkettő minden kulcsot örökölt, a gyártódobozokat is beleértve. Ha csak az egyiket javították volna meg, ellentmondás született volna egyetlen dokumentumon belül: a /Pages csomóponton ülő 180 pontos széles TrimBox és az oldalon ülő 380 pontos széles CropBox mellett a GetPageBox továbbra is 180 széles trimet jelentett volna, miközben a CapturePageEx 380 széles formot épített volna. A v3.539.44-ben mindkét út a négy örökölhető kulcsra szűkíti a /Parent bejárást, a gyártódobozok kizárólag a levélről olvasódnak, és a csavarodott szülői bejegyzés a fájlban marad, érintetlenül, se törölve, se átírva
A HasPageBox kihagyta a közvetlen szülői tömböket
A HasPageBox 0-t ad, ha az oldalnak nincs a kért típusú doboza, 1-et, ha saját doboza van (közvetlenül vagy indirekt hivatkozáson át tárolva), 2-t pedig, ha MediaBoxot vagy CropBoxot örökölt egy ősétől. A régi kód csak akkor adott 2-t, ha az örökölt érték indirekt hivatkozás volt, így egy örökölt közvetlen tömb 0-t adott. A javítás szétválasztja a dereferálást a tömbteszttől, és most már mindkét reprezentáció 2-t ad. v3.539.44 óta a HasPageBox egy BleedBoxra, TrimBoxra vagy ArtBoxra csak 0-t vagy 1-et adhat
A tanulság jóval túlmutat az oldaldobozokon. Amikor egy specifikációs szemantikának két implementációs belépője van egy libraryben, javítsd őket együtt, és mátrixként teszteld, ne egy boldog-út fájllal. A PDFlibPas regressziós készlete két szülői dobozreprezentációt (közvetlen és indirekt tömb) keresztez három levélállapottal (hiányzik, közvetlen tömb, indirekt tömb) és három capture opcióval (bleed, trim, art), ami 18 forgatókönyvet ad, és mindegyik ellenőrzi a lekérdezés eredményét, a kapcsolt határokat, a legális MediaBox- és CropBox-öröklést, meg az érintetlen szülői bejegyzést
Hogyan olvasom ki Delphiben a tényleges TrimBoxot?
Hívd meg a GetPageBox(4, Dimension)-ot a kiválasztott oldalon. A PDFlibPas helyetted alkalmazza az alapértelmezési láncot, így az eredmény a tényleges TrimBox, akkor is, ha az oldalnak nincs egyje. Párosítsd a HasPageBox-szal, amikor tudni kell, honnan jött az érték, amit egy preflight riport általában akar
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 = saját, 2 = örökölt
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.
Mind a GetPageBox, mind a SetPageBox a dokumentum aktuális koordinátabeállításaiban dolgozik. A példák itt az alapértékekkel futnak: 0-s hipuspont (bal-alsó, a PDF user space-szel egyező) és pont mértékegység, így a Top dimenzió a felső él, alulról mérve. SetOrigin(1) után a Top és Bottom dimenziók felülről lefelé mérődnek, SetMeasurementUnits(1) után pedig minden érték milliméterben jön. A szélesség és magasság nem függ a hipusponttól
A /Pages csomópontokon elcsatangolt gyártódobozok felkutatása
v3.539.44 óta a doboz API már nem lát TrimBoxot egy /Pages csomóponton, ami helyes, de egy preflight eszköz általában jelenteni akarja az ilyen fájlt, nem pedig csendben spec szerint olvasni. Az oldalfa csomópontok rendes objektumok, így az alacsonyszintű objektum API megtalálhatja őket: járjad végig az objektumszámokat GetMaxObjectNumber-ig, olvasd mindegyiket GetObjectToString-gel, és keress egy gyártódoboz kulcsot hordozó /Pages szótárt. Az ellenőrzés második fele az oldalankénti teszt, ami a PDF/X-et érdekli, és a HasPageBox most úgy válaszol rá, ahogy egy PDF/X validátor tenné, mert a szülői TrimBox már nem számít
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. Gyártódobozok oldalfa csomópontokon: nem szabványosak és ignoráltak
for ObjNum := 1 to Lib.GetMaxObjectNumber do
begin
Src := ''; // a szabad számok nem adnak szöveget
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: minden oldalnak kell saját TrimBox vagy 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. Opcionális javítás: 6 x 9 inch trim egy 6.25 x 9.25 inch media boxban
// (pontok, bal-alsó hipuspont: Left, Top, Width, Height)
if Missing > 0 then
Log.Add(Format('TrimBox written on %d pages',
[Lib.SetPageBoxRange('', 4, 9, 657, 432, 648)]));
end;
A szövegillesztés pragmatikus ellenőrzés, nem parser. Arra épít, hogy a PDFlibPas minden szótárbejegyzést kulcsként, egy szóközként és értékként sorosít, ami az GetObjectToString-gel visszaolvasott objektumokra igaz. A javítási lépés döntést érdemel, nem reflexet: a csavarodott szülői érték jól lehet az, amit a szerző szánt, de erősítsd meg a nyomdai munkalappal, mielőtt hivatalossá teszed. Az üres tartományú SetPageBoxRange minden oldalra ráírja a dobozt, és a frissített oldalak számát adja. Ha egy oldal meglévő doboza indirekt tömb, amit másik oldal vagy /Pages csomópont osztozhat, a SetPageBox annak az oldalnak új közvetlen tömböt ad a megosztott objektum átírása helyett. BleedBox, TrimBox vagy ArtBox állítása ráadásul a nyitott dokumentumot PDF 1.3-ra emeli, arra a verzióra, ami ezeket a bejegyzéseket bevezette
Oldalak kényszerítése a TrimBoxra CapturePageExszel
A CapturePageEx(Page, 3) az oldalt Form XObjectté alakítja, aminek a határolódobozát az oldal tényleges TrimBoxja adja, a DrawCapturedPage pedig bármekkora méretben másik oldalra teszi azt a formot. v3.539.42 óta a 3-as opció TrimBox nélküli oldalon a CropBoxot adja, ahogy a referencia írja, nem pedig a MediaBoxot az összes szennyszélével
A capture két tulajdonsága formálja a kódot. A capture destruktív: a kapcsolt oldal kikerül a dokumentumból, és a dokumentum sosem mehet nullára, ezért az első kimeneti ívet fűzd hozzá, mielőtt bármit kapcsolnál. A capture egyetlen dokumentumon belül működik csak, ezért húzd egyetlen dokumentumba az összes bemenetet előbb; a PDF források egy menetben való fésülése és közbeszúrása technikái közvetlenül alkalmazhatók
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;
// Az 1. oldal tényleges trim mérete (ez a layout egységes trimet feltételez)
Lib.SelectPage(1);
TrimW := Lib.GetPageBox(4, 2);
TrimH := Lib.GetPageBox(4, 3);
// Hozzáfűzöd és méretezed az első ívet; a NewPage kiválasztja az új oldalt
Lib.NewPage;
Lib.SetPageDimensions(2 * TrimW, TrimH);
// Minden capture elveszi az 1. oldalt, így a következő forrásoldal előrelép
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;
// Csak az ív maradt: ívenként két levágott oldal, egymás mellett
Lib.SelectPage(1);
for I := 0 to SourceCount - 1 do
begin
if (I > 0) and (I mod 2 = 0) then
Lib.NewPage; // ugyanakkora, mint az aktuális ív
// Alapértelmezett hipuspont: a Top a felső él, alulról mérve
Lib.DrawCapturedPage(Captures[I], (I mod 2) * TrimW, TrimH, TrimW, TrimH);
end;
Lib.SaveToFile(OutFile);
finally
Lib.Free;
end;
end;
A trim alapú capture mindent levág, ami a TrimBoxon kívül esik, ami pont az, amit egy digitális korrektúránál vagy cut-and-stack layoutnál akarsz. Nyomdai ívnél, amit nyomtatás után vágnak, kapcsolj 2-es opcióval, hogy a vérzés megmaradjon, és hézagold a cellákat a vérzés szélességével. Mivel a capture elveszi a forrásoldalakat, a rájuk mutató bookmarkok és linkek célt veszítenek, ezért külön kimeneti fájlba kényszeríts, ne pedig olyan dokumentumot szerkessz, aminek a navigációjára még szükséged van; a oldalak cseréje bookmarkok törése nélkül az oldalműtét ezen oldalát fedi le
Amikor a forrásnak érintetlennek kell maradnia, az ImportPageAsFormXObject(SourceDocumentID, SourcePage, Options) ugyanazokat a 0-tól 4-ig terjedő opciókat veszi (a jelenlegi dokumentumhoz add Lib.SelectedDocument-ot), érintetlenül hagyja a forrás oldalfáját, az örökölt oldalforgatást a form mátrixába normalizálja, és olyan handlet ad vissza, amit a DrawCapturedPage elfogad. A CapturePageEx nem vonja vissza a /Rotate-ot, így a forgatott bemenetnek előbb arra a lépésre van szüksége, és a oldalforgatás kilapítása oldaldobozok törése nélkül megmutatja, mi történik mindegyik dobozzal, ha megteszed. Egy figyelmeztetés azokhoz a bemenetekhez, amik /Pages csomópontokon hordozhatnak gyártódobozokat: az import út a saját őskeresésén át oldja fel a dobozát, a v3.539.44-ben összeigazított két úttól elkülönülten, ezért nézd meg előbb a forrásoldalon a HasPageBox(4)-et, és adj 1-es opciót (CropBox), ha az 0-t ad. Így az eredmény a spechez kötődik, nem ahhoz, ahogy a fájlt véletlenül megírták
Oldaldoboz gyorsreferencia
- Tényleges CropBox: az oldal saját CropBoxja, ennek hiányában a legközelebbi örökölt CropBox, végül a tényleges MediaBox (ISO 32000-1 §14.11.2)
- Tényleges BleedBox, TrimBox és ArtBox: a levéloldal saját bejegyzése, ennek hiányában a tényleges CropBox
- Csak a
Resources,MediaBox,CropBoxésRotateöröklődik/Pagescsomópontokról (§7.7.3.4, 30. táblázat); a/Pagescsomópontokon lévő gyártódobozok ignorálódnak 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 nincs doboz, 1 az oldal saját doboza (közvetlen vagy indirekt), 2 örökölt MediaBox vagy CropBox (közvetlen vagy indirekt)CapturePageEx(Page, Options): 0 MediaBox, 1 CropBox MediaBox-esessel, 2-től 4-ig BleedBox, TrimBox vagy ArtBox CropBox-esessel- Frissíts v3.539.44-re vagy újabbra következetes alapértékekért és öröklésért dobozlekérdezés és capture között
Az oldaldobozok az a hely, ahol a PDF csendes alapértékei millimétertörtekben mért prepress tűrésekkel találkoznak, és egy library vagy mindenhol ugyanúgy alkalmazza azokat az alapértékeket, vagy két választ ad egy kérdésre. A teljes doboz-, capture- és Form XObject API a PDFlibPas PDF Library for Delphi termékoldalon van dokumentálva