Proč to dělá tohle? CSTUG

Původní verze LaTeXu poskytovala příkaz \include, který pouze řešil problém dlouhých dokumentů: na relativně pomalých počítačích se používal příkaz \includeonly. S obrovským nárůstem rychlosti počítačů příkaz \includeonly ztratil na ceně (i když stále má svůj význam ve velmi rozsáhlých projektech). Proto je tento zjednodušený příkaz zahrnut i v současných verzích LaTeXu a plete lidi, kteří toto nepochopili.

V případě, že použijete \includeonly, tak \include vytváří zvláštní .aux soubory pro každý vložený soubor a vytváří checkpointy na důležité parametry (jako číslo stránky, obrázku, tabulky nebo poznámky pod čarou). Jako přímý důsledek musí vymazat stránku před i po příkazu \include. Tento mechanismus nepracuje, pokud se příkaz \include objeví v souboru, který byl sám vložen do jiného. Toto LaTeX ohlásí jako chybu. Nyní tedy můžeme odpovědět na dvě nejzákladnější otázky:

  • Proč LaTeX vsouvá na začátek a konec stránky příkaz \include?
    Odpověď: Protože musí. Pokud se vám to nelíbí, nahraďte příkaz \include příkazem \input – nebudete moct víckrát použít příkaz \includeonly, ale pravděpodobně to stejně nebudete potřebovat, takže žádné obavy.
  • Proč nemůžu vnořovat vložené soubory? Ve verzi LaTeXu 2.09 to vždy fungovalo.
    Odpověď: Ve skutečnosti to nefungovalo (ani ve verzi 2.09), chyba však nebyla diagnostikována. Když jste ale byl v té době spokojený s chováním pod LaTeXem verze 2.09, nahraďte příkaz \include příkazem \input (s přidaným příkazem \clearpage).


Proč jsou ignorovány parametry odstavce

TeX při uspořádávání textu nepracuje tak, že by bral slovo za slovem, či řádku za řádkou. Nejmenší jednotkou, kterou TeX formátuje, jsou celé odstavce. Odstavec je načten celý do paměti a není dále zpracováván, dokud není načtena značka konce odstavce. Právě v ten okamžik se uplatní parametry odstavce. Chyby použití parametrů formátování odstavce často vznikají právě proto, že se zapomíná na to, v jaké posloupnosti se odstavec zpracovává.

Předpokládejme následující větu v LaTeXu:

  {\raggedright % zarovnávání  textu  vlevo
  Tento text by měl být na výstupu zarovnán
  pouze vlevo.  Chceme,  aby  se  tak stalo
  pouze  v~tomto  odstavci,  a~ proto  zde
  ukončíme skupinu.}

Další text je zpracován normálně...

TeX otevře skupinu a nastaví parametry formátování odstavce tak, aby byl text uvnitř této skupiny zarovnáván pouze vlevo, poté uloží dvě věty textu, uzavře skupinu a nastaví původní parametry odstavce. Poté načte prázdný řádek, který je zpracován stejně jako příkaz \par; vysází dvě věty textu. Jelikož však skupina byla ukončena před načtením konce odstavce, nastavení parametrů uvnitř skupiny ztratilo význam a odstavec bude vysázen s běžnými parametry.

Aby nastavené parametry zůstaly v platnosti po celou dobu zpracovávání odstavce, je třeba ukončit odstavec uvnitř skupiny. Nahradíme-li poslední tři řádky v předchozím příkladě za:

  ukončíme skupinu.\par}
  Další text je zpracován normálně...
ukončí se odstavec ve chvíli, kdy jsou parametry odstavce nastavené v uzavřené skupině stále v platnosti.

Další alternativa spočívá v tom, že si můžete definovat nové prostředí, které udělá příslušnou práci za vás. Pro příklad, který byl zmiňován výše, je již v LaTeXu definováno takovéto prostředí:

\begin{flushleft}
  Tento text by měl být...
\end{flushleft}


Proč se v LaTeXu užívá ochran (protection)

LaTeX si ukládá některá data, jež bude zpracovávat až později. Těmito daty jsou zejména argumenty některých příkazů, takzvané pohyblivé argumenty. Pohyblivé, protože se s daty nějakým způsobem manipuluje. Jedná se o argumenty těch příkazů, které zapisují do obsahu, seznamu tabulek atd., tj. data, která jsou zapisována do pomocného souboru, z něhož jsou později opět čtena. Jinými daty jsou ta, která se mohou objevit v záhlavích. Nejvýznamnějšími příkazy tohoto typu jsou popisy obrázků a tabulek (captions) a veškeré nadpisy. Úplný seznam lze najít v Lamportově manuálu (viz Knihy o TeXu a příbuzná literatura).

Co se za tím vším skutečně skrývá? Příkazy, jež se použijí v pohyblivých argumentech, jsou v průběhu ukládání plně expandovány. Někdy je výsledkem takového rozvoje špatný TeXovský kód, což se projeví až při jeho následném čtení. Příkazem \protect\cmd je LaTeXu řečeno, aby uložil \cmd bez expanze, jako \cmd.

Co je to „křehký příkaz“ (fragile command)? To je příkaz, který je během ukládání rozvinut do chybného TeXovského kódu.

Co je to „robustní příkaz“ (robust command)? To je příkaz, který je během ukládání rozvinut do správného TeXovského kódu.

Nikdo se (samozřejmě) z takto nepřehledné situace neraduje. Skupina projektu LaTeX3 při práci na LaTeXu 2e odstranila potřebu některých ochran, avšak techniky, které jsou jim dostupné v současném LaTeXu, činí věc poměrně složitou. Dlouhodobým cílem této skupiny zůstává odstranění všech ochran.


Proč \verb nefunguje uvnitř ...

Funkce příkazu LaTeXu pro sazbu textu v tom tvaru, v němž je uveden ve zdrojovém souboru (verbatim), je založena na využití změny kategorie (category codes) jednotlivých znaků. Knuth v této souvislosti říká: „Je potřeba věnovat jistou péči tomu, aby vše proběhlo ve správném sledu...“. Kategorie znaku se od okamžiku, kdy je mu přiřazena, nemění. Proto \verb předpokládá, že je prvním příkazem, který se dívá na svůj textový parametr. Není-li tomu tak, TeX již přiřadil kategorie jednotlivým znakům a \verb již nemá šanci kategorii měnit. Například:

    \verb+\error+
