
\kap Tvorba tabulek
%%%%%%%%%%%%%%%%%%%

\sub Opakovací výplňky typu \tt\char`\\leaders           [leaders]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Slovo |\leaders| bychom mohli přeložit jako \uv{vedení}, s~významem vést oči
v~obsahu knihy od názvu kapitoly k~číslu strany.\inl[leaders]\inl[tabulky]
Typografové k~tomu obvykle používají tečky, ale |\leaders| umí stanovený
prostor opakovaně vyplňovat čímkoli.

|\leaders| se chová jako |\hskip| v~horizontálním seznamu nebo |\vskip|
ve vertikálním seznamu. Vytvoří tedy pružný výplněk. Na rozdíl od
netisknoucích pružných výplňků vytvořených pomocí |\hskip| a |\vskip|,
primitiv |\leaders| do místa výplňku obvykle něco tiskne. Povel |\leaders|
má tyto parametry:

\beguk
\leaders <box or rule> <glue specification>
\enduk

kde <box or rule> je konstrukce boxu podle pravidla <box> nebo
linka |\hrule| nebo |\vrule| s~<rule specification>
(viz syntaktická pravidla v~části~B). Je možné použít oba primitivy
pro linky bez závislosti na tom, zda se vytváří horizontální nebo
vertikální seznam. Parametr <box or rule> určuje, co se bude (třeba
opakovaně) ve výplňku sázet. Dále <glue specification> udává charakter
výplňku <glue>. Ve vertikálním módu můžeme použít třeba |\vskip|,
|\vss| a v~horizontálním módu |\hskip|, |\hfil| a další. Tato specifikace
určuje chování |\leaders|, pokud se na něj díváme jako na pružný výplněk.

\beguk Ukázka použití \leaders
\leaders \hbox{abc}\hfil % chová se jako \hfil a sází abcabc...
\leaders \hrule\hskip5cm % linka 0.4pt vysoká a 5cm široká
\leaders \vrule\hskip5cm % výška, hloubka podle vnějšího boxu
\leaders \vrule\hfil     % nyní pruží všechny tři rozměry
\leaders \vrule height3pt depth0pt \hskip10pt  % totéž jako:
  \vrule height3pt depth0pt width10pt
\leaders \hbox{abc}\vfil % jen ve vert. módu, pod sebou boxy abc
\enduk

Vyložíme nejprve činnost primitivu |\leaders|, je-li <box or rule>
skutečně linkou, tj.~je-li použit primitiv |\hrule| nebo |\vrule|.
V~horizontálním módu se ignoruje šířka linky
a místo ní se
použije šířka podle výsledného rozměru pružného výplňku. Uvažujme
například:

\beguk
\hbox to1cm {\leaders \hrule \hfil}     !lnl[prvnilead]
\hbox to1cm {\leaders \vrule height0.4pt \hfil}
\hbox to1cm {\vbox to0.4pt{}\leaders \vrule \hfil}
\enduk

Ve všech třech případech dostáváme stejný výsledek. Box s~nulovou hloubkou
vysoký 0,4\,pt a široký 1\,cm. Celý box je černý, takže vidíme
vodorovnou linku délky 1\,cm a tloušťky 0,4\,pt.
V~prvním případě (na řádku~\lnr[prvnilead]) plyne výška boxu z~implicitní
výšky |\hrule|, ve druhém případě jsme ji napsali explicitně a ve třetím
vyplynula výška |\vrule| z~nejvyššího elementu v~boxu, kterým je prázdný
|\vbox|.

Ve vertikálním módu se ignoruje výška a hloubka linky. Hloubka bude nulová
a výška bude rovna výšce pružného výplňku, který |\leaders|
vyplňuje. Například:

\beguk
\vbox to1mm{\leaders \hrule height10cm depth10cm width10cm\vfil}
\enduk

vytvoří box s~výškou 1\,mm, hloubkou 0\,mm a šířkou 10\,cm. Celý je
vyplněn černou barvou. Poznamenejme, že i jiné barvy jsou možné,
je-li použit vhodný |\special| pro konkrétní tiskové zařízení (například
PostScriptové zařízení).

Je-li výsledný rozměr pružného výplňku z~|\leaders| záporný, nekreslí se
nic. \endpar

Například v~záhlaví této knížky je použito:

\beguk
\leaders\hrule height3pt depth-2.6pt \hfil
\enduk

což vyplňuje prostor vedle textu záhlaví čarou. Celková tloušťka této čáry
je tedy \hbox{$3\,{\rm pt}-2{,}6\,{\rm pt} = 0{,}4\,{\rm pt}$} a je na
řádku ve výšce 2,6\,pt od účaří. \endpar

Uvažujme nyní případ, kdy <box or rule> je skutečně <box>.
Pak se mezera, odpovídající definitivní
velikosti pružného výplňku určeného <glue specification>, vyplní opakovaně
daným boxem. Zhruba řečeno, daný box se použije tolikrát, aby se jeho
kopiemi položenými vedle sebe celá mezera vyplnila. Tušíme, že to naprosto
přesně nejde, protože boxem šířky $w$ můžeme opakovaně vyplnit jen mezeru
o~velikosti celého násobku $w$, ale mezera, daná <glue specification>
může být jakákoli.

\TeX{} řeší uvedené dilema třemi různými způsoby podle toho, zda se použije
primitiv |\leaders|, nebo jeho alternativy
|\cleaders| a |\xleaders|.\inl[xleaders]\inl[cleaders]
Všechny tři primitivy mají stejnou syntaxi zápisu a liší se jen v~algoritmu
umísťování opakovaných boxů do stanovené mezery. Nejprve ilustrujeme chování
těchto primitivů na obrázku:

\setbox1=\vbox{\hrule\hbox to30pt{\vrule height12pt depth4pt
  \hfil\vrule\kern-0.4pt}\hrule\kern-4pt}
\setbox0=\vbox{\hrule\hbox to30pt{\vrule height12pt depth4pt
  \hfil x\hfil\vrule\kern-0.4pt}\hrule\kern-4pt}
\bigskip
\setbox2=\hbox{nějaký text vlevo\hskip30pt}
\vbox{\lineskiplimit=-20pt
\hbox to\hsize{nějaký text vlevo\leaders\copy0 \hfil použití " "|\leaders|}
\vskip-\baselineskip
\hbox to\wd2{\leaders\box1\hfil}
\bigskip
\hbox to\hsize{nějaký text vlevo\cleaders\copy0 \hfil použití |\cleaders|}
\bigskip
\hbox to\hsize{nějaký text vlevo\xleaders\box0 \hfil použití |\xleaders|}}

Tento obrázek je výstupem tohoto kódu (až na rámečky):

\beguk
\hbox to\hsize{nějaký text vlevo%
    \leaders\hbox to30pt{\hfil x\hfil}\hfil použití " \leaders"}
\hbox to\hsize{nějaký text vlevo%
   \cleaders\hbox to30pt{\hfil x\hfil}\hfil použití "\cleaders"}
\hbox to\hsize{nějaký text vlevo%
   \xleaders\hbox to30pt{\hfil x\hfil}\hfil použití "\xleaders"}
% uvozovky "..." jsou zde hranicemi verbatim prostředí.
\enduk

Opakovaný box má ve všech případech šířku 30\,pt a má uprostřed
písmeno~\uv{x}. Popíšeme nyní všechny tři způsoby umístění boxů:

Při použití |\leaders| se \TeX{} podívá na levý okraj
vnějšího boxu, v~němž je |\leaders| použit. Odtud začne pomyslně
opakovat stanovený box až do chvíle, kdy je pokryt celý text
vlevo od místa, kde začíná |\leaders|. Pak naváže a začne skutečně sázet
boxy až do chvíle, kdy je vyplněna požadovaná mezera tak, že se další box
už nevejde.

Je-li použit |\cleaders|, \TeX{} vloží do požadované mezery maximální počet
boxů, všechny jsou těsně u~sebe. Vlevo i vpravo od této skupiny boxů
\TeX{} doplní stejnou mezeru k~vyplnění případného nevyčerpaného zbytku.

Při použití |\xleaders| \TeX{} vloží rovněž maximální počet boxů, ovšem
před první, za poslední a mezi každé dva vloží stejnou mezeru. Velikosti
těchto mezer vyplní v~součtu nevyčerpaný zbytek.  \endpar

První alternativa, tj.~použití |\leaders|, se hodí pro vkládání teček do
obsahů. Pokud třeba máme

\beguk
\tocline{<název kapitoly>}{<číslo strany>}
\enduk

pak můžeme definovat

\beguk
\def\tocline #1#2{%
    \line{#1 \leaders\hbox to1em{\hfil.\hfil}\hfil\ #2}}
\enduk

Protože \TeX{} měří usazení boxů s~tečkami od levého okraje vnějšího boxu,
budou všechny tečky na jednotlivých řádcích v~obsahu přesně pod sebou.
Nezáleží na tom, jak dlouhý je text s~názvem kapitol. \endpar

Při opakování boxů ve vertikálním módu \TeX{} pracuje s~celkovou výškou
opakovaného boxu (tj.~výška plus hloubka). S~tímto rozměrem pracuje
analogicky, jako v~horizontálním módu pracuje s~šířku opakovaného boxu.
Boxy klade pod sebe.

Není-li výsledná mezera z~<glue specification> v~|\leaders| kladná, \TeX{}
nevysází žádný box. Není-li rozměr opakovaného boxu kladný, \TeX{} rovněž
netiskne žádný box. \uv{Rozměr} zde znamená šířku v~případě
horizontálního módu a celkovou výšku ve vertikálním módu.

V~místě pro zápis boxu můžeme použít primitiv |\box|. \TeX{} pak
opakuje box z~daného registru. Je ovšem třeba upozornit na to, že po
takovém použití boxu v~|\leaders| je hodnota registru ztracena. Box je sice
v~rámci jednoho |\leaders| opakován, ale nakonec je registr vyprázdněn.
Pokud budeme obsah boxu potřebovat i
nadále, musíme použít namísto |\box| primitiv~|\copy|.
\endpar

{\subpar Příklad.}
Popíšeme sazbu obsahu v~této knížce. Všimněme si, že tečky
jsou tam střídány tak, že když na ně položíme pravítko pod úhlem
$63{,}5^{\textstyle\circ}$, máme tečky na jeho hraně pěkně v~jedné řadě.

Uvažujme, že obsah sestavujeme z~pracovního souboru {\tt tbn.toc}, kde
jednotlivé řádky vypadají třeba takto:

\beguk Ukázka obsahu souboru tbn.toc
\partline {A}{Algoritmy}{9}
\chaptocline {1}{Vstupní části \TeX u}{10}
\subtocline {1.1}{Koncept vstupní brány \TeX u}{10}
\subtocline {1.2}{Input procesor}{12}
\subtocline {1.3}{Token procesor}{19}
\chaptocline {2}{Expand procesor}{31}
\subtocline {2.1}{Definování maker}{31}
<atd...>
\enduk

Vidíme, že pro kapitolu máme řádek začínající sekvencí "\chaptocline" a pro
sekci je použita sekvence "\subtocline".
Pro sazbu takových teček používáme |\leaders| s~opakovaným boxem:

\beguk
\hbox to12pt{\kern\dimen0.\hss}
\enduk

kde pro |\dimen|{\tt0} postupně střídáme hodnoty 3\,pt a 9\,pt. Pro
vystřídání hodnot je použito makro "\cvak". Celá věc vypadá tedy takto:

\beguk Makro pro sazbu obsahu se střídavými tečkami
\def\cvak{\ifdim\dimen0!<6pt \dimen0=9pt \else \dimen0=3pt \fi}  !inl[cvak]
\def\puntiky{\leaders\hbox to12pt{\kern\dimen0.\hss}\hfil}
\def\chaptocline #1#2#3{\medskip
   \line{#1.\enskip #2\hfil #3}\medskip}
\def\subtocline #1#2#3{%
   \line{\qquad #1\quad #2\puntiky\hbox to2em{\hfil#3}}\cvak}
\def\partline #1#2#3{\cvak\bigskip
   \line{\bf Část #1: #2 \puntiky\ #3}\cvak}
\enduk

Údaje o~stranách~kapitol nejsou v~řádkové osnově. Nelze tedy v~těchto
řádcích umístit tečky tak, aby v~obou směrech navazovaly. Proto jsem
se při návrhu typografie dokumentu rozhodl, že tam žádné tečky nebudou.
Jelikož mezi posledním řádkem sekce nějaké kapitoly a dalším řádkem sekce
následující kapitoly jsou vynechány dohromady dva řádky (půl plus jedna
plus půl), stačí v~tomto místě uplatnit jedno "\cvak". Dále se provede vždy
jedno "\cvak" za každým řádkem sekce. \endpar

V plainu jsou pomocí |\leaders| a |\cleaders| definována makra |\hrulefill|
a |\dotfill| (viz část~B). \endpar


\sub Tabulky s~pevnou šířkou sloupců                      [sloupce]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Pokud dopředu víme, jak široké budou jednotlivé sloupce tabulky, nemusíme
použít primitiv |\halign|, o~němž bude řeč v~další kapitole.
Využijeme možnost psát |\hbox|" to"<dimen>. Tím nastavíme šířky
jednotlivých sloupců.

Myšlenku hned ilustrujeme na příkladě. Každý semestr za mnou přijdou
kolegové, že potřebují vytisknout seznamy studentů, aby si do seznamů mohli
dělat v~průběhu jednotlivých týdnů v~semestru poznámky. Jména studentů
jsou výstupem jisté databáze. Předpokládejme, že jsou jména rozdělena po
jednotlivých studijních skupinách.
V~jedné skupině je maximálně 25~studentů. Vytiskneme pro
každou skupinu pětadvacetiřádkový formulář, kde v~každém řádku bude
napsáno jméno jednoho studenta. Je-li studentů méně než~25, budou zbylé
řádky prázdné, ale přihrádky a číslo nakreslíme.

Jeden řádek bude vypadat třeba takto:

\bigskip
\vbox{\parskip=0pt
\def\\#1 #2|{\hbox to#1{\enspace #2\hss}\cara}
\def\cara{\kern-0.4pt\vrule}
\def\radek #1|#2|{\vrule height4.5mm depth1.5mm
   \\10mm \hfill #1\enspace|\\50mm #2|\\19mm |\\1mm |%
   \zbytek{}\par \hrule}
\def\zbytek #1{\tempnum=0
   \loop \advance\tempnum by1
     \hfill\hbox to0pt{\hss#1\hss}\hfill\cara
   \ifnum\tempnum\<8 \repeat\hbox{}}
\hrule
\noindent\radek 1.|Mravenec Ferdinand|}

První sloupec obsahuje pořadové číslo, druhý jméno studenta, další
políčko je pro individuální využití učitele. Pak za dvojitou čarou
následuje patnáct přihrádek, protože semestr má obvykle patnáct
týdnů. Čtenář v~knize vidí jen osm přihrádek, protože na šířku sazby knihy
by všech patnáct přihrádek vypadalo moc stísněně.

Najednou tiskneme seznamy se stovkami až tisíci studenty
a je vhodné, aby formuláře pro jednotlivé studijní skupiny vypadaly
stejně. To znamená, že šířky přihrádek by měly být stanoveny jednotně.
Kdybychom použili |\halign|, museli bychom do \TeX{}u načíst celou
databázi, aby \TeX{} rozhodl podle \uv{nejdelšího} jména, jak širokou volit
přihrádku pro jména. To je neekonomické a dokonce neužitečné. Zatímco
výjimečně nějaký zahraniční student z~Dálného východu má jméno
složeno z~pěti nebo více slov,
většina studentů má jméno obvyklé délky. Kvůli těm výjimkám
ze zahraničí by seznamy všech ostatních studijních skupin v~přihrádce pro
jméno zbytečně plýtvaly místem. Je proto vhodnější toho Muhameda
Abu Ali Admal atd. nechat vysázet přes okraj přihrádky.

Volíme tedy pevnou šířku přihrádek. Například pro pořadové číslo nám bude
stačit 1\,cm, protože se tam pro každou skupinu vyskytuje nejvýše
číslo dvojciferné. Dále pro jméno jsme stanovili šířku přihrádky 5\,cm.
Další políčko určené pro potřebu učitele bude mít 2\,cm a pak ostatních
15~přihrádek vyplní stejnoměrně zbytek.

Pro začátek a pro jednoduchost předpokládejme, že vstupní soubor z~databáze
jmen už u~každého studenta obsahuje \TeX{}ovskou sekvenci a oddělovač, tedy
údaje vypadají takto:

\beguk
\p Mravenec Ferdinand*
\p Novák Jan*
<atd...>
\enduk

Vytvoříme makro "\p", které bude sázet jeden řádek formuláře:

\beguk Sazba tabulky s pevnými rozměry sloupců
\offinterlineskip !inl[pevnatab]
\newcount\num \newcount\tempnum
\parindent=0pt \parskip=0pt
\def\\#1 #2|{\hbox to#1{\enspace #2\hss}\cara} !lnl[hboxto]
\def\cara{\kern-0.4pt\vrule}
\def\radek #1|#2|{\vrule height4.5mm depth1.5mm !lnl[vrulehd]
   \\10mm \hfill #1\enspace|\\50mm #2|\\19mm |\\1mm |% !lnl[celyradek]
   \zbytek \par \hrule}
\def\zbytek{\tempnum=0
   \loop \advance\tempnum by1 \hfill\cara
   \ifnum\tempnum!<15 \repeat}
\def\p #1*{\advance\num1 \radek\the\num.|#1|}
\enduk

Zde jsme si na řádku~\lnr[hboxto] definovali zkratku, abychom nemuseli
pořád dokola psát |\hbox|" to"<dimen>. Makro "\radek" vytvoří celý jeden
řádek formuláře a vezme dva parametry: první, číslo, vytiskne do
první přihrádky a druhý, jméno studenta, do druhé. Linka "\vrule" se
stanovenou výškou a hloubkou (řádek~\lnr[vrulehd]) určuje
celkovou výšku každého řádku formuláře. Linka zde funguje jako podpěra.
Předpokládá se, že žádný text se jménem nebude mít nikdy výšku nebo hloubku
větší, než má tato linka. Tato linka také zahajuje odstavcový mód. Na
následujícím řádku v~kódu~(\lnr[celyradek]) pak definujeme celou strukturu
řádku formuláře. Sazba probíhá v~odstavcovém módu. Pomocí makra "\zbytek"
vložíme patnáct vertikálních čar stejně daleko od sebe (|\hfill| vytlačí
|\parfillskip| z~konce řádku odstavce). Pak uzavřeme odstavec. Konečně
nakreslíme vodorovnou čáru pod řádkem (|\hrule|).

Nesmíme zapomenout přidat čáru nad prvním řádkem formuláře.
Dále po načtení
seznamu studentů jedné skupiny se musíme postarat o~to, aby byl zbytek
řádků do dvaceti doplněn prázdnými kolonkami. Proto ještě definujeme makro
"\dodelej", které dotiskne zbytek do dvaceti pěti řádků:

\beguk
\def\dodelej{\loop \advance\num by1
   \ifnum\num!<26 {\radek\the\num.||}\repeat \vfil\break}
\enduk

Všimněme si, že jsme zde sazbu řádku makrem "\radek" uzavřeli do skupiny.
Máme tam totiž ještě jeden |\loop| a je potřeba, aby se vnitřní cyklus 
s~vnějším nehádal.

Nyní makro trošku vylepšíme, abychom nemuseli v~databázi před každé jméno
psát "\p" a na konec nemuseli připojovat hvězdičku. Nechť se pomocí
|\obeylines| stane konec vstupního řádku "^^M" aktivní. Znak "^^M" bude
sloužit jako separátor:

\beguk
{\obeylines\gdef\p #1^^M{\advance\num1 \radek\the\num.|#1|}}
\enduk

Nyní stačí načíst soubor z~databáze při aktivním "^^M" a při
|\everypar|"={\p}". \endpar

Uvedený příklad rozvedeme na ukázce maker pro zpracování skutečného
výstupu z~databáze, která se používá na naší fakultě. Tvůrci výstupních
procedur z~databáze předpokládali, že uživatel použije jednoduchou
řádkovou tiskárnu a na \TeX{} vůbec nepomysleli. O~to budeme mít v~makru
více práce správně načíst výstupní sestavu z~databáze, která
vypadá takto:

\beguk
  Komponenta studium                 Datum a čas: 23.09.96 15:17
!indent
    Ročník:   1   Skupina:     21
!indent
   Příjmení           Jméno             Rodné číslo
   -----------------  ----------------  -----------
   Mravenec           Ferdinand         1234567890
   Erben              Karel Jaromír     0987654321
   <atd., další studenti>
   <prázdné řádky>
                                            Strana 1     ze 18
^^L
!indent
  Komponenta studium                 Datum a čas: 23.09.96 15:17
!indent
    Ročník:   1   Skupina:    22
!indent
   Příjmení           Jméno             Rodné číslo
   ------------------ ----------------- -----------
   Novák              Jan               3333333333
   <další studenti a prázdné řádky>
                                            Strana 2     ze 18
^^L
<další skupiny stejně>
\enduk

Makro, které dokáže takovou databázi načíst a vytisknout tabulky se seznamy
studentů podle právě popsaného formátu, může vypadat následovně:

\beguk Zpracování databáze studentů do tabulek
\nopagenumbers \offinterlineskip !inl[database]!inl[seznam]
\newcount\num \newcount\tempnum
\special{landscape}  \hsize=\vsize \voffset=-.5cm  !lnl[speclandscape]
\parindent=0pt  \parskip=0pt
\def\\#1 #2|{\leavevmode\hbox to#1{\enspace #2\hss}\cara}
\def\cara{\kern-0.4pt\vrule}
\def\radek #1|#2|{\vrule height4.5mm depth1.5mm
   \\10mm \hfill #1\enspace|\\50mm #2|\\19mm |\\1mm |%
   \zbytek{}\par \hrule}
\def\zbytek #1{\tempnum=0
   \loop \advance\tempnum by1
     \hfill\hbox to0pt{\hss#1\hss}\hfill\cara
   \ifnum\tempnum!<15 \repeat\hbox{}}
\def\dodelej{\loop \advance\num by1 %
   \ifnum\num!<26 {\radek\the\num.||}\repeat \vfil\break \num=0}
\def\setcatnumbers#1{\tempnum=`0 % kategorie pro číslice
    \loop \catcode\tempnum=#1 
       \ifnum\tempnum!<`9 \advance\tempnum by1 \repeat}
\obeylines  !lnl[startobey]
\def\p #1^^M{\advance\num1 \radek\the\num.|#1|}%
\def\demask Komponenta #1Ročník: #2 Skupina: #3^^M{\everypar={}%
   \vrule height5mm depth2mm width0mm %
   {\let\cara=\relax % Sazba záhlaví tabulky; nepotřebujeme čáry
    \\80mm \bf \vrule width0pt depth4pt  Skupina #3|% podpěra
    \zbytek{\the\tempnum}}\par\hrule                % čísla týdnů!lnl[uzijzbytek]
   \setcatnumbers{9} \maskpata}
\def\maskpata #1-^^M#2Strana#3^^M{\everypar={\p}#2\everypar={}%
   \dodelej \everypar={\demask}\setcatnumbers{12}}
\everypar={\demask}
\input database.txt
\end
\enduk

Některé části makra si ještě zaslouží naši pozornost.
Na řádku~\lnr[speclandscape] jsme použili |\special|"{landscape}", což dává
do {\tt dvi} pokyn pro ovladač {\tt dvips}, aby se formulář tiskl na stranu
otočen o~90$^{\textstyle\circ}$. Proto budeme mít pro jednotlivé řádky dost
místa a můžeme nastavit |\hsize|{\tt=}|\vsize|.

Makro "\zbytek" jsme rozšířili, aby obsahovalo jeden parametr, který bude
umístěn uprostřed každé přihrádky pro týden semestru. Použití
"\zbytek{\the\tempnum}" na řádku~\lnr[uzijzbytek]
nám tedy vysází čísla jednotlivých týdnů, což potřebujeme do záhlaví.

Od řádku \lnr[startobey] nastavujeme znak~"^^M" (konec řádku) jako aktivní
a má význam |\par|. Konec řádku bude totiž oddělovačem pro parametry našich
dalších maker. Makro "\demask" se spustí z~"\everypar", sotva se \TeX{} dostane
k~prvnímu slovu v~databázi, kterým je slovo {\tt Komponenta}. Vše až po
slovo {\tt Ročník} přeskočíme a do "#3" ukládáme číslo skupiny, které
budeme sázet v~záhlaví. Po sazbě záhlaví potřebujeme z~následujících
vstupních údajů \uv{oddělit zrno od plev}, což provede makro "\maskpata".
Do parametru "#1" se schovají zbytečnosti ze vstupního záhlaví včetně všech
podtrhovacích znaků \uv{"---"} až po poslední. To jsou plevy.
V~parametru "#2" máme zrno, které je navíc načteno v okamžiku, kdy číslice
{\tt 0}~až~{\tt 9} mají kategorii~9 (ignoruj), takže ignorujeme údaje
o~rodných číslech. Konečně v~parametru~"#3" jsou zase plevy. Při
|\everypar|"={\p}" pak zpracujeme jednotlivé řádky \uv{zrna} způsobem, který
jsme popsali výše. Nakonec vracíme číslicím původní kategorii~12, abychom
mohli přečíst ze záhlaví další strany číslo skupiny.

Všimneme si ještě, že výstup z~databáze klade mezi jednotlivé stránky
určené pro levnou řádkovou tiskárnu znak "^^L" (konec strany). To nás
v~plainu vůbec nevyvádí z~míry, protože tento znak zde má aktivní kategorii
a má význam "\par". \endpar


\begingroup

{\subpar Makro plainu emulující vlastnosti psacího stroje.}
Ve zbylé části této sekce se budeme zabývat makry plainu pro
tzv.~|\tabalign|\inl[tabalign],
což umožňuje nastavovat a posléze používat sloupečky
s~pevnou šířkou. Využití makra není příliš časté. Snad je to proto, že až
příliš připomíná vlastnosti psacího stroje. Víme, že příprava sazby a
používání psacího stroje jsou dvě zcela odlišné věci.

Nejprve vyložíme vlastnosti makra na uživatelské úrovni a teprve v~další
části se pustíme do rozboru maker plainu, kterými jsou tyto vlastnosti
implementované.

V prostředí |\tabalign| uživatel používá tzv.~{\it tabulátory},\inl[tabulator]
které označuje symbolem~\uv{"&"}. Jedná se o~neviditelné značky, jejichž
poloha v~řádku určuje umístění levých okrajů jednotlivých sloupců.

Řádek řízený prostředím |\tabalign| se zapisuje ve formátu
|\+|<obsah řádku>"\cr". V~<obsahu řádku> se mohou vyskytovat znaky~\uv{"&"},
které mohou mít jeden z~těchto významů:

\begitems
* Nastavení polohy tabulátoru podle polohy aktuálního bodu sazby.
* Využití polohy tabulátoru pro umístění následujícího textu.
\enditems

Znaky~\uv{"&"} v~<obsahu řádku> počítáme zleva \uv{první, druhý, třetí} atd.
Přitom $n$-tý znak~\uv{"&"} reprezentuje $n$-tý tabulátor. Není-li $n$-tý
tabulátor nastaven, přítomnost $n$-tého znaku~\uv{"&"} jej nastavuje. Je-li
$n$-tý tabulátor nastaven, nenastavuje se znovu, ale začátek textu
za $n$-tým znakem~\uv{"&"} bude (po ignorování vstupních mezer) umístěn
v~poloze tabulátoru. Na počátku není nastaven žádný tabulátor. Příklad:

\beguk
\+ Tady nastavuji první:& tu druhý:& a píšu další text \cr
\+ text od kraje & od prvního & od druhého, nastavuji třetí:&\cr
\+ &&& text od třetího tabulátoru\cr
\enduk

Tento experiment nám dává následující výsledek:

\bigskip
\+ Tady nastavuji první:& tu druhý:& a píšu další text \cr
\+ text od kraje & od prvního & od druhého, nastavuji třetí:&\cr
\+ &&& text od třetího tabulátoru\cr

V~ukázce si můžeme všimnout dvou vlastností: (1)~Text podle tabulátorů
se může beztrestně překrývat. (2)~Text sázený z~<obsahu řádku> může
beztrestně vyčnívat ze zrcadla sazby daného šířkou |\hsize|.

Pomocí makra |\cleartabs| použitém v~<obsahu řádku> můžeme mazat nastavení
všech tabulátorů vpravo od zápisu |\cleartabs|. Jinými slovy,
všechny následující znaky~\uv{"&"} použité za |\cleartabs| v~<obsahu řádku>
polohu tabulátoru znovu nastavují a nepoužívají. Navážeme-li na náš
předchozí příklad:

\beguk
\+ další text od kraje & od prvního a znovu
   nastavuji druhý:\cleartabs & a nastavuji ještě třetí:&\cr
\enduk

Pomocí makra |\settabs|, které používáme mimo <obsah řádku>, lze vymazat
polohy všech tabulátorů a nastavit je znova. Použije-li se toto makro ve
formátu

\beguk
\settabs\+ <obsah řádku>\cr
\enduk

pak znaky~\uv{"&"} uvnitř <obsahu řádku> nastavují tabulátory, ale výsledný text
se netiskne. Můžeme tedy nastavit tabulátory podle nějakého
abstraktního textu, o~kterém víme, že reprezentuje například nejširší zápis
v~jednotlivých sloupečcích.

Existuje ještě jeden způsob použití makra |\settabs| pro nastavení
tabulátorů. Při zápise "\settabs<number>\columns" \TeX{} nastaví
$n=<number>$ tabulátorů tak, že poslední ($n$-tý) je přesně na pravém
okraji zrcadla určeného podle |\hsize| a všechny ostatní jsou rovnoměrně
daleko od sebe a rozdělují šířku zrcadla na $n$ stejně širokých
sloupečků. Například po:

\beguk
\settabs 3\columns
\+ Od kraje & Od prvního & Od druhého \cr
% "& Od třetího" neukážeme, protože by vyčníval ven ze zrcadla.
\enduk

dostáváme:

\bigskip
\settabs 3\columns
\+ Od kraje & Od prvního & Od druhého \cr

Zarovnání textu nalevo v~každém sloupečku je zařízeno pomocí |\hss|
připojovaném vpravo k~textu. Proto můžeme ve sloupci text centrovat třeba
takto:

\beguk
\settabs 3\columns
\+\hss první sloupec&\hss druhý&\hss a třetí&\cr
\enduk

Kdybychom těsně před |\cr| nenapsali znak~\uv{"&"}, nebude \TeX{} k~textu
žádné |\hss| připojovat. Text posledního sloupce těsně před |\cr| totiž
není nijak upravován. \endpar

\endgroup

Nyní si popíšeme činnost makra plainu, které nám uvedené prostředí
|\tabalign| nabízí. Zjistíme, že se makro nakonec opírá o~primitiv
|\halign|, ovšem není využita jeho vlastnost sestavování sloupečků podle
nejširšího textu.

Makro budeme komentovat po částech. Nejprve uvedeme deklarace:\pgl[tabalign]

\beguk Plain
\newif\ifus@ \newif\if@cr
\newbox\tabs \newbox\tabsyet \newbox\tabsdone
\enduk

Pomocí "\ifus@" se ptáme na to, zda si uživatel přeje tisknout
<obsah řádku>. Víme, že při "\settabs\+<obsah řádku>\cr" podléhá sestavení
<obsahu řádku> stejným pravidlům, pouze výsledek nakonec nevytiskneme.
V~takovém případě bude "\ifus@" dávat \uv{false}.
Dále podle "\if@cr" makro zjišťuje, zda je přečtena poslední položka,
za kterou už následuje |\cr|. Tuto položku makro zpracuje odlišným způsobem.

V~boxu "\tabs" je seznam boxů, jejichž šířky určují šířky
jednotlivých sloupců v~opačném pořadí, než ve kterém počítáme sloupce.
Šířka posledního boxu v~"\tabs" tedy určuje šířku prvního sloupce.
Například po "\settabs 3\columns" máme v~"\tabs" tři boxy,
každý má šířku rovnou třetině |\hsize|.

Na začátku sestavování <obsahu řádku> vložíme boxy z~"\tabs" do "\tabsyet".
Dále v~průběhu sestavování <obsahu řádku> budeme jednotlivé boxy
odpovídající zpracovávaným položkám přerovnávat z~"\tabsyet" do
"\tabsdone". Při této činnosti můžeme některé boxy přidat (nové
tabulátory), některé mazat ("\cleartabs") a posléze třeba zase přidávat. Na
konci řádku (při~"\cr") vrátíme (případně) nové údaje o~tabulátorech do
"\tabs".

Nyní následují makra "\cleartabs" a "\settabs" a začátek makra "\+".

\beguk Plain
\def\cleartabs{\global\setbox\tabsyet=\null \setbox\tabs=\null}!pgl[cleartabs]
\def\settabs{\setbox\tabs=\null \futurelet\next\sett@b} !pgl[settabs]
\let\+=\relax % in case this file is being read in twice
\def\sett@b{\ifx\next\+
    \def\nxt{\afterassignment\s@tt@b\let\nxt}%
  \else\let\nxt=\s@tcols\fi \let\next=\relax \nxt} !lnl[nextrelax]
\def\s@tt@b{\let\nxt\relax \us@false \m@ketabbox}
\def\tabalign{\us@true \m@ketabbox} % non-\outer version of \+
\outer\def\+{\tabalign}
\def\s@tcols#1\columns{\count@=#1 \dimen@=\hsize
  \loop \ifnum\count@>\z@ \@nother \repeat}
\def\@nother{\dimen@ii=\dimen@ \divide\dimen@ii by\count@
  \setbox\tabs=\hbox{\hbox to\dimen@ii{}\unhbox\tabs}%
  \advance\dimen@ by-\dimen@ii \advance\count@ by-1 }
\enduk

Vidíme, že "\cleartabs" maže boxy v~"\tabsyet", ale nikoli v~"\tabsdone".
To znamená, že už zpracované položky budou mít tabulátory zachovány, ale
následující budou nově nastaveny. Makro "\settabs" nuluje všechny
tabulátory (v~"\tabs") a dále se pomocí |\futurelet| ptá, zda následuje
token "\+". Pokud ano, provede se zpracování řádku ("\m@ketabbox") 
s~parametrem \uv{netiskni výsledek} ("\us@false"). Pokud ne, makro se chová
jako "\s@tcols", tj.~čte <number> před separátorem "\columns" a vkládá jej
do "\count@". V~"\@nother" se postupně v~cyklu uloží do "\tabs" prázdné
boxy o~šířce |\hsize|/<number>. Promyslete si, proč.

Důležitou vlastností makra "\+" je, že je definováno prefixem |\outer|. To
zamezí chybě typu "\+<obsah řádku> \+" (tj. zapomenuté "\cr"). Na druhé
straně s~tím máme v~makru trochu problémy a musíme se s~tím vyrovnat.
Následující rozbor makra je tedy možno chápat též jako ukázku různých
nebezpečí, číhajících při definování jakéhokoli makra prefixem
|\outer|.

V~těle definice makra "\setb@x" je použit token "\+", a proto v~tu chvíli
nesmí být typu |\outer|. Z~toho důvodu je před tím řečeno "\let\+=\relax",
abychom zrušili možný původní význam tokenu "\+". To může být užitečné při
opakovaném čtení souboru {\tt plain.tex}.

V~makru "\settabs" se může pomocí |\futurelet| ztotožnit význam "\next"
s~tokenem "\+". Pak ovšem má "\next" typ |\outer| a už nikdy by nebylo možné
jej použít v~těle definice. Přitom slůvko "\next" používáme v~tělech
definic rádi. Proto na řádku~\lnr[nextrelax] dáváme tokenu "\next" význam
|\relax|.

Vlastní makro "\+" má verzi bez |\outer| s~názvem "\tabalign". Tato verze
se dá použít v~našich vlastních definicích. Makro "\tabalign" expanduje
nakonec na "\m@ketabbox" při "\us@true", tj.~bude se tisknout výsledný
<obsah řádku>.

Konečně uvedeme makra, která zpracovávají <obsah řádku>.

\beguk Plain
\def\m@ketabbox{\begingroup
  \global\setbox\tabsyet=\copy\tabs
  \global\setbox\tabsdone=\null
  \def\cr{\@crtrue\crcr\egroup\egroup     !lnl[dvaegroup]
    \ifus@ \unvbox0\lastbox \fi\endgroup  !pgl[lasttrik]!lnl[lasttrik]
    \setbox\tabs\hbox{\unhbox\tabsyet\unhbox\tabsdone}}%!lnl[obnovatabs]
  \setbox0=\vbox\bgroup\@crfalse             !lnl[zahajtab]
    \ialign\bgroup&\t@bbox##\t@bb@x\crcr}
\def\t@bbox{\setbox0=\hbox\bgroup}
\def\t@bb@x{\if@cr\egroup % now \box0 holds the column
  \else\hss\egroup                                 !lnl[začniat]
     \global\setbox\tabsyet=\hbox{\unhbox\tabsyet
          \global\setbox1=\lastbox}% now \box1 holds its size
     \ifvoid1 \global\setbox1=\hbox to\wd0{}%
     \else \setbox0=\hbox to\wd1{\unhbox0}\fi
     \global\setbox\tabsdone=\hbox{\box0\unhbox\tabsdone} !lnl[konecat]
  \fi \box0}
\enduk

Nejprve v~"\m@ketabbox" otevřeme skupinu a v~rámci ní nastavíme výchozí
hodnoty pro \uv{přerovnávací řady} vzorových boxů "\tabsyet" a "\tabsdone".
Předefinujeme "\cr", takže všude v~souvislosti s~|\halign| budeme
používat náhradní |\crcr|. Do definice "\cr" se podíváme až nakonec. Nyní
začneme číst makro od řádku~\lnr[zahajtab]. Tam otvíráme |\vbox| a do něj
(jak uvidíme později) postupně vložíme výsledek jednořádkového |\halign|.

Makro "\ialign" je ekvivalent k~|\halign| s~nastavením nulového |\tabskip|.
Zde máme v~deklaraci řádku jediné schéma položky s~příznakem návratu
(terminologie k~|\halign|, viz následující sekci).
Při zahájení položky se expanduje "\t@bbox", tj.~provede se
\uv{|\setbox|"0=\hbox{"}. Jakmile narazíme při zpracování položky na
znak~\uv{"&"}, box~0 se uzavře (viz "\t@bb@x"). Máme tedy v~boxu~0 právě
načtenou položku. Pokud je položka ukončena znakem~\uv{"&"}, dává "\if@cr"
hodnotu \uv{false}, neboť jsme zatím nedosáhli konce řádku pomocí "\cr".
Povíme si, co se v~tomto případě odehrává (řádky~\lnr[začniat] až
\lnr[konecat]):

Předně položka se uzavře s~připojením |\hss| na konec textu položky.
Pak se do boxu~1 vloží z~"\tabsyet" box, který by měl svou šířkou
udávat šířku právě zpracovávané položky. Přitom se z~"\tabsyet" tento box
zruší. Pokud se odtud nepodařilo získat informaci o~šířce položky
(|\ifvoid|{\tt1}), budeme tabulátor nastavovat. Proto nastavíme v~boxu~1
šířku právě načtené položky (|\wd|{\tt0}). Pokud informace o~tabulátoru
existuje (|\else|), nebude se tabulátor nastavovat, ale upraví se šířka
boxu~0 s~načtenou položkou na požadovanou šířku |\wd|{\tt1}. Nakonec se do
"\tabsdone" uloží (případně nový) box udávající šířku položky, neboli polohu
tabulátoru. Ven z~makra ke zpracování v~položce |\halign| vystupuje vždy
box~0.

Ukončí-li uživatel řádek pomocí |\cr|, toto makro expanduje na "\@crtrue"
následované |\crcr| (tj.~skutečným ukončením řádku v~|\halign|). Nyní už 
v~závěrečné části schématu položky není žádná rozsáhlá činnost, pouze se
text z~boxu~0 dostává do sazby položky, jak jí rozumí primitiv |\halign|.

První |\egroup| na řádku~\lnr[dvaegroup] uzavírá |\halign|. Tento primitiv
už nemá co na práci. Jeden řádek \uv{tabulky} s~rozměry položek tak, jak je
připravilo makro, vloží do vertikálního seznamu. Druhé |\egroup| uzavírá
|\vbox| otevřený na řádku~\lnr[zahajtab]. Tento box tedy nyní vstupuje do
pracovního boxu~0. V~tomto boxu máme jediný řádek odpovídající
<obsahu řádku>, který jsme právě zpracovali. Má-li být tento řádek tištěn
("\ifus@"), vrátíme jej do vnějšího vertikálního seznamu pomocí
|\unvbox|{\tt0}. Zde stojí za pozornost trik s~|\lastbox|
z řádku~\lnr[lasttrik]. Tento trik je rozebrán u hesla |\unvbox| v části~B.

Nakonec na řádku~\lnr[obnovatabs] obnovíme stav tabulátorů udržovaný 
v~"\tabs". Nejprve vkládáme nevyčerpané tabulátory z~"\tabsyet" a za nimi
použité a případně nově zpracované tabulátory z~"\tabsdone". \endpar



\sub Tabulky pomocí \tt\char`\\halign                      [halign]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Algoritmy, které souvisejí s~primitivem |\halign|, jsou poměrně komplikované.
Začneme proto zlehka, jednoduchým příkladem:

