PDF stranica ne pohranjuje piksele i ne pohranjuje stablo objekata oblika na način na koji to radi SVG. Ona pohranjuje program. Svaka linija, krivulja, popuna i postavljena slika na stranici rezultat je izvođenja niza operatora u toku sadržaja (content stream), od vrha prema dnu, u odnosu na stanje pokrenute grafike. Shvatite ovu jednu činjenicu i većina ponašanja formata prestaje biti iznenađujuća: zašto popuna treba zaseban operator iscrtavanja (painting operator) nakon izgradnje putanje, zašto boje i širine linija cure (leak) iz jednog oblika u drugi osim ako ih ne ogradite (bracket), zašto isti kod crtanja može sletjeti na potpuno različita mjesta nakon jedne transformacije koordinata. Ovo je obilazak (tour) tog modela izvođenja definiranog u ISO 32000: operatori koje sretnete kada otvorite tok sadržaja i pravila koja odlučuju što se prikazuje na stranici
Tok sadržaja (content stream) je postfiks bajtkod (postfix bytecode)
Tok (stream) sadržaja ravna je sekvenca bajtova (byte sequence) operanada za kojima slijede operatori. Operandi dolaze prvi, operator koji ih konzumira zadnji, što je obrnuto od poziva funkcije (function call) i identično radu mašine bazirane na stogu (stack machine): gurnite (push) brojeve, a zatim izdajte glagol. Nema ugnježđivanja (nesting), nema sintakse izraza, nema varijabli. Obris (outline) trokuta iznosi pet redaka ovoga:
100 100 m % moveto: započnite novi subpath na (100, 100)
200 200 l % lineto: dodajte segment na (200, 200)
300 100 l % lineto: dodajte segment na (300, 100)
h % closepath: poveži nazad na početak
S % stroke: oboji obris putanje
Operatori su namjerno sažeti (terse). Stvarna stranica ih ima tisuće, obično komprimiranih s FlateDecode. Cijena te kompaktnosti je da stream ne nosi nikakvu strukturu koju možete pretraživati: čitač ne može pitati "gdje je naslov (heading) na ovoj stranici", on može samo pokrenuti program i vidjeti koja tinta (ink) gdje pada. To je temeljni razlog zašto je ekstrakcija teksta iz proizvoljnih PDF-ova teška
Ishodište je dolje lijevo, a Y raste prema gore
Prije nego što bilo koja koordinata ima smisla, morate znati gdje je (0, 0). PDF postavlja ishodište (origin) u donji lijevi kut stranice, pri čemu se X povećava udesno, a Y povećava prema gore, mjereno u točkama na 72 točke po inču (ISO 32000-2 §8.3.2). Na stranici veličine US Letter (US pismo) gornji rub (top edge) nalazi se na y = 792, a ne na y = 0. Svatko tko dolazi od grafike za ekrane (screen graphics), gdje je ishodište gore lijevo a Y raste prema dolje, ovo pogrešno napravi iz prve (gets this backwards on the first try) i nacrta prvu liniju (first line) izvan dna stranice. Jedinica je također neovisna o mediju: 72 jedinice je jedan inč, bilo da se stranica iscrtava na zaslonu telefona ili uređaju za osvjetljavanje slika (imagesetter)
Većina biblioteka za crtanje (page-drawing libraries) stranica izravno nasljeđuje tu konvenciju. U HotPDF-u, na primjer, TextOut i pozivi staza mjere od donjeg lijevog u točkama (points), tako da vrijednost u blizini visine stranice stavlja sadržaj na vrh:
// HotPDF, Delphi: y se mjeri od donjeg ruba prema gore, u točkama
Pdf.CurrentPage.SetLineWidth(2.0);
Pdf.CurrentPage.MoveTo(100, 700); // blizu vrha stranice
Pdf.CurrentPage.LineTo(300, 700);
Pdf.CurrentPage.Stroke; // ispušta (emits) moveto/lineto/stroke operatore
Ta sekvenca poziva kompilira se na točno gore navedene m, l i S operatore. Biblioteka je tu tipkač (typist) toka sadržaja, ništa više, a znati što ispušta (emits) ono je što vam omogućuje rasuđivanje o izlazu (reason about the output) kad neki oblik padne tamo gdje ga niste očekivali
Izradite putanju, a zatim je obojite
PDF odvaja izgradnju staze (path construction) od bojanja staze (path painting), a odvajanje nije cjepidlačenje (pedantry). Prvo opisujete oblik pomoću konstrukcijskih operatora (construction operators) koji ne dodaju ništa vidljivo, zatim izdajete jedan operator za crtanje (painting operator) koji odlučuje što učiniti s akumuliranom stazom (accumulated path). Isti trokut može biti obris, ispunjena popuna ili oboje, ovisno isključivo o glagolu (verb) kojim završavate
Operatori izvedbe su malobrojni. m započinje novu potputanju (subpath) na točki. l dodaje ravni segment. c dodaje kubičnu (cubic) Bezierovu krivulju od šest operanada, dvije kontrolne točke (control points) i krajnje točke. re je kratica koja dodaje cijeli pravokutnik (rectangle) iz x, y, visine (height), širine (width) četvorke (quadruple). h zatvara trenutnu podputanju natrag na njezin početak. Niti jedan od njih ne stavlja tintu na stranicu; oni samo akumuliraju geometriju
200 250 m % započni potputanju
300 350 400 450 500 250 c % kubični Bezier: dvije kontrolne točke, zatim krajnja točka
150 200 re % pravokutnik 150 x 200, dodan kao vlastita potputanja
h % zatvori (close)
Originalni primjer koristio je sada zastarjelu varijantu y operatora krivulje; c sa svoje tri eksplicitne točke je forma koju ćete vidjeti u praksi i za kojom ćete posegnuti. Nakon što staza postoji, jedan ju operator iscrtavanja (painting operator) dovršava. Vokabular je malen i vrijedan pamćenja, budući da svaki oblik na svakoj stranici završava s jednim od ovoga:
Siscrtava (strokes) obris staze koristeći trenutnu širinu crte i boju potezafispunjava (fills) unutrašnjost trenutnom bojom ispune (fill color) i nenultim (nonzero) pravilom namotavanja (winding rule)f*ispunjava korištenjem parno-neparnog pravila (even-odd rule), što je važno za samo-presijecajuće (self-intersecting) oblike i oblike s rupama (holes)Bpopunjava i nakon toga iscrtava potez u jednoj operaciji;bprvo zatvara stazu (path)nne slika (paints) ništa, na koji način staza (path) postaje izrezana regija (clip region) bez ostavljanja vidljivog traga (visible mark)
Pravilo namatanja (winding rule) dio je u kojem ljudi griješe (get wrong). Nenulti (f, B) prebrojava prijelaze zrake (ray) s predznakom od ispitne (test) točke i popunjava gdje god izbrojani zbroj (count) nije nula, stoga se rupa zadržava praznom samo ako se njezina potputanja namotava suprotno vanjskoj (outer one). Parno-neparno (f*, B*) mijenja vrijednost na svakom raskrižju (crossing) bez obzira na smjer. Ako oblik "krafne" ("donut") izađe čvrst (solid), unutarnji krug je namotan na isti način kao vanjski krug, a vi ga morate preokrenuti (reverse) ili promijeniti (switch) na parno-neparno (even-odd)
Boja je način rada (mode), a ne parametar
Boja u toku sadržaja je ljepljiva (sticky). Postavite boju i ona ostaje postavljena dok ne postavite drugu ili ne vratite (restore) ranije stanje, zbog čega nenagrađena (unbracketed) promjena boje tiho oboji sve što je nacrtano nakon nje. PDF također čuva (keeps) boju ispune i boju poteza (stroke color) kao dvije neovisne postavke, s malim slovima operatora (lowercase operators) za punjenje (fill) i s velikim (uppercase) za poteze (stroke). Prostori boja uređaja imaju svatko svoju skraćenicu (shorthand):
0.5 g % DeviceGray fill, srednje siva (0 = crna, 1 = bijela)
0.2 0.6 0.8 rg % DeviceRGB fill
0.8 0.2 0.1 RG % DeviceRGB stroke (velika slova = stroke)
0.2 0.8 0.0 0.1 k % DeviceCMYK fill
DeviceRGB odgovara zaslonskom izlazu, DeviceCMYK je ono što očekuje produkcija ispisa, a DeviceGray najmanji je izbor za jednobojni (monochrome) sadržaj. Prostori uređaja (device spaces) zgodni su (convenient), ali nekalibrirani: ista RGB trojka (triple) može se iscrtati (render) različito na dva monitora, što je problem čije su rješenje postali kolorni (color) prostori zasnovani na ICC-u te izlazne namjere (output intents) PDF/A. Za rad osjetljiv na boje odabirete kalibrirani prostor sa cs i CS i postavljate komponente sa sc i scn, no u slučaju običnih dokumenata krilatice (shorthands) uređaja podnose taj teret. Knjižnica ih obavija tipiziranim pozivima (typed calls). HotPDF, recimo (for instance), dobiva jedan TColor te proizvodi odgovarajuće (matching) operatore:
Pdf.CurrentPage.SetRGBFillColor(clRed);
Pdf.CurrentPage.Rectangle(100, 100, 200, 150); // x, y, širina, visina
Pdf.CurrentPage.Fill;
Pdf.CurrentPage.SetRGBFillColor(RGB(0, 255, 0));
Pdf.CurrentPage.Circle(150, 400, 50); // x, y, polumjer
Pdf.CurrentPage.Fill;
Stanje grafike i stog (stack) q/Q
Sve što nije sama putanja živi unutar grafičkog stanja (graphics state): trenutna (current) matrica transformacije (transformation matrix), boje ispune (fill colors) i poteza (stroke colors), širina (width) crte (line), isprekidani (dash) uzorak (pattern), područje izrezivanja (clip region), alfa (alpha). Stanje je globalno i promjenjivo (mutable), tako da je jedini siguran način da napravite lokalnu promjenu da sačuvate (save) cijelu stvar, modificirate ju, crtate (draw) i okrenete (roll) natrag. To čine q i Q. q gura (pushes) kopiju trenutnog stanja na stog (stack); Q ju iskače (pops), odbacujući svaku izmjenu stvorenu (made) otkad je upareno (matching) q
q % sačuvaj cijelo grafičko stanje
2 0 0 2 100 100 cm % spajanje (concatenate) transformacije: omjer (scale) 2x, prijevod (translate) u (100,100)
0.8 g % sivo punjenje, dovedeno (scoped) u taj blok
% ... crtajte stupnjevani (scaled), sivi sadržaj ...
Q % vraćanje: prevodite i mijenjate boje unatrag
Neuravnoteženi q i Q uobičajen su način da pogriješi (goes wrong) ručno izgrađen ili spojeni (stitched) tok sadržaja. Zalutali (stray) q bez uparenog Q napušta stog dubok (stack deep) po završetku stranice; još (extra) jedan Q stvara stanje praznog reda čekanja (underflows). U bilo kojoj soluciji preglednik (viewer) bi uspio sačuvati na snazi neku staru (old clip) transformaciju (transform), te bi tako i nestalo sadržaja a ono sletjelo na krivi prostor. Ostanu li grafike nevidljive (vanish) unatoč razlogu kojega sam proces uspijeva pojasniti, za vas je bitno prvotno prokontrolirati (audit) takav niz, taj stog o kom govorimo (state stack)
CTM transformira svaku koordinatu
Trenutna (current) matrica transformacije (transformation matrix) stoji između brojeva u vašim operatorima (operators) i konkretne stranice. Predispozicijom i same iscrtanosti svaka (coordinate) je prethodno bila umnožena s CTM (CTM), tako da preinačavanje njene pozicije zapravo pretumbava (changes) onaj dio i smisao iz koga svake buduće nastale crtovnosti započinju djelovati a unatoč samome netakanju jednoga čvorišta. Pomoćna stavka cm nastavlja (concatenates) postavljati težu stavku (matrix) u pogledu onoga na onoj prvoj pri čemu prima na ruke tih (operands) šestorica onih čije mapiranje odgovara sa (affine matrix) [a b c d e f]:
1 0 0 1 100 50 cm % pomak (translate) za (100, 50): e i f prenose offset
2 0 0 1.5 0 0 cm % povećaj (scale) x za 2, y za 1.5: a i d su faktori razmjera
0.707 0.707 -0.707 0.707 0 0 cm % okreni za 45 stupnjeva (cos/sin unutar a, b, c, d)
Dvije stvari ljudima podmeću nogu (trip people up). Kao prvo, cm slaže (composes) radije nego da nadomješta (replaces), pa se na taj način skupljaju (accumulate) te funkcije preinačavanja (transforms) pri čemu im biva nužan poredak: razvrstavanje prije preseljavanja ne uprizoruje identično prebacivanju u usporedbi sa raspoređivanjem. Isto tako (Second), zakrivljenja (rotation) pa raspoređivanje (scaling) okreću svoju okosnicu s osvrtom oko postojećega (current) korijenja tj. ne kod centra vašega predloška tj. ukoliko biste željeli (rotate) preokrenuti dotičnu postavu, tu valja preseliti unutar temeljnih okvira (origin), preinačiti, nakon čega i prenijeti (translate) povratno gdje sve prethodi bivajući omotano kod (wrapped in) q/Q. Sličnim smislom rukuje i sama navedena matrica koja tako ulaže vizualizacije a sve navedeno vrijedno spomena (worth seeing)
Slike i sadržaj (content) koji se može ponovno upotrijebiti su XObjects
Rasterske slike (raster images) ne žive u nizu s ispisom na ekran (content stream). Spremljeni su kao slikovni XObjects, vanjski objekti s vlastitim rječnikom koji opisuje širinu, visinu, bitnu dubinu, prostor boja i kompresijski filter, dok ih tok za izražavanje sadržaja tek povlači po osnovama srodnosti. Pomoću te strukture opisuje se u ovakvoj potpori i sam format JPEGa:
/Photo <<
/Type /XObject
/Subtype /Image
/Width 640
/Height 480
/BitsPerComponent 8
/ColorSpace /DeviceRGB
/Filter /DCTDecode % podaci slike su u JPEG obliku
>>
Slikovni XObject crta (draws) u jedinični kvadrat (unit square): uvijek zauzima područje od (0, 0) do (1, 1) u korisničkom prostoru. Ne proslijeđujete (pass) mu poziciju ili veličinu (size). Umjesto toga (Instead) postavljate CTM na takav način unutar jediničnog kvadrata i time ciljano usmjeravate u pravokutnik a po čemu isti oživljavate uz pomoć Do. Na osnovama rečenoga i postavljanje određene slikovne materije mora prolaziti preseljeno djelovanjem (transform) uslijed poticanja s popratnim pozivanjem čime omotavanje prolazi fazom održanja kako mjerodavnosti po opsegu ne bi dopustile krvarenje tj. proširenje po pitanju slijednoga poteza (operation):
q
640 0 0 480 50 300 cm % mapirajte jedinični kvadrat na bazu okvira 640x480 s pozicijom u (50, 300)
/Photo Do % oslikajte (paint) slikovni (image) XObject
Q
Sličan (The same) Do obrazac upravlja i XObjects formama na osnovama čega posjeduju višekratni komadić grafike, oznake ili (logo) po izrazu preslikanih markera, po izričaju samoga toka ali po vlastitom opsegu (bounding box). Navodeći jedinstveno, izricanje biva obvezno višekratno posluženo izrazom CTM formata jer se time ti nizovi slova zapisuju samo jednokratno (only once). Knjižnični ogranci zamataju sve dotične postupke poticanjem pozivanja pojedinačnoga namještanja po tom formatu pri čemu i sam potkrijepljeni oblik po poticanju AddImage registrira to preslikavanjem u sam proces preko postavljanja formata (ShowImage) u ciljanim granama ali ovdje to preuzima konkretniji karakter x, y, (širini - width i visini - height) gdje namjere izgrađivanja samih redova ručno popuštaju u pozadini:
var
Bmp: TBitmap;
ImgIndex: Integer;
begin
Bmp := TBitmap.Create;
try
Bmp.LoadFromFile('logo.bmp');
ImgIndex := Pdf.AddImage(Bmp, icFlate);
// x, y (dolje-lijevo), širina, visina, kut rotacije
Pdf.CurrentPage.ShowImage(ImgIndex, 50, 300, 200, 150, 0);
finally
Bmp.Free;
end;
end;
Po okriljem ove same dionice ovim redovnim poslovanjem pohranjuje se rječnička obrada XObject u postavljanju uz (sets) CTM strukture pa na tome redu razmjera uspostavlja namjenu jediničnog kvadrata emitiranjem funkcije Do. Taj prijelazni oblik sam po sebi onaj je čiji postupak zaslužuje potpunu (worth knowing) pažnju po svome radu uslijed same rasvjetljivosti unutar davanja osebujnih rezimea tj.: gdje se prikazi fotografije dotiču širenjima na osnovi ne-srazmjernosti s predzadanosti; na tragu spomenutog - jedna marka logotipa postavljena po istim osobinama unutar tih stranica formirana unutar četrdesetak preslikanosti odnosi se na jedan XObject unutar 40 višekratnih doziva, dok god iscrtavanje slike okrenute postrance znači preokretanje znakovlja pri rješavanju u matricama, bez asocijacija k upropaštenim dosjeima (corrupt file)
Kamo ovo vodi
Grafički model (graphics model) je mali kada mu jednom vidite oblik (shape). Tok sadržaja (content stream) u suštini sačinjava struktura procesno pokretanoga djelovanja koje je po svojoj koncepciji postfiks-a (postfix bytecode) upotrjebljiva u pokretima ne-čvrstoga odnosno rješivoga oblika i (mutable state); pri uspostavljanju takvih okosnica startaju po koordinatama baze donje razine te ljevice uz proces preseljavanja CTM okosnicom (pass through); kod spomenutoga formiranjem se šutećim koračanjem a poprima kolorite planskim prožimanjem po snazi samo jedne (painted with one) linije; zadržavanje po pitanju linijskih te boja izdvajanja poprima po naravi (persist) postojanost na temeljima osnova sve dok se unutra struktura kod q/Q (bracket) ne okruži granicama; te se konačno ponovnim višekratnim vraćanjima unutar procesa takvi XObjects objekti upošljavaju s mijenjanjima na okosnicama (transforming) jediničnoga opsega. Praktički u osnovi svako smetajuće postavljanje predočavanjima (rendering) izlazi s obzirom na svako od pet objašnjenih obrazaca. Kroz to kada vam posrijedi za cilj izlazi pronalazak grafički uposlenih operanada za djelovanje prožete okosnice unutar (sit inside the larger object model) višeslojno većih elemenata za uokviravanje, uz oslonac onoga po (page dictionaries) na temeljenosti s tabličnim posloživanjima koje predočuju prema samima, kod (tehnički pregled strukture PDF datoteke) je potkrijepljenjem presvučen cjelokupni okoliš za poslovanje s osnovama tih po unakrsnoj formi poveznica a uspostava po redu kretanja na gradnji od osnova sa strukturom građenom do samog (izgradnja jednostavnog PDF-a ispočetka) hoda unutar raspona do krajeva po liniji (bytes end to end). Stranice pri ispisu teksta nalaze vlastitu operabilnu kategoriju zajedno s pripadajućim posrtanjima u popratnom poglavlju o Rukovanje tekstom i fontom u PDF-u
Delphi pozivi crtanja (drawing calls) prikazani ovdje, MoveTo, LineTo, Stroke, Rectangle, Fill, SetRGBFillColor, AddImage, i ShowImage, dio su HotPDF Component za Delphi i C++Builder, koji ispušta (emits) ove content-stream operatore za vas