将目录数据转换为 Lua 数据结构,并创建自定义目录样式

将目录数据转换为 Lua 数据结构,并创建自定义目录样式

鉴于 Lua 中的嵌套循环和数据结构比 TeX 中更优雅,我认为在 Lua 中编写多种目录样式会更容易。要做到这一点,需要做两件事:一种将目录数据转换为 Lua 数据结构的方法,然后遍历该数据结构以tex.print排版具有所需样式的目录。

下面是我在网上查找使用特定字体的 PDF 样本时偶然发现的目录样式的图像。PDF 元数据表明它是在 InDesign 中完成的,我想在 LuaLaTeX 中重新创建这种样式。这种样式的一些显著特点是:

  • 这是一本多部分的书。没有明确说明部分编号,而是通过“全部小写”样式暗示过渡到新部分,并在过渡到新部分后将“部分”编号重置为 1。
  • 章节编号对于每个部分而言都是唯一的,并且会随着同一部分内各章的增加而增加。
  • 章节第一节编号前的缩进确保其位于与排版在前一章节最后一节之后相同的水平位置。例如,查看“pācittiya”部分中第 82 -> 83 节之间的过渡,以及“sekhiya”部分中 (26->27, 56->57, 72->73) 之间的过渡。此缩进规则仅适用于属于同一部分的章节之间。
  • 部分可以直接包含节,而无需包含底层章节。

我认为 lua 中 toc 数据结构的每个节点中的值都会有相关字段,例如:类型(部分/章节/节/其他)、数字(可以根据类型进行上下文关联)、起始页码、要排版的 tex 字符串(可以有换行符、自定义样式宏等\textcolor)以及指向子节点头的指针。如果 LuaTeX库中的代码可以重用,\textbf那么这也许可以是一个嵌套的链表数据结构,如 nodelist 。node

注意:如果这样做有帮助,那么部分章节部分的传统命名/层次结构可以被替换为一些不同的、不会妨碍使用选定的乳胶文档类的东西。

屏幕截图显示了所需的目录样式

答案1

以下是保留 Lua 版目录的解决方案。我用它inspect.lua来显示变量的结构toc。这是其中的摘录,您可以在日志文件中看到完整的字符串。树中的每个实体都与一个label字段相关联,以便可以创建指向它的链接或访问其页码。

我大致知道如何重新创建目录布局,但由于您可能对 Lua 目录部分更感兴趣,因此我现在不深入讨论该部分。如果您需要帮助,请告诉我。

