%%  OPmac -- Olsak's PlainTeX macros
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Tech-documentation, Petr Olsak, 2012

\input utf8off \clearmubyte

% This file is encoded in ISO-8859-2 because
% UTF-8 encTeX has a conflict with DocByTeX

\def\projectversion{Dec. 2012}
\def\headtitle{OPmac}

\widowpenalty=10000 
\clubpenalty=10000

\emergencystretch=2em
\hbadness=2200

\showboxbreadth=1500 \showboxdepth=2

\def\csplain{\CS plain}
\def\CS{CS}

\input docby.tex

\setlinecomment{\percent} \noactive{\nb\percent} \noactive{\percent\cbrace}
\noactive{\nb fontdimen}

\title OPmac -- rozšiřující makra plain\TeX{}u

\author Petr Olšák

\centerline{\ulink[http://www.olsak.net/opmac.html]%
                         {www.olsak.net/opmac.html}}

\def\db{\dg\nb}
\def\du#1{\api{\nb#1}}
\let\quotehook=\langleactive
\def\insdef#1 {\ifirst{docby.tex}{def\nb#1 }{^^B\cbrace}{++}}
\def\inssdef#1 {\ifirst{docby.tex}{def\nb#1}{\empty}{+-}}
\bgroup
   \catcode`\[=1 \catcode`]=2 \catcode`\{=12 \catcode`\}=12
   \gdef\obrace[{] \gdef\cbrace[}]
\egroup
\def\indexhook{%
%   Kontrolní sekvence označené šipkou ($\succ$) jsou
%   uživatelskými příkazy. Ostatní kontrolní sekvence jsou
%   v~OPmac interní. 
   Tučně je označena strana, kde je slovo
   dokumentováno, pak následuje seznam všech stran, na kterých se slovo 
   vyskytuje.
%   Uživatelské příkazy mají v seznamu stránek podtržené číslo,
%   což je stránka, kde je příkaz vyložen na uživatelské úrovni.
   \medskip}
\def\nn#1 {\noactive{\nb#1}} \nn insert \nn undefined

\def\cnvbookmark#1{\lowercase{\lowercase{#1}}}
\def\bookmarkshook{\lo ěe \lo šs \lo čc \lo řr \lo žz \lo ýy
   \lo áa \lo íi \lo ée \lo úu \lo ůu \lo óo \lo ňn }
\def\lo #1#2{\lccode`#1=`#2}


\dotoc \bookmarks

\sec Úvod
%%%%%%%%%

OPmac je balík jednoduchých doplňujících maker k plain\TeX{}u umožňující
uživatelům základní La\TeX{}ovou funkcionalitu: změny velikosti písma,
automatickou tvorbu obsahu a rejstříku, práci s {\tt bib} databázemi,
referencemi, možnost proložení referencí hyperlinkovými odkazy atd.


\sec Uživatelská dokumentace
%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Uživatelská dokumentace je zatím v souboru "opmac-u.tex" a "opmac-u.pdf".
Do tohoto místa ji zahrnu později a prolinkuji ji s technickou dokumentací.


\sec Technická dokumentace
%%%%%%%%%%%%%%%%%%%%%%%%%%

Tato část dokumentace je určena pro tvůrce maker, kteří se chtějí zde
uvedenými makry inspirovat a případně je přizpůsobit svému požadavku.
Předpokládá se znalost \TeX{}u, tj. například aspoň zběžná orientace 
v~\TeX{}booku naruby. Na tuto knihu je na mnoha místech odkazováno pod
zkratkou TBN.


\subsec Základní makra
%%%%%%%%%%%%%%%%%%%%%%

Na začátku souboru "opmac.tex" zjistíme, zda není soubor čtený podruhé. V
takovém případě čtení odmítneme. Ptáme se na to, zda je definováno makro \db
OPmacversion, které vzápětí definujeme. Je-li někdo překvapen, proč jsem
nepoužil "\expandafter\endpinput\fi", může si prostudovat TBN, stranu~358,
heslo "\endinput".

\ifirst {opmac.tex}{OPmacversion}{\empty}{+-}

Dva pracovní registry:
\dgn \nb tmpnum
\dgn \nb tmpdim

\inext {newcount}{\empty}{+-}

OPmac nebude nikdy hlásit chyby. Často ale bude psát pomocí \db opwarning 
na terminál varování.

\inext {warning}{\empty}{+-}

Makro \db addto "<makro>{<tokeny>}" přidá na konec "<makra>" dané "<tokeny>".

\inext {addto}{\empty}{+-}

V OPmac budeme pracovat se seznamem \db protectlist, který bude obsahovat
makra, jež chceme mít tzv. robustní, tj. chceme, aby se při "\write" v output
rutině neexpandovala. Každému makru v~seznamu předchází "\doprotect", takže
seznam "\protectlist" vypadá takto:

\def\begtthook{\langleactive}
\begtt
\doprotect<makro1> \doprotect<makro2> ...
\endtt

\def\begtthook{}
Seznam budeme spouštět v output rutině s tím, že "\doprotect" tam bude mít
význam makra, které zařídí, aby jeho parametr získal význam "\relax".
Tím bude zabráněno jeho expanzi.
Naprogramujeme \db addprotect "<makro>", které zařídí vložení "<makra>" do seznamu. 

\inext {protectlist}{\empty}{+-}

Některá makra budou fungovat jen v pdf\TeX{}u při nastaveném "\pdfoutput=1".
Připravíme si tedy test \db ifpdftex, který pak použijeme při čtení souboru
"opmac.tex". Test nikdy nebudeme vkládat do maker, takže při čtení souboru
"opmac.tex" už musí být jasné, zda bude výstup směrován do DVI nebo PDF.
Pozdější změna "\pdfoutput" může způsobit potíže.

\inext {ifpdftex}{\empty}{+-}

Makra \db sdef a \db sxdef umožňují pohodlně definovat kontrolní sekvence
ohraničené pomocí "\csname...\endcsname".

\inext {sdef}{\empty}{+-}

Makro \db adef umožní nastavit znak na aktivní a rovnou ho definovat, což
normálně uvnitř maker není jednoduché (TBN str. 25 a 26). 
Využijeme toho, že "~" je aktivní znak a pomocí
"\lccode" a "\lowercase" jej přepíšeme na požadovaný znak. Dostaneme tím 
aktivní token s požadovanou ASCII hodnotou a tento token definujeme.
Pomocí \db lccodetiezero vrátíme po přiřazení (tj. po provedení definice)
"\lccode" vlnky na původní hodnotu.

\inext {adef}{\empty}{+-}

Makrem \db isdefined "{<jméno>}\iftrue" se ptáme, zda je definovaná
"\csname<jméno>\endcsname". To závěrečné připojené "\iftrue" makro sežere,
ale uživatel ho píše zejména z toho důvodu, aby mu tato konstrukce fungovala
uvnitř vnořených "\if..\fi"

\inext {isdefined}{^^B\cbrace}{++}

Makro \db isinlist "<list>{<tokeny>}\iftrue" zjistí, zda "<tokeny>" jsou
(jako string) obsaženy v~makru "<list>". Přitom sežere "\iftrue" ze stejných
důvodů, jak je uvedeno před chvílí.

\inext {isinlist}{^^B\cbrace}{++}

Makro \db isnextchar "<znak>{<co dělat při ano>}{<co dělat při ne>}" 
pracuje poněkud odlišně od předchozích maker. Zjistí, zda následující znak
je "<znak>" a pokud ano, vykoná vnitřek první závorky, jinak vykoná vnitřek
druhé závorky. Pomocí "\futurelet" uloží zkoumaný znak do "\next" a spustí
\db isnextcharA.

\inext {isnextchar}{\empty}{+-}

Předefinujeme makro \db uv z \csplain{}u. Tam je toto makro navrženo tak, aby
mohlo mít za svůj parametr verbatim text. Důsledkem toho nefunguje správně
kerning. Považuji za lepší mít správně kerning a případné uvozování verbatim
textů řešit třeba pomocí "\clqq...\crqq".

\inext {def\nb uv}{}{+-}

Knuth v souboru "plain.tex" zanechal řídicí sekvenci "\\" v provizorním stavu
(cvičení: podívejte se v jakém). Domnívám se, že je lepší ji dát jednoznačný
význam "\undefined". Některým uživatelům totiž může OPmac připomínat
La\TeX{} a není tedy vyloučeno, že je napadne psát "\\". Měli by na to
dostat jednoznačnou odpověď: {\tt undefined control sequence}.

\inext {undefined}{}{+-}

Do pracovního souboru určeného k novému načtení budeme chtít vložit
komentáře za znakem procento. K tomu potřebujeme mít procento jako obyčejný
znak kategorie 12. Na tento znak se v našem kódu překlopí otazník, Takže 
\db percent expanduje na znak procento s kategorií 12.

\inext {percent}{}{+-}

Makro plain\TeX{}u "\,"
funguje jen v matematické sazbě. Uživatel bude chtít makro často
použít například mezi číslem a jednotkou v textovém módu: "5\,mm", takže
makro předefinujeme.

\inext {kern}{}{+-}


\subsec Globální parametry
%%%%%%%%%%%%%%%%%%%%%%%%%%

Zakážeme vdovy a sirotky a dále nastavíme registry pro listingy tiskového
materiálu na smysluplnější hodnoty, než jsou implicitní.

\inext {widowpenalty}{\empty}{+-}

Následující makra a registry ovlivní chování klíčových maker OPmac způsobem,
jak je popsáno v~komentářích. Mnohé z těchto maker a registrů byly zmíněny v
uživatelské dokumentaci.
\dgn \nb iindent
\dgn \nb ttindent
\dgn \nb ttskip
\dgn \nb ttpenalty
\dgn \nb tthook
\dgn \nb intthook
\dgn \nb iiskip
\dgn \nb bibskip
\dgn \nb tabstrut
\dgn \nb tabiteml
\dgn \nb tabitemr
\dgn \nb vvkern
\dgn \nb hhkern
\dgn \nb multiskip
\dgn \nb colsep
\dgn \nb mnoteindent
\dgn \nb mnotesize
\dgn \nb picdir
\dgn \nb bibtexhook
\dgn \nb chaphook
\dgn \nb sechook
\dgn \nb secchook
\dgn \nb cnvhook
\dgn \nb pghook

\inext {newdimen}{\count=7 \empty}{+-}



\subsec Loga
%%%%%%%%%%%%

V logu \db OPmac je pomocí "\thefontscale" zvětšeno písmeno O. Logo \db CS je
přepsáno beze změny z CS\TeX{}u. Tím snadno vytvoříme i logo \db csplain.

\inext {def\nb OPmac}{\empty}{+-}

Troufám si tvrdit, že logo \db LaTeX (ačkoli je plain\TeX{}isté asi moc
nebudou potřebovat) je v~následujícím kódu daleko lépe řešeno, než v
samotném La\TeX{}u. Počítá totiž ve spolupráci s makrem \db slantcorr 
i se sklonem písma při usazování zmenšeného A. 

\inext {LaTeX}{\empty}{+-}

Loga se občas mohou vyskytnout v nadpisech. Zabezpečíme je tedy proti
rozboření při zápisu do REF souboru.

\inext {addprotect}{\empty}{+-}


\subsec Velikosti fontů, řádkování
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\csplain{} od verze "<Nov. 2012>" definuje makro 
\db resizefont "<fontselector>", které změní
velikost fontu daného svým přepínačem a tento změněný font si ponechá
stejný přepínač. Změna velikosti je dána obsahem makra \db sizespec.
Tam může být například napsáno "at13pt" nebo "scaled800".
Dále CSplain definuje makro 
\db resizeall, které změní velikost
fontů s registrovanými přepínači. Registrování se provádí makrem
\db regfont. Implicitně jsou registrovány přepínače 
 "\tenrm", "\tenit", "\tenbf", "\tenbi", a "\tentt".
Do nových velikostí tedy půjdeme se starými názvy přepínačů
"\ten<něco>" a to slovo "ten" budeme chápat jen jako historický relikt,
který nám ovšem napoví, že kontrolní sekvence je fontovým přepínačem. 

OPmac si zjistí, zda je definovaný "\regfont". Pokud ne, upozorní na starou
verzi \csplain{}u na terminálu a potřebná makra si definuje. Je to kopie
kódu ze souboru "csfontsm.tex" z balíčku \csplain{}.

\inext {regfont}{\empty}{+-}

Implicitně jsou zavedeny \CS{}fonty, takže k nim přidáme AMS fonty z
"ams-math.tex", které vizuálně odpovídají. Později si může uživatel zavést
jiné makro (např. "tx-math.tex") a zavede si třeba i jiné textové fonty. To nezmění
vlastnosti maker v OPmac, pokud nové soubory maker správně předefinují
makra "\setmathsizes[<text>/<script>/<scriptscript>]", "\normalmath" a
"\boldmath".

\inext {ams-math}{\empty}{+-}

Po načtení souboru ams-math disponujeme makry \db regtfm na registraci
různých metrik pro různé designované velikosti fontů a \db whichtfm, které
expanduje na svůj parametr nebo na metriku, která je registrována pro
velikost \db dgsize. Registrace metrik \CS{}fontů je rovněž provedena
v souboru {\tt ams-math.tex}. Jak bylo řečeno,
makro "\resizefont<fontselector>" \csplain{}u změní
velikost fontu "<fontselector>" podle obsahu makra "\sizespec" (viz soubor
{\tt csfontsm.tex}). Toto makro "\resizefont" volá pomocné makro \db
resizefontskipat na odstranění \uv{sizespec} z názvu metriky. Toto pomocné
makro je v OPmac předefinováno s využitím "\whichtfm", takže když před 
voláním makra "\resizefont" připravíme správnou "\dgsize", \TeX{} použije 
metriku designovanou na požadovanou velikost:

\inext {resizefontskip}{\empty}{+-}

Makra "\typosize", "\fontsizex", "\textfontsize", 
"\setbaselineskip" požadují zápis parametru bez jednotky. 
Jednotkou je \db ptunit, která je nastavena na 1pt. Uživatel může
jednotku změnit (např. "\ptunit=1mm" při návrhu plakátu). Dále \db fontdim
je registr, který udává aktuální velikost písma.

\inext {ptunit}{\empty}{+-}

Často budeme potřebovat odstranit jednotku "pt" ve výpisu "\the<dimen>".
Provedeme to pomocí "\expandafter\ignorept\the<dimen>". Protože "\the"
vyrábí znaky pt s kategorií 12, je makro \db ignorept definováno trikem přes
"\lowercase". Z otazníku vznikne p kategorie 12 a z vykřičníku vznikne t.

\inext {lccode}{\empty}{+-}

