diff options
author | Karl Berry <karl@freefriends.org> | 2012-12-21 17:17:30 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2012-12-21 17:17:30 +0000 |
commit | 2257828845c52e3a8b6becfa850d1eafdf6f20e5 (patch) | |
tree | e2b67b1c091e49fe54a1a551823339974ca31410 /Master/texmf-dist/doc/cstex/opmac-d.tex | |
parent | 99eb8d14f7469f1d9a1c7edc39c573ccaa36dc44 (diff) |
new doc package cstex (20dec12)
git-svn-id: svn://tug.org/texlive/trunk@28595 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/doc/cstex/opmac-d.tex')
-rw-r--r-- | Master/texmf-dist/doc/cstex/opmac-d.tex | 2170 |
1 files changed, 2170 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/cstex/opmac-d.tex b/Master/texmf-dist/doc/cstex/opmac-d.tex new file mode 100644 index 00000000000..95d241e0f0a --- /dev/null +++ b/Master/texmf-dist/doc/cstex/opmac-d.tex @@ -0,0 +1,2170 @@ +%% OPmac -- Olsak's PlainTeX macros +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Tech-documentation, Petr Olsak, 2012 + +\input utf8off \clearmubyte + +% This file is encoded in ISO-8859-2 because +% UTF-8 encTeX has a conflict with DocByTeX + +\def\projectversion{Dec. 2012} +\def\headtitle{OPmac} + +\widowpenalty=10000 +\clubpenalty=10000 + +\emergencystretch=2em +\hbadness=2200 + +\showboxbreadth=1500 \showboxdepth=2 + +\def\csplain{\CS plain} +\def\CS{CS} + +\input docby.tex + +\setlinecomment{\percent} \noactive{\nb\percent} \noactive{\percent\cbrace} +\noactive{\nb fontdimen} + +\title OPmac -- rozšiřující makra plain\TeX{}u + +\author Petr Olšák + +\centerline{\ulink[http://www.olsak.net/opmac.html]% + {www.olsak.net/opmac.html}} + +\def\db{\dg\nb} +\def\du#1{\api{\nb#1}} +\let\quotehook=\langleactive +\def\insdef#1 {\ifirst{docby.tex}{def\nb#1 }{^^B\cbrace}{++}} +\def\inssdef#1 {\ifirst{docby.tex}{def\nb#1}{\empty}{+-}} +\bgroup + \catcode`\[=1 \catcode`]=2 \catcode`\{=12 \catcode`\}=12 + \gdef\obrace[{] \gdef\cbrace[}] +\egroup +\def\indexhook{% +% Kontrolní sekvence označené šipkou ($\succ$) jsou +% uživatelskými příkazy. Ostatní kontrolní sekvence jsou +% v~OPmac interní. + Tučně je označena strana, kde je slovo + dokumentováno, pak následuje seznam všech stran, na kterých se slovo + vyskytuje. +% Uživatelské příkazy mají v seznamu stránek podtržené číslo, +% což je stránka, kde je příkaz vyložen na uživatelské úrovni. + \medskip} +\def\nn#1 {\noactive{\nb#1}} \nn insert \nn undefined + +\def\cnvbookmark#1{\lowercase{\lowercase{#1}}} +\def\bookmarkshook{\lo ěe \lo šs \lo čc \lo řr \lo žz \lo ýy + \lo áa \lo íi \lo ée \lo úu \lo ůu \lo óo \lo ňn } +\def\lo #1#2{\lccode`#1=`#2} + + +\dotoc \bookmarks + +\sec Úvod +%%%%%%%%% + +OPmac je balík jednoduchých doplňujících maker k plain\TeX{}u umožňující +uživatelům základní La\TeX{}ovou funkcionalitu: změny velikosti písma, +automatickou tvorbu obsahu a rejstříku, práci s {\tt bib} databázemi, +referencemi, možnost proložení referencí hyperlinkovými odkazy atd. + + +\sec Uživatelská dokumentace +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Uživatelská dokumentace je zatím v souboru "opmac-u.tex" a "opmac-u.pdf". +Do tohoto místa ji zahrnu později a prolinkuji ji s technickou dokumentací. + + +\sec Technická dokumentace +%%%%%%%%%%%%%%%%%%%%%%%%%% + +Tato část dokumentace je určena pro tvůrce maker, kteří se chtějí zde +uvedenými makry inspirovat a případně je přizpůsobit svému požadavku. +Předpokládá se znalost \TeX{}u, tj. například aspoň zběžná orientace +v~\TeX{}booku naruby. Na tuto knihu je na mnoha místech odkazováno pod +zkratkou TBN. + + +\subsec Základní makra +%%%%%%%%%%%%%%%%%%%%%% + +Na začátku souboru "opmac.tex" zjistíme, zda není soubor čtený podruhé. V +takovém případě čtení odmítneme. Ptáme se na to, zda je definováno makro \db +OPmacversion, které vzápětí definujeme. Je-li někdo překvapen, proč jsem +nepoužil "\expandafter\endpinput\fi", může si prostudovat TBN, stranu~358, +heslo "\endinput". + +\ifirst {opmac.tex}{OPmacversion}{\empty}{+-} + +Dva pracovní registry: +\dgn \nb tmpnum +\dgn \nb tmpdim + +\inext {newcount}{\empty}{+-} + +OPmac nebude nikdy hlásit chyby. Často ale bude psát pomocí \db opwarning +na terminál varování. + +\inext {warning}{\empty}{+-} + +Makro \db addto "<makro>{<tokeny>}" přidá na konec "<makra>" dané "<tokeny>". + +\inext {addto}{\empty}{+-} + +V OPmac budeme pracovat se seznamem \db protectlist, který bude obsahovat +makra, jež chceme mít tzv. robustní, tj. chceme, aby se při "\write" v output +rutině neexpandovala. Každému makru v~seznamu předchází "\doprotect", takže +seznam "\protectlist" vypadá takto: + +\def\begtthook{\langleactive} +\begtt +\doprotect<makro1> \doprotect<makro2> ... +\endtt + +\def\begtthook{} +Seznam budeme spouštět v output rutině s tím, že "\doprotect" tam bude mít +význam makra, které zařídí, aby jeho parametr získal význam "\relax". +Tím bude zabráněno jeho expanzi. +Naprogramujeme \db addprotect "<makro>", které zařídí vložení "<makra>" do seznamu. + +\inext {protectlist}{\empty}{+-} + +Některá makra budou fungovat jen v pdf\TeX{}u při nastaveném "\pdfoutput=1". +Připravíme si tedy test \db ifpdftex, který pak použijeme při čtení souboru +"opmac.tex". Test nikdy nebudeme vkládat do maker, takže při čtení souboru +"opmac.tex" už musí být jasné, zda bude výstup směrován do DVI nebo PDF. +Pozdější změna "\pdfoutput" může způsobit potíže. + +\inext {ifpdftex}{\empty}{+-} + +Makra \db sdef a \db sxdef umožňují pohodlně definovat kontrolní sekvence +ohraničené pomocí "\csname...\endcsname". + +\inext {sdef}{\empty}{+-} + +Makro \db adef umožní nastavit znak na aktivní a rovnou ho definovat, což +normálně uvnitř maker není jednoduché (TBN str. 25 a 26). +Využijeme toho, že "~" je aktivní znak a pomocí +"\lccode" a "\lowercase" jej přepíšeme na požadovaný znak. Dostaneme tím +aktivní token s požadovanou ASCII hodnotou a tento token definujeme. +Pomocí \db lccodetiezero vrátíme po přiřazení (tj. po provedení definice) +"\lccode" vlnky na původní hodnotu. + +\inext {adef}{\empty}{+-} + +Makrem \db isdefined "{<jméno>}\iftrue" se ptáme, zda je definovaná +"\csname<jméno>\endcsname". To závěrečné připojené "\iftrue" makro sežere, +ale uživatel ho píše zejména z toho důvodu, aby mu tato konstrukce fungovala +uvnitř vnořených "\if..\fi" + +\inext {isdefined}{^^B\cbrace}{++} + +Makro \db isinlist "<list>{<tokeny>}\iftrue" zjistí, zda "<tokeny>" jsou +(jako string) obsaženy v~makru "<list>". Přitom sežere "\iftrue" ze stejných +důvodů, jak je uvedeno před chvílí. + +\inext {isinlist}{^^B\cbrace}{++} + +Makro \db isnextchar "<znak>{<co dělat při ano>}{<co dělat při ne>}" +pracuje poněkud odlišně od předchozích maker. Zjistí, zda následující znak +je "<znak>" a pokud ano, vykoná vnitřek první závorky, jinak vykoná vnitřek +druhé závorky. Pomocí "\futurelet" uloží zkoumaný znak do "\next" a spustí +\db isnextcharA. + +\inext {isnextchar}{\empty}{+-} + +Předefinujeme makro \db uv z \csplain{}u. Tam je toto makro navrženo tak, aby +mohlo mít za svůj parametr verbatim text. Důsledkem toho nefunguje správně +kerning. Považuji za lepší mít správně kerning a případné uvozování verbatim +textů řešit třeba pomocí "\clqq...\crqq". + +\inext {def\nb uv}{}{+-} + +Knuth v souboru "plain.tex" zanechal řídicí sekvenci "\\" v provizorním stavu +(cvičení: podívejte se v jakém). Domnívám se, že je lepší ji dát jednoznačný +význam "\undefined". Některým uživatelům totiž může OPmac připomínat +La\TeX{} a není tedy vyloučeno, že je napadne psát "\\". Měli by na to +dostat jednoznačnou odpověď: {\tt undefined control sequence}. + +\inext {undefined}{}{+-} + +Do pracovního souboru určeného k novému načtení budeme chtít vložit +komentáře za znakem procento. K tomu potřebujeme mít procento jako obyčejný +znak kategorie 12. Na tento znak se v našem kódu překlopí otazník, Takže +\db percent expanduje na znak procento s kategorií 12. + +\inext {percent}{}{+-} + +Makro plain\TeX{}u "\," +funguje jen v matematické sazbě. Uživatel bude chtít makro často +použít například mezi číslem a jednotkou v textovém módu: "5\,mm", takže +makro předefinujeme. + +\inext {kern}{}{+-} + + +\subsec Globální parametry +%%%%%%%%%%%%%%%%%%%%%%%%%% + +Zakážeme vdovy a sirotky a dále nastavíme registry pro listingy tiskového +materiálu na smysluplnější hodnoty, než jsou implicitní. + +\inext {widowpenalty}{\empty}{+-} + +Následující makra a registry ovlivní chování klíčových maker OPmac způsobem, +jak je popsáno v~komentářích. Mnohé z těchto maker a registrů byly zmíněny v +uživatelské dokumentaci. +\dgn \nb iindent +\dgn \nb ttindent +\dgn \nb ttskip +\dgn \nb ttpenalty +\dgn \nb tthook +\dgn \nb intthook +\dgn \nb iiskip +\dgn \nb bibskip +\dgn \nb tabstrut +\dgn \nb tabiteml +\dgn \nb tabitemr +\dgn \nb vvkern +\dgn \nb hhkern +\dgn \nb multiskip +\dgn \nb colsep +\dgn \nb mnoteindent +\dgn \nb mnotesize +\dgn \nb picdir +\dgn \nb bibtexhook +\dgn \nb chaphook +\dgn \nb sechook +\dgn \nb secchook +\dgn \nb cnvhook +\dgn \nb pghook + +\inext {newdimen}{\count=7 \empty}{+-} + + + +\subsec Loga +%%%%%%%%%%%% + +V logu \db OPmac je pomocí "\thefontscale" zvětšeno písmeno O. Logo \db CS je +přepsáno beze změny z CS\TeX{}u. Tím snadno vytvoříme i logo \db csplain. + +\inext {def\nb OPmac}{\empty}{+-} + +Troufám si tvrdit, že logo \db LaTeX (ačkoli je plain\TeX{}isté asi moc +nebudou potřebovat) je v~následujícím kódu daleko lépe řešeno, než v +samotném La\TeX{}u. Počítá totiž ve spolupráci s makrem \db slantcorr +i se sklonem písma při usazování zmenšeného A. + +\inext {LaTeX}{\empty}{+-} + +Loga se občas mohou vyskytnout v nadpisech. Zabezpečíme je tedy proti +rozboření při zápisu do REF souboru. + +\inext {addprotect}{\empty}{+-} + + +\subsec Velikosti fontů, řádkování +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\csplain{} od verze "<Nov. 2012>" definuje makro +\db resizefont "<fontselector>", které změní +velikost fontu daného svým přepínačem a tento změněný font si ponechá +stejný přepínač. Změna velikosti je dána obsahem makra \db sizespec. +Tam může být například napsáno "at13pt" nebo "scaled800". +Dále CSplain definuje makro +\db resizeall, které změní velikost +fontů s registrovanými přepínači. Registrování se provádí makrem +\db regfont. Implicitně jsou registrovány přepínače + "\tenrm", "\tenit", "\tenbf", "\tenbi", a "\tentt". +Do nových velikostí tedy půjdeme se starými názvy přepínačů +"\ten<něco>" a to slovo "ten" budeme chápat jen jako historický relikt, +který nám ovšem napoví, že kontrolní sekvence je fontovým přepínačem. + +OPmac si zjistí, zda je definovaný "\regfont". Pokud ne, upozorní na starou +verzi \csplain{}u na terminálu a potřebná makra si definuje. Je to kopie +kódu ze souboru "csfontsm.tex" z balíčku \csplain{}. + +\inext {regfont}{\empty}{+-} + +Implicitně jsou zavedeny \CS{}fonty, takže k nim přidáme AMS fonty z +"ams-math.tex", které vizuálně odpovídají. Později si může uživatel zavést +jiné makro (např. "tx-math.tex") a zavede si třeba i jiné textové fonty. To nezmění +vlastnosti maker v OPmac, pokud nové soubory maker správně předefinují +makra "\setmathsizes[<text>/<script>/<scriptscript>]", "\normalmath" a +"\boldmath". + +\inext {ams-math}{\empty}{+-} + +Po načtení souboru ams-math disponujeme makry \db regtfm na registraci +různých metrik pro různé designované velikosti fontů a \db whichtfm, které +expanduje na svůj parametr nebo na metriku, která je registrována pro +velikost \db dgsize. Registrace metrik \CS{}fontů je rovněž provedena +v souboru {\tt ams-math.tex}. Jak bylo řečeno, +makro "\resizefont<fontselector>" \csplain{}u změní +velikost fontu "<fontselector>" podle obsahu makra "\sizespec" (viz soubor +{\tt csfontsm.tex}). Toto makro "\resizefont" volá pomocné makro \db +resizefontskipat na odstranění \uv{sizespec} z názvu metriky. Toto pomocné +makro je v OPmac předefinováno s využitím "\whichtfm", takže když před +voláním makra "\resizefont" připravíme správnou "\dgsize", \TeX{} použije +metriku designovanou na požadovanou velikost: + +\inext {resizefontskip}{\empty}{+-} + +Makra "\typosize", "\fontsizex", "\textfontsize", +"\setbaselineskip" požadují zápis parametru bez jednotky. +Jednotkou je \db ptunit, která je nastavena na 1pt. Uživatel může +jednotku změnit (např. "\ptunit=1mm" při návrhu plakátu). Dále \db fontdim +je registr, který udává aktuální velikost písma. + +\inext {ptunit}{\empty}{+-} + +Často budeme potřebovat odstranit jednotku "pt" ve výpisu "\the<dimen>". +Provedeme to pomocí "\expandafter\ignorept\the<dimen>". Protože "\the" +vyrábí znaky pt s kategorií 12, je makro \db ignorept definováno trikem přes +"\lowercase". Z otazníku vznikne p kategorie 12 a z vykřičníku vznikne t. + +\inext {lccode}{\empty}{+-} + +Makra \db typosize a \db typoscale změní velikosti a nastavují +výchozí font "\tenrm" a výchozí +matematiku "\normalmath". Nehrajeme si na OFS nebo NFSS, které se snaží ctít +naposledy nastavený duktus a variantu. Uživatel si variantu písma a tučný +duktus pro matematiku musí nastavit až po zavolání makra na změnu velikosti fontu. + +\inext {typosize}{\empty}{+-} + +Makro \db fontsizex "[<velikost>]" předpokládá svůj parametr bez jednotky. +Písmeno x v názvu značí, že makro není v uživatelské dokumentaci. Uživatel +totiž může použít "\typosize[<velikost>/]" a třeba ho napadne si nějaké +vlastní makro "\fontsize" definovat. +Je-li parametr "<velikost>" prázdný, makro "\fontsizex" neudělá nic. +Jinak pomocí "\textfontsize" nastaví +velikost textových fontů. Dále zavolá +"\setmathsizes[\fontsize/.7\fontsize/.5\fontsize]", ovšem v parametru musí +odstranit jednotky a parametr přichystá pro makro "\setmathsizes" +expandovaný. Příkazem "\normalmath" nakonec nastaví matematické fonty do +nové velikosti. + +\inext {fontsizex}{^^B\cbrace}{++} + +Makro \db textfontsize "[<velikost>]" předpokládá svůj parametr bez +jednotky. Připojí jednotku "\ptunit", nastaví "\dgsize" a "\sizespec" a +zavolá "\resizeall", což je makro definované v \csplain{}u, které postupně +volá "\resizefont" na všechny registrované fonty. + +\inext {textfontsize}{^^B\cbrace}{++} + +Makro \db setbaselineskip "[<velikost>]" předpokládá parametr bez jednotky. +Připojí jednotku "\ptunit" a nastaví "\baselineskip" bez dodatečné +pružnosti. Nastaví další registry, které s "\baselineskip" souvisejí. +Záměrně není nastavena "\topskip", "\splittopskip", +"\above/belowdisplayskip". Tyto parametry (globální pro celý dokument) by si +měl uživatel nastavit sám. + +\inext {setbaselineskip}{^^B\cbrace}{++} + +Makro \db withoutunit "\makro<dimen>" odstraní jednotku z "<dimen>" a takto +upravené číslo vloží do parametru "\makro", které očekává údaj bez jednotky +v~hranaté závorce. + +\inext {withoutunit}{\empty}{+-} + +Makra \db fontscalex "<factor>", \db textfontscale "<factor>" a +\db scalebaselineskip "<factor>" +přepočítají "<factor>" podle aktuálního "\fontdim" resp. "\baselineskip" +na absolutní jednotku a zavolají odpovídající makro definované před chvílí. + +\inext {fontscalex}{\count=3 ^^B\cbrace}{++} + +Makro \db thefontsize si alokuje aktuální font do sekvence \db thefont a +tento nový fontový přepínač podrobí změně velikosti "\resizefont". +Makro \db thefontscale přepočítá parametr na absolutní velikost a zavolá +"\thefontsize". + +\inext {thefontsize}{\count=2 ^^B\cbrace}{++} + +Plain\TeX{}ový \db magstep má na konci "\relax", takže nefunguje jako pouze +expandující makro. My ale "\magstep" očekáváme v parametrech příkazů +"\typoscale" a podobných, proto v "\magstep" je nahrazeno "\relax" méně +drsným "\space". To separuje číselný parametr dostatečně. + +\inext {magstep}{\empty}{+-} + +Makro \db em přepíná kontextově do odpovídající varianty a ve spolupráci s +makrem "\additcorr" přidává italickou korekci. Makro \db additcorr +si pomocí "\lastskip" zapamatuje poslední mezeru, pak ji odstraní, vloží +italickou korekci a nakonec vrátí tu odstraněnou mezeru. + +\inext {def\nb em}{\empty}{+-} + +Fontová makra zabezpečíme proti rozkladu v parametru "\write". + +\inext {addprotect}{\empty}{+-} + + +\subsec Texty ve více jazycích +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Makro \db mtext "<značka>" je zkratkou za \uv{multilingual text}. +Toto makro si podle značky a aktuálního jazyka (dle registru "\language") +vyhledá, jaký text má vypsat. + +\inext {mtext}{\empty}{+-} + +Jednotlivé texty definujeme pomocí "\sdef{mt:<značka>:<jazyk>}" takto: + +\inext {chap:en}{\empty}{+-} + +Některé texty jsou zapsány pomocí "\v" notace. Je lepší udělat to takto +než vytvořit soubor "opmac.tex" závislý na kódování. Aby byla tato notace +správně interpretována, spustíme "\csaccents", což je makro \csplain{}u. +Pokud někdo používá OPmac s jiným formátem, než \csplain, neprovede se nic, +protože konstrukce "\csname csaccents\endcsname" se v takovém případě přerodí v +"\relax". Makro "\csaccents" spustíme jen tehdy, pokud je už uživatel +nespustil před "\input opmac". To poznáme podle toho, zda je definovaná +sekvence "\r". + +\inext {}{}{+-} + +CSplain od verze "Nov. 2012" připravuje následující +makra, která konvertují číslo "\language" na značku jazyka pro všechny +jazyky, které mají nataženy vzory dělení slov. +Pro jistotu (pokud je použita starší verze CSplainu) tuto koverzi +\uv{naučíme} i makro OPmac: + +\inext {sdef}{\empty}{+-} + + + +\subsec REF soubor +%%%%%%%%%%%%%%%%%% + +OPmac používá pro všechny potřeby (obsah, reference, citace, rejstřík, +poznámky na okraji) +jediný soubor "\jobname.ref" (tzv. REF soubor). +Navíc, pokud není potřeba, vůbec tento soubor +nezakládá. Často totiž budeme chtít dělat s OPmac jen jednoduché věci a je +únavné pořád na disku kvůli tomu uklízet smetí. + +Je potřeba deklarovat souborové deskriptory \db reffile a \db testin: + +\inext {newwrite}{\empty}{+-} + +Do souboru zapisujeme makrem \db wref "\<sekvence>{<data>}", které vloží do +"\reffile" řádek obsahující "\<sekvence><data>". +Implicitně ale není "\reffile" založeno, takže implicitní hodnota tohoto +makra je \db wrefrelax, tedy nedělej nic. + +\inext {wrefrelax}{\empty}{+-} + +Makro \db inputref spustíme na konci čtení souboru "opmac.tex", tedy v +situaci, kdy už budeme mít definovány všechny kontrolní sekvence, které se v +REF souboru mohou vyskytnout. Nyní si toto makro jen připravíme. +Makro ověří existenci souboru "\jobname.ref" a pokud existuje, provede +"\input \jobname.ref". V takovém případě po načtení REF souboru jej otevře +k~zápisu a připraví "\wref" do stavu, kdy toto +makro bude ukládat data do souboru. + +\inext {inputref}{^^B\cbrace}{+-} + +Makro \db openref kdekoli v dokumentu si vynutí založení souboru +"\jobname.ref". Toto makro neprovede nic, je-li REF soubor už založen. To +pozná podle toho, že makro "\wref" nemá význam "\wrefrelax". Jestliže soubor +ještě není založen, makro jej založí, předefinuje "\wref" a vloží první +řádek do souboru. Tím je zaručeno, že při příštím \TeX{}ování dokumentu +je soubor neprázdný, takže jej OPmac rovnou přečte a znovu založí na začátku +své činnosti. Nakonec se "\openref" zasebevraždí, aby se nemuselo při +opakovaném volání obtěžovat vykonávat nějakou práci. Práce už je totiž +hotova. + +\inext {openref}{^^B\cbrace}{++} + +Pro zápisy do REF souboru používáme tuto konvenci: první kontrolní sekvence +na řádku je vždy tvaru "\X<název>", takže máme přehled, která kontrolní sekvence +pochází z REF souboru. + + + +\subsec Lejblíky a odkazy +%%%%%%%%%%%%%%%%%%%%%%%%% + +K vytvoření zpětného odkazu provedeme tři kroky (v tomto pořadí): + +\begitems +\item* V místě "\label[<lejblík>]" si zapamatujeme "<lejblík>". +\item* V době vygenerování čísla (sekce, kapitoly, caption, atd.) + propojíme "<lejblík>" s tímto číslem. Provedeme to + pomocí "\sxdef{lab:<lejblík>}{<číslo>}". +\item* V místě "\ref[<lejblík>]" vytiskneme "\csname lab:<lejblík>\endcsname", + tedy "<číslo>". +\enditems + +To je základní idea pro zpětné odkazy. V takovém případě nepotřebujeme REF +soubor. Pokud ale chceme dopředné odkazy, je potřeba použít REF soubor +zhruba takto: + +\begitems +\item* V době vygenerování čísla (sekce atd.) navíc uložíme informaci + "\Xlabel{<lejblík>}{<číslo>}" do REF souboru. +\item* V době čtení REF souboru (tedy na začátku dokumentu) provede "\Xlabel" + přiřazení pomocí "\sdef{lab:<lejblík>}{<číslo>}". +\item* Nyní může přijít "\ref[<lejblík>]" + se svým "\csname lab:<lejblík>\endcsname" kdekoli v dokumentu. +\enditems + +Přejdeme od idejí k implementaci. +Makro \db label "[<lejblík>]" si pouze zapamatuje "<lejblík>" do makra \db +lastlabel, aby s touto hodnotou mohlo později pracovat makro, které automaticky +generuje nějaké číslo. + +\inext {def\nb label}{\empty}{+-} + +Makro, které automaticky generuje nějaké číslo, má za úkol zavolat +\db wlabel "<číslo>". Toto makro propojí "\lastlabel" a "<číslo>" tak, že +definuje sekvenci "\lab:\lastlabel" jako makro s hodnotou "<číslo>". +Kromě toho zapíše expandované "\lastlabel" i "<číslo>" do +REF souboru (jen, je-li otevřen, zpětné reference totiž fungují i bez +souboru). Nakonec vrátí makru "\lastlabel" jeho původní nedefinovanou +hodnotu, tj.~lejblík +už byl použit. Další makro automaticky generující číslo zavolá "\wlabel", +který nyní neprovede nic (pokud tedy uživatel nenapsal další +"\label[<lejblík>]"). + +\inext {wlabel}{^^B\cbrace}{++} + +\goodbreak +Makro \db ref "[<lejblík>]" zkontroluje definovanost "\lab:<lejblík>". +Je-li to pravda, vytiskne "\lab:<lejblík>" (krz reflink, aby to bylo +případně klikací). Jinak vytiskne dva otazníky a předpokládá, že v tomto +případě jde o dopřednou referenci. Vynutí si tedy otevření REF souboru +zavoláním "\openref". + +\inext {ref}{^^B\cbrace}{++} + +Makro \db pgref "[<lejblík>]" dělá podobnou práci, jako "\ref", jen s makrem +"\""pgref:<lejblík>". Toto makro je definováno až při znovunačtení REF +souboru makrem \db Xlabel, protože ke správnému určení čísla stránky +potřebujeme asynchronní "\write". + +\inext {pgref}{\empty}{+-} + + + +\subsec Kapitoly, sekce, podsekce +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Fonty pro titul, kapitoly a sekce \db titfont, +\db chapfont, \db secfont a \db seccfont +jsou definovány jako odpovídající zvětšení a nastavení tučného duktu. +Ten je nastaven pomocí \db bfshape jako "\bf" a navíc je ztotožněn +"\tenit" s "\tenbi", takže když nyní uživatel napíše "\it", dostane tučnou +kurzívu. + +\inext{titfont}{\empty}{+-} + +Pro číslování kapitol, sekcí a podsekcí potřebujeme čítače: + +\dgn \nb chapnum \dgn \nb secnum \dgn \nb seccnum + +\inext{newcount}{\empty}{+-} + +Makro pro titul \db tit počítá s tím, že bude titul na více řádcích. Sází ho +tedy jako odstavec s~pružnými "\leftskip" a "\rightskip". Příkaz "\unskip" +těsně za parametrem "#1" odstraní mezeru z konce řádku, která tam obvykle +je. Teprve poté je nadpis řádně centrován. + +\inext{tit}{^^B\cbrace}{++} + +Makra \db chap, \db sec a \db secc nastaví odpovídající čítače, dále vytvoří +číslování pro tisk (sestávající z více čísel) v makrech \db thechapnum, +\db thesecnum a \db theseccnum a zavolají odpovídající makro "\printchap", +"\printsec" a "\printsecc". +Poté zavolá "\wlabel" na +propojení případného labelu s číslem kapitoly/sekce/podsekce a +pomocí \db wcontents uloží do "\reffile" potřebná data pro obsah.\par\vskip-1pt + +\inext{chap}{\empty}{+-} + +Text titulu sekce a její číslo jsou vloženy do "\mark", takže tyto údaje +můžete použít v plovoucím záhlaví. Tato vlastnost není dokumentována v +uživatelské části, protože je poněkud techničtějšího charakteru. Makro \db +insertmark "\thesecnum{<text>}" vloží do "\mark" data ve formátu +"{<secnum>} {<text>}", takže je možno je použít přímo expanzí např. +"\firstmark", nebo je oddělit a zpracovat zvlášť. Parametru "<text>" je +zabráněna expanze pomocí protažení tohoto parametru přes "\toks", viz TBN +str. 54 dole a strany 55--57. Příkaz "\mark" se totiž snaží o expanzi. + +\inext{insertmark}{\empty}{+-} + +Příklad použití plovoucího záhlaví v "\headline": + +\begtt +\headline{\expandafter\domark\firstmark\hss} +\def\domark#1#2{\llap{\it\headsize #1. }\rm\headsize #2} +\def\headsize{\thefontsize[10]} +\endtt + +Makro "\headsize" v této ukázce zaručí, že bude mít záhlaví vždy +požadovanou velikost. Bez toho ta záruka není, pokud tedy uživatel v sazbě +dokumentu střídá velikosti písma. Output rutina totiž může přijít náhle, +třeba v okamžiku, kdy je zapnutá jiná velikost písma. + +Pokud chcete kombinovat na levých a pravých stránkách plovoucí záhlaví z +kapitol a sekcí, inspirujte se v TBN na stranách 259 a 260. + +Makra \db printchap, \db printsec a \db printsecc vytisknou číslo a titul +kapitoly, sekce resp. podsekce. Příkazem "\firstnoindent" dávají najevo, +že následující odstavec nebude mít odstavcovou zarážku. + +\inext {printchap}{\count=3 ^^B\cbrace}{++} + +Makro \db afternoindent potlačí odstavcovou zarážku pomocí přechodného +naplnění "\everypar" kódem, který odstraní box vzniklý z "\indent" a +vyprázdní pomocí \db wipeepar registr "\everypar". +Makro \db firstnoindent je ztotožněno s +"\afternoindent", ale uživatel může psát "\let\firstnoindent=\relax". +Pak bude "\afternoindent" pracovat jen za verbatim výpisy. + +\inext{afternoindent}{firstnoindent}{++} + +Nechceme, aby se nám odstavce tvořené z titulů kapitol a sekcí rozdělily do +více stran. Proto místo příkazu "\par" je použito \db nbpar. +Konečně uživatel může odřádkovat například v titulu pomocí \db nl. +Tomuto makru později v "\output" rutině změníme význam na mezeru. + +\inext{nbpar}{\empty}{+-} + + +\subsec Popisky, rovnice +%%%%%%%%%%%%%%%%%%%%%%%% + +Nejprve deklarujeme potřebné čítače: +\dgn \nb tnum \dgn \nb fnum \dgn \nb dnum + +\inext{newcount}{\empty}{+-} + +Výchozí hodnoty maker, které se vypisují v místě generovaného čísla jsou: + +\inext{thetnum}{\empty}{+-} + +Makro \db caption "/<typ> " zvedne čítač "\<typ>num" o jedničku, +dále nastaví pružné mezery s odsazením "\iindent" a s centrováním posledního +řádku (viz TBN str.~234), propojí pomocí "\wlabel" číslo s případným +lejblíkem, vytiskne slovo Tabulka/Obrázek v závislosti na jazyku makrem +"\mtext", vytiskne číslo "\the<typ>num" a zpracuje odstavec s nastavenými +"\leftskip", "\rightskip". Předefinuje "\par" tak, že první výskyt prázdného +řádku ukončí skupinu a tím se všechna nastavení vrátí do původního stavu. + +\inext{caption}{^^B\cbrace}{++} + +Makro \db eqmark zvedne "\dnum" o jedničku. +V display módu pak použije primitiv "\eqno", za kterým +následuje "\thednum". V interním módu (v boxu) vytiskne jen "\thetnum". +V obou případech propojí případný lejblík s číslem pomocí makra "\wlabel". + +\inext{eqmark}{\empty}{+-} + +\subsec Odrážky +%%%%%%%%%%%%%%% + +Odsazení každé další vnořené úrovně odrážek bude o "\iindent" větší. +Jeho hodnota je nastavena na "\parindent" v době čtení {\tt opmac.tex}. +Jestliže uživatel později změní "\parindent", měl by odpovídajícím způsobem +změnit "\iindent". Kromě toho deklarujeme čítač pro odrážky \db itemnum. + +\inext{newcount}{\empty}{+-} + +Makro \db begitems vloží "\iiskip", zahájí novou skupinu, pronuluje "\itemnum", +zvětší odsazení o~"\iindent" a pomocí "\adef" definuje hvězdičku jako +aktivní makro, které provede "\startitem". Makro \db enditems ukončí +skupinu a vloží "\iiskip". + +\inext{begitems}{\empty}{+-} + +Makro \db startitem ukončí případný předchozí odstavec, posune čítač, zahájí +první odstavec jako "\noindent" a vyšoupne doleva text definovaný v +\db printitem, který je implicitně nastaven makrem "\begitems" na +\db normalitem. + +\inext{startitem}{\empty}{+-} + +Makro \db style "<znak>" přečte "<znak>" a rozvine jen na makro +"\item:<znak>". Tato jednotlivá makra jsou definována pomocí "\sdef". +Není-li makro "\item:<znak>" definováno, použije se "\normalitem". + +\inext{style}{\empty}{+-} + +Čtvereček kreslíme jako "\vrule" odpovídajících rozměrů makrem +\db fullrectangle "{<dimen>}". + +\inext{fullrectangle}{\empty}{+-} + +Pro převod mezi numerickou hodnotou čítače a příslušným písmenem a, b, c +atd. je vytvořeno makro \db athe "<number>". + +\inext{athe}{\empty}{+-} + + +\subsec Tvorba obsahu +%%%%%%%%%%%%%%%%%%%%% + +Do \db toclist budeme ukládat data pro obsah. +Pomocí \db ifischap se budeme ptát, zda v dokumentu jsou kapitoly. + +\inext{toclist}{\empty}{+-} + +V době čtení REF souboru vložíme veškerá data obsahu do makra "\toclist" +tak, že v tomto bufferu budeme mít za sebou sekvence "\tocline" následované pěti +parametry. Makra \db Xchap, \db Xsec a \db Xsecc +mají parametry "{<číslo>}{<text>}{<strana>}" a jsou definovány následovně: + +\inext{Xchap}{\empty}{+-} + +Makro \db tocline "{<odsazení>}{<font>}{<číslo>}{<text>}{<strana>}" vytvoří +řádek obsahu. Řádek tiskneme jako odstavec, protože "<text>" může být třeba +delší. Registr "\leftskip" nastavíme jako součin "<odsazení>" krát "\iindent". +Pokud se v dokumentu vyskytují kapitoly, odsadíme ještě o další "\iindent". +Registr "\rightskip" nastavíme na 2"\iindent", aby delší "<text>" se zalomil +dřív než v místě, kde jsou stránkové číslice. Konec odstavce se stránkovou +číslicí pak vytáhneme mimo tento rozsah pomocí "\hskip-2\iindent". Odstavec +pruží v "\tocdotfill", protože tento výplněk má větší pružnost než +"\parfillskip". Registr "\parfillskip" má "1fil", zatímco "\tocdotfill" má +pružnost "1fill". Makro \db tocdotfill je implementováno pomocí "\leaders" +jako opakované tečky, které budou lícovat pod sebou. + +\inext{tocline}{\empty}{+-} + +Makro \db maketoc jednoduše spustí "\toclist", Pokud je "\toclist" prázdný, +upozorní o tom adekvátním způsobem na terminál. + +\inext{maketoc}{\empty}{+-} + + +\subsec Sestavení rejstříku +%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Slovo do rejstříku vložíme pomocí \db iindex "{<heslo>}". Protože výskyt +slova na stránce není v době zpracování znám, je nutné použít REF soubor s +asynchronním "\write". + +\inext{iindex}{\empty}{+-} + +Nyní naprogramujeme čtení parametru makra +\db ii "<slovo>,<slovo>,...<slovo> ". Vzhledem k tomu, že za +přítomnosti zkratky "@" budeme potřebovat projít seznam slov oddělených čárkou +v~parametru ještě jednou, zapamatujeme si tento seznam do "\tmp". + +\inext{leavevmode}{\empty}{+-} + +Makro \db iiA sejme vždy jedno slovo ze seznamu. Podle prázdného parametru +poznáme, že jsme u konce a neděláme nic. Při výskytu "<slovo>=@" (poznáme to +podle shodnosti parametru s \db iiatsign \unskip), +spustíme "\iiB", jinak vložíme údaj o slově do rejstříku pomocí "\iindex". +Nakonec makro "\iiA" volá rekurzivně samo sebe. + +\inext{iiA}{\empty}{+-} + +Makro \db iiB rovněž sejme vždy jedno slovo ze seznamu a na konci volá +rekurzivně samo sebe. Toto makro ovšem za použití makra \db iiC prohodí pořadí +prvního podslova před prvním lomítkem se zbytkem. Není-li ve slově lomítko, +pozná to makro "\iiC" podle toho, že parametr "#2" je prázdný. V takovém případě +neprovede nic, neboť slovo je už zaneseno do rejstříku v makru "\iiA". + +\inext{iiB}{\empty}{+-} + +Makro \db iid "<slovo> " pošle slovo do rejstříku a současně je zopakuje do +sazby. Pomocí "\futurelet" a \db iiD zjistí, zda následuje tečka nebo čárka. Pokud +ne, vloží mezeru. + +\inext{iid}{\empty}{+-} + +Při čtení REF souboru se vykonávají makra \db Xindex "{<heslo>}{<strana>}", +která postupně vytvářejí makra tvaru "\,<heslo>", ve kterých je shromažďován +seznam stránek pro dané "<heslo>". +Kromě toho každé makro "\,<heslo>" je vloženo do seznamu \db iilist. +Na konci čtení +REF souboru tedy máme v~"\iilist" seznam všech hesel jako řídicí sekvence (to +zabere nejmíň místa v \TeX{}u). Každé +"\,<heslo>" na konci čtení REF souboru obsahuje dva údaje ve svorkách, první údaj +obsahuje pomocná data a druhý obsahuje seznam stránek. Tedy "\,<heslo>" je makro +s obsahem "{<pomocná-data>}{<seznam-stránek>}". + +Seznam stránek není jen tupý seznam všech stránek, na kterých se objevil +záznam "\ii" pro dané slovo. Některé stránky se totiž mohou opakovat a my je +chceme mít jen jednou. Pokud stránky jsou souvisle za sebou: 13, 14, 15, 16, +chceme navíc takový seznam nahradit zápisem 13--16. Makro +"\Xindex{<heslo>}{<strana>}" je z tohoto důvodu poněkud sofistikovanější. + +\ilabel [Xindex:first] {firstdata} +\ilabel [Xindex:second] {seconddata} + +\inext{Xindex}{\empty}{+-} + +Činnost makra "\Xindex" si vysvětlíme za chvíli podrobněji. Nejprve ovšem +definujeme pomocné makro \db firstdata "\,<heslo>"~"\<cs>", které expanduje na +"\<cs>"~"<první-datový-údaj-hesla>&". Je-li třeba "\,aa" definováno jako +"{prvni}{druhy}", pak "\firstdata"~"\,aa"~"\cosi" expanduje na "\cosi"~"prvni&". +Tím máme možnost vyzískat data z makra. Podobně makro +\db seconddata "\,<heslo>"~"\<cs>" expanduje na +"\<cs>"~"<druhý-datový-údaj-hesla>&". + +\inext{firstdata}{\empty}{+-} + +Než se pustíme do výkladu makra "\Xindex", vysvětlím, proč pro hesla rejstříku +tvořím jednu řídicí sekvenci, která je makrem se dvěma datovými údaji. +Mohl bych jednodušeji pracovat se dvěma různými řídicími sekvencemi, +např. "\,<heslo>" a "\:<heslo>". Důvod je prostý: šetřím paměť \TeX u. Dá se totiž +očekávat, že počet hesel v rejstříku můžeme počítat na tisíce a je rozdíl +alokovat kvůli tomu tisíce kontrolních sekvencí nebo dvojnásobné množství +takových sekvencí. + +V makru "\Xindex" čteme "\firstdata" na řádku \cite[Xindex:first] +a "\seconddata" na řádku \cite[Xindex:second]. +Čtení je provedeno makry \db XindexA a \db XindexB. První úsek dat je tvaru +"<poslední-strana>/<stav>" a druhý úsek dat obsahuje rozpracovaný seznam stránek. +Podíváme-li se na definice "\XindexA" a "\XindexB", shledáme, že seznam stránek +bude uložen v "\tmp", dále "<poslední-strana>" bude v "\tmpa" a "<stav>" je v +"\tmpb". + +\inext{XindexA}{\empty}{+-} + +Rozlišujeme dva stavy: "<stav>=+", pokud je seznam stránek zakončen konkrétní +stránkou. Tato konkrétní stránka je uložena v "<poslední-strana>". Druhým +stavem je "<stav>=-", když je seznam stránek ukončen "--" (přesněji obsahem +makra \db iiendash, které můžete snadno předefinovat) a v tomto případě +"<poslední-strana>" obsahuje poslední stránku, na které byl zjištěn výskyt +hesla. Tato strana nemusí být v~seznamu stránek explicitně uvedena. + +Makro "\Xindex{<heslo>}{<strana>}" tedy postupně vytváří seznam stran +zhruba takto: + +\def\begtthook{\langleactive} +\begtt +if (první výskyt \,<heslo>) { + založ \,<heslo> do iilist; + <seznam-stran> = "<strana>"; <stav> = +; <posledni-strana> = <strana>; + return; +} +if (<strana> == <posledni-strana>) return; +if (<stav> == +) { + if (<strana> == <posledni-strana>+1) { + <seznam-stran> += "--"; + <stav> = - ; + } +else { + <seznam-stran> += ", <strana>"; + <stav> = + ; + } + else { + if (<strana> > <posledni-strana>+1) { + <seznam-stran> += "<posledni-strana>, <strana>"; + <stav> = + ; + } + } +} +<poslední-strana> = <strana>; +\endtt + +\def\begtthook{} +Makro \db makeindex nejprve definuje přechodný význam rekurzivního +"\act" tak, aby byly uzavřeny seznamy stránek (tj. aby seznam nekončil +znakem "--") a do první +datové oblasti každého makra typu "\,<heslo>" vloží konverzi textu "<heslo>" do +tvaru vhodném pro abecední řazení českých slov. +Pomocí "\expandafter \act \iilist \relax" se +požadovaná činnost vykoná pro každý prvek v "\iilist". +Dále makro "\makeindex" provede seřazení "\iilist" podle abecedy +makrem "\dosorting" a nakonec provede tisk jednotlivých hesel. K tomu účelu +znovu přechodně předefinuje "\act" a předloží mu "\iilist". + +\ilabel [mkindex:od] {firstdata} +\ilabel [mkindex:do] {after\nb act} +\ilabel [mkindex:orippx] {orippx} + +\inext{makeindex}{^^B\cbrace}{++} + +Makro \db printiipages sebere z "<druhého-datového-údaje>" seznam stránek a +jednoduše je vytiskne. + +\inext{printiipages}{\empty}{+-} + +Makro \uv{prepare index item} \db prepii "\,<heslo>" odstraní prostřednictvím +\db prepiiA z názvu kontrolní sekvence backslash a čárku a zbytek tiskne pomocí +"\printii". Pokud ale je "\,<heslo>" uloženo v~seznamu "\iispeclist", pak se +expanduje sekvenci s názvem "\\,<heslo>", ve které je uloženo, co se +má místo hesla vytisknout. Data těchto výjimek jsou připravena makrem "\iis" + +\inext{prepii}{\empty}{+-} + +Kontrolní otázka: proč se nedotazujeme jednoduše na to, zda je +"\\,<heslo>" definovaná řídicí sekvence? Odpověď: museli bychom ji +sestavit pomocí "\csname...\endcsname", ale to založí do \TeX{}ové paměti +novou řídicí sekvenci pro každé heslo v rejstříku. My se snažíme počet +těchto řídicích sekvencí redukovat na minimum. Počítáme s tím, že obyčejných +hesel bude tisíce a výjimek jen pár desítek. + +Makro \db iis "<heslo> {<text>}" vloží další údaj do slovníku výjimek pro +hesla v rejstříku. Přesněji: vloží "\,<heslo>" do \db iispeclist a definuje +sekvenci "\\,<heslo>" jako "<text>". + +\inext{iis}{\empty}{+-} + +Makro \uv{print index item} \db printii "<heslo>&" vytiskne jeden údaj do +rejstříku. Makro projde prostřednictvím \db printiiA jednotlivá podslova +oddělená lomítkem a přepíše je do rejstříku odděleny mezerou. Přitom +kontroluje, zda se podslova rovnají odpovídajícím podslovům z předchozího +hesla, které je uloženo v \db previi. Pokud ano, místo podslova vloží \db +iiemdash, což je pomlka. Na konci činnosti se spustí makro \db printiiB, +které nastaví "\previi" na \db currii (nové slovo se pro další zpracování stává +předchozím) a vytiskne seznam stránek. Makrem \db everyii +(implicitně je prázdné) dovolíme uživateli vstoupit do procesu tisku hesla. +Může například psát "\def\everyii{\indent}", pokud chce. + +\ilabel [printii:iindent] {iindent} +\inext{printii}{\empty}{+-} + +Makro \db iiparparams nastavuje parametry sazby odstavce v rejstříku. Vlevo +budeme mít "\leftsip" rovný "\iindent", ale první řádek posuneme o +"-\iindent" (viz řádek kódu~\cite[printii:iindent]) takže první řádek je +vystrčen doleva. Vpravo máme pružnou mezeru, aby se seznam čísel stran mohl +rozumně lámat, když je moc dlouhý. Makro "\iiparparams" si musí poznačit do +makra \db orippx původní údaje měněných hodnot. Není možné se totiž schovat +do skupiny, protože rejstřík je obvykle tištěn pomocí +"\begmulti...\endmulti" a toto makro občas ukončuje plnění boxu a spouští +"\flushcolumns". Kdybychom měli "\makeindex" ve skupině, pak by při +"\flushcolumns" došlo ke křížení skupin. Na konci práce "\makeindex" na +řádku \cite[mkindex:orippx] je makro "\orippx" zavoláno a tím jsou parametry +odstavce vráceny do původní podoby. + +\inext{iiparparams}{\empty}{+-} + +Pomocné makro \db scanprevii "<expanded-previi>&" se podívá do "\previi", +odloupne z něj úsek před prvním lomítkem a tento úsek definuje jako +"\tmpa". + +\inext{scanprevii}{}{+-} + +Výchozí hodnota "\previi" před zpracováním prvního slova v rejstříku je +prázdná. + +\inext{previi}{\empty}{+-} + + +\subsec Abecední řazení rejstříku +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Nejprve se zaměříme na vytvoření makra +"\isAleB"~"\,<heslo1>"~"\,<heslo2>", které rozhodne, zda je "<heslo1>" řazeno za +"<heslem2>" nebo ne. Výsledek zkoumání můžeme prověřit pomocí "\ifAleB". + +Pro porovnání dvou údajů vyžaduje norma dva průchody. V prvním (primární +řazení) se rozlišuje jen mezi písmeny A B C Č D E F G H Ch I J K L M N O P Q +R Ř S Š T U V W X Y Z Ž. Pokud jsou hesla z tohoto pohledu stejná, pak se +provede druhý průchod (sekundární řazení), ve kterém jsou řazena +neakcentovaná písmena před přehlasovaná před čárkovaná před háčkovaná před +stříškovaná před kroužkovaná a dále s nejnižší prioritou malá písmena před +velká. Připravíme si tedy dvě sady "\lccode" dvojic: pro první průchod a pro +druhý. Porovnávaná hesla zkonvertujeme pomocí "\lowercase" při nastavení +"\lccode" odpovídajícího průchodu. Pak takto zkonvertovaná hesla teprve +začneme porovnávat. + +Makro \db setprimarysorting připraví "\lccode" znaků české a slovenské +abecedy pro první průchod a \db setsecondarysorting pro druhý průchod. +Makro "\setprimarysorting" expanduje \db sortingdata +a předhodí před takto expandovaná data "\act". +Povšimneme si, že pro první průchod dostanou stejný "\lccode" všechny znaky +na společném řádku makra "\sortingdata", zatímco v druhém průchodu budou mít +všechny znaky z tohoto makra rozdílný "\lccode", ve vzestupném pořadí. +Je to tím, že v makru "\setprimarysorting" se zvedá "\tmpnum" jen v místě +čárky, zatímco v "\setsecondarysorting" se "\tmpnum" zvedá pro každý znak. +Nejnižší hodnotu má mezera vyznačená v "\sortingdata" pomocí "{ }". +Tím je zaručeno, že kratší slovo je řazeno dřív než delší slovo se stejným +začátkem, obsahující celé kratší slovo (ten~tučňák$\prec$tento). +Je sice pravda, že ASCII hodnota mezery je ještě menší, ale my musíme +mezeru někam šoupnout na jiný kód než 32, jinak by nám ji nepřečetlo +makro s~neseparovaným parametrem. Ovšem my budeme chtít mezeru přečíst. + +\ilabel [sorten] {sort:en} +\ilabel [lan] {lan:} +\ilabel [chsorting] {chsorting} + +\inext{sortingdata}{\count=3 ^^B\cbrace}{++} + +Jedním z problémů českého řazení je dvojhláska ch. Tu potřebujeme +proměnit v jediný znak. Pro potřeby řazení proměníme ch v "^^T", Ch v "^^U" +a CH v "^^V". Uděláme to následujícím okultním kódem, který definuje makro +\db preparesorting "\,<heslo>". Toto makro připraví pomocí do "\tmpb" +"<heslo>" zkonvertované krz "\lowercase", +ovšem nejprve je dvojhláska ch nahrazena jedním znakem. +Makro \db chsorting je implicitně nedefinované, což znamená, že pracujeme s +dvojhláskou ch. Na řádku \cite[sorten], \cite[lan] a \cite[chsorting] je ovšem nastaveno +"\chsorting" jako "n", což je vzkaz, že dvojhlásku ch nechceme +interpretovat. + +\inext{bgroup}{\empty}{+-} + +Tento kód je bohužel obtížněji čitelný, protože makra "\iiscanch" a další +potřebují mít separátor "ch" ve stavu, kdy jednotlivá písmena mají "\catcode" 12. +Pracujeme totiž s výstupem primitivu "\string", který bohužel vše (až na +mezeru) balí do tokenů s kategorií 12. Proto je celý kód obalen do +"\bgroup", "\egroup" a "\lowercase". Tam jsou znaky 4, 5, 6, 7, +které mají kategorii 12, šoupnuty na c, h, C, H. Po tomto dešifrování tedy +vidíme, že makro \db iiscanch je definováno takto: + +\begtt +\gdef\iiscanch #1ch#2\relax{#1\if$#2$\else^^T\iiscanch #2\relax\fi} +\iiscanch Schází hrách, který bych házel na stěnu.ch\relax +\endtt + +Uvedený příklad expanduje postupně na + +\begtt +#1<-S +#2<-ází hrách, který bych házel na stěnu.ch +=> s^^T\iiscanch #2\relax + +#1<-ází hrá +#2<-, který bych házel na stěnu.ch +=> s^^Tází hrá^^T\iiscanch #2\relax + +#1<-, který by +#2<- házel na stěnu.ch +=> s^^Tází hrá^^T, který by^^T\iiscan #2\relax + +#1<- házel na stěnu. +#2<- +=> s^^Tází hrá^^T, který by^^T házel na stěnu. +\endtt +a to nahradí všechny výskyty dvojhlásky ch znakem "^^T". Analogicky pracují +makra \db iiscanCh a \db iiscanCH. Makro \db preparesortingA nakonec zavolá +všechna tři makra, takže máme nahrazeny všechny dvojhlásky ch, Ch i CH. + +V rámci optimalizace rychlosti jsou před algoritmem na setřídění seznamu +všechna hesla jednorázově zkonvertovaná podle pravidel prvního průchodu +řazení a tato data jsou uložena v prvním datovém údaji hesla (ve druhém máme +seznam stránek). Není tedy nutné dělat konverzi při každém porovnávání dvou +hesel. Ovšem, pokud porovnání hesel vyjde bez rozdílu, je potřeba provést +druhý průchod řazení (sekundární řazení). Ten nastavujeme jednotlivě jen pro +takové dvojice hesel, kde to je potřeba. Pravděpodobnost, že to je vůbec +někdy potřeba, je mizivá. Data pro primární řazení jsou tedy už připravena na +řádcích \cite[mkindex:od] až \cite[mkindex:do] v makru "\makeindex". + +V českém řazení se nemá přihlížet na interpunkční znaky (tečka, středník, +otazník, atd.). Hesla máme řadit tak, jako kdyby tam tyto znaky nebyly. +Kdyby se dvě hesla podle tohoto pravidla nelišila, norma předepisuje nasadit +cca čtvrtý průchod, ve kterém se tyto znaky rozliší. Čtvrtý průchod +implementován není: hesla lišící se jen interpunkčními znaky, jsou v OPmac +při řazení nerozlišitelná, tj. jsou řazena v pořadí, v jakém vstupují do +rejstříku. Ignorování interpunkčních znaků je provedeno tak, že všem +těmto znakům je přidělen makrem \db setignoredchars "\lccode" tečky a +tečka je při zpracování v~"\setprimarysorting" a "\setsecondarysorting" odstraněna +makrem \db removedot. + +\inext{removedot}{\empty}{+-} + +Připravíme si "\newif", kterým ohlásíme výsledek porovnání dvou hesel: + +\inext{newif}{\empty}{+-} + +Makro \db isAleB "\,<heslo1>"~"\,<heslo2>" spustí\hfil\break +"\testAleB"~"<zkonvertované heslo1>&\relax"~"<zkonvertované heslo2>&\relax"~% +"\,<heslo1>"~"\,<heslo2>". + +\inext{isAleB}{^^B\cbrace}{++} + +Idea makra \db testAleB lexikograficky porovnávající dvě slova je v tom, že ze +dvou stringů v~parametru oddělených "\relax" postupně odlupuje vždy +první znak "#1" a "#3" z každého stringu a ten porovnává a samozřejmě při rovnosti +rekurzivně zavolá samo sebe. Pokud jsme se dostali na konec bez rozhodnutí, co +je menší, narazíme na znak "&". V takovém případě přestoupíme do +sekundárního průchodu. + +\inext{testAleB}{^^B\cbrace}{++} + +Makro \db testAleBsecondary "\,<heslo1>"~"\,<heslo2>" založí skupinu, v ní +nastaví "\lccode" dle sekundárního řazení a pomocí "\preparesorting" +připraví zkonvertovaná data do "\tmpa" a "\tmpb". Na chvosty těchto dat +přidám nulu a jedničku, aby porovnání vždy nějak dopadlo, a spustím +\db testAleBsecondaryX, což pracuje obdobně, jako "\testAleB". + +\inext{testAleBsecond}{\count=2 ^^B\cbrace}{++} + +Nyní můžeme pomocí "\isAleB\,<heslo1>\,<heslo2>\ifAleB" rozhodnout, který +ze dvou daných parametrů má být řazen dříve. Stačí tedy už jen naprogramovat +celkové řazení seznamu. Toto makro vycházející z algoritmu mergesort +vytvořil můj syn Miroslav. Makro bylo poprvé použito v DocBy\TeX{}u, což je +nástroj, kterým je například pořízena i tato dokumentace. + +Makro \db dosorting pomocí pomocného makra "\act" doplní za každý údaj v +"\iilist" čárku a dále předloží makru "\mergesort" jako parametr obsah +"\iilist" ukončený "\end,\end", vyprázdní "\iilist" a spustí "\mergesort". + +\inext{dosorting}{\empty}{+-} + +Makro \db mergesort pracuje tak, že bere ze vstupní fronty vždy dvojici +skupin +položek, každá skupina je zatříděná. Skupiny jsou od sebe odděleny +čárkami. Tyto dvě skupiny spojí do jedné a zatřídí. Pak přejde na +následující dvojici skupin položek. Jedno zatřídění tedy vypadá +například takto: dvě skupiny: "eimn,bdkz," promění v~jedinou +skupinu "bdeikmnz,". V tomto příkladě jsou položky jednotlivá písmena, +ve skutečnosti jsou to kontrolní sekvence, které obsahují celá slova. + +Na počátku jsou skupiny jednoprvkové ("\iilist" odděluje každou +položku čárkou). Makro "\mergesort" v tomto případě projde seznam a vytvoří +seznam zatříděných dvoupoložkových skupin, uložený zpětně v +"\iilist". V dalším průchodu znovu vyvrhne "\iilist" do vstupní +fronty, +vyprázdní ho a startuje znovu. Nyní vznikají čtyřpoložkové zatříděné +skupiny. Pak osmipoložkové~atd. V~závěru (na řádku~\cite[konecsortu]) +je první skupina celá setříděná a druhá obsahuje "\end", tj. +všechny položky jsou už setříděné v první skupině, takže stačí +ji uložit do "\iilist" a ukončit činnost. Pomocí \db gobbletoend +odstraníme druhé "\end" ze vstupního proudu. + +\noactive{dvojice} +\ilabel [merge:porovnani] {isAleB} +\ilabel [merge:trojka] {mergesort p1+} +\ilabel [merge:p1] {ifx,} +\ilabel [merge:p2] {fif\nb mergesort\cbrace} +\ilabel [konecsortu] {empty\nb iilist} +\inext {def\nb mergesort }{^^B\cbrace}{++} + +{\def\quotehook{\catcode`\<12} +Jádro "\mergesort" vidíme na řádcích~\cite[merge:porovnani] +až~\cite[merge:trojka]. Makro "\mergesort" sejme ze vstupního proudu +do "#1" první položku první skupiny, do "#2" zbytek první skupiny a do +"#3" první položku druhé skupiny. Je-li "#1<#3", je do výstupního +zatříděného seznamu "\indexbuffer" vložen "#1", ze vstupního proudu je +"#1" odebrán a "\mergesort" je zavolán znovu. V případě "#3<#1" +je do "\indexbuffer" vložen "#3", ze vstupního proudu je "#3" odebrán a +"\mergesort" je zavolán znovu. Řádky~\cite[merge:p1] +až~\cite[merge:p2] řeší případy, kdy je jedna ze skupin prázdná: +je potřeba vložit do "\indexbuffer" zbytek neprázdné skupiny a přejít +na další dvojici skupin. Ostatní řádky makra se vyrovnávají se +skutečností, že zpracování narazilo na zarážku "\end,\end" a je tedy +potřeba vystartovat další průchod. +\par} + + +\subsec Více sloupců +%%%%%%%%%%%%%%%%%%%% + +Makro pro sazbu do více sloupců je převzato z TBN, kde je podrobně +vysvětleno na stranách 224 až~245. Základní myšlenka makra spočívá v tom, že +se naplní jeden velký "\vbox" (box6) jedním sloupcem a "\endmulti" jej +rozlomí do sloupců požadované výšky a strčí do sazby. Není k tomu nutno +měnit výstupní rutinu. Makro z TBN je zde v OPmac ve dvou věcech přepracováno: +\begitems +\item * Důslednější balancování sloupců vylučující možnost ztráty sazby + a umožňující mít sazbu s nezlomitelnými mezerami mezi řádky. +\item * Makro měří kumulovanou sazbu a umožňuje při rozsáhlém množství + tiskového materiálu přechodně přejít do režimu \uv{vyprazdňování}. +\enditems + +Makra \db begmulti, \db endmulti, \db corrsize, \db makecolumns a \db +splitpart pracují zhruba tak, jak je popsáno v TBN. + +\ilabel [mul:endgraf] {endgraf} +\ilabel [mul:balance] {last balancing} +\ilabel [mul:relax] {relax\cbrace} +\ilabel [mul:prevgraf] {prevdepth} + +\inext{corrsize}{\count=2 \empty}{+-} + +Výstup rozlomené sazby do sloupců probíhá ve dvou režimech: když je třeba +sloupci zaplnit celou stránku, použijeme "\makecolumns". Toto makro neřeší +otázku, že může v kumulovaném boxu~6 zbýt nějaká sazba, protože se +předpokládá, že lámání bude pokračovat na další straně. Pokud ale +je na aktuální straně vícesloupcová sazba ukončena, použijeme +propracovanější \db balancecolumns. Toto makro si zazálohuje materiál +z~boxu~6 do boxu~7 a jme se zkoušeti rozlomit box~6 na sloupce s +výškou "\dimen0". Pokud ale po rozlomení není výchozí box~6 zcela prázdný, makro +zvětší krapánek (o 0,2"\baselineskip") požadovanou výšku, vrátí se k~zálohované +sazbě v boxu~7 a zkusí rozlomit znovu. To opakuje tak dlouho, dokud je box~6 +prázdný. + +\inext{balancecolumns}{^^B\cbrace}{++} + +Když je sazba plněna do boxu~6, může ji být tak moc, že tento box +překročí maximální výšku boxu, která je v \TeX{}u bohužel +omezena na cca pět metrů +(16383~pt). Proto na řádku~\cite[mul:endgraf] je předefinován "\par", který +přičítá do "\dimen0" celkovou výšku postupně kumulované sazby. Jakmile tato +výška dosáhne "0.9\maxdimen", předefinujeme makro +"\balancecolumns" na "\flushcolumns" a spustíme předčasně "\endmulti". Toto makro +vyprázdní box pomocí opakovaného "\splitpart", ovšem nevyprázdní ho celý. +Jen tu část, která zaplní celé stránky. Jakmile bude chtít +"\splitpart" přejít k~vybalancování sazby pomocí +"\balancecolumns" na řádku \cite[mul:balance], spustí se místo běžného +"\balancecolumns" makro \db flushcolumns. Toto makro ignoruje zbytek činnosti +"\endmulti" až po "\relax" na řádku \cite[mul:relax], takže vyskočí z +trojitě zanořeného "\if". Musí tedy vrátit příslušné množství "\fi" a dále +vystartuje nový "\setbox6=\vbox". Uvnitř tohoto boxu nejprve vysype zbytek +boxu~6, pomocí "\unskip\unskip" odstraní "\vfil" a "\vskip-\prevdepth", +který tam vložil "\endmulti" na řádku~\cite[mul:prevgraf], +vrátí se k zálohovanému významu makra \db ibalancecolumns +a znovu definuje "\par" obdobným způsobem jako v~"\begmulti". +Pak pokračuje ve čtení sazby. + +\inext{flushcolumns}{\empty}{+-} + + +\subsec Barvy +%%%%%%%%%%%%% + +Deklarujeme \db ifpgcolor, což budeme potřebovat v makru "\locpgcolor". +Dále deklarujeme \db lastpage. Tento registr budeme průběžně zvětšovat při +čtení REF souboru v makru "\Xpage" a na konci čtení REF souboru tedy bude +obsahovat číslo poslední strany. + +\inext{ifpgcolor}{\empty}{+-} + +Od verze pdf\TeX{}u 1.40 nabízí tento program primitivy +"\pdfcolorstackinit" a "\pdfcolorstack". Makra v OPmac tyto +primitivy nepoužívají, protože: + +\begitems +\item * Řešení v OPmac jsem vytvořil a použil podstatně dřív, než si programátoři + pdf\TeX{}u vůbec všimli, že existuje problém s přecházením barev na nové + stránky. +\item * Primitivy "\pdfcolorstackinit" a "\pdfcolorstack" stále nejsou dokumentované. +\item * Barevné řešení v "pdftex.def" pro La\TeX{} nepovažuji za koncepčně + správné, protože se snaží pomocí "\aftergroup" vytvořit zdání, že + barvy fungují lokálně uvnitř skupiny. To je zbytečná komplikace + navíc, která jde proti přirozenosti nastavování barev v PDF souboru. +\enditems + +Barvy se v PDF přepínají pomocí PDF speciálů "<num> <num> <num> <num> k" +(pro text a plochy) a "<num> <num> <num> <num> K" pro linky. Pro oba typy +barev připravíme barevná makra \db Blue, \db Red, \db Brown, \db Green, \db +Yellow, \db Cyan, \db Magenta, \db White, \db Grey, \db LightGrey, \db Black. +Uživatel si může definovat další. + +\inext{Blue}{\empty}{+-} + +Problém barev v PDF je, že od výskytu speciálu pro barvu jsou změněné barvy +až po jiný výskyt takového speciálu nebo po konec strany. Na každé nové +straně začíná sazba v barvě černé. Nám ovšem někdy může obarvený text přetéci +na další stranu. Pak ale musíme v "\output" rutině (v makru "\prepage") +nastavit barvu, která přetekla. Ovšem jak poznáme, že něco přeteklo do další +strany? Jedině pomocí REF souboru a asynchronního "\write". Proto makro \db +setcmykcolor nevkládá do PDF jen požadovaný speciál, ale taky ukládá o sobě +informaci do REF souboru. Jakmile se při čtení REF souboru objeví "\Xpage", +zkontrolujeme, zda naposledy nastavená barva byla černá. Pokud ne, dáme +pokyn makru "\prepage" prostřednictvím makra "\pg:<číslo strany>", aby +aktivovalo hned po "\headline" barvu naposledy nastavenou. To je tedy +základní idea, nyní implementace: + +\inext{ifpdftex}{\count=2 \empty}{+-} + +Makro \db pdfK má implicitně hodnotu "k" (barva pro texty a plochy) a +přechodně při použití \db linecolor má hodnotu "K". Právě nastavenou barvu +pak makro "\setcmykcolor" uloží do REF souboru. +Jen tehdy, když je explicitně +známo, že barva nepřeteče do další strany, je možné použít před použitím +přepínače barvy prefix \db locpgcolor, který potlačí v takovém případě tisk +informací do REF souboru. Následně budou potlačeny zápisy informací do REF souboru +všech barevných přepínačů až po "\Black" včetně. Další barevné přepínače (po +"\Black") se chovají už normálně. +Toto opatření je zde jen proto, aby byl REF soubor +poněkud menší a přehlednější i v případě, kdy navrhujeme bohatě barevný dokument. + +Makra \db Xpdfcolork a \db XpdfcolorK udržují při čtení REF souboru +informaci o naposledy vložené barvě v makrech \db pdflastcolork a +\db pdflastcolorK (název koresponduje typu barvy). + +\inext{Xpdfcolork}{\empty}{+-} + +Output rutina vkládá na každé stránce do REF souboru +\db Xpage "{<číslo strany>}". +Toto makro při čtení REF souboru kontroluje, zda byla naposledy použita +na předcházející straně barva černá. Pokud ne, přidá do makra +"\pg:<číslo strany>" pokyn pro nastavení barvy ve tvaru +\db setpgcolor "<k nebo K>{<čísla barvy>}". +Toto makro se spustí v output rutině na straně "<číslo strany>" +za "\headline", ale před sestavením boxu s textem. + +\inext{Xpage}{\empty}{+-} + +Makro \db draft vloží do "\headline" box nulové výšky a šířky "\draftbox", +který vystrčí svou šedou sazbu ven ze svého rozměru a je tištěn dřív, než +jakýkoli jiný materiál na stránce. + +\inext{draft}{\empty}{+-} + +V makru \db draftbox "{<text>}" je "<text>" otočen o 55 stupňů, zvětšen +desetkrát a vytištěn v barvě "\LightGrey". K tomu jsou +využity PDF transformace souřadnic. + +\inext{draftbox}{\empty}{+-} + + +\subsec Klikací odkazy +%%%%%%%%%%%%%%%%%%%%%% + +Makro \db destactive "[<typ>:<lejblík>]{<výška>}" založí cíl odkazu jen tehdy, když +je "<lejblík>" neprázdný. Ve vertikálním módu se nalepí na předchozí box +díky "\prevdepth=-1000pt" a po vložení boxu s cílem vrátí hodnotu +"\prevdepth" do původního stavu, aby následující box byl správně řádkován. +V horizontálním módu postě vloží "\destbox". Makro +\db destbox "[<typ>:<lejblík>]{<výška>}" vytvoří box nulové výšky a z~něj +vystrčí nahoru cíl klikacího odkazu vzdálený od účaří o "<výška>". Interně +použije pdf\TeX{}ový primitiv "\pdfdest" s parametrem "xyz", což +charakterizuje obvyklou možnost chování PDF prohlížeče při odskoku na cíl. +Podrobněji viz manuál k~pdf\TeX{}u. + +\inext{dest}{\empty}{+-} + +V uživatelské dokumentaci je zmíněno místo "\destactive" makro \db dest +se stejnými parametry. Toto makro je implicitně prázdné a tedy nečiné. +Teprve "\hyperlinks" je přinutí k činnosti. + +Klikací text vytvoří makro \db link "[<typ>:<lejblík>]{<barva>}{<text>}". +Makro používá +pdf\TeX{}ový primitiv "\pdfstartlink", ve kterém je vymezena výška a hloubka +aktivní plochy. Nakonec přepne na požadovanou "<barvu>" (pokud není černá), +vytiskne aktivní "<text>" a přepne zpět na černou barvu. Pdf\TeX{}ový primitiv +"\pdfendlink" ukončí sazbu aktivního textu. + +\inext{link}{^^B\cbrace}{++} + +Makro \db urllink "[<typ>:<lejblík>]{<text>}" pracuje +analogicky jako "\link". Jen navíc přidává některé +atributy do PDF výstupu a pracuje s barvou "\urlcolor". + +\inext{urllink}{^^B\cbrace}{++} + +Makro \db url "{<odkaz>}" vysází "<odkaz>" strojopisem, přičemž znak "/" je +na přechodnou dobu aktivní a definován jako "\\". Toto makro při sestavování +externího odkazu zpětně expanduje na normální "/", ale při sazbě expanduje +na "\discretionary", aby bylo možné v něm rozdělit sazbu do více řádků. + +\inext{url}{\empty}{+-} + +Makra \db toclink, \db pglink, \db citelink, \db reflink, \db ulink, která se +specializují na určitý typ linku, implicitně nedělají nic: + +\inext{toclink}{\empty}{+-} + +Ovšem po použití makra \db hyperlinks "{<barva-lok>}{<barva-url>}" se +uvedená makra "\toclink", "\pglink", "\citelink" a "\reflink" probouzejí k +životu: + +\inext{hyperlinks}{\empty}{+-} + +Pdf\TeX{}ové primitivy pro klikací odkazy dovolují dopravit do PDF další +atributy odkazu za slovem "attr". Tam je možné dát najevo, že chceme vidět +aktivní plochy ve formě rámečků. To zařídí makro \db pdfborder "{<typ>}", které +expanduje na nic, pokud není kontrolní sekvence "\<typ>border" definována. +Jinak expandují na "arrt" "/C" s obsahem podle "\<typ>border". + +\inext{pdfborder}{\empty}{+-} + +Pokud je dokument zpracován do DVI výstupu, je vhodné výše zmíněná makra +deaktivovat: + +\inext{ifpdftex}{\empty}{+-} + + +\subsec Outlines -- obsah v záložce PDF dokumentu +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Hlavní problém implementace strukturovaného obsahu do záložky PDF dokumentu +spočívá v~tom, že při vkládání jednotlivých položek obsahu je nutno znát +počet přímých potomků každé položky (v~rámci stromové struktury položek), +ovšem tito přímí potomci budou zařazeni později. OPmac tento problém řeší +dvěma průchody nad daty, které jsou vytvořeny pro tisk obsahu, tj. v makru +"\toclist". V prvním průchodu spočítá potřebné potomky a ve druhém +průchodu zařadí všechny položky postupně jako \uv{outlines} do záložky. +Připomeneme si, že v "\toclist" se nachází seznam maker tvaru +"\tocline{<odsazení>}{<font>}{<číslo>}{<text>}{<strana>}". +Makro \db outlines "{<úroveň>}" nejprve nastaví "\tocline" na hodnotu +"\outlinesA" a projde "\toclist". Pak je nastaví na hodnotu "\outlinesB" a +znovu projde "\toclist". + +\inext{def\nb outlines}{^^B\cbrace}{++} + +V makru \db outlinesA "{<odsazení>}{<font>}{<číslo>}{<text>}{<strana>}" +počítáme potomky. Makro je navrženo tak, aby bylo +snadno rozšířitelné na libovolnou úroveň hloubky stromu, nicméně pro potřeby +OPmac stačí hloubka tři (kapitoly, sekce, podsekce). Úroveň uzlu přečteme v +parametru "<odsazení>". Pro kapitolu je "<odsazení>"=0, pro sekci je +"<odsazení>"=1 a pro podsekci je "<odsazení>"=2. +Představme si vedle sebe řadu counterů +"\count0:\count1:\count2". Při sekvenčním čtení jednotlivých uzlů stromu +si každý uzel zvětší v této pomyslné řadě hodnotu svého +counteru o~jedničku. Kapitoly zvětšují "\count0", sekce "\count1", podsekce +"\count2". Stačí tedy zvětšit "\count<odsazení>". Řada counterů pak +jednoznačně určuje zpracovávaný uzel. +Uzly pro kapitoly mají přidělenu kontrolní sekvenci "ol:\the\count0" +a uzly pro sekce mají přidělenu kontrolní sekvenci +"ol:\the\count0:\the\count1". Jsou to makra, jejichž obsahem je počet +potomků daného uzlu. Makrem \db addoneol "<csname>" zvětšíme obsah dané +kontrolní sekvence o~jedničku. Příkazem "\ifcase<odsazení>" řešíme, kterému +rodiči je třeba zvednout tuto hodnotu. Při nule (kapitola) nikomu, +neboť daný uzel nemá rodiče. Při "<odsazeni>"=1 zvětšíme o +jedničku počet potomků nadřazené kapitole a při "<odsazeni>"=2 nadřazené +sekci. Asi by bylo přehlednější na začátku definovat všechny potřebné +sekvence "ol:<něco>" a nastavit jim hodnotu~0. Ovšem šetříme pamětí i časem, +takže zakládáme sekvenci "ol:<něco>" teprve v makru "\addoneol" a to tehdy, +když je ji poprvé potřeba zvětšit o~jedničku. + +\inext{outlinesA}{\count=2 ^^B\cbrace}{++} + +V makru \db outlinesB "{<odsazení>}{<font>}{<číslo>}{<text>}{<strana>}" +vkládáme jednotlivou položku obsahu do záložek pomocí pdf\TeX{}ového +primitivu\hfil\break +"\pdfoutline goto name{<lejblík>} count<potomci> {<text>}". +Číslo "<potomci>" je opatřeno znaménkem mínus právě tehdy, když chceme, aby +položka ve výchozím stavu nezobrazovala své potomky, ale jen trojúhelníček. +Potomci se zobrazí až po kliknutí na trojúhelníček. V makru \db outlinelevel +máme makrem "\outlines" připravenu úroveň rozevření, kterou si uživatel +přeje. Nejprve přičtením "\count<odsazení>" dostaneme řadu +"\count0:\count1:\count2" do stejného stavu, jako v předchozím prvním průchodu +a máme tím jednoznačně přidělen uzel stromu. +Do "\tmpnum" vložíme údaj o počtu potomků daného uzlu. K tomu je potřeba +rozvětvit výpočet příkazem "\ifcase", protože pro různou úroveň uzlu máme +údaj v~různě definovaném makru. Příkazem "\protectlist" zastavíme expanze +případných maker registrovaných pomocí "\addprotect" a definujeme vlnku jako +mezeru (v záložce vypadá líp než vlnka). Dále pomocí "\setcnvcodesA" +expandujeme "\toasciidata". Pomocí "\setlccodes\toasciidata" připravíme +"\lccode" znaků tak, aby "\lowercase" odstranil háčky a čárky. To +vzápětí provedeme, ale nejprve ještě do toho může promluvit uživatel v makru +"\cnvhook", které je implicitně nastaveno na makro prázdné. + +\ilabel [outlines:setlccodes] {setlccodes} + +\inext{outlinesB}{^^B\cbrace}{++} + +Makro \db setcnvcodesA zkontroluje podle definovanosti "\r", zda je zapnutý +"\csaccents" a pokud je, expanduje "\toasciidata". +Makro \db toasciidata potřebujeme expandovat, protože +neobsahuje přímý zápis znaků. Důvod je zřejmý, nechceme, aby se +soubor {\tt opmac.tex} stal závislý na použitém kódování. + +\inext{setcnvcodesA}{\count=2 ^^B\cbrace}{++} + +Na řádku \cite[outlines:setlccodes] +se makro \db setlccodes spustí jako "\setlccodes AAÁAÄAáa...{}{}". +Toto makro si odloupne dva parametry "xy", provede "\lccode`x=`y" a +v~rekurzivním cyklu pokračuje v~činnosti, dokud nenarazí na "{}{}". + +\inext{setlccodes}{\empty}{+-} + +Makro \db insertoutline "{<text>}" vloží jedinou položku do záložky. Pro +tuto položku se předpokládá nulový počet potomků. Využití: uživatel může +takto odkázat na začátek nebo konec dokumentu. + +\inext{insertoutline}{\empty}{+-} + +Pokud je dokument zpracován do DVI výstupu, je vhodné výše zmíněná makra +deaktivovat: + +\inext{ifpdftex}{\empty}{+-} + + + +\subsec Verbatim +%%%%%%%%%%%%%%%% + +Verbatim výpisy budou odsazeny o "\ttindent". Je nastaven na hodnotu +"\parindent" v době čtení souboru a společně s "\parindent" by měl +uživatel změnit i "\ttindent". Čítač \db ttline čísluje řádky běžného verbatim výstupu, +čítač \db viline čísluje řádky souboru čteného pomoci "\verbinput". Souborový +deskriptor \db vifile bude přiřazen souboru v makru "\verbinput". + +\inext{newcount}{\empty}{+-} + +Makra \db setverb, \db begtt "...\endtt" jsou dokumentována v TBN, str. 29. + +\inext{setverb}{testparA}{++} + +Makro "\begtt" očichá na konci své činnosti, zda se nachází pod "\endtt" +prázdný řádek (alias "\par"). K tomu slouží makra \db testparA (přeskočí +mezeru, která za "\endtt" vždy je), \db testparB (přečte následující znak +pomocí "\futurelet") a \db testparC (ošetří, zda tento následující znak je +"\par"). + +\inext{testparA}{\empty}{+-} + +Makro \db activettchar pracuje podobně, jako makro "\adef". Navíc +potřebuje použít nově načtený znak ve své aktivní kategorii jako separátor +vymezující konec parametru. + +\inext{activettchar}{\empty}{+-} + +Makro \db verbinput si pomocí "\tmpa" ověří, zda minule byl čten stejný +soubor. Pokud ne, otevře soubor "#2" ke čtení pomocí "\openin" a uloží do +\db vifilename jméno naposledy otevřeného souboru. Dále zkontroluje pomocí +"\ifeof", zda je možné ze souboru číst. Pokud ne, vypíše se varování a +pomocí \db skiptorelax se přeskočí zbytek obsahu makra až po "\relax", +takže se neprovede nic dalšího. Je-li soubor úspěšně otevřen nebo byl-li +otevřen již minule, pustí se makro "\verbinput" do prozkoumání parametru +"#1" zapsaného v závorce před jménem souboru. + +\inext{verbinput}{\empty}{+-} + +Cílem vyhodnocení parametru v závorce makra "\verbinput" jsou dva údaje: \db +vinolines bude obsahovat počet řádků, které je od začátku souboru nutno +přeskočit, než se má zahájit přepisování řádků a \db vidolines bude +obsahovat počet řádků, které se mají přepsat ze souboru do dokumentu. +Písmena "vi" na začátku těchto názvů představují zkratku pro "verbinput". +Vyšetření parametru ukončeného textem "+\relax" se v makru \db +viscanparameter větví na případ, kdy parametr obsahuje symbol "+" a použije +se pak \db viscanplus. Druhý případ, kdy uživatel nenapsal symbol plus +(takže parametr "#2" makra "\viscanparameter" je prázdný) je dále +vyšetřen v makru \db viscanminus. Obě makra si oddělí do svých parametrů +první a druhou číslici (každá z nich může být prázdná) a nastaví podle +zdokumentovaných pravidel pro zápis parametru odpovídající interní údaje +"\vinolines" a "\vidolines". Vychází přitom z předpokladu, že registr +"\viline" obsahuje číslo naposledy přečteného řádku (nebo nulu, jsme-li na +začátku souboru). + +\inext{viscanparameter}{\count=3 ^^B\cbrace}{++} + +Makro \db doverbinput provede samotnou práci: přeskočí "\vinolines" řádků a +přepíše "\vidolines" řádků. To provede v prvním a druhém cyklu "\loop". Než +se k těmto cyklům dostane, musí udělat jisté přípravné práce. Nejprve odečte +od "\vinolines" počet už přečtených řádků, protože při opakovaném čtení +stejného souboru jej neotevíráme znova, jen přeskočíme příslušný menší počet +řádků. Pokud ale se ukáže, že rozdíl je záporný (je potřeba se v souboru +vracet dozadu), makro znovuotevře soubor ke čtení pomocí "\openin" a upraví +podle toho příslušné údaje o řádcích. Pak zahájí skupinu, dále pomocí +"\setverb" nastaví speciálním znakům kategorii 12 a pomocí "\adef{ }{ }" +nastaví mezeře aktivní kategorii (bude expandovat na neaktivní mezeru jako +"\space") a také nastaví kategorii 12 znaku, který byl deklarován pomocí +"\activettchar". Připraví odsazení podle "\ttindent" a spustí uživatelský +"\tthook". Je-li potřeba tisknout čísla řádků, připraví si na to font +"\sevenrm", který má velikost rovnu 0,7 násobku základní velikosti. A pustí se do +zmíněných dvou cyklů "\loop". V obou cyklech se může stát, že narazíme +nečekaně na konec souboru. To je ošetřeno testem "\ifeof\vifile" a následnou +úpravou čítače "\tmpnum" tak, abychom okamžitě vyskočili z cyklu. Druhý +cyklus obsahuje ještě jeden speciální rys: přeje-li si uživatel číst až do +konce souboru, je nastaveno "\vidolines" na nulu a před zahájením cyklu je +čítač "\tmpnum" nastaven na "-1". Uvnitř cyklu je pak zajištěno, že v tomto +případě není čítač zvětšován o jedničku. Po ukončení práce v těchto dvou +cyklech je ukončena skupina, vložena mezera "\ttskip" a makrem "\testparB" +se ověří, zda následuje prázdný řádek. + +\inext{doverbinput}{^^B\cbrace}{++} + +V prvním cyklu "\loop" v těle makra "\doverbinput" se opakovaně volá +\db vireadline, což je makro, které přečte další řádek ze souboru. V druhém +cyklu se opakovaně volá "\vireadline" následované \db viprintline. Toto +makro vytiskne přečtený řádek do dokumentu. Před řádkem může být v "\llap" +vytištěno číslo řádku. Záleží na hodnotě "\ttline". Je to naprogramováno v +souladu s uživatelskou dokumentací. + +\inext{vireadline}{\empty}{++} + + +\subsec Jednoduchá tabulka +%%%%%%%%%%%%%%%%%%%%%%%%%% + +Tabulku makrem "\table" vytvoříme jako "\vbox", ve kterém je "\halign". Je +tedy potřeba načíst deklaraci typu "{llc|rr}" a převést ji na deklaraci pro +"\halign". Tato deklarace obsahuje znak "#" a tento znak se obtížně přidává +do těla maker. Nashromáždíme tedy postupně deklaraci pro "\halign" do +registru typu "\toks", který je nazvaný \db tabdata. Dále definujeme interní +\db tabstrutA, který bude obsahovat uživatelův "\tabstrut", ovšem přechodně +budeme toto makro měnit. Také deklarujeme čítač \db colnum, ve kterém budeme +mít po přečtení deklarace uložen počet sloupců tabulky. +Dále během skenování "<deklarace>" vytvoříme makro \db +ddlinedata, které bude obsahovat "&\dditem &\dditem ..." (počet těchto dvojic +bude roven $n-1$, kde $n$ je počet sloupců). Pokud je v deklaraci dvojitá +svislá čára, bude v makru "\ddlinedata" na příslušném místě ještě "\vvitem". +Makro "\ddlinedata" pak použijeme v "\crli" a v "\tskip", +Strýček Příhoda to může použít jinde a jinak. Konečně makro \db vvleft je +neprázdné, pokud úplně vlevo tabulky je dvojitá čára. + +\inext{newtoks}{\empty}{+-} + +Makro \db table "{<deklarace>}{<data>}" vypadá takto: + +\inext{table}{\empty}{+-} + +Makro \db scantabdata postupně čte znak po znaku z deklarace "\table" a podle +přečteného znaku ukládá do "\tabdata" odpovídající úsek skutečné deklarace pro +"\halign". Volá přitom "\addtabvrule" nebo "\addtabitem{\tabdeclare<znak>}". + +\inext{scantabdata}{^^B\cbrace}{++} + +OPmac předdefinuje tři "<znaky>" pro "<deklaraci>", sice "<znaky>" "c", "l", "r" +v makrech \db tabdeclarec, \db tabdeclarel, \db tabdeclarer. + +\inext{tabdeclarec}{\empty}{+-} + +Makro \db unsskip vkládané na konec každé datové položky odebere mezeru, +pokud má nenulovou základní velikost. Uživatelé totiž někdy dávají kolem +datových položek mezery a někdy ne, přitom chtějí, aby se jim to chovalo +stejně. Je náročné si pamatovat, že mezery před položkou jsou +ignorovány primitivem "\halign", ale mezery za položkou jsou podstatné. Tak +raději i mezery za položkou uděláme nepodstatné. + +\inext{unsskip}{}{++} + +Příklad: po deklaraci: "{|cr||cl|}" makro "\scantabdata" vytvoří: + +\begtt +tabdata: \vrule\tabiteml\hfil#\unsskip\hfil\tabitemr + &\tabiteml\hfil#\unsskip\tabitemr \vrule\kern\vvkern\vrule + &\tabiteml\hfil#\unsskip\hfil\tabitemr + &\tabiteml#\unsskip\hfil\tabitemr\vrule +ddlinedata: &\dditem &\dditem\vvitem &\dditem &\dditem +\endtt + +Makra \db addtabitem, \db addtabdata a \db addtabvrule vloží do "\tabdata" a +"\ddlinedata" požadovaný údaj. Makro "\addtabitem" pozná podle "\colnum=0", +zda vkládá data pro první sloupec (nepřidává~"&") nebo pro další sloupce +(přidává~"&"). Makro "\addtabvrule" pozná podle "\tmpa", zda před +ním předchází další "\vrule". Pokud ano, vloží dodatečnou mezeru +"\kern\vvkern" a přidá "\vvitem" do "\ddlinedata". + +\inext{addtabitem}{\empty}{+-} + +Než se pustíme do výkladu dalších maker, předvedeme příklad, ve kterém je +definováno další písmeno "P" pro "<deklaraci>". Písmeno "P" vymezí +tabulkovou položku, jež má stanovenou šířku a delší text se láme +do více řádků. Je možné si vyzkoušet třeba tento kód: + +\begtt +\newdimen\Pwidth +\def\tabdeclareP {\enskip\vtop{\hsize=\Pwidth \rightskip=0pt plus1fil + \baselineskip=1.2em \lineskiplimit=0pt \noindent ##\tabstrut}\enskip} + +\Pwidth=3cm \table{|c|P|}{\crl + aaa & Tady je delší textík, který se nevejde na jeden řádek. \crl + bb & A tady je taky je něco delšího. \crl} +\endtt + +Pusťme se nyní do rozboru maker na ukončení řádků. +Makro \db crl přidá čáru pomocí "\noalign". Makro \db crll přidá dvojitou +čáru pomocí "\noalign". + +\inext{crl}{\empty}{+-} + +Makro \db crli provede "\cr" a dále se vnoří do řádku tabulky, ve kterém +klade postupně následující +"\omit\tablinefil &\omit\tablinefil &..." +Přitom v místě dvojité vertikální čáry naklade navíc "\tabvvline". +Makro \db tablinefil vloží natahovací čáru na šířku celé položky a makro +\db tabvvline vloží dvě "\vrule" vzdáleny od sebe o "\vvkern". Tím vzniká +přetrzené místo v postupně tvořené lince. Ke správnému nakladení uvedených +povelů použije makro "\crli" obsah makra "\ddlinedata" a vlevo přidává +"\vvleft". Před spuštěním makra "\ddlinedata" definuje odpovídajícím +způsobem \db dditem a \db vvitem. + +\inext{crli}{\empty}{+-} + +Makro \db tskip prostřednictvím \db tskipA přechodně vyprázdní "\tabstrut" +předefinováním "\tabstrutA" a také vyprázdní "\dditem" a "\vvitem", aby +po použití "\ddlinedata" vznikl řádek tabulky s prázdnými položkami. +Řádek je vypodložený strutem stanovené výšky "\tmpdim". +Nakonec je potřeba vrátit "\tabstrutA" do původního stavu. + +\inext{tskip}{\empty}{+-} + +Globální změna šířek všech linek tvořených pomocí "\vrule" a "\hrule" je +provedena makry \db rulewidth a \db rulewidthA. Myšlenka je dokumentována v +TBN na str.~328. +\dgn \nb orivrule +\dgn \nb orihrule + +\inext{orihrule}{\empty}{+-} + +Makro \db frame "{<text>}" vloží vnější "\vbox{\hrule..\hrule}". +V něm se nachází další box "\hbox{\vrule\kern\vvkern..\kern\vvkern\vrule}" +a v něm "\vbox{\kern\hhkern..\kern\hhkern}". +Nejvíce uvnitř je pak "\hbox{<text>}". To by pro sazbu rámovaného textu +stačilo, nicméně my ještě řešíme úpravu výsledného boxu tak, aby měl účaří +ve stejném místě jako je účaří textu. Proto uložíme "\hbox{<text>}" do boxu0 +a změříme mu hloubku. V proměnné "\tmpdim" spočítáme celkovou hloubku +výsledného boxu. Ve výpočtu přičítáme výšku "\hrule", která nemusí být +0.4pt. Proto si její výšku změříme v boxu1. Ve vnějším "\vbox"u po +nakreslení spodní "\hrule" se pak vracíme pomocí "\kern-\tmpdim" na úroveň +účaří a zde umístíme strut hloubky "\tmpdim", aby sazba směrem dolů +nepřečnívala, ale byla obsažena v~hloubce výsledného boxu. + +\inext{frame}{\empty}{+-} + + +\subsec Vložení obrázku +%%%%%%%%%%%%%%%%%%%%%%% + +Nejprve deklarujeme \db picw. + +\inext{picw}{\empty}{+-} + +Makro \db inspic je zkratka za použití primitivů "\pdfximage", +"\pdfrefximage" a "\pdflastximage". Kdo si to má pořád pamatovat. +Není-li aktivován PDF výstup, napíšeme jen varování a neprovedeme nic. +Pokud budeme pracovat se stejnými obrázky v jednom dokumentu, bude +vhodné možnosti primitivů využít naplno. + +\inext{ifpdftex}{\empty}{+-} + + +\subsec PDF transformace +%%%%%%%%%%%%%%%%%%%%%%%% + +Makro \db pdfscale "{<vodorovně>}{<svisle>}" pracuje jednoduše: + +\inext{pdfscale}{\empty}{+-} + +Na druhé straně makro \db pdfrotate "<úhel>" vytvoří +"\pdfsetmatrix{"$\cos\varphi\enspace\sin\varphi\enspace + {-}\sin\varphi\enspace\cos\varphi$"}", +což není jednoduché, protože funkce $\cos$, $\sin$ nejsou v \TeX{}u +implementovány. Balíček "trig.sty" nabízí vyhodnocování těchto funkcí pomocí +Taylorových polynomů, nicméně OPmac nechce být závislý na balíčcích a také +chce ukázat alternativní způsob implementace. Makro "\pdfrotate" pracuje +zhruba takto: je-li argument 0, neprovede nic, je-li argument 90, provede +otočení o~90 stupňů. V ostatních případech zavolá makro \db pdfrotateA, +které rozloží argument na celou "#1" a zlomkovou "#2" část. V další části +na řádcích~\cite[rot:360] až \cite[rot:smallsin] se +zabývá jen celými stupni. Nejprve pomocí prvního a druhého "\loop" posune +argument o celé násobky 360 stupňů tak, že poté je argument mezi +0 až 360 stupni, a přitom se hodnoty funkcí $\sin$ a $\cos$ nezměnily. +Ve třetím "\loop" postupně snižuje argument o 90 stupňů a přitom dělá rotaci +o 90 stupňů tak dlouho, až máme argument mezi nulou a devadesáti. +Je-li dále argument větší než 44 stupňů, otočíme se o 45 a snížíme argument +o 45. Je-li dále argument větší než 22, otočíme se o 22 a snížíme argument o +22. Nyní máme argument v množině $\{0, 1, 2, 3, \ldots, 22\}$. Pro každý +prvek z této množiny argumentů máme předpřipraveny hodnoty funkcí +$\cos$ a $\sin$ v makrech \db smallcos a \db smallsin. Použijeme je pro +závěrečnou rotaci. Tím máme sazbu otočenou o celé stupně. +Další část makra na řádcích \cite[rot:174532] až \cite[rot:else] +řeší jemné dotočení podle zlomkové části argumentu. V intervalu nula až +jeden stupeň aproximujeme funkci $\cos$ konstantní jedničkou a funkci $\sin$ +lineární funkcí $x\cdot \pi/180$. V daném rozmezí je to velmi dobrá +aproximace. + +\ilabel [rot:360] {360} +\ilabel [rot:smallsin] {smallsin} +\ilabel [rot:174532] {174532} +\ilabel [rot:else] {else\space\space\space\space} + +\inext{pdfrotate}{\empty}{+-} + +Pro případ, že nepracujeme s PDF výstupem, definujeme klíčové primitivy +pdf\TeX{}u jako makra, která nedělají nic. + +\inext{ifpdftex}{\empty}{+-} + + + +\subsec Poznámky pod čarou a na okraji stránek +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Makro \db fnote předpokládá, že správné číslo poznámky na dané stránce je +připraveno v makru "\fn:<číslo>", kde "<číslo>" je celkové číslo poznámky napříč +celým dokumentem sledované globálním čítačem \db fnotenum. +Makro ohlásí svou existenci do REF souboru záznamem "\Xfnote" (bez +parametru). Dále vytiskne značku pomocí "\fnmarkx" a ve skupině přejde na +menší sazbu a zavolá plain\TeX{}ové makro "\vfootnote", které vloží sazbu +pomocí tzv. insertu (TBN, kapitola~6.7). Plain\TeX{}ové nastavení této +třídy insertu není makrem OPmac nijak měněno. + +\inext{fnotenum}{^^B\cbrace}{++} + +Makro \db fnotemark přičte lokálně k "\fnotenum" svůj parametr a vytiskne +odpovídající značku. Celá práce makra probíhá ve skupině, takže po ukončení +makra se "\fnotenum" vrátí do své původní hodnoty. + +\inext{fnotemark}{^^B\cbrace}{++} + +Makro \db fnotetext teprve zvedne čítač "\fnotenum" globálně a vytiskne +poznámku pomocí plain\TeX{}ového "\vfootnote". + +\inext{fnotetext}{^^B\cbrace}{++} + +Makro \db fnmarkx vytiskne otazník nebo \db thefnote. Předpokládá se, že si +uživatel předefinuje "\thefnote" k obrazu svému. Lokální číslo poznámky na +stránce má připraveno v makru \db locfnum. + +\inext{fnmarkx}{\empty}{+-} + +Při čtení REF souboru se pro každou stranu přečte nejprve "\Xpage", což +je makro, které pronuluje \db fnotenumlocal. +Makru \db Xfnote tedy stačí pozvednout +"\fnotenumlocal" o jedničku a pomocí +"\sxdef" si tuto hodnotu zapamatovat v makru "\fn:<číslo>". + +\inext{Xfnote}{\empty}{+-} + +Registr \db mnotenum. +globálně čísluje okrajové poznámky a plní podobnou funkci, jako registr +"\fnotenum" pro podčárové poznámky. + +\inext{mnotenum}{\empty}{+-} + +Makro \db mnote ve vertikálním módu založí box nulové výšky pomocí +"\mnoteA" a vycouvá na původní místo sazby pomocí "\vskip-\baselineskip". V +odstavcovém módu toto makro nalepí box nulové výšky pod právě vytvořený +řádek v odstavci. Víme, že "\vadjust" nalepí svůj materiál bez mezery pod +tento řádek. My ovšem potřebujeme vycouvat nahoru na účaří řádku. To nejde +snadno provést, protože hloubka řádku je proměnlivá. Proto do je řádku +vložen "\strut" a předpokládá se, že nyní má řádek hloubku "\dp\strutbox" a +o tento rozměr makro vycouvá nahoru. Vloží požadovaný box výšky nula na +úrovni účaří a pak se vrátí na původní místo. + +\inext{mnote}{^^B\cbrace}{++} + +Makro \db mnoteA si zjistí, zda je v makru "\mn:<číslo>" uložen primitivní +příkaz "\left" nebo "\right". Podle toho pozná, zda má umístit poznámku +doleva nebo doprava. Rovněž dá o sobě vědět do REF souboru vložením +sekvence "\Xmnote" (bez parametru). Sazba musí v obou případech vyprodukovat +box nulové výšky i hloubky. Proto je "\vtop", uvnitř kterého je text poznámky +zpracován, vložen přechodně do boxu0 a je mu pronulována hloubka. Nulová +výška je zařízena pomocí "\vbox to0pt{\vss\box0}". Vlastní sazbu poznámky +zahajujeme pomocí "\noindent" s tím, že je připraven pružný "\leftskip" nebo +"\rightskip" podle toho, zda poznámku klademe vlevo nebo vpravo. +Při kladení vlevo musíme použít "fill", abychom přeprali natahovací mezeru z +"\parfillskip". + +\inext{mnoteA}{^^B\cbrace}{++} + +Makro \db Xmnote pracuje během čtení REF souboru a využívá toho, že makro +"\Xpage" nastavuje číslo právě procesované strany do registru "\lastpge". +Takže stačí použít "\sxdef" následujícím způsobem: + +\inext{Xmnote}{\empty}{+-} + +Makro \db fixmnotes "<token>" předefinuje v cyklu všechny definované +kontrolní sekvence "\mn:<číslo>" tak, že budou obsahovat "<token>". +Tím toto makro zlikviduje práci makra "\Xmnote", ale o to nám právě jde v +případě, když chceme mít poznámky na jednoznačně dané straně. + +\inext{fixmnotes}{\empty}{+-} + + +\subsec Bibliografické reference +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Nejprve uvedeme deklarace deskriptoru \db auxfile a čítačů +\db bibnum a \db lastcitenum. + +\inext{auxfile}{\empty}{+-} + +Makro \db cite "[<lejblík1>,<lejblík2>,...]" si prostřednictvím \db citeA +zavolá "\rcite{<lejblík>}" pro každou jednotlivou položku oddělenou čárkou ve +svém parametru. Makro "\citeA" v sobě skrývá fintu: parametr nemá jediný, +ale dva "#1#2". Protože první z nich je neseparovaný, ignorují se případné +mezery za čárkou a "#1" obsahuje první písmeno "<lejblíku>". Uvnitř makra +měníme \db citesep, což je čárka a mezera, která se má mezi údaji +vytisknout. Makro \db nocite "[<lejblík1>,<lejblík2>,...]" je definováno +stejně, jen připraví jiný význam makra "\docite", krz které budeme tisknout +v "\rcite" potřebný údaj. Veškerá +činnost maker "\cite" a "\nocite" probíhá uvnitř skupiny. + +\inext{cite}{\empty}{+-} + +Makro \db rcite "{<lejblík>}" řeší zhruba řečeno následující věci: + + +\begitems +\item * Zjistí, zda je definován "\csname bib:<lejblík>\endcsname". Pokud ano, + vytiskne jeho hodnotu, pokud ne, vytiskne do textu otazníky a na terminál + varování. Tato kontrolní sekvence začne být známá po použití + "\bib[<lejblík>]" nebo "\bibitem{<lejblík>}". Tato makra uloží + odpovídající informaci do REF souboru, odkud ji při opakovaném \TeX{}ování + vyzvedneme. Je to klasická činnost, kterou provozujeme i u ostatních + křížových referencí. +\item * Uloží o sobě zprávu do bufferu "\citelist". To použijeme + v makrech "\usebibtex" nebo "\usebbl". +\enditems + +Makro "\rcite" je naprogramováno zhruba takto + +\def\begtthook{\langleactive} +\begtt +function rcite(<lejblík>) { + if (<lejblík> == '*') { <zapiš do> \citelist '*'; return; } + if (\bib:<lejblík> == nedef) { + <zapiš do> \citelist <lejblík>; + <na terminál:> "Warning, cite [label] unknown"; + <do tiskového výstupu:> "??"; + \bib:<lejblík> = empty; + return; + if (\bib:<lejblík> == empty) { + <do tiskového výstupu:> "??"; + return; + } + if (\bib:<lejblík> končí znakem '&') { + <zapiš do> \citelist <lejblík>; + <odstraň znak & z obsahu makra> \bib:<lejblík>; + } + <tiskni obsah makra> \bib:<lejblík>; +} +\endtt + +\def\begtthook{\langleactive} +Výklad kódu: Protože chceme šetřit pamětí bufferu "\citelist", zapisujeme +tam každý "<lejblík>" jen jednou. Zda se nedeklarovaný "<lejblík>" vyskytl +poprvé poznáme podle nedefinované hodnoty "\bib:<lejblík>". Zda se vyskytl +později znovu poznáme podle toho, že má hodnotu "empty". Zda se deklarovaný +"<lejblík>" vyskytl poprvé poznáme podle znaku "&" v jeho obsahu. + +Návrh kódu v C-like notaci nyní převedeme do maker v \TeX{}u: + +\inext{rcite}{^^B\cbrace}{++} + +Asi nejzajímavější vychytávka v tomto makru se týká testu na znak "&". +Implicitně při čtení REF souboru se do makra "bib:<lejblík>" uloží +"\bibnn{<hodnota>}&". Příkaz "\if" za sebou totálně expanduje vše +následující, takže nejprve narazí na "&", pak se obsah "bib:<lejblík>" +expanduje prostřednictvím \db bibnn "{<hodnota>}" na nic a za tímto +\uv{nic} se zjeví druhý znak "&", který se tedy přilepí na ten první. +Ano, je pravda, že tyto dva znaky jsou stejné. +Odstranění tohoto znaku probíhá znovu totální expanzí, +tentokrát "\bibnn" první parametr "<hodnota>" zopakuje a druhý parametr +se znakem "&" zahodí. + +Než se pustíme do výkladu makra "\docite", připravíme si makra \db printcite +"<položka>" a \db printdashcite "<položka>". První z nich tiskne jednu +položku oddělenou od případné +další čárkou, druhé tiskne položku, před kterou předchází pomlčka +vyznačující interval položek. Pointa makra "\printcite" je v tom, že si samo +po prvním zavolání připraví separátor "\citesep" (který je na začátku +činnosti "\cite" prázdný), takže při opakovaném volání "\printcite" se +vytiskne i požadovaný separátor. Pomlčka v "\printdashcite" je schována do +"\hbox", aby nedocházelo těsně za ní ke zlomu řádku. + +\inext{printcite}{\empty}{+-} + +Makro \db docite "<položka>" vytiskne otazníky při prázdném parametru a jinak +vytiskne prostřednictvím "\printcite" jednu "<položku>". Kromě toho řeší při +nenulovém "\lastcitenum" slučování po sobě následujících čísel položek do +intervalů. Naposledy vytištěnou položku uchovává v registru "\lastcitenum". +Při příštím zavolání zvětší "\lastcitenum" o jedničku a srovná ji s +"<položkou>". Jsou-li si rovny, jde o následující položku v řadě a takovou +položku netiskneme, nicméně si její hodnotu uchováme v "\tmpb". Pokud je +mezi souvislou řadou položek díra, tj. "\lastcitenum" se nerovná +"<položce>", pak dovytiskneme předchozí interval pomocí +"\printdashcite{\the\tmpb}" a následně vytiskneme i "<položku>". +Makro \db shortcitations jednoduše nastavuje "\lastcitenum" na nenulovou +hodnotu a tím probudí k životu hlavní část makra "\docite". + +\inext{docite}{\empty}{+-} + +Následuje kód makra \db bib "[<lejblík>]", které prostřednictvím +\db wbib "{<lejblík>}{<hodnota>}" vloží do REF souboru propojené údaje o +tom, jaké má "<lejblík>" přiřazeno číslo v seznamu literatury. +Makro "\wbib" připojí před "\wref" příkaz "\immediate" právě tehdy, když +"\wref" je ve stavu, kdy skutečně zapisuje do souboru REF. +Makro \db Xbib pracuje při čtení souboru REF a dělá to, co jsme si řekli už +dříve: nastaví hodnotu makra "\bib:<lejblík>" na "\bibnn{<hodnota>}&". + +\inext{bib}{\empty}{+-} + +Makro \db addcitelist "{<lejblík>}" přidá do \db citelist údaj ve tvaru +"\lcite[<lejblík>]". Hranaté závorky jsou použity proto, aby fungoval test +"\isinlist\citelist{[<lejblík>]}". Jak uvidíme za chvíli, makro +"\addcitelist" změní během činnosti makra "\usebibtex" svůj význam na +\db writeaux, aby případné použití "\cite" až za "\usebitex" rovnou +zapisovalo do AUX souboru. Podobně makro "\addcitelist" změní v makru +"\usebbl" svůj význam \db writeXcite "{<lejblík>}", +aby v příštím průchodu \TeX{}em +mělo makro "\usebbl" přehled i o výskytech "\cite", které jsou napsány +později, než "\usebbl". + +\inext{addcitelist}{\empty}{+-} + +Než se pustíme do výkladu maker "\usebibtex", "\genbbl" a "\usebbl", uvedeme +stručně popis činnosti Bib\TeX{}u. Příkaz "bibtex <dokument>" způsobí, +že program {\tt bibtex} se podívá do souboru "<dokument>.aux" a tam si všímá +sekvencí \db bibdata "{<bib-báze>}", \db bibstyle "{<bib-style>}" a \db +citation "{<lejblík>}". Na základě toho následně přečte soubor +"<bib-báze>.bib" se zdrojovými zápisy bibliografických údajů. +Pro konverzi těchto zdrojových zápisů do výstupního souboru "<dokument>.bbl" +použije stylový soubor "<bib-style>.bst". Není-li mezi sekvencemi +"\citation" uvedeno "\citation{*}", program {\tt bibtex} zahrne do výstupu +jen ty bibliografické údaje, které mají "<lejblík>" shodný s některým z +"<lejblíků>" uvedených v parametrech sekvencí "\citation". Každá sekvence +"\citation{<lejblík>}" v souboru "<dokument>.aux" typicky odpovídá jednomu +použití příkazu "\cite[<lejblík>]". + +Makro \db usebibtex "{<bib-báze>}{<bst-styl>}" otevře soubor AUX +prostřednictvím \db openauxfile "{<bib-báze>}{<bst-styl>}". Napíše tam tedy +požadovaná data pro Bib\TeX{}. Dále z "\citelist" přepíše do AUX souboru +lejbílky ve formátu "\citation{<lejblík>}". Nakonec se uvnitř skupiny pustí do +čtení souboru BBL prostřednictvím makra "\readbblfile". + +\inext{usebibtex}{\count=2 ^^B\cbrace}{++} + +Makro \db readbblfile "{<soubor>}" vyzkouší, zda je "<soubor>.bbl" připraven +ke čtení. Pokud ne, podá o tom odpovídající zprávu na terminál. Jinak +nastaví čítač "\bibnum" na nulu a (vědomo si toho, že je spuštěno ve +skupině) pustí se do lokálních re-definic La\TeX{}ových konstrukcí, které se +typicky v BBL souborech používají. Nastaví "\leftskip" na "\iindent" a +spustí "\bibtexhook". Konečně načte soubor BBL. + +\inext{readbblfile}{^^B\cbrace}{++} + +V BBL souboru se vyskytují povely \db bibitem. Za každým z nich se možná +objeví parametr v~hranaté závorce "[<značka>]" a následně je uveden +"{<lejblík>}". Pak na dalších řádcích jsou bibliografická data jednoho +záznamu ukončená prázdným řádkem. Objeví-li se "[<značka>]", dává tím +Bib\TeX{} najevo, že se má tato "<značka>" použít místo běžného číslování +záznamů. Následuje kód, který takové údaje přečte, vytiskne a vloží do REF +souboru o tom zprávu prostřednictvím "\wref{<lejblík>}{<hodnota>}". +Rozlišují se dva režimy tisku: není-li přítomna "[<značka>]" (makro "\tmpa" +je prázdné), pak pomocí "\llap" vytiskneme "[<číslo>]". Jinak se posuneme o +"-\iindent" a tiskneme "[<značku>]" následovanou mezerou. Pak se vytisknou +další údaje bibliografického záznamu. +\dgn\nb bibitemB \dgn\nb bibitemC \dgn\nb bibitemD + +\inext{bibitem}{^^B\cbrace}{++} + +Makro \db genbbl "{<bib-báze>}{<bst-style>}" otevře AUX soubor a zapíše do +něj údaje potřebné pro Bib\TeX{} včetně "\citation{*}". Poté se makro pokusí +přečíst výstup z Bib\TeX{}u pomocí "\readbblfile". V tomto případě pracuje +"\bibitem" ve zvláštním režimu, kdy netiskne "<hodnoty>", ale "<lejblíky>". +Z toho důvodu je předefinováno makro "\bibitemC". + +\inext{genbbl}{^^B\cbrace}{++} + +Makro \db usebbl "/<typ> <bbl-file>" spustí jiné makro s názvem "\bbl:<typ>". +Tři taková makra jsou definována pomocí "\sdef". První "\bbl:a" je +jednoduché: prostě projde BBL soubor a vytiskne údaje z~něj. +Druhé makro "\bbl:b" projde BBL +soubor v režimu, při kterém jsou bibliografická data každého záznamu (až po +prázdný řádek alias "\par") přečtena do parametru "#2" makra "\bibitemC". +Celý údaj je pak vytištěn jen za předpokladu, že "[<lejblík>]" je přítomen +v seznamu "\citelist". Třetí makro "\bbl:c" pracuje jako druhé až na to, že +údaj netiskne, ale zapamatuje si ho do makra "\bb:<lejblík>". Po takovém +projití BBL souboru ještě projde "\citelist", kde se "\lcite[<lejblík>]" +promění v "\bb:<lejblík>", takže se záznam vytiskne. Nyní ale v pořadí, v +jakém jsou "<lejblíky>" zařazeny do "\citelist". + +\inext{usebbl}{\count=3 ^^B\cbrace}{++} + +Za zmínku stojí ještě práce uvedených maker s "\citelist". Před výskytem makra +"\usebbl" se lejblíky z "\cite" a "\nocite" hromadí v "\citelist". Ovšem +další "\cite" a "\nocite" se mohou vyskytovat za příkazem "\usebbl". Pokud +se tak stane, pracuje "\addcitelist" nyní ve významu "\writeXcite" a uloží +potřebnou informaci do REF souboru. Při dalším \TeX{}ování se tato informace +přečte makrem \db Xcite "{<lejblík>}" z REF souboru takto: + +\inext{Xcite}{}{++} + +\noindent To tedy znamená, že se uloží do seznamu "\citelistB". Konečně +makra "\bbl:b" a "\bbl:c" si dva seznamy "\citelist" a "\citelistB" před svou +činností spojí do seznamu jediného nazvaného "\citelist". + + +\subsec Úprava output rutiny +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Místo původního makra "\plainoutput" používá OPmac makro \db opmacoutput. + +\inext{opmacoutput}{}{++} + +OPmac mění output rutinu proti originální "\plainoutput" jen v nejnutnějších věcech. +Řeší tyto dva problémy: + +\begitems +\item * Místo přímého "\shipout" nechá nejprve box sestavit jako "\box0", + pak provede "\protectlist" a pak provede "\shipout\box0". +\item * Mezi "\makeheadline" a "\pagebody" vkládá "\prepage". +\enditems + +První úprava zabrání expanzi maker přidaných pomocí "\addprotect" v době +práce příkazu "\shipout", tedy v době, kdy expandují parametry "\write". +Těmto makrům je přidělen prostřednictvím \db doprotect "<makro>" +pro tento okamžik význam "\relax". Je potřeba ještě +vysvětlit, proč bylo nutné sestavit nejprve "\box0" a teprve poté jej poslat +ven pomocí "\shipout". Je to z toho důvodu, že v době sestavování "\box0" +jsou expandována "\headline" a "\footline" a pro ten případ ještě chceme, +aby všechna makra správně expandovala. + +\inext{opmacoutput}{\empty}{+-} + +Druhá úprava vkládá makro \db prepage, které zajistí tisk stránkové číslice +do "\reffile" (to je docela užitečné) a kromě toho nastaví správnou barvu +textu strany pomocí PDF speciálu. + +\inext{prepage}{\empty}{+-} + +Když budou barvy přecházet na další stranu, jistě nechceme mít obarvenu +stránkovou číslici. Je tedy potřeba do \db footline vložit "\Black". Když +bude uživatel měnit velikost fontů v dokumentu, jistě nechce mít +stránkovou číslici pokaždé jinak velkou. Proto je do "\footline" vloženo +"\thefontsize". Je nastaveno pevně na 10pt. Předpokládáme, že pokud bude +někdo chtít jinak velkou stránkovou číslici, jednoduže si "\footline" +nastaví podle svého. Jinak je "\footline" shodná s původním nastavením +v~plain\TeX{}u. + +\inext{footline}{\empty}{+-} + + +\subsec Okraje +%%%%%%%%%%%%%% + +V registrech \db pgwidth a \db pgheight budeme mít po zavolání "\setpagedimens" +šířku a výšku strany. V~registru \db shiftoffset budeme mít případný rozdíl +okrajů mezi levou a pravou stránkou. + +\inext{pgwidth}{\empty}{+-} + +Makro \db margins "/<typ> <formát> (<levý>,<pravý>,<horní>,<dolní>)<jednotka> " +si nastaví registry "\pgwidth" a "\pgheight" prostřednictvím "\setpagedimens" a dále v +souladu s uživatelskou dokumentací nastaví potřebné okraje. V makru "\tmp" +je schována jednotka, kterou uživatel taky může zapomenout napsat. V~takovém +případě vypíšeme varování a doplníme jednotku "mm". Jakmile měníme +"\hoffset" nebo "\voffset", nastavíme je nejprve na "-1in" (tím se dostaneme +na okraj papíru) a pak budeme požadovanou velikost okraje k těmto registrům +přidávat. Nemohu za to, že Knutha napadla taková ne příliš podařená myšlenka +dát výchozí bod sazby kamsi doprostřed papíru umístěný pomocí ujetých +jednotek. Za zmínku stojí ještě dvě myšlenky. Makro \db rbmargin +"\h(v)offset\h(v)size{<okraj>}" provede výpočet hodnoty "\hoffset" nebo +"\voffset" v případě, že je dána protější hodnota okraje než je okraj přímo +nastavitelný pomocí "\h(v)offset". +A konečně posun okraje při přechodu z pravé na levou stránku "\shiftoffset" +počítáme jako "\pgwidth"~"-"~"\hsize"~"-"~"2*<levý>" což dá stejnou hodnotu jako +"<pravý>-<levý>". Změna "\hoffset" o tuto hodnotu je provedena v makru +"\pghook", tedy v "\output" rutině, schována do skupiny, takže po ukončení +"\output" rutiny se vrátí "\hoffset" na původní hodnotu. + +\inext{margins}{\empty}{+-} + +Makro \db setpagedimens "<formát> " spustí +\db setpagedimensA "<šířka> <výška>/" a k tomu musí +dopředu vyexpandovat obsah makra "\pgs:<formát>". To provedeme pomocí tří +"\expandafter". + +\inext{setpagedimens}{\empty}{+-} + +Jednotlivé "<formáty>" papíru je potřeba deklarovat. + +\inext{sdef}{\empty}{+-} + +Makro \db magscale "[<factor>]" zvětší/zmenší sazbu nastavením +registru "\mag" a definuje dosud prázdné makro \db trueunit hodnotou +"true", aby později při činnosti makra "\setpagedimensA" zůstaly zachovány +rozměry stránek. Pokud ale je makro "\magscale" spuštěno až po nastavení +velikosti stránek, jsou tyto velikosti dodatečně korigovány na \uv{true} +jednotky pomocí makra \db truedimen. + +\inext{trueunit}{\empty}{+-} + + +\subsec Závěr +%%%%%%%%%%%%% + +Nakonec pomocí "\inputref" přečteme REF soubor (pokud existuje) a vrhneme se +na zpracování dokumentu, který nám připravil uživatel. Přeji dobré pořízení. + +\inext{inputref}{\empty}{+-} + +\doindex + + +\bye + + |