检查发货箱

检查发货箱

请考虑以下示例 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和坐标将每个字母与相应的字体匹配。不过,我无法告诉您原点在哪里。单位可能是。ybp

答案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

最后一个坐标是页码。并且...不要弄乱例程shipoutbox255。只需添加一个钩子并使用ShipoutBox。否则,如果您使用像这样的包,您只能获得部分输出scrlayer-notecolumn。还请记住,一些包也可以有钩子,shipout/before并可能丢弃整个页面。

相关内容