Makra \db typosize a \db typoscale změní velikosti a nastavují 
výchozí font "\tenrm" a výchozí
matematiku "\normalmath". Nehrajeme si na OFS nebo NFSS, které se snaží ctít
naposledy nastavený duktus a variantu. Uživatel si variantu písma a tučný 
duktus pro matematiku musí nastavit až po zavolání makra na změnu velikosti fontu.

\inext {typosize}{\empty}{+-}

Makro \db fontsizex "[<velikost>]" předpokládá svůj parametr bez jednotky.
Písmeno x v názvu značí, že makro není v uživatelské dokumentaci. Uživatel
totiž může použít "\typosize[<velikost>/]" a třeba ho napadne si nějaké
vlastní makro "\fontsize" definovat.
Je-li parametr "<velikost>" prázdný, makro "\fontsizex" neudělá nic. 
Jinak pomocí "\textfontsize" nastaví
velikost textových fontů. Dále zavolá 
"\setmathsizes[\fontsize/.7\fontsize/.5\fontsize]", ovšem v parametru musí
odstranit jednotky a parametr přichystá pro makro "\setmathsizes"
expandovaný. Příkazem "\normalmath" nakonec nastaví matematické fonty do
nové velikosti.

\inext {fontsizex}{^^B\cbrace}{++}

Makro \db textfontsize "[<velikost>]" předpokládá svůj parametr bez
jednotky. Připojí jednotku "\ptunit", nastaví "\dgsize" a "\sizespec" a
zavolá "\resizeall", což je makro definované v \csplain{}u, které postupně
volá "\resizefont" na všechny registrované fonty.

\inext {textfontsize}{^^B\cbrace}{++}

Makro \db setbaselineskip "[<velikost>]" předpokládá parametr bez jednotky.
Připojí jednotku "\ptunit" a nastaví "\baselineskip" bez dodatečné
pružnosti. Nastaví další registry, které s "\baselineskip" souvisejí.
Záměrně není nastavena "\topskip", "\splittopskip",
"\above/belowdisplayskip". Tyto parametry (globální pro celý dokument) by si
měl uživatel nastavit sám.

\inext {setbaselineskip}{^^B\cbrace}{++}

Makro \db withoutunit "\makro<dimen>" odstraní jednotku z "<dimen>" a takto
upravené číslo vloží do parametru "\makro", které očekává údaj bez jednotky
v~hranaté závorce.

\inext {withoutunit}{\empty}{+-}

Makra \db fontscalex "<factor>", \db textfontscale "<factor>" a
\db scalebaselineskip "<factor>"
přepočítají "<factor>" podle aktuálního "\fontdim" resp. "\baselineskip"
na absolutní jednotku a zavolají odpovídající makro definované před chvílí.

\inext {fontscalex}{\count=3 ^^B\cbrace}{++}

Makro \db thefontsize si alokuje aktuální font do sekvence \db thefont a
tento nový fontový přepínač podrobí změně velikosti "\resizefont".
Makro \db thefontscale přepočítá parametr na absolutní velikost a zavolá
"\thefontsize".

\inext {thefontsize}{\count=2 ^^B\cbrace}{++}

Plain\TeX{}ový \db magstep má na konci "\relax", takže nefunguje jako pouze
expandující makro. My ale "\magstep" očekáváme v parametrech příkazů
"\typoscale" a podobných, proto v "\magstep" je nahrazeno "\relax" méně
drsným "\space". To separuje číselný parametr dostatečně.

\inext {magstep}{\empty}{+-}

Makro \db em přepíná kontextově do odpovídající varianty a ve spolupráci s
makrem "\additcorr" přidává italickou korekci. Makro \db additcorr 
si pomocí "\lastskip" zapamatuje poslední mezeru, pak ji odstraní, vloží
italickou korekci a nakonec vrátí tu odstraněnou mezeru.

\inext {def\nb em}{\empty}{+-}

Fontová makra zabezpečíme proti rozkladu v parametru "\write".

\inext {addprotect}{\empty}{+-}


\subsec Texty ve více jazycích
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Makro \db mtext "<značka>" je zkratkou za \uv{multilingual text}.
Toto makro si podle značky a aktuálního jazyka (dle registru "\language")
vyhledá, jaký text má vypsat.

\inext {mtext}{\empty}{+-}

Jednotlivé texty definujeme pomocí "\sdef{mt:<značka>:<jazyk>}" takto:

\inext {chap:en}{\empty}{+-}

Některé texty jsou zapsány pomocí "\v" notace. Je lepší udělat to takto
než vytvořit soubor "opmac.tex" závislý na kódování. Aby byla tato notace
správně interpretována, spustíme "\csaccents", což je makro \csplain{}u.
Pokud někdo používá OPmac s jiným formátem, než \csplain, neprovede se nic,
protože konstrukce "\csname csaccents\endcsname" se v takovém případě přerodí v
"\relax". Makro "\csaccents" spustíme jen tehdy, pokud je už uživatel
nespustil před "\input opmac". To poznáme podle toho, zda je definovaná
sekvence "\r".

\inext {}{}{+-}

CSplain od verze "Nov. 2012" připravuje následující 
makra, která konvertují číslo "\language" na značku jazyka pro všechny 
jazyky, které mají nataženy vzory dělení slov. 
Pro jistotu (pokud je použita starší verze CSplainu) tuto koverzi
\uv{naučíme} i makro OPmac:

\inext {sdef}{\empty}{+-}



\subsec REF soubor
%%%%%%%%%%%%%%%%%%

OPmac používá pro všechny potřeby (obsah, reference, citace, rejstřík,
poznámky na okraji)
jediný soubor "\jobname.ref" (tzv. REF soubor). 
Navíc, pokud není potřeba, vůbec tento soubor
nezakládá. Často totiž budeme chtít dělat s OPmac jen jednoduché věci a je
únavné pořád na disku kvůli tomu uklízet smetí.

Je potřeba deklarovat souborové deskriptory \db reffile a \db testin:

\inext {newwrite}{\empty}{+-}

Do souboru zapisujeme makrem \db wref "\<sekvence>{<data>}", které vloží do
"\reffile" řádek obsahující "\<sekvence><data>".
Implicitně ale není "\reffile" založeno, takže implicitní hodnota tohoto
makra je \db wrefrelax, tedy nedělej nic.

\inext {wrefrelax}{\empty}{+-}

Makro \db inputref spustíme na konci čtení souboru "opmac.tex", tedy v
situaci, kdy už budeme mít definovány všechny kontrolní sekvence, které se v
REF souboru mohou vyskytnout. Nyní si toto makro jen připravíme.
Makro ověří existenci souboru "\jobname.ref" a pokud existuje, provede
"\input \jobname.ref". V takovém případě po načtení REF souboru jej otevře 
k~zápisu a připraví "\wref" do stavu, kdy toto
makro bude ukládat data do souboru. 

\inext {inputref}{^^B\cbrace}{+-}

Makro \db openref kdekoli v dokumentu si vynutí založení souboru
"\jobname.ref". Toto makro neprovede nic, je-li REF soubor už založen. To
pozná podle toho, že makro "\wref" nemá význam "\wrefrelax". Jestliže soubor
ještě není založen, makro jej založí, předefinuje "\wref" a vloží první
řádek do souboru. Tím je zaručeno, že při příštím \TeX{}ování dokumentu 
je soubor neprázdný, takže jej OPmac rovnou přečte a znovu založí na začátku
své činnosti. Nakonec se "\openref" zasebevraždí, aby se nemuselo při
opakovaném volání obtěžovat vykonávat nějakou práci. Práce už je totiž
hotova.

\inext {openref}{^^B\cbrace}{++}

Pro zápisy do REF souboru používáme tuto konvenci: první kontrolní sekvence
na řádku je vždy tvaru "\X<název>", takže máme přehled, která kontrolní sekvence
pochází z REF souboru.



\subsec Lejblíky a odkazy
%%%%%%%%%%%%%%%%%%%%%%%%%

K vytvoření zpětného odkazu provedeme tři kroky (v tomto pořadí):

\begitems
\item* V místě "\label[<lejblík>]" si zapamatujeme "<lejblík>".
\item* V době vygenerování čísla (sekce, kapitoly, caption, atd.)
       propojíme "<lejblík>" s tímto číslem. Provedeme to
       pomocí "\sxdef{lab:<lejblík>}{<číslo>}".
\item* V místě "\ref[<lejblík>]" vytiskneme "\csname lab:<lejblík>\endcsname", 
       tedy "<číslo>".
\enditems

To je základní idea pro zpětné odkazy. V takovém případě nepotřebujeme REF
soubor. Pokud ale chceme dopředné odkazy, je potřeba použít REF soubor
zhruba takto:

\begitems
\item* V době vygenerování čísla (sekce atd.) navíc uložíme informaci 
       "\Xlabel{<lejblík>}{<číslo>}" do REF souboru.
\item* V době čtení REF souboru (tedy na začátku dokumentu) provede "\Xlabel"
       přiřazení pomocí "\sdef{lab:<lejblík>}{<číslo>}".
\item* Nyní může přijít "\ref[<lejblík>]"
       se svým "\csname lab:<lejblík>\endcsname" kdekoli v dokumentu.
\enditems       

Přejdeme od idejí k implementaci.
Makro \db label "[<lejblík>]" si pouze zapamatuje "<lejblík>" do makra \db
lastlabel, aby s touto hodnotou mohlo později pracovat makro, které automaticky
generuje nějaké číslo.

\inext {def\nb label}{\empty}{+-}

Makro, které automaticky generuje nějaké číslo, má za úkol zavolat
\db wlabel "<číslo>". Toto makro propojí "\lastlabel" a "<číslo>" tak, že
definuje sekvenci "\lab:\lastlabel" jako makro s hodnotou "<číslo>".
Kromě toho zapíše expandované "\lastlabel" i "<číslo>" do
REF souboru (jen, je-li otevřen, zpětné reference totiž fungují i bez
souboru). Nakonec vrátí makru "\lastlabel" jeho původní nedefinovanou 
hodnotu, tj.~lejblík
už byl použit. Další makro automaticky generující číslo zavolá "\wlabel",
který nyní neprovede nic (pokud tedy uživatel nenapsal další 
"\label[<lejblík>]").

\inext {wlabel}{^^B\cbrace}{++}

\goodbreak
Makro \db ref "[<lejblík>]" zkontroluje definovanost "\lab:<lejblík>".
Je-li to pravda, vytiskne "\lab:<lejblík>" (krz reflink, aby to bylo
případně klikací). Jinak vytiskne dva otazníky a předpokládá, že v tomto
případě jde o dopřednou referenci. Vynutí si tedy otevření REF souboru
zavoláním "\openref".

\inext {ref}{^^B\cbrace}{++}

Makro \db pgref "[<lejblík>]" dělá podobnou práci, jako "\ref", jen s makrem
"\""pgref:<lejblík>". Toto makro je definováno až při znovunačtení REF
souboru makrem \db Xlabel, protože ke správnému určení čísla stránky 
potřebujeme asynchronní "\write".

\inext {pgref}{\empty}{+-}



\subsec Kapitoly, sekce, podsekce
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Fonty pro titul, kapitoly a sekce \db titfont, 
\db chapfont, \db secfont a \db seccfont 
jsou definovány jako odpovídající zvětšení a nastavení tučného duktu.
Ten je nastaven pomocí \db bfshape jako "\bf" a navíc je ztotožněn
"\tenit" s "\tenbi", takže když nyní uživatel napíše "\it", dostane tučnou
kurzívu. 
 
\inext{titfont}{\empty}{+-}

Pro číslování kapitol, sekcí a podsekcí potřebujeme čítače:

\dgn \nb chapnum  \dgn \nb secnum \dgn \nb seccnum

\inext{newcount}{\empty}{+-}

Makro pro titul \db tit počítá s tím, že bude titul na více řádcích. Sází ho
tedy jako odstavec s~pružnými "\leftskip" a "\rightskip". Příkaz "\unskip"
těsně za parametrem "#1" odstraní mezeru z konce řádku, která tam obvykle
je. Teprve poté je nadpis řádně centrován.

\inext{tit}{^^B\cbrace}{++}

Makra \db chap, \db sec a \db secc nastaví odpovídající čítače, dále vytvoří
číslování pro tisk (sestávající z více čísel) v makrech \db thechapnum,
\db thesecnum a \db theseccnum a zavolají odpovídající makro "\printchap",
"\printsec" a "\printsecc".
Poté zavolá "\wlabel" na
propojení případného labelu s číslem kapitoly/sekce/podsekce a
pomocí \db wcontents uloží do "\reffile" potřebná data pro obsah.\par\vskip-1pt

\inext{chap}{\empty}{+-}

Text titulu sekce a její číslo jsou vloženy do "\mark", takže tyto údaje
můžete použít v plovoucím záhlaví. Tato vlastnost není dokumentována v
uživatelské části, protože je poněkud techničtějšího charakteru. Makro \db
insertmark "\thesecnum{<text>}" vloží do "\mark" data ve formátu 
"{<secnum>} {<text>}", takže je možno je použít přímo expanzí např.
"\firstmark", nebo je oddělit a zpracovat zvlášť. Parametru "<text>" je
zabráněna expanze pomocí protažení tohoto parametru přes "\toks", viz TBN
str. 54 dole a strany 55--57. Příkaz "\mark" se totiž snaží o expanzi.

\inext{insertmark}{\empty}{+-}

Příklad použití plovoucího záhlaví v "\headline":

\begtt
\headline{\expandafter\domark\firstmark\hss}
\def\domark#1#2{\llap{\it\headsize #1. }\rm\headsize #2}
\def\headsize{\thefontsize[10]}
\endtt

Makro "\headsize" v této ukázce zaručí, že bude mít záhlaví vždy
požadovanou velikost. Bez toho ta záruka není, pokud tedy uživatel v sazbě
dokumentu střídá velikosti písma. Output rutina totiž může přijít náhle,
třeba v okamžiku, kdy je zapnutá jiná velikost písma.

Pokud chcete kombinovat na levých a pravých stránkách plovoucí záhlaví z
kapitol a sekcí, inspirujte se v TBN na stranách 259 a 260.

Makra \db printchap, \db printsec a \db printsecc vytisknou číslo a titul
kapitoly, sekce resp. podsekce. Příkazem "\firstnoindent" dávají najevo,
že následující odstavec nebude mít odstavcovou zarážku.

\inext {printchap}{\count=3 ^^B\cbrace}{++}

Makro \db afternoindent potlačí odstavcovou zarážku pomocí přechodného
naplnění "\everypar" kódem, který odstraní box vzniklý z "\indent" a
vyprázdní pomocí \db wipeepar registr "\everypar". 
Makro \db firstnoindent je ztotožněno s
"\afternoindent", ale uživatel může psát "\let\firstnoindent=\relax".
Pak bude "\afternoindent" pracovat jen za verbatim výpisy.

