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

\chyph
\def\projectversion{Dec. 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}{+-}

Makro \db maybebreak "<rozměr>" umožní uživateli rozlomit řádek 
nebo stránku v místě použití. Zlom se uskuteční, chybí-li do konce
řádku/stránky zhruba méně než "<rozměr>" místa. Jinak se zlom neuskuteční a
nestane se nic. Makro je závislé na módu \TeX{}u (vertikální/horizontální). 
Chcete-li jím lámat
stránky, pište třeba "\par\maybebreak3cm". Makro využívá triku, že přičte a
odečte stejnou hodnotu roztažitelnosti mezery, takže tyto dvě mezery těsně
za sebou se (při nezlomení v~"\penalty-130") anulují.

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

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 tomto
souboru naráz. Podrobnosti lze nalézt v uvedeném souboru.

Makro \db replacestrings "{<string1>}{<string2>}" vymění v makru "\tmpb"
veškeré výskyty "<string1>" za "<string2>". Pro tento účel definuje pracovní
makro \db replacestringsA se separátorem "<string1>". Jak to pracuje je
ukázáno na příkladu níže.
Před spuštěním "\replacestringsA" je třeba nejprve vyvrhnout obsah "\tmpb"
do vstupní fronty, poté provést "\def\tmpb{}" a nakonec provést
"\replacestringsA". To je uděláno pomocí triku s "\edef\tmpb" a se dvěma "\expandafter". 
V makru pracujeme s tokeny "!" a "?" kategorie 3, které slouží jako
separátory. Předpokládáme, že takové nestandardní tokeny se ve zpracovávaném 
textu nikdy neobjeví, protože vykřičník a otazník mají normálně kategorii
12.

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

Jak to pracuje si ukážeme na příkladu "\replacestrings{XX}{YY}", pokud máme
v "\tmpb" uložen třeba text "ahaXXuffXXkonec". Makro "\replacestringsA" je v
takovém případě definováno jako:

\begtt
\def\replacestringsA #1XX#2!{%
   \ifx!#2!\addto\tmpb{#1}\else\addto\tmpb{#1YY}\replacestringsA#2!\fi}%
\endtt
%
a jednotlivé kroky zpracování probíhají takto:

\begtt
\replacestringsA ahaXXuffXXkonec?XX!
#1 = "aha" #2 = "uffXXkonec?XX!"
\addto\tmpb{ahaYY}
\replacestringsA uffXXkonec?XX!
#1 = "uff" #2 = "konec?XX!"
\addto\tmpb{uffYY}, tj. \tmpb obsahuje "ahaYYuffYY".
\replacestringsA konec?XX!
#1 = "konec?" #2 = "" (prázdný parametr)
\addto\tmpb{konec?}, tj. \tmpb obsahuje "ahaYYuffYYkonec?" a rekurze končí.
\endtt
%
Dále se předefinuje "\def\replacestrin""gsA#1?{\def\tmpb{#1}}" a provede se

\begtt
\replacestringsA ahaYYuffYYkonec?
#1 = "ahaYYuffYYkonec"
\def\tmpb{ahaYYuffYYkonec}
\endtt
%
tedy tímto algoritmem odstraníme koncový otazník. Proč jsme ho tam vlastně
dávali? Kdyby tam nebyl, tak by nesprávně fungovalo
"\replacestrings{XX}{YY}" při "\tmpb" ve tvaru "ahaX".


\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 fnotehook
\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 implementace 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. Nyní jen
uvedeme, že "\Xindex" je jen speciální variantou obecného makra 
\db Xindexg při "#1=,". Takže pracuje s kontrolními sekvencemi typu
"\,<heslo>". Následující text popisuje jen tento případ. Makro "\Xindexg"
je možné použít pro pralelní vytváření dalších seznamů stránek
stejných hesel (např. seznam vyznačený kurzívou, tučně atd.). Jak to udělat
je popsáno v OPmac triku 0072.

