% Self-documenting file \ifx \Comment \Undefined \long \def \Comment #1\EndComment {} \else \ifx \EndComment \Undefined \def \EndComment {\message {Error: \noexpand \EndComment encountered without preceding \noexpand \Comment}} \else %%% both defined; let's hope they're the right definitions! \fi \fi \Comment This file, "letterspacing.tex", created by Philip Taylor of Royal Holloway, University of London (P.Taylor@Rhul.Ac.Uk) for Kaveh Bazargan of Focal Image (Kaveh@Focal.Demon.Co.Uk) may be freely distributed provided that the following licence conditions are honoured : \EndComment \Comment % The Licence Unlimited copying and redistribution of this file is permitted so long as the file is not modified in any way. Modifications may be made for private purposes (though this is discouraged, as it could result in documents typesetting differently on different systems) but if such modifications are re-distributed, the modified file must not be capable of being confused with the original. In particular, this means (a) the filename (the portion before the extension, if any) must not match any of : LETTERSPACE LETTER-SPACE LETTERSPACING LETTER-SPACING regardless of case, and (b) the file must contain conditions identical to these, except that the modifier/distributor may, if he or she wishes, augment the list of proscribed filenames. \EndComment \Comment As far as is possible, `inaccessible' control sequences (i.e.~control sequences containing one or more commercial-at$\,$s) are used to minimise the risk of accidental collision with user-defined macros; however, the control sequences used to access the natural dimensions of the text are required to be user accessible, and therefore must contain only letters. \EndComment %%% Open control sequences \newdimen \naturalwidth \newdimen \naturaldepth \newdimen \naturalheight \Comment All control sequences defined hereafter are inaccessible to the casual user, including the control sequence used to store the category code of commercial-at; this catcode must be saved in order to be able to re-instate it at end of module, as we have no idea in what context the module will be |\input|. Having saved the catcode of commercial-at, we then change it to |11| (i.e.~that of a letter) in order to allow it to be used in the cs-names which follow. \EndComment %%% Concealed control sequences \expandafter \chardef \csname \string @code\endcsname = \the \catcode `\@ \catcode `\@ = 11 \Comment We will need a a box register in order to measure the natural dimensions of the text, and a token-list register in which to save the tokens to be letter-spaced. \EndComment \newbox \l@tterspacebox \newtoks \l@tterspacetoks \Comment We will need to test whether a particular macro expands to a space, so we will need another macro which does so expand with which to compare it; we will also need a `more infinite' variant of |\hss|. \EndComment \def \sp@ce { } \def \hsss {\hskip 0 pt plus 1 fill minus 1 fill\relax} \Comment Many of the macros will used delimited parameter structures, typically terminated by the reserved control sequence |\@nd|; we make this a synonym for the empty macro (which expands to nothing), so that should it accidentally get expanded there will be no side effects. We also define a brief synonym for |\expandafter|, just to keep the individual lines of code reasonably short. \EndComment \let \@nd = \empty \let \@x = \expandafter \Comment We will also need to compare a token which has been peeked at by |\futurelet| with a space token; because of the difficulty of accessing such a space token (which would usually be absorbed by a preceding control word), we establish |\sp@cetoken| as a synonym. The code to achieve this is messy, because of the very difficulty just outlined, and so we `waste' a control sequence |\t@mp|; we then return this to the pool of undefined tokens. \EndComment \let \sp@cetoken = \relax \edef \t@mp {\let \sp@cetoken = \sp@ce} \t@mp \let \t@mp = \undefined \Comment The user-level macro |\letterspace| has exactly the same syntax as that for |\hbox| and |\vbox|, as explained in the introduction; the delimited parameter structure for this macro ensures that everything up to the open brace which delimits the beginning of the text to be letter-spaced is absorbed as parameter to the macro, and the brace-delimited text which follows is then assigned to the token-list register |\l@tterspacetoks|; |\afterassignment| is used to regain control once the assignment is complete. \EndComment %%% Primary (user-level) macro \def \letterspace #1#% {\def \hb@xmodifier {#1}% \afterassignment \l@tterspace \l@tterspacetoks = } \Comment Control then passes to |\l@tterspace|, which starts by setting an |\hbox| containing the text to be typeset; the dimensions of this box (and therefore of the text) are then saved in the open control sequences previously declared, and the |\hbox| becomes of no further interest. A new |\hbox| is now created, in which the same text, but this time letter-spaced, will be set; the box starts and ends with |\hss| glue so that if only a single character is to be letter-spaced, it will be centered in the box. If two or more characters are to be letter-spaced, they will be separated by |\hsss| glue, previously declared, which by virtue of its greater degree of infinity will completely override the |\hss| glue at the beginning and end; thus the first and last characters will be set flush with the edges of the box. The actual mechanism by which letter-spacing takes place is not yet apparent, but it will be seen that it is crucially dependent on the definition of |\l@tt@rspace|, which is expanded as the box is being set; the |\@x| (|\expandafter|) causes the actual tokens stored in |\l@tterspacetoks| to be made available as parameter to |\l@tt@rspace| rather than the token-list register itself, as it is these tokens on which |\l@tt@rspace| will operate. The |\@nd| terminates the parameter list, and the |{}| which immediately precedes it ensures that that there is always a null element at the end of the list: without this, the braces which are needed to protect an accent/character pair would be lost if such a pair formed the final element of the list, at the point where they are passed as the second (delimited) parameter to |\p@rtiti@n|; by definition, removes the outermost pair of braces from both simple and delimited parameters during parameter substitution if such braces form the first and last tokens of the parameter, and thus if a brace-delimited group ever becomes the second element of a two-element list, the braces will be irrevocably lost. The |{}| ensure that such a situation can never occur. \EndComment %%% Secondary (implementation-level) macro \def \l@tterspace {\setbox \l@tterspacebox = \hbox {\the \l@tterspacetoks}% \naturalwidth = \wd \l@tterspacebox \naturaldepth = \dp \l@tterspacebox \naturalheight = \ht \l@tterspacebox \hbox \hb@xmodifier \bgroup \hss \@x \l@tt@rspace \the \l@tterspacetoks {}\@nd \hss \egroup } \Comment The next macro is |\l@tt@rspace|, which forms the crux of the entire operation. The text to be letter-spaced is passed as parameter to this macro, and the first step is to check whether there is, in fact, any such text; this is necessary both to cope with pathological usage (e.g.~|\letterspace {}|), and to provide an exit route, as the macro uses tail-recursion to apply itself iteratively to the `tail' of the text to be letter-spaced; when no elements remain, the macro must exit. Once the presence of text has been ensured, the token-list representing this text is partitioned into a head (the first element), and the tail (the remainder); at each iteration, only the head is considered, although if the tail is empty (i.e.~the end of the list has been reached), special action is taken. If the first element is a space, it is treated specially by surrounding it with two globs of |\hsss| glue, to provide extra stretchability when compared to the single glob of |\hsss| glue which will separate consecutive non-space tokens; otherwise, the element itself is yielded, followed by a single glob of |\hsss| glue. This glue is suppressed if the element is the last of the list, to ensure that the last token aligns with the edge of the box. When the first element has been dealt with, the macro uses tail recursion to apply itself to the remaining elements (i.e.~to the tail); |\@x| (|\expandafter|) is again used to ensure that the tail is expanded into its component tokens before being passed as parameter. \EndComment %%% Tertiary (implementation-level) macro \def \l@tt@rspace #1\@nd {\ifx \@nd #1\@nd \let \n@xt = \relax \else \p@rtition #1\@nd \ifx \h@ad \sp@ce \hsss \h@ad \hsss \else \h@ad \ifx \t@il \@nd \else \hsss \fi \fi \@x \def \@x \n@xt \@x {\@x \l@tt@rspace \t@il \@nd}% \fi \n@xt } \Comment The operation of token-list partitioning is conceptually simple: one passes the token list as parameter text to a macro defined to take two parameters, the first simple and the second delimited; the first element of the list will be split off as parameter-1, and the remaining material passed as parameter-2. Unfortunately this na\"\i ve approach fails when the first element is a bare space, as the semantics of prevent such a space from ever being passed as a simple parameter (it could be passed as a delimited parameter, but as one does not know what token follows the space, defining an appropriate delimiter structure would be tricky if not impossible). The technique used here relies upon the adjunct macro |\m@kespacexplicit|, which replaces a leading bare space by a space protected by braces; such spaces may legitimately be passed as simple parameters. Once that operation has been completed, the re-constructed token list is passed to |\p@rtiti@n|, which actually performs the partitioning as above; again |\@x| (|\expandafter|) is used to expand |\b@dy| (the re-constructed token list) into its component elements before being passed as parameter text. \EndComment %%% Adjunct macros -- list partitioning \def \p@rtition #1\@nd {\m@kespacexplicit #1\@nd \@x \p@rtiti@n \b@dy \@nd } \def \p@rtiti@n #1#2\@nd {\def \h@ad {#1}% \def \t@il {#2}% } \Comment The operation of making a space explicit relies on prescience: the code needs to know what token starts the token list before it knows how to proceed. Prescience in is usually accomplished by |\futurelet|, and this code is no exception: |\futurelet| here causes |\h@ad| to be |\let| equal to the leading token, and control is then ceded to |\m@kesp@cexplicit|. The latter compares |\h@ad| with |\sp@cetoken| (remember the convolutions we had to go through to get |\sp@cetoken| correctly defined in the first place), and if they match (i.e.~if the leading token is a space), then |\b@dy| (the control sequence through which the results of this operation will be returned) is defined to expand to a protected space (a space surrounded by braces), followed by the remainder of the elements; if they do not match (i.e.~if the leading token is \stress {not} a space), then |\b@dy| is simply defined to be the original token list, unmodified. If the leading token \stress {was} a space, it must be replaced by a protected space: this is accomplished by |\pr@tectspace|. The |\pr@tectspace| macro uses a delimited parameter structure, as do most of the other macros in this suite, but the structure used here is unique, in that the initial delimiter is a space. Thus, when a token-list starting with a space is passed as parameter text, that space is regarded as matching the space in the delimiter structure and removed; the expansion of the macro is therefore the tokens remaining once the leading space has been removed, preceded by a protected space |{ }|. \EndComment %%% Adjunct macros -- ... -> {}... \def \m@kespacexplicit #1\@nd {\futurelet \h@ad \m@kesp@cexplicit #1\@nd} \def \m@kesp@cexplicit #1\@nd {\ifx \h@ad \sp@cetoken \@x \def \@x \b@dy \@x {\pr@tectspace #1\@nd}% \else \def \b@dy {#1}% \fi }% \@x \def \@x \pr@tectspace \sp@ce #1\@nd {{ }#1} \Comment The final step is to re-instate the category code of commercial-at. \EndComment %%% re-instate category code of commercial-at \catcode `\@ = \the \@code \Comment Thus letter-spacing is accomplished. The author hopes both that the code will be found useful and that the explanation which accompanies it will be found informative and interesting. \EndComment