\inext{afternoindent}{firstnoindent}{++}

Nechceme, aby se nám odstavce tvořené z titulů kapitol a sekcí rozdělily do
více stran. Proto místo příkazu "\par" je použito \db nbpar.
Konečně uživatel může odřádkovat například v titulu pomocí \db nl.
Tomuto makru později v "\output" rutině změníme význam na mezeru.

\inext{nbpar}{\empty}{+-}


\subsec Popisky, rovnice
%%%%%%%%%%%%%%%%%%%%%%%%

Nejprve deklarujeme potřebné čítače:
\dgn \nb tnum \dgn \nb fnum \dgn \nb dnum 

\inext{newcount}{\empty}{+-}

Výchozí hodnoty maker, které se vypisují v místě generovaného čísla jsou:

\inext{thetnum}{\empty}{+-}

Makro \db caption "/<typ> " zvedne čítač "\<typ>num" o jedničku,
dále nastaví pružné mezery s odsazením "\iindent" a s centrováním posledního
řádku (viz TBN str.~234), propojí pomocí "\wlabel" číslo s případným
lejblíkem, vytiskne slovo Tabulka/Obrázek v závislosti na jazyku makrem
"\mtext", vytiskne číslo "\the<typ>num" a zpracuje odstavec s nastavenými
"\leftskip", "\rightskip". Předefinuje "\par" tak, že první výskyt prázdného
řádku ukončí skupinu a tím se všechna nastavení vrátí do původního stavu.

\inext{caption}{^^B\cbrace}{++}

Makro \db eqmark zvedne "\dnum" o jedničku. 
V display módu pak použije primitiv "\eqno", za kterým
následuje "\thednum". V interním módu (v boxu) vytiskne jen "\thetnum".
V obou případech propojí případný lejblík s číslem pomocí makra "\wlabel".

\inext{eqmark}{\empty}{+-}

\subsec Odrážky
%%%%%%%%%%%%%%%

Odsazení každé další vnořené úrovně odrážek bude o "\iindent" větší.
Jeho hodnota je nastavena na "\parindent" v době čtení {\tt opmac.tex}.
Jestliže uživatel později změní "\parindent", měl by odpovídajícím způsobem
změnit "\iindent". Kromě toho deklarujeme čítač pro odrážky \db itemnum.

\inext{newcount}{\empty}{+-}

Makro \db begitems vloží "\iiskip", zahájí novou skupinu, pronuluje "\itemnum", 
zvětší odsazení o~"\iindent" a pomocí "\adef" definuje hvězdičku jako
aktivní makro, které provede "\startitem". Makro \db enditems ukončí
skupinu a vloží "\iiskip".

\inext{begitems}{\empty}{+-}

Makro \db startitem ukončí případný předchozí odstavec, posune čítač, zahájí
první odstavec jako "\noindent" a vyšoupne doleva text definovaný v
\db printitem, který je implicitně nastaven makrem "\begitems" na 
\db normalitem.

\inext{startitem}{\empty}{+-}

Makro \db style "<znak>" přečte "<znak>" a rozvine jen na makro
"\item:<znak>". Tato jednotlivá makra jsou definována pomocí "\sdef".
Není-li makro "\item:<znak>" definováno, použije se "\normalitem".

\inext{style}{\empty}{+-}

Čtvereček kreslíme jako "\vrule" odpovídajících rozměrů makrem 
\db fullrectangle "{<dimen>}".

\inext{fullrectangle}{\empty}{+-}

Pro převod mezi numerickou hodnotou čítače a příslušným písmenem a, b, c
atd. je vytvořeno makro \db athe "<number>".

\inext{athe}{\empty}{+-}


\subsec Tvorba obsahu
%%%%%%%%%%%%%%%%%%%%%

Do \db toclist budeme ukládat data pro obsah.
Pomocí \db ifischap se budeme ptát, zda v dokumentu jsou kapitoly.

\inext{toclist}{\empty}{+-}

V době čtení REF souboru vložíme veškerá data obsahu do makra "\toclist"
tak, že v tomto bufferu budeme mít za sebou sekvence "\tocline" následované pěti
parametry. Makra \db Xchap, \db Xsec a \db Xsecc
mají parametry "{<číslo>}{<text>}{<strana>}" a jsou definovány následovně:

\inext{Xchap}{\empty}{+-}

Makro \db tocline "{<odsazení>}{<font>}{<číslo>}{<text>}{<strana>}" vytvoří
řádek obsahu. Řádek tiskneme jako odstavec, protože "<text>" může být třeba
delší. Registr "\leftskip" nastavíme jako součin "<odsazení>" krát "\iindent".
Pokud se v dokumentu vyskytují kapitoly, odsadíme ještě o další "\iindent".
Registr "\rightskip" nastavíme na 2"\iindent", aby delší "<text>" se zalomil
dřív než v místě, kde jsou stránkové číslice. Konec odstavce se stránkovou
číslicí pak vytáhneme mimo tento rozsah pomocí "\hskip-2\iindent". Odstavec
pruží v "\tocdotfill", protože tento výplněk má větší pružnost než
"\parfillskip". Registr "\parfillskip" má "1fil", zatímco "\tocdotfill" má
pružnost "1fill". Makro \db tocdotfill je implementováno pomocí "\leaders"
jako opakované tečky, které budou lícovat pod sebou.

\inext{tocline}{\empty}{+-}

Makro \db maketoc jednoduše spustí "\toclist", Pokud je "\toclist" prázdný,
upozorní o tom adekvátním způsobem na terminál.

\inext{maketoc}{\empty}{+-}


\subsec Sestavení rejstříku
%%%%%%%%%%%%%%%%%%%%%%%%%%%

Slovo do rejstříku vložíme pomocí \db iindex "{<heslo>}". Protože výskyt
slova na stránce není v době zpracování znám, je nutné použít REF soubor s
asynchronním "\write".

\inext{iindex}{\empty}{+-}

Nyní naprogramujeme čtení parametru makra 
\db ii "<slovo>,<slovo>,...<slovo> ". Vzhledem k tomu, že za
přítomnosti zkratky "@" budeme potřebovat projít seznam slov oddělených čárkou
v~parametru ještě jednou, zapamatujeme si tento seznam do "\tmp".

\inext{leavevmode}{\empty}{+-}

Makro \db iiA sejme vždy jedno slovo ze seznamu. Podle prázdného parametru
poznáme, že jsme u konce a neděláme nic. Při výskytu "<slovo>=@" (poznáme to
podle shodnosti parametru s \db iiatsign \unskip),
spustíme "\iiB", jinak vložíme údaj o slově do rejstříku pomocí "\iindex".
Nakonec makro "\iiA" volá rekurzivně samo sebe.

\inext{iiA}{\empty}{+-}

Makro \db iiB rovněž sejme vždy jedno slovo ze seznamu a na konci volá
rekurzivně samo sebe. Toto makro ovšem za použití makra \db iiC prohodí pořadí
prvního podslova před prvním lomítkem se zbytkem. Není-li ve slově lomítko,
pozná to makro "\iiC" podle toho, že parametr "#2" je prázdný. V takovém případě
neprovede nic, neboť slovo je už zaneseno do rejstříku v makru "\iiA".

\inext{iiB}{\empty}{+-}

Makro \db iid "<slovo> " pošle slovo do rejstříku a současně je zopakuje do 
sazby. Pomocí "\futurelet" a \db iiD zjistí, zda následuje tečka nebo čárka. Pokud
ne, vloží mezeru.

\inext{iid}{\empty}{+-}

Při čtení REF souboru se vykonávají makra \db Xindex "{<heslo>}{<strana>}",
která postupně vytvářejí makra tvaru "\,<heslo>", ve kterých je shromažďován
seznam stránek pro dané "<heslo>".
Kromě toho každé makro "\,<heslo>" je vloženo do seznamu \db iilist. 
Na konci čtení
REF souboru tedy máme v~"\iilist" seznam všech hesel jako řídicí sekvence (to
zabere nejmíň místa v \TeX{}u). Každé
"\,<heslo>" na konci čtení REF souboru obsahuje dva údaje ve svorkách, první údaj
obsahuje pomocná data a druhý obsahuje seznam stránek. Tedy "\,<heslo>" je makro
s obsahem "{<pomocná-data>}{<seznam-stránek>}".

Seznam stránek není jen tupý seznam všech stránek, na kterých se objevil
záznam "\ii" pro dané slovo. Některé stránky se totiž mohou opakovat a my je
chceme mít jen jednou. Pokud stránky jsou souvisle za sebou: 13, 14, 15, 16,
chceme navíc takový seznam nahradit zápisem 13--16. Makro 
"\Xindex{<heslo>}{<strana>}" je z tohoto důvodu poněkud sofistikovanější.

\ilabel [Xindex:first] {firstdata}
\ilabel [Xindex:second] {seconddata}

\inext{Xindex}{\empty}{+-}

Činnost makra "\Xindex" si vysvětlíme za chvíli podrobněji. Nejprve ovšem
definujeme pomocné makro \db firstdata "\,<heslo>"~"\<cs>", které expanduje na
"\<cs>"~"<první-datový-údaj-hesla>&". Je-li třeba "\,aa" definováno jako
"{prvni}{druhy}", pak "\firstdata"~"\,aa"~"\cosi" expanduje na "\cosi"~"prvni&". 
Tím máme možnost vyzískat data z makra. Podobně makro 
\db seconddata "\,<heslo>"~"\<cs>" expanduje na 
"\<cs>"~"<druhý-datový-údaj-hesla>&".

\inext{firstdata}{\empty}{+-}

Než se pustíme do výkladu makra "\Xindex", vysvětlím, proč pro hesla rejstříku
tvořím jednu řídicí sekvenci, která je makrem se dvěma datovými údaji.
Mohl bych jednodušeji pracovat se dvěma různými řídicími sekvencemi,
např. "\,<heslo>" a "\:<heslo>". Důvod je prostý: šetřím paměť \TeX u. Dá se totiž
očekávat, že počet hesel v rejstříku můžeme počítat na tisíce a je rozdíl
alokovat kvůli tomu tisíce kontrolních sekvencí nebo dvojnásobné množství
takových sekvencí.

V makru "\Xindex" čteme "\firstdata" na řádku \cite[Xindex:first] 
a "\seconddata" na řádku \cite[Xindex:second].
Čtení je provedeno makry \db XindexA a \db XindexB. První úsek dat je tvaru
"<poslední-strana>/<stav>" a druhý úsek dat obsahuje rozpracovaný seznam stránek.
Podíváme-li se na definice "\XindexA" a "\XindexB", shledáme, že seznam stránek
bude uložen v "\tmp", dále "<poslední-strana>" bude v "\tmpa" a "<stav>" je v
"\tmpb".

\inext{XindexA}{\empty}{+-}

Rozlišujeme dva stavy: "<stav>=+", pokud je seznam stránek zakončen konkrétní
stránkou. Tato konkrétní stránka je uložena v "<poslední-strana>". Druhým
stavem je "<stav>=-", když je seznam stránek ukončen "--" (přesněji obsahem
makra \db iiendash, které můžete snadno předefinovat) a v tomto případě
"<poslední-strana>" obsahuje poslední stránku, na které byl zjištěn výskyt
hesla. Tato strana nemusí být v~seznamu stránek explicitně uvedena.

Makro "\Xindex{<heslo>}{<strana>}" tedy postupně vytváří seznam stran 
zhruba takto:

\def\begtthook{\langleactive}
\begtt
if (první výskyt \,<heslo>) {
  založ \,<heslo> do iilist;
  <seznam-stran> = "<strana>"; <stav> = +; <posledni-strana> = <strana>;
  return;
}
if (<strana> == <posledni-strana>) return; 
if (<stav> == +) {
  if (<strana> == <posledni-strana>+1) { 
    <seznam-stran> += "--"; 
    <stav> = - ;
  }
else {
    <seznam-stran> += ", <strana>";
    <stav> = + ;
  }
  else {
    if (<strana> > <posledni-strana>+1) { 
      <seznam-stran> += "<posledni-strana>, <strana>";
      <stav> = + ;  
    }
  }
}    
<poslední-strana> = <strana>; 
\endtt

\def\begtthook{}
Makro \db makeindex nejprve definuje přechodný význam rekurzivního 
"\act" tak, aby byly uzavřeny seznamy stránek (tj. aby seznam nekončil 
znakem "--") a do první
datové oblasti každého makra typu "\,<heslo>" vloží konverzi textu "<heslo>" do
tvaru vhodném pro abecední řazení českých slov.
Pomocí "\expandafter \act \iilist \relax" se
požadovaná činnost vykoná pro každý prvek v "\iilist". 
Dále makro "\makeindex" provede seřazení "\iilist" podle abecedy
makrem "\dosorting" a nakonec provede tisk jednotlivých hesel. K tomu účelu 
znovu přechodně předefinuje "\act" a předloží mu "\iilist".

\ilabel [mkindex:od] {firstdata}
\ilabel [mkindex:do] {after\nb act}
\ilabel [mkindex:orippx] {orippx}

\inext{makeindex}{^^B\cbrace}{++}

Makro \db printiipages sebere z "<druhého-datového-údaje>" seznam stránek a
jednoduše je vytiskne.

\inext{printiipages}{\empty}{+-}

Makro \uv{prepare index item} \db prepii "\,<heslo>" odstraní prostřednictvím
\db prepiiA z názvu kontrolní sekvence backslash a čárku a zbytek tiskne pomocí
"\printii". Pokud ale je "\,<heslo>" uloženo v~seznamu "\iispeclist", pak se
expanduje sekvenci s názvem "\\,<heslo>", ve které je uloženo, co se
má místo hesla vytisknout. Data těchto výjimek jsou připravena makrem "\iis"

\inext{prepii}{\empty}{+-}

Kontrolní otázka: proč se nedotazujeme jednoduše na to, zda je
"\\,<heslo>" definovaná řídicí sekvence? Odpověď: museli bychom ji
sestavit pomocí "\csname...\endcsname", ale to založí do \TeX{}ové paměti
novou řídicí sekvenci pro každé heslo v rejstříku. My se snažíme počet
těchto řídicích sekvencí redukovat na minimum. Počítáme s tím, že obyčejných
hesel bude tisíce a výjimek jen pár desítek.

Makro \db iis "<heslo> {<text>}" vloží další údaj do slovníku výjimek pro
hesla v rejstříku. Přesněji: vloží "\,<heslo>" do \db iispeclist a definuje
sekvenci "\\,<heslo>" jako "<text>".