Údaje o stranách spojených s rejstříkovým heslem jsou ukládány do makra
"\,<heslo>" a jsou rozděleny do dvou částí ve tvaru "{prvni}{druhy}".
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>&". Jsou použita pomocná makra
\db firstdataA a \db seconddataA.

\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> == <empty> || <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

V makru "\Xindex" pracujeme ještě se dvěma pomocnými makry 
\db pgfolioA a \db pgfolioB. Pro kladné stránky se tato makra chovají
stejně, jako kdyby tam vůbec nebyla. Ovšem v plain\TeX{}u
(viz maro "\folio") se mohou stránkové číslice vyskytnout 
na začátku dokumentu záporné, v takovém případě se mají tisknout
římskými číslicemi. Proto jsou uvedená makra definována poněkud chytřeji.

\inext{pgfolioA}{\empty}{+-}

\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ého 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:par] {exhyphenpenalty}

\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 na 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 "\makeindex" nastavuje na řádku \cite[mkindex:par] lokálně 
parametry sazby odstavce v rejstříku. Vlevo
budeme mít "\leftskip" 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ý. 

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
před "<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ím
ř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á. 

Nejprve připravíme data pro porovnávací algoritmus.

\inext{sortingdata}{\empty}{+-}

Mezi jednotlivými čárkami v makru \db sortingdata jsou skupiny znaků, které
se z hlediska prvního průchodu řadicím algoritmem nerozlišují. Jednotlivé
znaky v "\sortingdata" se rozliší při případném druhém průchodu. Řazení
znaků v "\sortingdata" odpovídá požadovanému abecednímu řazení.

Dále je makrem \db setignoredchars vyjmenován seznam znaků, které se při
řazení zcela ignorují (jakoby tam vůbec nebyly). Typicky jde o interpunkci.
Makro nastaví všem těmto znakům pomocí "\setlccodes" kód tečky a tato tečka
se posléze z porovnávaného textu odstraní. Seznam znaků je oddělen tečkou a
ukončen dvojicí "{}{}". Seznam ignorovaných znaků odpovídá pravidlům českého
řazení. Norma doporučuje sice pro případ, kdy se hesla neliší jinak než těmito znaky, 
nasadit další průchod řazení, ale pro jednoduchost a velkou výjimečnost
takové situace toto v OPmac implementováno není.

Makra \db specsortingdatacs a \db specsortingdatask deklarují náhrady před
použitím řadicího algoritmu. Jednotivá náhrada je deklarována jako
"<string1>:<string2>" a je od další deklarace náhrady oddělena mezerou.
Tímto způsobem jsou implementovány spřežky ch, Ch a CH, které se nahrazují
znaky "^^T", "^^U" a "^^V". Ty vystupují v řadicím algoritmu jako jediný
znak, a mají své místo v makru "\sortingdata" Slovenština má sice další
spřežky dz, Dz, DZ, dž, Dž, DŽ, ale ty jsou řazeny těsně za D, takže jsou
řazeny správně i za situace, kdy nejsou nahrazeny jediným znakem. Nicméně,
pokud by někdo chtěl tyto spřežky (například pro ošetřování výjimek) použít
jako samostatné znaky, může si definovat své makro "\sortingdata", které by
obsahovalo

\begtt
   ...
   dD\v d\v D,%
   ^^N^^O^^P,%  dz Dz DZ
   ^^Q^^R^^S,%  dž Dž DŽ
   eE\'e\'E\v e\v E,%
   ...
\endtt
a dále definuje
\begtt
\def\specsortingdatask {ch:^^T Ch:^^U CH:^^V
   dz:^^N Dz:^^O DZ:^^P d\v z:^^Q D\v z:^^R D\v Z:^^S}
\endtt
%
Makra pro spřežky mají název ve tvaru "\specsortingdata<kód jazyka>".
Použije se makro odpovídající jazyku podle nastaveného dělení slov.
Není-li makro pro použitý jazyk definováno, žádné náhrady se neprovedou.
Je třeba upozornit (například uživatele maďarštiny), pokud by se v těchto
spřežkách chtěli rozšoupnout, vyhněte se znakům "^^I" a "^^M", kterým
plain\TeX, resp. ini\TeX, nastavuje speciální kategorie.

