Hlavní navigace

PostScript: vyplňované cesty, transformace a natočený text

31. 5. 2007
Doba čtení: 11 minut

Sdílet

Tentokrát bude popsán význam souřadnicových systémů v PostScriptu, včetně transformační matice, ukážeme si, jak je možné pomocí změny matice tisknout písmo či jiné objekty, a to v různém směru či zvětšení. Také si předvedeme způsob vyplňování cest, včetně změny způsobu výplně.

Obsah

1. Souřadnicové systémy v PostScriptu – prostor uživatele a prostor zařízení
2. Transformační matice (CTM) a její změna
3. Hrátky s transformační maticí a vykreslovaným písmem
4. První demonstrační příklad – transformovaný text
5. Vyplňované cesty
6. Druhý demonstrační příklad – kresba vyplněných cest
7. Změna způsobu vyplňování uzavřených cest
8. Třetí demonstrační příklad – změna způsobu vyplňování uzavřených cest
9. Obsah dalšího pokračování tohoto seriálu

1. Souřadnicové systémy v PostScriptu – prostor uživatele a prostor zařízení

V předchozí části tohoto seriálu jsme si na několika demonstračních příkladech ukázali způsob vykreslování textu. Pomocí příkazu scalefont bylo možné nastavit velikost textu (ta je specifikována v typografických bodech, tj. v jednotce o velikosti 1/72 palce) a příkazem moveto začátek vykreslování textu. Chyběla nám však možnost změnit natočení textu a případně provádět jeho zkosení a zrcadlení (zkosením textu sice dostaneme typografický zmetek, ale zrcadlení se v některých případech může hodit, například při tisku na fólie, natočení textu se často používá například v popiskách grafů). PostScriptu pro natočení, zkosení a zrcadlení textu neposkytuje žádné specializované příkazy. Ty by totiž byly nadbytečné, protože je možné použít obecnější nástroj – lineární transformace.

Při popisu obecných vlastností PostScriptu jsem se již zmínil o tom, že při zadávání všech souřadnic (například koncových bodů cest) se používá souřadný systém, který je nezávislý na daném tiskovém zařízení, tj. při specifikaci souřadnic jsou použity délkové jednotky a nikoli tiskové body, pixely či podobné (na zařízení závislé a tudíž obecně bezrozměrné) hodnoty. Také již víme, že základní délkovou jednotkou je jeden typografický bod (point), jehož délka je rovna 1/72 palce, což odpovídá 0,3527 milimetru (odtud je ostatně odvozeno rozlišení původních monitorů pro počítače Apple). Interpreter PostScriptu (RIP) těsně před rastrováním provádí převod zadaných souřadnic do interního souřadného systému daného zařízení, tj. obrazovky, tiskárny či osvitové jednotky.

Při inicializaci nové tiskové strany leží počátek souřadného systému v levém dolním rohu strany. Hodnoty na x-ové ose rostou směrem doprava a hodnoty na y-ové ose směrem nahoru. Pokud někomu takto nastavený souřadný systém nevyhovuje (třeba já jsem zvyklý na opačný smysl osy y), je možné použít příkazy pro zrcadlení, otáčení či změnu měřítka, kterými se mění obsah transformační matice. Takto definovaný souřadnicový systém se nazývá uživatelský prostor (user space). Tiskárna či osvitová jednotka používá svůj vlastní souřadnicový systém nazývaný prostor zařízení. Transformace souřadnic mezi uživatelským prostorem a prostorem zařízení je prováděna právě na základě nastavené transformační matice (CTM – Current Transformation Matrix). Jak je možné s touto transformační maticí manipulovat, se dozvíme v následující kapitole.

2. Transformační matice (CTM) a její změna

Transformační matice (CTM – Current Transformation Matrix) slouží, jak již ostatně její název napovídá, k transformaci zadávaných souřadnic do obecně jiného souřadného systému. Pro zadané souřadnice [x, y] je transformace na nové souřadnice [x', y'] prováděna pomocí následujících vztahů:

x'=ax+cy+tx
y'=bx+dy+ty

Koeficienty a, b, c, d, tx a ty je možné zapsat v maticovém tvaru (jedná se o matici velikosti 3×3) a celá transformace je potom zapsána takto:

[x', y', 1]=[x, y, 1]T

kde T je transponovaná transformační matice. Vzhledem k tomu, že se v transformační matici vyskytuje pouze šest neznámých koeficientů (poslední sloupec je roven vektoru [0, 0, 1]T), je v PostScriptu z celé matice uschováno pouze těchto šest koeficientů ve formě šestisložkového vektoru:

[a b c d tx ty]