{ {
    arga = "",
    argb = "Part 1",
    content = { {
        arga = "",
        argb = "Chapter 1",
        content = { {
            arga = "",
            argb = "Section I",
            content = {},
            index = 1,
            label = "5371A59F385D7156E74C245C6A1225D3DAC02A8ED95DFBEF",
            level = 3
          }, {
            arga = "",
            argb = "Section II",
            content = {},
            index = 2,
            label = "7F84FBD64E18D211C25EE7F86B00322F8602CE9093259EDD",
            level = 3
          }, {
            arga = "",
            argb = "Section III",
            content = {},
            index = 3,
            label = "B834D3A96C2E4B71FE95D553AD63F5270272127F5892CADF",
            level = 3
          }, {...
\documentclass{scrbook}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{etoolbox}
\usepackage{blindtext}
\usepackage{luacode}
\usepackage{xparse}

\begin{luacode*}
--https://github.com/kikito/inspect.lua
inspect = require "inspect"

toc = {}
most_recent_hash = ""

function hash_and_store(parts)
  local str = ""
  for _, stuff in pairs(parts) do
    str = str .. tostring(stuff)
  end
  local bin_hash = sha2.digest256(str)
  local hex_hash = ""
  for i=1,#bin_hash do
    hex_hash = hex_hash .. string.format("%02X", bin_hash:byte(i))
  end
  most_recent_hash = hex_hash:sub(1, 48)
end

function get_toc_item(tbl)
  local keys = {"level", "index", "label", "arga", "argb", "content"}
  local default_vals = {0, 0, "", "", "", {}}
  local item = {}
  
  local temp = nil
  for ind, key in pairs(keys) do
    temp = tbl[key]
    if temp == nil then
      rawset(item, key, default_vals[ind])
    else
      rawset(item, key, temp)
    end
  end
  
  return item
end

function insert_to_toc(level, arga, argb)
  
  local search_level = 1
  local tbl = toc
  local hash_path = {}
  
  while search_level < level do
    table.insert(hash_path, #tbl)
    if #tbl == 0 then
      local item = get_toc_item{level=search_level, index=0}
      table.insert(tbl, item)
      tbl = item.content
    else
      tbl = tbl[#tbl].content
    end
    search_level = search_level + 1
  end
  
  table.insert(hash_path, argb)
  hash_and_store(hash_path)
  
  local item_index = #tbl + 1
  local item = get_toc_item{level=level, index=item_index, label=most_recent_hash,arga=arga,argb=argb}
  table.insert(tbl, item)
  
  
end

\end{luacode*}

% redefine commands to link Lua and TeX
\ExplSyntaxOn

\tl_new:N \l_doc_tmpa_tl

\cs_set:Npn \doc_patch_command:nn #1#2 {
  \cs_gset_eq:cc {__doc_old_#1} {#1}
  \exp_args:Nc \RenewDocumentCommand{#1}{som}{
    \IfBooleanTF{##1}{
      % starred commands are not numbered
      \IfValueTF{##2}{
        \use:c {__doc_old_#1} * [##2] {##3}
      }{
        \use:c {__doc_old_#1} * {##3}
      }
    }{
      \IfValueTF{##2}{
        \directlua{
          insert_to_toc(#2, "\luaescapestring{##2}", "\luaescapestring{##3}")
        }
        \use:c {__doc_old_#1} [##2] {##3}
        \label{\directlua{tex.print(most_recent_hash)}}
      }{
        \directlua{
          insert_to_toc(#2, "", "\luaescapestring{##3}")
        }
        \use:c {__doc_old_#1} {##3}
        \directlua{texio.write(most_recent_hash)}
        \label{\directlua{tex.print(most_recent_hash)}}
      }
    }
  }
}

\AtBeginDocument{
  \doc_patch_command:nn {part}{1}
  \doc_patch_command:nn {chapter}{2}
  \doc_patch_command:nn {section}{3}
}


\ExplSyntaxOff

\begin{document}

\tableofcontents

% define some commands for creating a dummy document
\ExplSyntaxOn
\int_new:N \g_doc_part_int
\int_new:N \g_doc_chapter_int
\int_new:N \g_doc_section_int

\sys_gset_rand_seed:n {1}
\int_gset:Nn \g_doc_part_int {1}
\int_gset:Nn \g_doc_chapter_int {1}
\int_gset:Nn \g_doc_section_int {1}

\cs_set:Nn \doc_rand_text: {
  \tl_set:Nx \l_tmpa_tl {\exp_not:N\Blindtext[\int_rand:nn {1}{12}]}
  \tl_use:N \l_tmpa_tl
}

\newcommand{\dummypart}{
  \exp_args:Nx \part{Part~\int_use:N \g_doc_part_int}
  \int_gincr:N \g_doc_part_int
}

\newcommand{\dummychapter}{
  \exp_args:Nx \chapter{Chapter~\int_use:N \g_doc_chapter_int}
  \int_gincr:N \g_doc_chapter_int
}

\newcommand{\dummysection}{
  \exp_args:Nx \section{Section~\int_to_Roman:n {\g_doc_section_int}}
  \int_gincr:N \g_doc_section_int
}

\int_step_inline:nn {3} {
  \dummypart
  \int_set:Nn \l_tmpa_int {\int_rand:nn {2}{4}}
  \int_step_inline:nn {\l_tmpa_int} {
    \dummychapter
    \int_set:Nn \l_tmpb_int {\int_rand:nn {5}{10}}
    \int_step_inline:nn {\l_tmpb_int} {
        \dummysection
        \doc_rand_text:
    }
  }
}

\ExplSyntaxOff

\directlua{
  texio.write(inspect(toc))
}


\end{document}

相关内容