Implementaci řadicího algoritmu zahájíme makrem \db setprimarysorting, které
se spustí jednou při sestavení rejstříku, přečte výše uvedená data a
připraví odpovídající datové stuktury pro první průchod řadicího algoritmu.
Hlavní činností tohoto makra je, že připraví "\lccode" znaků vyjmenovaných v
"\sortingdata" podle jejich vzestupného pořadí, přitom znakům v jedné
skupině (oddělené čárkou) přiřadí stejné "\lccode". Text před řazením pak
budeme konvertovat použitím "\lowercase".

\inext{setprimarysorting}{\empty}{+-}

Vidíme, že makro "\setprimarysorting" nejprve expanduje "\sortingdata",
aby se realizovaly znaky typu "\v c" podle nastaveného kódování.
Dělá to jen tehdy, když je definováno makro "\r", tj. uvedené sekvence pro
akcenty expandují na správné kódy. Rovněž pomocí 
"\let"\db asciisorting \unskip"=t"
je možné zabránit použití "\sortingdata" a řadicí algoritmus řadí podle
ASCII.

Dále "\setprimarysorting" připraví makro
\db specsortingdata (bez přípony jazyka) z makra 
"\specsortingdata<aktuální jazyk>". Nejrve je expanduje a pak pomocí triku s
"\meaning" za spoluráce s~makrem \db setprimarysortingA převede všechny znaky 
v makru na kategorii 12, protože toto budeme pro řadicí algoritmus potřebovat.

Konečně se v makru "\setprimarysorting" připraví (za použití opakovaného
volání "\act") "\lccode" všech znaků zmíněných v "\sortingdata".
Povšimneme si, že pro první průchod dostanou stejný "\lccode" všechny znaky
ve skupině mezi čárkami. 
Je to tím, že v makru "\setprimarysorting" se zvedá "\tmpnum" jen v místě
čárky. 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. 
Makrem "\setignoredchars" se zcela nakonec nastaví ignorovaným znakům
"\lccode" tečky.

Makro \db setsecondarysorting se volá opakovaně a příležitostně pro případy,
kdy jsou hesla z~hlediska primárního řazení totožná. Nastaví jinak "\lccode"
znaků. Tentokrát mají všechny znaky ze "\sortingdata" rozdílný "\lccode", ve
vzestupném pořadí.

\inext{setsecondarysorting}{\empty}{+-}

Makro \db preparesorting se volá (s nastavenými parametry podle
"\setprimarysorting") pro každé heslo jednou. Heslo je uloženo v názvu
kontrolní sekvence, která je parametrem makra "\preparesorting". Data pro
primární řazení jsou už připravena na řádcích \cite[mkindex:od] až
\cite[mkindex:do] v makru "\makeindex". V případech, kdy jsou dvě hesla
shodná z hlediska primárního řazení (to nastane asi velmi výjimečně), je pro
danou dvojici hesel znovu zavoláno makro "\preparesorting", tentokrát s
přednastavenými daty podle "\setsecondarysorting". Makro "\preparesorting"
má za úkol uložit výsledek své konverze do "\tmpb".

\inext{preparesorting}{\empty}{+-}

Všimneme si, že "\preparesorting" vykonává jádro své činnosti v \db
preparesortingA, které přebere text hesla extrahovaný do parametru "#3".
Toto makro pomocí \db preparesortingB opakovaně volá "\replacestrings", aby
nahradilo spřežky odpovídajícími náhradami. Dále pomocí "\lowercase" provede
konverzi a konečně pomocí "\replacestrings{.}{}" odstraní z hesla nejen
tečky, ale i znaky vyjmenované v makru "\setignoredchars".

Připravíme si pomocí "\newif" makro \db ifAleB, 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 obejít problém \uv{dimension too large}.
\enditems 

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