Na lineárních transformacích je zajímavá a užitečná jedna vlastnost: skládání transformací je možné provádět násobením matic odpovídajících jednotlivým transformacím. To znamená, že pro několik složených transformací (např. posun, otočení, zpětný posun a změna měřítka) je zapotřebí pouze jedenkrát vypočítat transformační matici a tu potom libovolněkrát použít. Tímto způsobem se šetří jak strojový čas (v případě osvitu jde o kritickou veličinu), tak i složitost celého RIPu, který si nemusí pamatovat jednotlivé transformace, ale pouze jejich „kumulativní“ tvar. Základními příkazy, pomocí kterých je možné v PostScriptu měnit transformační matici, jsou příkazy translate, rotate a scale. Pomocným příkazem, který explicitně provádí transformace, je příkaz transform.

3. Hrátky s transformační maticí a vykreslovaným písmem

Všechny tři příkazy pro změnu transformační matice si nyní podrobněji popíšeme. Příkaz scale při svém zavolání očekává, že jsou na zásobníku parametrů uloženy dvě reálné hodnoty sx a sy. Tyto hodnoty reprezentují zvětšení ve směru horizontální a vertikální osy. Příkaz translate na zásobníku parametrů také očekává dvě reálné hodnoty tx a ty. Jedná se o posuny ve směru horizontální a vertikální souřadné osy. Konečně příkaz rotate na zásobníku parametrů očekává pouze jedinou hodnotu α, což je úhel otočení. Vliv příkazů translate, rotate a scale se kumuluje, proto je možné vytvářet i složené transformace (rotace okolo obecného bodu, zkosení atd.).

Poslední uvedený příkaz, tj. transform,o­čekává při svém zavolání, že jsou na zásobníku parametrů uloženy dvě reálné hodnoty reprezentující souřadnice [x, y]. Výsledkem spuštění příkazu jsou nové dvě hodnoty [x', y'] – zde se ukazuje jedna z předností zásobníkově orientovaného jazyka: je možné vytvořit příkaz, který vrací více hodnot bez nutnosti použití vyšších datových struktur.

Ukažme si nyní příklad výpisu textu bez úpravy transformační matice. Výpis textu začíná na souřadnicích [0, 0], což je levý dolní roh použitého papíru. Zdánlivě zbytečný příkaz moveto je nutné použít, aby byl inicializován takzvaný CP – Current Point, což je ukazatel na právě aktivní bod použitý buď při vytváření cest nebo právě při vykreslování písma:

% nalezeni fontu ve slovniku
/Helvetica findfont

% nastaveni velikosti (v typografickych bodech)
220 scalefont

% nastaveni fontu jako aktivniho
setfont

% presun CP je nutne uvest, i kdyz se jedna
% o pocatek souradnic
0 0 moveto

% vypis textu "42"
(42) show

showpage 

Místo přesunu CP na nové souřadnice (například na střed papíru, protože pro A4 jsou meze rovny 595×842 typografickým bodům) můžeme CP ponechat na stávající hodnotě a místo něj změnit transformační matici. V tomto případě se přímo nabízí použití příkazu translate:

% nalezeni fontu ve slovniku
/Helvetica findfont

% nastaveni velikosti (v typografickych bodech)
220 scalefont

% nastaveni fontu jako aktivniho
setfont

% zmena transformacni matice - posun pocatku
% souradneho systemu zhruba do stredu listu
200 350 translate

% presun CP je nutne uvest, i kdyz se jedna
% o pocatek souradnic
0 0 moveto

% vypis textu "42"
(42) show

showpage 

Mnohem zajímavější a v tomto případě i užitečnější je použití příkazu rotate. Ukažme si, jak daný text vykreslit ze středu papíru pod úhlem 45°:

% nalezeni fontu ve slovniku
/Helvetica findfont

% nastaveni velikosti (v typografickych bodech)
220 scalefont

% nastaveni fontu jako aktivniho
setfont

% zmena transformacni matice - posun pocatku
% souradneho systemu zhruba do stredu listu
% a rotace o 45 stupnu
200 350 translate
45 rotate

% presun CP je nutne uvest, i kdyz se jedna
% o pocatek souradnic
0 0 moveto

% vypis textu "42"
(42) show 

4. První demonstrační příklad – transformovaný text

