%% DocBy.\TeX{} -- nástroj na dokumentování zdrojových kódů \def\projectversion{\dbtversion} \def\headtitle{DocBy.\TeX} \showboxbreadth=1500 \showboxdepth=2 \chyph \input docby.tex \title DocBy.\TeX{} -- nástroj na dokumentování zdrojových kódů \author Petr Olšák \centerline{\ulink[http://www.olsak.net/docbytex.html]% {www.olsak.net/docbytex.html}} \def\db{\dg\nb} \def\du#1{\api{\nb#1}} \let\quotehook=\langleactive \def\insdef#1 {\ifirst{docby.tex}{def\nb#1 }{^^B\cbrace}{++}} \def\inssdef#1 {\ifirst{docby.tex}{def\nb#1}{\empty}{+-}} \bgroup \catcode`\[=1 \catcode`]=2 \catcode`\{=12 \catcode`\}=12 \gdef\obrace[{] \gdef\cbrace[}] \egroup \def\indexhook{Kontrolní sekvence označené šipkou ($\succ$) jsou uživatelskými příkazy. Ostatní kontrolní sekvence jsou v~DocBy.\TeX{}u interní. Tučně je označena strana, kde je slovo dokumentováno, pak následuje seznam stran s výskyty slova. Uživatelské příkazy mají v seznamu stránek podtržené číslo, což je stránka, kde je příkaz vyložen na uživatelské úrovni. \medskip} \def\nn#1 {\noactive{\nb#1}} \nn insert \nn undefined \def\cnvbookmark#1{\lowercase{\lowercase{#1}}} \def\bookmarkshook{\lo ěe \lo šs \lo čc \lo řr \lo žz \lo ýy \lo áa \lo íi \lo ée \lo úu \lo ůu \lo óo \lo ňn } \def\lo #1#2{\lccode`#1=`#2} \dotoc \bookmarks \sec Úvod %%%%%%%%% DocBy.\TeX{} umožňuje jednoduše dokumentovat pomocí \TeX{}u zdrojové kódy programu napsaném v~jazyce~C případně v jakémkoli jiném jazyce. Na rozdíl od Knuthova literárního programování tento nástroj nepoužívá žádné preprocesory nebo filtry pro oddělení informace pro člověka a pro počítač. Vycházím z toho, že programátor je zvyklý psát tyto informace odděleně a chce mít věci pod vlastní kontrolou. Rovněž mnozí programátoři uvítají, že mohou psát dokumentaci dodatečně, a přitom skoro nezasahovat do už napsaného (a možná odladěného) zdrojového kódu. Doba, kdy Knuth navrhoval literární programování, pokročila a tvůrce dokumentace dnes může mít zároveň ve více oknech otevřeno více textů. Některé jsou určeny pro člověka a jiné pro počítač. Nevnímám tedy tak hlasitou potřebu tyto informace slučovat do jednoho souboru, jako tomu bylo kdysi. V první části (sekce~\cite[uziv]) dokumentu seznamujeme čtenáře s použitím \docbytex{}u na uživatelské úrovni. V další sekci jsou dokumentovaná výchozí makra \docbytex{}u, u nichž se předpokládá, že je bude chtít náročný uživatel měnit, aby přizpůsobil chování \docbytex{}u obrazu svému. Dále následuje sekce~\cite[design] s dokumentací maker, která rovněž budou měněna, pokud uživatel bude chtít jiný vzhled dokumentu. V~poslední sekci~\cite[implementace] je dokumentován kompletní \docbytex{} na implementační úrovni. Takže se tam můžete dočíst, jak makra fungují. Tento dokument je zpracován \docbytex{}em, takže slouží mimo jiné jako ukázka, co je možné tímto nástrojem vytvořit. \sec [uziv] Pro uživatele %%%%%%%%%%%%%%%%%%%%%%%%% \subsec [cleneni] Členění souborů %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \docbytex{} je implicitně navržen pro dokumentování zdrojových kódů v~jazyce~C. Proto i následující ukázka dokumentuje hypotetický program napsaný v tomto jazyce. Chcete-li dokumentovat jiný jazyk, můžete implicitní chování \docbytex{}u pozměnit. Tomu je věnována sekce~\cite[zmeny]. Předpokládá se, že zdrojové kódy programu jsou členěny na moduly. Každý modul je myšlenkově samostatná záležitost. Alespoň pro programátora. Každý modul má své jméno (například "cosi") a je napsán v souborech "cosi.h" a "cosi.c", případně v dalších. Tyto soubory se kompilují, aby vznikl "cosi.o" a v~závěru kompilace se linkují všechny kompilované moduly do výsledného programu. Chceme-li takové zdrojové kódy dokumentovat, připíšeme ke každému modulu soubor s příponou ".d", například "cosi.d", který obsahuje dokumentaci k danému modulu. Dále založíme třeba soubor "program.tex", ze kterého postupně načítáme dokumentace jednotlivých modulů pomocí příkazu "\module"\du{module}. V \uv{hlavním souboru} "program.tex" můžeme též použít příkazy "\title" pro vyznačení názvu programu, "\author" se jménem autora programu a třeba "\dotoc" pro vytvoření obsahu a "\doindex" pro vygenerování rejstříku. Samozřejmě zde můžeme napsat třeba úvodní poznámky ke zdrojovým kódům programu a použít plno dalších vymezovacích příkazů (viz dále). Obsah souboru "program.tex" může vypadat třeba takto: \begtt \input docby.tex \title Program lup -- dokumentace ke zdrojovým textům \author Progr a Mátor \dotoc % tady bude obsah \sec Členění zdrojových textů Zdrojové texty programu "lup" jsou rozděleny do tří modulů. V "base.c" jsou definovány pomocné funkce a v "base.h" jsou jejich prototypy. Podobně ve "win.c" jsou funkce pro okenní záležitosti a "win.h" obsahuje jejich prototypy. Konečně "main.c" obsahuje hlavní funkci programu. \module base \module win \module main \doindex % v tomto místě bude sestaven rejstřík \bye \endtt V tomto příkladě jsme se rozhodli čtenáře dokumentace seznamovat s programem \uv{zdola nahoru}, tedy od elementárních funkcí až k hotovému programu. Někdo možná preferuje cestu \uv{shora dolů} a může mít v dokumentaci napsáno: \begtt \module main \module win \module base \doindex \bye \endtt Oba přístupy jsou možné, protože dokumentace je automaticky provázána hyperlinky. Čtenář se kdykoli může podívat na dokumentaci té funkce, jejíž použití zrovna čte, a obráceně může projít výskyty veškerého použití funkce, když čte její dokumentaci. \subsec [priklad] Příklad dokumentace modulu %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Soubor s dokumentací jednotlivého modulu budu pro tento případ značit "cosi.d". Ten je načten příkazem "\module"~"cosi ". V souboru "cosi.d" je možno se literárně vyřádit a kdykoli vložit část existujícího zdrojového kódu programu se stejným jménem modulu. To provedeme příkazem "\ins"~"c keyword "\du{ins}, který vloží do dokumentace část zdrojového kódu ze souboru "cosi.c", která je vymezena pomocí slova "keyword". Místo písmene "c" je možno použít "h" nebo jakoukoli jinou příponu souboru, ze kterého chceme vložit část do dokumentace. K vymezení částí, které se mají vložit, je nutno mít ve zdrojovém souboru text "//: keyword". Vše vysvětlí následující příklad. Předpokládejme, že v souboru "cosi.d" máme napsánu tuto dokumentaci: \begtt Struktura \dg dvojice se používá jako návratová hodnota funkce "uzasna_funkce" a sdružuje dvě hodnoty typu "float". \ins c dvojice Funkce \dg [struct dvojice] uzasna_funkce() si vezme jeden parametr "p" a vrátí ve struktuře "dvojice" dvojnásobek a trojnásobek tohoto parametru. \ins c uzasna_funkce \endtt V tomto případě je nutné, aby v souboru "cosi.c" existoval vymezující text "//: "{\tt dvojice} a text "//: "{\tt uzasna\_funkce}. Tyto texty vymezují úseky, které se mají do dokumentace vložit. Soubor "cosi.c" může vypadat třeba takto: \begtt #include //: dvojice struct dvojice { float x, y; }; //: uzasna_funkce struct dvojice uzasna_funkce (float p) { struct dvojice navrat; navrat.x = 2*p; // tady nasobim p dvema navrat.y = 3*p; // tady nasobim p tremi return navrat; } \endtt Výsledek po zpracování části dokumentace z "cosi.d" pak vypadá takto: \bigskip Struktura \dg [struct] dvojice se používá jako návratová hodnota funkce "uzasna_funkce" a sdružuje dvě hodnoty typu "float". \def\modulename{cosi} \ins c dvojice Funkce \dg [struct dvojice] uzasna_funkce() si vezme jeden parametr "p" a vrátí ve struktuře "dvojice" dvojnásobek a trojnásobek tohoto parametru. \ins c uzasna_funkce V ukázkovém zdrojovém kódu je první vložený úsek vymezen na začátku textem "//: "{\tt dvojice} a na konci textem "//:". Druhý úsek je vymezen textem "//: "{\tt uzasna\_funkce} a končí na konci souboru. Na pořadí úseků, které zahrnujeme ze zdrojového textu do dokumentace, nezáleží. Klidně jsme mohli dokumentaci začít od povídání o úžasné funkci (včetně vložení jejího kódu) a potom ještě dopsat, co to je ta struktura "dvojice" a následně vložit deklaraci této struktury. Kdybychom před řádek "#include "{\tt} vložili třeba text "//: start", bylo by možné příkazem "\ins"~"c start " vložit do dokumentace začátek souboru "cosi.c", který v ukázce vložen není. Všimněme si, že \TeX{} zapsal čísla řádků přesně podle toho, jak jsou ve zdrojovém kódu. Tj. počítal i přeskakovaný řádek "#include "{\tt} i přeskakované prázdné a vymezující řádky. Vymezení "//: keyword" se může v řádku nacházet kdekoli, není nutné, aby se vyskytovalo na začátku řádku. Řádek s~tímto vymezením není do dokumentace zahrnut a pokud následuje za řádkem s~vymezením prázdný řádek, ani ten není do dokumentace zahrnut. Stejně tak koncové vymezení "//:" se může v řádku nacházet kdekoli a celý řádek s tímto vymezením není do dokumentace zahrnut. Pokud před tímto koncovým řádkem je prázdný řádek, ani ten není do dokumentace zahrnut. Konečně za povšimnutí stojí použití příkazu "\dg" v dokumentaci. Za ním následuje slovo (separované mezerou), které dokumentujeme. Toto slovo se v dokumentaci výrazně označí (v PDF verzi červenou barvou navíc v barevném rámečku) a jakýkoli jiný výskyt takového slova ve zdrojovém textu nebo mezi uvozovkami {\tt\char`\"...\char`\"} bude automaticky označen modrou barvou a bude klikací. Kliknutí na modrý výskyt slova kdekoli v dokumentaci vrátí čtenáře na červený výskyt, kde je slovo dokumentováno. Dokumentované slovo může mít před sebou v hranatých závorkách text, který např. označuje typ funkce a za sebou může mít kulaté závorky "()". Tím můžeme dát najevo, že dokumentujeme funkci. V místě dokumentace se neobjeví ani tento nepovinný text ani závorky, ale v poznámce pod čarou a v~rejstříku se tyto informace vytisknou. \uv{Palcové uvozovky} {\tt\char`\"...\char`\"} vymezují kusy kódu uvnitř odstavce. Text takto uvozený je psán strojopisem a pokud se v něm vyskutují deklarovaná slova, tato slova automaticky modrají a stávají se klikatelnými odkazy. Text mezi těmito uvozovkami je navíc přepisován ve \uv{verbatim} módu \TeX{}u, tj. žádné znaky nemají speciální vlastnosti (s výjimkou koncové palcové uvozovky). Na stránce, kde je slovo dokumentováno (pomocí "\dg"), je v poznánkách pod čarou slovo znovu zmíněno a vedle této zmínky je seznam všech stránek, na kterých se kdekoli v textu vyskytuje použití tohoto slova. Dále jsou všechna dokumentovaná slova zahrnuta do závěrečného abecedního rejstříku, který odkazuje jednak na stránku, kde je slovo dokumentováno, i na stránky se všemi výskyty slova. \subsec Jaký \TeX{} pro \docbytex{}? %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Aby fungovaly všechny výše uvedené vlastnosti, je potřeba použít pdf\TeX{} rozšířený o enc\TeX{}. Dále je dle "\language" detekován jazyk, který se použije v automaticky generovaných slovech. \docbytex{} se ohlásí na terminálu například těmito slovy: \def\begtthook{\catcode`\!=0} \begtt This is DocBy.TeX, version !dbtversion, modes: enc+PDF+ENG \endtt \def\begtthook{} \docbytex{} rozlišuje tři módy, každý může nabývat dvou stavů: mód "enc/NOenc", dále mód "PDF/DVI" a konečně mód jazyka "ENG/CS". Mód "enc"\api{enc} se zapne, je-li detekována přítomnost enc\TeX{}u. Pokud enc\TeX{} není dostupný, vypíše o~tom \docbytex{} varování a přejde do "NOenc"\api{NOenc} módu. V tomto módu nefunguje automatická detekce slov, která jsou dokumentována, takže tato slova nemodrají a nestávají se klikacími odkazy. V rejstříku pak také není seznam stránek se všemi výskyty slova, ale jen místo, kde je slovo dokumentováno. V~tomto případě tedy je deaktivována nejdůležitější vlastnost \docbytex{}u, takže je žádoucí vynaložit jisté úsilí a enc\TeX{} zprovoznit. V současných distribucích \TeX{}u bývá enc\TeX{} v pdf\TeX{}u zahrnut a je aktivován například ve formátu "pdfcsplain". Mód "PDF"\api{PDF} je detekován, pokud je použit pdf\TeX{}, jinak \docbytex{} přejde do módu "DVI"\api{DVI} a napíše o tom varování na terminál. V módu "DVI" nefungují barvy ani klikací odkazy. Ovšem seznam stránek s~použitím dokumentovaného slova se generuje, je-li přítomen enc\TeX. \docbytex{} detekuje mód jazyka "ENG" (angličtina), je-li "\language=0". To je implicitní chování. Pokud například v "csplainu" nastavíte "\chyph" před "\input docby.tex", \docbytex{} to vyhodnotí jako dokument v češtině ("CS"). Jiné jazyky nejsou zatím podporovány. V módu "ENG" jsou automaticky generované názvy \uv{Contents}, \uv{Index} anglické, v módu "CS" jsou tyto názvy \uv{Obsah}, \uv{Rejstřík} české. V~sekci~\cite[nazvy] je řečeno, jak jsou tato slova generována a co tedy udělat, když chcete mít dokument v jiném jazyce. \subsec Vyhledávání slov enc\TeX{}em %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Slova, která se stávají klikatelnými odkazy vyhledává enc\TeX{}. Ten má zabudován tzv. \uv{hladový algoritmus}. To znamená, že jsou-li dokumentována např. slova "abc" a "abcde", pak text "abcdefgh" zmodrá až po písmeno "e" a odkazuje na "abcde", zatímco "abcdx" zmodrá až po písmeno "c" a odkazuje na "abc". To bývá obvykle žádoucí. % V enc\TeX{}u není možno programovat vyhledávání podle regulárních výrazů, takže není možné jednoduše říci, aby enc\TeX{} hledal jen slova, která jsou ohraničena mezerou, tečkou, závorkou, středníkem, atd. Místo toho enc\TeX{} tupě vyhledá slovo třeba uvnitř jiného slova. Může se tedy stát, že máme dokumentováno kratší slovo, které se objevuje jako část jiných nedokumentovaných slov. Například je dokumentována struktura "turn", ale ve výpisech programu nechceme, aby v každém výskytu klíčového slova "return" zmodrala jeho část. V takovém případě je potřeba explicitně definovat "return" jako \uv{normální} nedokumentované slovo. K tomu slouží příkaz "\noactive{}"\du{noactive}, tedy například "\noactive{return}". Tento příkaz globálně deklaruje "" jako vyhledávané slovo (pro enc\TeX), ale specifikuje jej jako neaktivní. Může se také stát, že máme dokumentováno slovo, které se objevuje ve zdrojových textech i v jiném (nedokumentovaném) významu. Přitom dokumentované slovo poznáme podle toho, jak vypadá text před slovem a za slovem. Pak lze použít deklaraci "\onlyactive{}{}{}"\du{onlyactive}, která sama o sobě nedělá nic. Pokud ale vyznačíme "" pomocí "\dg" (nebo podobného makra na dokumentování slov, viz sekce~\cite[ddsl]), pak bude "" automaticky modrat jen tehdy, předchází-li mu text "" a následuje text "". Texty "" nebo "" mohou být prázdné (ne oba současně) a k jednomu "" můžeme napsat více různých deklarací "\onlyactive". \docbytex{} aktivuje enc\TeX{} (pomocí "\mubytein=1") jen uvnitř skupiny, když zpracovává text mezi palcovými uvozovkami ({\tt\char`\"...\char`\"}) nebo při načítání zdrojového textu programu. Předpokládá se, že nepoužíváte enc\TeX{} k~dekódování UTF-8 kódu. Pokud používáte, zkuste si zapnout "\mubytein=1" pro celý dokument, ale na {\it vlastní riziko}. V takovém případě vám budou modrat slova nebo jejich části i v~běžném textu a pokud je dokumentované slovo podmnožinou nějaké \TeX{}ové sekvence, kterou používáte, pak se dočkáte nepříjemných chyb. \subsec Generování rejstříku, obsahu, poznámek pod čarou a záložek %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Generování rejstříku i obsahu probíhá v \docbytex{}u zcela automaticky. Pro vytvoření rejstříku není nutné používat externí program (\docbytex{} si slova abecedně zatřídí sám). Stačí tedy vložit na požadovaná místa příkazy "\dotoc"\du{dotoc} a "\doindex"\du{doindex}. Upozorňuji, že rejstřík ani obsah nejsou správně vygenerovány po prvním průchodu \TeX{}u. Je potřeba \TeX{}ovat dvakrát. Po druhém průchodu dojde zřejmě k přestránkování textu (protože je například vložen obsah). Je tedy nutné \TeX{}ovat ještě jednou. Tři průchody \TeX{}em jsou (snad) dostačující. Slovo \uv{snad} vychází z problému s poznámkami pod čarou podrobně popsaném v sekci~\cite[specfootnote]. Poznámky pod čarou se totiž průběžně v průchodech mění a ovlivňují zpětně vertikální sazbu. \docbytex{} proto provádí na konci zpracování v příkaze "\bye"\du{bye} kontrolu, zda nedošlo ke změnám v referencích. Je proto užitečné používat "\bye" místo "\end". V závěru zpracování pak \docbytex{} vypíše zprávu "OK, all references are consistent" nebo vypíše varování, že některé reference jsou nekonzistentní a že je tedy potřeba \TeX{}ovat znovu. Další test konzistence můžeme provést například následujícím skriptem: \begtt #!/bin/bash cp dokument.ref dokument.r0 pdfcsplain dokument.d diff dokument.r0 dokument.ref \endtt \docbytex{} se snaží (z důvodu záruky konvergence dokumentu) fixovat zpracování poznámek pod čarou po druhém průchodu. Pokud poté měníte rozsáhle dokument, takže seznamy stránek vedle poznámek pod čarou jsou výrazně jiné délky, \docbytex{} to nepozná a může docházet k přeplnění nebo nenaplnění stránek. V takovém případě je rozumné vymazat soubor {\tt.ref} a znovu spustit tři průchody. Pro vytvoření záložek se strukturovaným obsahem v PDF výstupu slouží příkaz "\bookmarks"\du{bookmarks}. Je zcela jedno, v které části dokumentu je tento příkaz napsaný, neboť sestaví stukturovaný seznam záložek prolinkovaný s dokumentem na základě údajů ze souboru~{\tt.ref}. Může se stát, že některé texty v záložkách nejsou optimálně čitelné. O možnostech, jak toto řešit, pojednává sekce~\cite[hooky]. \subsec [vkladani] Vkládání zdrojových textů podrobněji %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Kromě jednoduchého příkazu "\ins" na vkládání zdrojových textů jsou k dispozici příkazy "\ifirst"\du{ifirst} a "\inext"\du{inext}, které nabízejí uživateli daleko více možností. Příkaz "\ifirst{}{}{}{}" vloží do dokumentu část souboru "" (plný název souboru včetně přípony) od prvního řádku, na kterém se vyskytuje text "" po řádek, na kterém se vyskytuje text "", nebo (pokud text "" nelze nalézt) po konec souboru. Neexistuje-li ani řádek s textem "", \docbytex{} vypíše pouze varování na terminál. Příkaz "\ifirst" si své parametry nejprve expanduje a pak teprve použije. Aktivní vlnka v parametru expanduje na mezeru. Parametr "" udává, zda se bude tisknout výchozí řádek (s~textem "") a koncový řádek (s~textem ""). Tento parametr obsahuje právě dva znaky (plus nebo mínus) s následujícím významem: \begtt jak: -- netiskne se výchozí ani koncový řádek jak: +- tiskne se výchozí řádek a netiskne se koncový řádek jak: -+ netiskne se výchozí řádek, tiskne se koncový řádek jak: ++ tisknou se oba řádky \endtt Je-li parametr "" prázdný (zapíšeme pomocí "{}"), tiskne se od začátku souboru. Je-li parametr "" prázdný, tiskne se jediný řádek. Je-li parametr "=\end",\du{end} tiskne se až do konce souboru. Koncový řádek v tomto případě neexistuje. Má-li parametr "" (nebo "") hodnotu "\empty"\du{empty} (zapíšeme pomocí "{\empty}"), tiskne se od (nebo do) prvního prázdného řádku. Parametr "" ovlivní jeho tisk. Parametry "" nebo "" mohou mít na svém začátku znak "^^B" (tím dáváme najevo, že text musí na řádku začínat) nebo na svém konci znak "^^E" (tím dáváme najevo, že text musí na řádku končit). Takže třeba "^^Btext^^E" znamená, že se vyhledává řádek, ve kterém je pouze "text" a nic jiného. V parametrech "" a "" se nesmějí vyskytovat speciální \TeX{}ové znaky (speciální kategorie). Pro použití znaků "\", "{", "}", "%" a {\tt\char`\"} v těchto parametrech jsou v~\docbytex{}u připraveny zástupné kontrolní sekvence "\nb"\du{nb}, "\obrace"\du{obrace}, "\cbrace"\du{cbrace}, "\percent"\du{percent} a "\inchquote"\du{inchquote}. Sekvence pro další speciální znaky "#", "$", atd. si musíte vytvořit např. pomocí: \begtt {\catcode`\#=12 \gdef\vezeni{#}} \endtt Jsou-li parametry "" a "" stejné, nebo oba texty jsou na stejném řádku, pak se při "=++" nebo "=+-" vytiskne tento jeden řádek. Při "=-+" nebo "=--" se tiskne až do konce souboru nebo do dalšího výskytu textu "". Příkaz "\ifirst" si zapamatuje název čteného souboru a pozici posledního přečteného řádku v~daném souboru. Pak je možné použít příkaz "\inext{}{}{}", který začíná hledat výchozí řádek s textem "" od místa v souboru, kde naposledy skončilo čtení příkazem "\ifirst" nebo "\inext". Parametry "", "" a "" mají stejný význam, jako u příkazu "\ifirst". V registru "\lineno"\du{lineno} je po ukončení příkazu "\ifirst" nebo "\inext" číslo řádku, které bylo naposledy přečteno (třebaže tento řádek nebyl vytištěn). Pokud bylo dosaženo konce souboru, obsahuje "\lineno" počet řádků souboru. Pomocí "\ifeof\infile" je možné se zeptat, zda bylo dosaženo konce souboru. Příklady \begtt \ifirst {soubor.txt}{textik}{textik}{++} % vytiskne první výskyt řádku % obsahující slovo textik \inext {textik}{textik}{++} % vytiskne následující výskyt % řádku obsahující slovo textík \ifirst {soubor.c}{//: odkud}{//:}{--} % analogie příkazu \ins \ifirst {soubor.c}{funkce(}{)}{++} % tisk prototypu funkce \ifirst {soubor.c}{funkce(}{^^B\cbrace}{++} % tisk celého kódu funkce \ifirst {soubor.txt}{}{\end}{++} % tisk celého souboru \ifirst {soubor.txt}{}{\empty}{+-} % tisk po prázdný řádek \endtt Je-li první řádek, který se má tisknout, prázdný, netiskne se. Je-li poslední řádek, který se má tisknout, prázdný, také se netiskne. Toto je implicitní chování. Pokud napíšete "\skippingfalse",\du{skippingfalse} uvedená inteligence je zrušena a přepisují se i prázdné řádky vpředu a vzadu. Příkazem "\skippingtrue"\du{skippingtrue} se vrátíte k původnímu nastavení. Parametrům "" a "" může předcházet text "\count=<číslo> ".\du{count} Hodnota "<číslo>" označuje, kolikátý výskyt textu "" nebo "" se má použít. Například "{\count=3 }" znamená, že se má při vyhledávání "" přeskočit dva jeho výskyty a začít přepisovat soubor až od výskytu třetího. Podobně "{\count=5 }" značí, že se při přepisování souboru ignorují čtyři výskyty "" a přepisování se zastaví až u výskytu pátého. Implicitně, není-li "\count=<číslo> " uvedeno, předpokládá se "\count=1 ". Pokud je text "" prázdný, pak "\count" označuje číslo řádku, na kterém se má zahájit výpis. Je-li prázdný parametr "", pak "\count" označuje počet přepisovaných řádků. Toto platí pro "=++" a pro "\skippingfalse". Při jiných hodnotách "" se uvedená čísla logicky posunou o jedničku. Při prázdném "" nebo "" není mezera za "\count=<číslo>" povinná. Příklady: \begtt \skippingfalse \ifirst {soubor.txt}{\count=20}{\count=10}{++} % tisk řádků 20 až 29 \ifirst {soubor.txt}{}{\count=2 \empty}{+-} % tisk po druhý prázdný řádek \ifirst {soubor.txt}{\count=50}{\end}{++} % tisk od 50. řádku do konce \ifirst {soubor.tex}{\count=5 \nb section}{\count=2 \nb section}{+-} % tisk páté sekce z TeXového souboru \endtt \subsec [lineodkazy] Odkazy na čísla řádků %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Pomocí "\cite[]" je možné odkazovat na číslo řádku ve výpisu zdrojového kódu. Tento příkaz se promění na skutečné číslo řádku. Před použitím příkazu "\ifirst" nebo "\inext" je nutné "" deklarovat příkazem "\ilabel []{}".\du{ilabel} Těchto příkazů může být před použitím "\ifirst" resp. "\inext" více. Na pořadí příkazů "\ilabel" před jedním "\ifirst" nebo "\inext" nezáleží. Existují-li deklarované ""y a ""y, pak příkaz "\ifirst" nebo "\inext" si všímá výskytu ""u ve vkládaných řádcích. Pokud takový "" najde, přiřadí číslo řádku odpovídajícímu ""u, takže příkaz "\cite" bude fungovat, jak má. Parametr "" musí být jednoznačný v celém dokumentu. Příkaz "\cite" funguje dopředně i zpětně. Příkazy "\ilabel" mají lokální působnost a spolupracují jen s nejbližším následujícím "\ifirst" a "\inext". Takže před použitím dalšího "\ifirst" resp. "\inext" je potřeba deklarovat další vyhledávané texty pomocí "\ilabel" znovu. \docbytex{} nevypíše žádné varování, pokud nějaký "" deklarovaný v "\ilabel" nenajde. Ovšem při použití "\cite" se objeví varování, že není známý "" a toto varování nezmizí ani při opakovaném \TeX{}ování. Pokud se "" vyskytuje ve více řádcích ukázky, je odkazován řádek s prvním výskytem. V následující ukázce je čten již známý soubor "cosi.c" (viz kapitolu~\cite[priklad]). \begtt Na řádku~\cite[ufunkce] je deklarovaná úžasná funkce. \ilabel [ufunkce] {funkce (float} \ilabel [navratx] {navrat.x} \ifirst {cosi.c}{}{}{++} Zvláště upozorňuji na geniální myšlenku na řádku~\cite[navratx], kde je vstupní parametr vynásoben dvěma. \endtt \subsec Verbatim ukázky pomocí {\tt\nb begtt}/{\tt\nb endtt} a palcových uvozovek %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Verbatim ukázky můžete do dokumentace vkládat pomocí "\begtt"\du{begtt} a "\endtt"\du{endtt}. Ty jsou (na rozdíl od vkládaných souborů) napsány přímo ve zdrojovém textu \TeX{}u. Všechny řádky za "\begtt" jsou vloženy beze změn až po ukončovací "\endtt". Řádky nejsou číslovány a texty v nich nemodrají a nestávají se klikatelnými odkazy. Následující sekce~\cite[hooky] a~\cite[begtt] obsahují informace, jak je možné toto implicitní chování změnit. Verbatim ukázky uvnitř odstavce lze vymezit palcovými uvozovkami {\tt\char`\"...\char`\"}. V tomto prostředí probíhá tisk strojopisem a je aktivní enc\TeX{}, takže dokumentovaná slova se stávají automaticky odkazy na místo, kde je "\dg". Doporučuje se toto prostředí používat na výpisy veškerých částí kódů dokumentovaného programu, které jsou vloženy uvnitř textu v odstavci (analogie matematického prostředí "$...$"). \subsec [ddsl] Deklarace dokumentovaného slova %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Na deklaraci slova, které dokumentujeme, lze použít příkaz "\dg",\du{dg} "\dgn",\du{dgn} "\dgh",\du{dgh} "\dl",\du{dl} "\dln"\du{dln} nebo "\dlh"\du{dlh}. Významy jednotlivých příkazů vysvětlíme později. Nejprve se věnujme syntaxi parametrů. Všechny příkazy mají stejnou syntaxi, takže nebude vadit, když bude vyložena jen v souvislosti s příkazem "\dg". Syntaxe je poněkud zvláštní. Účelem totiž bylo minimalizovat práci písaře, takže jsem se vyhnul kučeravým závorkám, parametr separuji podle mezery nebo něčeho jiného, atd. Existují tyto možnosti syntaxe parametrů: \def\begtthook{\langleactive \mubytein=1} \begtt \dg % separované mezerou \dg [] % navíc nepovinný "přední" \dg [] % může na [] navazovat bez mezery \dg () % s dvojicí "()" separované mezerou \dg []() % kombinace předchozího \dg , % separované čárkou \dg [] , % kombinace předchozího \dg (), % s dvojicí "()" separované čárkou \dg [](), % kombinace předchozího \dg . % slovo separované tečkou atd... \endtt \def\begtthook{} Obecně: za příkazem "\dg" může následovat nepovinná "[". Pokud následuje, pak se přečte "" až po ukončovací "]". Parametr "" může obsahovat mezery. Za ukončovací "]" může a nemusí být mezera. Pokud tam je, pak ji makro přesune před koncovou závorku "]", takže "\dg"~"[aha] slovo" je totéž jako "\dg"~"[aha ]slovo". Dále následuje čtení parametru "". Tento parametr nesmí obsahovat mezeru, čárku, tečku, středník a dvojtečku. Čtení parametru je ukončeno, jakmile se objeví mezera nebo čárka nebo tečka nebo středník nebo dvojtečka. Uvedená interpunkce není součástí parametru "" a po zpracování parametru se vrátí do vstupní fronty, takže se běžně vytiskne. Nakonec se zjistí, zda přečtený parametr až po separátor není ve tvaru "()". Pokud je, pak symbol "()" se nepovažuje za součást parametru "", ale mluvíme o "" následovaném dvojicí~"()". Pozor, za separátorem typu čárka, tečka, středník a dvojtečka se musí vyskytnout mezera. Ne nutně ihned, ale dříve, než se objeví úsek textu, který má být přečten s jinými kategoriemi (např.~{\tt\char`\"...\char`\"}). Není tedy možné psát "\dg" {\tt text,\char`\"...\char`\"}. Pokud za separátorem mezera následuje znak {\tt\char`\`} (obrácený apostrof), mezera ani tento znak se netiskne. To je možné využít například pro vložení nezlomitelné mezery nebo pro jiné účely: "\dg"~" `~" nebo "\dg"~" `"{\tt\char`\"...\char`\"}. Příkazy "\dgh", "\dgn", "\dln", "\dlh" separující mezeru netisknou nikdy, protože tyto příkazy většinou netisknou nic (viz níže). Parametr "" je dokumentované slovo. Pokud se takové "" vyskytne někde jinde v dokumentu mezi {\tt\char`\"...\char`\"} nebo ve vloženém zdrojovém kódu, automaticky zmodrá a stává se klikatelným odkazem na místo, kde je použito "\dg". V místě použití "\dg" je slovo zvýrazněno červenou barvou. Je vytištěno samotné bez parametru "" a bez případných závorek "()". V poznámce pod čarou se vypíše "" (červeně). Tam je i případný "" (před slovem) a za ním je případná dvojice "()". Vedle tohoto výpisu je seznam stránek s výskyty "". V rejstříku se objeví něco podobného, jako v poznámce pod čarou. Rejstřík je řazen abecedně podle "", nikoli podle "". Příkaz "\dg" deklaruje "" globálně. Bude na něj odkazováno v celém dokumentu. Příkaz "\dgh" pracuje jako "\dg", ale slovo nebude v místě "\dgh" vypsáno ("\dg" hidden). Bude tam jen cíl odkazů a "" se objeví v poznámce a v rejstříku. Příkaz "\dgn" způsobí, že první následující výskyt "" ve vypisovaném zdrojovém kódu se stane cílem všech ostatních odkazů, zčervená (tedy nezmodrá) a v místě tohoto výskytu se objeví příslušná poznámka pod čarou. Příkaz "\dgn" čteme jako "\dg" next, nebo "\dg" následující. Příkaz "\dl" deklaruje "" lokálně. Bude na něj odkazováno svým krátkým jménem "" jen v místě stejného jmenného prostoru, typicky při dokumentaci jednoho modulu. Každý modul zahájený příkazem "\module" zavádí jmenný prostor tvaru "./", kde "" je jméno modulu. Slovo deklarované pomocí "\dl" žije ve dvou variantách. V krátké variantě jako "" jen v rozsahu jednoho jmenného prostoru a v dlouhé variantě "./" žije globálně v celém dokumentu. Případný výskyt dlouhého názvu odkáže na místo deklarace napříč celým dokumentem. Podrobněji o jmenných prostorech a možnosti jejich změny najdete v~sekci~\cite[jmenneprostory]. Každé "" musí být v dokumentu deklarováno nejvýše jednou, jinak \docbytex{} ohlásí chybu. V případě "\dl" musí existovat jednoznačný dlouhý název. Příkaz "\dlh" je skrytý "\dl". Příkaz "\dln" znamená "\dl" next. Analogicky, jako příkazy "\dgh" a "\dgn". Pokud někoho irituje vysoká inteligence těchto příkazů při čtení parametrů, může použít interní verzi příkazů s povinnými třemi parametry obalenými do kučeravých závorek: "\iidg",\du{iidg} "\iidgh",\du{iidgh} "\iidgn",\du{iidgn} "\iidl",\du{iidl} "\iidlh",\du{iidlh} "\iidln".\du{iidln} Parametry vypadají takto: "\iidg{}{}{}". Pravda, tyto příkazy umožňují více než jejich krátké verze: umožňují do parametru "" propašovat čárku, mezeru, středník atd. a do parametru "" napsat cokoli, nejen kulaté závorky. \subsec [jmenneprostory] Jmenné prostory %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Jmenný prostor je pravidlo, podle kterého se krátký název dokumentovaného "" transformuje při použití "\dl" na název dlouhý. Je možné jej nastavit nebo změnit pomocí příkazu "\namespace"\du{namespace}, který se použije takto: "\namespace" "{#1}...\endnamespace".\du{endnamespace} Pokud je uvnitř tohoto prostředí použit příkaz "\dl", je slovu přidělen krátký název "" a dlouhý název "". Uvnitř takto deklarovaného prostředí se všechny výskyty krátkého názvu "" transformují na dlouhý název a jsou prolinkovány s odpovídajícím místem "\dl". Jmenný prostor je lokální uvnitř svého prostředí, takže vně prostředí se "" chová, jakoby nebyl žádný příkaz "\dl" použit. Například uvnitř prostředí "\namespace"~"{#1//uff}...\endnamespace" je ke každému slovu deklarovanému pomocí "\dl" přidělen dlouhý název "//uff" a výskyty "" odkazují na místo "\dl". Vně všech prostředí "\namespace...\endnamespace" není jmenný prostor definován, takže tam není možné použít příkaz "\dl". Ovšem příkaz "\module"~" " nastaví jmenný prostor na "{#1./}", takže uvnitř dokumentace modulu je možné používat příkaz~"\dl". V rejstříku a v poznámce pod čarou se tisknou dlouhé názvy. Rejstřík abecedně řadí podle dlouhých~názvů. V obsahu se tisknou názvy krátké. Příklad práce se jmennými prostory: \begtt \namespace {ju::#1} %% nastavuji namespace ju Tady deklaruji slovo \dl aha. Tady slovo "aha" automaticky odkazuje na místo deklarace. Slovo "ju::aha" také odkazuje na místo deklarace. \endnamespace \namespace {hele::#1} %% nastavuji namespace hele Tady znovu deklaruji slovo \dl aha. Zde slovo "aha" odkazuje na lokální deklaraci uvnitř "hele" \endnamespace %% ruším namespace Zde slovo "aha" neodkazuje nikam, ale slova "ju::aha" a "hele::aha" stále odkazují na místa, kde byla deklarována. \endtt Prostředí "\namespace...\endnamespace" je možné vnořovat, ovšem vnořená prostředí musejí mít jiný jmenný prostor než prostředí vnější. Prostředí jmenných prostorů pracují globálně nezávisle na "\bgroup", "\egroup". Příkaz "\endnamespace" použitý vně všech prostředí "\namespace...\endnamespace" neudělá nic. Prostředí není nutné před příkazem "\bye" ukončovat. \subsec Místo pro dokumentaci aplikačního rozhraní %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Může se stát, že píšeme dokumentaci jednak pro uživatele, které zajímá způsob použití dokumentovaných funkcí a co zhruba dělají (tzv.~API), ale nezajímá je, jak je funkce naprogramovaná. Druhak chceme mít dokumentován i způsob, jak funkce funguje uvnitř. V takovém případě musí dokumentované "" odkazovat na dvě místa v dokumentu. Místo, kde je podrobně "" popsáno, je vymezeno příkazem "\dg" nebo podobným. Místo, kde slovo dokumentujeme pro uživatele (je-li toto místo odlišné od prvního místa), lze vyznačit příkazem "\api{}"\du{api}. V místě použití "\api{}" se nestane nic, jen se tam umístí neviditelný cíl odkazů. V obsahu se pak "" objeví s odkazem na toto místo. V rejstříku se v seznamu stránek objeví jedna stránka podtržená: to je stránka, kde byl použit příkaz~"\api{}". Ovšem, aby se v rejstříku "" vůbec objevilo, musí se někde v dokumentu vyskytovat i jeho plná deklarace pomocí "\dg" nebo podobných příkazů. Na stránce, kde je použito "\dg", je pod čarou vedle slova seznam stránek a rovněž je tam jedna stránka podtržená. Když čtete implementační popis pro "", snadno se tedy dostanete na stránku, kde je API k tomuto "". V~rejstříku a obsahu jsou také slova, která byla deklarovaná pomocí "\api", zleva vyznačena textem "\apitext"\du{apitext}. Ten je implicitně nastaven na šipku. Můžete se podívat do rejstříku a do obsahu tohoto dokumentu. V tomto místě bylo použito "\api{\nb api}", zatímco skutečná definice příkazu "\api" je v sekci~\cite[reference]. Je-li použito "\api{}", pak je možné se na místo odkazovat také pomocí "\cite[+]". Tato konstrukce se promění v číslo stránky, kde je dokumentováno API daného slova. Například v tomto dokumentu se "\cite[+\nb api]" promění na:~\cite[+\nb api]. Pokud toto slovo má také svůj API cíl (vytvořený pomocí "\api"), pak se červený text (tištěný v místě "\dg") stává aktivním odkazem na API cíl. Tam typicky čtenář najde výskyt slova, který je zase klikatelným odkazem na "\dg" cíl. Takže tyto dva cíle jsou prolinkovány křížem. \subsec [kapsec] Sekce, sekcičky, část, titul %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Sekce se uvozují příkazem "\sec"~"\par"\du{sec}. Každá sekce může mít několik podsekcí (sekciček), které lze vyznačit příkazem "\subsec"~"\par"\du{subsec}. Symbol "\par" zde znamená, že název sekce či podsekce je oddělen od dalšího textu prázdným řádkem (viz ukázku v~\cite[cleneni]). Několik sekcí může tvořit část. Část je uvozena příkazem "\part"~"\par"\du{part}. Části jsou automaticky označeny písmeny A,B,C,\dots a jsou vyznačeny výrazněji než sekce v~místě začátku části i v~obsahu. Části ale nenarušují číslování sekcí. Tj. sekce jsou číslovány od jedné napříč celým dokumentem bez ohledu na to, zda jsou nebo nejsou rozděleny na části. Příkaz "\module " automaticky založí sekci s názvem "Modul " a deklaruje svůj jmenný prostor. Toto chování lze změnit, viz~\cite[nazvy], \cite[module]. Příkaz "\title\par"\du{title} vytiskne název dokumentu větším písmem a v rámečku. Je-li definováno makro "\projectversion"\du{projectversion}, bude jeho obsah vytištěn drobně vpravo nahoře doplněný zepředu textem "verze". Pokud váš projekt nemá verzi, může se hodit třeba: \begtt \def\projectversion{\the\day. \the\month. \the\year} \endtt Příkaz "\author\par"\du{author} napíše do středu řádku tučně "", což bývá obvykle jméno autora (jména autorů). Do záhlaví každé stránky se začne přepisovat zleva název aktuální sekce a zprava název dokumentu. Uživatel může text pro pravé záhlaví změnit změnou makra "\headtitle"\du{headtitle}. Příkazy "\sec" a "\subsec" mohou mít v hranaté závorce nepovinný parametr "". V takovém případě vypadají parametry takto: "\sec"~"[] \par". Po takovém použití je možné se na sekci (podsekci) odkazovat příkazem "\cite[]". Tento příkaz se promění v číslo odkazované sekce (podsekce) a navíc se stane aktivním odkazem. Pomocí příkazu "\savetocfalse"\du{savetocfalse} lze před použitím příkazu "\sec" nebo "\subsec" zajistit, že název sekce se nedostane do obsahu a nebude mít své číslo. Místo čísla se vytiskne obsah makra "\emptynumber"\du{emptynumber}, které je implicitně prázdné. Příkaz "\savetocfalse" ovlivní jen první následující "\sec" nebo "\subsec". \subsec [krizodkaz] Křížové odkazy %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Cíl, kam směřuje odkaz, je potřeba vyznačit pomocí "". To je možné udělat v příkaze "\sec", "\subsec" (viz předchozí sekci~\cite[kapsec]) nebo kdekoli v textu samostatným příkazem "\label[]"\du{label}. Také je možné odkazovat na číslo řádku (viz sekci~\cite[lineodkazy]). Všechny lejblíky musejí být jednoznačné (bez ohledu na jejich typ) napříč celým dokumentem. Příkaz "\pgref[]"\du{pgref} expanduje na číslo strany, na které se vyskytuje cíl odkazu. Příkaz "\numref[]"\du{numref} expanduje v~závislosti na typu cíle na: \begitems \item * číslo sekce, je-li cílem sekce, \item * dvojčíslí ".", je-li cílem podsekce, \item * číslo řádku, je-li cílem řádek zdrojového kódu, \item * prázdné makro, je-li "" deklarovaný pomocí "\label". \enditems Oba příkazy "\pgref" a "\numref" expandují na uvedené texty bez další inteligence. Tj. výstupní text se nestává klikatelným odkazem. K aktivaci odkazu v PDF módu slouží makro "\ilink"~"[]{}"\du{ilink}. Toto makro vytiskne modře "", který se stává klikatelným odkazem na cíl, deklarovaný pomocí "". Takže již známý příkaz "\cite[]"\du{cite} udělá zhruba to samé, jako "\ilink[]{\numref[]}". Skutečný příkaz "\cite" navíc ověří, zda není "\numref[]" prázdné makro. Pokud je, obarví namísto výstupu "\numref" výstup makra "\pgref". Pokud "" jako argument příkazu "\pgref", "\numref" nemá svůj cíl, příkaz "\pgref" expanduje na hodnotu $-1000$ a "\numref" expanduje na prázdný výstup. Jsou to expanzní makra, takže v nich není implementován například tisk varování. Podívejte se na definici příkazu "\cite" (na straně~\cite[@\nb cite]), jak se dá tisk varování implementovat. Makro "\module"~" " založí sekci s lejblíkem "m:", takže lze na ní pak odkazovat. Například si můžete vytvořit makro \begtt \def\refmodul[#1]{\ilink[m:#1]{\tt#1}} \endtt % které aktivizuje svůj parametr, pokud tento je názvem nějakého modulu. Třeba "\refmodul[base]" vytiskne slovo "base" strojopisem a modře a stává se klikatelným odkazem na začátek sekce \uv{Modul base}, pokud je tato sekce založena příkazem "\module". Makra "\dg", "\dgn", "\dgh" interně provedou příkaz "\label[@]" a makra "\dl", "\dln", "\dlh" provedou příkaz "\label[@]", kde "" je "" po transformaci podle aktuálního jmenného prostoru. Na místa, kde jsou slova dokumentovaná, je tedy možné odkazovat například pomocí "\link[@]{ dokumentované na straně~\pgref[@]}". Makro "\api{}" interně provede "\label[+]", takže je možné na toto místo odkazovat třeba pomocí "\ilink[+]{API: }". \docbytex{} nenabízí kromě čísel sekcí, podsekcí a čísel řádků žádné další automatické číslování. Pokud tedy chcete implementovat např. číslování obrázků, čísla publikací atd., musíte si napsat makra vlastní. K tomu můžete využít makro "\labeltext[]{}"\du{labeltext}, které uloží v horizontálním módu do sazby neviditelný cíl odkazu, a při dalším průchodu \TeX{}em expanduje makro "\numref" na "". Použití makra ukážeme na příkladě, ve kterém definujeme makro "\bib[]". Toto makro zahájí sazbu další položky v seznamu literatury. Odkazovat na knihu pak lze pomocí "\cite[b:]". \begtt \newcount\bibnum \def\bib [#1]{\par\advance\bibnum by1 \indent \llap{[\the\bibnum] }\labeltext[b:#1]{[\the\bibnum]}\ignorespaces} \endtt \subsec Vkládání obrázků %%%%%%%%%%%%%%%%%%%%%%%% Příkazem "\ifig" "<šířka> "\du{ifig} je možné vložit obrázek. Obrázek musí být připraven v souboru "fig/.eps" (v případě DVI módu) a v souboru "fig/.pdf" (v~případě PDF módu). Adresář, kde \docbytex{} vyhledává obrázky ("fig/"), lze změnit předefinováním sekvence "\figdir"\du{figdir} . Rozměr "<šířka>" je bez jednotky a udává poměr požadované šířky obrázku ku šířce sazby. Obrázek je umístěn zarovnán doleva na odstavcovou zarážku. Máte-li připraven obrázek ve formátu "eps", pak jej do "pdf" převedete příkazem {\def\begtthook{\langleactive} \begtt ps2pdf -dEPSCrop .eps \endtt \par} \subsec Výčty %%%%%%%%%%%%% Seznam položek obklopíte "\begitems"\du{begitems} a "\enditems"\du{enditems}. V tomto prostředí je text odsazen zleva o~odstavcovou zarážku. Prostředí lze vnořovat. Jednotlivou položku zahájíte pomocí "\item"~" ",\du{item} přitom "" se vystrčí vlevo od "". Je-li "" hvězdička, promění se v puntík. Další možnost: "\item"~"\the\itemno) ",\du{itemno} což vytvoří číslované výčty, v každém prostředí číslovány od jedné. Makro plainu "\item" není předefinováno globálně, ale jen uvnitř "\begitems...\enditems". Můžete tedy použít i makro plainu, pokud se vám koncept položek nabízený \docbytex{}em nelíbí. \noactive{/*}\noactive{*/}\noactive{//} \setlinecomment{\percent} \noactive{\nb\percent} \noactive{\percent\cbrace} \sec [zmeny] Pro náročné %%%%%%%%%%%%%%%%%%%%%%%% V této sekci jsou uvedeny a vysvětleny definice základních příkazů \docbytex{}u. Uživatel si může tyto definice změnit, pokud chce změnit chování \docbytex{}u. Pokud například pracuje s jiným programovacím jazykem, může si změnit makro "\docsuffix" nebo kompletně předefinovat makra "\module" a "\ins". \subsec [nazvy] Interní názvy %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Příkazem "\doindex" vytvoří \docbytex{} automaticky novou sekci s názvem \uv{Rejstřík}. Podobně při tvorbě obsahu nebo natažení modulu vzniká název \uv{Obsah} nebo \uv{Modul}. Před názvem verze v~titulu při použití "\projectversion" se objeví slůvko \uv{verze}. Část (vytvořená pomocí "\part") má v záložkách uvozující text ">> CAST". Tyto texty jsou definovány v makrech \db titindex, \db tittoc, \db titmodule, \db titversion a \db opartname. \ifirst {docby.tex}{Intern}{\empty}{--} Za povšimnutí stojí, že jsou jinak tato makra definována při použití klasického "plain"u a jinak při použití "csplain"u. To ovšem neznamená, že uživatel si tyto názvy nemůže předefinovat ještě jinak, nezávisle na použitém formátu. \subsec [hooky] Vložené skupiny příkazů (hooks) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Některá složitější makra ("\begtt", palcové uvozovky, "\ifirst", "\inext", "\doindex", "\dotoc") dovolují vkládat uživateli na začátku zpracování různé příkazy (tzv.~hooks). Implicitně jsou tyto vložky prázdné: \ifirst {docby.tex}{begtthook}{\empty}{+-} Makro \db begtthook je vloženo po založení skupiny a nastavení všech kategorií těsně před začátkem zpracování prostředí "\begtt...\endtt". Makro \db quotehook je vloženo po založení skupiny a nastavení všech kategorií těsně před začátkem zpracování prostředí {\tt\char`\"...\char`\"}. Makro \db indexhook je vloženo makrem "\doindex" po založení sekce a před přechodem do sazby ve dvou sloupcích. V tomto dokumentu je v něm úvodní povídání k rejstříku. Makro \db tochook je vloženo makrem "\dotoc" po založení sekce před sazbou prvního řádku obsahu. Makro \db bookmarkshook je vloženo uvnitř skupiny na začátku zpracování záložek. Je možné v něm nastavit expanze maker vyskytujících se v nadpisech na rozumnou hodnotu pro záložky. Pokud navíc nastavíte "\let\cnvbookmark=\lowercase", budou všechny znaky pro záložky procházet filtrem "\lowercase". Uvnitř "\bookmarkshook" je pak možné nastavit "\lccode" vybraným znakům (například pro odstranění háčků a čárek). Makro \db outputhook je vloženo na začátek výstupní rutiny. Je vhodné v něm nastavit vybrané příkazy na hodnotu "\relax", aby se neexpandovaly do souboru {\tt.ref}. Příklady použití \bgroup \catcode`'13 \def'{\nb} \catcode`?=13 \def?{<} \def\begtthook{\langleactive\mubytein=1} \begtt \def\quotehook{\obeyspaces} % ve výpisech "..." budou normální mezery \def\quotehook{\langleactive} % ?text> se promění na \def\begtthook{\mubytein=1} % mezi 'begtt...'endtt bude aktivní encTeX \def\begtthook{\setsmallprinting} % ukázky 'begtt...'endtt budou malé \def\begtthook{\catcode`\!=0} % mezi 'begtt...'endtt fungují !prikazy \def\indexhook{To čubrníte, jaký tu mám rejstřík.} \def\outputhook{\let\mylogo=\relax} % \mylogo nebude expandovat \endtt \par \egroup \subsec [module] Příkaz {\tt\nb module} a {\tt\nb ins} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Uživatelská dokumentace k těmto příkazům je v sekci~\cite[cleneni]. Příkaz \db module " " načte soubor s názvem "\docsuffix", kde makro \db docsuffix obsahuje příponu souboru včetně tečky. \ilabel [linkincomment] {extension} \insdef docsuffix Příkaz "\module" vloží název čteného souboru (bez přípony) do pomocného makra \db modulename. Toto makro pak využívá příkaz \db ins " ". \inssdef ins \subsec Zelenající komentáře %%%%%%%%%%%%%%%%%%%%%%%%%%%% Příkazy "\ifirst" a "\inext" si také všímají (implicitně) C komentářů tvaru "//.." a "/* .. */". Tyto komentáře barví ve výpisu programu zeleně. Zrušit tuto vlastnost lze příkazem "\noactive". Pomocí \db setlinecomment "{}" lze nastavit nový typ komentářů, které budou barveny zeleně od "" do konce řádku. Příkazy mají globální platnost. Například {\def\begtthook{\mubytein=1} \begtt \noactive{/*}\noactive{*/}\noactive{//} \setlinecomment{\percent} \noactive{\nb\percent}} \endtt \par} \noindent nastaví komentáře podle zvyklostí v \TeX{}u a PostScriptu. Příkazem \db setlrcomment "{}{}" lze nastavit komentáře typu "/*...*/". Pro změnu vlastností obarvování komentářů stačí uvedená makra použít. Kdo chce vědět, jak jsou implementovaná, nechť čte dále. \inext {mubyte}{\empty}{+-} Uvedené příkazy jsou prázdné v módu bez enc\TeX{}u a při detekci enc\TeX{}u zapíší informace do enc\TeX{}ové tabulky prostřednictvím primitivů "\mubyte...\endmubyte". Příkazy \db linecomment a \db leftcomment se díky enc\TeX{}u automaticky vloží před detekovanou sekvenci znaků. Tyto příkazy nastaví barvu textu na zelenou: \inext {linecomment}{\empty}{+-} Na druhé straně příkaz \db rightcomment potřebuje vypnout zelenou barvu až po přeskočení detekované sekvence. Proto enc\TeX{} v tomto případě detekovanou sekvenci zruší a příkaz "\rightcomment" má za úkol ji vrátit do sazby zpět a teprve poté pomocí \db returntoBlack se vrátit k~černé barvě. \inext {returntoBlack}{\empty}{+-} Je potřeba vysvětlit, proč přepínače barev jsou tak komplikovaně zapsány. Přepínač totiž v PDF zapíná barvu nezávisle na skupině a barva textu se drží tak dlouho, dokud není použit jiný přepínač barvy. Každý tisk řádku kódu je uveden přepínačem "\Black", takže při poznámce \uv{do konce řádku} stačí jen přepnout na "\Green". Ovšem uvnitř komentáře se může objevit link obalený příkazy "\Blue...\Black" (viz např. řádek~\cite[linkincomment] v předchozí sekci). Pak ale chceme, aby "\Black" vrátil barvu "\Green". Proto je provedeno předefinování pomocí "\let". Toto předefinování je lokální. Protože řádek je tištěn uvnitř skupiny, je další řádek už černý. Při tisku komentáře, který má úvodní a koncový znak a může přesáhnout jeden řádek, musíme globálně předefinovat "\Black" na "\Green", aby i další řádky (uvozené příkazem "\Black") byly zelené. Koncový znak komentáře pak musí uvést barvy do původního stavu. \docbytex{} inicializuje poznámky podle pravidel jazyka C: \inext {setlinecomment}{\empty}{+-} \sec [design] Pro designéry %%%%%%%%%%%%%%%%%%%%%%%%%%% Následuje dokumentace definic maker ovlivňující vzhled dokumentu. Jejich předefinování může způsobit změnu vzhledu podle požadavku uživatele. Místo komplikovaných maker s množstvím parametrů pro řízení vzhledu jsou zde jednoduchá dobře dokumentovaná makra pro jedno použití. Předpokládá se, že při potřebě jiného vzhledu dokumentu je uživatel předefinuje. Makra zabývající se vzhledem dokumentu jsou pokud možno oddělena od složitosti ostatních maker, ve kterých probíhá hlavní zpracování \docbytex{}u. To umožňuje designérovi zaměřit se jen na programování vzhledu a neutopit se v různých cyklech a rekurzích interních maker \docbytex{}u. Typicky jsou makra pro vzhled ve dvou verzích: pro pdf\TeX{} a bez pdf\TeX{}u. To je důvod, proč ve výpisech se často vyskytuje test "\ifx\pdfoutput\undefined". \subsec Parametry a pomocná makra pro nastavení vzhledu %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Velikost \db hsize ani \db vsize neměníme. Buď si ji nastaví uživatel, nebo se převezme velikost z~plainu (vhodné pro papír letter) či csplainu (vhodné pro papír A4). Nastavujeme ale větší \db parindent, neboť chceme do proužku vymezeného "\parindent" dát podbarvené čtverečky u názvů sekcí. \ifirst {docby.tex}{parindent=}{\empty}{+-} Připravíme si \uv{zúženou šířku} \db nwidth využitou např. jako šířka záhlaví: \inext {nwidth}{\empty}{+-} Příkazem plainu "\raggedbottom" nastavíme pružnost stránky dole, a ne mezi jednotlivými řádky. Nastavením "\exhyphenpenalty=10000" zakážeme zlom za pomlčkou (v~tisku rozsahu stránek, např. 11--13, takový zlom působí rušivě). \inext {raggedbottom}{\empty}{+-} Zavedeme potřebné fonty \db bbf, \db bbbf, \db btt, \db ttsmall, \db rmsmall, \db itsmall a \db partfont. \inext {bbf=}{\empty}{+-} Makro \db setsmallprinting přepne do malého strojopisu, připraví \db ttstrut vhodné velikosti a pomocí "\offinterlineskip" připraví tisk řádků v režimu, kdy se o sebe opírají. Hodnota "\parskip" je nastavena na "-1pt", aby docházelo k mírnému překrývání a nevznikaly v tisku nebo na obrazovce pruhy. Analogicky pracuje makro \db setnormalprinting. \inext {setsmallprinting}{\empty}{+-} V návrhu vzhledu pracuji jen s barvami \db Blue, \db Red, \db Brown, \db Green, \db Yellow a \db Black. Pokud budete chtít další barvy, definujte si je.\par\penalty1234 \inext {pdfoutput}{\empty}{+-} Barvy jsou definovány pomocí makra \db setcmykcolor, které je v případě DVI výstupu nastaveno na prázdné makro a v případě PDF výstupu je použit PDF~"\special". Takže příkazy "\Brown" atd. je možné použít i ve verzi maker pro DVI, ovšem v této verzi neudělají nic. Barva \db oriBlack je konstantně černá barva. Některá makra totiž normální "\Black" předefinovávají a pak se potřebují vrátit pomocí "\oriBlack" ke skutečné černé barvě. Makro \db rectangle "{}{}{<šířka>}{}" vytvoří rámeček o stanovených rozměrech se stanoveným obsahem. V PDF verzi je rámeček ve tvaru plného žlutého obdélníku na kterém se nachází "" zatímco v~DVI verzi se vytvoří obrysový rámeček. Pozor: parametr "" musí obsahovat přepínač barvy, jinak nebude v PDF verzi viditelný. Na druhé straně makro "\rectangle" se postará o návrat do \uv{normální} černé barvy. \inext {pdfoutput}{\empty}{+-} Nakonec připravíme makro \db docbytex jako zkratku pro logo \docbytex{}u. \inext {def\nb docbytex}{\empty}{+-} \subsec Vzhled sekcí a podsekcí %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Makra \db printsec "{}" a \db printsecbelow, jsou volána z makra pro vytvoření sekce "\sec" a mají za úkol vytisknout nadpis. Ostatní problematika, kterou musí řešit makro "\sec" (reference do obsahu, cílové reference, čísla sekcí, plovoucí záhlaví atd.) je zde odstíněna a nemusíme se jí v tuto chvíli zabývat. Musíme ale dodržet následující úmluvu: Na začátku makra "\printsec" přejdeme pro jistotu do vertikálního módu, pak vložíme potřebné mezery, pak vložíme text nadpisu. V okamžiku, kdy přejdeme do horizontálního módu, vložíme makro "\makelinks", které zajistí umístění cílů odkazů. Nakonec přejdeme do vertikálního módu příkazem "\par" a {\it nevkládáme žádné další vertikální mezery}. Makro "\sec" vloží pod vytištěný nadpis do horizontálního seznamu další prvky a posléze zavolá "\printsecbelow". Tam teprve vložíme mezery obvykle blokované proti zlomu pomocí "\nobreak". Základní řazení vertikálního seznamu v \TeX{}u totiž vypadá takto: box, (whatsit, mark, atd.), penalty, glue. O objekty uvedené v~závorce se postará "\sec", my zde řešíme jen box (v makru "\printsec"), a dále penaltu a glue (v makru "\printsecbelow"). K dispozici máme hodnotu "\secnum" a "\subsecnum" a dále můžeme použít test "\ifsavetoc", kterým se ptáme, zda daný nadpis bude v obsahu. Nebude-li, měli bychom místo "\the\secnum" tisknout "\emptynumber". V makru "\seclabel" je obsah lejblíku sekce, nebo je makro prázdné. To můžeme využít při tisku v režimu \uv{nahrubo}, například tisknout tyto lejblíky do okrajů. \docbytex{} tuto vlastnost implicitně neimplementuje. \ifirst {docby.tex}{def\nb printsec }{\empty}{+-} Makra \db printsubsec a \db printsubsecbelow fungují analogicky jako právě zmíněná, ale spolupracují s makrem "\subsec". \inext {def\nb printsubsec }{\empty}{+-} Makro \db printpart vytiskne nadpis části a dopředu dá veliké písmeno. Makro \db printpartbelow tiskne mezeru pod nadpisem části. \inext {def\nb printpart }{\empty}{+-} Makro \db emptynumber, které se použije při "\savetocfalse", je implicitně nastaveno na prázdnou hodnotu. \inext {emptynumber}{\empty}{+-} \subsec Titul, autor %%%%%%%%%%%%%%%%%%%% Makro \db title "\par" čte parametr "" pomocí makra "\secparam", které se postará o~případné ignorování mezery na konci parametru (viz~sekci~\cite[secsec]). Makro "\secparam" uloží parametr "" do tokenlistu "\sectitle" a spustí interní \db iititle. Toto makro pracuje ve dvou módech (DVI a PDF). V~obou módech "\iititle" uloží "" do makra "\headtitle" (pokud je toto makro prázdné, tedy neinicializované uživatelem) a pomocí příkazu "\noheadline" potlačí na aktuální stránce tisk záhlaví. \inext {def\nb title}{\empty}{+-} Makro "\title" v DVI verzi je prosté "\centerline", zatímco v PDF verzi tiskne podkladový obdélník šířky "\nwidth". Pokud není makro \db projectversion definováno, nastavíme mu výchozí hodnotu jako prázdné makro: \inext {ifx\nb project}{\empty}{+-} Makro \db author "\par" je společné v obou módech. Umístí jméno autora tučně a na střed. \inssdef author \subsec Hlavičky a patičky %%%%%%%%%%%%%%%%%%%%%%%%%% \docbytex{} nemění výstupní rutinu plainu. Využívá tedy klasické nástroje na modifikaci vzhledu, tj. text "\footline" a "\headline". Návrh vzhledu stránky nepočítá s pravou a levou stranou, protože dokumentaci většinou čteme na monitoru a když ji tiskneme, tak kdo ví, na čem... Text \db footline je nastaven tak, aby byla stránková číslice uprostřed podbarvena případně orámována pomocí "\rectangle". \inext {footline}{\empty}{+-} Text \db headline se mění. Implicitně obsahuje jen makro \db normalhead, ale při použití příkazu "\noheadline" na chvíli změní svůj obsah. \ilabel[headlinebox] {headlinebox} \inext {headline}{\empty}{+-} Makro "\normalhead" uloží stránkový link pomocí "\savepglink" a "\vbox/\hbox" gymnastikou vytvoří potřebné záhlaví. Zleva je tištěn název sekce ("\firstmark") a zprava konstantní text "\headtitle". Makro \db noheadline nastaví "\headline" přechodně na text, podle kterého se vloží jen stránkový odkaz a provede změna obsahu "\headline" na standardní hodnotu. Operace musíme provádět globálně, protože jsme uvnitř výstupní rutiny. \inssdef noheadline Makro \db headtitle obsahuje text shodný v celém dokumentu tištěný vpravo v záhlaví. Implicitně je makro prázdné, po použití příkazu "\title" obsahuje název dokumentu, pokud si uživatel makro nedefinoval sám. \inext {headtitle}{\empty}{+-} Pomocné makro \db headlinebox udělá v DVI módu prázdný čtvereček a v PDF módu plný (žlutý) čtvereček. Je použito na řádku~\cite[headlinebox] pro vytvoření čtverečkované čáry v záhlaví, \inext {ifx\nb pdfoutput}{\empty}{+-} \subsec Tisk cíle odkazu a odkazů pod čarou %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Cíl odkazu vytvořený makry "\dg" nebo "\dl" je potřeba vytisknout výrazně, aby jej čtenář pokud možno rychle našel. Tisk probíhá v makru \db printdg "{}{}{}", kde "" je text před slovem a "" je prázdný parametr nebo obsahuje "()", pokud tyto závorky uživatel v příkaze "\dg", "\dl" použil. Současný návrh \docbytex{}u tiskne z těchto tří parametrů jen jeden, sice "". V DVI módu tiskne "" v rámečku a v PDF módu tiskne "" červeně a na pozadí je žlutý obdélník. \inext {ifx\nb pdfoutput}{\empty}{+-} Červený text se tiskne pomocným makrem \db printdginside, které tiskne jednoduše červeně, pokud ke slovu neexistuje "\api" cíl a tiskne červeně pomocí "\ilink", jestliže existuje "\api" cíl. \inext {def\nb printdginside}{\empty}{+-} Údaj pod čáru tiskneme makrem \db printfnote "{}{}{}{}", kde parametry "" a "" mají stejný význam, jako u makra "\printdg". Parametr "" (krátká verze slova) tiskneme červeně, ostatní parametry černě. Parametr "" (dlouhá verze slova) není použit. K naprogramování tohoto makra využiji makro "\specfootnote"~"{}", které pošle text do speciální poznámky pod čarou. Dále je potřeba vědět, že "\pgref[+]" vrátí číslo strany, kde je "\api" deklarace "" nebo vrátí $-1000$. Toto číslo vložíme do "\apinum" a je-li nezáporné, tak jej uvedeme jako první v seznamu stránek a podtržené. Seznam stránek vytiskneme pomocí "\listofpages{}". V seznamu bude chybět stránka "\apinum", protože makro "\listofpages" ji vynechává. Prázdný seznam stránek (při kterém netiskneme dvojtečku ani čárku) poznáme podle toho, že "\box0" má nulovou šířku. \inext {def\nb printfnote}{\empty}{+-} \subsec Tisk údaje v obsahu a v rejstříku %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Příkaz \db ptocline "{<číslo>}{}{}" se postará o tisk údaje o sekci nebo části do obsahu. Dále příkaz \db ptocsubline "{<číslo>}{}{}" vytiskne údaj o subsekci. Jak je patrné, tyto dva příkazy se liší jen o jeden "\indent": \inext {def\nb ptocline}{\empty}{+-} Příkaz \db mydotfill vytiskne tečky do obsahu tak, aby byly pod sebou zarovnány. Příkaz \db ptocentry "{}{}" vytiskne jednu položku o dokumentovaném slově do obsahu. Parametr "=+", pokud je v daném místě "\api" dokumentace, a "=@", je-li v daném místě "\dg" dokumentace. "" je prázdné, ale při použití "\dl" je v něm krátká verze slova, zatímco ve "" je dlouhá verze slova. Dlouhou verzí odkazujeme, krátkou verzi tiskneme. \inext {def\nb ptocentry}{\empty}{+-} Kdyby bylo potřeba tisknout text před slovem nebo závorky za slovem, je možné využít kontrolní sekvenci "\csname-\endcsname" jako v následujícím makru "\printindexentry". Makro \db myldots vytvoří tři tečky, které jsou zarovnány s ostatními tečkami v obsahu. Makro \db printindexentry "{}" tiskne údaj o slově do rejstříku. Začíná ve vertikálním módu uvnitř sloupce, vytiskne údaj a pomocí "\par" se musí vrátit do vertikálního módu. \inext {def\nb printindexentry}{\empty}{+-} Pomocí \db separeright uložím do "\tmpa" text vlevo od slova a do "\tmpb" text vpravo od slova. Makro "\refdg" tyto údaje uložilo do makra "\csname-\endcsname" oddělené od sebe značkou "\right". Pomocí makra "\pgref[@]" získám stránku s "\dg" deklarací slova. Pomocí "\pgref[+]" získám stránku s "\api" deklarací slova. Tuto stránku (pokud existuje) tisknu podtrženě. \subsec Tisk zdrojového textu %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Makra "\ifirst" a "\inext" přetisknou požadovanou část zdrojového textu. Při řešení návrhu vzhledu tisku nás nyní pouze zajímá, že tato makra založí skupinu, pak zavolají příkaz \db printiabove, pak pro tisk každého řádku zavolají \db printiline "{<číslo>}{}" a nakonec před ukončením skupiny se spustí \db printibelow. Právě tato tři makra si nyní naprogramujeme. Budeme rozlišovat mezi DVI a PDF módem. \ilabel [isnameprinted] {raise8} \ilabel [isnameprinted2] {pt \nb inputfilename} \inext {ifx\nb pdfoutput}{\empty}{+-} V DVI módu tiskneme nahoře čáru se jménem souboru pomocí "\leaders" a makra \db specrule. Dole pak tiskneme jen jednoduchou čáru. V~PDF módu nahoře pouze nastavíme "\setsmallprinting" a vložíme malou mezeru. Dole vložíme střední mezeru. Makro "\printiline" přejde nejprve do horizontálního módu, tam vloží v DVI módu podpěru a dále box s číslem a box s řádkem. Mezi řádky vkládám penaltu~11. V PDF módu se místo podpěry tiskne celý žlutý proužek v~"\rlap". Protože přes první řádek je potřeba vpravo nahoru vytisknout jméno souboru (později než žlutý proužek), je potřeba zjistit, zda tisknu první řádek nebo další řádky. K tomu slouží kontrolní sekvence \db isnameprinted, která je typicky "\undefined". Po vytištění jména souboru (řádky~\cite[isnameprinted] a~\cite[isnameprinted2]) nastavím "\isnameprinted" na "\relax" a tím poznám, že už je práce provedena. Až makro "\ifirst" nebo "\inext" ukončí skupinu, bude zase mít "\isnameprinted" hodnotu "\undefined". \subsec [begtt] Tisk z prostředí {\tt\nb begtt}/{\tt\nb endtt} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Makro "\begtt" založí skupinu a zavolá \db printvabove. Dále pro každý tištěný řádek volá makro \db printvline "{<číslo>}{}" a nakonec zavolá \db printvbelow. Číslo řádku jsme se rozhodli nevyužít. V DVI verzi kreslíme jen čáry nahoře a dole. V~PDF verzi kreslíme žluté čáry nahoře a dole a v~každém řádku pomocí "\rlap" kreslíme žluté obdélníky vpravo a vlevo. \inext {ifx\nb pdfoutput}{\empty}{+-} \subsec Vkládání obrázků %%%%%%%%%%%%%%%%%%%%%%%% Obrázky jsou vkládány nalevo podle odstavcové zarážky. Tato zarážka je dostatečně velká, takže to působí docela dobře. Celkovou šířku prostoru pro obrázek \db figwidth spočítám jako "\hsize" mínus "\parindent" \inext {newdimen\nb figwidth}{\empty}{+-} Makro \db ifig " " v DVI módu vloží ".eps" a využije k tomu makrobalík {\tt epsf.tex}. V PDF módu vloží ".pdf" a využije k tomu pdf\TeX{}ové primitivy "\pdfximage", "\pdfrefximage", "\pdflastximage". \inext {ifx\nb pdfoutput}{\empty}{+-} Makro \db figdir obsahuje adresář, ze kterého se obrázky loví. \subsec Výčty %%%%%%%%%%%%% Makra pro výčty jsou natolik jednoduchá, že asi nepotřebují dalšího konimentáře. \db begitems zahájí prostředí s výčty, \db enditems ukončí toto prostředí, \db itemno čísluje a \db dbtitem " " zahajuje položku, přičemž se uvnitř prostředí převtělí na \db item. \inext {newcount\nb itemno}{def\nb enditems}{++} \sec [implementace] Pro otrlé %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Zde je dokumentována implementace \docbytex{}u. Je zde výpis všech jeho interních maker včetně podrobného komentáře, jak fungují. Asi není rozumné tato makra měnit, ledaže by si chtěl čtenář naprogramovat \docbytex{} vlastní. \subsec Pomocná makra %%%%%%%%%%%%%%%%%%%%% Makro \db dbtwarning zprostředkuje tisk varovných hlášek: \inext {def\nb dbtwarning}{\empty}{+-} Makra \db defsec "{}", \db edefsec "{}" a \db undef "{}" jsou zkratky za časté operace s~"\csname\endcsname". \inext {def\nb defsec}{\empty}{+-} Makro "\undef" je potřeba použít takto: {\def\begtthook{\langleactive\mubytein=1} \begtt \undef{}\iftrue \else \fi \endtt \par} Nutnost použití "\iftrue" se bohatě vyplatí, až budeme "\undef" přeskakovat vnějšími podmínkami typu~"\if". Definuji makro \db nb (normální backslash). Toto makro je pak možné používat při vyhledávání textu s tímto znakem. Rovněž definuji aktivní tabelátor a zástupné sekvence \db obrace, \db cbrace, \db percent a \db inchquote. {\catcode`\%=12 \noactive{\nb %} \noactive{[%} } \inext {catcode}{\empty}{+-} Makro \db softinput je vysvětleno v \TeX{}booku naruby na straně 288, takže bez komentáře. \inext {def\nb softinput}{\empty}{+-} Makro \db setverb nastaví kategorie všech speciálních znaků na normální. Viz \TeX{}book naruby, stranu~28. \inext {def\nb setverb}{\empty}{+-} \subsec Inicializace %%%%%%%%%%%%%%%%%%%% Ohlásíme se na terminál: \inext {This is DocBy}{\empty}{+-} Makro \db dbtversion obsahuje verzi \docbytex{}u a je definováno na začátku souboru {\tt docby.tex}. Tam je autor \docbytex{}u pozmění, pokud přejde na novou verzi. \inssdef dbtversion Je-li použit "csplain", je aktivován UTF-8 vstup pomocí enc\TeX{}u. To ale bohužel není kompatibilní s použitím enc\TeX{}u \docbytex{}em. Je tedy potřeba deaktivovat UTF-8 vstup a české texty napsat například v ISO-8859-2. \inext {utf8off}{\empty}{+-} %Inicializujeme csplain mód: % %\inext {ifx\nb chyph\nb undefined \nb else}{\empty}{+-} Inicializujeme enc\TeX{}ový mód: \inext {encTeX ??}{\empty}{+-} Makro \db enctextable "{}{}" vloží do enc\TeX{}ové tabulky vzor "". Jakmile takový vzor enc\TeX{} objeví, zruší jej ze vstupního proudu a promění jej v kontrolní sekvenci "\.", která expanduje na "". Například makro "\dg"~"" aktivuje pro enc\TeX{} "", takže provede (mimo jiné) "\enctextable{}{\sword{}}", což způsobí, že se "" v načítaném zdrojovém kódu promění na "\sword{}". Makro "\enctextable" odmítá uložit do enc\TeX{}ové tabulky slova, která jsou v seznamu \uv{zakázaných} slov \db owordbuffer. Tam jsou slova (oddělená z obou stran čárkou), která se nesmějí aktivovat kvůli "\onlyactive". Pro taková slova provede "\enctextable" jen definici sekvence "\.". Makro \db noactive "{}" vloží do enc\TeX{}ové tabulky vyhledávaný text, který ve vstupu zůstane a před něj bude vložena sekvence \db emptysec. Protože enc\TeX{} neumí ze své tabulky zrušit údaj (umí jen přepsat informaci, na co se má vyhledávaný text proměnit), je potřeba texty, které už v encTeXové tabulce nepotřebujeme, deaktivovat alespoň pomocí "\noactive". Na \db sword "{}" se díky enc\TeX{}u proměňují texty, které se mají automaticky stát klikatelnými linky. \inext {def\nb sword}{\empty}{+-} Makro \db onlyactive "{}{}{}" zakáže vkládat "" do enc\TeX{}ové tabulky (vloží je do "\owordbuffer", ovšem jen za předpokladu, že už tam není), a nechá celý text "" proměnit v~"\oword{#1}{#2}{#3}". Dále pomocí "\noactive" dekativuje "" (při čtení "\reffile" totiž pravděpodobně bylo aktivováno). Makro \db oword "{}{}{}" tiskne normálně "", dále, pokud je definováno "\.", tak je spustí, jinak tiskne normálně "". Konečně tiskne vždy normálně text "". \inext {def\nb onlyactive}{\empty}{+-} Nakonec inicializujeme DVI/PDF mód: \inext {ifx\nb pdfoutput}{\empty}{+-} \subsec Makra {\tt\nb ifirst}, {\tt\nb inext}, {\tt\nb ilabel} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Deklarujeme \db lineno jako číslo řádku, \db ttlineno jako číslo řádku pro "\begtt...\endtt" výpisy, \db ifcontinue pro řízení cyklu a \db infile je deskriptor souboru otevřeného ke čtení. \db ifskipping implementuje uživatelské \db skippingfalse a \db skippingtrue. \inext {newcount\nb lineno}{\empty}{+-} %Makra "\ifirst", "\inext" jsou pro uživatele popsána %v~sekci~\cite[vkladani] a makro "\ilabel" má své povídání %v~sekci~\cite[lineodkazy]. Příkaz \db ifirst "{}{}{}{}" nejprve pomocí "\readiparamwhy" analyzuje parametr "", pak otevře soubor ke čtení primitivem "\openin". Je-li otevření neúspěšné, vypíše varování, jinak si uloží název souboru do makra \db inputfilename a analyzuje parametry pomocí "\scaniparam": "" je uloženo do "\tmpa" a "" do "\tmpb". Do "\tmpA" a "\tmbB" se uloží počet opakování (z konstruktoru "\count="). Nakonec se spustí makro "\insinternal" s expandovanými parametry "", "". K tomu je použit známý trik s makrem "\act". \insdef ifirst Příkaz \db inext "{}{}{}" pracuje analogicky, jako "\ifirst", pouze neotevírá soubor, ale pomocí testu na definovanost makra "\inputfilename" kontroluje, zda náhodou nebyl spuštěn příkaz "\inext" bez předchozího "\ifirst". \insdef inext V rámci expanze parametrů chceme, aby zmizely všechny kontrolní sekvence, které nám do textu vložil automaticky enc\TeX{}. To provede makro \db noswords. \inssdef noswords Makro \db readiparamwhy načte znaky "+" nebo "-" z parametru "" a uloží je do sekvencí \db startline a \db stopline. \inssdef readiparamwhy Makro \db scaniparam "^^X" čte "" ve tvaru "\count= ". Do sekvence "" uloží "" a do sekvence "" uloží "". Protože konstruktor "\count=" je nepovinný, dá trochu více práce parametr analyzovat. K tomu slouží i pomocná makra \db scaniparamA, \db scaniparamB, \db scaniparamC. V případě nepřítomnosti "\count=" je v~"" jednička. \inssdef scaniparam Hlavní práci při vkládání zdrojového textu do dokumentace dělá makro \db insinternal s parametry "{}{}". \ilabel [prvniloop] {preskakovani} \ilabel [konec:prvniloop] {ifcontinue \nb repeat} \ilabel [druhyloop] {pretisk} \ilabel [konec:druhyloop] {readnewline \nb repeat} \ilabel [insinternal:end] {printibelow} \insdef insinternal Makro "\insinternal" se skládá ze dvou hlavních cyklů. První (na řádcích~\cite[prvniloop] až~\cite[konec:prvniloop]) čte postupně řádky ze vstupního souboru (makrem "\readnewline") a uloží je do makra "\etext". V tomto cyklu hledá výskyt textu "" a nic netiskne. Druhý cyklus na řádku~\cite[druhyloop] až~\cite[konec:druhyloop] čte postupně řádky ze vstupního souboru a hledá výskyt textu "". V této chvíli tiskne pomocí makra "\printilineA". Před prvním cyklem jsou provedeny přípravné práce: nastavení kategorií, fontů, "\mubytein". Dále je v přípravné fázi definováno makro \db testline se separátorem "", pomocí něhož budeme testovat přítomnost textu "". Variantní definice makra "\testline" následují pro speciální případ parametru "" (viz uživatelská dokumentace v~sekci~\cite[vkladani]). Ukončení cyklu je řízeno podmínkou "\ifcontinue". Příkaz \db nocontinue provede "\continuefalse", ovšem ne vždy. Pokud je zadáno "\count>1", tj. "\tempnum>1", pak příkaz pouze zaznamená výskyt hledaného textu a sníží "\tempnum" o jedničku. \inssdef nocontinue Před druhým cyklem v makru "\insinternal" jsou provedeny podobné přípravné práce jako před prvním, znovu je definováno makro "\testline", tentokrát se separátorem "". Vyhledávání probíhá podobně, jako když jsme hledali "". Pomocí "\ifx+\startline" testujeme, zda tisknout výchozí řádek. Pomocí "\ifx+\stopline" testujeme, zda tisknout ukončovací řádek. Makro "\ilabellist" obsahuje testování přítomnosti lejblíků deklarovaných příkazem "\ilabel". Trikoidní je makro \db returninsinternal "{}{}{}", které se spustí při dosažení konce čteného souboru. Marko opustí svůj cyklus pomocí parametru "", který je separován textem "\printibelow", takže to přeskočí větší část obsahu makra "\insinternal" až po řádek~\cite[insinternal:end]. Abychom správně opustili vnořené podmínky, jsou přečtena v druhém parametru případná "\fi" a v makru použita. První parametr obsahuje varovací hlášku, chceme-li vypsat varování. Chceme-li být zticha, je parametr prázdný. \insdef returninsinternal Makro \db readnewline je naproti tomu jednoduché: \inssdef readnewline Pracujeme s řádkem čteného souboru ve dvou verzích: neexpandovaným \db text a expandovaným \db etext při "\noswords". Tím máme zaručeno, že v "\etext" nejsou kontrolní sekvence vytvořené enc\TeX{}em (pro test přítomnosti "" nebo "" by tam ty sekvence překážely). Verze s enc\TeX{}ovými sekvencemi "\text" se použije při tisku. Makro \db printilineA musí mít svou inteligenci: nesmí bezhlavě tisknout prázdné řádky, ale ty tiskne až se zpožděním, následuje-li tisk neprázdného řádku. Tím je zaručeno, že se při "\skippingtrue" nevytiskne poslední prázdný řádek. Makro \db lastline má tři stavy: "\empty" (na začátku), "\relax" (po vytištění řádku), "<číslo řádku>" (je-li předchozí řádek prázdný). \insdef printilineA Pro uložení deklarací pomocí \db ilabel "[]{}" slouží makro \db ilabellist, které musíme nastavit nejprve na prázdnou hodnotu. \insdef ilabellist Makro "\ilabel" nejprve expanduje své parametry (pomocí "\act") a zavolá interní \db ilabelee. Toto makro přidá do "\ilabellist" toto: \def\begtthook{\langleactive\mubytein=1} \begtt \expandafter\testilabel\etext\end{}{} \endtt Makro \db testilabel "<řádek>\end{}{}" si definuje pomocné makro "\tmp" se separátorem "", aby zjistilo, zda je "" uvnitř "<řádek>". Pokud se to povede, registruje cíl odkazu pomocí "\labeltext". \inext {testilabel}{\empty}{+-} \def\begtthook{} \subsec Příkazy {\tt\nb begtt}, {\tt\nb endtt} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Makro \db begtt a "\endtt" je podrobně popsáno v \TeX{}booku naruby na stranách~27 až~30. Makru \db startverb dodáme kompletní verbatim text separovaný "\endtt". Tento text je dělený znakem "^^M" (kategorie 12) na řádky a koncový řádek obsahuje token "\end". Makro spustí ve spolupráci s makrem \db runttloop cyklus a řádky rozebere, každý řádek zvlášť předá makru "\printvline". Na konci cyklu se provede makro \db endttloop. To udělá závěrečné činnosti (zavolá "\printvbelow", ukončí skupinu) a pomocí makra \db scannexttoken otestuje první následující token. Pokud to není "\par", není pod "\endtt" prázdný řádek, takže se provede "\noindent". \ifirst {docby.tex}{def\nb begtt }{\empty}{+-} V numerickém registru "\ttlineno" je číslo řádku průběžně zvětšované v celém dokumentu. Pokud by někdo chtěl toto číslo využít, může jej nulovat například na začátku každé sekce. \subsec [ns] Jmenné prostory %%%%%%%%%%%%%%%%%%%%%%%%%%%% Každý jmenný prostor si udržuje své \db namespacemacro, což je makro s jedním parametrem, které příkazem "\namespace{}" mimoděk definuje uživatel. Na počátku je "\namespacemacro" prázdné: \ifirst {docby.tex}{def\nb namespacemacro}{\empty}{+-} Ke každému jmennému prostoru budeme chtít přiřadit lejblík. Rozhodl jsem se za lejblík považovat výsledek expanze "\namespacemacro{@!}". Budu jej nadále značit "". Existuje sice určité riziko nejednoznačnosti "", ale předpokládám, že v praxi nenastane. Každý jmenný prostor už na počátku musí vědět, jaká všechna lokální slova obsahuje, aby jejich výskyt mohl směřovat na místo, kde je deklarace "\dl", která může být třeba později než výskyt. Jmenný prostor na svém startu musí tedy do enc\TeX{}ových tabulek uložit všechna lokální slova a na svém konci vrátit vše pokud možno do původního stavu. Je tedy zřejmé, že není vhodné čekat až na příkaz "\dl", ale že je třeba využít soubor "\reffile". V prvním průchodu tedy jmenné prostory nemohou být aktivní. Po přečtení "\reffile" má každý jmenný prostor k dispozici makro "\ns:", které obsahuje seznam všech svých lokálně deklarovaných slov ve formátu {\def\begtthook{\mubytein=1\langleactive} \begtt \locword{}\locword{}\locword{}... \endtt \par} Protože ukládání do enc\TeX{}ové tabulky je globální, definujeme v rámci duševní hygieny všechna makra s tím spojená globálně. Proto je prostředí "\namespace...\endnamespace" nezávislé na skupinách \TeX{}u. Při startu \db namespace je třeba definovat "\namespacemacro". Původní hodnotu "\namespacemacro" uložíme do "\no:", abychom se k němu mohli na konci prostředí "\namespace...\endnamespace" vrátit. Dále definujeme makro \db locword tak, aby uložilo potřebné údaje do enc\TeX{}ové tabulky a před tím ještě si uložilo stávající významy předefinovávaných kontrolních sekvencí. Pak se prostě spustí "\ns:". \inext {def\nb namespace }{^^B\cbrace}{++} Na konci \db endnamespace znovu definujeme makro "\locword" tentokrát tak, aby vrátilo pozměněným sekvencím původní význam. Pokud původní význam byl \uv{nedefinovaná sekvence}, je potřeba do enc\TeX{}ové tabulky vložit aspoň "\nword", protože zcela odstranit údaj z tabulky nelze. Dále se vrátíme k~původní hodnotě "\namespacemacro", kterou máme uloženu v "\no:". \inext {def\nb endnamespace}{^^B\cbrace}{++} Uvedená makra pracují s užitečnou zkratkou \db ewrite, která zapíše text do "\reffile" se zpožděním (primitivem "\write"), ale expanzi udělá hned. Přitom neexpanduje "\nb". \inext {def\nb ewrite}{\empty}{+-} Enc\TeX{} od startu jmenného prostoru vkládá tedy místo každého lokálního "" kontrolní sekvenci "\.", která expanduje na "\lword{}". Jakmile se tedy objeví výskyt lokálního slova, pracuje \db lword takto: \inext {def\nb lword}{\empty}{+-} Makro \db genlongword "{}" vytvoří z krátké verze slova dlouhou verzi slova a uloží ji do "". Výskyt "" dává o sobě vědět v parametru "\ilink" i při zápisu do souboru svým dlouhým (jednoznačným) jménem, zatímco krátké jméno se tiskne. Zbývá zařídit čtení ze souboru "\reffile". Makro \db refns "{}" se objeví v souboru v místě začátku jmenného prostoru a \db refnsend "{}" na konci jmenného prostoru. Mezi nimi se vyskytují "\refdg{}{}{}{}", přičemž si nyní všímáme jen takových výskytů, které mají neprázdné "". Právě tyto výskyty zanesl do "\reffile" příkaz "\dl". \inext {def\nb refns}{\empty}{+-} Makro "\refns" si zapamatuje předchozí "", který je uložen v makru \db currns, do sekvence "\o:" a definuje pak "\currns" jako "". Připraví také výchozí stav makra "\ns:" na prázdnou hodnotu. Makro "\refdg" pak postupně plní buffer "\ns:" (viz řádky~\cite[ns1] až~\cite[ns2] v~definici makra "\refdg" v~sekci~\cite[reference]). Konečně makro "\refnsend" vrátí "\currns" do stavu, v jakém bylo před vstupem do stávajícího jmenného prostoru. \subsec {\tt\nb dg} a přátelé %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Makra \db dg, \db dl, \db dgn, \db dgh, \db dln, \db dlh uloží do "\tmpA" svůj název, spustí sken parametrů pomocí \db dgpar a nakonec se promění ve svou interní verzi pomocí "\csname ii\tmpA\endcsname". \inext {def\nb dg}{\count=2 \empty}{+-} Předchozí makra připraví čtení nepovinného parametru. Hlavní práci provede makro \db dparam. \ilabel [managebrackets] {managebrackets} \ilabel [ii] {csname ii} \inext {def\nb dparam}{\empty}{+-} Je-li za ukončovací závorkou "]" mezera, pak je parametr "#2" prázdný (je separovaný mezerou). V~této situaci se makro "\dparam" protočí ještě jednou prostřednictvím makra \db nextdparam, které sežere obsah zbytku makra "\dparam", vloží mezeru dovnitř závorky a spustí "\dparam" ještě jednou. Nyní už je možné začít parametr "#2", tj. "" rozdělit na část před první čárkou, tečkou, středníkem nebo dvojtečkou a za za tímto znakem. Část před bude v "\tmpa" a část za (včetně separátoru) bude v "\tmpb". Tuto práci vykoná postupné volání makra \db varparam: \inext {def\nb varparam}{\empty}{+-} Makro "\varparam" definuje pomocné makro "\tmp#1#2 ", kterému je předloženo " ". Je-li "#2" prázdné, pak zabral až "" na konci, takže uvnitř "" není "". Pak v "\tmpa" zůstává "". Je-li uvnitř "" separátor, pak je potřeba doplnit k "\tmpb" zbytek za separátorem včetně tohoto separátoru. V "#2" máme "" a my potřebujeme do "\tmpb" uložit stávající obsah "\tmpb" před kterým předchází "". Tuto práci udělá \db gobblelast, kterému je předložen "\end". Makro definuje "\tmp#1" a předloží mu "". Je tedy v "#1" holý "" a do "\tmpb" se dostává "". Po rozdělení vyhledání separátoru máme n "\tmpa" , ovšem může obsahovat na konci "()". Proto spustíme na řádku~\cite[managebrackets] makro \db managebrackets, které se postará o případné oddělení těchto závorek. Pokud se závorky skutečně oddělily od "\tmpa", zůstávají v~\db printbrackets. \inext {def\nb managebrackets}{\empty}{+-} Makro \db maybespace v závěru činnosti makra "\dparam" vytiskne za obsahem "\tmpb" mezeru, ale jen tehdy, když je jméno makra dvoupísmenkové ("\dg", "\dl") a nenásleduje znak "`". \inext {def\nb maybespace}{\empty}{+-} Na řádku~\cite[ii] vytvoří makro "\dparam" z původního příkazu "\dg*" resp. "\dl*" jeho interní verzi "\iidg*" resp. "\iidl*". Parametry předá expandovány, aby s nimi bylo méně práce. Stačí tedy naprogramovat uvedená interní makra. Makro \db iidg vloží do enc\TeX{}ové tabulky "\sword" (je to mírně nadbytečné, totéž se provede na začátku zpracování při čtení "\reffile" příkazem "\refdg"). Dále makro vytvoří cíl odkazu tvaru "@", uloží informaci do "\reffile" ve formátu "\refdg{}{}{}{}", vytiskne "" zvýrazněné pomocí "\printdg" a vloží poznámku pod čáru pomocí "\printfnote". \inext {def\nb iidg }{^^B\cbrace}{++} Makro \db iidl nevkládá nic do enc\TeX{}ové tabulky, vytvoří cíl pomocí "\label [@]", zapíše info do "\reffile" ve formátu "\refdg{}{}{}{}", vytiskne "" zvýrazněné pomocí "\printdg" a vloží poznámku pomocí "\printfnote{}{}{}". \inext {def\nb iidl }{^^B\cbrace}{++} Makra \db iidgh a \db iidlh dělají to samé jako jejich non-"h" protějšky, jen netisknou slovo v místě výskytu. Lokálně tedy předefinujeme, aby "\printdg" nedělalo nic. \inext {def\nb iidgh}{\empty}{+-} Makro \db iidgn předefinuje makro "\.", které vyrábí enc\TeX, tak, že výsledkem expanze je "\fword{}{}{}" (namísto obvyklého "\sword{}"). \inext {def\nb iidgn}{\empty}{+-} Až se \db fword spustí (při prvním následujícím výskytu ""), má za úkol provést "\iidgh", vytisknout "" červeně a vrátit "\." do původního stavu. \inext {def\nb fword}{\empty}{+-} Makro \db iidln si uloží stávající význam "\." do sekvence "\;" a předefinuje makro "\.", které vyrábí enc\TeX, tak, že výsledkem je "\flword{}{}{}". \inext {def\nb iidln}{\empty}{+-} Makro \db flword má za úkol provést "\iidlh", vytisknout "" červeně a vrátit význam makra "\." do původního stavu (který je uložen v sekvenci "\;". Byl-li tento původní význam nedefinován, je potřeba potlačit další činnost makra "\." registrováním jako "\nword{}", protože z enc\TeX{}ové tabulky už záznam nelze odebrat. \inext {def\nb flword}{\empty}{+-} \subsec [specfootnote] Speciální poznámky pod čarou %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Poznámky pod čarou jsou řazeny vedle sebe a obsahují jen slova, která mají na stránce své "\dg". Protože toto řešení je vizuálně nekompatibilní s uživatelskými poznámkami pod čarou, jednoduše je zakážeme: \inext {let\nb footnote=}{\empty}{+-} Pro speciální poznámky pod čarou využiji už deklarovaný insert "\footins". Problém je, jak odhadnout, kolik zabere vertikálního místa v poznámkách jedno slovo, když jich může být vedle sebe více. Dirty trick z \TeX{}booku (vkládat inserty ve výšce rovné jistému procentu své šířky) se neujal, neboť zlom často nekonvergoval, ale osciloval. V druhém průchodu poznámky teprve dostávají své seznamy stránek a tyto seznamy se pak mohou dále upřesňovat, což zpětně ovlivní vertikální sazbu. Po její změně se mění seznamy stránek a tak pořád dokola. Rozhodl jsem se tedy pracovat pouze s průměrným koeficientem poznámek, který budou mít všechny poznámky společný. Tento koeficient získám jako celkový počet řádků poznámek v celém dokumentu dělený počtem poznámek. Každá poznámka pak \uv{překáží} v hlavním vertikálním seznamu výškou řádku poznámek (10pt) násobenou tímto koeficientem. Stačí tedy nastavit "\count\footins". Aby problém určitě konvergoval, bylo nutné fixovat výše uvedený koeficient po druhém průchodu. Kdybych jej každý následující průchod měnil, zase se nedočkáme konvergence. Získat uvedený koeficient hned po prvním průchodu není rozumné, protože v té době poznámky ještě nemají vedle sebe seznamy stránek. Výchozí koeficient pro první a druhý průchod je tedy nastaven na "\count\footins=200" (předpokládám zhruba pět poznámek na řádku). Pracovat s průměrem místo s každou jednotlivou poznámkou může samozřejmě způsobit, že některé stránky jsou plnější a některé prázdnější. Proto je potřeba mít rezervu ve "\skip\footins" a vertikálně pružit kolem poznámkové čáry. \inext {skip\nb footins}{\empty}{+-} V registru \db totalfoocount se bude postupně přičítat jednička za každou poznámku a na konci zpracování tam tedy je celkový počet poznámek. V registru \db totalfoodim bude na konci zpracování celková výška všech řádků s poznámkami. \inext {newcount\nb totalfoocount}{\empty}{+-} Makro \db specfootnote "{}" vloží do insertu "\footins" jediný "\hbox{}" a připočte jedničku do "\totalfoocount". \inext {def\nb specfootnote}{\empty}{+-} Protože jsem se rozhodl neměnit výstupní rutinu plainu, musel jsem se \uv{nabourat} aspoň do její části na tisk poznámek pod čarou. Je to provedeno předefinováním makra "\footnoterule" výstupní rutiny plainu. Separátor "\unvbox\footins" způsobí odstranění stejného textu z output rutiny plainu. \inext {def\nb footnoterule}{\empty}{+-} Makro rozebere vertikální seznam insertů "\footins" a poskládá je vedle sebe do boxu~4. Pak nastaví parametry sazby na praporek a vypustí box~4 do horizontálního seznamu ("\noindent") ukončeném "\endgraf". Tím jsou ve výstupní rutině poznámky pod čarou vysázeny. Nakonec připočteme "\totalfoodim". V závěru zpracování v makru "\bye" (viz řádek~\cite[write]) zapíšeme do souboru "\reffile" informaci o~počtu poznámek "", o celkové výšce řádků poznámek v dokumentu "" a přidáme aktuální koeficient příspěvku poznámek do vertikálního seznamu "". Informaci zapisujeme jen tehdy, když je "\indexbuffer" neprázdný, tj. když probíhá aspoň druhý průchod. Kdybychom zapisovali i první průchod, dostali bychom velmi zkreslené informace (poznámky v~tu chvíli nemají vedle sebe seznamy stránek). % Uvedenou informaci zapsanou v předchozím průchodu přečteme na začátku zpracování makrem \db refcoef "{}{}{}" a nastavíme podle toho společný koeficient všech poznámek "\count\footins". Makro změní koeficient z výchozí hodnoty 200 na vypočtenou jen jednou. Při dalších průchodech už zůstává u vypočtené hodnoty. Pomocné makro \db gobblerest odstraní cifry za desetinou tečkou včetně nápisu~"pt". \inext {def\nb refcoef}{\empty}{+-} Výstupní rutina "\plainoutput" není změněna. Potřebuji ale uvnitř "\output" potlačit expanzi některých maker, které se objeví v argumentu "\write". Tato makra jsou tedy uvnitř "\output" nastavena na "\relax". Aby toto nastavení nezměnilo sazbu záhlaví, je potřeba "\makeheadline" provést před změnou maker a uložit si výsledek do boxu. \inext {\nb output=}{\empty}{+-} \subsec [secsec] Sekce, podsekce %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Nejprve zapíšeme deklarace \db secnum, \db subsecnum, \db sectitle, \db ifsavetoc. Poslední deklarace připraví uživatelské \db savetocfalse. \ifirst {docby.tex}{newcount\nb secnum}{\empty}{+-} Makra \db sec a \db subsec mají možnost nepovinného parametru "[]", za ním může a nemusí být mezera, kterou musíme ignorovat. Na konci parametru "" před "\par" rovněž může a nemusí být mezera, kterou musíme ignorovat. Dá tedy práci parametry správně načíst. Makra si uloží svůj název do "\tmpA" a spustí proces načítání parametrů pomocí "\secparam". \inext {def\nb sec}{\empty}{+-} Makro \db secparam se vypořádá s případným nepovinným parametrem "[]". Pokud je přítomen, uloží "" do pomocného makra \db seclabel, jinak tam je prázdno. Makro \db secparamA se vypořádává s případnou mezerou za hranatou závorkou "]" a odstraní ji. Makro \db secparamB "\par" načte "", ale ten může mít nežádoucí mezeru zcela na konci. S tím se vypořádá makro \db nolastspace ve spolupráci s makrem \db setparamC. Posledně jmenované makro uloží už od nežádoucí mezery ošetřený "" do "\sectitle" a spustí "\iisec" resp. "\iisubsec". \inext {def\nb secparam}{\empty}{+-} Makro \db iisec nejprve nastaví hodnoty "\secnum" a "\subsecnum", dále definuje \db makelinks, kde je připravena tvorba odkazů (to použije makro "\printsec"). Dále zavolá "\printsec" na vytištění názvu sekce. Poté uloží informace do "\reffile" ve tvaru "\reftocline"~"{}{}{}" Nakonec se provede "\mark{ }" a vloží se závěrečná mezera pomocí "\printsecbelow". \inext {def\nb iisec}{^^B\cbrace}{++} Makro \db iisubsec, které vytváří podsekci, pracuje analogicky, jako makro "\iisec". \inext {def\nb iisubsec}{^^B\cbrace}{++} Makro "\part" bylo zapracováno dodatečně ve verzi Jan. 2009. Registr \db partnum uchovává číslo části a makro \db thepart toto číslo konvertuje na písmeno. \inext {newcount\nb partnum}{\empty}{+-} Makro \db part má svou implementaci v makru \db iipart podobně jako například makro "\sec". \inext {def\nb part}{\empty}{+-} \subsec [reference] Odkazy, reference %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Klikací odkazy řeší makra \db savelink "[]" a a \db ilink "[]{}". Makro "\savelink" uloží do sazby cíl odkazu. Cíl odkazu vystrčí do výšky \db linkskip nad účaří. Makro "\ilink" (čti interní link) je dokumentováno v~sekci~\cite[krizodkaz]. Konečně makro "\savepglink" uloží cíl numerického typu (číslo stránky), který bude využit makrem "\pglink" při odkazech na stránky. \ifirst {docby.tex}{Odkazy, ref}{\empty}{--} Uvedená makra jsem definoval zvlášť pro DVI výstup (jako prázdná makra) a zvlášť pro PDF výstup. Až zase tvůrci pdf\TeX{}u změní syntaxi nebo názvy primitivů, bude stačit pozměnit uvedená makra. V makru "\ilink" je přímo řečeno, že se má použít modrá barva pro vytvoření odkazů a že odkaz má být bez rámečku. Pokud to někomu nevyhovuje, může si makro předefinovat. Trik s předefinováním "\nb" (normální backslash) při tvorbě PDF linků vychází ze zkušenosti, že pokud se v názvu linku objeví backslash, některé PDF prohlížeče si s tím neporadí a chovají se podivně. Je tedy nutné, aby argument příkazů "\savelink" a "\ilink" byl neexpandovaný. Makro \db savepglink (definice je v předchozím výpisu) je použito v "\headline" každé stránky, takže vytvoří cíl \uv{nahoře} na každé stránce. Makro \db pglink "" přečte "" (může být ve tvaru numerického registru i přímo jako číslo) a vytvoří link na stránku s tímto číslem. Číslo samotné je vytištěno modře a dá se na ně kliknout. Ke čtení numerického registru je použit primitiv "\afterassignment" a pomocné makro \db dopglink. V souboru "\jobname.ref" se prostřednictvím makra "\labeltext[]{}" uloží řádek, který obsahuje "\reflabel"~"{}{}{}". Makrem \db reflabel tyto údaje přečtu a zapíšu do kontrolních sekvencí "^^X" a "^^Y". Tyto kontrolní sekvence jsou následně využity v~makrech \db numref a \db pgref. Za povšimnutí stojí, že pokud je "" prázdný (to jsou například všechny případy dokumentovaných slov), pak kontrolní sekvenci "^^X" vůbec nedefinuji, abych šetřil pamětí, kterou má \TeX{} rezervovánu na kontrolní sekvence. \inext {reflabel}{\empty}{+-} Makro \db labeltext "[]{}", jak bylo před chvílí řečeno, uloží do souboru potřebné údaje. Jednak zapíše PDF link pomocí makra "\savelink" a dále uloží do souboru "\reffile" potřebné údaje. K tomu je makro \db writelabel "[]{}", které pracuje se zpožděným "\write" (aby číslo strany bylo správně). V okamžiku načtení parametru "" jej potřebuji expandovat, protože tam obvykle bývá něco jako "\the\secnum". Pro vyřešení tohoto problému jsem na chvíli prohodil parametry ("" totiž nechci expandovat) a zavedl pomocné makro \db writelabelinternal "{}{}". První část, tj. "\writelabel{}" expanduji pomocí "\edef". \inext {labeltext}{\empty}{+-} Makro \db label je už definováno jednoduše jako \uv{prázdný} "\labeltext". \inext {label}{\empty}{+-} Makro \db cite "[]" vytiskne klikatelný text. Při chybném "" vytiskne varování na terminál. Makro je dokumentováno v~sekci~\cite[krizodkaz]. \inext {cite}{\empty}{+-} S odkazy souvisí makro \db api "{}", které vloží "\label[+]" dá o sobě vědět ještě jednou do "\reffile". \inext {def\nb api}{\empty}{+-} Makro \db apitext obsahuje text tištěný vedle "" do obsahu a rejstříku. Při činnosti makra \db bye zapíšeme do souboru "\reffile" údaje pro "\refcoef" (řádek~\cite[write]) a dále se zabýváme testem konzistence referencí. \ilabel [texttoc] {text\nb tocbuffer} \ilabel [vypust] {indexbuffer} \ilabel [write] {write\nb reffile} \inext {def\nb bye}{^^B\cbrace}{++} Test konzistence vypadá následovně: nejprve uzavřeme zápis do souboru "\reffile", pak pomocí "\setrefchecking" předefinujeme kontrolní sekvence vyskytující se v "\reffile" a soubor znovu načteme. Nyní makra v něm napsaná dělají test a pokud narazí na problém, provedou "\continuefalse". Můžeme tedy pomocí "\ifcontinue" zjistit, jak test dopadl. Po přečtení souboru je potřeba udělat ještě důkladnou kontrolu všech automatických odkazů. Proč je tato kontrola vyřešena vypuštěním "\indexbuffer" do vstupní fronty bude jasné po prostudování makra \db setrefchecking. \ilabel[jerelax] {=\nb relax} \inext {def\nb setrefchecking}{\empty}{+-} Zde předefinujeme makro "\refcoef", aby nedělalo nic. Dále nová verze "\reflabel" kontroluje, zda odkaz je na stejné stránce, jako byl a má stejný text. Nové makro "\refuseword" pracuje jako jeho originální protějšek, jen místo sekvencí "w:" plní sekvence "-". Tyto sekvence už známe, nyní je využijeme jinak. Šetříme pamětí \TeX{}u, proto nezakládáme sekvence nové. Nejprve je nutné těmto sekvencím nastavit výchozí hodnotu "\relax", což je provedeno na řádku~\cite[jerelax]. Pak znovu předefinuji sekvenci "\,", aby provedla test shodnosti sekvence "w:" se sekvencí "-" a v makru "\bye" na řádku~\cite[vypust] spustím tento test expandováním makra "\indexbuffer\relax". Když makro najde nekonzistenci, ohlásí chybu a uteče pomocí \db ignoretorelax. Dále je předefinováno makro "\refdg", aby pouze zapisovalo do "\tocbuffer". Ostatní makra z "\reffile" také zapisují do "\tocbuffer". Stávající verzi "\tocbuffer" uložíme do "\text" a "\tocbuffer" se při načtení "\reffile" vytvoří znovu. Na řádku~\cite[texttoc], zda se nezměnil obsah. \subsec Tvorba obsahu, rejstříku a záložek %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Obsah i rejstřík se mohou pomocí "\dotoc" a "\doindex" objevit kdekoli v dokumentu (třeba na začátku, na konci, uprostřed...). Musíme být připraveni je kdykoli vytisknout. Soubor "\reffile" z minulého běhu můžeme otevřít ke čtení jen na začátku, pak jej mažeme a začínáme znova zapisovat. Při čtení ze souboru "\reffile" tedy musíme uložit všechny potřebné informace k sazbě obsahu i rejstříku. Používáme na to makro "\tocbuffer" a "\indexbuffer". Na začátku tyto \uv{buffery} vyprázdníme. Makro \db addtext "\to" budeme používat na vkládání "" do "", čímž buffery postupně naplníme. \inext {def\nb tocbuffer}{\empty}{+-} V souboru \db reffile se vyskytují tyto příkazy: {\def\begtthook{\langleactive\catcode`\!=0\mubytein=1} \begtt \reftocline{<číslo>}{}{} % údaje o sekci a subsekci pro obsah \refdg{}{}{}{} % údaj o použití \dg, \dl \refapiword{} % údaj o výskytu \api{} \refuseword{}{} % údaj o přímém výskytu \reflabel{}{}{} % viz sekci !cite[reference], odkazy, reference \refcoef{}{}{} % viz sekci !cite[specfootnote], spec. poznámky \refns{} % viz sekci !cite[ns], jmenné prostory \refnsend{} % viz sekci !cite[ns], jmenné prostory \endtt \par} Při čtení souboru "\reffile" ukládáme potřebné údaje do bufferů. Nejprve se zaměříme na {\bf obsah} a definujeme \db reftocline "{<číslo>}{}{}". \inext {def\nb reftocline}{\empty}{+-} V \db tocbuffer tedy máme postupně údaje o všech sekcích a podsekcích v za sebou jdoucích sekvencích \db dotocline "{<číslo>}{}{}". Mezi sekcí a subsekcí rozlišíme jen podle toho, zda parametr "<číslo>" obsahuje tečku. K tomu slouží pomocné makro \db istocsec. \inext {def\nb dotocline}{\empty}{+-} Kdybychom spustili makro "\tocbuffer", dostaneme obsah. Ale ten se neskládá jen z údajů o~sekcích a podsekcích. Ještě je potřeba přečíst \db refdg a \db refapiword, abychom mohli vkládat do obsahu i údaje o~dokumentovaných slovech. \ilabel [right] {right} \ilabel [ns1] {\nb dl} \ilabel [ns2] {locword} \inext {def\nb refdg}{\empty}{+-} Makro "\refdg" pracuje s parametry "{}{}{}{}", kde "" je text před slovem, "" je dlouhé slovo, "" obsahuje případné závorky "()". Je-li dlouhé slovo rozdílné od krátkého slova (při použití "\dl"), obsahuje "" krátké slovo, jinak je tento parametr prázdný. Makro "\refdg" ukládá informace nejen do "\tocbuffer", ale také do "\indexbuffer". Rovněž při prázdném "" makro ukládá "\sword" do enc\TeX{}ové tabulky a při neprázdném "" makro cosi kutí se jmennými prostory. Nyní je ale naše pozornost věnována tvorbě obsahu. Ten vytvoří makro \db dotoc. \inext {def\nb dotoc}{\empty}{+-} {\bf Rejstřík} je vybudován z bufferu \db indexbuffer, ve kterém je seznam deklarovaných slov v dokumentu. Každé slovo je v bufferu zapsáno jako kontrolní sekvence (to zabere v paměti \TeX{}u nejmíň místa) a je odděleno od další sekvence oddělovačem. Před zatříděním podle abecedy jsou položky v~"\indexbuffer" odděleny čárkami za položkami, po zatřídění jsou položky odděleny "\," před položkami. Takže obsah "\indexbuffer" vypadá zhruba takto: {\def\begtthook{\langleactive} \begtt před zatříděním: \- , \- , \- , \- , ... po zatřídění: \, \- \, \- \, \- \, \- ... \endtt \par} \noindent Zde zápis "\-" znamená jednu kontrolní sekvenci. Každá taková kontrolní sekvence je makrem tvaru "\right". To zařídí řádek~\cite[right]. Rejstřík vytiskneme makrem \db doindex. \ilabel [calculatedimone] {calculatedimone} \inext {def\nb doindex }{^^B\cbrace}{++} Příkaz "\calculatedimone" s následujícím testem "\dimen1" souvisí se sazbou do dvou sloupců, což necháme na sekci~\cite[dvasloupce]. Makro tedy založí příkazem "\sec" sekci nazvanou "\titindex" a pokud je "\indexbuffer" neprázdný, spustí sazbu rejstříku. Nejprve se příkazem "\sortindex" setřídí "\indexbuffer" podle abecedy (viz sekci~\cite[abeceda]). Pak makro "\doindex" založí dvousloupcovou sazbu ("\begmulti 2") a oddělovači "\," přidělí význam "\doindexentry". Nakonec vypustí "\indexbuffer" do vstupní fronty, takže další práci opakovaně provede makro \db doindexentry "\-", které se postará o tisk slova v~rejstříku. \inext {def\nb doindexentry}{\empty}{+-} Makro "\doindexentry" pomocí \db ignoretwo odstraní z kontrolní sekvence "\-" úvodní dva znaky "\-", takže v "\tmp" zůstane "". Pokud "" začíná backslashem, uděláme z něj makrem \db remakebackslash sekvenci "\nb", neboť přímý backslash není uložen v PDF odkazech (zlobí některé PDF prohlížeče, viz sekci~\cite[reference]. Nakonec se vytiskne položka v rejstříku už známým makrem "\printindexentry". Při tvorbě {\bf strukturovaných záložek} je potřeba vědět, kolik má každý uzel potomků. Tento údaj je počítaný při čtení "\reffile" voláním makra \db addbookmark "" (viz makra "\reftocline" a "\refdg"). Parametr "" může být číslo sekce, nebo dvojčíslí ".". V makru \db currb je "", ke kterému je potřeba přičítat potomka a \db currsecb je případný nadřazený "" sekce. Makro "\addbookmark" připočte jedničku k hodnotě makra "\bk:". \inext {def\nb addbookmark}{\empty}{+-} Makro \db bookmarks založí skupinu, předefinuje "\dotocline" a "\ptocentry" (tj.~makra obsažená v~"\tocbuffer") vloží první záložku s názvem dokumentu a spustí "\tocbuffer". \inext {def\nb bookmarks}{^^B\cbrace}{++} Makro \db setoutline "[]{}{}" vytvoří záložku "" a prolinkuje ji s cílem označeným "". V "\tempnum" musí být uložen počet potomků záložky. \inext {def\nb setoutline}{\empty}{+-} V tomto makru je použito konverzní makro \db cnvbookmark, které je implicitně neaktivní. Uživatel může například nastavit "\let\cnvbookmark=\lowercase" a nechat konvertovat pomocí "\lccode" znak "č" na "c", znak "ž" na "z", atd. Nastavení "\lccode" musí mít v "\bookmarkshook". Dále je text před vložením do záložky podroben konverzi \db nobraces, která ve spolupráci s makrem \db nobrA sundá případné závorky "{}". Takže, pokud máme třeba "{\tt text} v \TeX{}u", po konverzi dostáváme "text v TeXu". \subsec [abeceda] Abecední řazení rejstříku %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Tuto práci provede makro "\sortindex". Původně bylo v \docbytex{}u implementováno algoritmem bubblesort, což vyšlo na šest řádků makrokódu (prezentováno na tutoriálu \TeX{}perience~2008), ale pro větší rejstříky to bylo pomalé. Např. pro rejstřík tohoto dokumentu to vygenerovalo 52 tisíc dotazů na porovnání a trvalo to asi dvě vteřiny. Můj syn Mirek byl pozorný posluchač tutoriálu, takže nabyté znalosti okamžitě využil a přepsal třídicí makro na mergesort. Ten na stejně velkém rejstříku generuje 1600 dotazů na porovnání, tedy třicetkrát méně. Cena za to je skutečnost, že makro už nemá jen šest řádků, ale je mírně komplikovanější. Od možnosti použít quicksort jsme upustili, protože implementace by vyžadovala vyšší paměťové nároky na inputstack \TeX{}u. Nejprve deklarujeme podmínku pro výsledek srovnání dvou položek \db ifAleB a vytvoříme pomocná makra \db nullbuf, \db return a \db fif. Pomocné makro "\return" ve spolupráci se zakrytým "\fi" uvnitř "\fif" budeme používat pro únik z košatých hluboce vnořených podmínek typu "\if..\else..\fi". Jak uvidíte, makro pracuje na úrovni expandprocesoru a nebude potřeba psát žádné "\expandafter". \inext {newif\nb ifAleB}{\empty}{+-} Makro \db sortindex vypustí do vstupní fronty celý "\indexbuffer", přimaluje k němu "\end,\end", pronuluje "\indexbuffer" a spustí "\mergesort". \inext {def\nb sortindex}{^^B\cbrace}{++} 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é ("\indexbuffer" 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 "\indexbuffer". V dalším průchodu znovu vyvrhne "\indexbuffer" 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 "\indexbuffer" a ukončit činnost. Pomocí "\gobblerest" 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 indexbuffer} \ilabel [napercarky] {napercarky} \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} Vlastní srovnání dvou položek dělá makro \db isAleB "". Položky jsou tvaru kontrolní sekvence "\-" a "\-". Makro konvertuje své parametry pomocí "\string" na řadu znaků a expanduje na "\testAleB"~"\relax\relax". Navíc je na tento test aplikováno "\lowercase", neboť nerozlišujeme při řazení mezi velkými a malými písmeny. \ilabel [teckanula] {0\nb relax} %\ilabel [teckajedna] {2.1} \inext {def\nb isAleB }{^^B\cbrace}{++} Makro \db testAleB "\relax\relax" zjistí, zda je "" menší než "". Makro volá samo sebe, pokud jsou první porovnávané znaky stejné. Rekurze určitě skončí, neboť na řádku~\cite[teckanula] jsou k porovnávaným slovům připojeny různé ocasy. \inext {def\nb testAleB}{^^B\cbrace}{++} Makro \db napercarky vloží mezi položky do "\indexbuffer" separátory "\,". To se provede uvnitř "\edef\indexbuffer" na řádku~\cite[napercarky]. \inext {def\nb napercarky}{\empty}{+-} \subsec Transformace seznamu stránek %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Každý výskyt "" uloží do "\reffile" údaj \db refuseword "{}{}", který přečteme na začátku zpracování v dalším průchodu: \inext {def\nb refuseword}{^^B\cbrace}{++} V sekvenci "\w:" tedy máme seznam stránek s výskyty "", stránky jsou odděleny čárkami. Seznam může vypadat třeba takto: \begtt 2,5,5,10,11,12,12,13,13,13,27 \endtt Cílem je takovýto seznam stránek vytisknout ve formátu "2, 5, 10--13, 27", tj. odstranit duplicity a nahradit souvisle jdoucí řadu stránek zápisem ve tvaru "--". Tuto práci dělá makro \db listofpages "{}", které předhodí makru "\transf" expandovaný seznam stránek ukončený ",0,". \inext {def\nb listofpages}{^^B\cbrace}{++} Makro "\transf" vyloučí ze seznamu stránek ty, které jsou rovny \db dgnum a \db apinum. Nechceme totiž, aby se v seznamu opakovala hlavní stránka "" a podtržená stránka. Tyto stránky jsou vytištěny už dříve. Deklarujeme uvedené registry: \inext {newcount\nb apinum}{\empty}{+-} Kromě toho jsme deklarovali pomocné \db tempnum (aktuálně zpracovávaná stránka), \db ifdash (zda zpracováváme souvislou skupinu stránek a vytiskli jsme "--") a \db iffirst (zda vkládáme první číslo, tj. není nutné vložit čárku). Makro \db transf ",0," spustí cyklus pomocí \db cykltransf. \inext {def\nb transf}{def\nb carka}{++} Makro "\cykltransf" je takový malý stavový automat. Věřím, že mu čtenář porozumí bez dalšího komentáře. \subsec [dvasloupce] Více sloupců %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Sazba do více sloupců je kompletně převzata z \TeX{}booku naruby, strany 244--246. \ilabel [dimen1] {dimen1<2} \inext {newdimen\nb colsep}{TBN}{++} Zde navíc řešíme problém, že na začátku přepnutí do dvou sloupců pomocí \db begmulti "2" si makro na řádku~\cite[dimen1] zkontroluje, zda není blízko dna stránky a v takovém případě zahájí dva sloupce až na nové stránce. Ovšem vypadá hloupě, pokud se kvůli tomu ulomí nadpis \uv{Rejstřík} od jeho obsahu. Je tedy potřeba provést podobné měření stránky už před tiskem nadpisu \uv{Rejstřík}. K tomu slouží makro \db calculatedimone spuštěné na řádku~\cite[calculatedimone] v makru "\doindex". \inext {def\nb calculatedimone}{\empty}{+-} \subsec Závěrečná nastavení, kategorie %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Kategorie je rozumné nastavit až na konci souboru {\tt docby.tex}. Na rozdíl od plainu přidáváme aktivní kategorii pro znak palce a nastavujeme podtržítko na obyčejnou kategorii, protože se ve zdrojových kódech programů často používá a vůbec ne ve významu matematického indexu. Kdyby mu tento význam zůstal, byly by jen potíže. Podtržítko je další problém. Skoro vždy chceme, aby se ve vnitřních makrech chovalo jako normální podtržítko, ale když tiskneme text s podtržítkem fontem, který na dané pozici podtržítko nemá (to je Knuthův odkaz), pak by se to mělo udělat plainovským makrem "\_", Toto makro tedy schováme do \db subori a pak ho probudíme k~životu jen v~okamžiku tisku v~makrech "\printsec", "\printsubsec", "\title" a "\normalhead". Uživatel tedy může napsat "\sec moje\_funkce" a v~makrech se bude "\_" jako obyčejné podtržítko, zatímco při tisku v nadpise se použije "\subori". \inext {catcode`\nb_}{\empty}{+-} Nastavením "\everymath" a "\everydisplay" zaručíme matematikům stále možnost používat podtržítko ve významu indexu. Aktivní palec spustí lokální verbatim prostředí uvnitř odstavce: \inext {catcode`\nb}{\empty}{+-} Makro \db langleactive nastaví aktivní kategorii pro znak je menší ({\tt<}), takže bude možné zapisovat {\tt} a vytiskne se "". \inext {def\nb langleactive}{\empty}{+-} V tomto dokumentu jsem "\langleactive" použil v makru "\quotehook", protože nechat znak {\tt<} aktivní všude nedělalo dobrotu. \doindex \bye