\ilabel[mul:prevgraf]  {prevgraf}

\inext{mullines}{\count=5 ^^B\cbrace}{++}

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 se nedá změřit jeho
výška pomocí "\dimen0=\ht6". Box samotný sice může být vyšší než pět metrů,
ale "\dimen0" nikoli: objeví se chyba \uv{dimension too large}.
Z toho důvodu je v makrech zavedena proměnná \db mullines, která pomocí
předefinovaného "\par" (na řádku \cite[mul:prevgraf]) 
počítá počet řádků sazby. Je-li "\mullines" větší než
"\tmpnum" (což při daném "\baselineskip" odpovídá 0,8"\maxdimen"), makro
pracuje, jakoby výška boxu 6 byla 0,8"\maxdimen", tedy rozběhne se
"\splitpart" a "\makecolumns". Přitom makro "\makecolumns" snižuje hodnotu
"\mullines" o~počet vytištěných řádků, takže příště už může být "\mullines"
menší než "\tmpnum". K tomu určitě na několika posledních stránkách dojde,
takže nakonec "\balancecolumns" pracuje s přesnou výškou boxu~6. 

 
\subsec [barvy] Barvy
%%%%%%%%%%%%%%%%%%%%%

Až po verzi OPmac Nov. 2014 byly barvy implementovány pomocí "\pdfliteral"
za použití maker, která sama implementují "\colorstack" pomocí REF
souboru. V prosinci 2014 jsem se rozhodl tento kód z OPmac odstranit a
využít přímo primitivní "\pdfcolorstack" (v pdf\TeX{}u od verze 1.40). 
OPmac se tak zbavil asi 30~řádků
poměrně komplikovaného kódu a ušetřil množství zápisů do REF souboru. Tyto
změny jsou v souladu s~myšlenkou \uv{v jednoduchosti je síla}. Uvedené rozhodnutí není
zcela zpětně kompatibilní, protože opouští možnost samostatného nastavení
barvy pro tenké linky a pro text. Domnívám se, že to nevadí, protože pokud
uživatel potřebuje elementární manipulaci s barvami, použije sám přímo
"\pdfliteral". Makra "\setcmykcolor" se nyní opírají o "\pdfcolorstack" a
nastavují oba typy barev společně. Bylo sice možné inicializovat dva
zásobníky barev (pro linky a pro text), ale to by fungovalo jen
v~pdf\TeX{}u. Nikoli v~Xe\TeX{}u. Cílem ovšem je, aby se barvy v pdf\TeX{}u a Xe\TeX{}u
chovaly pokud možno stejně. Navíc, když uživatel napíše barevně odmocninu,
musí mít oba typy barev zapnuty současně na stejnou hodnotu, jinak má véčko
odmocniny v jiné barvě než vodorovnou čáru. Je tedy i pro uživatele
jednodušší tyto dva typy barev nerozlišovat.

Makro \db localcolor (na rozdíl od předchozí verze) pouze 
nastavuje "\localcolortrue". Podle \db localcolortrue
resp. \db localcolorfalse se bude větvit činnost přepínačů barev, které
ukládají aktuální barvu do zásobníku. To je tedy druhá mírná odlišnost od starší verze
OPmac Nov. 2014, kdy makro "\localcolor" přímo ukládalo aktuální barvu do zásobníku
barev, zatímco přepínače barev toto neřešily. Původní typické použití makra
"\localcolor" není ve sporu s jeho novým významem.

\inext{iflocalcolor}{\empty}{+-}

Makro "\longlocalcolor" dříve umožňovalo přechod barvy na další stránku,
nyní je tato vlastnost přímo řešena díky "\pdfcolorstack", takže netřeba
rozlišovat mezi "\localcolor" a \db longlocalcolor.
Makro \db linecolor nyní nedělá nic, protože nerozlišujeme mezi barvou linek
a barvou textu. V původní verzi bylo prefixem pro barvy linek.