V dnešním prvním demonstračním příkladu je ukázána tvorba transformovaného (konkrétně natočeného) textu. Nejprve je nastaven zabudovaný bezpatkový font o velikosti jednoho palce (72 typografických bodů). Posléze je souřadný systém přesunut doprostřed papíru formátu A4. Protože se mi nechtělo z hlavy počítat polovinu horizontálního a vertikálního rozlišení, použil jsem operátor div, který jednoduše vydělí dvě hodnoty uložené na zásobníku parametrů a posléze do zásobníku vrátí výsledek této operace. Následně je 11× vytištěn text, pokaždé s jinou barvou a také jiným natočením (transformace natáčení se kumulují, tj. úhel natočení se vlastně sčítá). V reálu by se takto postscriptový soubor pravděpodobně nevytvářel, protože je možné použít programovou smyčku pro opakování části kódu, tuto možnost si však ukážeme až v následující části tohoto seriálu. Zdrojový text prvního demonstračního příkladu má tvar:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Prvni demonstracni priklad
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% nalezeni fontu ve slovniku
/Helvetica findfont

% nastaveni velikosti (v typografickych bodech)
72 scalefont

% nastaveni fontu jako aktivniho
setfont

% zmena transformacni matice - posun pocatku
% souradneho systemu do stredu listu
595 2 div 842 2 div translate

% vypis textu
0 0 moveto
1.0 0.0 0.0 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.9 0.0 0.1 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.8 0.0 0.2 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.7 0.0 0.3 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.6 0.0 0.4 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.5 0.0 0.5 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.4 0.0 0.6 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.3 0.0 0.7 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.2 0.0 0.8 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.1 0.0 0.9 setrgbcolor
(  PostScript) show

% natoceni a vypis dalsiho textu
32 rotate
0 0 moveto
0.0 0.0 1.0 setrgbcolor
(  PostScript) show

showpage 

42_1
Obrázek 1: První demonstrační příklad po zobrazení interpreterem PostScriptu

5. Vyplňované cesty

V předchozích částech tohoto seriálu jsme si ukázali, jakým způsobem je možné vykreslovat obrysy otevřených i uzavřených cest (path). Také jsme si řekli, jak je možné ovlivnit způsob vykreslení tohoto obrysu, tj. jeho barvu, tloušťku čáry a způsob napojení a zakončení čar. PostScript však umožňuje také vyplnění nadefinované cesty; to je ostatně i základní operace, která se provádí při vykreslování písma (každý vykreslovaný znak je vlastně představován samostatnou sekvencí příkazů kreslicích buď obrys či kostru jednotlivých částí znaku). Možnosti vyplňování cest se postupně rozšiřovaly – PostScript Level 1 podporoval pouze tvorbu tiskové stránky ve stupních šedi, PostScript Level 2 přidal barvy a ve třetí verzi je podporováno i Gouraudovo stínování, což je způsob interpolace barev, používaný mimo jiné i při práci s cestami.

6. Druhý demonstrační příklad – kresba vyplněných cest

Dnešní druhý demonstrační příklad je velmi jednoduchý. Ve zdrojovém kódu příkladu jsou příkazy pro tvorbu několika cest, které jsou posléze vykresleny příkazem fill a nikoli příkazem stroke (ten jsme si vysvětlili minule). Pokud je vám tvar cest povědomý – jedná se o cesty použité v demonstračních příkladech uvedených v předchozí části tohoto seriálu.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Druhy demonstracni priklad
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% sloupec 1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% prvni krivka
5.0 setlinewidth % zmena sirky cesty
0.0 setgray      % zmena urovne sede
newpath
 50 700
moveto
 50 800
150 800
150 700
curveto
fill

% druha krivka
0.1 setgray      % zmena urovne sede
newpath
 50 550
moveto
100 650
100 650
150 550
curveto
fill

% treti krivka
0.2 setgray      % zmena urovne sede
newpath
 50 400
moveto
125 500
 75 500
150 400
curveto
fill

% ctvrta krivka
0.3 setgray      % zmena urovne sede
newpath
 50 250
moveto
150 350
 50 350
150 250
curveto
fill

% pata krivka
0.4 setgray      % zmena urovne sede
newpath
 75 100
moveto
150 200
 50 200
125 100
curveto
fill

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% sloupec 2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% prvni krivka
0.5 setgray      % zmena urovne sede
newpath
250 700
moveto
250 800
350 800
350 700
curveto
fill

% druha krivka
0.6 setgray      % zmena urovne sede
newpath
250 550
moveto
300 650
300 650
350 550
curveto
fill

% treti krivka
0.7 setgray      % zmena urovne sede
newpath
250 400
moveto
325 500
275 500
350 400
curveto
fill

% ctvrta krivka
0.8 setgray      % zmena urovne sede
newpath
250 250
moveto
350 350
250 350
350 250
curveto
fill

% pata krivka
0.9 setgray      % zmena urovne sede
newpath
275 100
moveto
350 200
250 200
325 100
curveto
fill

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% sloupec 3
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% prvni krivka
1.0 setgray      % zmena urovne sede
newpath
450 700
moveto
450 800
550 800
550 700
curveto
fill