\inext{iis}{\empty}{+-}

Makro \uv{print index item} \db printii "<heslo>&" vytiskne jeden údaj do
rejstříku. Makro projde prostřednictvím \db printiiA jednotlivá podslova
oddělená lomítkem a přepíše je do rejstříku odděleny mezerou. Přitom
kontroluje, zda se podslova rovnají odpovídajícím podslovům z předchozího
hesla, které je uloženo v \db previi. Pokud ano, místo podslova vloží \db
iiemdash, což je pomlka. Na konci činnosti se spustí makro \db printiiB,
které nastaví "\previi" na \db currii (nové slovo se pro další zpracování stává
předchozím) a vytiskne seznam stránek. Makrem \db everyii
(implicitně je prázdné) dovolíme uživateli vstoupit do procesu tisku hesla.
Může například psát "\def\everyii{\indent}", pokud chce.

\ilabel [printii:iindent] {iindent}
\inext{printii}{\empty}{+-}

Makro \db iiparparams nastavuje parametry sazby odstavce v rejstříku. Vlevo
budeme mít "\leftsip" rovný "\iindent", ale první řádek posuneme o
"-\iindent" (viz řádek kódu~\cite[printii:iindent]) takže první řádek je
vystrčen doleva. Vpravo máme pružnou mezeru, aby se seznam čísel stran mohl
rozumně lámat, když je moc dlouhý. Makro "\iiparparams" si musí poznačit do
makra \db orippx původní údaje měněných hodnot. Není možné se totiž schovat
do skupiny, protože rejstřík je obvykle tištěn pomocí
"\begmulti...\endmulti" a toto makro občas ukončuje plnění boxu a spouští
"\flushcolumns". Kdybychom měli "\makeindex" ve skupině, pak by při
"\flushcolumns" došlo ke křížení skupin. Na konci práce "\makeindex" na
řádku \cite[mkindex:orippx] je makro "\orippx" zavoláno a tím jsou parametry
odstavce vráceny do původní podoby.

\inext{iiparparams}{\empty}{+-}

Pomocné makro \db scanprevii "<expanded-previi>&" se podívá do "\previi", 
odloupne z něj úsek před prvním lomítkem a tento úsek definuje jako
"\tmpa". 

\inext{scanprevii}{}{+-}

Výchozí hodnota "\previi" před zpracováním prvního slova v rejstříku je
prázdná.

\inext{previi}{\empty}{+-}


\subsec Abecední řazení rejstříku
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Nejprve se zaměříme na vytvoření makra 
"\isAleB"~"\,<heslo1>"~"\,<heslo2>", které rozhodne, zda je "<heslo1>" řazeno za
"<heslem2>" nebo ne. Výsledek zkoumání můžeme prověřit pomocí "\ifAleB".

Pro porovnání dvou údajů vyžaduje norma dva průchody. V prvním (primární
řazení) se rozlišuje jen mezi písmeny A B C Č D E F G H Ch I J K L M N O P Q
R Ř S Š T U V W X Y Z Ž. Pokud jsou hesla z tohoto pohledu stejná, pak se
provede druhý průchod (sekundární řazení), ve kterém jsou řazena
neakcentovaná písmena před přehlasovaná před čárkovaná před háčkovaná před
stříškovaná před kroužkovaná a dále s nejnižší prioritou malá písmena před
velká. Připravíme si tedy dvě sady "\lccode" dvojic: pro první průchod a pro
druhý. Porovnávaná hesla zkonvertujeme pomocí "\lowercase" při nastavení
"\lccode" odpovídajícího průchodu. Pak takto zkonvertovaná hesla teprve
začneme porovnávat.

Makro \db setprimarysorting připraví "\lccode" znaků české a slovenské
abecedy pro první průchod a \db setsecondarysorting pro druhý průchod.
Makro "\setprimarysorting" expanduje \db sortingdata 
a předhodí před takto expandovaná data "\act".
Povšimneme si, že pro první průchod dostanou stejný "\lccode" všechny znaky
na společném řádku makra "\sortingdata", zatímco v druhém průchodu budou mít
všechny znaky z tohoto makra rozdílný "\lccode", ve vzestupném pořadí.
Je to tím, že v makru "\setprimarysorting" se zvedá "\tmpnum" jen v místě
čárky, zatímco v "\setsecondarysorting" se "\tmpnum" zvedá pro každý znak.
Nejnižší hodnotu má mezera vyznačená v "\sortingdata" pomocí "{ }". 
Tím je zaručeno, že kratší slovo je řazeno dřív než delší slovo se stejným
začátkem, obsahující celé kratší slovo (ten~tučňák$\prec$tento).
Je sice pravda, že ASCII hodnota mezery je ještě menší, ale my musíme 
mezeru někam šoupnout na jiný kód než 32, jinak by nám ji nepřečetlo 
makro s~neseparovaným parametrem. Ovšem my budeme chtít mezeru přečíst. 

\ilabel [sorten] {sort:en}
\ilabel [lan] {lan:}
\ilabel [chsorting] {chsorting}

\inext{sortingdata}{\count=3 ^^B\cbrace}{++}

Jedním z problémů českého řazení je dvojhláska ch. Tu potřebujeme
proměnit v jediný znak. Pro potřeby řazení proměníme ch v "^^T", Ch v "^^U"
a CH v "^^V". Uděláme to následujícím okultním kódem, který definuje makro
\db preparesorting "\,<heslo>". Toto makro připraví pomocí do "\tmpb" 
"<heslo>" zkonvertované krz "\lowercase", 
ovšem nejprve je dvojhláska ch nahrazena jedním znakem.
Makro \db chsorting je implicitně nedefinované, což znamená, že pracujeme s
dvojhláskou ch. Na řádku \cite[sorten], \cite[lan] a \cite[chsorting] je ovšem nastaveno
"\chsorting" jako "n", což je vzkaz, že dvojhlásku ch nechceme
interpretovat.

\inext{bgroup}{\empty}{+-}

Tento kód je bohužel obtížněji čitelný, protože makra "\iiscanch" a další
potřebují mít separátor "ch" ve stavu, kdy jednotlivá písmena mají "\catcode" 12.
Pracujeme totiž s výstupem primitivu "\string", který bohužel vše (až na
mezeru) balí do tokenů s kategorií 12. Proto je celý kód obalen do
"\bgroup", "\egroup" a "\lowercase". Tam jsou znaky 4, 5, 6, 7, 
které mají kategorii 12, šoupnuty na c, h, C, H. Po tomto dešifrování tedy 
vidíme, že makro \db iiscanch je definováno takto:

\begtt
\gdef\iiscanch #1ch#2\relax{#1\if$#2$\else^^T\iiscanch #2\relax\fi}
\iiscanch Schází hrách, který bych házel na stěnu.ch\relax
\endtt

Uvedený příklad expanduje postupně na 

\begtt
#1<-S
#2<-ází hrách, který bych házel na stěnu.ch
=> s^^T\iiscanch #2\relax

#1<-ází hrá
#2<-, který bych házel na stěnu.ch
=> s^^Tází hrá^^T\iiscanch #2\relax

#1<-, který by
#2<- házel na stěnu.ch
=> s^^Tází hrá^^T, který by^^T\iiscan #2\relax

#1<- házel na stěnu.
#2<-
=> s^^Tází hrá^^T, který by^^T házel na stěnu.
\endtt
a to nahradí všechny výskyty dvojhlásky ch znakem "^^T". Analogicky pracují
makra \db iiscanCh a \db iiscanCH. Makro \db preparesortingA nakonec zavolá
všechna tři makra, takže máme nahrazeny všechny dvojhlásky ch, Ch i CH.

V rámci optimalizace rychlosti jsou před algoritmem na setřídění seznamu
všechna hesla jednorázově zkonvertovaná podle pravidel prvního průchodu
řazení a tato data jsou uložena v prvním datovém údaji hesla (ve druhém máme
seznam stránek). Není tedy nutné dělat konverzi při každém porovnávání dvou
hesel. Ovšem, pokud porovnání hesel vyjde bez rozdílu, je potřeba provést
druhý průchod řazení (sekundární řazení). Ten nastavujeme jednotlivě jen pro
takové dvojice hesel, kde to je potřeba. Pravděpodobnost, že to je vůbec
někdy potřeba, je mizivá. Data pro primární řazení jsou tedy už připravena na
řádcích \cite[mkindex:od] až \cite[mkindex:do] v makru "\makeindex".

V českém řazení se nemá přihlížet na interpunkční znaky (tečka, středník,
otazník, atd.). Hesla máme řadit tak, jako kdyby tam tyto znaky nebyly.
Kdyby se dvě hesla podle tohoto pravidla nelišila, norma předepisuje nasadit
cca čtvrtý průchod, ve kterém se tyto znaky rozliší. Čtvrtý průchod
implementován není: hesla lišící se jen interpunkčními znaky, jsou v OPmac
při řazení nerozlišitelná, tj. jsou řazena v pořadí, v jakém vstupují do
rejstříku. Ignorování interpunkčních znaků je provedeno tak, že všem
těmto znakům je přidělen makrem \db setignoredchars "\lccode" tečky a
tečka je při zpracování v~"\setprimarysorting" a "\setsecondarysorting" odstraněna
makrem \db removedot.

\inext{removedot}{\empty}{+-}

Připravíme si "\newif", kterým ohlásíme výsledek porovnání dvou hesel:

\inext{newif}{\empty}{+-}

Makro \db isAleB "\,<heslo1>"~"\,<heslo2>" spustí\hfil\break 
"\testAleB"~"<zkonvertované heslo1>&\relax"~"<zkonvertované heslo2>&\relax"~%
"\,<heslo1>"~"\,<heslo2>".

\inext{isAleB}{^^B\cbrace}{++}

Idea makra \db testAleB lexikograficky porovnávající dvě slova je v tom, že ze
dvou stringů v~parametru oddělených "\relax" postupně odlupuje vždy
první znak "#1" a "#3" z každého stringu a ten porovnává a samozřejmě při rovnosti
rekurzivně zavolá samo sebe. Pokud jsme se dostali na konec bez rozhodnutí, co
je menší, narazíme na znak "&". V takovém případě přestoupíme do
sekundárního průchodu.

\inext{testAleB}{^^B\cbrace}{++}

Makro \db testAleBsecondary "\,<heslo1>"~"\,<heslo2>" založí skupinu, v ní
nastaví "\lccode" dle sekundárního řazení a pomocí "\preparesorting"
připraví zkonvertovaná data do "\tmpa" a "\tmpb". Na chvosty těchto dat
přidám nulu a jedničku, aby porovnání vždy nějak dopadlo, a spustím
\db testAleBsecondaryX, což pracuje obdobně, jako "\testAleB".

\inext{testAleBsecond}{\count=2 ^^B\cbrace}{++}

Nyní můžeme pomocí "\isAleB\,<heslo1>\,<heslo2>\ifAleB" rozhodnout, který
ze dvou daných parametrů má být řazen dříve. Stačí tedy už jen naprogramovat
celkové řazení seznamu. Toto makro vycházející z algoritmu mergesort 
vytvořil můj syn Miroslav. Makro bylo poprvé použito v DocBy\TeX{}u, což je
nástroj, kterým je například pořízena i tato dokumentace.
 
Makro \db dosorting pomocí pomocného makra "\act" doplní za každý údaj v
"\iilist" čárku a dále předloží makru "\mergesort" jako parametr obsah
"\iilist" ukončený "\end,\end", vyprázdní "\iilist" a spustí "\mergesort".

\inext{dosorting}{\empty}{+-}

Makro \db mergesort pracuje tak, že bere ze vstupní fronty vždy dvojici
skupin
položek, každá skupina je zatříděná. Skupiny jsou od sebe odděleny
čárkami. Tyto dvě skupiny spojí do jedné a zatřídí. Pak přejde na
následující dvojici skupin položek. Jedno zatřídění tedy vypadá
například takto: dvě skupiny: "eimn,bdkz," promění v~jedinou 
skupinu "bdeikmnz,". V tomto příkladě jsou položky jednotlivá písmena,
ve skutečnosti jsou to kontrolní sekvence, které obsahují celá slova.

Na počátku jsou skupiny jednoprvkové ("\iilist" odděluje každou
položku čárkou). Makro "\mergesort" v tomto případě projde seznam a vytvoří
seznam zatříděných dvoupoložkových skupin, uložený zpětně v
"\iilist". V dalším průchodu znovu vyvrhne "\iilist" do vstupní
fronty,
vyprázdní ho a startuje znovu. Nyní vznikají čtyřpoložkové zatříděné
skupiny. Pak osmipoložkové~atd. V~závěru (na řádku~\cite[konecsortu]) 
je první skupina celá setříděná a druhá obsahuje "\end", tj. 
všechny položky jsou už setříděné v první skupině, takže stačí 
ji uložit do "\iilist" a ukončit činnost. Pomocí \db gobbletoend
odstraníme druhé "\end" ze vstupního proudu.

\noactive{dvojice}
\ilabel [merge:porovnani] {isAleB}
\ilabel [merge:trojka] {mergesort p1+}
\ilabel [merge:p1] {ifx,}
\ilabel [merge:p2] {fif\nb mergesort\cbrace}
\ilabel [konecsortu] {empty\nb iilist}
\inext {def\nb mergesort }{^^B\cbrace}{++}

