luatex hlist 节点移位方向似乎不正确,实现 pdf 对象平移(位移)的最干净的方法是什么?

luatex hlist 节点移位方向似乎不正确,实现 pdf 对象平移(位移)的最干净的方法是什么?

从 hlist 节点的描述来看luatex 手册,其字段“shift”似乎是垂直移位。但是当我使用 post_linebreak_filter 更改它时,它会将水平移位添加到 hlist。这背面邮报too 将 shift 描述为 hlist 的垂直移位:“元数据”中列出的另一个参数是 shift:这是应用 TeX 命令所产生的框位移值:\raise、\lower(应用于 \hbox);”我是否遇到了 luatex 中的错误,或者我的使用模式不正确?无论哪种方式,我都想选择一个 hlist 节点,并在页面上水平和垂直移动它,同时不影响页面上的其他 pdf 内容(实际上是将 pdf 平移矩阵应用于 hlist)。不幸的是,pdf_setmatrix节点不能用于垂直/水平位移(在PDF 1.7 手册\pdfsetmatrix)因为 pdftex 已禁用它(阅读中的定义pdftex 手册),并警告不要使用\pdfliteral(pdftex 等同于 luatexpdf_literal节点)。虽然我不确定在换行后阶段,luatex 的警告是否正确?如果正确,那么实现水平和垂直平移的最安全方法是什么?(我猜测水平平移是在 hlist 的开头添加一个 kern 节点,这是最干净的吗?我不确定进行垂直平移的最干净的方法是什么……)

下面是我得到的输出截图,后面是向第 3 行的 hlist 添加垂直和水平翻译的完整代码,代码下面是来自上面描述中提到的文档的一些有用的截图:

输出

代码:>>lualatex test.tex

% test.tex
\documentclass[notitlepage,letterpaper]{article}
\usepackage[letterpaper,left=2in,right=2in,top=1in,bottom=1in]{geometry}
\usepackage{blindtext}

\directlua{
    function my_post_lb_filter(head,groupcode)
      local linenumber=1;
      local HLIST = node.id("hlist")
      local WHATSIT = node.id("whatsit")
      local KERN = node.id("kern")
      for n in node.traverse(head) do
        if n.id==HLIST then
          if linenumber==3 then
            % Add vertical translation
            n.shift=20*65536; % This should shift linenumber 3 vertically by 20, in reality it shifts horizontally
            % Add horizontal translation
            local hkern = node.new(KERN)
            hkern.subtype=1;
            hkern.attr=n.attr;
            hkern.kern = 80*65535;
            n.head = node.insert_before(n.list,n.head,hkern)
          end
        linenumber=linenumber+1;
        end
      end
      return head
    end
  luatexbase.add_to_callback('post_linebreak_filter', my_post_lb_filter, 'Play with luatex node library')
}

\begin{document}
  \blindtext[1]
\end{document}

luatex 手册中的 hlist shift 描述:

列表

pdf对象翻译:

pdf 翻译

pdftex 手册中的 pdfliteral 与 pdfsetmatrix:

pdftex 转换

答案1

关于shift:文档具有误导性,但如果框是垂直移动的包含在水平列表中(\lower\raise在水平模式下),如果包含在垂直列表中(\moveleft并且\moveright处于垂直模式),并且如果它不在任何外部列表中,它根本不会移动。

因此,一个“修复”方法是将其包装在外部 hbox 中:

% test.tex
\documentclass[notitlepage,letterpaper]{article}
\usepackage[letterpaper,left=2in,right=2in,top=1in,bottom=1in]{geometry}
\usepackage{blindtext}

\directlua{
    function my_post_lb_filter(head,groupcode)
      local linenumber=1;
      local HLIST = node.id("hlist")
      local WHATSIT = node.id("whatsit")
      local KERN = node.id("kern")
      for n in node.traverse(head) do
        if n.id==HLIST then
          if linenumber==3 then
            % Add vertical translation
            n.shift=20*65536; % This should shift linenumber 3 vertically by 20, in reality it shifts horizontally
            % Add horizontal translation
            local hkern = node.new(KERN)
            hkern.subtype=1;
            hkern.attr=n.attr;
            hkern.kern = 80*65535;
            n.head = node.insert_before(n.list,n.head,hkern)
            % We want to wrap n in a hlist, but we have to keep
            % node.traverse happy so we can't easily change the
            % current node. Instead, create a new hlist inside of the
            % current list, inheriting all attributes and the content
            % of n. Then this list becomes the new head of n.
            %
            % First a little optimization: We want to create a copy of n with
            % local nn = node.copy(n)
            % but doing so now would lead to n.head being deep-copied
            % into nn.head. Given that we want to overwrite nn.head
            % anyway, this would waste memory and time, especially if
            % the line is very complicated. So save the line
            local saved_head = n.head
            % make it empty to hide it and avoid copying something
            n.head = nil
            % and then make the copy. Now nothing in n..head is copied
            % (because nothing is there)
            local nn = node.copy(n)
            % Finally we again want nn to  contain the original content contained by n, so assign the saved content. 
            nn.head = saved_head
            % So much for the inner hlist nn. But what should be the new
            % content of the outer hlist n? it should contain only the
            % inner hlist nn. Given that nn was just created, it doesn't
            % has a next node anyway, so we can just assign it as new head.
            n.head = nn
            n.shift = 0
          end
        linenumber=linenumber+1;
        end
      end
      return head
    end
  luatexbase.add_to_callback('post_linebreak_filter', my_post_lb_filter, 'Play with luatex node library')
}

\begin{document}
  \blindtext[1]
\end{document}

(此版本保留尺寸以模拟 PDF 运算符变体,从而导致重叠)

在此处输入图片描述

当然,在我看来,一个更好的选择就是使用正常的 kern 节点,就像水平定位一样:

\documentclass[notitlepage,letterpaper]{article}
\usepackage[letterpaper,left=2in,right=2in,top=1in,bottom=1in]{geometry}
\usepackage{blindtext}

\directlua{
    function my_post_lb_filter(head,groupcode)
      local linenumber=1;
      local HLIST = node.id("hlist")
      local WHATSIT = node.id("whatsit")
      local KERN = node.id("kern")
      for n in node.traverse(head) do
        if n.id==HLIST then
          if linenumber==3 then
            % Add vertical translation
            local vkern = node.new(KERN)
            vkern.kern = 20*65536
            head = node.insert_before(head, n, vkern)
            if 'you want the text to overlap' then
              vkern = node.copy(vkern)
              vkern.kern = -vkern.kern
              node.insert_after(head, n, vkern)
            end
            % Add horizontal translation
            local hkern = node.new(KERN)
            hkern.subtype=1;
            hkern.attr=n.attr;
            hkern.kern = 80*65535;
            n.head = node.insert_before(n.list,n.head,hkern)
          end
          linenumber=linenumber+1;
        end
      end
      return head
    end
  luatexbase.add_to_callback('post_linebreak_filter', my_post_lb_filter, 'Play with luatex node library')
}

\begin{document}
  \blindtext[1]
\end{document}

在此处输入图片描述 (如果您不想要重叠的文本,请更改'you want the text to overlap'false删除该块。

相关内容