\beguk Úvodní příklad na \halign
\tabskip=2em plus 5em
\halign{{\bf Položka:} #\hfil & \hfil#\hfil \quad konec\cr !lnl[defradku]
  první & toto bude na střed \cr \noalign{\hrule\smallskip}!lnl[mezerypol]
  druhá & další text \cr
  třetí \cr}          !lnl[třetí]
\enduk

Tento kód vyprodukuje následující tabulku:

$$\vbox{\tabskip=2em
\halign{{\bf Položka:} #\hfil &\hfil#\hfil \quad konec\cr
  první & toto bude na střed \cr \noalign{\hrule\smallskip}
  druhá & další text \cr
  třetí \cr}}\pgl[prvnitab]
$$

"\halign" má (zhruba) tuto strukturu zápisu:

\beguk
\halign{<deklarace řádku> \cr
  <položky tabulky prvního řádku> \cr
  <položky tabulky druhého řádku> \cr
  ...    
  <položky tabulky posledního řádku> \cr}
\enduk

Pojmem {\it tělo tabulky\/}\inl[telotab]
označujeme vše, co je zapsáno mezi
závorkami "{...}" za primitivem |\halign|.
Tělo tabulky se dělí na dvě části: {\it deklaraci řádku\/}\inl[deklradku]
ukončenou prvním "\cr" a {\it datovou část\/}\inl[datacast]
tabulky.
V datové části jsou {\it položky\/}\inl[polozka]
tabulky, které jsou od sebe
odděleny tokenem kategorie~4 (ASCII kód
nehraje roli, obvykle se používá \krab[\&]4.). Jednotlivé řádky jsou
v datové části odděleny od sebe sekvencí "\cr".
Za sekvencí "\cr" může být primitiv |\noalign| a za ním
pak "{<vertikální seznam>}". Pokud se tak stane, \TeX{} zatím neotvírá
další položku, ale akceptuje <vertikální seznam>, který uloží pod právě
ukončený řádek. Pokud je |\noalign| hned za prvním |\cr|, které ukončuje
deklaraci řádku, seznam se uloží nad tabulku před první řádek tabulky.

