0

Given nested loops & data-structures are more elegant in Lua (than in TeX), I think plurality of table of contents style would be easier to write in Lua. For that to work, two things would be needed: A way to translate TOC data into a Lua data structure, and then traversing that data structure to do tex.print to typeset a table of contents with desired styling.

Below is an image of table of contents style that I came across while finding a pdf specimen on the web that uses a particular font. PDF metadata suggests that it was done in InDesign, I would like to recreate this style in LuaLaTeX. Some salient features of this style are:

  • Its a multi-part book. Instead of explicitly stating part numbers, transition to a new part is implied from “all small caps” styling, and resetting the "section" number to 1 after transition to new part.
  • Section numbers are unique to a part, and increment across chapters within a part.
  • The indent before first section number of a chapter ensures that it falls in the same horizontal position if it were typeset after the last section of previous chapter. Look for instance transition between sections 82 -> 83 in part "pācittiya", and (26->27, 56->57, 72->73) in part "sekhiya". This indent rule applies only between chapters of belonging to same part.
  • A part can directly have sections, without having underlying chapter.

I would imagine the values in each node of toc data-structure in lua would have relevant fields like: type (part/chapter/section/whatever), number (which can be contextual based on type), starting page number, tex string to be typeset (that can have line breaks, custom styling macros like \textcolor, \textbf etc), and a pointer to head of child nodes. Perhaps this can be a nested linkedlist data-structure like nodelist if the code from LuaTeX node library can be reused.

Note: If it helps to do so then the traditional naming/hierarchy of part-chapter-section could be replaced with something different that doesn't get into the way of using a selected latex document class.

Screenshot showing desired table of contents style

4
  • 1
    It's not clear how Lua can help really, by the time you have written the data to Lua you could have written the form for the toc to be printed, but basically you just need to define \l@chapter, \l@section etc to add to a Lua table as well as their existing use of writing to the toc file. Oct 27 '20 at 9:28
  • In pdflatex .aux file are creared, and via outer script you can read anything from it for chapter, section, etc., (after first running) and generate own toc file and input it. May be in LuaLaTeX you can do this too.
    – Elisey
    Oct 27 '20 at 14:12
  • It seems to me that you will need to change the structure of the document, not just the ToC. Why reset section numbers to 1 after each \part when you say that section numbers are unique to a part (i.e., no part can have the same section numbers within it)? What are the styles of a \section number with and without a preceding \chapter? Etc. Oct 27 '20 at 18:41
  • 1
    @PeterWilson Not sure why structure of the document needs to be changed. This TOC is from a book that divided "topics" of a "part" across "chapters". Its a matter of stylistic effect to have topic numbers increment across chapters. I would assume there is nothing special about name "section" or "topic", and call it section as thats the next in hierarchy below chapter (in spirit of Shakespear's quote "Whats in a name?...").
    – reportaman
    Nov 4 '20 at 7:26
1

Below is a solution to keeping a Lua version of TOC. I use inspect.lua to show the structure of toc variable. Here is an excerpt of it, you can see the full string in the log file. Each entity in the tree is associated with a label field so that one can create links to it or access its page number.

I have a rough idea of how to recreate the TOC layout, but since you might be more interested in the Lua TOC part, I am not diving into that now. If you need help, please let me know.

{ {
    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}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.