我正在尝试调试一个更大的问题,但确凿的证据是 LuaTeX 的字体加载器库存在一些(显然)奇怪的行为。
最小示例
这是文件backmap.tex
:
\directlua { tex.enableprimitives('',tex.extraprimitives()) }
\directlua { dofile('define_font.lua') }
\font\noto={../../fonts/amiri-regular.ttf}
\bye
这是文件define_font.lua
:
function read_font (name, size, fontid)
-- Load file using fontloader.open
local f = fontloader.open (name)
local fonttable = fontloader.to_table(f)
fontloader.close(f)
for char, glyph in pairs(fonttable.map.map) do
if char == 0x0020 -- SPACE
or char == 0x110300 -- Some ‘character’ in Amiri font
or char == 0x06A9 then -- ARABIC LETTER KEHEH
local backmap = fonttable.map.backmap[glyph]
texio.write_nl(string.format("\n\nGLYPH: 0x%x, CHAR: 0x%x, BACKMAP: 0x%x", glyph, char, backmap))
if char ~= backmap then
texio.write_nl("char and backmap value DIFFERENT")
texio.write_nl(string.format("fonttable.map.map[0x%x]: 0x%x", char, fonttable.map.map[char]))
texio.write_nl(string.format("fonttable.map.map[0x%x]: 0x%x", char, fonttable.map.map[backmap]))
else
texio.write_nl("char and backmap value SAME")
end
end
end
return { }
end
-- Register OpenType font loader in define_font callback.
callback.register('define_font', read_font, "font loader")
运行后的输出如下luatex --fmt=plain backmap.tex
:
This is LuaTeX, Version beta-0.87.1 (TeX Live 2016/dev)
(./backmap.tex
GLYPH: 0x3, CHAR: 0x20, BACKMAP: 0xa0
char and backmap value DIFFERENT
fonttable.map.map[0x20]: 0x3
fonttable.map.map[0xa0]: 0x3
GLYPH: 0x206, CHAR: 0x6a9, BACKMAP: 0x6a9
char and backmap value SAME
GLYPH: 0x987, CHAR: 0x110300, BACKMAP: 0x110300
char and backmap value SAME
! error (font): lua-loaded font '51' has no name!
! ==> Fatal error occurred, bad output DVI file produced!
No pages of output.
(
解释
我正在尝试读取中的所有字符和字形fonttable.map.map
,然后将的值char
与的相应值进行比较fonttable.map.backmap[glyph]
。
我的期望是,该fonttable.map.backmap
表应将字形映射回与该特定字形相同的字符。但是,正如您从上面的输出中看到的那样,与 U+0020(空格)对应的字形的反向映射反而映射到 U+00A0(无间断空格)。
这给我带来问题的原因是,我正在使用外部整形引擎(Harfbuzz)来获取段落中字符的字形,然后尝试检查反向映射以查看字形是否映射回 U+0020(SPACE)字符,然后拦截它以添加粘合节点。
我的问题:
- 字体这样是否有效,即反向映射与字形不匹配回到相同的字符?
- 关于如何拦截空格,有什么建议吗?我应该寻找任何类型的空格并将其替换为胶水吗?
答案1
这没问题,返回的代码点位于 PUA(私有使用区域)中,因为它们应该与输入文件中使用的其他字形一起显示。这种情况发生在非标准连字符、各种脚本(例如阿拉伯语)以及由整形过程生成的其他字形(未定义为普通 Unicode 字符)中。
您还可以只塑造单词,并将原始粘连保留在节点列表中。这样,您将保留用户插入的非中断空格和其他类型的空格。另一种方法是使用带有 Unicode 字符信息的表来检测各种空格类型并相应地采取行动。但是,当塑造器生成的所有空格都是非喙状空格时,它对我来说似乎不太有用。特别是在乌尔都语的情况下,我们在 LuaTeX 中没有功能性连字
有一个例子可以说明各种空间的问题:
\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
local glue_id = node.id "glue"
local glyph_id = node.id "glyph"
local penalty_id = node.id "penalty"
local kern_id = node.id "kern"
local utfchar = unicode.utf8.char
local onept = tex.sp "1pt"
local function clb(head)
local t = {}
local function printword()
print(table.concat(t))
t = {}
end
for n in node.traverse(head) do
if n.id == glyph_id then
t[#t+1] = utfchar(n.char)
elseif n.id == glue_id then
printword()
local spec = n.spec
local width = spec.width / onept
local stretch = spec.stretch / onept
local shrink = spec.shrink /onept
print("glue type: ",n.subtype, width, stretch, shrink)
elseif n.id == penalty_id then
print("penalty", n.penalty / onept)
elseif n.id == kern_id and n.subtype == 1 then
printword()
print("kern", n.kern / onept)
end
end
return head
end
luatexbase.add_to_callback("pre_linebreak_filter", clb, "print spaces")
\end{luacode*}
\begin{document}
hello world~there\,are\quad various\qquad space\ types
\end{document}
这会在控制台中产生以下输出:
hello
glue type: 0 3.3333282470703 1.6666564941406 1.1111145019531
penalty 0.152587890625
world
glue type: 0 3.3333282470703 1.6666564941406 1.1111145019531
there
kern 1.6667175292969
are
glue type: 0 10.000015258789 0 0
various
glue type: 0 20.000030517578 0 0
space
glue type: 0 3.3333282470703 1.6666564941406 1.1111145019531
penalty 0.152587890625
types
glue type: 15 0 1 0
为了支持这些情况,我会保存表格中单词之间的粘连、惩罚和字距节点,然后将它们插回到正确的位置。我猜在 BiDi 中这可能很难。