\inext{backward}{\empty}{+-}

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 "{<CMYK barva>}" nastaví požadovanou barvu. 
Nejprve přepne makro "\ensureblacko" do aktivního stavu. V tomto stavu makro
setrvá právě tehdy, když je v dokumentu použit aspoň jednou přepínač barvy.
Dále makro "\setcmykcolor" nastaví při
"\localcolorfalse" barvu přímo a při "\localocolortrue" barvu vloží do
zásobníku a pomocí "\aftergroup" zajistí návrat k původní hodnotě.
Navíc nastaví na odpovídající hodnotu makro \db currentcolor.

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

Makro "\currentcolor" je nastaveno na výchozí hodnotu \db pdfblackcolor

\inext{pdfblackcolor}{currentcolor}{++}

Makro \db ensureblacko "{<sazba>}" je použito pro sazbu záhlaví a zápatí ve
výstupní rutině v~makru "\opmacoutput". Implicitně se "\ensureblacko{<sazba>}" chová
stejně jako samotná "<sazba>", ale po použití přepínače barvy
"\setcmykcolor" začne fungovat jako \db ensureblackoA, což zajistí bravu
"<sazby>" v~černém. Je to provedeno tak, že je na začátku "<sazby>"
alokována nová úroveň zásobníku barev s výchozí černou barvou a na konci
"<sazby>" je tato úroveň zásobníku ukončena.

\inext{ensureblacko}{\empty}{+-}

Makra \db colorstackpush "{<CMYK barva>}" a \db colorstackpop implementují
práci se zásobníkem barev za použití odpovídajících \TeX{}ových primitivů.
Není-li přítomen pdf\TeX{} ve verzi aspoň 1.40, je barva nastavena pomocí
"\pdfliteral" (což v komplikovanějších případech při přechodu na další
stránky nefunguje správně), jinak je použit "\pdfcolorstack", který je
inicializován pomocí "\pdfcolorstackinit". Povšimněte si, že se současně
pracuje s barvou textu "<c> <m> <y> <k> k" i s barvou tenkých linek 
"<c> <m> <y> <k> K". 
Konečně makro \db colorstackset "{<CMYK barva>}" nastavuje barvu přímo s
umístěním této bravy na vrchol zásobníku místo bravy předchozí.

\inext{pdfcolorstack}{\empty}{+-}

Makra "\colorstackpush", "\colorstackpop" a "\colorstackset" jsou
odpovídajícím způsobem předefinována v souboru "opmac-xetex.tex", aby bylo
možné pracovat s barvami i v Xe\TeX{}u.

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

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

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{}, barvy nefungují, takže makro "\draft" 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ží obsah makra \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{\labelfont[\detokenize\expandafter{#3}]}\vss \else 
   \if#1c\vss\llap{\labelfont[\detokenize\expandafter{\tmpb}] }\kern-\prevdepth
   \else \vss \fi\fi}}
\def\labelfont{\localcolor\Red\tt\thefontsize[10]}
\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 linkactive "[<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. K použití je připraveno makro
\db link, které dostane hodnotu "\linkactive" při "\hyperlinks", jinak pouze
přepíše svůj argument.

\inext{link}{\empty}{+-}

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 "attr" "/Border[0 0 0]", pokud není kontrolní sekvence 
"\<typ>border" definována. Jinak expanduje na "arrt" "/Border[0 0 .6]" a 
"/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. Makro "\|" je lokálně definováno jako prázdné, ale při "\urlfont"
nabývá hodnoty \db urlspecchar. Takže ve skutečném odkaze se neprojeví, ale
při tisku ano. Uživatel si může "\urlspecchar" definovat dle svých představ
(například jako "\hfil\break").

\inext{url}{\empty}{+-}
   
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, musí psát "\%"
nebo si změní kategorie sám. Podobná poznámka platí pro znaky "{", "}", "\",
"#" a "$".  


\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\tabstrutA 
      &\tabiteml\hfil#\unsskip\tabitemr \vrule\kern\vvkern\vrule\tabstrutA 
      &\tabiteml\hfil#\unsskip\hfil\tabitemr\tabstrutA
      &\tabiteml#\unsskip\hfil\tabitemr\vrule\tabstrutA
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##\unskip\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ší "\hbox{\vrule<vnitřek>\vrule}".
Uvnitř tohoto boxu se nachází "\vtop{<další>\kern\vvkern\hrule}", takže "<další>"
zůstává na účaří. Přitom "<další>" je "\vbox{\hrule\kern\vvkern<další2>}",
takže "<další2>" zůstává na účaří. V tuto chvíli jsou již vytvořeny čáry
vlevo, vravo, nahoře i dole. Konečně "<další2>" je
"\hbox{\kern\hhkern<text>\kern\hhkern}".

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

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, stringu \db bibmark 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 (což je číslo citovaného záznamu) 
  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" bude obsahovat
  "<číslo citace>" 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
nedeklarovaný "<lejblík>" později znovu poznáme podle toho, že má makro 
"\bib:<lejblík>" 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}{+-}