bude fungovat (vysází se „\error“), ale
    \newcommand{\unbrace}[1]{#1}
    \unbrace{\verb+\error+}
fungovat nebude (pokusí se spustit příkaz \error). Další chyby, se kterými se můžete setkat jsou „\verb ended by end of line“ nebo „\verb illegal in command argument“.

Proto se také v manuálu LaTeXu tolik naléhá na to, aby se příkaz verbatim neobjevil v argumentu žádného jiného příkazu. Tyto příkazy jsou nejen křehké (fragile), ale dokonce zcela nepoužitelné jako parametr jiných příkazů, bez ohledu na ochranu pomocí Proč se v LaTeXu užívá ochran (protection).

Jako první by jste si měli položit otázku, jestli je csx{verb} skutečně nezbytné.

  • Když \texttt{\emph{váš text}} produkuje stejné výsledky jako csx{verb}+váš text+, pak csx{verb} není zapotřebí.
  • Když používáte csx{verb} k vysázení URL, emailové adresy nebo něčeho podobného, pomůže příkaz csx{url} z balíku url (CTANurl): netrpí problémy csx{verb}.
  • Když vkládáte csx{verb} do parametru boxovacího příkazu (jako je csx{fbox}), zvažte použití prostředí lrbox:

    \newsavebox{\mybox}
    ...
    \begin{lrbox}{\mybox}
      \verb!VerbatimStuff!
    \end{lrbox}
    \fbox{\usebox{\mybox}}
    

Jinak jsou zde tři částečná řešení tohoto problému:

  • Balík fancyvrb (CTANfancyvrb) definuje příkaz \VerbatimFootnotes, který předefinovává příkaz \footnotetext (a odtud i \footnote) tak, že můžete vkládat příkaz \verb jako jeho argument. Tento přístup by mohl být v podstatě rozšířen i na argumenty ostatních příkazů, ale, bohužel, může kolidovat s jinými balíky: například \VerbatimFotenotes ovlivňuje volbu para z balíku footmisc.
  • Balík fancyvrb definuje příkaz \SaveVerb s odpovídajícím příkazem \UseVerb, který vám umožňuje uložit a pak znovu použít obsah jeho argumentů. Detaily týkající se použití tohoto extrémně silného násroje hledejte v dokumentaci k tomuto balíku.

    Poněkud jednodušší je balík verbdef (CTANverbdef), který definuje robustní příkaz, který expanduje do zadaného argumentu.

  • Pokud vám dělá problémy jediný znak (v jeho nepřítomnosti by jste klidně mohly použít csx{texttt}), zvažte použití csx{string}. \texttt{my\string_name} vysází to samé jako \verb+my_name+ a bude fungovat v parametru příkazu. Nebude ale fungovat v posuvném parametru, žádná dávka ochran (csx{protect} — viz Proč se v LaTeXu užívá ochran (protection)) jej k tomu v tomhle případě nedonutí.


Žiaden riadok na ukončenie

Chyba

! LaTeX Error: There's no line here to end.

See the LaTeX manual or LaTeX Companion ... for explanation.

je reakciou LaTeXu na príkaz \{}\ tam, kde to nečaká. Najčastejším prípadom je, keď chcete mať návestie položky zoznamu na samostatnom riadku:

\begin{description}
\item[Veľmi dlhý popisok] \\
  Text...
\end{description}

\{}\ je vlastne v tomto prípade dosť zlý príkaz (aj keby fungoval), pretože by donútil odstavec tvorený textom položky, aby ukončíl riadok, na ktorom nie je nič okrem návestia. Toto by viedlo k varovnej hláške „Underfull \hbox“ (obvykle s „nekonečnou“ nevhodnosťou hodnoty 10000); táto správa síce okrem spomalenia behu LaTeXu neškodí, ale každá správa, ktorá neprináša žiadnu informáciu, zbytočne odvracia pozornosť uzívateľa.

Správnym riešením je napísanie nového druhu prostredia description, ktoré robí to, čo chcete. (The LaTeX Companion — viď Otázku Knihy o TeXu a příbuzná literatura — poskytuje poskytuje v týchto veciach celkom široký výber.)

Jednoduchým a rýchlym riešením, ktoré sa vyhne varovaniam, je napísať:

\begin{description}
\item[Veľmi dlhý popisok] \hspace*{\fill} \\
  Text...
\end{description}

čo vyplní podtečený (under-full) riadok predtým, než si vynúti jeho uzavretie. Balík expdlist poskytuje rovnakú funkčnosť svojím príkazom csx{breaklabel}, mdwlist ju poskytuje príkazom csx{desclabelstyle}.

Ďalším častým prípadom správy je používanie prostredia center (alebo flushleft, prípadne flushright) a rozhodnutie, že potrebujete zvlášť oddeliť riadky prostredia:

\begin{center}
  Prvý riadok (nadpisu)\\
  \\
  telo centrovaného textu...
\end{center}

Riešenie je prosté: použite príkaz \{}\ spôsobom, akým sa používať má, aby poskytol viac priestoru než jeden riadkový zlom. \{}\ akceptuje voliteľný parameter špecifikujúci, koľko priestoru naviac pridať; požadovaný efekt v hore uvedenom príklade docielite takto:

\begin{center}
  Prvý riadok (nadpis)\\[\baselineskip]
  telo centrovaného textu...
\end{center}

expdlist.sty: CTANexpdlist
mdwlist.sty: distribuované ako súčasť CTANmdwtools


Chyby související se změnou velikosti písmen

TeX poskytuje dva primitivní příkazy umožňující konverzi textu do malých písmen, \lowercase, a velkých písmen, \uppercase. Tyto příkazy se nepoužívají příliš často, ale dokáží nadělat v dokumentu zmatek.

Oba příkazy nerozvíjejí text, který je jejich parametrem — výsledkem příkazu \uppercase{abc} je „ABC“, avšak výsledek příkazu \uppercase{\abc} je \abc ať už \abc znamená cokoli. Příkazy jednoduše interpretují tabulku ekvivalentů mezi velkými a malými formami znaků. Nemají například smysl pro matematiku, a tak

  \uppercase{About $y=f(x)$}
dává
  ABOUT $Y=F(X)$
což zřejmě není to, co jsme původně zamýšleli.

csx{uppercase} a csx{lowercase} navíc špatně pracují s neamerickými znaky, např. csx{uppercase{csx{ae}}} je to samé jako csx{ae}.

LaTeX poskytuje příkazy csx{MakeUppercase} a csx{MakeLowercase}, které opravují posledně zmíněný problém. Tyto příkazy jsou používány ve standardních třídach k vytvoření verzálkových hlaviček kapitol a sekcí.

csx{MakeUppercase} a csx{MakeLowercase} bohužel neřeší další problémy csx{uppercase}, takže například titulek sekce obsahující \begin{tabular} ... \end{tabular} vytvoří hlavičku obsahující \begin{TABULAR}, což je však prostředí, které vůbec neexistuje. Nejjednodušším řešením je použití uživatelem definovaného, např.:

\newcommand{\mytable}{\begin{tabular}...
  \end{tabular}}
\section{Titulek sekce \protect\mytable{}
  s~tabulkou}

Všimněte si, že csx{mytable} musí být chráněno, jinak bude expandováno a přepsáno do verzálek; stejný výsledek dosáhnete deklarováním s csx{DeclareRobustCommand} (csx{protect} již nebude potřeba).

Balík textcase Davida Carlisla adresuje množství z těchto problémů transparentním spůsobem. Definuje příkazy csx{MakeTextUppercase} a csx{MakeTextLowercase}, které mění velikost písmen, se skvělými vlastnostmi standardních LaTeXovských csx{Make*} příkazů, ale bez uvedených problémů. Balík nahrajete pomocí \usepackage[overload]{textcase} a on redefinuje LaTeXovské příkazy (ne TeXovské primitívní příkazy! csx{uppercase} a csx{lowercase}) tak, aby nadpisy sekcí a podobné věci neprodukovaly špatné nadpisy stránek.
textcase.sty: distribuováno jako součást CTANcarlisle


Proč je znak # v makrech uveden dvakrát

Při psaní makra je třeba mít na paměti, že ## zastupuje # obdobně jako #1 zastupuje cokoli, co je prvním argumentem makra.

Definujeme-li a poté použijeme makro:

  \def\a#1{+++#1+++#1+++#1+++}  \a{b}
makro po expanzi dává „+++b+++b+++b+++“, což jsme očekávali. Avšak změníme-li nyní část makra:
  \def\a#1{+++#1+++\def\x #1{xxx#1}}
\a{b} se rozvine na „+++b+++\def\x b{xxxb}“. Definuje se zde makro \x, které je ohraničené znakem b a které nemá žádný argument. Může se to zdát divné, ačkoli jde jen o zvláštní případ předchozího příkladu. Chceme-li, aby v \a bylo definováno makro \x s jedním argumentem, musíme upravit předchozí makro takto:
  \def\a#1{+++#1+++\def\x ##1{xxx##1}}
\a{b} se nyní rozvine do tvaru „+++b+++\def\x #1{xxx#1}“, neboť #1 bude nahrazeno znakem `b' a ## bude nahrazeno #.

Pro definici vnořenou uvnitř definice se použije ####1, jelikož v každé úrovni je ## nahrazeno #. V další úrovni je nutné použít osm #, atd.


Medzery v makrách

Je jednoduché napísať makrá produkujúce medzeru v sádzanom výstupe tam, kde to nie je želané ani očakávané. Medzery vytvorené makrami sú zradné, pretože sa nezdružujú s medzerami okolo makra (na rozdiel od následných medzier, ktoré píšete), takže váš výstup môže mať jednu nafúknutú medzeru zloženú z jednej alebo viacerých medzier, ktoré neboli združené. Výstup môže mať medzeru aj tam, kde ju vôbec nikto nečaká.

Medzery vznikajú (vo vnútri makra ako aj inde), znakmi medzery, tabulátoru alebo konca riadku. Pri písaní makier by ste si mali pamätať dve jednoduché pravidlá: pravidlá ignorovania prázdneho miesta (medzier) pri písaní makier sú rovnaké ako pri písaní obyčajného textu a pravidlá ignorovania medzier neplatia na medzery vzniknuté počas expanzie makra.

Medzery sú ignorované vo vertikálnom móde (medzi odstavcami), na začiatku riadku a po názve príkazu. Kedže postupnosti medzier splývajú do jednej medzery, vyzerá to, že medzery sú ignorované aj vtedy, ak nasledujú inú medzeru. Medzery môžu mať po niektorých druhoch nezátvorkovaných parametrov (napr. priradenie premenným countdimen v Plain TeXu) a po určitých kontrolných slovách (napr. v csx{hbox} to) syntaktický význam, takže znova máme prípady, kde to „vyzerá“, ako by medzery boli ignorované keď pracujú potichu samy pre seba.

Všimnite si nasledujúce makro celkom verne adaptované z makra, ktoré sa objavilo na comp.text.tex:

\newcommand{\stline}[1]
  { \bigskip \makebox[2cm]{ \textbf{#1} } }

(originál bol na jeden riadok).

Definícia makra obsahuje päť medzier:

  • po symbole { otvárajúcom telo makra; táto medzera bude ignorovaná nie z dôvodu, že sa makro objavuje na začiatku riadku, ale pretože makro bolo navrhnuté na operovanie medzi odstavcami
  • za csx{bigskip}; táto medzera bude ignorovaná (počas definície makra), pretože nasleduje názov príkazu
  • za symbolom { povinného parametru csx{makebox}; hoci sa táto medzera nevyhnutne objaví na začaitku výstupného riadku, nebude ignorovaná
  • za symbolom } uzatvárajúcim parameter csx{textbf}; táto medzera nebude ignorovaná, ale môže byť prehliadnutá, ak je parameter do povolených 2cm
  • za symbolom } uzatvárajúcim povinný parameter csx{makebox}; táto medzera nebude ignorovaná
Pôvodný autor makra sa obával, že začiatky riadkov s týmto makrom nebudú pri ľavom okraji a text za makrom nebude vždy správne zarovnaný. Tieto problémy vznikli z medzery na začaitku povinného parametru csx{makebox} a medzery bezprostredne za tým istým parametrom. Autor napísal svoje makro týmto spôsobom, na zdôraznenie významu rozličných častí. Význam sa bohužiaľ srtatil v problémoch spôsobených makrom.

Hlavná technika potlačenia medzier je použitie znakov %: všetko za % je ignorované, dokonca aj samotný koniec riadku (takže ani koniec riadku neprispeje nechcenou medzerou). Druhou technikou je zabezpečenie toho, že koncu riadku predchádza názov príkazu (kedže sa koniec riadku správa ako medzra, bude po názve príkazu ignorovaný). Hore uvedený príkaz by sme teda napísali (ako skúsený programátor s rovnakým dôrazom na štruktúru):

\newcommand{\stline}[1]{%
  \bigskip
  \makebox[2cm]{%
    \textbf{#1}\relax
  }%
}

Uistili sme sa, že každá medzera v upravenej definícii je ignorovaná, takže sa žiadna neobjaví vo výstupe. Upravená definícia sa explicitne vysporiada s každým koncom riadku (hoci, ako sme uviedli vyššie, medzera na konci prvého riadku makra by bola pri použití makra ignorovaná). Toto je v skutočnosti najlepšia technika — je jednoduchšie slepo potlačiť medzery, než v každom bode analyzovať, či to vlastne potrebujete. Na potlačenie medzier boli použité tri techniky:
  • umiestnenie znaku % na koniec riadku (na riadkoch 1, 3 a 5),
  • „prirodzené“ ukončenie riadku riadiacou sekvenciou (riadok 2) a
  • ukončenie riadku „umelou“ riadiacou sekvenciou (riadok 4); riadiaca sekvencia v tomto prípade (csx{relax}) nemá za mnohých okolností žiadny účinok (aku tu), ale toto použitie je zastaralé — znak % by bol lepší.
Dávajte si pozor na (obvyklé) pokušenie umiestniť medzeru pred znak %: ak to spravíte, môžete úplne vynechať aj %.

V „skutočnom živote“ sú medzery objavujúce sa v makrách samozrejme záhadnejšie ako v našom príklade. najbežnejšie medzery vznikajú z nechránených koncov riadkov a toto je chyba, ktorá sa občas vyskytne aj v makrách písaných skúsenými programátormi.


Definícia aktívnych znakov

Jednotlivé znaky sa môžu správať ako makrá (definované príkazy) a Plain TeX aj LaTeX definujú znak „textasciitilde“ ako nezlomiteľnú medzeru. Znak sa spraví definovateľným, alebo „aktívnym“, nastavením jeho kódu kategórie (catcode) na csx{active} (13): \catcode`_=\active.

Ľubovolný znak v princípe môže byť týmto spôsobom aktivovaný a definovaný ako makro (\def_{csx{_}} – odpoveď na Otázku Ako používať znak podčiarknutia), ale musíte sa mať na pozore: kým ľudia čakajú aktívnu vlnku, iné aktívne znaky môžu byť nečakané a môžu zle interagovať s inými makrami. Definovaním aktívneho znaku naviac znemožníte použitie znaku na iné účely a existuje iba málo voľných znakov, ktoré by mohli byť týmto spôsobom zmenené.

Pre definovanie znaku `z' ako príkazu je potrebné napísať niečo ako:

\catcode`\z=\active
\def z{Ahoj}%

a každé následné „z“ v texte sa stane pozdravom. Toto by bolo pre väčšinu dokumentov zlým nápadom, ale mohlo by to mať špeciálne využitia. (V \def z, už „z“ nie je interpretované ako písmeno; medzera teda nie je potrebná — \defz postačuje; rozhodli sme sa ponechať medzeru aspoň pre malé zprehľadnenie.) Niektoré balíky LaTeX obsahujú takéto definície. Napríklad balík shortvrb a jeho príkaz csx{MakeShortVerb}.

TeX Používa kódy kategórií na interpretáciu znakov pri tom, ako sú čítané zo vstupu. Zmena hodnoty catcode neovplyvní znaky, čo boli už prečítané. Z tochto dôvodu je vhodné, ak majú znaky pevný kód kategórie v celom dokumente. Ak je hodnota catcode zmenená za určitým účelom (toto robí príkaz csx{verb}), potom upravené znaky nebudú pri výskyte v parametri iného príkazu správne interpretované (ako napríklad v Otázke Proč \verb nefunguje uvnitř ...). Exemplárny prípad je balík doc, ktorý spracúva .dtx súbory používajúc balík shortvrb pre definíciu textbar...textbar ako skratky pre csx{verb}textbar...textbar. textbar je ale používané tiež v preambuliach tabuľkových prostredí, takže tabuľky v .dtx súboroch môžu mať vertikálne oddelenie riadkov medzi stĺpcami iba pomocou určitých špeciálnych opatrení.

Ďalším dôsledkom je, že priradenia catcode uskutočnené v makrách často nefungujú, ako by sa čakalo (viď Otázku Aktívne znaky v parametroch príkazov). Napríklad definícia

\def\mistake{%
\catcode`_=\active
\def_{\textunderscore\-}%
}

nefunguje, pretože sa pokúša definovať bežný znak _: Keď je makro použité, zmena kategórie neplatí pre znak podčiarknutia v definícii makra. Namiesto toho môžete použiť:

\begingroup
\catcode`_=\active
\gdef\works{%    všimnite si globálne \gdef
  \catcode`_=\active
  \def_{\textunderscore\-}%
}
\endgroup

Alternatívny spôsob vytvorenia takejto izolovanej definície záleží na zvláštnych vlastnostiach csx{lowercase}, ktorý zmení znaky bez zmeny ich hodnoty catcode. Kedže vždy existuje jeden aktívny znak („textasciitilde“), môžeme oklamať csx{lowercase}, aby zaplátalo definíciu bez toho, aby sme explicitne zmenili catcode:

\begingroup
  \lccode`\~=`\_
  \lowercase{\endgroup
    \def~{\textunderscore\-}%
  }%

Tieto dve definície majú rovnaký celkový efekt (znak je definovaný ako príkaz, ale znak nezostáva aktívny) až na to, že prvá definuje príkaz csx{global}.

Pre použitie aktívnych znakov v matematickom móde je lepšie nechať znaku bežný catcode a prideliť mu špeciálny aktívny matematický kód:

\begingroup
  \lccode`~=`x
  \lowercase{\endgroup
    \def~{\times}%
  }%
\mathcode`x="8000

Špeciálny znak nemusí byť predefinovaný kedykoľvek sa stane aktívnym — definícia príkazu pretrvá aj po návratu catcode na pôvodnú hodnotu; definícia sa stane znova dostupnou, keď sa znak opäť stane aktívnym.
doc.sty: distribuované ako súčasť zdrojových súborov CTANlatex
shortvrb.sty: distribuované ako súčasť CTAN2etools


Aktívne znaky v parametroch príkazov

Občas je celkom príjemné spraviť jeden alebo dva znaky aktívnymi v parametri príkazu, aby sme uľahčili autorom písanie parametrov.

Aktívne znaky môžu byť v takýchto situáciach bezpečne použité, je však treba opatrnosti.

Pri zvažovaní tejto odpovede vyvstal príklad: ašpirujúci programátor makier na comp.text.tex žiadal o pomoc pri snahe donútiť #b produkovať hudobné symboly krížiku a béčka v makre pre špecifikáciu akordov.

Prvým problémom je, že # aj b majú dôležité využitie na inom mieste v TeXu (mierne povedané!), takže znaky sa môžu stať aktívnymi iba počas vykonávania príkazu.

Použitím techník diskutovaných v Otázke Definícia aktívnych znakov môžeme definovať:

\begingroup
  \catcode`\#=\active
  \gdef#{$\sharp$}
\endgroup

a:

\begingroup
  \lccode`\~=`\b
  \lowercase{\endgroup
    \def~{$\flat$}%
  }

Druhým problémom je časovanie: príkaz musí spraviť každý zo znakov aktívnym predtým, než sú načítané jeho parametre. Toto znamená, že príkaz samotný nemôže mať parametre, ale musí byť rozdelený na dve časti. Píšeme teda:

\def\chord{%
  \begingroup
    \catcode`\#=\active
    \catcode`\b=\active
    \Xchord
}
\def\Xchord#1{%
    \chordfont#1%
  \endgroup
}

a príkaz môžeme použiť ako \chord{F\#} alebo \chord{Bb minor}.

Dôležité sú dve vlastnosti:

  • csx{begingroup} v csx{chord} otvára skupinu, ktorá je uzavretá pomocou csx{endgroup} v csx{Xchord}; táto skupina obmedzuje zmenu kódov kategórie, čo je dôvodom existencie celého cvičenia.
  • Hoci # je aktívny počas vykonávania csx{Xchord}, nie je aktívny, keď je práve definovaný, takže použitie #1 nepotrebuje špeciálnu pozornosť.

Zapamätajte si, že technika použitá v makrách ako csx{chord} je analogická technike použitej v príkazoch ako csx{verb}; a rovnako ako csx{verb} (viď Otázku Proč \verb nefunguje uvnitř ...), csx{chord} nebude fungovať vo vnútri parametru iného príkazu (chybové správy, ak sa vôbec objavia, budú pravdepodobne dosť zvláštne).


Definícia makra z parametru

Je bežné chcieť príkaz na vytvorenie iného príkazu, často je požadované, aby bol názov nového príkazu odvodený z parametru. LaTeX to robí celý čas, napríklad csx{newenvironment} vytvára start- a end-environment príkazy, ktorých názvy sú odvodené z názvu environment príkazu.

Zjavný prístup:

\def\relay#1#2{\def\#1{#2}}

nefunguje (TeX ho interpretuje ako divnú redefiníciu \#). Trik spočíva v použití csx{csname}, čo je TeXovské primitívum na generovanie názvov príkazov z náhodného textu, spolu s csx{expandafter}. Uvedená definícia by mala vyzerať:

\def\relay#1#2{%
  \expandafter\def\csname #1\endcsname{#2}%
}

S touto definíciou je \relay{blah}{bleah} ekvivalentné csx{def}\blah{bleah}.

Všimnite si, že definícia csx{relay} vynecháva zátvorky okolo názvu príkazu v spúšťanom csx{newcommand}. Toto je z dôvodu, že nie sú potrebné (to sú v skutočnosti zriedvo) a za týchto okolností robia kód makra mirne nudnejším.

Vytvorený názov nemusí byť (samozrejme) iba parameter:

\def\newrace#1#2#3{\expandafter\def
    \csname start#1\endcsname{%
      #2%
  }%
  \expandafter\def
    \csname finish#1\endcsname{%
      #3%
  }%
}