% druha krivka
1 0 0 setrgbcolor % nastaveni barvy podle RGB
newpath
450 550
moveto
500 650
500 650
550 550
curveto
fill

% treti krivka
0 1 0 setrgbcolor % nastaveni barvy podle RGB
newpath
450 400
moveto
525 500
475 500
550 400
curveto
fill

% ctvrta krivka
0 0 1 setrgbcolor % nastaveni barvy podle RGB
newpath
450 250
moveto
550 350
450 350
550 250
curveto
fill

% pata krivka
1 0 1 setrgbcolor % nastaveni barvy podle RGB
newpath
475 100
moveto
550 200
450 200
525 100
curveto
fill

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% vykresleni cele stranky
showpage 

42_2
Obrázek 2: Druhý demonstrační příklad po zobrazení interpreterem PostScriptu

7. Změna způsobu vyplňování uzavřených cest

PostScript umožňuje vytváření uzavřených cest obsahujících „díry“. Jedná se o velmi užitečnou vlastnost, která je ještě znásobena tím, že je možné použít dva režimy určování děr. Jak je však možné vůbec díry vytvářet? První možností je vytvořit cestu, která sama sebe protíná – typickým představitelem je pěticípá hvězda. Druhou možností je vytvořit cestu, která se skládá z několika částí, které nejsou vzájemně propojeny. Zní to sice podivně, ale i tento útvar PostScript pokládá za cestu. Jak tedy PostScript pozná, které části má považovat za body uvnitř cesty (vyplněné) a které části leží mimo vyplněnou cestu. K dispozici máme dvě strategie. První se jmenuje non-zero winding number rule a druhá even-odd rule.

Strategie non-zero winding number rule pracuje tak, že se ohodnocují křížení paprsku začínajícího v daném bodě (pro který provádíme test) s cestou. Pokud cesta paprsek protíná ve směru zprava doleva, je k počitadlu připočtena hodnota 1. Pokud naopak cesta paprsek protíná v opačném směru, je od počitadla hodnota 1 odečtena. Počitadlo je před testem nastaveno na nulu a po provedení testu je na základě jeho hodnoty rozhodnuto, zda daný bod leží uvnitř či vně vyplněné cesty. Pokud je výsledná hodnota počitadla rovna nule, leží bod vně cesty, pro všechny ostatní hodnoty leží uvnitř. Z toho vyplývá, že pouhou změnou orientace části cesty (hranice díry) je možné změnit způsob vyplnění.

Strategie even-odd rule pracuje sice také na principu ohodnocování křížení paprsku začínajícího v testovaném bodě, ovšem eviduje se pouze počet průsečíků paprsku s cestou. Podle toho, zda je počet průsečíků sudý či lichý je rozhodnuto, jestli testovaný bod leží uvnitř či vně vyplněné cesty. Při aplikaci tohoto pravidla tedy nezáleží na orientaci cesty. Toto pravidlo je aplikováno při zavolání příkazu eofill, nikoli tedy fill.

8. Třetí demonstrační příklad – změna způsobu vyplňování uzavřených cest

Ve třetím demonstračním příkladu je ukázána aplikace strategie non-zero winding number rule. Jsou vykresleny dva čtverce, přičemž oba čtverce v sobě obsahují jeden menší čtverec. Hrany tohoto „podčtverce“ jsou v jednom případě orientovány proti směru hodinových ručiček a ve druhém případě ve směru hodinových ručiček. Výsledkem jsou dva zcela odlišné objekty: jeden s dírou a druhý bez díry:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Treti demonstracni priklad
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% prvni cesta
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% cesta slozena ze dvou krivek
newpath
100 400 moveto
300 400 lineto
300 600 lineto
100 600 lineto
150 450 moveto
250 450 lineto
250 550 lineto
150 550 lineto
fill


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% druha cesta
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% cesta slozena ze dvou krivek
newpath
400 400 moveto
600 400 lineto
600 600 lineto
400 600 lineto
450 450 moveto
450 550 lineto
550 550 lineto
550 450 lineto
fill


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% vykresleni cele stranky
showpage 

UX DAy - tip 2

42_3
Obrázek 3: Třetí demonstrační příklad po zobrazení interpreterem PostScriptu

9. Obsah dalšího pokračování tohoto seriálu

V následujícím pokračování seriálu o grafických formátech a metaformátech si popíšeme pravděpodobně nejzajímavější část PostScriptu – možnosti programování v Turingovsky kompletním programovacím jazyce, včetně vytváření nových příkazů (maker, funkcí), používání podmínek a programových smyček. Vzhledem k tomu, že je PostScript postavený na abstraktním zásobníkovém procesoru, si také vysvětlíme i princip používání tohoto zajímavého a z programátorského pohledu velmi užitečného nástroje.

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.