Při použití \db nonumcitations potlačíme případné předchozí
"\shortcitations" a "\sortcitations" a dále nastavíme \db citelinkA
na jinou, než implicitní prázdnou hodnotu. Makro "\citelinkA" vytiskne 
"\bim:<číslo citace>", tedy značku citace (je to nastaveno v "\Xbib").
Není-li značka citace známá, vypíšeme varování a tiskneme "<číslo citace>".
Makro \db etalchar je potřebné při použití Bib\TeX{}ového stylu "alpha".

\inext{nonumcitations}{\empty}{+-}

Makro \db ecite "[<lejblík>]{<text>}" nejprve provede 
"\citeA#1,,,", tedy vlastně "\nocite[<lejblík>]" a pak si 
\db eciteB vyzvedne ze "\savedcites"
první údaj před čárkou, tedy "<číslo citace>", a uloží do "#1". V~"#2" je případný zbytek ze
"\savedcites" a dále v "#3" pokračuje "<text>". Makro vytiskne jen
"<text>", když je odkaz nedefinován, jinak vytiskne "<text>" prostřednictvím
makra "\citelink".

\inext{ecite}{\empty}{+-}

Následuje kód makra \db bib "[<lejblík>]". Nejprve je ošetřeno, zda je použit
zkrácený nebo rozšířený zápis "\bib[<lejblík>] = {<značka>}". Případná
mezera před rovnítkem je odstaněna pomocí triku s~"\rommannumeral", který
při záporném čísle expanduje na prázdný výsledek, ale případná mezera za "`\." při
skenování tohoto čísla je pozřena.
Při zkráceném zápisu makra "\bib" (bez rovnítka)
se zavolá "\bibB" s prázdným "\bibmark", v druhém případě se
"\bibmark" nejprve naplní prostřednictvím makra \db bibA.
Makro \db bibB vloží prostřednictvím
\db wbib "{<lejblík>}{<číslo citace>}{<značka>}" do REF souboru propojené údaje o
tom, jaké má "<lejblík>" přiřazeno "<číslo citace>" v seznamu literatury.
Makro "\tmpb" je naplněno "<lejblíkem>" pro případné použití v "\dest" (při
draft módu) nebo pro použití v makru "\printbib".
Makro "\wbib" připojí před "\wref" příkaz "\immediate", aby byly zapsány
do REF souboru aktuální hodnoty parametrů.

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

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{<číslo citace>}&".
Dále definuje "\bim:<číslo citace>" jako třetí parametr, který je při použití 
"\bib" prázdný, ale při čtení "*.bbl" souboru vygenerovaného pomocí 
"alpha.bst" nebo "apalike.bst" tam bude uložena "<značka>". Dále "\Xbib"
definuje \db lastbibnum jako "<číslo citace>", takže po přečtení REF souboru
obsahuje největší použité "<číslo citace>". To se může hodit, pokud designér
chce odsadit seznam literatury podle šířky největšího čísla citace. 