S príkazmi

\def\start#1{\csname start#1\endcsname}
\def\finish#1{\csname finish#1\endcsname}

by sa tieto mohli správať trochu ako LaTeXovské prostredia (environments).


Zistenie, že je niečo prázdne

Predpokladajme, že potrebujete vediet, či je parameter vášho príkazu prázdny, teda rozoznať medzi \cmd{}\cmd{blah}. Toto je celkom jednoduché:

\def\cmd#1{%
  \def\tempa{}%
  \def\tempb{#1}%
  \ifx\tempa\tempb
    
  \else
    
  \fi
}

Prípad, keď chcete ignorovať parameter pozostávajúci iba z medzier (nemusí byť teda úplne prázdny), je komplikovanejší. Tento prípad je vyriešený vo fragmente kódu ifmtarg definujúcom príkazy csx{@ifmtarg} a csx{@ifnotmtarg}, ktoré rozoznajú (v opačných smeroch) medzi druhým a tretím parametrom. Kód balíku sa objavuje aj v LaTeXovskej triede memoir.

Ifmtarg tvorí vyzývavé čítanie, existuje aj diskusia tochto problému v čísle dva série článkov „Around the bend“ Mika Downesa.
Around the bend: séria článkov — CTANaro-bend
ifmtarg.sty: CTANifmtarg
memoir.cls: CTANmemoir