Primitiv |\cr| musí být uveden i za posledním řádkem tabulky, jinak
se dočkáme chyby. Pokud si konstrukci |\halign| píšeme sami, pak toto
omezení není problémem, protože si na to dáme pozor. Pokud ale vytváříme
makro pro uživatele, který jednou použije |\cr| na konci své tabulky a
podruhé ne, pak mohou nastat problémy. Proto existuje primitiv |\crcr|,
který je po sekvenci |\cr| (a případně za konstrukcí |\noalign|) ignorován,
zatímco všude jinde se chová jako |\cr|. V~makrech tedy doporučujeme psát
pro uzavření konstrukce tabulky |\crcr|"}".

Deklarace řádku se člení na {\it schémata položek},\inl[schema]
která jsou od sebe
oddělena tokenem kategorie 4 (\krab[\&]4.). Každé schéma položky {\it
musí\/} obsahovat právě jeden token kategorie 6 (obvykle se používá
\krab[\#]6.). Samozřejmě, pokud je schéma součástí těla nějaké definice,
znak \krab[\#]6. musí být zdvojený. S~tímto problémem jsme se už setkali
v~sekci o~definování maker~\rsec[def].

Při čtení obsahu jedné položky \TeX{} otevře |\hbox| a tím též vstoupí do
nové úrovně skupiny. V~tomto boxu začne nejprve zpracovávat tokeny
uvedené v~odpovídajícím schématu položky až po znak \krab[\#]6. mimo něj.
Přitom provádí expanzi maker. V~našem příkladě se třeba |\bf| expanduje
na |\fam||\bffam||\tenbf|.  Poté přejde \TeX{} do datové části a čte
(a expanduje) tokeny odtud až po token uzavírající položku (\krab[\&]4. nebo
|\cr|). Pak se vrátí k~druhé části schématu položky za znakem \krab[\#]6. a
dokončí zpracování položky. Tím vytvoří horizontální seznam a uzavře
|\hbox| položky. Takto postupně prochází všechny položky v~datové části
|\halign|. Pro každou z~nich vytvoří speciální |\hbox|.

Poznámka k~mezerám. Při zahájení čtení položky z~datové části jsou
ignorovány všechny mezery až po první token různý od mezery. Další
(případné) mezery v~datové části už jsou významné. V~našem příkladě na
řádku~\lnr[mezerypol] je před slovem {\tt toto} ve druhé položce mezera
ignorována, ale za slovem {\tt střed} mezera zůstává.
Rovněž v~deklaraci řádku se ignoruje mezera za znakem \krab[\&]6., takže
například na řádku~\lnr[defradku] je mezera za tímto znakem nevýznamná.

Je-li v~datové části v~rámci jednoho řádku méně položek, než je
schémat položek v~deklaraci řádku, boxy zbývajících položek
zůstanou prázdné~--- tj.~nebudou obsahovat ani materiál ze schématu položek.
Viz řádek~\lnr[třetí] v~naší ukázce, kde chybí druhá položka v~datové
části, a tudíž se nevyprodukuje ani slovo \uv{konec}, společné všem
ostatním řádkům.

Pokud je v~datové části na jednom řádku více položek, než je schémat
položek v~deklaraci řádku, dočkáme se chybového hlášení {\tt Extra
alignment tab has been changed to |\cr|}. V~deklaraci řádku ale můžeme
definovat jednoduchou smyčku, která pracuje takto: Po vyčerpání schémat
položek se \TeX{} vrací na schéma
s~tzv.~{\it příznakem návratu\/}\inl[priznaknavratu]
a od něj znovu
postupuje až k~poslednímu schématu. Pak se případně znovu vrací na totéž
schéma s~příznakem návratu, atd. Schéma s~příznakem návratu se značí znakem
kategorie 4 na jeho začátku. Syntakticky to znamená, že znak \krab[\&]4. se
vyskytuje těsně za otevírací závorkou \krab[\char`\{]1. konstrukce
|\halign| (tj. hned první schéma má příznak návratu) nebo se sejdou dva
znaky \krab[\&]4. vedle sebe. To neznačí prázdné schéma, nýbrž se tím praví,
že následující schéma má příznak návratu. V~deklaraci řádku může mít
příznak návratu nejvýše jedno schéma, ke kterému se pak cyklus vrací.
Například:

\beguk Opakování schémat v deklaraci řádku
\halign{1:# & 2:# && 3:# & 4:# \cr
  a & b & c & d & e & f & g & h \cr
  i \cr
  j & k~& l & m & n \cr}
\enduk

dává na výstupu:

$$\vbox{
\halign{1:# & 2:# && 3:# & 4:# \cr
  a & b & c & d & e & f & g & h \cr
  i \cr
  j & k~& l & m & n \cr}} \qquad \endpar
$$

Nyní přejdeme k~výkladu nejdůležitější vlastnosti primitivu |\halign|.
Tabulka je sestavována ve dvou průchodech. V~prvním průchodu se
vytvoří boxy jednotlivých položek způsobem popsaným výše, přičemž se na
tyto boxy neklade žádný požadavek na stažení nebo roztažení. Pak \TeX{}
spočítá maximální šířku takových boxů v~každém sloupci položek. Pro
$i$-tý sloupec budeme značit toto maximum znakem~$w_i$.

V~druhém průchodu sestavuje \TeX{} jednotlivé řádky tabulky tak, že do
vnějšího vertikálního seznamu nejprve zavede <vertikální materiál>
sestavený z~případného |\noalign|, který byl hned za prvním |\cr|. Dále
vloží první řádek tabulky jako |\hbox| obsahující: \uv{$t_0$, box první
položky upravený na šířku $w_1$, dále $t_1$, pak box druhé položky
upravený na šířku $w_2$, $t_2$ atd. až po box poslední položky upravený na
šířku $w_n$ a konečně $t_n$}. Znaky $t_0$ až $t_n$ zde znamenají výplňky
typu <glue> odpovídající hodnotě |\tabskip| před tabulkou, mezi položkami a
za tabulkou. Podrobněji o~nich viz níže. Tím je první řádek tabulky
zaveden do vnějšího vertikálního seznamu. Pak se vloží do tohoto
seznamu případně <vertikální materiál> z~|\noalign| uvedeném za prvním
řádkem, pak se analogicky vloží |\hbox| s~druhým řádkem tabulky apod. Protože
jsou $t_0$ až $t_n$ a $w_1$ až $w_n$ v~každém řádku stejné, budou
jednotlivé položky lícovat pod sebou.

Při novém stanovení šířky boxu položky na šířku $w_i$ je potlačeno hlášení
{\tt Underfull hbox}. Může se tedy stát, že \TeX{} například vytvoří 
{\it bez varování\/} položku s~obludnými mezislovními mezerami.
Obvykle ale bývají v~boxech položek pružné výplňky typu <glue>, které
jsou často specifikovány ve schématu položky a kterými programátor maker řídí
způsob umístění položky. Například na řádku~\lnr[defradku] je řečeno, že na
konci každé položky z~prvního sloupce bude |\hfil|, takže položky budou
zarovnány na levou zarážku. V~druhém sloupci je proměnlivý text obehnán
výplňkem |\hfil| z~obou stran, takže tento text se bude centrovat, zatímco
opakovaný text \uv{konec} bude usazen přesně pod sebou. \endpar

{\subpar Registr |\tabskip|.}
Jak jsou stanoveny hodnoty mezisloupcových výplňků typu <glue> označované
výše jako $t_0$ až $t_n$\.? Tyto hodnoty se získají z~hodnoty registru
|\tabskip| v~době čtení deklarace řádku. V~našem příkladě na
řádku~\lnr[defradku] je tato hodnota rovna {\tt 2em plus 5em}, to znamená,
že $t_0$, $t_1$ i $t_2$ budou mít tuto hodnotu. Primitiv |\halign| může
před otevírací závorkou mít určenu šířku tabulky klíčovým slovem
{\tt to} nebo {\tt spread}, jak to známe ze specifikací boxů. Pokud je tato
specifikace uvedena, pak začne pracovat roztahovatelnost nebo
stahovatelnost příslušných mezisloupcových výplňků $t_i$ tak, aby například
při |\halign|" to10cm{...}" byla celková šířka každého řádku (a~tím i celé
tabulky)~10\,cm.

Zbývá otázka, jak zařídit, aby se jednotlivé hodnoty $t_0$ až $t_n$ od sebe
lišily. K~tomu si budeme muset upřesnit algoritmus čtení 
deklarace řádku v~okamžiku, kdy \TeX{} zahajuje zpracování |\halign|.
Při tomto čtení se {\it neprovádí expanze\/} a jednotlivé řady tokenů
odpovídající jednotlivým schématům položek si \TeX{} uchovává ve vyhrazené
paměti pro použití při pozdějším čtení datové části.
Při čtení deklarace řádku si \TeX{}
speciálním způsobem všímá pouze těchto tokenů: "\cr" pro konec čtení
deklarace řádku, "&" pro oddělení jednotlivých schémat položek (token
kategorie 4), "#" pro místo, kam vložit proměnlivý text položky (token
kategorie 6), |\tabskip| pro změnu registru |\tabskip| mezi jednotlivými
položkami a konečně |\span|\pgl[spantoken]\inl[span], pokud je nutno
následující token v~tuto chvíli expandovat. Za tokenem |\tabskip| výjimečně
probíhá expanze tak dlouho, až hlavní procesor získá celou hodnotu podle
syntaktického pravidla <glue>, kterou přiřadí do registru. Celá část tvaru
|\tabskip|<equals><glue> je ze schématu položky vyhozena jako zbytečná.
Nicméně vnitřní registr |\tabskip| už byl změněn a o~to šlo. Hodnota $t_0$
je pak rovna hodnotě |\tabskip| v~okamžiku vstupu do konstrukce |\halign| a
hodnota $t_i$ je rovna hodnotě |\tabskip| v~okamžiku ukončení čtení
$i$-tého schématu položky.  Uvedeme si příklad:

\beguk Parametr \tabskip v \halign
\tabskip=0pt % to je sice implicitní, ale jistota je jistota.
        % (pro tyto účely lze použít zkratku v plainu: \ialign)
\halign to\hsize{\tabskip=0pt plus1fil       #\hfil
                &\tabskip=0pt          \hfil #\hfil \cr
   toto bude  & a toto bude zase\cr
   vlevo      & vpravo\cr
   na stránce & na stránce\cr}
\enduk

Uvedený kód nám dá:
\bigskip

\tabskip=0pt % to je sice implicitní, ale jistota je jistota.
\halign to\hsize{\tabskip=0pt plus1fil       #\hfil
                &\tabskip=0pt          \hfil #\hfil \cr
   toto bude  & a toto bude zase\cr
   vlevo      & vpravo\cr
   na stránce & na stránce\cr}

V tomto příkladě je $t_0${\tt=0pt}, $t_1${\tt=0pt plus1fil} a
$t_2${\tt=0pt}. Poznamenejme, že po sestavení tabulky se hodnota |\tabskip|
vrací do hodnoty $t_0$, protože všechny ostatní změny tohoto registru
proběhly lokálně v~rámci skupiny |\halign|"{...}". \endpar

Uvedeme si několik příkladů, které jsou zajímavým důsledkem skutečnosti, že
v~době čtení deklarace řádku se neprovádí expanze. Chceme zařadit
pod sebe desetinná čísla, aby byla zarovnána podle desetinné čárky:

\beguk Desetinné čárky budou pod sebou
\halign{\catcode`\,=4\hfil #,&#\hfil &\quad --- #\hfil \cr !inl[licuj]
  127,34    & tady je text, kde čárka nevadí \cr
    3,1415  & vidíme, že čísla mají čárku pod sebou \cr}
\enduk

A~na výstupu skutečně máme:

$$
\vbox{\halign{\catcode`\,=4\hfil #,&#\hfil &\quad --- #\hfil \cr
  127,34    & tady je text, kde čárka nevadí \cr
    3,1415  & vidíme, že čísla mají čárku pod sebou \cr}}
$$

Ukážeme si, jak to funguje. Při čtení deklarace řádku se
do prvního schématu uloží

\beguk
!krab[catcode].!krab[`]12.!krab[,].!krab[=]12.!krab[4]12.!krab[hfil].!krab[#]6.!krab[,]12.
\enduk

Čárka má ve schématu kategorii 12, protože se zatím neprovedlo
přiřazení |\catcode|. K~tomuto přiřazení dojde, až \TeX{} otevře |\hbox|
první položky a zpracuje uvedenou sadu tokenů ze schématu. Proto
uživatelova čárka v~datové oblasti má stejnou funkci, jako separátor mezi
položkami. První položka má tedy proměnlivý text \uv{{\tt 127}} a druhá
\uv{{\tt 34}}. Další čárky uvedené ve třetí položce (a~případně i ve druhé)
už mají zase kategorii 12, protože se uzavřením boxu první položky ukončila
skupina.

Stejný výsledek bez změny kategorií můžeme dosáhnout též následujícím
trikem:

\beguk Desetinné čárky budou pod sebou, jiné řešení
\def\cislo#1,{#1,&}
\halign{\hfil\cislo#&#\hfil &\quad --- #\hfil \cr
  127,34    & tady je text, kde čárka nevadí \cr
    3,1415  & vidíme, že čísla mají čárku pod sebou \cr}
\enduk

Makro "\cislo" bude expandovat až při sestavování položky a
nikoli v~době načítání deklarace řádku. \endpar

V~dalším příkladě budeme ve schématu položky znovu přepínat kategorie a
umožníme uživateli psát v~rovnicích pouze znak {\tt =}, který bude zarovnán
pod sebou. Nepotřebujeme používat \LaTeX{}ovské konstrukce typu
"&=&". Příklad následuje i s~ukázkou, jak to dopadne, ovšem už bez
vysvětlování. Všimněte si, že je zde použita smyčka v~deklaraci řádku
(příznak návratu má první schéma), takže uživatel může psát vedle sebe
libovolné množství rovnic (pokud se mu ovšem vejdou na řádek).

\beguk Znak "=" jako oddělovač v tabulce
\let\eq== \thickmuskip=10mu % aby kolem bin.rel. bylo více místa!inl[rovnice]
$$\vbox{\halign{&\catcode`\==4 \hfil$#$&${}=#$\hfil\qquad\cr
      z^2 =  x^2+y^2      & a+b    = c+d+e          & \sigma\cr
\sum_{i\eq0}^\infty x_i=T & \alpha = \beta = \gamma & \tau \cr}}
$$
\enduk

\bigskip
\hbox to\hsize{\hfil
\let\eq== \thickmuskip=10mu
\vbox{\halign{&\catcode`\==4 \hfil$#$&${}=#$\hfil\qquad\cr
        z^2 =  x^2+y^2      & a+b    = c+d+e          & \sigma \cr
\sum_{i\eq0}^\infty x_i = T & \alpha = \beta = \gamma & \tau   \cr}}
\hfil}

Vidíme, že každá legrace něco stojí. Nemusíme sice psát "&=&", zato ale
v~případě potřeby symbolu {\tt=} v~jiném
smyslu, než pro zarovnání (viz index sumy), musíme použít náhradní symbol.
Zde jsme zavedli symbol "\eq". Kdybychom použili řešení bez změny kategorie
(podobně jako v~příkladě se zarovnáváním podle desetinné čárky), pak můžeme
psát i "\sum_{i=0}". Zase ale budeme mít problémy se sloupcem "\sigma" a
"\tau". \endpar

{\subpar Příklad na automatické generování schémat položek.}\inl[genschema]
V~tomto příkladě ukážeme makra, která umožní jisté pohodlí při
zadávání matic. Uživatel napíše:

\beguk
$$\matice{cc|r}{1&p& -1\\ p-1&2& 0} \sim
  \matice{cc|c}{1&p& -1\\ 0&(p-2)(p+1)& 1-p}. $$
\enduk

a na výstupu dostane:

\begingroup
\catcode`\|=12
\newtoks\schemata \newdimen\tbs
\def\addschema#1{\schemata\expandafter{\the\schemata#1}}
\def\addseparator{\ifx\params\empty \else \addschema&\fi}
\def\vemznak#1#2;{\def\params{#2}\csname P:#1\endcsname}
\expandafter\def\csname P:c\endcsname{%
   \addschema{\hfil$##$\hfil}\addseparator}
\expandafter\def\csname P:r\endcsname{%
   \addschema{\hfil$##$}\addseparator}
\expandafter\def\csname P:|\endcsname{%
   \addschema{\vrule\hskip2\tbs}}
\def\genschemata{\ifx\params\empty
   \else\expandafter\vemznak\params;\expandafter\genschemata\fi}
\tbs=.4em  \let\\=\cr
\def\matice#1#2{\left(\vcenter{\offinterlineskip
   \schemata={\tabskip=2\tbs \strut}
   \def\params{#1}\genschemata \tabskip=\tbs
   \halign{\span\the\schemata \tabskip=\tbs \cr #2\crcr}}\right)}
$$\matice{cc|r}{1&p& -1\\ p-1&2& 0} \sim
  \matice{cc|c}{1&p& -1\\ 0&(p-2)(p+1)& 1-p}. $$
\endgroup

Vidíme, že uživatel stanoví v~jednoduché hlavičce vlastnosti sloupců
({\tt c}:~centruj, {\tt r}:~zarovnej doprava a {\tt\char`\|}: nakresli
svislou čáru). To známe z~\LaTeX{}u. Naším úkolem bude přečíst tyto údaje a
vygenerovat odpovídající schéma pro |\halign|. Navrhneme proto následující
makro:

\beguk Automatické generování schémat položek pro \halign
\newtoks\schemata \newdimen\tbs
\def\addschema#1{\schemata\expandafter{\the\schemata#1}}
\def\addseparator{\ifx\params\empty \else \addschema&\fi}
\def\vemznak#1#2;{\def\params{#2}\csname P:#1\endcsname} !lnl[vemznak]
\expandafter\def\csname P:c\endcsname{%
   \addschema{\hfil$##$\hfil}\addseparator}
\expandafter\def\csname P:r\endcsname{%
   \addschema{\hfil$##$}\addseparator}
\expandafter\def\csname P:|\endcsname{%
   \addschema{\vrule\hskip2\tbs}}
\def\genschemata{\ifx\params\empty
   \else\expandafter\vemznak\params;\expandafter\genschemata\fi}
\tbs=.4em  \let\\=\cr  !lnl[tbs]
\def\matice#1#2{\left(\vcenter{\offinterlineskip
   \schemata={\tabskip=2\tbs \strut} !lnl[prvnischema]
   \def\params{#1}\genschemata \tabskip=\tbs
   \halign{\span\the\schemata \tabskip=\tbs \cr #2\crcr}}\right)}!lnl[uzijschema]
\enduk

<Deklaraci řádku> pro |\halign| postupně vytvoříme v~proměnné "\schemata".
Přitom uživatelovy parametry máme v~makru "\params". Pomocné makro
"\addschema" přidá do proměnné "\schemata" co potřebujeme a "\addseparator"
přidá oddělovač \krab[\&]4.. Makro "\vemznak" z~řádku~\lnr[vemznak] vezme
z~parametrů "\params" další znak a expanduje na \krab[P:<znak>].. Právě
tato makra udělají potřebnou práci; například \krab[P:c]. přidá do proměnné
"\schemata" text:~\uv{"\hfil$#$\hfil&"}.

Makro "\genschemata" opakovaně bere token z~"\params" a přidává
odpovídající úseky do proměnné "\schemata". Na řádku~\lnr[uzijschema]
je obsah proměnné "\schemata" použit primitivem "\halign".
Výchozí hodnota této proměnné (řádek~\lnr[prvnischema]) obsahuje vertikální
podpěru (|\strut|), protože jednotlivé řádky budou na sebe těsně
navazovat. Změnou definice |\strut| můžeme změnit řádkování v~matici.
Konečně změnou údaje "\tbs" (řádek~\lnr[tbs]) měníme poloviční
vzdálenost mezi sloupci. Registr |\tabskip| je totiž na okrajích matice
roven "\tbs" a mezi sloupci má hodnotu "2\tbs". \endpar

{\subpar Výjimky z pravidel daných schématy položek.}
V~datové části potřebujeme občas
vložit položku, která nepodléhá formátování podle schématu položky.
V~jednoduchých případech stačí, když do místa položky vložíme výplněk typu
<glue>, který spolupracuje s~výplňky, použitými ve schématu položky.
Například, pokud ve schématu položky máme zarovnávání nalevo implementováno
jako "#\hfil", pak položka |\hfil|" A" bude výjimečně centrovaná a položka
|\hfill|" A" nám odcestuje doprava.

Mocnějším nástrojem je primitiv |\omit|, který musí po expanzi proměnlivé
části položky přicházet (po ignorování mezer) jako první. Pokud je tomu
tak, pak se box položky vyplní pouze materiálem z~datové části a
odpovídající schéma položky je ignorováno.

Nejmocnějším nástrojem je primitiv |\span|.\inl[span]
S~ním jsme se už setkali při
výkladu způsobu čtení schémat položek na straně~\pgr[spantoken]. Jeho
výskyt v~datové části |\halign| má ale docela jiný (a~pro nás podstatně
zajímavější) význam. Sekvence |\span| zde může nahradit libovolný separátor
typu "&". Tiskový materiál pro sousední položky spojené primitivem |\span|
pak mají společný sdílený box. V~případě, že není použit |\omit|, obsahuje
materiál společného boxu nejen datovou část položek, ale i schémata
příslušných položek. Bývá obvyklé spojit |\span| s~|\omit|. Například:

\beguk
.. & \omit \span \omit \span \omit Tento text obsazuje prostor
 tří za sebou jdoucích položek, jsou ignorována schémata& ..\cr
\enduk

Místo |\omit||\span|"...\omit" lze použít stručnější zápis pomocí makra
plainu |\multispan| (viz část~B).

\TeX{} může mít jisté potíže vložit materiál položky vytvořený pomocí
|\span| do struktury boxů zarovnaných pod sebou o~šířkách
$w_1$ až $w_n$. Předpokládejme například, že materiál ze |\span| nahrazuje
položky druhého až čtvrtého sloupce, tedy nahrazuje boxy o~šířkách $w_2$,
$w_3$ a $w_4$. Dále nechť $g_2$ a $g_3$ jsou základní rozměry (tj. bez
roztažení a stlačení) z~|\tabskip| brané z~hodnot $t_2$ a $t_3$.
Spočítejme si šířku místa, kam by měl být vložen box s~materiálem ze
|\span| v~našem příkladě (položky 2 až 4):

$$ W = w_2 + g_2 + w_3 + g_3 + w_4 $$

Pokud přirozená šířka boxu s~materiálem z~našeho |\span|
je menší nebo rovna hodnotě $W$, pak se do výsledného řádku tabulky
zařadí tento box, ale přepočítán na šířku~$W$ (pracují pružné výplňky
uvnitř boxu). Tento box nahrazuje v~řádku tabulky obvyklý materiál typu: 
\uv{box $w_2$, $t_2$, box $w_3$, $t_3$ a box $w_4$}. 
Pokud dále dojde ke stlačení či roztažení
výplňků |\tabskip| v~místě $t_2$ a $t_3$ (protože je použit |\halign|" to"
nebo |\halign|" spread"), pak se dodatečně upraví nová šířka našeho boxu ze
|\span| a ke slovu se znovu dostanou pružné výplňky uvnitř tohoto boxu.

Výše popsané chování je v~souladu s~tím, co od konstrukcí |\span|
můžeme očekávat. Problémy ovšem nastávají, pokud přirozená šířka materiálu
ze |\span| je {\it větší\/} než rozměr $W$.\pgl[wnasili]
Pak totiž dojde k~nejhoršímu a \TeX{} zvětší nějakou hodnotu $w_i$ tak, aby
rozměr $W$ byl roven přirozené šířce materiálu ze |\span|. Zjistit, na
kterém sloupci bude toto ukrutné násilí provedeno, je možné prostudováním
poměrně složitého algoritmu z~\TeX{}booku ze strany 245.\inl[texbook]
Většinou nás to
ale neuklidní, protože ten sloupec obvykle za nic nemůže.
\TeX{} provádí popsanou změnu hodnoty $w_i$ dříve, než začne vkládat
první řádek tabulky do výsledného vertikálního seznamu. Tím není narušen
základní požadavek, aby položky jednotlivých sloupců lícovaly pod sebou.
S~problémem se ještě setkáme níže v~našich ukázkách. \endpar

{\subpar Čtení, expanze a zpracování položek v~datové části.}
Než \TeX{} začne expandovat první část schématu
položky, vyžádá si z~datové části položky (po přeskočení mezer) jeden
token od expand procesoru. Z~tohoto tokenu se může vyklubat (po
expanzi) |\omit| nebo |\noalign|. V~takovém případě víme, že se \TeX{}
bude chovat jinak.  Pokud se tak nestalo, \TeX{} začne expandovat a
v~hlavním procesoru provádět první část schématu, pak naváže na již
expandovaný token z~datové části a dále se pustí do čtení a expanze
případného dalšího obsahu položky.

Jakmile se při zpracování datové části položky objeví token kategorie
4 nebo |\cr| nebo |\span|, \TeX{} {\it okamžitě\/} nahradí tento token
druhou částí schématu.  K~této události
může dojít na různých úrovních zpracování vstupní informace. Například
pokud se makro v~datové části položky expanduje na token \krab[\&]4., pak
\TeX{} přejde k~druhé části schématu po expanzi tohoto makra.
Pokud je ale načítán text parametru makra a v~tomto textu se
vyskytne \krab[\&]4., pak je už v~průběhu načítání parametru provedena
záměna za druhou část schématu a čtení parametru pokračuje z~druhé části
schématu. Do parametru tedy token \krab[\&]4. nevstupuje. Tuto skutečnost
ilustrujeme na příkladě:

\beguk Ilustrace expanze schémat a datových částí tabulky
\def\a#1\zarazka{\message{#1}} \let\zarazka=\relax
\halign{\a X#Y\zarazka & #Z\zarazka\cr
  abc& de\cr
  fgh& i\a jkl\cr
  mno& \a pqr\cr
  stu& xyz\cr}
\enduk

První sloupec je bezproblémový. Tam se při čtení parametru makra "\a"
(zapsaného v~první části schématu) 
plynule přejde na datovou část položky a po dosažení tokenu
\krab[\&]4. zpět na druhou část schématu. 
Takže do parametru "#1" vstupuje například z~prvního řádku 
text \uv{{\tt XabcY}}.
Druhý sloupec je podstatně zajímavější. V~prvním řádku druhého sloupce se
ještě neděje nic neobvyklého a "\zarazka" ve schématu pracuje jako |\relax|.
V~druhém řádku druhého sloupce se při načítání parametru makra "\a" promění
token |\cr| v~druhou část schématu, takže do "#1" vstupuje \uv{{\tt jklZ}}.
Ve třetím řádku druhého sloupce nám to havaruje. Je to tím, že první
token z~datové části (v~tomto případě se jedná o makro "\a") 
je expandován dřív, než vůbec začne zpracování
položky. V~té chvíli ještě není |\cr| ani \krab[\&]4. nijak
interpretováno. Do parametru "#1" se tedy dostává "pqr\cr stu& xyz\cr}"
a obdržíme chybu {\tt Argument of "\a" has an extra~"}"}. Abychom tento
problém odstranili, definujme makro "\a" jinak:

\beguk
\def\a{\relax\A} \def\A#1\zarazka{\message{#1}} !endpar
\enduk

Při načítání parametru makra v datové části položky se při výskytu "&" nebo
"\cr" provede přechod do závěrečné části schématu položky jen
za podmínky, že se nezpracovává vnořená skupina. Smysl
této dodatečné podmínky osvětlí příklad:

\beguk Značky & a \cr v parametrech maker
$$\matrix{\pmatrix{1&2\cr 3&4}\cr \pmatrix{5\cr 6}}$$
%                   ^  ^   ^                 ^
% Označené "&" a "\cr" neukončí položku vnější \matrix, protože
% uvnitř skupiny je potlačeno ukončení čtení položky.
\enduk

Vidíme, že díky potlačení konce položky uvnitř skupiny je možno
jako text parametru makra |\pmatrix| zavést \uv{"1&2\cr 3&4"}. Lze tedy
psát vnořené tabulky typu |\halign| i prostřednictvím maker, které si do
parametru načtou datovou část všech položek současně.
V~našem příkladě je takovým makrem |\pmatrix| z~plainu. \endpar

{\subpar Tabulky s linkami |\vrule|, |\hrule|.}
V~závěru této sekce se zaměříme na způsoby, jakými se do
tabulek |\halign| zavádějí linky. Uvidíme, že věc není na první
pohled vůbec jednoduchá. Dovednost vytvářet takové tabulky je prvním
stupněm umění makrojazyka \TeX{}u. Vrcholným uměním pak je dovednost
vytvářet makra tvořící rozhraní mezi primitivem |\halign| a uživatelem,
který nechce nebo nemůže přemýšlet v~ka\-te\-go\-riích \uv{box, kern,
glue}. Příkladem takového umění je makro pro prostředí "tabular"
v~\LaTeX{}u nebo ještě propracovanější makra pro tabulky od Michaela
Spivaka.

Nejprve uvedeme pravidla, podle kterých \TeX{} zachází s~linkami
|\hrule| a |\vrule| při sestavování tabulky. Pokud se v~boxech položek
vyskytovaly v~první hladině (tj. nikoli uvnitř dalších boxů) linky
|\vrule| s~neurčitou výškou nebo hloubkou, jsou tyto údaje dopočítány na
výšku a hloubku řádku tabulky jako celku.

Pokud se vyskytovaly ve <vertikálním materiálu> z~|\noalign| linky
|\hrule| s~neurčitou šířkou, pak po kompletaci tabulky získají tyto linky
definitivní šířku rovnou šířce tabulky a dalšímu případnému prodlužování do
šířky vnějšího |\vbox|u nepodléhají. \endpar

Svislé linky se většinou do tabulek zařazují jako |\vrule| uvnitř
schémat položek. Vodorovné linky se do tabulek obvykle vkládají pomocí
|\noalign|"{\hrule}". To nám ovšem zcela rozhodí řádkování~--- viz náš první
příklad na straně~\pgr[prvnitab]. Vkládat do |\noalign| ještě další mezery
(jako ve zmíněném prvním příkladě, řádek~\lnr[mezerypol]) není vhodné
řešení, protože bychom neměli napojeny vertikální linky nad sebou
vytvořené v~jednotlivých položkách pomocí |\vrule|. Z~tohoto důvodu je
bezpodmínečně nutné použít při sazbě tabulek s~čárami makro
|\offinterlineskip|, které nastavuje |\baselineskip| na nulu a dále je
nutno vypodložit každý řádek tabulky neviditelnou podpěrou typu |\strut|.
Takovou podpěru zařadíme do schématu některé položky. Bez této podpěry
bychom měli řádky tabulky (1) velmi nízké, (2) byly by přilepeny jeden na
druhý a (3) nebyly by stejně vysoké. \endpar

Tabulka vytvořená pomocí |\halign| může být zlomena \uv{v~půli}
stránkovým zlomem. Je to z~toho důvodu, že výsledek práce |\halign| jsou
jednotlivé řádky vkládané do vertikálního seznamu, přičemž tyto řádky mají
mezi sebou <glue> z~|\baselineskip| nebo z~|\lineskip|.
V~těchto místech může dojít ke stránkovému zlomu. Abychom tomu zabránili,
dáme obvykle celou tabulku do |\vbox|u.

Jestliže vkládáme za každý řádek "\noalign{\hrule}", tabulka se nikdy
nezlomí. V~takovém případě totiž mezi řádky chybí zmíněný výplněk <glue>, ve
kterém by se mohla tabulka zlomit. Chceme-li dovolit stránkový zlom, pišme
třeba |\noalign|"{"|\hrule||\penalty|"50}".
%
Pokud dovolíme stránkový zlom uvnitř tabulky, máme vodorovnou čáru obvykle
za posledním řádkem předchozí strany, ale už ne nad prvním řádkem strany
následující. A~už vůbec se nám na následující straně neopakuje hlavička.
Řešení tohoto problému uvádíme na jiném místě této knihy (viz
sekci příkladů výstupních rutin~\rsec[exoutput], strana~\pgr[longtable]).
\endpar

Půjdeme rovnou na věc a ukážeme si sazbu tabulky s~jednoduchými i dvojitými
linkami.

$$
\vbox\bgroup\offinterlineskip
\def\vvrule{\vrule \kern1.4pt \vrule}
\def\\{\crcr\omit\vvrule\hrulefill\vvrule&
            \multispan2\hrulefill\vvrule\cr}
\def\vkern{\multispan3\vrule height1.2pt \hfil \vrule}
\def\strut{\vrule height11pt depth 5pt width0pt }
\halign {\vvrule\quad#\hfil\quad\vvrule &\quad\hfil#\quad\vrule
       &\quad\hfil#\quad\vvrule\strut \cr  % konec deklarace
\noalign{\hrule} \vkern\\
& % první položka (vlevo od "Prodej zboží") je prázdná
\multispan2\hfil\quad\bf Prodej zboží\quad\hfil \vvrule\strut\cr
\hfil\vbox to0pt{\vss\hbox{\bf Měsíc}\vss}%
        &\multispan2\hrulefill \vvrule\cr
% Vlastní data -------------------
           & \bf A\hfil & \bf B\hfil \\ \vkern\\
% --------------------------------
    Leden  &         865&    16\\
    Únor   &         917&     8\\
    Březen &        1036&    18\\
\vkern\cr\noalign{\hrule}}\egroup \pgl[linkytab]
$$

Tato tabulka byla vytvořena takto:

\beguk Ukázka tabulky s čárami
\bgroup\offinterlineskip !inl[carovatab]
\def\vvrule{\vrule \kern1.4pt \vrule}
\def\\{\crcr\omit\vvrule\hrulefill\vvrule&
            \multispan2\hrulefill\vvrule\cr}
\def\vkern{\multispan3\vrule height1.2pt \hfil \vrule}
\def\strut{\vrule height11pt depth 5pt width0pt }
\halign {\vvrule\quad#\hfil\quad\vvrule &\quad\hfil#\quad\vrule
       &\quad\hfil#\quad\vvrule\strut \cr  % konec deklarace
\noalign{\hrule} \vkern\\
& % první položka (vlevo od "Prodej zboží") je prázdná
\multispan2\hfil\quad\bf Prodej zboží\quad\hfil \vvrule\strut\cr!lnl[vvrulecr]
\hfil\vbox to0pt{\vss\hbox{\bf Měsíc}\vss}%
        &\multispan2\hrulefill \vvrule\cr
% Vlastní data -------------------
           & \bf A\hfil & \bf B\hfil \\ \vkern\\
% --------------------------------
    Leden  &         865&    16\\
    Únor   &         917&     8\\
    Březen &        1036&    18\\
\vkern\cr\noalign{\hrule}}\egroup
\enduk

Makro "\vvrule" je použito pro dvojitou vertikální linku, která je v každém
řádku tabulky použita vlevo, vpravo a mezi prvním a druhým sloupcem.
Za pozornost stojí makro "\\", které ukončí řádek a vytvoří pod ním
horizontální linku. Tato linka není vedena přes celou šířku tabulky~--- to
by stačilo použít "\cr\noalign{\hrule}". Místo toho je horizontální linka
přerušena v místě dvojitých vertikálních čar. Proto je definována jako
samostatný řádek tabulky, ve kterém je vodorovná čára vykreslena po úsecích
jako |\hrulefill|. Všimneme si, že tento řádek tabulky má výšku pouze
0,4\,pt, takže makra "\vvrule", která jsou v něm použita, vykreslí jen
miniaturní čtverečky. Makro "\vkern" použijeme pro mezeru ve dvojitých
horizontálních linkách. Zde požadujeme, aby se nakreslily kratičké
vertikální linky na levém a pravém okraji tabulky a nic víc. Konečně
"\strut" vytváří neviditelnou podpěru v~každém řádku a volbou výšky a
hloubky této podpěry regulujeme velikosti všech řádků tabulky.

Vlastní tabulka se skládá ze tří sloupců. Ke schématům položek jednotlivých
sloupců asi není co dodat. Poněkud komplikovanější je ovšem sazba záhlaví,
protože zde máme méně standardní strukturu, která není přímo implementována
do |\halign|. Záhlaví sestává ze tří řádků, ačkoli čtenář vysázené tabulky
vidí řádky dva. V~prvním řádku je sloupec \uv{Měsíc} prázdný a text
\uv{Prodej zboží} je zaveden jako |\span| přes dva následující sloupce.
Protože v~použitém |\multispan| je skryto i |\omit|, je potřeba postarat se
ručně o~koncové "\vvrule\strut", viz řádek~\lnr[vvrulecr] v~kódu. Druhý
řádek tabulky má pouze výšku vodorovné linky (tj.~0,4\,pt) a obsahuje
v~první položce text \uv{Měsíc} centrovaný vertikálně na střed tohoto
řádku. Další dvě položky tohoto řádku jsou vyplněny jako |\span| obsahující
natahovací vodorovnou linku |\hrulefill|. Tento trik už známe z~makra~"\\".
Na třetím řádku tabulky je znovu přeskočena oblast \uv{Měsíc} a pokračuje
záhlaví A~a~B. Pak už následují data zapsaná běžným způsobem.

Zkusíme nyní natáhnout tabulku na šířku sazby, takže pišme
|\halign|{\tt~to}|\hsize|. Hodnoty $t_0$ a $t_n$ necháme
nulové, zatímco ostatní hodnoty |\tabskip| mezi jednotlivými sloupci budou
natahovací. Pokud to uděláme bez další úpravy, dočkáme se nepříjemného
výsledku: \bigskip

\vbox\bgroup\offinterlineskip
\def\vvrule{\vrule \kern1.4pt \vrule}
\def\\{\crcr\omit\vvrule\hrulefill\vvrule&
            \multispan2\hrulefill\vvrule\cr}
\def\vkern{\multispan3\vrule height1.2pt \hfil \vrule}
\def\strut{\vrule height11pt depth 5pt width0pt }
\halign to\hsize{\tabskip=0pt plus 1fil\vvrule\quad#\hfil\quad\vvrule
       &\quad\hfil#\quad\vrule
       &\quad\hfil#\quad\vvrule\strut\tabskip=0pt \cr  % konec deklarace
\noalign{\hrule} \vkern\\
& % první položka (vlevo od "Prodej zboží") je prázdná
\multispan2\hfil\quad\bf Prodej zboží\quad\hfil \vvrule\strut\cr
\hfil\vbox to0pt{\vss\hbox{\bf Měsíc}\vss}%
        &\multispan2\hrulefill \vvrule\cr
% Vlastní data -------------------
           & \bf A\hfil & \bf B\hfil \\ \vkern\\
% --------------------------------
    Leden  &         865&    16\\
    Únor   &         917&     8\\
    Březen &        1036&    18\\
\vkern\cr\noalign{\hrule}}\egroup

Vidíme, že některé čáry kreslené pomocí |\hrulefill| nám nezasahují do
prostoru, ve kterém pruží |\tabskip|. Dále šíře prvního sloupce zůstala
stejná, zatímco ostatní sloupce se vizuálně protáhly. Čtenář si sám
rozmyslí, proč se tak stalo.

Abychom překonali tento problém, musíme vnitřní koncepci naší tabulky zcela
přepracovat. Místo tří sloupců budeme pracovat s~osmi sloupci podle
následujícího náčrtu:

$$
\vbox{\halign{&\hbox to2em{\hss#\unskip\hss}\cr
& 1.      && 2.    && 3.&& 4.      && 5.&& 6.      && 7.&& 8.      &\cr
& $\Vert$ && Měsíc &&   && $\Vert$ && A && $\vert$ && B && $\Vert$ &\cr
0   &&   hfil  && hfil  &&  0 &&hfil&&hfil &&  hfil  && hfil &&   0 \cr}}
$$

V prvním řádku tohoto náčrtu je vyznačeno číslo sloupce, ve druhém
je zakreslen charakteristický obsah odpovídajícího sloupce a ve třetím řádku
jsou uvedeny velikosti |\tabskip| mezi jednotlivými sloupci. Vidíme, že
vlastní data budeme zapisovat do druhého, pátého a sedmého sloupce.
V~prvním, čtvrtém a osmém sloupci bude kreslena dvojitá vertikální čára a
v~šestém sloupci čára jednoduchá. Ve třetím sloupci nebude nikdy kresleno
nic~--- tento sloupec bude použit jako záchytný bod pro horizontální linky
|\hrulefill|. Tyto linky budou totiž v~místě \uv{Měsíc} implementovány jako
|\span| přes 1.~až 3.~položku a v~místě \uv{Prodej zboží} budou kresleny
pomocí |\span| přes 4.~až~8.~položku. Takto definované linky budou
obsahovat všechny pružné části tabulky, pocházející
z~mezisloupcových~|\tabskip| a budou se tedy správným způsobem natahovat.
Makro pro naši tabulku nyní vypadá takto:

\beguk Tabulka pracující i s pružným \tabskip
\bgroup\offinterlineskip !lnl[beghalign]!inl[tabskipcary]
\def\vvrule{\vrule \kern1.4pt \vrule}
\def\\{\crcr\multispan3\vvrule\hrulefill&
            \multispan5\vvrule\hrulefill\vvrule\cr}
\def\vkern{\multispan8\vrule height1.2pt \hfil \vrule}
\def\strut{\vrule height11pt depth 5pt width0pt }
\halign to\hsize{\tabskip=0pt plus 1fil\vvrule#&\quad#\hfil\quad
   &\tabskip=0pt#&\vvrule#\tabskip=0pt plus1fil&\quad\hfil#\quad
   &\vrule#&\quad\hfil#\quad &\vvrule#\strut\tabskip=0pt\cr
\noalign{\hrule} \vkern\\
&&&&\multispan3\hfil\quad\bf Prodej zboží\quad\hfil &\cr !lnl[zboží]
&\hfil\vbox to0pt{\vss\hbox{\bf Měsíc}\vss}%
        &&\multispan5\vvrule\hrulefill\vvrule\cr
% Vlastní data -------------------
&           &&& \bf A\hfil && \bf B\hfil &\\ \vkern\\
% --------------------------------
&    Leden  &&&         865&&    16&\\
&    Únor   &&&         917&&     8&\\
&    Březen &&&        1036&&    18&\\
\vkern\cr\noalign{\hrule}}\egroup   !lnl[endhalign]
\enduk

%A~výsledek vypadá následovně:

\bigskip
\vbox\bgroup\offinterlineskip
\def\vvrule{\vrule \kern1.4pt \vrule}
\def\\{\crcr\multispan3\vvrule\hrulefill&\multispan5\vvrule\hrulefill\vvrule\cr}
\def\vkern{\multispan8\vrule height1.2pt \hfil \vrule}
\def\strut{\vrule height11pt depth 5pt width0pt }
\halign to\hsize{\tabskip=0pt plus 1fil\vvrule#&\quad#\hfil\quad
    &\tabskip=0pt#&\vvrule#\tabskip=0pt plus1fil&\quad\hfil#\quad &\vrule#&\quad\hfil#\quad
    &\vvrule#\strut\tabskip=0pt \cr  % konec deklarace
\noalign{\hrule} \vkern\\
&&&&\multispan3\hfil\quad\bf Prodej zboží\quad\hfil &\cr
&\hfil\vbox to0pt{\vss\hbox{\bf Měsíc}\vss}%
        &&\multispan5\vvrule\hrulefill\vvrule\cr
% Vlastní data -------------------
&           &&& \bf A\hfil && \bf B\hfil &\\ \vkern\\
% --------------------------------
&    Leden  &&&         865&&    16&\\
&    Únor   &&&         917&&     8&\\
&    Březen &&&        1036&&    18&\\
\vkern\cr\noalign{\hrule}}\egroup

Myslím, že teprve v~této ukázce si čtenář povšiml další záludnosti.
Nadpis~{\bf B} není uprostřed sloupce\.! Je to tím, že přirozená
šířka položky \uv{Prodej zboží} je větší, než součet přirozených šířek
sloupců A~a B a hodnot |\tabskip| mezi nimi. Přirozená šířka |\tabskip| je
totiž 0\,pt. Jak jsme uvedli na straně~\pgr[wnasili], \TeX{} v~takových
situacích pozmění nějakou hodnotu $w_i$. V~našem případě změnil hodnotu
$w_7$ odpovídající šířce sloupce~B. Zatímco v~ukázce na
straně~\pgr[linkytab] nám to vůbec nevadilo a dokonce nám tato změna šířky
sloupce~B připadala přirozená, zde to bije do očí, protože prostor
v~tabulce je opticky větší. Abychom překonali tento poslední problém,
použijme na řádku~\lnr[zboží] makro plainu |\hidewidth| následujícím
způsobem:

\beguk
&\multispan3\hfil\hidewidth\bf Prodej zboží\hidewidth\hfil&\cr
\enduk

Tím bude mít náš |\span| přirozenou šířku zápornou a problém odpadne.
Jiným řešením je vložit |\tabskip| s~dostačující přirozenou šířkou, aby
šířka |\span| byla menší než napočítaná hodnota $W$. V~naší ukázce stačí
volit |\tabskip|{\tt=20pt~plus1fil}. \endpar

V~\TeX{}u je možné také vkládat více konstrukcí typu |\halign| do sebe.
Jinak řečeno: každá položka tabulky může obsahovat libovolné množství
dalších tabulek. Aby toho nebylo dost, existuje ještě primitiv
|\valign|,\inl[valign]
který se chová stejně, jako |\halign| na vstupu, ale vyrábí transponovanou
tabulku. Jednotlivé položky se vkládají do |\vbox|ů, |\cr| ukončuje sloupce
a |\vbox|y položek se přepočítají na maximální výšku v~každém řádku.
Hloubky těchto boxů jsou nulové, protože se nejprve provede posun účaří na
spodní část každého boxu (to je jediný algoritmus, který nemá v~případě
|\halign| obdobu). Konečně |\valign| vrací hotové sloupce do horizontálního
seznamu oddělené od sebe materiálem z~|\noalign|. Přitom položky ve
sloupcích jsou od sebe odděleny výplňky typu <glue> podle |\tabskip|.

V~posledním příkladě této sekce ukážeme použití |\valign|. Zůstaneme
u~naší oblíbené tabulky a přidáme jí ještě jeden poněkud větší řádek. Každá
položka tohoto řádku bude obsahovat kratší vodorovnou linku a nad ní i pod
ní bude samostatný odstavec. Odstavce budou mít centrované řádky, ba co
víc, horní odstavce budou centrovány podél vodorovné osy jako celek a
spodní odstavce budou mít na vodorovné pomyslné lince společný první řádek.
Přitom chceme tuto konstrukci udělat nezávislou na množství textu, který se
v~odstavcích může vyskytnout. K~tomu se hodí |\valign|. Tabulka: \bigskip

\vbox\bgroup
\def\setparams{\parindent=0pt \hsize=.27\hsize
  \baselineskip=12pt \lineskiplimit=-10pt \parskip=0pt
  \leftskip=0pt plus3em \rightskip=0pt plus3em
  \parfillskip=0pt}
\let\hi=\hidewidth
\offinterlineskip
\def\vvrule{\vrule \kern1.4pt \vrule}
\def\\{\crcr\multispan3\vvrule\hrulefill&\multispan5\vvrule\hrulefill\vvrule\cr}
\def\vkern{\multispan8\vrule height1.2pt \hfil \vrule}
\def\strut{\vrule height11pt depth 5pt width0pt }
\halign to\hsize{\tabskip=0pt plus 1fil\vvrule#&\quad#\hfil\quad
       &\tabskip=0pt#&\vvrule#\tabskip=0pt plus1fil&\quad\hfil#\quad &\vrule#&\quad\hfil#\quad
       &\vvrule#\strut\tabskip=0pt \cr  % konec deklarace
\noalign{\hrule} \vkern\\
&&& % první položka (vlevo od "Prodej zboží") je prázdná
&\multispan3\hfil\hidewidth{\bf Prodej zboží}\hidewidth\hfil &\cr
&\omit \hfil\vbox to0pt{\vss\hbox{\bf Měsíc}\vss}\hfil
       &&\multispan5\vvrule\hrulefill\vvrule\cr
% Vlastní data -------------------
&           &&& \bf A\hfil && \bf B\hfil &\\ \vkern\\
% --------------------------------
&    Leden  &&&         865&&    16&\\
&    Únor   &&&         917&&     8&\\
&    Březen &&&        1036&&    18&\\
\hbox{\setparams \valign{\vfil\noindent\strut#\strut\vfil
                    &\hrule\bf\noindent\strut#\strut\vfil\cr
      \bf Shrnutí & Závěr\cr
       V~případě zboží~A~vidíme poměrně rychlý
          nárust obratu zvláště v~březnu. Zdá se, že
          to bude tím, že se v~tuto dobu oteplilo.&
          Ponechat a dále sledovat vývoj.\cr
       Pokud jde o~zboží~B, je to mizerné.&
          Zrušit výrobu\cr }%
  \global\setbox3=\lastbox \global\setbox2=\lastbox
  \global\setbox1=\lastbox}%
& \hi\box1\hi &&& \hi\box2\hi && \hi\box3\hi &\\
\vkern\cr\noalign{\hrule}}
\egroup

byla vytvořena stejným způsobem, jako je uvedeno na
řádcích~\lnr[beghalign]--\lnr[endhalign], jen přibyla nová definice
pro formátování jednotlivých odstavců:

\beguk Formátování odstavců v tabulce
\def\setparams{\parindent=0pt \hsize=.27\hsize
  \baselineskip=12pt \lineskiplimit=-10pt
  \leftskip=0pt plus3em \rightskip=0pt plus3em
  \parfillskip=0pt}
\let\hi=\hidewidth
\enduk

a místo řádku \lnr[endhalign] je napsáno:

\beguk Ukázka \valign uvnitř \halign
\hbox{\setparams \valign{\vfil\noindent\strut#\strut\vfil !inl[vhalign]
                    &\hrule\bf\noindent\strut#\strut\vfil\cr
      \bf Shrnutí & Závěr\cr
       V~případě zboží~A~vidíme poměrně rychlý
          nárust obratu zvláště v~březnu. Zdá se, že
          to bude tím, že se v~tuto dobu oteplilo.&
          Ponechat a dále sledovat vývoj.\cr
       Pokud jde o~zboží~B, je to mizerné.&
          Zrušit výrobu\cr }% konec \valign
  \global\setbox3=\lastbox \global\setbox2=\lastbox
  \global\setbox1=\lastbox}%
& \hi\box1\hi &&& \hi\box2\hi && \hi\box3\hi &\\
\vkern\cr\noalign{\hrule}}\egroup % konec \halign a celé skupiny
\enduk

Zde se nejprve nanečisto vyrobí |\hbox| se sloupečky tak, jak je vytvořil
|\valign|. Pak se tyto sloupečky posbírají do boxů "\box1" až "\box3"
a tyto boxy se umístí do položek hlavní tabulky |\halign|.
Kolem každé položky jsme vložili "\hi", což jsme definovali jako
zkratku za |\hidewidth|. Tím máme zaručeno, že tyto položky budou mít
zápornou přirozenou šířku a neovlivní výpočet maxima šířky boxu ve sloupci.
Kdybychom to neudělali, číselné údaje, které jsme sázeli na pravou zarážku
ve sloupcích A~a B, by byly posunuty doprava podle pravé hrany odstavce. Nám
se víc líbí, že jsou uprostřed sloupce, i když na pravou zarážku.
\endpar