\inext{Xbib}{\empty}{+-}

Makro \db printbib se vloží na začátek každého záznamu v seznamu literatury.
Implicitně vytiskne "\the\bibnum" v hranaté závorce a při "\nonumcitations"
netiskne nic. V obou případech nastaví odsazení druhého a dalších řádků 
odstavce na "\iindent". Designér si může toto makro předefinovat dle svého
uvážení.

\inext{printbib}{\empty}{+-}

Makro \db addcitelist "{<lejblík>}" přidá do \db citelist údaj ve tvaru
\db citeI "[<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 "\usebibtex" 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ůže 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>}{<číslo citace>}{<značka>}".
Makro "\tmpb" je naplněno "<lejblíkem>" pro případné použití v "\dest" (při
draft módu) nebo pro použití v makru "\printbib".
\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 "\citeI[<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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%

OPmac mění output rutinu proti originální "\plainoutput" jen v nejnutnějších věcech.
Řeší následující 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 * Pomocí "\ensureblacko" jsou řešeny barvy záhlaví, zápatí, "\topins"
  a "\footins".
\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 * Makro "\pagecontents" obsahuje navíc "\prepage" (kvůli odkazům na stránku).
\enditems

Místo původního makra "\plainoutput" používá OPmac makro "\opmacoutput",
která je obklopeno makry "\begoutput" a "\endoutput". Makro \db begoutput
zapíše do REF souboru údaj o čísle strany a předefinuje makra, která se
mohou vyskytnout v záhlaví či zápatí stránky, pokud od nich chceme, aby 
se chovaly jinak než obvykle. Makro \db endoutput je prázdné a je určeno 
pro strýčka Příhodu.

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

Makro \db opmacoutput se chová analogicky, jako "\plainoutput". Rozdíl je v
tom, že nejprve sestaví celou stranu do boxu0 a v té době expandují makra v
"\headline" a "\footline". Pak spustí "\pghook" a
"\protectlist". Makro "\protectlist" nastaví díky \db doprotect kontrolní sekvence
označené jako "\addprotect<sekvence>" na "\relax", takže během "\shipout" (tedy
během expanze záznamů "\write") se nebudou expandovat.
Další činnost je zcela shodná s činností makra "\plainoutput".

\inext{opmacoutput}{doprotect}{++}

Barvy jsou v textu nastaveny pomocí "\pdfcolorstack", takže na začátku
následující strany začíná barva, která skončila na straně předchozí. 
My ale nechceme, aby
barva textu ovlivnila barvu záhlaví a zápatí. Proto je sazba "\makeheadline"
a "\makefootline" realizována pomocí makra "\ensureblacko". 

Makro \db prepage se spustí na začátku "\pagecontents" a zajistí uložení
cíle pro odskok podle čísla strany. Makra \db preboxcclv a \db postboxcclv
se spustí na začátku a na konci sazby boxu~255, jsou prázdná a zůstávají v
kódu pro zachování zpětné kompatibility.

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

OPmac předefinovává makro \db pagecontents z plain\TeX{}u tak, že přidává
makra "\prepage", "\preboxcclv" a "\postboxcclv". Také obsah boxů "\topins"
a "\footins" tiskne pomocí "\ensureblacko".

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

Makro \db Xpage z REF souboru nastavuje \db lastpage a "\fnotenumlocal".
S těmito registry také spolupracují makra "\Xlabel", "\Xmnote" a "\Xfnote".
 
\inext{lastpage}{\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 setpagedimensB "(<šířka>,<výška>)<jednotka> ", pokud je prvním znakem
"<formátu>" závorka, jinak spustí \db setpagedimensA, což je makro, které
použije definovaný formát, ten expanduje a zavolá "\setpagedimensB". Pomocné
makro \db setpagedimensC "<reg>=<num>:<jednotka>" přiřadí do "<reg>" daný
rozměr.

\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
