![检查发货箱](https://linux22.com/image/389982/%E6%A3%80%E6%9F%A5%E5%8F%91%E8%B4%A7%E7%AE%B1.png)
请考虑以下示例 LaTeX 文件:
\documentclass{standalone}
\begin{document}
Test $x^2$
\end{document}
当由其中一个 TeX 引擎处理时,人们会得到一个 pdf,其中的字形与通过 shipout 过程(IIUC)很好地组合在一起的框相关联。
假设 pdf 页面是帆布。我希望 luatex 输出一个文件,其中包含每个字形框在笛卡尔坐标系中的坐标帆布(让我们以帆布)。
有什么想法吗?(也许这个lua-visual-debug
包是一个很好的起点,但我不是该领域的专家,而且我几乎不了解它是如何遍历所有页面节点的)。
这里的整个想法是让 luatex 构建页面、段落或公式,并使用可编写脚本的图形编辑器(如 gimp 或 inkscape)实际绘制帆布(在我的用例中,那将是搅拌机,但我认为这并不重要)。
请注意,我真正需要的是字形的坐标起源(如果这个概念存在的话!),那就是在帆布(该框只是 TeX 查看其输出的方式,忽略了字形的实际绘制方式,再次是 IIUC)。
答案1
检查发货箱
您可以做的是覆盖\shipout
原语以执行一些自定义 Lua 代码,该代码遍历垂直列表并检查节点。这需要相当新的 LuaTeX 才能使用token.scan_list
。我还没有找到如何以合理的方式确定起始位置,因此所有坐标都相对于第一个字形边界框的左下角。坐标为pt
。
\documentclass{standalone}
\directlua{
local mode
local x = 0
local y = 0
local glue = { hmode = 0, vmode = 0 }
local function glyph_boxes(head)
for n in node.traverse(head) do
if n.id == node.id"hlist" then
mode = "hmode"
y = y - n.shift
glyph_boxes(n.list)
y = y + n.shift
elseif n.id == node.id"vlist" then
mode = "vmode"
glue.hmode = 0
glyph_boxes(n.list)
elseif n.id == node.id"glue" then
glue[mode] = glue[mode] + n.width
elseif n.id == node.id"kern" then
glue[mode] = glue[mode] + n.kern
elseif n.id == node.id"glyph" then
x = x + glue.hmode
local c = string.utfcharacter(n.char)
local f = font.getfont(n.font)
print(c, f.name, x / 2^16, y / 2^16)
x = x + n.width
glue.hmode = 0
end
end
end
function shipout()
local box = token.scan_list()
tex.setbox(255, box)
glue.vmode = 0
print() % just for nice formatting
glyph_boxes(tex.box[255])
tex.shipout(255)
end
}
\def\shipout{\directlua{shipout()}}
\begin{document}
Test $x^2$
\end{document}
日志中输出:
T [lmroman10-regular]:+tlig; 0.0 0.11000061035156
e [lmroman10-regular]:+tlig; 6.3899993896484 0.11000061035156
s [lmroman10-regular]:+tlig; 10.830001831055 0.11000061035156
t [lmroman10-regular]:+tlig; 14.770004272461 0.11000061035156
x cmmi10 21.990005493164 0.11000061035156
2 cmr7 27.705276489258 3.7389221191406
SVG 导出
获取字形坐标的另一种方法是使用dvisvgm
驱动程序。这需要更多的手工操作,但通常不太复杂。在这种情况下,文档只是
\documentclass{standalone}
\begin{document}
Test $x^2$
\end{document}
排版使用
dvilualatex test.tex # or simply latex
dvisvgm --font-format=woff --no-merge test
日志可能包含一些有关无法嵌入某些字体的信息。这无关紧要,因为我们不想渲染 SVG,而只想提取数据。该--no-merge
选项可防止驱动程序将相邻字母合并为单个 XML 实体。为了简洁起见,我删除了 base64 编码的字体数据。
<?xml version='1.0' encoding='UTF-8'?>
<!-- This file was generated by dvisvgm 2.3.5 -->
<svg height='8.109622pt' version='1.1' viewBox='-72.000004 -72.000007 48.811833 8.109622' width='48.811833pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
<style type='text/css'>
<![CDATA[
@font-face{font-family:cmr7;src:url(/* base64 data */) format('woff');}
@font-face{font-family:cmmi10;src:url(/* base64 data */) format('woff');}
text.f0 {font-family:cmmi10;font-size:9.96264px}
text.f1 {font-family:cmr7;font-size:6.973848px}
text.f2 {font-family:[lmroman10-regular]:+tlig;;font-size:10px}
]]>
</style>
<g id='page1'>
<text class='f2' x='-72.000004' y='-63.890385'>T</text>
<text class='f2' x='-63.662905' y='-63.890385'>e</text>
<text class='f2' x='-54.498905' y='-63.890385'>s</text>
<text class='f2' x='-45.334905' y='-63.890385'>t</text>
<text class='f0' x='-32.853344' y='-63.890385'>x</text>
<text class='f1' x='-27.159412' y='-67.505749'>2</text>
</g>
</svg>
希望这就是您需要的所有信息。您class
可以使用嵌入的 CSS 和画布上的x
和坐标将每个字母与相应的字体匹配。不过,我无法告诉您原点在哪里。单位可能是。y
bp
答案2
LuaTeX 中有一个特殊函数pdf.getpos()
,它直接从引擎获取坐标。要使用它,您需要将 Lua whatsit 添加到发货箱。
\documentclass{scrbook}
\usepackage{fontspec}
\usepackage{luacode}
\setmainfont{Libertinus Serif}
\begin{luacode}
function PrintPos()
print(pdf.getpos())
end
function ProcessList(head)
for n in node.traverse(head) do
if node.is_glyph(n) then
local printpos = node.new("whatsit","late_lua")
printpos.data = PrintPos
node.insert_before(head, n, printpos)
end
if n.head ~= nil then
ProcessList(n.head)
end
end
end
function PrintCoordinates()
local soBox = tex.getbox("ShipoutBox")
ProcessList(soBox)
end
\end{luacode}
\AddToHook{shipout/before}{\directlua{PrintCoordinates()}}
\begin{document}
Hello World!
\end{document}
它输出:
5157309 49122001
5478085 49122001
5667536 49122001
5856987 49122001
6398072 49122001
7023118 49122001
7384798 49122001
7651752 49122001
7841203 49122001
30992932 7677017
最后一个坐标是页码。并且...不要弄乱例程shipout
和box255
。只需添加一个钩子并使用ShipoutBox
。否则,如果您使用像这样的包,您只能获得部分输出scrlayer-notecolumn
。还请记住,一些包也可以有钩子,shipout/before
并可能丢弃整个页面。