Číslovanie rovníc, obrázkov a tabuliek

Mnoho LaTeXovských tried (vrátane štandardnej triedy book) čísluje veci po kapitolách, takže obrázky v kapitole 1 sú číslované 1.1, 1.2 a tak ďalej. Toto niekedy nie je pre požiadavky používateľa vhodné.

Namiesto prepísanie celej triedy je možné použiť balíky removefrremreset. Oba definujú príkaz csx{@removefromreset}, po vložení balíku teda môžete napísať niečo ako:

  \makeatletter
  \@removefromreset{figure}{chapter}
a automatické prečíslovávanie sa zastaví. Potom musíte znovu definovať spôsob, akým sú čísla obrázkov (v tomto prípade) tlačené:
  \renewcommand{\thefigure}{\@arabic\c@figure}
  \makeatother
(nezabudnite pre každé počítadlo, s ktorým chcete manipulovať, uskutočniť všetky kroky uvedené v csx{makeatletter} ...@ csx{makeatother}).

Technika môže byť použitá aj na zmenu pri resetovaní počítadla vo viacúrovňovej štruktúre. Predpokladajme, že vaša trieda čísluje stránky ako .. a vy ich chcete číslovať po kapitolách. Skúste:

\@removefromreset{figure}{section}
\@addtoreset{figure}{chapter}
\renewcommand{\thefigure}%
             {\thechapter.\@arabic\c@figure}

(príkaz csx{@addtoreset} je súčasť LaTeXu samotného).

Balík chngcntr poskytuje jednoduché prostriedky sprístupnenia dvoch diskutovaných druhov zmien definujúc príkazy csx{counterwithin} a csx{counterwithout}. Trieda memoir tieto funkcie poskytuje tiež.
chngcntr.sty: CTANchngcntr
memoir.cls: CTANmemoir
removefr.tex: CTANremovefr (toto je konštruované ako „fragment“ pre použitie v iných balíkoch: nahrajte pomocou \input{removefr})
remreset.sty: distribuované ako súčasť CTANcarlisle


csx{edef} nefunguje s csx{protect}

Robustné príkazy LaTeXu sú buď „prirodzene robustné“ — nikdy nepotrebujú csx{protect}, alebo „self-protected“ (chránia sa samy) — majú csx{protect} nejakým spôsobom zabudované do svojej definície. „Self-protected“ príkazy sú robustné iba v kontexte, kde je mechanizmus csx{protect} správne spracovaný. Kedže csx{edef} je TeXovské primitívum a nie príkaz LaTeXu, telo definície csx{edef} nespracúva csx{protect} správne.

Tento problém je riešený interným príkazom LaTeXu csx{protected@edef}, ktorý robí prácu csx{edef}, zatiaľ čo udržuje mechanizmus csx{protect} funkčný. Existuje zodpovedajúci príkaz csx{protected@xdef}, ktorý robí to, čo csx{xdef}.

Kedže sú tieto príkazy interné, musia byť samozrejme pozorne sledované (viď Otázku \@ a @ ve jménech maker).


Voliteľné parametre ako csx{section}

Voliteľné parametre v makrách definovaných pomocou csx{newcommand} nepracujú úplne rovnakým spôsobom ako voliteľný parameter csx{section}. Štandardná hodnota voliteľného parametru csx{section} je hodnota povinného parametru, ale csx{newcommand} vyžaduje, aby ste štandardné hodnoty vedeli dopredu.

Potrebný trik je použitie makra v štandardnom parametri:

