%%  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{Mar. 2014}
\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{%
   Tučně je označena strana, kde je slovo
   dokumentováno, pak následuje seznam všech stran, na kterých se slovo 
   vyskytuje.
   \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.
Xe\TeX{} sice není pdf\TeX, ale po dobu čtení maker jej za pdf\TeX{} budeme
považovat a na konci čtení maker (viz sekci~~\ilink[zaver]{\numref[zaver]}) 
to spravíme.

\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.
"\lccode" nastavíme ve skupině, takže po ukončení skupiny se vrací k
výchozí hodnotě.

\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}{}{+-}

Podobně je naprogramováno makro \db bslash, které vytiskne obyčejné zpětné
lomítko:

\inext {bslash}{}{+-}

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 {muskip}{}{+-}

Definovaná makra chceme při "\write" do souboru nechat v původním stavu:

\inext {addprotect}{}{+-}

Makro "\exfont" se vyskytuje v souboru "exchars.tex" z \CS{}plainu.
Příkaz "\addprotect\exfont" zaprotektuje všechny znaky deklarované v toto
souboru naráz. Podrobnosti lze nalézt v uvedeném souboru.


\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
\dgn \nb toclinehook
\dgn \nb mnotehook
\dgn \nb captionhook

\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}{+-}

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}{+-}

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". Soubor "ams-math.tex" načteme jen tehdy, když není definováno
"\normalmath". Je totiž možné, že uživatel načetl matematické makro ještě
před zavoláním "\input opmac".

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

Po načtení souboru "ams-math.tex" disponujeme makry \db regtfm na registraci
různých metrik pro různé designované velikosti fontů a \db whichtfm je
definováno tak, aby 
expandovalo 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}. 

Č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í.
Na řádku~\cite[typoscale:roundA] je "#1" převedeno na "(#1/1000)pt":
Číslo 3277sp je $2^{16}/20$sp, tedy 1/20pt. Tato hodnota je nejprve vynásobena
"#1" a vydělena 50. Proč bylo číslo 1000 rozloženo na $20\times50$?
Aby nedošlo k přetečení hodnoty typu dimen při velkém "#1".

\ilabel [typoscale:roundA] {3277sp}

\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 typobase nastaví "\baselineskip" a "\fontdim" podle
\db baselineskipB a \db fontdimB, což jsou makra, která mají uloženu
základní velikost řádkování a základní velikost písma.

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

Makro \db em přepíná kontextově do odpovídající varianty a ve spolupráci s
makry "\additcorr" a "\afteritcorr" 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.
Makro \db afteritcorr se probudí k činnosti na konci skupiny a přidá
italickou korekci, pokud nenásleduje tečka nebo čárka.

\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}{+-}

Je-li detekován místo CSplainu e\TeX, nastavíme značky jazyků dle hodnoty
"\language" v e\TeX{}u:

\inext {uselanguage}{\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. Ostatní balast v kódu (kontrolující definovanost
makra "\csname l0:<lejblík>\endcsname") je od toho, aby OPmac pohlídal
případné dvojí použití stejného "<lejblíku>" a upozornil na to.

\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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Od verze OPmac Jul. 2013 jsou zcela přepracována pomocná makra pro návrh
typografie kapitol, sekcí a podsekcí. Nyní má autor typografického návrhu
lépe pod vlastní kontrolou, co se vkládá do vertikálního seznamu, což je pro
programování možných stránkových zlomů a nezlomů důležité. 

Autor typografie dokumentu by měl definovat pro
tisk kapitoly, sekce a podsekce makra "\printchap", "\printsec" a
"\printsecc". Makra mají jeden parametr "#1", který obsahuje text titulku.
Typická struktura každého takového makra je:

\bgroup\def\begtthook{\langleactive}
\begtt
\par 
<penalta obvykle záporná, neboli bonus, pro zlomení stránky před nadpisem>
<mezera před nadpisem>
{<nastavení fontu> \noindent \dotocnum{<značka>}#1\nbpar}
<případné vložení značky (insertmark) pro plovoucí záhlaví>
\nobreak <mezera pod napisem>
\endtt
\par\egroup

Pro realizaci maker "\printchap", "\printsec" a "\printsecc" může autor
návrhu využít následujících interních maker OPmac:

\begitems
\item* "\dotocnum{<značka>}" -- umístí cíle odkazů, zařídí obsah, vytiskne "<značku>"
\item* "\thetocnum"  -- "<značka>", např. 3.2.4 pro secc, 3.2 pro sec a 3 pro chap
\item* "\insertmark{<text>}" -- vloží "\mark" s expandovaným "\thetocnum" 
        a neexpandovaným "<textem>"
\item* "\nbpar" -- jako "\par", ale mezi řádky je nezlomitelná mezera
\item* "\remskip<velikost>" -- mezera (pod nadpisem) odstranitelná 
        následujícím "\norempenalty"
\item* "\norempenalty<číslo>" -- vloží penaltu "<číslo>" jen pokud nepředchází 
       "\remskip"
\enditems

Aby fungoval obsah a cíle odkazů, je {\it nutné\/} použít "\dotocnum".
Parametr makra "\dotocnum" nemusí obsahovat jen "\thetocnum", ale také
tečky a mezery kolem "<značky>". Předchází-li "\nonum", makro "\dotocnum"
nevytiskne celý svůj druhý prametr, tedy včetně případného \uv{okolí}
značky. Návrh tisku sekce může vypadat takto:

\begtt
\def\printsec#1{\par
  \norempenalty-500
  \vskip 12pt plus 2pt
  {\secfont \noindent \dotocnum{\thetocnum\quad}#1\nbpar}%
  \placemark{#1}%
  \nobreak \remskip 6pt plus 1pt
}
\endtt
V tomto návrhu bude nad nadpisem penalta $-500$ (bonus za zlomení nad
nadpisem), dále je "12pt" mezera, pak je titulek "#1" vytištěný fontem
"\secfont". Před tímto titulkem je číselná značka oddělená od titulku
mezerou "\quad". Titulek může být na více
řádcích. Protože je ukončen "\nbpar", nebude povolen mezi jednotlivými řádky
titulku řádkový zlom. 

Vysvětlíme si nyní na příkladu činnost a smysl "\remskip" a "\norempenalty".
Předpokládejme pro ilustraci definici "\printsec", jako je uvedená výše.
Pokud je dále třeba definice podsedkce "\printsecc" zahájena příkazy 

\begtt
\par \norempenalty-200 \vskip 8pt plus2pt
\endtt
%
pak se mohou dít tyto věci:

\begitems
\item* Následuje-li podsekce těsně za sekcí, pak se vymaže spodní mezera od sekce
  "6pt plus1pt" a místo ní se vloží mezera "8pt plus2pt". Mezery se tedy
  nesčítají. Navíc v tomto případě se nevloží penalta $-200$, takže mezi
  sekcí a podeskcí nedojde nikdy ke stránkovému zlomu.
\item* Následuje-li za sekcí obyčejný text, pak je pod sekcí a nad textem mezera
  "6pt plus 1pt", která je nezlomitelná.
\item* Předchází-li před podseskcí obyčejný text, pak se vloží před nadpisem
  podsekce "\penalty-200" následovaná "\vskip 8pt plus2pt". Tato mezera je
  ochotně zlomitelná (bonus $-200$), takže se může nadpis podsekce objevit na
  následující straně.
\enditems

Je možné mezeru pod nadpisem složit ze dvou druhů:

\begtt
\def\printsec{%
  ...
  \nobreak \vskip 2pt \remskip 4pt plus1pt}
\endtt

V tomto příkladě se odstraní při následující podsekci z celkové mezery 
"6pt plus1pt" jen její část "4pt plus1pt".

Defaultní hodnoty maker \db printchap, \db printsec a \db printsecc vypadají
v OPmac takto:

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

Příkazem "\firstnoindent" dávají tato makra najevo,
že následující odstavec nebude mít odstavcovou zarážku.

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}{++}

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}{+-}

V další části této sekce je implemantace maker "\chap", "\sec" a "\secc".
Pro číslování kapitol, sekcí a podsekcí potřebujeme čítače a další registry:

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

\inext{newcount}{newcount}{++}

Makro \db notoc je možno použít jako prefix před "\chap", "\sec", "\secc" s
tím, že se kapitola, sekce, podsekce nedostane do obsahu. Makro
\db nonum je možnot použít jako prefix před stejnými makry s tím, že
kapitola, sekce, podsekce nebude mít číslo. I nečíslovaní kapitola se může
dostat do obsahu. Je-li obsah klikací, pořebuje mít svoje referenšní číslo
pro vytvoření linku. K tomu slouží registr "\nonumnum". 

\inext{notoc}{\empty}{+-}

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. Aktuální čítač má vždy název \db thetocnum.
S touto hodnotou (nazávisle na tom, zde jde o kapitlu, sekci nebo podsekci,
bude pracovat "\dotocnum". Dále makra připraví obsah makra \db dotocnumafter,
což je proměnlivá část makra "\dotocnum".
Konečně uvedená makra "\chap", "\sec" a "\secc" 
zavolají odpovídající makro "\printchap",
"\printsec" a "\printsecc".

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

V proměnlivé části makra "\dotocnum", tedy v "\dotocnumafter", se řeší
uložení informací o kapitole, sekci nebo podsekci do obsahu. K tomu je
využito makro \db wcontents, které provede 
\bgroup\def\begtthook{\langleactive}
\begtt
\write\reffile{\string#1{<expandovaný thetocnum>}{#2}{\the\pageno}}
\endtt
\par\egroup

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

Makro \db dotocnum "{<text>}" umístí cíl odkazu do místa, které je od
účaří vzdáleno o "\destheight". Toto místo se kryje s horní hranou okna
prohlížeče po kliknutí na odkaz. Dále toto makro vygeneruje data pro obsah
pomocí předpřipraveného "\dotocnumafter".
Pokud předchází "\notoc", makro nezapíše nic do obsahu. Pokud předchází
"\nonum", makro nevytiskne svůj parametr, takže je titulek bez čísla.
Dále v~takovém případě je pro hyperlinkové odkazy číslo "\nonumnum" uvozeno
vykřičníkem, což navazuje v~obsahu na makro "\toclinkA".

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

V makrech "\chap", "\sec", "\secc" je po zavolání "\printchap", "\printsec"
nebo "\printsecc" voláno \db resetnonunotoc, které vrátí hodnotu přepínačů
"\ifnonum" a "\ifnotoc" na imlicitní hodnoty. Tím je zaručeno, že makra
"\nonum" a "\notoc" ovlivní jen následující kapitolu, sekci nebo podsekci
a fungují jako prefixy. Makro navíc kvůli přechodu na verzi OPmac Jul. 2013
kontroluje, zda makra "\printchap", "\printsec" a "\printsecc" skutečně
použila makro "\dotocnum". Pokud ne, náležitě na to upozorní.

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

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 "{<text>}" vloží do "\mark" data ve formátu 
"{<thetocnum>} {<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.

Makro \db remskip "<velikost>" je implimentováno jako "\vskip<velikost>"
následované smluvenou nezlomitelnou penaltou 11333. Makro \db norempenalty
pak podle této hodnoty poslední penalty v seznamu větví svou činnost.
V registru \db remskipamount je uložena naposledy vložená mezera z "\remskip".

\inext{remskip}{\empty}{+-}

Makro \db othe pracuje stejně jako primitiv "\the" s tím rozdílem, že
nezobrazí nic (a sejme následující tečku), pokud je hodnota registru nulová.
Tímto způsobem lze tisnout dokument jen se sekcemi bez kapitol. Číslo
kapitoly se pak nezobrazuje jako "0.", protože se nezobrazuje vůbec.

\inext{othe}{\empty}{+-}

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 a vytiskne zahájení popisku makrem "\printcaption".
Makro "\caption" tím končí svou činnost a dále je zpracován 
odstavec s~nastavenými
"\leftskip", "\rightskip". Sekvence "\par" je předefinována tak, že první výskyt 
"\par" (alias 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 printcaption "{<slovo>}{<číslo>}" vytiskne zahájení popisku
tabulky a obrázku. Za číslem implicitně není žádná interpunkce, jen
"\enspace". Je možné předefinovat "\printcaption" s interpunkcí třeba takto:
"\def\printcaption#1#2{{\bf#1 #2:}\space}"

\inext{printcaption}{\empty}{+-}

Předefinujeme makro z plain\TeX{}u "\endinsert" tak, že dopředu vložíme
"\par". Pak bude možné těsně za odstavec zahájený pomocí "\caption" vkládat
"\endinsert". Těžko lze totiž přesvědčovat uživatele, aby tam dával prázdný
řádek.

\inext{endinsert}{\empty}{+-}

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}{+-}

Makro \db toclinkA je citlivé na vykřičník jako první znak argumentu. Pokud
tam je, vytiskne jen mezeru velikosti 0.8em, jinak vytiskne argument.
Vykřičník do argumentu dáme v případě kapitol a sekcí prefixovaných
jako "\nonum". V obsahu pak nechceme mít žádné číslo, jen příslušnou mezeru.

\inext{toclinkA}{\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. Toto porovnání je protaženo krz
"\meaning", protože nechceme porovnávat kategorie, ale jen stringy.
Pokud se stringy rovnají, místo podslova se vloží \db
iiemdash, což je pomlka. Na konci činnosti se
nastaví "\previi" na \db currii (nové slovo se pro další zpracování stává
předchozím) a vytiskne se 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] Barvy
%%%%%%%%%%%%%%%%%%%%%

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é.
\enditems

\noactive {\nb writecolorfalse} \noactive {\nb writecolortrue}
 
Deklarujeme \db ifwritecolor, což nastaveno na true způsobí, že makro
"\writecolor" bude zapisovat do REF souboru informaci o zrovna nastavené barvě.
Tuto informaci pak zužitkujeme ve výstupní rutině při nastavování barev,
které přetékají ze strany na stranu.
Dále deklarujeme \db lastpage. Tento registr budeme 
potřebovat pro identifikaci jednotlivých stran při čtení REF souboru.

\inext{ifwritecolor}{\empty}{+-}

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}{+-}

Makro \db setcmykcolor nastaví barvu textu (při "\pdfK" obsahující \uv{k}) 
nebo barvu linek (při "\pdfK" obsahující
\uv{K}). Dále si toto makro globálně uloží nastavenou barvu do 
\db currcolork, resp. \db currcolorK. Také podle předchozí "\currcolork",
resp. "\currcolorK" testuje, zda je potřeba aktuální barvu
změnit. Pokud ne, tak "\pdfliteral" pro nastavení barvy nevloží.

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

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ě
nastavit barvu, která přetekla. Ovšem jak poznáme, že něco přeteklo do další
strany? Jedině pomocí asynchronního "\write". Proto makro
"\setcmykcolor" nevkládá do PDF jen požadovaný speciál, ale taky ukládá
pomocí \db writecolor "<k nebo K>" informaci do REF souboru ve formátu 
"\Xpdfcolork{<CMYK barvy>}" nebo "\XpdfcolorK{<CMYK barvy>}". 

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

Makro \db pdfK má implicitně hodnotu "k" (barva pro texty a plochy) a
přechodně při použití \db linecolor "<přepínač barvy>" má hodnotu "K". 

\inext{pdfK}{\empty}{+-}

Výchozí hodnoty maker "\currcolork" a "\currcolorK" jsou rovny barvě černé,
neboli makru \db pdfblackcolor.

\inext{pdfblackcolor}{\empty}{+-}
 
Následuje výklad makra \db localcolor. Uvědomíme si, co od něj
vlastně očekáváme. Ukázka první:

\begtt
\Blue základní text je modrý.
{\localcolor \Green Zelený, {\localcolor \Red červený,} tady zpátky zelený,
 \Grey Gandalf šedý} a tady zpátky základní modrý text.
\endtt

Ukázka druhá:

\begtt
\Blue základní text je modrý.
{\localcolor \Green \linecolor\Red Tady je zelený text a červené linky.}
Zde je zpátky text modrý a linky černé.
\endtt

Z ukázky první plyne, že "\localcolor" si musí uložit informaci o právě
nastavené barvě do zásobníku, ze kterého ji na konci skupiny vyzvedneme
makrem "\restorecolor". Toto makro pošleme na konec skupiny příkazem
"\aftergroup". Z ukázky druhé plyne, že do zásobníku musíme uložit nejen
informaci o barvě textu (k) ale též informaci o barvě linek (K) a makro
"\restorecolor" musí zrestaurovat oba typy barev.

Pro zásobník je rezervováno makro \db savedcolors, do kterého jsou údaje
vkládány vlevo a zleva jsou též vyzvedávány. Jeden údaj je
ve tvaru "<vzkaz>{<barva k>}{<barva K>}". Výchozí hodnota zásobníku je
prázdná.

\inext{savedcolors}{\empty}{+-}

Často se stává, že barvy uvnitř skupin jsou současně barvami v boxech a pak
máme jistotu, že barva nepřeteče to další strany. Je tedy zbytečné ukládat
informaci o přechodu barev do REF souboru. Takže makro "\localcolor"
nastavuje lokálně uvnitř dané skupiny "\writecolorfalse". Pokud uživatel
pracuje se skupinou, která má tendenci utéci na další stranu, použije místo
"\localcolor" makro \db longlocalcolor. Makra "\localcolor" a
"\longlocalcolor" tedy vypadají takto:

\inext{localcolor}{\empty}{+-}

Vidíme, že "<vzkaz>=1" v zásobníku "\savedcolors" znamená, že je třeba změnu
barvy provedenou na konci skupiny zapsat do REF souboru.

Makro \db restorecolor usazené za koncem skupiny pomocí "\aftergroup" si
vyzvedne potřebné tři údaje ze zásobníku. K tomu definuje makro "\tmp",
které to provede. Dále do "\tmpa" vloží "<barvu k>" a do "\tmpb" vloží
"<barvu K>" a testem proti "\currcolork", resp. "\currcolorK" zjistí, zda je
vůbec potřeba barvu měnit. Pokud ne, nedělá nic. Jinak zapíše potřebný
"\pdfliteral" a přenastaví makro "\currcolork", resp. "\currcolorK".
Konečně, při "<vzkaz>=1", zapíše nově nastavenou barvu do REF souboru.
Celou práci vykoná uvnitř skupiny, takže lokální změny se vracejí po
ukončení práce makra k původním hodnotám.

\inext{restorecolor}{\empty}{+-}

Přepínače barev stejně jako makra "\localcolor" nebo "\longlocalcolor" se
mohou vyskytnout v~nadpise. Takže je potřeba je zabezpečit proti rozsypání.

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

Aby mohla output rutina správně obsloužit barvy, je třeba učinit několik
opatření, která jsou zhruba načrtnuta v následujícím schémátku.

\begtt
\output = {
\begoutput  % Záloha aktuálních barev, nastavení \currcolorkK=černá
...
\makeheadline % Uživatel může měnit barvy, ale musí se vrátit k černé.
\pagecontents = {
  ...\topins     % Výchozí barva je černá, k černé je třeba se vrátit.
  \preboxcclv    % Nastavení barvy, která přetekla na tuto stranu.
  \unvbox256     % Sazba pro tuto stranu.
  \postboxcclv   % Návrat k barvě černé.
  ...\footins    % Poznámky pod čarou.
}
\makefootline % Výchozí barva je černá, uživatel může nastavit cokoli.
...
\endoutput  % návrat \currcolorkK k původním zálohovaným barvám 
}
\endtt

Při nastavování barev v "\headline" a "\footline" je možné použít
"\localcolor", protože zásobník se tím při práci output rutiny posune a také
znovu splaskne na původní hodnotu a neovlivní tedy stav v běžné sazbě (mimo
output rutinu). Ovšem běžná sazba má nastaveny nějak hodnoty "\currcolork" a
"\currcolorK" a není žádoucí, aby byly tyto hodnoty output rutinou měněny.
Proto jsou v makru \db begoutput tyto hodnoty zálohovány a v makru 
\db endoutput se k nim output rutina vrátí. Uvnitř output rutiny potlačíme
všem přepínačům barev jejich tendenci ukládat něco do REF souboru, takže je
v makru "\begoutput" použito "\writecolorfalse".

\inext{begoutput}{\empty}{+-}

Poněkud složitější je makro "\preboxcclv", které má nastavit barvu, která
přetekla z předchozí strany. Abychom se k funkci tohoto makra dobrali,
vraťme se k makrům
\db Xpdfcolork "{<CMYK barva>}" a \db XpdfcolorK "{<CMYK barva>}", 
která jsou uložena v REF souboru pro každé (potenciálně dlouhé) přepnutí barvy.
Povšimněte si, že output rutina ukládá do REF souboru pro každou stranu 
makrem "\begoutput" údaj "\Xpage{<číslo strany>}". Tyto údaje
tvoří oddělovače mezi jednotlivými stránkami.

Příkazy "\Xpdfcolork" a "\XpdfcolorK" ukládají při čtení REF souboru do 
\db pdflastcolork, resp. \db pdflastcolorK, naposledy použitou barvu. Takže na
příští straně, až narazíme při čtení REF souboru na další "\Xpage", budeme
vědět, zda je tato naposledy použitá barva černá nebo jiná. Pokud jiná, je
potřeba ji nastavit jako výchozí i pro tuto (tedy příští) stranu. 
Makro \db Xpage v takovém případě uloží do makra 
"\pgc:<číslo strany>" povel "\setpgcolor <k nebo K> {<CMYK barvy>}".
Opakuje-li se stejná strana (např. postupné odkrývání výkřiků ve
slideshow), další "\Xpage" se stejnou stranou nedělají nic.

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

Makro \db preboxcclv jednoduše spustí "\pgc:<číslo strany>". Není-li
"\pgc:<číslo strany>" definováno, je to známka, že barva na začátku je
černá a díky dvojici "\csname", "\endcsname" se provede "\relax", tedy nic.
Jinak se nastaví správná barva pomocí \db setpgcolor.

\inext{preboxcclv}{}{++}

Makro \db postboxcclv nastaví zpět černou barvu. Dělá to inteligentně.
Nejprve nastaví správné hodnoty makrům "\currcolork" a "\currcolorK" po vložení
boxu~255. K tomu účelu předefinuje "\setpgcolor" tak, aby pouze ukládal tyto
hodnoty a spustí "\pgc:<číslo strany+1>". 
Poté příkazy "\Black" a "\linecolor\Black" budou vědět,
jaká je aktuální barva. A je-li černá, neudělají nic, jinak vloží příslušný
"\special".

\inext{postboxcclv}{\empty}{+-}

Není-li použit pdf\TeX{}, některá makra pro barvu deaktivujeme:

\inext{ifpdftex}{\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}{+-}

Když není použit pdf\TeX{}, není makro deaktivujeme.

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


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

Makro \db destactive "[<typ>:<lejblík>]" 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 prostě vloží "\destbox". Makro 
\db destbox "[<typ>:<lejblík>]" vytvoří box nulové výšky a z~něj
vystrčí nahoru cíl klikacího odkazu vzdálený od účaří o "\destheight". 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. PDF prohlížeče většinou  
lícují horní hranu okna přesně s~místem cíle, je tedy 
potřeba cíl umístit poněkud výše, abychom viděli i odkazovaný text.
K~tomu právě slouží registr \db destheight.

\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.

Někdy je účelné v režimu \uv{draft} dokumentu tisknout v místě cílů odkazů
jména lejblíků, aby autor viděl, jaké lejblíky použil a lépe se mu dílo
modifikovalo. Stačí předefinovat pro tento režim makro
"\destbox" třeba takto:

\begtt
\def\destbox[#1#2:#3]{\vbox to0pt{\kern-\destheight
   \pdfdest name{#1#2:#3} xyz\relax
   \if#1r\llap{\localcolor\Green\ttt[#3]}\vss
   \else \if#1c\vss\llap{\localcolor\Green\ttt[\tmpb] }\kern-\prevdepth
   \else \vss \fi\fi}}
\endtt

Při tomto řešení budou lejblíky z "\label" tištěny nahoru v místě cíle
zatímco lejblíky z~"\bib" a "\bibitem" budou tištěny vedle položky se seznamem
literatury. V obou případech budou lejblíky zelené a díky "\llap" neovlivní
polohu ostatní sazby. 

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".
Toto makro vytvoří externí odkaz. Je použito v~makru "\url" 
prostřednictvím makra "\ulink".

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

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. Zde je také definováno makro \db urlcolor.

\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}{+-}

Makro \db url "{<text>}" se používá k tisku URL. Vytiskne "<text>" fontem
\db urlfont, přitom kolem znaků lomítko, tečka a dalších přidává nulovou
mezeru s dodatečnou mírnou roztažitelností \db urlskip. Mezera vpravo od
těchto znaků je navíc zlomitelná s penaltou definovanou v makru \db
urlbskip. Dvojité lomítko \db urlslashslash má zlomitelnou mezeru jen na
konci.

\inext{url}{\empty}{+-}
   
Je třeba vysvětlit, proč je v makru "\urlskip" použito "\null", neboli
"\hbox{}". Tento box se přilepí na předchozí slovo a tím zakážeme toto slovo
dělit podle vzorů dělení slov. Spojovník při rozděleném slovu je totiž pro  
čtenáře matoucí: nemůže vědět, zda je nebo není součástí URL.

Makro "\url{<text>}" pracuje tak, že uloží "<text>" do "\tmpb" a nechá
vyměnit příslušné znaky uvnitř "\tmpb" pomocí "\replacestrings".
Nakonec vytiskne "<text>" prostřednictvím "\ulink".

Aktivní vlnku lze v "<textu>" vyměnit za "\char`\~". Podobně lze řešit
některé další znaky, ale ne všechny: procento, backlash.
U těchto znaků bychom nejprve museli vyměnit jejich
kategorie. Pak by ale makro "\url" nefungovalo uvnitř parametrů jiných
maker. V zájmu jednoduchosti makra "\url" to neděláme. Takže pokud uživatel
má v URL znak procento, nahradí ho sekvencí "\percent" manuálně a pokud tam má 
další speciální znak, vyřeší to podobným makrem jako "\percent".

Makro \db replacestrings "{<string1>}{<string2>}" vymění v makru "\tmpb"
veškeré výskyty "<string1>" za "<string2>". Pro tento účel definuje pracovní
makro "\tmp", které pracuje podobně, jako makro "\iiscanch" (doporučujeme se
podívat na výklad makra "\iiscanch"). Makro "\tmp" ale není na rozdíl od
"\iiscanch" expandující. Místo toho postupně kumuluje 
výsledek do nového "\tmpb" pomocí "\addto". Před spuštěním "\tmp"
expandujeme "\tmpb" pomocí pěti "\expandafter" a poté ho pronulujeme, 
takže to pracuje jako
"\def\tmbb{}\tmp<původní obsah tmpb>\/<string1>\relax".

\inext{replacestring}{\empty}{+-}
   
Na konci "<textu>" je vložena sekvence "\/", která jej odděluje od přidaného
"<string1>". Kdybychom ji tam nedali, pak při "<string1>=//" a při lomítku
na konci "<textu>" bychom měli "...///" a to způsobí potíže.
V závěru makra "\replacestring" je přidaná sekvence "\/" zase odstraněna.


\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. Jako lejblík je použito
"oul:<oulnum>", kde \db oulnum průběžně zvětšujeme o jedničku.

\inext{oulnum}{\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. Do sekvencí \db savedttchar a \db savedttcharc
je uložena ASCII hodnota znaku a jeho původní kategorie.

\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##\tabstrutA}\hss\enskip}

\Pwidth=3cm \table{|c|P|}{\crl \tskip3pt
   aaa & Tady je delší textík, který se nevejde na řádek. \crl \tskip3pt
   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. Makro \db crlli sestává ze dvou "\crli"
oddělených od sebe vertikální mezerou vloženou pomocí "\noalign".

\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 picwidth a \db picheight. Z důvodu zpětné
kompatibility je dále ztotožněn "\picwidth" se sekvencí \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.

\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}{+-}

Makro \db runningfnotes vypne lokální číslování poznámek na každé stránce.
Místo toho se budou poznámky číslovat podle registru "\fnotenum". Ten se
zvětšuje o jedničku v celém dokumentu. Chcete-li mít poznámky číslované
zvlášť například v každé kapitole, je nutno navíc resetovat tento čítač
například pomocí "\addto\chaphook{\global\fnotenum=0}". 

\inext{runningfnotes}{\empty}{+-}

Registr \db mnotenum. 
globálně čísluje okrajové poznámky a plní podobnou funkci, jako registr
"\fnotenum" pro podčárové poznámky. Registr \db mnoteskip udává hodnotu
vertikálního posunu 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}{++}

Text uvnitř "\mnote" přebírá barvu kvůli "\vadjust" \uv{se zpožděním}, podle
barvy, která je na konci řádku, vedle kterého je "\mnote" umístěna. Makra
tedy při zpracování parametru "\mnote" nemohou dopředu vědět, jaká to bude
barva, a nelze tedy použít zásobník barev řízený makry. Uživateli přitom
musíme dát možnost barvu "\mnote" nastavit, například:

\begtt
\def\mnotehook{\noindent\Black}
\endtt
zajistí, že budou všechny poznámky na okraji černé bez ohledu na to, jakou
barvu mají řádky, vedle kterých ty poznámky stojí. K tomu účelu je nastavení
případných barev uživatelem jednak v makru "\mnoteA" obklopeno do dvojice
"\pdfsave"--"\pdfrestore" a dále je nastartován pomocí \db mnoteC lokální
zásobník barev, který je pomocí \db mnoteD vrácen do stavu, v jakém byl před
zahájemním zpracování "\mnote". Uživatel může v prametru "\mnote" používat 
"{\localcolor\Green X{\localcolor\Blue Y}Z}" s~očekávaným výsledkem.
Barvy neoznačené "\localcolor" končí svou platnost na konci poznámky
"\mnote".

\inext{mnoteC}{^^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>" definuje interní makro \db mnotesfixed
s obsahem "\left" nebo "\right" podle přání uživatele. Makro "\mnoteA" se
pak na definovanost "\mnotesfixed" ptá a pokud je definované, nepoužije
údaje přečtené ze souboru.

\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 opakovaně zavolá 
"\citeA<lejblík-i>", kde se připraví čísla citovaných publikací do
lokálně tvořeného seznamu "\savedcites". Poté zavolá "\printsavedcites",
které lokálně tvořený seznam čísel vytiskne. Kromě toho makro "\citeA" udělá
plno dalších potřebných věcí, jak uvidíme za chvíli.
Makro \db nocite se chová jako "\cite" až na to, že se nic netiskne.
Makro \db rcite vytiskne čísla publikací, ale bez hranatých závorek kolem.
Makro \db savedcites je globálně prázdné a zaplní se vždy znovu uvnitř
skupiny vymezené makrem "\cite" nebo "\nocite" nebo "\rcite".

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

Makro \db citeA "<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,
  přidá obsah tohoto makra do "\savedcites", pokud ne, přidá do
  "\savedcites" otazník a na terminál vypíše varování.
  Kontrolní sekvence "\csname bib:<lejblík>\endcsname" 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 "\citeA" je naprogramováno zhruba takto

\def\begtthook{\langleactive}
\begtt
function citeA(<lejblík>) {
  if (<lejblík> == '*') { <zapiš do> \citelist '*'; return; }
  if (\bib:<lejblík> == nedef) {
    <přidej do> \citelist <lejblík>;
    <na terminál:> "Warning, cite [label] unknown";
    <přidej do> \savedcites "?,";
    <lokálně vypni třídění a zkracování seznamu> \savedcites;
    \bib:<lejblík> = empty;
    return;
  if (\bib:<lejblík> == empty) {
    <přidej do> \savedcites "?,";
    <lokálně vypni třídění a zkracování seznamu> \savedcites;
    return;
  }
  if (\bib:<lejblík> končí znakem '&') {
    <přidej do> \citelist <lejblík>;
    <odstraň znak & z obsahu makra> \bib:<lejblík>;
  }
  <přidej do> \savedcites <expandovaný> "\bib:<lejblík>,";  
} 
\endtt

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{citeA}{^^B\cbrace}{++}

Makro snímá svůj parametr jako "#1#2," aby mohly být "<lejblíky>" odděleny
před čárkou mezerou, která je neseparovaným parametrem "#1" ignorována.
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í.

Makro \db printsavedcites případně setřídí seznam "\savedcites" 
podle velikosti zavoláním "\sortcitesA" 
a dále opakovaně na jednotlivé prvky seznamu zavolá makro
"\citeB", které prvky seznamu vytiskne a případně je zkrátí pomocí intervalů
(místo "3,4,5" píše "3--5"). Pomocnou proměnnou "\tmpb" využije makro
"\citeB", jak uvidíme později při výkladu tohoto makra.

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

Makro \db sortcitesA seřadí seznam "\savedcites" podle velikosti. Takže třeba
"4,7,3,5," se promění na "3,4,5,7,". Implicitně je definováno jako prázdné
makro, takže řazení se neprovede. Nicméně uživatel ho použitím makra
\db sortcitations v~hlavičce svého dokumentu probudí k životu.

Oživené "\sortcitesA" nejprve vyvrhne do čtecí fronty obsah "\savedcites"
ukončený další čárkou (máme zde dvě čárky vedle sebe) a následně spustí \db
sortcitesB, které postupně odebírá jednotlivé prvky ze čtecí fronty, předává
je do nově tvořeného setříděného seznamu, kam je vkládá na správné místo.
Výchozí hodnota nově tvořeného seznamu obsahuje číslo "300000", které bude
vždy na konci seznamu, protože se předpokládá větší než jakýkoli tříděný
prvek. Zajímavý trik s "\edef\savedcites{...\expandafter}" způsobí, že se
"\savedcites" nejprve vyvrhne (po aplikaci dvou "\expandafter") do čtecí
fronty a teprve poté dostane novou hodnotu pomocí "\edef". Na konci makra
"\sortcitesA" ze seznamu odebereme koncové číslo "300000".

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

Vložení prvku do zatříděného seznamu probíhá pomocí \db sortcitesC, což je
makro, které nově tvořený seznam, který je nyní také vyvržen ve čtecí
frontě, projde zleva doprava, dokud nenarazí na číslo větší než vkládané.
Při té činnosti opakovaně sbírá hodnoty a vkládá je zpět do "\savedcites".
Je-li zařazovaný prvek "\tmpa" menší než odebraný prvek z fronty, vloží se
pomocí \db sortcitesD do "\savedcites" původní "\savedcites" následovaný
"\tmpa" následovaný testovaným prvkem následovaný zbytkem vstupní
fronty (až po "\end").

\inext{sortcitesC}{\empty}{+-}

Makro \db citeB "<položka>," ukončí činnost při prázdném parametru,
jinak se po vytištění "<položky>" zavolá znova.
Vytiskne dva otazníky, je-li parametrem otazník, 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 "\citeB".

\inext{citeB}{\empty}{+-}

Činnost "\cite" je konečně završena voláním maker
\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í upraví separátor \db citesep, který je globálně a tedy na začátku
činnosti "\cite" prázdný. Při opakovaném volání "\printcite" se tedy
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}{+-}

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,
která je obklopeno makry "\begoutput" a "\endoutput" kvůli barvám, jak bylo
vysvětleno v sekci~\ilink[barvy]{\numref[barvy]}.

\inext{opmacoutput}{}{++}

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

\begitems
\item * Místo přímého "\shipout" nechá nejprve box sestavit jako "\box0",
  pak provede "\protectlist" a pak provede "\shipout\box0". Tím jsou
  zabezpečeny tzv. protektované příkazy při "\write".
\item * Je vložen "\pghook" po sestavení boxů, ale před
  "\shipout". Implicitně je "\pghook" prázdný. 
  Mění jej makro "\margins" pro účely pravolevého střídání okrajů.
\item * Do "\pagecontents" vkládá "\prepage" (kvůli odkazům na stránku)
  a "\preboxcclv", "\postboxcclv" (kvůli barvám).
\enditems

První úprava zabrání expanzi maker zabezpečený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}{doprotect}{++}

K makru "\begoutput" přidáme lokální změnu makra "\nl" v mezeru.
Makro "\nl" implicitně zalamuje řádky a může se vyskytovat v titulcích,
takže může být dopraveno do plovoucího záhlaví, kde je zalamování řádků
neřádoucí. 
Původní obsah makra "\begoutput" je definován v~sekci~\ilink[barvy]{\numref[barvy]}
a řeší zejména správné nastavení barev.
Makro \db prepage vkládá klikatelný cíl pro stránku, je-li známo její číslo.

\inext{begoutput}{\empty}{+-}

Poslední úprava mění plainovské \db pagecontents tak, že vkládá
"\prepage", "\preboxcclv" a "\postboxcclv". Jinak nechává obsah makra
stejný, jako v plain\TeX{}u.

\inext{catcode}{\empty}{+-}

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>)<jednotka>&", k~to\-mu 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 [zaver] Závěr
%%%%%%%%%%%%%%%%%%%%%

V případě, že je použit Xe\TeX, načteme dodatečná makra
ze souboru "opmac-xetex.tex". Tato makra nahrazují některá makra z OPmac
Xe\TeX-specifickou variantou nebo emulují pdf\TeX{}ové primitivy.
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{version}{\empty}{+-}

\doindex


\bye


