Last active
January 28, 2023 01:42
-
-
Save hmenke/6e8ff7c90a5e5df3c4895f60059a2ef7 to your computer and use it in GitHub Desktop.
Emulate XeTeX font primitives in LuaTeX
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\ifdefined\directlua | |
\input XeTeXemulate.sty | |
\input luaotfload.sty | |
\XeTeXemulate | |
\fi | |
\font\1="[lmroman10-italic.otf]" at 10pt\1 | |
\font\2=cmr10 at 10pt | |
\edef\x{\the\XeTeXfonttype\1}“\x” | |
\edef\x{\the\XeTeXfonttype\2}“\x” | |
\edef\x{\the\XeTeXversion}“\x” | |
\edef\x{\XeTeXrevision}“\x” | |
\edef\x{\char\XeTeXfirstfontchar\1}“\x” | |
\edef\x{\char\XeTeXfirstfontchar\2}“\x” | |
\edef\x{\char\XeTeXlastfontchar\1}“\x” | |
\edef\x{\char\XeTeXlastfontchar\2}“\x” | |
\edef\x{\XeTeXglyph150}“\x” | |
\edef\x{\the\XeTeXcountglyphs\1}“\x” | |
\edef\x{\the\XeTeXcountglyphs\2}“\x” | |
\edef\x{\XeTeXglyphname\1 150}“\x” | |
\edef\x{\the\XeTeXglyphindex"acircumflexgrave"\relax}“\x” | |
\edef\x{\the\XeTeXglyphindex"acircumflexgrave" }“\x” | |
\edef\x{\the\XeTeXcharglyph"00A5}“\x” | |
\edef\x{\the\XeTeXglyphbounds 1 \XeTeXcharglyph`f}“\x” | |
\edef\x{\the\XeTeXglyphbounds 2 \XeTeXcharglyph`f}“\x” | |
\edef\x{\the\XeTeXglyphbounds 3 \XeTeXcharglyph`f}“\x” | |
\edef\x{\the\XeTeXglyphbounds 4 \XeTeXcharglyph`f}“\x” | |
\edef\x{[\the\XeTeXglyphindex yen ] x}“\x” | |
\edef\x{[\the\XeTeXglyphindex "yen" ] x}“\x” | |
\edef\x{[\the\XeTeXglyphindex"yen"\relax] x }“\x” | |
\edef\x{[\the\XeTeXglyphindex"yen"] x }“\x” | |
\edef\x{[\the\XeTeXglyphindex "yen" *2]}“\x” | |
\edef\x{[\the\XeTeXglyphindex "yen"\relax*2]}“\x” | |
\edef\x{[\XeTeXglyphindex"yen"] }“\meaning\x” | |
\edef\x{[\XeTeXglyphindex] }“\meaning\x” | |
\bye |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\ifcsname ProvidesPackage\endcsname | |
\NeedsTeXFormat{LaTeX2e} | |
\ProvidesPackage{XeTeXemulate}[2018/09/03 v0.0 Emulate the XeTeX font primitives in LuaTeX] | |
\fi | |
\def\XeTeXemulate{% | |
\chardef\XeTeXversion=0 | |
\def\XeTeXrevision{.99999}% | |
% Circumvent \outer error | |
\csname newcount\endcsname\XeTeXtracingfonts \XeTeXtracingfonts=0 | |
\csname newcount\endcsname\XeTeXuseglyphmetrics \XeTeXuseglyphmetrics=1 | |
% | |
\let\XeTeXmathcode\Umathcode | |
% | |
\protected\def\XeTeXfonttype{% | |
\directlua{ | |
local fid = token.scan_int() | |
local tfmdata = font.getfont(fid) | |
local fonttype = tfmdata.format == "opentype" and 2 or 0 | |
tex.print([[\numexpr]] .. fonttype .. [[\relax]]) | |
}\fontid | |
}% | |
% | |
\protected\def\XeTeXfirstfontchar{% | |
\directlua{ | |
local fid = token.scan_int() | |
local tfmdata = font.getfont(fid) | |
local min | |
for slot in pairs(tfmdata.characters) do | |
min = min and (slot < min and slot or min) or slot | |
end | |
tex.print([[\numexpr]] .. min .. [[\relax]]) | |
}\fontid | |
}% | |
% | |
\protected\def\XeTeXlastfontchar{% | |
\directlua{ | |
local fid = token.scan_int() | |
local tfmdata = font.getfont(fid) | |
local max | |
for slot in pairs(tfmdata.characters) do | |
max = max and (slot > max and slot < 2^16 and slot or max) or slot | |
end | |
tex.print([[\numexpr]] .. max .. [[\relax]]) | |
}\fontid | |
}% | |
% | |
\def\XeTeXglyph{% | |
\directlua{ | |
local index = token.scan_int() | |
local tfmdata = font.getfont(font.current()) | |
if tfmdata.format \string~= "opentype" then | |
tex.error([[Cannot use \string\XeTeXglyph]] .. [[ with ]] .. | |
tfmdata.name .. [[; not a native platform font]]) | |
end | |
for slot, char in pairs(tfmdata.characters) do | |
if char.index == index then | |
tex.print(utf.char(char.unicode)) | |
return | |
end | |
end | |
}}% | |
% | |
\protected\def\XeTeXcountglyphs{% | |
\directlua{ | |
local fid = token.scan_int() | |
local tfmdata = font.getfont(fid) | |
local count = 0 | |
if tfmdata.format == "opentype" then | |
for _ in pairs(tfmdata.characters) do | |
count = count + 1 | |
end | |
end | |
tex.print([[\numexpr]] .. count .. [[\relax]]) | |
}\fontid | |
}% | |
% | |
\def\XeTeXglyphname{% | |
\directlua{ | |
local fid = token.scan_int() | |
local index = token.scan_int() | |
local tfmdata = font.getfont(fid) | |
if tfmdata.format \string~= "opentype" then | |
tex.error([[Cannot use \string\XeTeXglyphname]] .. [[ with ]] .. | |
tfmdata.name .. [[; not a native platform font]]) | |
end | |
local count = 0 | |
for slot, char in pairs(tfmdata.shared.rawdata.descriptions) do | |
if char.index == index then | |
tex.print(char.name) | |
return | |
end | |
end | |
}\fontid | |
}% | |
% | |
\protected\def\XeTeXglyphindex{% | |
\directlua{ | |
local k = string.gsub(token.scan_string(),'"','') | |
token.scan_keyword(' ') % remove optional space | |
local tfmdata = font.getfont(font.current()) | |
if tfmdata.format \string~= "opentype" then | |
tex.error([[Cannot use \string\XeTeXglyphindex]] .. [[ with ]] .. | |
tfmdata.name .. [[; not a native platform font]]) | |
end | |
local index = 0 | |
for slot, char in pairs(tfmdata.shared.rawdata.descriptions) do | |
if char.name == k then | |
index = char.index | |
break | |
end | |
end | |
tex.print([[\numexpr]] .. index .. [[\relax]]) | |
}}% | |
% | |
\protected\def\XeTeXcharglyph{% | |
\directlua{ | |
local id = token.scan_int() | |
local tfmdata = font.getfont(font.current()) | |
if tfmdata.format \string~= "opentype" then | |
tex.error([[Cannot use \string\XeTeXcharglyph]] .. [[ with ]] .. | |
tfmdata.name .. [[; not a native platform font]]) | |
end | |
local index = tfmdata.characters[id].index | |
tex.print([[\numexpr]] .. index .. [[\relax]]) | |
}}% | |
% | |
\protected\def\XeTeXglyphbounds{% | |
\directlua{ | |
local edge = token.scan_int() | |
local slot = token.scan_int() | |
local tfmdata = font.getfont(font.current()) | |
if tfmdata.format \string~= "opentype" then | |
tex.error([[Cannot use \string\XeTeXglyphname]] .. [[ with ]] .. | |
tfmdata.name .. [[; not a native platform font]]) | |
end | |
for _,char in pairs(tfmdata.shared.rawdata.descriptions) do | |
if char and char.index == slot then | |
local bbox = char.boundingbox | |
local dimen = 0 | |
if edge == 1 then | |
dimen = bbox and bbox[1] / 100 * 2^16 or 0 | |
elseif edge == 2 then | |
dimen = tfmdata.characters[char.unicode].height | |
elseif edge == 3 then | |
dimen = bbox and (char.width - bbox[3]) / 100 * 2^16 or 0 | |
elseif edge == 4 then | |
dimen = tfmdata.characters[char.unicode].depth | |
end | |
tex.print([[\dimexpr]] .. tex.round(dimen) .. [[sp\relax]]) | |
break | |
end | |
end | |
}}% | |
} | |
\endinput |
I'd actually rather reimplement the entire thing in pure Lua and expose the commands via token.set_lua
. That makes them feel a lot more like actual primitives since they expand in a single step. That will need some expandafter magic for \fontid
, but other than that should be straight-forward.
@kberry https://github.com/latex3/xxetex is in the works
Thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I ended up with thefor \XeTeXglyph, used to typeset the visible glyph (a la Unicode font charts) for U+FE00 (variation selector-1). Using Base rendering mode was also necessary to stop the normal Unicode interpretation. I'll append my whole test document for the heck of it, but the relevant thing for your code is just the tweaks to the Lua condition and error string.