1

Is there any way a macro can know either of the following?

  • The last printable character TeX read from the source file
  • The contents of the last character box TeX produced

Rationale: I am working on a XeLaTeX package that will support Stone Sans Phonetic, an old commercial IPA font. Stone Sans Phonetic was created before the Unicode era, and handles accents using ligatures rather than combining characters — that is, to put an ø̂ on the page, you write code that prints the characters ø and ^ side-by-side, and the font has a ligature that combines them.

Unfortunately, some ligatures are missing. For instance, printing n and ^ side-by-side doesn't put an on the page.

What I'd like to do, then, is use \newunicodechar to make a Unicode combining circumflex trigger a macro that does the following:

  1. Determine whether the character to its left was one that will produce a ligature with ^.
  2. If it was, print a ^ in Stone Sans Unicode.
  3. If it wasn't, print a Unicode combining circumflex in some fallback font.

Is this feasible?

(FWIW, I would have expected \lastbox to do what I'm looking for, but this code

\documentclass{article}
\begin{document}
\newbox\temp
a\setbox\temp=\lastbox
\showbox\temp
\end{document}

shows \temp to be void. What am I missing, and/or is there a different way to do what I need?)

  • Welcome to TeX.SX! Could you please provide a MWE (minimal, but compilable code example) to help us help you? – TeXnician Oct 23 '17 at 13:00
  • Ok, I've expanded the last code block in my question to be compilable on its own. The behavior I wanted, with that code saved as test.tex, is that after running xelatex test.tex the file test.log would show \temp to contain a letter a. The behavior I saw is that \temp was shown to be void. I think this means I've misunderstood what \lastbox does, and I'd like to know if there is some other way of getting the behavior I wanted. – Leah Velleman Oct 23 '17 at 13:05
  • 1
    As far as i know, this kind of back-referencing on letter level is not possible in TeX. The only way i know of, is to write a macro which takes the next two tokens as arguments (that is, your letter and the diacritic) and to internally evaluate your character set against them, but i think this is not suitable for your purpose, is it? – Lupino Oct 23 '17 at 13:09
  • 1
    A switch to lualatex is probably not an option, is it? – TeXnician Oct 23 '17 at 13:14
  • I'm making this package in the hope that it will be useful to others, so if lualatex offers better support for this feature I'd definitely look at taking advantage of it.

    In xelatex I do want to preserve the behavior that Unicode input Just Works as often as possible, so if xelatex doesn't offer a way to examine the last character, it probably makes sense to always use combining accents in a fallback font under xelatex.

    – Leah Velleman Oct 23 '17 at 14:43
  • The way to do that in xetex is probably to define a .vf virtual font that has the accents and use that , or to fix the font in fontforge or similar – David Carlisle Oct 23 '17 at 17:02
  • 1
    characters do not make boxes so \lastbox does nothing to help here – David Carlisle Oct 23 '17 at 17:07
  • With lualatex you could try to build a combo font (see e.g. https://tex.stackexchange.com/questions/371647/call-a-luatex-combo-font-through-nfss). But I have some doubt that one can pull in combining accents. – Ulrike Fischer Oct 23 '17 at 20:57

2 Answers2

1

This uses the tokcycle package to save the current token in the input stream, so that if the next token is a ^, the saved token can be compared for ligature processing.

Without working with your actual font and problem, it is hard to say if this method will suffice.

In this MWE, I implement artificially constructed ligatures for d^ and n^, within the \StoneSansLigs environment.

\documentclass{article}
\def\svd{d}
\def\svn{n}
\usepackage{tokcycle}
\def\lastsslchar{}
\tokcycleenvironment\StoneSansLigs
{%
\ifx##1^
  \ifx\svd\lastsslchar\relax\addcytoks{$\hat{\textrm{d}}$}\fi% d^ ligature
  \ifx\svn\lastsslchar\relax\addcytoks{$\hat{\textrm{n}}$}\fi% n^ ligature
  \gdef\lastsslchar{}%
\else
  \addcytoks[1]{\lastsslchar}%
  \gdef\lastsslchar{##1}%
\fi
}
{\addcytoks[1]{\lastsslchar}\processtoks{##1\expandafter}%
  \addcytoks[1]{\lastsslchar}}
{\addcytoks[1]{\lastsslchar##1}\gdef\lastsslchar{}}
{\addcytoks[1]{\lastsslchar##1}\gdef\lastsslchar{}}
\begin{document}
\StoneSansLigs
abd^c \textbf{pdd^q} n^m
\endStoneSansLigs
\end{document}

enter image description here

0

Characters do not make boxes, so \lastbox does not remove them from the current list. In fact there is really no way in classic tex or xetex to see the previous character (luatex would be a possibility).

One solution would be to make a virtual font (.vf) that had all the required ligatures, but a simpler and perhaps acceptable method is to use markup \zzhat here that adds appropriate commands in each case.

Here I have assumed that the font has an a^ ligature (but the output is wrong in this case as I'm using latin modern) and for any characters not defined to use a ligature, a fallback (using the standard \^ command) is used, as shown on o here.

enter image description here

\documentclass{article}

\def\zhat#1{%
\ifcsname zhat-\string#1\endcsname
 \csname zhat-\string#1\endcsname
\else
  \^{#1}%
\fi}

%if a^ ligature is there and o^ is not....

% add lines for all characters to use ligature (or any other character-specific code)
\expandafter\edef\csname zhat-a\endcsname{a\string^}

\begin{document}


zzz \zhat{a} \zhat{o} zzz

\end{document}
David Carlisle
  • 757,742