{\def\quotehook{\catcode`\<12}
Jádro "\mergesort" vidíme na řádcích~\cite[merge:porovnani]
až~\cite[merge:trojka]. Makro "\mergesort" sejme ze vstupního proudu
do "#1" první položku první skupiny, do "#2" zbytek první skupiny a do 
"#3" první položku druhé skupiny. Je-li "#1<#3", je do výstupního
zatříděného seznamu "\indexbuffer" vložen "#1", ze vstupního proudu je
"#1" odebrán a "\mergesort" je zavolán znovu. V případě "#3<#1"
je do "\indexbuffer" vložen "#3", ze vstupního proudu je "#3" odebrán a 
"\mergesort" je zavolán znovu. Řádky~\cite[merge:p1]
až~\cite[merge:p2] řeší případy, kdy je jedna ze skupin prázdná: 
je potřeba vložit do "\indexbuffer" zbytek neprázdné skupiny a přejít 
na další dvojici skupin. Ostatní řádky makra se vyrovnávají se
skutečností, že zpracování narazilo na zarážku "\end,\end" a je tedy
potřeba vystartovat další průchod.
\par}


\subsec Více sloupců
%%%%%%%%%%%%%%%%%%%%

Makro pro sazbu do více sloupců je převzato z TBN, kde je podrobně
vysvětleno na stranách 224 až~245. Základní myšlenka makra spočívá v tom, že
se naplní jeden velký "\vbox" (box6) jedním sloupcem a "\endmulti" jej
rozlomí do sloupců požadované výšky a strčí do sazby. Není k tomu nutno
měnit výstupní rutinu. Makro z TBN je zde v OPmac ve dvou věcech přepracováno: 
\begitems
\item * Důslednější balancování sloupců vylučující možnost ztráty sazby
        a umožňující mít sazbu s nezlomitelnými mezerami mezi řádky.
\item * Makro měří kumulovanou sazbu a umožňuje při rozsáhlém množství
        tiskového materiálu přechodně přejít do režimu \uv{vyprazdňování}.
\enditems 

Makra \db begmulti, \db endmulti, \db corrsize, \db makecolumns a \db
splitpart pracují zhruba tak, jak je popsáno v TBN.

\ilabel [mul:endgraf] {endgraf}
\ilabel [mul:balance] {last balancing}
\ilabel [mul:relax] {relax\cbrace}
\ilabel [mul:prevgraf] {prevdepth}

\inext{corrsize}{\count=2 \empty}{+-}

Výstup rozlomené sazby do sloupců probíhá ve dvou režimech: když je třeba
sloupci zaplnit celou stránku, použijeme "\makecolumns". Toto makro neřeší
otázku, že může v kumulovaném boxu~6 zbýt nějaká sazba, protože se
předpokládá, že lámání bude pokračovat na další straně. Pokud ale
je na aktuální straně vícesloupcová sazba ukončena, použijeme
propracovanější \db balancecolumns. Toto makro si zazálohuje materiál
z~boxu~6 do boxu~7 a jme se zkoušeti rozlomit box~6 na sloupce s 
výškou "\dimen0". Pokud ale po rozlomení není výchozí box~6 zcela prázdný, makro
zvětší krapánek (o 0,2"\baselineskip") požadovanou výšku, vrátí se k~zálohované 
sazbě v boxu~7 a zkusí rozlomit znovu. To opakuje tak dlouho, dokud je box~6
prázdný.

\inext{balancecolumns}{^^B\cbrace}{++}

Když je sazba plněna do boxu~6, může ji být tak moc, že tento box
překročí maximální výšku boxu, která je v \TeX{}u bohužel 
omezena na cca pět metrů
(16383~pt). Proto na řádku~\cite[mul:endgraf] je předefinován "\par", který
přičítá do "\dimen0" celkovou výšku postupně kumulované sazby. Jakmile tato
výška dosáhne "0.9\maxdimen", předefinujeme makro 
"\balancecolumns" na "\flushcolumns" a spustíme předčasně "\endmulti". Toto makro 
vyprázdní box pomocí opakovaného "\splitpart", ovšem nevyprázdní ho celý. 
Jen tu část, která zaplní celé stránky. Jakmile bude chtít 
"\splitpart" přejít k~vybalancování sazby pomocí
"\balancecolumns" na řádku \cite[mul:balance], spustí se místo běžného
"\balancecolumns" makro \db flushcolumns. Toto makro ignoruje zbytek činnosti
"\endmulti" až po "\relax" na řádku \cite[mul:relax], takže vyskočí z
trojitě zanořeného "\if". Musí tedy vrátit příslušné množství "\fi" a dále
vystartuje nový "\setbox6=\vbox". Uvnitř tohoto boxu nejprve vysype zbytek
boxu~6, pomocí "\unskip\unskip" odstraní "\vfil" a "\vskip-\prevdepth",
který tam vložil "\endmulti" na řádku~\cite[mul:prevgraf],
vrátí se k zálohovanému významu makra \db ibalancecolumns 
a znovu definuje "\par" obdobným způsobem jako v~"\begmulti". 
Pak pokračuje ve čtení sazby.

\inext{flushcolumns}{\empty}{+-}


\subsec Barvy
%%%%%%%%%%%%%

Deklarujeme \db ifpgcolor, což budeme potřebovat v makru "\locpgcolor".
Dále deklarujeme \db lastpage. Tento registr budeme průběžně zvětšovat při
čtení REF souboru v makru "\Xpage" a na konci čtení REF souboru tedy bude 
obsahovat číslo poslední strany.

\inext{ifpgcolor}{\empty}{+-}

Od verze pdf\TeX{}u 1.40 nabízí tento program primitivy
"\pdfcolorstackinit" a "\pdfcolorstack". Makra v OPmac tyto
primitivy nepoužívají, protože:

\begitems
\item * Řešení v OPmac jsem vytvořil a použil podstatně dřív, než si programátoři
        pdf\TeX{}u vůbec všimli, že existuje problém s přecházením barev na nové
        stránky.
\item * Primitivy "\pdfcolorstackinit" a "\pdfcolorstack" stále nejsou dokumentované.
\item * Barevné řešení v "pdftex.def" pro La\TeX{} nepovažuji za koncepčně
        správné, protože se snaží pomocí "\aftergroup" vytvořit zdání, že
        barvy fungují lokálně uvnitř skupiny. To je zbytečná komplikace
        navíc, která jde proti přirozenosti nastavování barev v PDF souboru.
\enditems

Barvy se v PDF přepínají pomocí PDF speciálů "<num> <num> <num> <num> k"
(pro text a plochy) a "<num> <num> <num> <num> K" pro linky. Pro oba typy
barev připravíme barevná makra \db Blue, \db Red, \db Brown, \db Green, \db
Yellow, \db Cyan, \db Magenta, \db White, \db Grey, \db LightGrey, \db Black. 
Uživatel si může definovat další.

\inext{Blue}{\empty}{+-}

Problém barev v PDF je, že od výskytu speciálu pro barvu jsou změněné barvy
až po jiný výskyt takového speciálu nebo po konec strany. Na každé nové
straně začíná sazba v barvě černé. Nám ovšem někdy může obarvený text přetéci
na další stranu. Pak ale musíme v "\output" rutině (v makru "\prepage")
nastavit barvu, která přetekla. Ovšem jak poznáme, že něco přeteklo do další
strany? Jedině pomocí REF souboru a asynchronního "\write". Proto makro \db
setcmykcolor nevkládá do PDF jen požadovaný speciál, ale taky ukládá o sobě
informaci do REF souboru. Jakmile se při čtení REF souboru objeví "\Xpage",
zkontrolujeme, zda naposledy nastavená barva byla černá. Pokud ne, dáme
pokyn makru "\prepage" prostřednictvím makra "\pg:<číslo strany>", aby
aktivovalo hned po "\headline" barvu naposledy nastavenou. To je tedy
základní idea, nyní implementace:

\inext{ifpdftex}{\count=2 \empty}{+-}

Makro \db pdfK má implicitně hodnotu "k" (barva pro texty a plochy) a
přechodně při použití \db linecolor má hodnotu "K". Právě nastavenou barvu
pak makro "\setcmykcolor" uloží do REF souboru. 
Jen tehdy, když je explicitně
známo, že barva nepřeteče do další strany, je možné použít před použitím
přepínače barvy prefix \db locpgcolor, který potlačí v takovém případě tisk
informací do REF souboru. Následně budou potlačeny zápisy informací do REF souboru
všech barevných přepínačů až po "\Black" včetně. Další barevné přepínače (po
"\Black") se chovají už normálně.
Toto opatření je zde jen proto, aby byl REF soubor
poněkud menší a přehlednější i v případě, kdy navrhujeme bohatě barevný dokument.

Makra \db Xpdfcolork a \db XpdfcolorK udržují při čtení REF souboru
informaci o naposledy vložené barvě v makrech \db pdflastcolork a 
\db pdflastcolorK (název koresponduje typu barvy). 

\inext{Xpdfcolork}{\empty}{+-}

Output rutina vkládá na každé stránce do REF souboru 
\db Xpage "{<číslo strany>}".
Toto makro při čtení REF souboru kontroluje, zda byla naposledy použita
na předcházející straně barva černá. Pokud ne, přidá do makra 
"\pg:<číslo strany>" pokyn pro nastavení barvy ve tvaru 
\db setpgcolor "<k nebo K>{<čísla barvy>}". 
Toto makro se spustí v output rutině na straně "<číslo strany>"
za "\headline", ale před sestavením boxu s textem.

\inext{Xpage}{\empty}{+-}

Makro \db draft vloží do "\headline" box nulové výšky a šířky "\draftbox",
který vystrčí svou šedou sazbu ven ze svého rozměru a je tištěn dřív, než
jakýkoli jiný materiál na stránce.

\inext{draft}{\empty}{+-}

V makru \db draftbox "{<text>}" je "<text>" otočen o 55 stupňů, zvětšen
desetkrát a vytištěn v barvě "\LightGrey". K tomu jsou 
využity PDF transformace souřadnic.

\inext{draftbox}{\empty}{+-}


\subsec Klikací odkazy
%%%%%%%%%%%%%%%%%%%%%%

Makro \db destactive "[<typ>:<lejblík>]{<výška>}" založí cíl odkazu jen tehdy, když
je "<lejblík>" neprázdný. Ve vertikálním módu se nalepí na předchozí box
díky "\prevdepth=-1000pt" a po vložení boxu s cílem vrátí hodnotu
"\prevdepth" do původního stavu, aby následující box byl správně řádkován.
V horizontálním módu postě vloží "\destbox". Makro 
\db destbox "[<typ>:<lejblík>]{<výška>}" vytvoří box nulové výšky a z~něj
vystrčí nahoru cíl klikacího odkazu vzdálený od účaří o "<výška>". Interně
použije pdf\TeX{}ový primitiv "\pdfdest" s parametrem "xyz", což
charakterizuje obvyklou možnost chování PDF prohlížeče při odskoku na cíl. 
Podrobněji viz manuál k~pdf\TeX{}u.

\inext{dest}{\empty}{+-}

V uživatelské dokumentaci je zmíněno místo "\destactive" makro \db dest
se stejnými parametry. Toto makro je implicitně prázdné a tedy nečiné. 
Teprve "\hyperlinks" je přinutí k činnosti.

Klikací text vytvoří makro \db link "[<typ>:<lejblík>]{<barva>}{<text>}". 
Makro používá
pdf\TeX{}ový primitiv "\pdfstartlink", ve kterém je vymezena výška a hloubka
aktivní plochy. Nakonec přepne na požadovanou "<barvu>" (pokud není černá),
vytiskne aktivní "<text>" a přepne zpět na černou barvu. Pdf\TeX{}ový primitiv
"\pdfendlink" ukončí sazbu aktivního textu. 

\inext{link}{^^B\cbrace}{++}

Makro \db urllink "[<typ>:<lejblík>]{<text>}" pracuje 
analogicky jako "\link". Jen navíc přidává některé
atributy do PDF výstupu a pracuje s barvou "\urlcolor".

\inext{urllink}{^^B\cbrace}{++}

Makro \db url "{<odkaz>}" vysází "<odkaz>" strojopisem, přičemž znak "/" je
na přechodnou dobu aktivní a definován jako "\\". Toto makro při sestavování
externího odkazu zpětně expanduje na normální "/", ale při sazbě expanduje
na "\discretionary", aby bylo možné v něm rozdělit sazbu do více řádků.

\inext{url}{\empty}{+-}

Makra \db toclink, \db pglink, \db citelink, \db reflink, \db ulink, která se
specializují na určitý typ linku, implicitně nedělají nic:

\inext{toclink}{\empty}{+-}

Ovšem po použití makra \db hyperlinks "{<barva-lok>}{<barva-url>}" se
uvedená makra "\toclink", "\pglink", "\citelink" a "\reflink" probouzejí k
životu:

\inext{hyperlinks}{\empty}{+-}

Pdf\TeX{}ové primitivy pro klikací odkazy dovolují dopravit do PDF další
atributy odkazu za slovem "attr". Tam je možné dát najevo, že chceme vidět
aktivní plochy ve formě rámečků. To zařídí makro \db pdfborder "{<typ>}", které
expanduje na nic, pokud není kontrolní sekvence "\<typ>border" definována.
Jinak expandují na "arrt" "/C" s obsahem podle "\<typ>border".

\inext{pdfborder}{\empty}{+-}

Pokud je dokument zpracován do DVI výstupu, je vhodné výše zmíněná makra
deaktivovat:

\inext{ifpdftex}{\empty}{+-}


\subsec Outlines -- obsah v záložce PDF dokumentu
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Hlavní problém implementace strukturovaného obsahu do záložky PDF dokumentu
spočívá v~tom, že při vkládání jednotlivých položek obsahu je nutno znát
počet přímých potomků každé položky (v~rámci stromové struktury položek),
ovšem tito přímí potomci budou zařazeni později. OPmac tento problém řeší
dvěma průchody nad daty, které jsou vytvořeny pro tisk obsahu, tj. v makru
"\toclist". V prvním průchodu spočítá potřebné potomky a ve druhém
průchodu zařadí všechny položky postupně jako \uv{outlines} do záložky.
Připomeneme si, že v "\toclist" se nachází seznam maker tvaru
"\tocline{<odsazení>}{<font>}{<číslo>}{<text>}{<strana>}".
Makro \db outlines "{<úroveň>}" nejprve nastaví "\tocline" na hodnotu 
"\outlinesA" a projde "\toclist". Pak je nastaví na hodnotu "\outlinesB" a 
znovu projde "\toclist". 

\inext{def\nb outlines}{^^B\cbrace}{++}

V makru \db outlinesA "{<odsazení>}{<font>}{<číslo>}{<text>}{<strana>}"
počítáme potomky. Makro je navrženo tak, aby bylo
snadno rozšířitelné na libovolnou úroveň hloubky stromu, nicméně pro potřeby
OPmac stačí hloubka tři (kapitoly, sekce, podsekce). Úroveň uzlu přečteme v
parametru "<odsazení>". Pro kapitolu je "<odsazení>"=0, pro sekci je
"<odsazení>"=1 a pro podsekci je "<odsazení>"=2. 
Představme si vedle sebe řadu counterů
"\count0:\count1:\count2". Při sekvenčním čtení jednotlivých uzlů stromu
si každý uzel zvětší v této pomyslné řadě hodnotu svého
counteru o~jedničku. Kapitoly zvětšují "\count0", sekce "\count1", podsekce
"\count2". Stačí tedy zvětšit "\count<odsazení>". Řada counterů pak
jednoznačně určuje zpracovávaný uzel.
Uzly pro kapitoly mají přidělenu kontrolní sekvenci "ol:\the\count0"
a uzly pro sekce mají přidělenu kontrolní sekvenci 
"ol:\the\count0:\the\count1". Jsou to makra, jejichž obsahem je počet
potomků daného uzlu. Makrem \db addoneol "<csname>" zvětšíme obsah dané
kontrolní sekvence o~jedničku. Příkazem "\ifcase<odsazení>" řešíme, kterému
rodiči je třeba zvednout tuto hodnotu. Při nule (kapitola) nikomu,
neboť daný uzel nemá rodiče. Při "<odsazeni>"=1 zvětšíme o
jedničku počet potomků nadřazené kapitole a při "<odsazeni>"=2 nadřazené
sekci. Asi by bylo přehlednější na začátku definovat všechny potřebné
sekvence "ol:<něco>" a nastavit jim hodnotu~0. Ovšem šetříme pamětí i časem,
takže zakládáme sekvenci "ol:<něco>" teprve v makru "\addoneol" a to tehdy,
když je ji poprvé potřeba zvětšit o~jedničku.

\inext{outlinesA}{\count=2 ^^B\cbrace}{++}

V makru \db outlinesB "{<odsazení>}{<font>}{<číslo>}{<text>}{<strana>}"
vkládáme jednotlivou položku obsahu do záložek pomocí pdf\TeX{}ového
primitivu\hfil\break
"\pdfoutline goto name{<lejblík>} count<potomci> {<text>}".
Číslo "<potomci>" je opatřeno znaménkem mínus právě tehdy, když chceme, aby
položka ve výchozím stavu nezobrazovala své potomky, ale jen trojúhelníček.
Potomci se zobrazí až po kliknutí na trojúhelníček. V makru \db outlinelevel
máme makrem "\outlines" připravenu úroveň rozevření, kterou si uživatel
přeje. Nejprve přičtením "\count<odsazení>" dostaneme řadu
"\count0:\count1:\count2" do stejného stavu, jako v předchozím prvním průchodu
a máme tím jednoznačně přidělen uzel stromu.
Do "\tmpnum" vložíme údaj o počtu potomků daného uzlu. K tomu je potřeba
rozvětvit výpočet příkazem "\ifcase", protože pro různou úroveň uzlu máme
údaj v~různě definovaném makru. Příkazem "\protectlist" zastavíme expanze
případných maker registrovaných pomocí "\addprotect" a definujeme vlnku jako
mezeru (v záložce vypadá líp než vlnka). Dále pomocí "\setcnvcodesA"
expandujeme "\toasciidata". Pomocí "\setlccodes\toasciidata" připravíme 
"\lccode" znaků tak, aby "\lowercase" odstranil háčky a čárky. To
vzápětí provedeme, ale nejprve ještě do toho může promluvit uživatel v makru
"\cnvhook", které je implicitně nastaveno na makro prázdné.

\ilabel [outlines:setlccodes] {setlccodes}

\inext{outlinesB}{^^B\cbrace}{++}

Makro \db setcnvcodesA zkontroluje podle definovanosti "\r", zda je zapnutý
"\csaccents" a pokud je, expanduje "\toasciidata". 
Makro \db toasciidata potřebujeme expandovat, protože 
neobsahuje přímý zápis znaků. Důvod je zřejmý, nechceme, aby se 
soubor {\tt opmac.tex} stal závislý na použitém kódování. 

\inext{setcnvcodesA}{\count=2 ^^B\cbrace}{++}

Na řádku \cite[outlines:setlccodes]
se makro \db setlccodes spustí jako "\setlccodes AAÁAÄAáa...{}{}".  
Toto makro si odloupne dva parametry "xy", provede "\lccode`x=`y" a 
v~rekurzivním cyklu pokračuje v~činnosti, dokud nenarazí na "{}{}".

\inext{setlccodes}{\empty}{+-}

Makro \db insertoutline "{<text>}" vloží jedinou položku do záložky. Pro
tuto položku se předpokládá nulový počet potomků. Využití: uživatel může
takto odkázat na začátek nebo konec dokumentu.

\inext{insertoutline}{\empty}{+-}

Pokud je dokument zpracován do DVI výstupu, je vhodné výše zmíněná makra
deaktivovat:

\inext{ifpdftex}{\empty}{+-}



\subsec Verbatim
%%%%%%%%%%%%%%%%

Verbatim výpisy budou odsazeny o "\ttindent". Je nastaven na hodnotu
"\parindent" v době čtení souboru a společně s "\parindent" by měl
uživatel změnit i "\ttindent".  Čítač \db ttline čísluje řádky běžného verbatim výstupu,
čítač \db viline čísluje řádky souboru čteného pomoci "\verbinput". Souborový
deskriptor \db vifile bude přiřazen souboru v makru "\verbinput".

\inext{newcount}{\empty}{+-}

Makra \db setverb, \db begtt "...\endtt" jsou dokumentována v TBN, str. 29.

\inext{setverb}{testparA}{++}

Makro "\begtt" očichá na konci své činnosti, zda se nachází pod "\endtt"
prázdný řádek (alias "\par"). K tomu slouží makra \db testparA (přeskočí 
mezeru, která za "\endtt" vždy je), \db testparB (přečte následující znak
pomocí "\futurelet") a \db testparC (ošetří, zda tento následující znak je
"\par").

\inext{testparA}{\empty}{+-}

Makro \db activettchar pracuje podobně, jako makro "\adef". Navíc
potřebuje použít nově načtený znak ve své aktivní kategorii jako separátor
vymezující konec parametru.

\inext{activettchar}{\empty}{+-}

Makro \db verbinput si pomocí "\tmpa" ověří, zda minule byl čten stejný
soubor. Pokud ne, otevře soubor "#2" ke čtení pomocí "\openin" a uloží do
\db vifilename jméno naposledy otevřeného souboru. Dále zkontroluje pomocí
"\ifeof", zda je možné ze souboru číst. Pokud ne, vypíše se varování a
pomocí \db skiptorelax se přeskočí zbytek obsahu makra až po "\relax",
takže se neprovede nic dalšího. Je-li soubor úspěšně otevřen nebo byl-li
otevřen již minule, pustí se makro "\verbinput" do prozkoumání parametru
"#1" zapsaného v závorce před jménem souboru.

\inext{verbinput}{\empty}{+-}

Cílem vyhodnocení parametru v závorce makra "\verbinput" jsou dva údaje: \db
vinolines bude obsahovat počet řádků, které je od začátku souboru nutno
přeskočit, než se má zahájit přepisování řádků a \db vidolines bude
obsahovat počet řádků, které se mají přepsat ze souboru do dokumentu.
Písmena "vi" na začátku těchto názvů představují zkratku pro "verbinput".
Vyšetření parametru ukončeného textem "+\relax" se v makru \db
viscanparameter větví na případ, kdy parametr obsahuje symbol "+" a použije
se pak \db viscanplus. Druhý případ, kdy uživatel nenapsal symbol plus
(takže parametr "#2" makra "\viscanparameter" je prázdný) je dále 
vyšetřen v makru \db viscanminus. Obě makra si oddělí do svých parametrů
první a druhou číslici (každá z nich může být prázdná) a nastaví podle
zdokumentovaných pravidel pro zápis parametru odpovídající interní údaje
"\vinolines" a "\vidolines". Vychází přitom z předpokladu, že registr
"\viline" obsahuje číslo naposledy přečteného řádku (nebo nulu, jsme-li na
začátku souboru).

\inext{viscanparameter}{\count=3 ^^B\cbrace}{++}

Makro \db doverbinput provede samotnou práci: přeskočí "\vinolines" řádků a
přepíše "\vidolines" řádků. To provede v prvním a druhém cyklu "\loop". Než
se k těmto cyklům dostane, musí udělat jisté přípravné práce. Nejprve odečte
od "\vinolines" počet už přečtených řádků, protože při opakovaném čtení
stejného souboru jej neotevíráme znova, jen přeskočíme příslušný menší počet
řádků. Pokud ale se ukáže, že rozdíl je záporný (je potřeba se v souboru
vracet dozadu), makro znovuotevře soubor ke čtení pomocí "\openin" a upraví
podle toho příslušné údaje o řádcích. Pak zahájí skupinu, dále pomocí
"\setverb" nastaví speciálním znakům kategorii 12 a pomocí "\adef{ }{ }"
nastaví mezeře aktivní kategorii (bude expandovat na neaktivní mezeru jako
"\space") a také nastaví kategorii 12 znaku, který byl deklarován pomocí
"\activettchar". Připraví odsazení podle "\ttindent" a spustí uživatelský
"\tthook". Je-li potřeba tisknout čísla řádků, připraví si na to font
"\sevenrm", který má velikost rovnu 0,7 násobku základní velikosti. A pustí se do
zmíněných dvou cyklů "\loop". V obou cyklech se může stát, že narazíme
nečekaně na konec souboru. To je ošetřeno testem "\ifeof\vifile" a následnou
úpravou čítače "\tmpnum" tak, abychom okamžitě vyskočili z cyklu. Druhý
cyklus obsahuje ještě jeden speciální rys: přeje-li si uživatel číst až do
konce souboru, je nastaveno "\vidolines" na nulu a před zahájením cyklu je
čítač "\tmpnum" nastaven na "-1". Uvnitř cyklu je pak zajištěno, že v tomto
případě není čítač zvětšován o jedničku. Po ukončení práce v těchto dvou
cyklech je ukončena skupina, vložena mezera "\ttskip" a makrem "\testparB"
se ověří, zda následuje prázdný řádek.

\inext{doverbinput}{^^B\cbrace}{++}

V prvním cyklu "\loop" v těle makra "\doverbinput" se opakovaně volá
\db vireadline, což je makro, které přečte další řádek ze souboru. V druhém
cyklu se opakovaně volá "\vireadline" následované \db viprintline. Toto
makro vytiskne přečtený řádek do dokumentu. Před řádkem může být v "\llap" 
vytištěno číslo řádku. Záleží na hodnotě "\ttline". Je to naprogramováno v
souladu s uživatelskou dokumentací.

\inext{vireadline}{\empty}{++}


\subsec Jednoduchá tabulka
%%%%%%%%%%%%%%%%%%%%%%%%%%

Tabulku makrem "\table" vytvoříme jako "\vbox", ve kterém je "\halign". Je
tedy potřeba načíst deklaraci typu "{llc|rr}" a převést ji na deklaraci pro
"\halign". Tato deklarace obsahuje znak "#" a tento znak se obtížně přidává
do těla maker. Nashromáždíme tedy postupně deklaraci pro "\halign" do
registru typu "\toks", který je nazvaný \db tabdata. Dále definujeme interní
\db tabstrutA, který bude obsahovat uživatelův "\tabstrut", ovšem přechodně
budeme toto makro měnit. Také deklarujeme čítač \db colnum, ve kterém budeme
mít po přečtení deklarace uložen počet sloupců tabulky.
Dále během skenování "<deklarace>" vytvoříme makro \db
ddlinedata, které bude obsahovat "&\dditem &\dditem ..." (počet těchto dvojic
bude roven $n-1$, kde $n$ je počet sloupců). Pokud je v deklaraci dvojitá
svislá čára, bude v makru "\ddlinedata" na příslušném místě ještě "\vvitem".
Makro "\ddlinedata" pak použijeme v "\crli" a v "\tskip", 
Strýček Příhoda to může použít jinde a jinak. Konečně makro \db vvleft je
neprázdné, pokud úplně vlevo tabulky je dvojitá čára.

\inext{newtoks}{\empty}{+-}

Makro \db table "{<deklarace>}{<data>}" vypadá takto:

\inext{table}{\empty}{+-}

Makro \db scantabdata postupně čte znak po znaku z deklarace "\table" a podle
přečteného znaku ukládá do "\tabdata" odpovídající úsek skutečné deklarace pro
"\halign". Volá přitom "\addtabvrule" nebo "\addtabitem{\tabdeclare<znak>}".

\inext{scantabdata}{^^B\cbrace}{++}

OPmac předdefinuje tři "<znaky>" pro "<deklaraci>", sice "<znaky>" "c", "l", "r"
v makrech \db tabdeclarec, \db tabdeclarel, \db tabdeclarer.

\inext{tabdeclarec}{\empty}{+-}

Makro \db unsskip vkládané na konec každé datové položky odebere mezeru,
pokud má nenulovou základní velikost. Uživatelé totiž někdy dávají kolem
datových položek mezery a někdy ne, přitom chtějí, aby se jim to chovalo
stejně. Je náročné si pamatovat, že mezery před položkou jsou
ignorovány primitivem "\halign", ale mezery za položkou jsou podstatné. Tak
raději i mezery za položkou uděláme nepodstatné.

\inext{unsskip}{}{++}

Příklad: po deklaraci: "{|cr||cl|}" makro "\scantabdata" vytvoří:

\begtt
tabdata: \vrule\tabiteml\hfil#\unsskip\hfil\tabitemr 
        &\tabiteml\hfil#\unsskip\tabitemr \vrule\kern\vvkern\vrule 
        &\tabiteml\hfil#\unsskip\hfil\tabitemr
        &\tabiteml#\unsskip\hfil\tabitemr\vrule
ddlinedata: &\dditem &\dditem\vvitem &\dditem &\dditem
\endtt

Makra \db addtabitem, \db addtabdata a \db addtabvrule vloží do "\tabdata" a
"\ddlinedata" požadovaný údaj. Makro "\addtabitem" pozná podle "\colnum=0", 
zda vkládá data pro první sloupec (nepřidává~"&") nebo pro další sloupce
(přidává~"&"). Makro "\addtabvrule" pozná podle "\tmpa", zda před 
ním předchází další "\vrule". Pokud ano, vloží dodatečnou mezeru
"\kern\vvkern" a přidá "\vvitem" do "\ddlinedata". 

\inext{addtabitem}{\empty}{+-}

Než se pustíme do výkladu dalších maker, předvedeme příklad, ve kterém je
definováno další písmeno "P" pro "<deklaraci>". Písmeno "P" vymezí 
tabulkovou položku, jež má stanovenou šířku a delší text se láme 
do více řádků. Je možné si vyzkoušet třeba tento kód:

\begtt
\newdimen\Pwidth
\def\tabdeclareP {\enskip\vtop{\hsize=\Pwidth \rightskip=0pt plus1fil
    \baselineskip=1.2em \lineskiplimit=0pt \noindent ##\tabstrut}\enskip}

\Pwidth=3cm \table{|c|P|}{\crl
   aaa & Tady je delší textík, který se nevejde na jeden řádek. \crl
   bb  & A tady je taky je něco delšího. \crl}
\endtt

Pusťme se nyní do rozboru maker na ukončení řádků.
Makro \db crl přidá čáru pomocí "\noalign". Makro \db crll přidá dvojitou
čáru pomocí "\noalign".

\inext{crl}{\empty}{+-}

Makro \db crli provede "\cr" a dále se vnoří do řádku tabulky, ve kterém
klade postupně následující 
"\omit\tablinefil &\omit\tablinefil &..." 
Přitom v místě dvojité vertikální čáry naklade navíc "\tabvvline".
Makro \db tablinefil vloží natahovací čáru na šířku celé položky a makro 
\db tabvvline vloží dvě "\vrule" vzdáleny od sebe o "\vvkern". Tím vzniká
přetrzené místo v postupně tvořené lince. Ke správnému nakladení uvedených
povelů použije makro "\crli" obsah makra "\ddlinedata" a vlevo přidává
"\vvleft". Před spuštěním makra "\ddlinedata" definuje odpovídajícím
způsobem \db dditem a \db vvitem.

\inext{crli}{\empty}{+-}

Makro \db tskip prostřednictvím \db tskipA přechodně vyprázdní "\tabstrut"
předefinováním "\tabstrutA" a také vyprázdní "\dditem" a "\vvitem", aby
po použití "\ddlinedata" vznikl řádek tabulky s prázdnými položkami. 
Řádek je vypodložený strutem stanovené výšky "\tmpdim".
Nakonec je potřeba vrátit "\tabstrutA" do původního stavu.

\inext{tskip}{\empty}{+-}

Globální změna šířek všech linek tvořených pomocí "\vrule" a "\hrule" je
provedena makry \db rulewidth a \db rulewidthA. Myšlenka je dokumentována v
TBN na str.~328.
\dgn \nb orivrule
\dgn \nb orihrule

\inext{orihrule}{\empty}{+-}

Makro \db frame "{<text>}" vloží vnější "\vbox{\hrule..\hrule}".
V něm se nachází další box "\hbox{\vrule\kern\vvkern..\kern\vvkern\vrule}"
a v něm "\vbox{\kern\hhkern..\kern\hhkern}". 
Nejvíce uvnitř je pak "\hbox{<text>}". To by pro sazbu rámovaného textu
stačilo, nicméně my ještě řešíme úpravu výsledného boxu tak, aby měl účaří
ve stejném místě jako je účaří textu. Proto uložíme "\hbox{<text>}" do boxu0
a změříme mu hloubku. V proměnné "\tmpdim" spočítáme celkovou hloubku
výsledného boxu. Ve výpočtu přičítáme výšku "\hrule", která nemusí být
0.4pt. Proto si její výšku změříme v boxu1. Ve vnějším "\vbox"u po
nakreslení spodní "\hrule" se pak vracíme pomocí "\kern-\tmpdim" na úroveň
účaří a zde umístíme strut hloubky "\tmpdim", aby sazba směrem dolů
nepřečnívala, ale byla obsažena v~hloubce výsledného boxu.

\inext{frame}{\empty}{+-}


\subsec Vložení obrázku
%%%%%%%%%%%%%%%%%%%%%%%

Nejprve deklarujeme \db picw.

\inext{picw}{\empty}{+-}

Makro \db inspic je zkratka za použití primitivů "\pdfximage", 
"\pdfrefximage" a "\pdflastximage". Kdo si to má pořád pamatovat.
Není-li aktivován PDF výstup, napíšeme jen varování a neprovedeme nic.
Pokud budeme pracovat se stejnými obrázky v jednom dokumentu, bude 
vhodné možnosti primitivů využít naplno.

\inext{ifpdftex}{\empty}{+-}


\subsec PDF transformace
%%%%%%%%%%%%%%%%%%%%%%%%

Makro \db pdfscale "{<vodorovně>}{<svisle>}" pracuje jednoduše:

\inext{pdfscale}{\empty}{+-}

Na druhé straně makro \db pdfrotate "<úhel>" vytvoří
"\pdfsetmatrix{"$\cos\varphi\enspace\sin\varphi\enspace
                {-}\sin\varphi\enspace\cos\varphi$"}",
což není jednoduché, protože funkce $\cos$, $\sin$ nejsou v \TeX{}u
implementovány. Balíček "trig.sty" nabízí vyhodnocování těchto funkcí pomocí
Taylorových polynomů, nicméně OPmac nechce být závislý na balíčcích a také
chce ukázat alternativní způsob implementace. Makro "\pdfrotate" pracuje
zhruba takto: je-li argument 0, neprovede nic, je-li argument 90, provede
otočení o~90 stupňů. V ostatních případech zavolá makro \db pdfrotateA,
které rozloží argument na celou "#1" a zlomkovou "#2" část. V další části 
na řádcích~\cite[rot:360] až \cite[rot:smallsin] se
zabývá jen celými stupni. Nejprve pomocí prvního a druhého "\loop" posune
argument o celé násobky 360 stupňů tak, že poté je argument mezi
0 až 360 stupni, a přitom se hodnoty funkcí $\sin$ a $\cos$ nezměnily.
Ve třetím "\loop" postupně snižuje argument o 90 stupňů a přitom dělá rotaci
o 90 stupňů tak dlouho, až máme argument mezi nulou a devadesáti.
Je-li dále argument větší než 44 stupňů, otočíme se o 45 a snížíme argument
o 45. Je-li dále argument větší než 22, otočíme se o 22 a snížíme argument o
22. Nyní máme argument v množině $\{0, 1, 2, 3, \ldots, 22\}$. Pro každý
prvek z této množiny argumentů máme předpřipraveny hodnoty funkcí 
$\cos$ a $\sin$ v makrech \db smallcos a \db smallsin. Použijeme je pro
závěrečnou rotaci. Tím máme sazbu otočenou o celé stupně.
Další část makra na řádcích \cite[rot:174532] až \cite[rot:else]
řeší jemné dotočení podle zlomkové části argumentu. V intervalu nula až
jeden stupeň aproximujeme funkci $\cos$ konstantní jedničkou a funkci $\sin$
lineární funkcí $x\cdot \pi/180$. V daném rozmezí je to velmi dobrá
aproximace.

\ilabel [rot:360] {360}
\ilabel [rot:smallsin] {smallsin}
\ilabel [rot:174532] {174532}
\ilabel [rot:else] {else\space\space\space\space}

\inext{pdfrotate}{\empty}{+-}

Pro případ, že nepracujeme s PDF výstupem, definujeme klíčové primitivy
pdf\TeX{}u jako makra, která nedělají nic.

\inext{ifpdftex}{\empty}{+-}



\subsec Poznámky pod čarou a na okraji stránek
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Makro \db fnote předpokládá, že správné číslo poznámky na dané stránce je
připraveno v makru "\fn:<číslo>", kde "<číslo>" je celkové číslo poznámky napříč
celým dokumentem sledované globálním čítačem \db fnotenum.
Makro ohlásí svou existenci do REF souboru záznamem "\Xfnote" (bez
parametru). Dále vytiskne značku pomocí "\fnmarkx" a ve skupině přejde na
menší sazbu a zavolá plain\TeX{}ové makro "\vfootnote", které vloží sazbu
pomocí tzv. insertu (TBN, kapitola~6.7). Plain\TeX{}ové nastavení této
třídy insertu není makrem OPmac nijak měněno.

\inext{fnotenum}{^^B\cbrace}{++}

Makro \db fnotemark přičte lokálně k "\fnotenum" svůj parametr a vytiskne
odpovídající značku. Celá práce makra probíhá ve skupině, takže po ukončení
makra se "\fnotenum" vrátí do své původní hodnoty.

\inext{fnotemark}{^^B\cbrace}{++}

Makro \db fnotetext teprve zvedne čítač "\fnotenum" globálně a vytiskne
poznámku pomocí plain\TeX{}ového "\vfootnote".

\inext{fnotetext}{^^B\cbrace}{++}

Makro \db fnmarkx vytiskne otazník nebo \db thefnote. Předpokládá se, že si
uživatel předefinuje "\thefnote" k obrazu svému. Lokální číslo poznámky na
stránce má připraveno v makru \db locfnum.

\inext{fnmarkx}{\empty}{+-}

Při čtení REF souboru se pro každou stranu přečte nejprve "\Xpage", což
je makro, které pronuluje \db fnotenumlocal. 
Makru \db Xfnote tedy stačí pozvednout
"\fnotenumlocal" o jedničku a pomocí 
"\sxdef" si tuto hodnotu zapamatovat v makru "\fn:<číslo>".

\inext{Xfnote}{\empty}{+-}

Registr \db mnotenum. 
globálně čísluje okrajové poznámky a plní podobnou funkci, jako registr
"\fnotenum" pro podčárové poznámky.

\inext{mnotenum}{\empty}{+-}

Makro \db mnote ve vertikálním módu založí box nulové výšky pomocí
"\mnoteA" a vycouvá na původní místo sazby pomocí "\vskip-\baselineskip". V
odstavcovém módu toto makro nalepí box nulové výšky pod právě vytvořený
řádek v odstavci. Víme, že "\vadjust" nalepí svůj materiál bez mezery pod
tento řádek. My ovšem potřebujeme vycouvat nahoru na účaří řádku. To nejde
snadno provést, protože hloubka řádku je proměnlivá. Proto do je řádku
vložen "\strut" a předpokládá se, že nyní má řádek hloubku "\dp\strutbox" a
o tento rozměr makro vycouvá nahoru. Vloží požadovaný box výšky nula na
úrovni účaří a pak se vrátí na původní místo.

\inext{mnote}{^^B\cbrace}{++}

Makro \db mnoteA si zjistí, zda je v makru "\mn:<číslo>" uložen primitivní
příkaz "\left" nebo "\right". Podle toho pozná, zda má umístit poznámku
doleva nebo doprava. Rovněž dá o sobě vědět do REF souboru vložením
sekvence "\Xmnote" (bez parametru). Sazba musí v obou případech vyprodukovat
box nulové výšky i hloubky. Proto je "\vtop", uvnitř kterého je text poznámky
zpracován, vložen přechodně do boxu0 a je mu pronulována hloubka. Nulová
výška je zařízena pomocí "\vbox to0pt{\vss\box0}". Vlastní sazbu poznámky
zahajujeme pomocí "\noindent" s tím, že je připraven pružný "\leftskip" nebo
"\rightskip" podle toho, zda poznámku klademe vlevo nebo vpravo.
Při kladení vlevo musíme použít "fill", abychom přeprali natahovací mezeru z
"\parfillskip".

\inext{mnoteA}{^^B\cbrace}{++}

Makro \db Xmnote pracuje během čtení REF souboru a využívá toho, že makro
"\Xpage" nastavuje číslo právě procesované strany do registru "\lastpge".
Takže stačí použít "\sxdef" následujícím způsobem:

\inext{Xmnote}{\empty}{+-}

Makro \db fixmnotes "<token>" předefinuje v cyklu všechny definované
kontrolní sekvence "\mn:<číslo>" tak, že budou obsahovat "<token>".
Tím toto makro zlikviduje práci makra "\Xmnote", ale o to nám právě jde v
případě, když chceme mít poznámky na jednoznačně dané straně.

\inext{fixmnotes}{\empty}{+-}


\subsec Bibliografické reference
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Nejprve uvedeme deklarace deskriptoru \db auxfile a čítačů 
\db bibnum a \db lastcitenum.

\inext{auxfile}{\empty}{+-}

Makro \db cite "[<lejblík1>,<lejblík2>,...]" si prostřednictvím \db citeA
zavolá "\rcite{<lejblík>}" pro každou jednotlivou položku oddělenou čárkou ve
svém parametru. Makro "\citeA" v sobě skrývá fintu: parametr nemá jediný,
ale dva "#1#2". Protože první z nich je neseparovaný, ignorují se případné
mezery za čárkou a "#1" obsahuje první písmeno "<lejblíku>". Uvnitř makra
měníme \db citesep, což je čárka a mezera, která se má mezi údaji
vytisknout. Makro \db nocite "[<lejblík1>,<lejblík2>,...]" je definováno
stejně, jen připraví jiný význam makra "\docite", krz které budeme tisknout
v "\rcite" potřebný údaj. Veškerá
činnost maker "\cite" a "\nocite" probíhá uvnitř skupiny.

\inext{cite}{\empty}{+-}

Makro \db rcite "{<lejblík>}" řeší zhruba řečeno následující věci:


\begitems
\item * Zjistí, zda je definován "\csname bib:<lejblík>\endcsname". Pokud ano,
  vytiskne jeho hodnotu, pokud ne, vytiskne do textu otazníky a na terminál
  varování. Tato kontrolní sekvence začne být známá po použití
  "\bib[<lejblík>]" nebo "\bibitem{<lejblík>}". Tato makra uloží
  odpovídající informaci do REF souboru, odkud ji při opakovaném \TeX{}ování 
  vyzvedneme. Je to klasická činnost, kterou provozujeme i u ostatních
  křížových referencí.
\item * Uloží o sobě zprávu do bufferu "\citelist". To použijeme
  v makrech "\usebibtex" nebo "\usebbl".
\enditems

Makro "\rcite" je naprogramováno zhruba takto

\def\begtthook{\langleactive}
\begtt
function rcite(<lejblík>) {
  if (<lejblík> == '*') { <zapiš do> \citelist '*'; return; }
  if (\bib:<lejblík> == nedef) {
    <zapiš do> \citelist <lejblík>;
    <na terminál:> "Warning, cite [label] unknown";
    <do tiskového výstupu:> "??";
    \bib:<lejblík> = empty;
    return;
  if (\bib:<lejblík> == empty) {
    <do tiskového výstupu:> "??";
    return;
  }
  if (\bib:<lejblík> končí znakem '&') {
    <zapiš do> \citelist <lejblík>;
    <odstraň znak & z obsahu makra> \bib:<lejblík>;
  }
  <tiskni obsah makra> \bib:<lejblík>;
} 
\endtt

\def\begtthook{\langleactive}
Výklad kódu: Protože chceme šetřit pamětí bufferu "\citelist", zapisujeme
tam každý "<lejblík>" jen jednou. Zda se nedeklarovaný "<lejblík>" vyskytl
poprvé poznáme podle nedefinované hodnoty "\bib:<lejblík>". Zda se vyskytl
později znovu poznáme podle toho, že má hodnotu "empty". Zda se deklarovaný
"<lejblík>" vyskytl poprvé poznáme podle znaku "&" v jeho obsahu. 

Návrh kódu v C-like notaci nyní převedeme do maker v \TeX{}u:

\inext{rcite}{^^B\cbrace}{++}

Asi nejzajímavější vychytávka v tomto makru se týká testu na znak "&".
Implicitně při čtení REF souboru se do makra "bib:<lejblík>" uloží
"\bibnn{<hodnota>}&". Příkaz "\if" za sebou totálně expanduje vše
následující, takže nejprve narazí na "&", pak se obsah "bib:<lejblík>"
expanduje prostřednictvím \db bibnn "{<hodnota>}" na nic a za tímto 
\uv{nic} se zjeví druhý znak "&", který se tedy přilepí na ten první. 
Ano, je pravda, že tyto dva znaky jsou stejné. 
Odstranění tohoto znaku probíhá znovu totální expanzí,
tentokrát "\bibnn" první parametr "<hodnota>" zopakuje a druhý parametr
se znakem "&" zahodí.

Než se pustíme do výkladu makra "\docite", připravíme si makra \db printcite
"<položka>" a \db printdashcite "<položka>". První z nich tiskne jednu 
položku oddělenou od případné
další čárkou, druhé tiskne položku, před kterou předchází pomlčka
vyznačující interval položek. Pointa makra "\printcite" je v tom, že si samo
po prvním zavolání připraví separátor "\citesep" (který je na začátku
činnosti "\cite" prázdný), takže při opakovaném volání "\printcite" se
vytiskne i požadovaný separátor. Pomlčka v "\printdashcite" je schována do
"\hbox", aby nedocházelo těsně za ní ke zlomu řádku.

\inext{printcite}{\empty}{+-}

Makro \db docite "<položka>" vytiskne otazníky při prázdném parametru a jinak
vytiskne prostřednictvím "\printcite" jednu "<položku>". Kromě toho řeší při
nenulovém "\lastcitenum" slučování po sobě následujících čísel položek do
intervalů. Naposledy vytištěnou položku uchovává v registru "\lastcitenum".
Při příštím zavolání zvětší "\lastcitenum" o jedničku a srovná ji s
"<položkou>". Jsou-li si rovny, jde o následující položku v řadě a takovou
položku netiskneme, nicméně si její hodnotu uchováme v "\tmpb". Pokud je
mezi souvislou řadou položek díra, tj. "\lastcitenum" se nerovná
"<položce>", pak dovytiskneme předchozí interval pomocí 
"\printdashcite{\the\tmpb}" a následně vytiskneme i "<položku>".
Makro \db shortcitations jednoduše nastavuje "\lastcitenum" na nenulovou
hodnotu a tím probudí k životu hlavní část makra "\docite".

\inext{docite}{\empty}{+-}

Následuje kód makra \db bib "[<lejblík>]", které prostřednictvím
\db wbib "{<lejblík>}{<hodnota>}" vloží do REF souboru propojené údaje o
tom, jaké má "<lejblík>" přiřazeno číslo v seznamu literatury.
Makro "\wbib" připojí před "\wref" příkaz "\immediate" právě tehdy, když
"\wref" je ve stavu, kdy skutečně zapisuje do souboru REF.
Makro \db Xbib pracuje při čtení souboru REF a dělá to, co jsme si řekli už
dříve: nastaví hodnotu makra "\bib:<lejblík>" na "\bibnn{<hodnota>}&".

\inext{bib}{\empty}{+-}

Makro \db addcitelist "{<lejblík>}" přidá do \db citelist údaj ve tvaru
"\lcite[<lejblík>]". Hranaté závorky jsou použity proto, aby fungoval test
"\isinlist\citelist{[<lejblík>]}". Jak uvidíme za chvíli, makro
"\addcitelist" změní během činnosti makra "\usebibtex" svůj význam na
\db writeaux, aby případné použití "\cite" až za "\usebitex" rovnou
zapisovalo do AUX souboru. Podobně makro "\addcitelist" změní v makru
"\usebbl" svůj význam \db writeXcite "{<lejblík>}", 
aby v příštím průchodu \TeX{}em
mělo makro "\usebbl" přehled i o výskytech "\cite", které jsou napsány 
později, než "\usebbl".

\inext{addcitelist}{\empty}{+-}

Než se pustíme do výkladu maker "\usebibtex", "\genbbl" a "\usebbl", uvedeme
stručně popis činnosti Bib\TeX{}u. Příkaz "bibtex <dokument>" způsobí,
že program {\tt bibtex} se podívá do souboru "<dokument>.aux" a tam si všímá
sekvencí \db bibdata "{<bib-báze>}", \db bibstyle "{<bib-style>}" a \db
citation "{<lejblík>}". Na základě toho následně přečte soubor
"<bib-báze>.bib" se zdrojovými zápisy bibliografických údajů.
Pro konverzi těchto zdrojových zápisů do výstupního souboru "<dokument>.bbl"
použije stylový soubor "<bib-style>.bst". Není-li mezi sekvencemi
"\citation" uvedeno "\citation{*}", program {\tt bibtex} zahrne do výstupu
jen ty bibliografické údaje, které mají "<lejblík>" shodný s některým z
"<lejblíků>" uvedených v parametrech sekvencí "\citation". Každá sekvence
"\citation{<lejblík>}" v souboru "<dokument>.aux" typicky odpovídá jednomu
použití příkazu "\cite[<lejblík>]".

Makro \db usebibtex "{<bib-báze>}{<bst-styl>}" otevře soubor AUX
prostřednictvím \db openauxfile "{<bib-báze>}{<bst-styl>}". Napíše tam tedy
požadovaná data pro Bib\TeX{}. Dále z "\citelist" přepíše do AUX souboru
lejbílky ve formátu "\citation{<lejblík>}". Nakonec se uvnitř skupiny pustí do
čtení souboru BBL prostřednictvím makra "\readbblfile".

\inext{usebibtex}{\count=2 ^^B\cbrace}{++}

Makro \db readbblfile "{<soubor>}" vyzkouší, zda je "<soubor>.bbl" připraven
ke čtení. Pokud ne, podá o tom odpovídající zprávu na terminál. Jinak
nastaví čítač "\bibnum" na nulu a (vědomo si toho, že je spuštěno ve
skupině) pustí se do lokálních re-definic La\TeX{}ových konstrukcí, které se
typicky v BBL souborech používají. Nastaví "\leftskip" na "\iindent" a
spustí "\bibtexhook". Konečně načte soubor BBL.

\inext{readbblfile}{^^B\cbrace}{++}

V BBL souboru se vyskytují povely \db bibitem. Za každým z nich se možná
objeví parametr v~hranaté závorce "[<značka>]" a následně je uveden
"{<lejblík>}". Pak na dalších řádcích jsou bibliografická data jednoho
záznamu ukončená prázdným řádkem. Objeví-li se "[<značka>]", dává tím
Bib\TeX{} najevo, že se má tato "<značka>" použít místo běžného číslování
záznamů. Následuje kód, který takové údaje přečte, vytiskne a vloží do REF
souboru o tom zprávu prostřednictvím "\wref{<lejblík>}{<hodnota>}".
Rozlišují se dva režimy tisku: není-li přítomna "[<značka>]" (makro "\tmpa"
je prázdné), pak pomocí "\llap" vytiskneme "[<číslo>]". Jinak se posuneme o
"-\iindent" a tiskneme "[<značku>]" následovanou mezerou. Pak se vytisknou
další údaje bibliografického záznamu.
\dgn\nb bibitemB \dgn\nb bibitemC \dgn\nb bibitemD

\inext{bibitem}{^^B\cbrace}{++}

Makro \db genbbl "{<bib-báze>}{<bst-style>}" otevře AUX soubor a zapíše do
něj údaje potřebné pro Bib\TeX{} včetně "\citation{*}". Poté se makro pokusí
přečíst výstup z Bib\TeX{}u pomocí "\readbblfile". V tomto případě pracuje
"\bibitem" ve zvláštním režimu, kdy netiskne "<hodnoty>", ale "<lejblíky>".
Z toho důvodu je předefinováno makro "\bibitemC".

\inext{genbbl}{^^B\cbrace}{++}

Makro \db usebbl "/<typ> <bbl-file>" spustí jiné makro s názvem "\bbl:<typ>".
Tři taková makra jsou definována pomocí "\sdef". První "\bbl:a" je
jednoduché: prostě projde BBL soubor a vytiskne údaje z~něj.
Druhé makro "\bbl:b" projde BBL
soubor v režimu, při kterém jsou bibliografická data každého záznamu (až po
prázdný řádek alias "\par") přečtena do parametru "#2" makra "\bibitemC".
Celý údaj je pak vytištěn jen za předpokladu, že "[<lejblík>]" je přítomen 
v seznamu "\citelist". Třetí makro "\bbl:c" pracuje jako druhé až na to, že
údaj netiskne, ale zapamatuje si ho do makra "\bb:<lejblík>". Po takovém
projití BBL souboru ještě projde "\citelist", kde se "\lcite[<lejblík>]"
promění v "\bb:<lejblík>", takže se záznam vytiskne. Nyní ale v pořadí, v
jakém jsou "<lejblíky>" zařazeny do "\citelist".

\inext{usebbl}{\count=3 ^^B\cbrace}{++}

Za zmínku stojí ještě práce uvedených maker s "\citelist". Před výskytem makra
"\usebbl" se lejblíky z "\cite" a "\nocite" hromadí v "\citelist". Ovšem
další "\cite" a "\nocite" se mohou vyskytovat za příkazem "\usebbl". Pokud
se tak stane, pracuje "\addcitelist" nyní ve významu "\writeXcite" a uloží
potřebnou informaci do REF souboru. Při dalším \TeX{}ování se tato informace
přečte makrem \db Xcite "{<lejblík>}" z REF souboru takto:

\inext{Xcite}{}{++}

\noindent To tedy znamená, že se uloží do seznamu "\citelistB". Konečně
makra "\bbl:b" a "\bbl:c" si dva seznamy "\citelist" a "\citelistB" před svou
činností spojí do seznamu jediného nazvaného "\citelist".


\subsec Úprava output rutiny
%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Místo původního makra "\plainoutput" používá OPmac makro \db opmacoutput.

\inext{opmacoutput}{}{++}

OPmac mění output rutinu proti originální "\plainoutput" jen v nejnutnějších věcech.
Řeší tyto dva problémy:

\begitems
\item * Místo přímého "\shipout" nechá nejprve box sestavit jako "\box0",
  pak provede "\protectlist" a pak provede "\shipout\box0".
\item * Mezi "\makeheadline" a "\pagebody" vkládá "\prepage".
\enditems

První úprava zabrání expanzi maker přidaných pomocí "\addprotect" v době
práce příkazu "\shipout", tedy v době, kdy expandují parametry "\write".
Těmto makrům je přidělen prostřednictvím \db doprotect "<makro>" 
pro tento okamžik význam "\relax". Je potřeba ještě
vysvětlit, proč bylo nutné sestavit nejprve "\box0" a teprve poté jej poslat
ven pomocí "\shipout". Je to z toho důvodu, že v době sestavování "\box0"
jsou expandována "\headline" a "\footline" a pro ten případ ještě chceme,
aby všechna makra správně expandovala. 

\inext{opmacoutput}{\empty}{+-}

Druhá úprava vkládá makro \db prepage, které zajistí tisk stránkové číslice
do "\reffile" (to je docela užitečné) a kromě toho nastaví správnou barvu
textu strany pomocí PDF speciálu. 

\inext{prepage}{\empty}{+-}

Když budou barvy přecházet na další stranu, jistě nechceme mít obarvenu
stránkovou číslici. Je tedy potřeba do \db footline vložit "\Black". Když
bude uživatel měnit velikost fontů v dokumentu, jistě nechce mít
stránkovou číslici pokaždé jinak velkou. Proto je do "\footline" vloženo
"\thefontsize". Je nastaveno pevně na 10pt. Předpokládáme, že pokud bude
někdo chtít jinak velkou stránkovou číslici, jednoduže si "\footline"
nastaví podle svého. Jinak je "\footline" shodná s původním nastavením 
v~plain\TeX{}u.

\inext{footline}{\empty}{+-}


\subsec Okraje
%%%%%%%%%%%%%%

V registrech \db pgwidth a \db pgheight budeme mít po zavolání "\setpagedimens"
šířku a výšku strany. V~registru \db shiftoffset budeme mít případný rozdíl
okrajů mezi levou a pravou stránkou.

\inext{pgwidth}{\empty}{+-}

Makro \db margins "/<typ> <formát> (<levý>,<pravý>,<horní>,<dolní>)<jednotka> " 
si nastaví registry "\pgwidth" a "\pgheight" prostřednictvím "\setpagedimens" a dále v
souladu s uživatelskou dokumentací nastaví potřebné okraje. V makru "\tmp"
je schována jednotka, kterou uživatel taky může zapomenout napsat. V~takovém
případě vypíšeme varování a doplníme jednotku "mm". Jakmile měníme
"\hoffset" nebo "\voffset", nastavíme je nejprve na "-1in" (tím se dostaneme
na okraj papíru) a pak budeme požadovanou velikost okraje k těmto registrům
přidávat. Nemohu za to, že Knutha napadla taková ne příliš podařená myšlenka
dát výchozí bod sazby kamsi doprostřed papíru umístěný pomocí ujetých
jednotek. Za zmínku stojí ještě dvě myšlenky. Makro \db rbmargin
"\h(v)offset\h(v)size{<okraj>}" provede výpočet hodnoty "\hoffset" nebo
"\voffset" v případě, že je dána protější hodnota okraje než je okraj přímo
nastavitelný pomocí "\h(v)offset".
A konečně posun okraje při přechodu z pravé na levou stránku "\shiftoffset"
počítáme jako "\pgwidth"~"-"~"\hsize"~"-"~"2*<levý>" což dá stejnou hodnotu jako
"<pravý>-<levý>". Změna "\hoffset" o tuto hodnotu je provedena v makru
"\pghook", tedy v "\output" rutině, schována do skupiny, takže po ukončení
"\output" rutiny se vrátí "\hoffset" na původní hodnotu.

\inext{margins}{\empty}{+-}

Makro \db setpagedimens "<formát> " spustí 
\db setpagedimensA "<šířka> <výška>/" a k tomu musí 
dopředu vyexpandovat obsah makra "\pgs:<formát>". To provedeme pomocí tří
"\expandafter".

\inext{setpagedimens}{\empty}{+-}

Jednotlivé "<formáty>" papíru je potřeba deklarovat.

\inext{sdef}{\empty}{+-}

Makro \db magscale "[<factor>]" zvětší/zmenší sazbu nastavením 
registru "\mag" a definuje dosud prázdné makro \db trueunit hodnotou 
"true", aby později při činnosti makra "\setpagedimensA" zůstaly zachovány
rozměry stránek. Pokud ale je makro "\magscale" spuštěno až po nastavení
velikosti stránek, jsou tyto velikosti dodatečně korigovány na \uv{true} 
jednotky pomocí makra \db truedimen.

\inext{trueunit}{\empty}{+-}


\subsec Závěr
%%%%%%%%%%%%%

Nakonec pomocí "\inputref" přečteme REF soubor (pokud existuje) a vrhneme se
na zpracování dokumentu, který nám připravil uživatel. Přeji dobré pořízení. 

\inext{inputref}{\empty}{+-}

\doindex


\bye