\newcommand\thing[2][\DefaultOpt]{%
  \def\DefaultOpt{#2}%
  ...%
}


Tvorenie návestí z počítadiel

Predpokladajme, že máme LaTeXovské počítadlo, ktoré sme definovali pomocou \newcounter{foo}. Hodnotu počítadla môžeme zvýšiť pomocou \addtocounter{foo}{1}, ale je to ťažkopádne na operáciu, ktorá nastáva tak často, takže existuje príkaz \stepcounter{foo}, ktorý vykonáva tento špeciálny prípad zväčšenia o jednotku.

Existuje interná premenná LaTeXu „aktuálne návestie“, ktorá si pamätá poslednú vec s možnosťou návestia, ktorú LaTeX spracoval. Mohli by ste (ak by ste na tom trvali) tú hodnotu nastaviť relevantným príkazom TeXu (zabezpečiac potrebné opatrenia, aby interný príkaz fungoval) — ale nie je to potrebné. Ak namiesto hore uvedených metód použijete \refstepcounter{foo}, interná premenná bude nastavená na novú hodnotu a (kým nepríde niečo iné) csx{label} bude odkazovať na počítadlo.


Zistenie, či ste na párnej alebo nepárnej strane

Otázka Jak dostat \marginpar na správnou stranu? diskutuje problém, ako donútiť príkazy csx{marginpar}, aby svoj výstup umiestnili na správny okraj dvojstranových dokumentov. Toto je obecný problém znalosti, kde leží určitý kus textu: výstupná rutina je asynchrónna a TeX/LaTeX obvykle spracuje dobrý kus ďalšej strany, než sa vôbec rozhodne akúkoľvek stranu vyprodukovať. Dôsledkom je, že počítadlo page (interne známe v LaTeXu ako csx{c@page}) je obvykle spoľahlivé, iba ak ste vo výstupnej rutine.

Riešením je použitie nejakej verzie csx{label} mechanizmu na zistenie, na ktorom konci stránky ste. Hodnota počítadla strán objavujúca sa v príkaze csx{pageref} bola vložená do behu výstupnej rutiny a je teda bezpečná.

csx{pageref} samotný však bezpečný nie je: môžete dúfať, že

\ifthenelse{\isodd{\pageref{foo}}}{odd}{even}
spraví potrebné, ale o balíkoch babel aj hyperref sa vie, že zasahujú do výstupu csx{pageref}; buďte opatrní!

Balík chngpage musí túto funkcionalitu poskytovať pre vlastné využitie, a preto poskytuje príkaz csx{checkoddpage}. Tento nastavuje návestie na privátne použitie a časť návestia s odkazom na stranu je potom preskúmaná (hyperref bezpečným spôsobom) na nastavenie platnosti csx{ifcpoddpage}, ak bol príkaz zavolaný na nepárnej strane. Trieda memoir obsahuje rovnaký príkaz nastavujúci platnosť csx{ifoddpage}. csx{label} samozrejme prispieva k LaTeXovským chybovým hláškam „Rerun to get cross-references right“ ...
chngpage.sty: CTANchngpage
memoir.cls: CTANmemoir


Ako zmeniť formát návestí

Štandardne pri vytvorení návestia toto preberie vzhľad označeného počítadla: konkrétne je nastavené na csx{the} — čo by bolo použité, ak by ste chceli vysádzať počítadlo do vášho textu. Toto nie je vždy to, čo potrebujete: ak napríklad máte vnorené číslované zoznamy s vonkajškom číslovaným a vnútrom označeným písmenami, očakávalo by sa, že sa chcete na položky vnútorného zoznamu odkazovať ako „2(c)“. (Zapamätajte si, že môžete zmeniť štruktúru položiek zoznamov — viď Otázku Pekne číslované zoznamy.) Zmena je samozrejme možná explicitným označením rodiča a použitím tohto návestia na skonštruovanie vysádzaného výsledku — niečo ako

\ref{parent-item}(\ref{child-item})

by bolo nudné a náchylné k chybám. Bolo by naviac nevhodné, kedže by ste konštruovali vizuálnu reprezentáciu, ktorá je neflexibilná (nemohli by ste vôbec zmeniť všetky odkazy na prvky zoznamu jedným ťahom).

LaTeX má v skutočnosti príkaz formátujúci návestia zabudovaný do každej definície návestia; štandardne je neplatný, ale je užívateľovi dostupný na programovanie. Pre ľubovolné návestie existuje interný príkaz LaTeXu csx{p@}; napríklad definícia návestia na vnútornej položke zoznamu je pravdepodobne uskutočnená príkazom \p@enumii{\csx{theenumii}}. Interné fungovanie tu bohužiaľ nie je úplne správne, musíte teda upraviť príkaz csx{refstepcounter}:

\renewcommand*\refstepcounter[1]{%
  \stepcounter{#1}%
  \protected@edef\@currentlabel{%
    \csname p@#1\expandafter\endcsname
      \csname the#1\endcsname
  }%
}

So zmenou na správnom mieste teraz môžete napríklad zmeniť návestia na všetkých vnútorných zoznamoch pridaním nasledujúceho kódu do preambuly:

\makeatletter
\renewcommand{\p@enumii}[1]{\theenumi(#1)}
\makeatother

Toto zabezpečí, že návestia číslovaných zoznamov druhej úrovne vyzerajú ako „1(a)“ (a tak ďalej). Analogická zmena funguje pre ľubovolné počítadlo používané v príkaze csx{label}.

Balík fncylab v skutočnosti spraví všetko uvedené (vrátane úpravy LaTeXu samotného). S týmto balíkom je uvedený kód (celkom efektívne) vykonaný príkazom:

\labelformat{enumii}{\theenumii(#1)}

Uvedený príklad, ktorý môžeme uskutočniť viacerými rozličnými spôsobmi, bol označený zastaralým objavením sa balíku enumitem, ktorý je diskutovaný v odpovedi o dekorovaní číslovaných zoznamov (Otázka Pekne číslované zoznamy).
enumitem.sty: distribuované ako súčasť CTANbezos
fncylab.sty: CTANfncylab


Ako prekročiť limit 9 parametrov

Ak sa nad tým zamyslíte, zistíte, že Knuthova syntax definície príkazov:

\def\blah#1#2 ... #9{}

je limitovaná na 9 parametrov. Neexistuje priamy spôsob ako toto obísť. Ako by ste vyjadrili desiaty parameter a zaistili, že syntax nepohltí iné správne použitie?

Ak skutočne potrebujete viac než 9 parametrov, správny postup je:

\def\blah#1#2 ... #9{%
  \def\ArgI{{#1}}%
  \def\ArgII{{#2}}%
  ...
  \def\ArgIX{{#9}}%
  \BlahRelay
}
\def\BlahRelay#1#2#3{%
  % parametre 1-9 sú teraz
  %   \ArgI-\ArgIX
  % parametre 10-12 sú
  %   #1-#3
  %
}

Táto technika je ľahko rozšíriteľná TeXovskými virtuózmi, ale zle sa odporúča.

Používatelia LaTeXu sa majú o niečo lepšie, kedže im stačí iba zadať počet parametrov v príkaze csx{newcommand}, ktorý definuje každú časť prevodného mechanizmu: Knuthove reštrikcie platia pre csx{newcommand} ako aj pre csx{def}. Používatelia LaTeXu však tiež majú cestu von z takejto barbarskej syntaxe príkazov – balík keyval. S keyval a trochou programovania môžete písať celkom sofistikované príkazy, ktorých vyvolanie môže vyzerať takto:

\flowerinstance{species=Primula veris,
  family=Primulaceae,
  location=Coldham's Common,
  locationtype=Common grazing land,
  date=1995/04/24,
  numplants=50,
  soiltype=alkaline
}

Výhoda takejto „ukecanosti“ je automatická zrozumiteľnosť. Sádzač si nemusí pamätať, že parameter 12 je soiltype atď. Príkazy môžu byť kopírované z poznámok k políčkam rýchlo a presne.
keyval.sty: distribuované ako súčasť CTANgraphics


Príkaz s dvomi voliteľnými parametrami

Ak ste si už prečítali Otázku Ako prekročiť limit 9 parametrov, môžete zrejme riešenie problému uhádnuť: predávanie príkazov.

LaTeX povoľuje príkazy s jediným voliteľným parametrom takto:

  \newcommand{\blah}[1][Default]{...}

Správne môžete buď s prítomným voliteľným parametrom (\blah[nonDefault]) alebo bez neho (csx{blah}). V druhom prípade bude mať csx{blah} parameter Default.

Pre definovanie príkazu s dvoma voliteľnými parametrami použijeme techniku predávania nasledovne:

  \newcommand{\blah}[1][Default1]{%
    \def\ArgI{{#1}}%
    \BlahRelay
  }
  \newcommand\BlahRelay[1][Default2]{%
    % prvý voliteľný parameter je teraz
    %   v \ArgI
    % druhý je v #1
    ...%
  }
csx{BlahRelay} môže mať samozrejme toľko povinných parametrov, koľko je povolených po zabratí jedného „miesta“ vlastným voliteľným parametrom, teda 8.

Varianty csx{newcommand} (a priateľov) s názvami ako csx{newcommandtwoopt} sú dostupné v balíku twoopt. Je však pravdepodobne lepšie, ak sa môžete písať príkazy sami, aby ste videli, prečo nie sú z programátorského hľadiska dobrým nápadom.

Príkaz s dvomi voliteľnými parametrami sa približuje hranici toho, čo je ešte rozumné: techniku môžete zjavne rozšíriť tak, aby poskytovala toľko volteľných parametrov, koľko si viete predstaviť. Pozrite si však poznámky k použitiu balíku keyval (znova v Otázke Ako prekročiť limit 9 parametrov), ktoré poskytujú alternatívny spôsob postupu.

Alternatívnym prístupom je poskytovaný programom newcommand od Scotta Pakina, ktorý vezme názov príkazu a definíciu množiny parametrov príkazu (v celkom zrozumiteľnom jazyku) a ako výstup poskytne TeXovské/LaTeXovské makrá umožňujúce definíciu príkazu. Príkaz vyžaduje, aby bol na vašom systéme nainštalovaný python.

Distribúcia twoopt obsahuje dokumentačný súbor twoopt.pdf. Podobne distribúcia newcommand obsahuje súbor newcommand.pdf.
newcommand.pdf: CTANnewcommand
twoopt.sty: distribuované ako súčasť CTANoberdiek


Úprava prezentácie čísel sekcií

Obecné problémy úpravy vzhľadu nadpisov sekcií sú dosť komplexné a sú pokryté odpoveďou na Otázku Vzhled nadpisů.

Ľudia však často chcú zmeniť iba vzhľad čísla sekcie v nadpise a niektorým z nich nevadí písanie pár makier. Táto odpoveď je pre nich.

Spôsob sadzby čísla sekcie určuje príkaz csx{@seccntformat}, ktorý dostane „názov“ (section, subsection, ...) nadpisu ako parameter. Obvykle dá na výstup iba číslo sekcie, a potom csx{quad} medzeru. Predpokladajme, že chcete dať za každé číslo sekcie (subsekcie, subsubsekcie, ...) bodku. Triviálna zmena môže byť implementovanáa jednoduchou modifikáciou príkazu:

\renewcommand*{\@seccntformat}[1]{%
  \csname the#1\endcsname.\quad
}

Veľa ľudí (z určitého dôvodu) chce bodku iba za číslo sekcie. Pre dosiahnutie tochto cielu musíme zmeniť chovanie csx{@seccntformat} podľa jeho parametru. Nasledujúca technika je tak trochu plýtvaním, ale stále je dostačujúco efektívna:

\let\@@seccntformat\@seccntformat
\renewcommand*{\@seccntformat}[1]{%
  \expandafter\let\expandafter\@tempa
    \csname @seccntformat@#1\endcsname
  \ifx\@tempa\relax
    \expandafter\@@seccntformat
  \else
    \expandafter\@tempa
  \fi
    {#1}%
}

Kód sa pozrie či bol definovaný príkaz druhej úrovne. Ak áno, použije ho, inak použije pôvodný. Príkaz druhej úrovne na zavedenie bodiek (iba) za čísla sekcií má rovnakú definíciu ako pôvodná verzia „všetky úrovne zhodne“:

\newcommand*{\@seccntformat@section}[1]{%
  \csname the#1\endcsname.\quad
}

Všimnite si, že všetky definície príkazov v tejto odpovedi sa týkajú interných príkazov LaTeXu (viď Otázku \@ a @ ve jménech maker), takže uvedený kód by mal byť radšej v súbore balíku.

Triedy koma-script majú na zmenu prezentácie čísel sekcií odlišné príkazy: csx{partformat}, csx{chapterformat} a csx{othersectionlevelsformat}, ale inak sú ich možnosti podobné čistému LaTeXu.
sada KOMA script: CTANkoma-script


Za moje prostredie je pridaná medzera

Napísali ste si vlastné prostredie env, ktoré aj funguje, až na to, že na začiatku prvého riadku textu za \end{env} je vysádzaná medzera. Toto sa s podobnými prostrediami dodávanými LaTeXom nestáva.

Mohli by ste vydať obmedzenie, že vaši užívatelia musia vždy umiestniť znak „%“ za prostredie, prostredia LaTeXu to nepotrebujú.

Tajomstvom prostredí LaTeXu je interný príznak spôsobujúci ignorovanie nechcených medzier. Našťastie internú formu používať nemusíte: od roku 1996 obsahuje LaTeX používateľský príkaz csx{ignorespacesafterend}, ktorý interný príznak nastavuje.


Definície LaTeXovských príkazov

Existuje viacero dôvodov, prečo by ste mohli chcieť vedieť definície príkazov LaTeXu: od najjednoduchšej obyčajnej zvedavosti, až po potrebu opraviť niečo, aby to fungovalo tak, ako chcete vy. Nič z uvedeného nie je čistým motívom, ale vedomosť a skúsenosť zriedkakedy prichádzajú prostredníctvom najčistejších motívov.

Najjednoduchšou odpoveďou je skúsiť csx{show} v LaTeXovskom behu berúcom príkazy z terminálu:

*\show\protected@edef
> \protected@edef=macro:
->\let \@@protect \protect
  \let \protect \@unexpandable@protect
  \afterassignment \restore@protect \edef .

(Výstup je preusporiadaný z dosť mätúcej verzie, ktorú TeX produkuje.) Teraz sa môžeme zamyslieť nad csx{@unexpandable@protect}:

*\show\@unexpandable@protect
> \@unexpandable@protect=macro:
->\noexpand \protect \noexpand .

a začíname pozorovať, ako funguje jedna časť mechanizmu csx{protect} (môžeme asi celkom bezpečne uhádnuť, čo robí csx{restore@protect}).

Mnoho príkazov jadra je deklarovaných robustne:

*\show\texttt
> \texttt=macro:
->\protect \texttt  .

takže csx{show} veľmi nepomôže. Definujte príkaz csx{pshow} podľa nasledujúcej ukážky a použite ten:

*\def\pshow#1{{\let\protect\show #1}}
*\pshow\texttt
> \texttt =\long macro:
#1->\ifmmode \nfss@text {\ttfamily #1}%
    \else \hmode@bgroup \text@command {#1}%
          \ttfamily \check@icl #1\check@icr
    \expandafter \egroup \fi .

Všimnite si, že názov chráneného príkazu je „základný“ príkaz s pripojenou medzerou. Toto je trochu obtiažne viditeľné na pár miestach vyššie. (Výstup bol znova upravený.)

Ak máte flexibilný textový editor, rovnaké zistenie môžete komfortnejšie uskutočniť prezrením súboru latex.ltx (ktorý sa dá zvyčajne v TDS systéme nájsť v adresári tex/latex/base).

Súbor latex.ltx je vlastne výstupom docstrip procesu na veľkom množstve .dtx súborov (viď Otázku Dokumentované LaTeXovské zdroje (.dtx soubory)), môžete sa teda odkazovať na ne. Distribúcia LaTeXu obsahuje súbor source2e.tex, väčšina systémov ju zachováva v tex/latex/base. Source2e.tex môže byť spracovaný, aby poskytol kompletný výpis jadra LaTeXu (proces nie je úplne priamočiary, ale súbor produkuje správy radiace, čo treba spraviť). Výsledkom je obrovský dokument s indexom čísiel riadkov riadiacich sekvencií celého jadra a oddelený index zmien zaznamenaných v každom súbore odvtedy, čo ho prevzal LaTeX tím.

Vytlačené jadro je dobrá vec, ale ťažko sa s ním manipuluje a často iba sedí na polici zriedkavo využívaný. Jedným problémom je rozdielna dokumentácia: obsiahnuté sú moduly od veľmi dobre zdokumentovaných, cez moduly obsahujúce iba automatickú dokumentáciu, až po moduly bez akejkoľvek užitočnej dokumentácie.

Každý .dtx súbor jaderného modulu bude v LaTeXu spracovaný oddelene, takže nemusíte pracovať s celým súborom source2e. Ľahko môžete zistiť, ktorý modul definuje makro, ktoré vás zaujíma: použite svoj „flexibilný textový editor“ aby ste zistili definíciu v latex.ltx, potom z toho bodu hľadajte spätne riadok začínajúci %%% From File: — tento riadok vám povie, ktorý .dtx súbor obsahuje definíciu, ktorá vás zaujíma. Pre csx{protected@edef} týmto postupom nájdeme:

%%% From File: ltdefns.dtx
Ak sa na súbor pozrieme, ltdefns.dtx obsahuje obsiahlu rozpravu o metódach spracovania ochrán (csx{protect}) a obsahuje tiež určitú automaticky skonvertovanú LaTeX 2.09 dokumentáciu.

Jadro samozrejme nie je celý LaTeX: váš príkaz môže byť definovaný v jednom zo súborov LaTeXovských tried alebo balíkov. Definíciu csx{thebibliography} napríklad nájdeme v triede article, ale súbor article.dtx neexistuje. Niektoré takéto súbory sú generované z častí jadra, niektoré z iných súborov distribúcie. Zdroj zistíte pri pohľade na začiatok súboru: v article.cls nájdeme:

%% This is file `article.cls',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% classes.dtx  (with options: `article')
takže potrebujeme formátovať súbor classes.dtx, aby sme videli definíciu v kontexte.

Všetky tieto .dtx súbory sú na CTANe ako súčasť hlavnej distribúcie LaTeXu.
\LaTeX{} distribúcia: CTANlatex


„Master“ a „slave“ počítadlá

Je bežné mať veci číslované po kapitolách (napríklad v štandardných triedach bookreport sú takto číslované obrázky, tabuľky a poznámky pod čiarou). Resetovanie sa uskutočňuje automaticky, keď sa zvýši hlavné („master“) počítadlo (keď je vykonaný príkaz csx{chapter} začínajúci kapitolu , počítadlo chapter sa zvýši a všetky závislé počítadlá sú nastavené na nulu).

Ako by ste to spravili sami? Môžete chcieť číslovať napríklad algoritmy po sekciách. Ak tieto veci definujete ručne, vzťah deklarujete pri definícii počítadla:

\newcounter{new-name}[master]

Uvedený kód nám hovorí, že pri každom zvýšení počítadle bude resetované počítadlo .

Čo ale v prípade, že máte nespolupracujúci balík definujúci veci za vás, ale tento neposkytuje programátorovi rozhranie na prinútenie počítadiel správať sa tak, ako chce?

Príkaz csx{newcounter} používa interný príkaz LaTeXu a vy ho môžete využiť tiež:

\@addtoreset{new-name}{master}

(pamätajte si však, že to musí byť medzi csx{makeatletter} a csx{makeatother} alebo vo vašom vlastnom balíku).

Balík chngcntr zapuzdruje príkaz csx{@addtoreset} do príkazu csx{counterwithin}. Takže:

\counterwithin*{corollary}{theorem}

spraví z počítadla corollary podriadené počítadlo (slave) počítadla theorem. Príkaz bez hviezdy:

\counterwithin{corollary}{theorem}

spraví to isté a zároveň redefinuje csx{thecorollary} ako <číslo teorémy>.<číslo dôsledku>, čo je dobrou schémou, ak sa niekedy chcete odkazovať na dôsledky — môže existovať veľa „dôsledkov 1“ v každom dokumente, takže je dobré zviazať jeho číslo s počítadlom theorem, ku ktorému patrí. Toto platí asi pri každom počítadle vo vnútri iného. Ak nepoužívate chngcntr, pre potrebné techniky si pozrite Otázku Predefinovanie csx{the-}príkazov počítadiel.

Všimnite si, že postup nefunguje, ak je nadriadené (master) počítadlo page, číslo aktuálnej strany. Počítadlo page je zväčšované hlboko vo vnútri výstupnej rutiny, ktorá je volaná chvíľku po tom, čo sa začal objavovať text pre novú stranu: na vysporiadanie sa s týmto sú potrebné špeciálne postupy. S jedným špeciálnym prípadom sme sa vysporiadali na inom mieste: Otázka Číslovanie poznámok „po stránkach“. Jeden z opísaných postupov v spomenutej otázke, použitie balíku perpage, môže byť aplikovaný na akékoľvek počítadlo. Príkaz:

\MakePerPage{counter}

prinúti resetovať sa na každej strane. Balík používa mechanizmus podobný návestiam a môže vyžadovať viac než jeden beh LaTeXu na stabilizovanie hodnôt počítadiel — LaTeX vygeneruje obvyklé varovania o zmene návestí.
chngcntr.sty: CTANchngcntr
perpage.sty: CTANperpage


Ovládanie vdov a sirôt

Vdovy (posledné riadky odstavcov na začiatku strany) a siroty (prvé riadky odstavcov na konci strany) prerušujú čitateľa a obecne sú považované za zlú formu; TeX/LaTeX vykonáva určité opatrenia, aby sa im vyhol, úplne automatická prevencia je často nemožná. Ak sádzate vlastný text, zvážte miernu zmenu slov tak, aby zlom dopadol inak. Rutina pre vytváranie stránky pri jej formovaní berie na vedomie csx{widowpenalty} a csx{clubpenalty} (vzťahuje sa k sirotám!). Tieto pokuty (penalties) sú obvykle nastavené na strednú hodnotu 150; toto mierne odradzuje od zlých zlomov. Hodnoty môžete zvýšiť výrazom (napríklad) csx{widowpenalty}=500; vertikálne zoznamy (z ktorých sú zhotovené stránky) sa typicky dajú veľmi slabo roztiahnuť alebo stlačiť, takže rutina musí zvážiť efekt roztiahnutia neroztiahnuteľného a pokuty, pokuta málokedy vyhraje. Tomuto rozporu sa dá predísť povolením rutine skrátiť stránku použitím direktívy csx{raggedbottom}; mnoho vydavateľov však trvá na štandardnom csx{flushbottom}; je málokedy akceptovateľné priviesť do vertikálneho zoznamu roztiahnuteľnosť, okrem bodov (ako nadpisy sekcií), kde to dizajn dokumentu explicitne povoľuje.

Keď ste už vyčerpali automatické opatrenia a máte konečný návrh, ktorý chcete „vypilovať“, musíte použiť ručné opatrenia. Zbaviť sa siroty je jednoduché: pred odstavec uveďte csx{clearpage} a odstavec nemôže začať na zlom mieste.

Zbaviť sa vdovy je komplikovanejšie. Ak je odstavec dlhý, mohlo by sa dať vysádzať ho „tesne“: vložte csx{looseness}=-1 bezprostredne za posledné slovo odstavca. Ak to nefunguje, skúste zmeniť veľkosť stránky: csx{enlargethispage}\{baselineskip} by mohlo pomôcť a dostať celý odstavec na jednu stranu. Zmenšením veľkosti stránky pomocou

  \enlargethispage{-\baselineskip}

môže vytvoriť (viac-menej) akceptovateľnú „dvojriadkovú vdovu“. (Všimnite si: csx{looseness}=1 zvyšujúc dĺžku strany o jednotku málokedy funguje — dotyčný odstavec má zvyčajne jednoslovný posledný riadok, ktorý nevyzerá oveľa lepšie ako priamo vdova.)


Proč LaTeX dělí poznámky pod čarou na

více stránek?}

LaTeX dělí poznámky pod čarou na více stránek, pokud si myslí, že to je to nejlepší, co může udělat. Typicky se tak stane, pokud poznámka pod čarou vyjde na samotný konec stránky, a tím ji může přeplnit. LaTeX by se mohl pokusit problém vyřešit ochuzením stránky o poznámku a taky řádek obsahující značku poznámky, ale jeho priority mu poradí, že rozdělení rozdélení poznámky je preferováno.

Jako vždy je nejlepším řešením problému změnit váš text tak, aby se v něm tento problém nevyskytl. Uvažujte, zda by se poznámka ve vašem textu mohla objevit na předchozí či další stránce. Pokud to není možné, můžete docílit toho, že LaTeX začne jinak „pohlížet“ na tyto priority: ty jsou ovládané příkazem \interfootnotelinepenalty — čím má větší parametr, tím méně LaTeX rozděluje poznámky. Nastavením \interfootnotelinepenalty=10000 (základní hodnota je 100) zcela zabráníte zabráníte rozdělování poznámek pod čarou. To ale vyvolá hlášku „Underfull \vbox“ pokud nezadáte \raggedbottom.

Alternativní technika spočívá v malém podvodu na LaTeX, kdy pomocí parametrů příkazu \enlargethispage změníte velikost aktuální stránky (např. mu můžete dát na vstup \enlargethispage{\baselineskip} pro přidání jednoho řádku na tuto stránku, můžete však použít jakoukoliv běžnou TeXovou jednotku délky, třeba 15mm nebo -20pt). Zmenšení velikosti běžné stránky si může vynutit useknutí textu a jeho přesunutí na další stránku; zvětšení stránky může způsobit, že se poznámky spojí do jednoho celku. To může být nevyhnutelné, pokud chcete změnit velikost více než jedné stránky.
fnbreak.sty: CTANfnbreak


Jak dostat \marginpar na správnou stranu?

V ideálním světé by okrajové poznámky byly na analogických místech na každé stránce: na sudých stránkách v levém okraji, na lichých v pravém. Hned vidíme, že poznámka vlevo musí být vysázena jinak než poznámka napravo. LaTeXovský příkaz csx{marginpar} proto v oboustraných (twoside) dokumentech akceptuje dva parametry:

\marginpar[left text]{right text}

LaTeX používá „zjevný“ test, aby dostal csx{marginpar} na správný okraj, ale úskalí je v tom, že vytváří stránky asynchronně. Když je csx{marginpar} spracován, zatímco je budována stránka ensuremath{n}, ale není použit až do stránky ensuremath{n}+1, pak se csx{marginpar} objeví na špatném okraji stránky. Toto je instance obecnějšího problému: viz Zistenie, či ste na párnej alebo nepárnej strane.

Řešením tohoto problému by mohlo být zapamatování si, na kterou stranu stránky má být použit \marginpar. Toto umí balík CTANmparhack, který využívá značky uložené v .aux souboru.


Kam zmizli moje písmená?

Napísali a spracovali ste zjavne zmysluplný text, ale výsledok neobsahuje ani stopu po niektorých písmenách, ktoré ste napísali. Pravdepodobným dôvodom je, že vybraný font neobsahuje reprezentáciu dotyčných znakov.

Ak napríklad napíšem „that will be GBP{}44.00“ do obyčajného TeXovského/LaTeXovského dokumentu alebo si vyberiem font rsfs10 (obsahujúci iba veľké písmená) a napíšete takmer čokoľvek, znak libry (GBP{}) alebo akékoľvek malé písmená alebo číslice budú vo výstupe chýbať. Chybové hlásenie neexistuje, musíte si prečítať log súbor, kde nájdete záhadné malé správy ako

Missing character:
           There is no ^^a3 in font cmr10!
Missing character:
             There is no 3 in font rsfs10!

(prvá demonštruje neochotu môjho TeXu vysporiadať sa so znakmi s osembitovou znakovou sadou, zatiaľ čo príklad rsfs10 ukazuje, že TeX zaznamenáva dotyčný chybný znak, ak si myslí, že je to možné).

Trochu lepšie pochopiteľné sú diagnostiky, ktoré dostanete z dvips, keď používate OT1 a T1 verzie fontov dodávaných v štandardnom kódovaní Adobe:

dvips: Warning: missing glyph `Delta'

Proces generujúci metriky pre používanie fontu generuje inštrukciu pre dvips, aby produkovalo tieto diagnostiky, aby ich neprítomnosť v tlačenom výstupe bola menej prekvapujúca, než by mohla byť. Dosť glyfov poskytnutých v Knuthovych textových kódovaniach a v Corkovskom kódovaní nie je v Adobe fontoch dostupných. V týchto prípadoch existuje vysádzaný symbol týchto znakov: dvips vytvára čierny obdĺžnik veľkosti fontu.


Príkazy požierajú nasledujúce medzery

Ľudia sú stále prekvapení, že jednoduché príkazy požierajú medzeru, ktorá za nimi nasleduje: je to jednoducho tak. Efekt vzniká pre spôsob práce TeXu, Lamport popisuje riešenie (umiestnite jedny zložené zátvorky za vyvolanie príkazu) v popise syntaxe LaTeXu. Takže požiadavka je vlastne súčasťou definície LaTeXu.

Anglické FAQ je napríklad napísané s definíciami vyžadujúcimi písanie csx{fred{relax}} pre takmer všetky vyvolania makier bez ohľadu na to, či je nasledujúca medzera potrebná: anglické FAQ je však písané veľmi oddanými (a podľa niekoho zvláštnymi) ľuďmi. Mnoho používateľov si myslí, že písanie tých všetkých zložených zátvoriek sa veľmi rýchlo stane veľmi únavným, a radšej by ich vôbec nepísali.

Alternatívnou štruktúrou, ktorá neporušuje návrh LaTeXu, je napísať csx{fred}csx{ } — príkaz csx{ } je „samoukončovací“ (ako \{}\) zložené zátvorky za neho už písať nemusíte. Znížite teda počet extra znakov na jeden.

Ak je aj ten jeden znak priveľa, balík xspace definuje príkaz csx{xspace}, ktorý háda, či by za ním mala byť medzera, a ak si myslí, že mala, vloží ju. Takže „fredcsx{xspace jim}“ vytvorí „fred jim“, zataiľ čo „fredcsx{xspace.@ jim}“ vytvorí „fred. jim“. Jeho použitie by samozrejme bolo úplne nezmyselné, csx{xspace} ale môžete zabudovať do vlastných makier:

\usepackage{xspace}
...
\newcommand{\restenergy}%
           {\ensuremath{mc^2}\xspace}
...
and we find \restenergy available to us...

Príkaz csx{xspace} musí byť poslednou vecou v definícii vášho makra (ako v príklade); nie je to úplne bezchybné, ale poradí si s väčšinou situácii v texte.

Balík xspace vám nič neušetrí, ak modifikované makro použijete raz alebo dvakrát v dokumente. V každom prípade buďte pri použití csx{xspace} opatrní — zmení vašu vstupnú syntax, čo môže byť mätúce, hlavne pre spolupracujúceho autora (predovšetkým ak vytvoríte nejaké príkazy, ktoré to používajú, a nejaké, ktoré nie). Žiadny príkaz zabudovaný do LaTeXu alebo akejkoľvek triedy, prípadne balíku, samozrejme csx{xspace} používať nebude.
xspace.sty: distribuované ako súčasť CTAN2etools


Matematické symboly sa nezvetšujú

„Veľké“ matematické symboly štandardne zostávajú rovnakej veľkosti nezávisle na veľkosť fontu textu dokumentu. Existuje pre to dobrý dôvod: fonty cmex nie sú navrhnuté na zmenu veľkosti, takže TeXovský algoritmus umiestňovania matematiky nepracuje tak dobre, ako by mohol, ak by boli fonty zväčšovateľné.

Toto správanie však mätie očakávania používateľov a môže viesť k trochu zvláštne vyzerajúcim dokumentom. Ak napriek varovanie chcete, aby sa fonty dali zväčšovať, použite balík exscale — stačí ho iba nahrať.
exscale.sty: súčasť distribúcie LaTeXu.


Prečo používať fontenc namiesto t1enc?

V raných časoch LaTeX 2e bol jediním spôsobom používania T1 kódovania balík t1enc. S vydaním z leta 1994 sa objavil balík fontenc a poskytol vyčerpávajúcu podporu použitia kódovania.

Napriek tomu tu balík t1enc zostáva (ako súčasť kódu kompatibility LaTeX 2.09), ale robí veľmi málo: iba vyberie kódovanie fontov T1 a na používateľovi nechá záležitosti generovania potrebných kódov znakov.

Generovanie takýchto kódov znakov by mohla byť jednoduchá záležitosť, keby T1 kódovanie zodpovedalo ľubovolnému široko podporovanému kódovaciemu štandardu, pretože v takomto prípade by ste mohli očakávať generovanie kódov znakov klávesnicou. T1 kódovanie je však zmesou rôznych štandardných kódovaní a zahŕňa kódové miesta v oblastiach tabuľky, ktoré štandardné kódovanie špecificky vylučujú, takže žiadne T1 klávesnice neboli (a nikdy nebudú) vyrobené.

Balík fontenc naproti tomu generuje kódové miesta T1 z bežných LaTeX príkazov (generuje napríklad kódové miesto znaku 'e z príkazu \'{}e). Pokiaľ teda nemáte programovo generovaný T1 vstup, použite \usepackage[T1]{fontenc} namiesto \usepackage{t1enc}.


Prečo sa trápiť s inputencfontenc?

Štandardné vstupné kódovanie pre západnú Európu (čakajúc príchod Unicode) je ISO 8859–1 (bežne známe podtitulom štandardu „Latin-1“). Latin-1 je v pokrytí kódových miest pozoruhodne blízko TeXovskému T1 kódovaniu.

Prečo by sme sa mali za týchto okolností trápiť s inputencfontenc? Kedže sa v podstate kopírujú, mohli by sme ich odstrániť a použiť t1enc (napriek jeho nedostatkom — viď Otázku Prečo používať fontenc namiesto t1enc?).

Nerobí sa to ale pre množstvo drobných dôvodov:

  • Zmätenie Spokojne ste v tomto móde písali a z  nejakého dôvodu prepnete na písanie v nemčine: efekt použitia „ss“ je tak trochu prekvapivý, kedže T1 a Latin-1 s kódovým miestom zaobchádzajú inak.
  • Kompatibilita Zistíte, že potrebujete pracovať s kolegom vo východnej Európe: ich klávesnice bude zrejme nastavená tak, aby produkovala Latin-2, takže jednoduché mapovanie nefunguje.
  • Tradičný LaTeX Napíšete niečo ako \'{}e namiesto 'e; iba fontenc má prostriedky pre konverziu tejto LaTeXovskej sekvencie do T1 znaku, takže primitívum csx{accent} prekĺzne do výstupu a rozdeľovanie je ohrozené.
Kombinácia inputencfontenc vyzerá pomalá a ťažkopádna, je však bezpečná.


Prečo nepoužiť eqnarray?

Prostredie eqnarray je pre príležitostného používateľa matematiky v LaTeXovských dokumentoch atraktívne: vyzerá, že povoľuje zarovnané systémy rovníc. Skutočne, tieto veci prostredie poskytuje, ale v rozostupoch znakov urobí pekný neporiadok. V systéme:

\begin{eqnarray}
  a & = & b + c \\
  x & = & y - z
\end{eqnarray}

nie sú rozostupy okolo znakov `=' tie definované v metrike fontu, z ktorého pochádza glyf — sú to csx{arraycolsep}, čo môže byť nastavené na veľmi divnú hodnotu z dôvodov spojených so skutočnými poliami na inom mieste dokumentu.

Používateľa oveľa lepšie obslúži sada AMSLaTeX, ktorá poskytuje prostredie align navrhnuté rešpektujúc potreby matematikov (oproti pohodliu LaTeXovských programátorov). Pre tento jednoduchý dôvod (align je schopné oveľa lepších vecí) použite:

\begin{align}
  a & = b + c \\
  x & = y - z
\end{align}

AMSLaTeX: CTANamslatex


Prečo používať csx{[} ...csx{]} namiesto $$...$$?

LaTeX definuje príkazy „inline“ a „display“ matematiky, zjavne analogické k tým, čo sú odvodené z TeXovských príkazov na zátvorkovanie matematických sekvencií so znakmi doláru (alebo dvojicami znakov doláru).

Ako sa ukázalo, LaTeXovské inline zoskupovanie matematiky csx{(} ... csx{)} má presne rovnaký účinok ako TeXovská primitívna verzia $ ... $. (Jediný rozdiel je, že LaTeXovská verzia kontroluje, či ste nepoužili csx{(} a csx{)} zle.)

Za týchto okolností sa často nájdu používatelia LaTeXu majúci nejaké skúsenosti s používaním Plain TeXu, ktorí jednoducho predpokladajú, že LaTeXovské zoskupovanie display matematiky csx{[} ... csx{]} môže byť vymenené TeXovským primitívnou display matematikou $$ ... $$.

Bohužiaľ sa mýlia: ak LaTeXovský kód bude upravovať display matematiku, môže tak učiniť iba úpravou csx{[} a csx{]}. Najzjavnejším spôsobom, ako sa to prejaví, je, že voľba triedy fleqn jednoducho nefunguje pre rovnice napísané pomocou $$ ... $$, či už používate iba štandardné triedy, alebo balík amsmath.

Existujú aj zákernejšie efekty (hlavne s balíkom amsmath), jednoduché pravidlo je csx{[} ... csx{]}, kedykoľvek je v LaTeXu potrebná neozdobená display matematika.


Proč nemůžu nahrát PiCTeX

PiCTeX je poměrně náročný na systémové zdroje. Naštěstí většina moderních TeXových distribucí nabízí poměrně dost prostoru a moderní počítače již jsou oproti svým předchůdcům o mnoho rychlejší, takže uživatele tato skutečnost nemusí znervózňovat. Nicméně PiCTeX má jednu nešťastnou tendenci – rád zaplňuje pole s pevnou délkou alokovaná TeXem – zvláště 256 „rozměrových“ registrů. To je problém zvláště, pokud v LaTeXu používáte pictex a další balíky, které potřebují stejné systémové zdroje. Pokud k tomu dojde, pak vám LaTeX vypíše chybovou hlášku
 ! No room for new \dimen.
S touto chybou nelze nic udělat: nemůžete totiž zvýšit počet dostupných rozměrových registrů, aniž by to udělal sám TeX (toto umí automaticky e-TeX a Omega – viz Otázku Projekt Omega a Otázku Projekt NTS a VTeX od firmy MicroPress Inc. – viz Otázku Komerční implementace TeXu). Je celkem praktické (u většiny moderních distribucí) použít rozšířenou sadu registrů e-TeXu: použijte balík etex (kterí se dodává s distribucemi e-TeXu) a alokační mechanismus je pozměněn, aby se vyspořádal s vétšími sadami registrů: PiCTeX se teď nahraje.

Když nemůžete použít e-TeX, musíte změnit PiCTeX; bohužel jeho autor není již delší dobu v TeXovém světě aktivní, proto se musíte uchýlit k „patchování“. Dostupná jsou dvě řešení:

  • CONTeXtové moduly m-pictex.tex (pro Plain-TeX a jeho varianty) a m-pictex.sty (pro LaTeX; oba soubory jsou dostupné v souboru CTANcont-tmf.zip). Soubor m-pictex.tex nabízí důmyslné řešení založené na úpravě kódu příkazu \newdimen.
  • Alternativně pictexwd Andrease Schella (CTANaddon) nahrazuje PiCTeX verzí, které používá o 33 „rozměrových“ registrů méně. Takže místo souborů pictex používejte pictexwd.
A jak může někdo použít PiCTeX, když je těžké sehnat manuál (viz Otázku Manuál k programu PiCTeX)? Naštěstí pro uživatele MSDOSu a Windows, může být MathsPic (CTANmathspic) použit pro přeložení z jiného jazyka do instrukcí PiCTeXu a manuál k MathsPicu je přímo součástí distribuce. MathsPic je napsán v Basicu; Perlová verze by měla být dostupná během roku 2001.


CSTUG
(c) 1997, 1998, 2003 Tomáš Hudec, Libor Škarvada
Poslední aktualizace: 13.11.2004 23:15