title |
---|
Demo |
BEFORE
\ChapterToc
AFTER
--[[ | |
Features of this filter: | |
– Not supported for HTML: --top-level-division=part. This filter numbers parts correctly. | |
– Chapter TOCs via \ChapterToc | |
Configure: | |
--metadata=header_tools_add_numbers:true | |
--metadata=header_tools_has_parts:true | |
]] | |
local headersToIgnore = { | |
["List of Figures"] = true, | |
["List of Listings"] = true, | |
["List of Tables"] = true, | |
} | |
local PART_LEVEL | |
local CHAPTER_LEVEL | |
local DO_NUMBER_HEADERS | |
local idToChapterToc = {} | |
-- ////////// Visit Headers \\\\\\\\\\ | |
-- Constantly update the counters (careful w.r.t. parts!) | |
-- If configured, prefer Headers with section numbers (text derived from counters) | |
-- Per chapter, collect the Headers (needed by chapter TOCs, below) | |
local counters = {} | |
local currentChapterToc | |
function VisitHeader(hdr) | |
local headerPlainText = pandoc.utils.stringify(hdr) | |
if headersToIgnore[headerPlainText] then | |
return nil | |
end | |
local counterStr = incCounters(hdr) | |
updateChapterToc(hdr, counterStr) | |
if DO_NUMBER_HEADERS then | |
local content = append({pandoc.Str(counterStr .. " ")}, hdr.content) | |
return pandoc.Header(hdr.level, content, hdr.attr) | |
else | |
return nil | |
end | |
end | |
function updateChapterToc(hdr, counterStr) | |
if hdr.level <= CHAPTER_LEVEL then | |
-- Part or new chapter? Don’t log sections, anymore | |
currentChapterToc = nil | |
end | |
if hdr.level == CHAPTER_LEVEL then | |
currentChapterToc = {} | |
idToChapterToc[hdr.identifier] = currentChapterToc | |
end | |
if hdr.level > CHAPTER_LEVEL and currentChapterToc ~= nil and (hdr.level - CHAPTER_LEVEL) <= 2 then | |
table.insert(currentChapterToc, | |
{ | |
indent = hdr.level, | |
content = pandoc.Plain({ | |
pandoc.Str(counterStr .. " "), | |
pandoc.Link(hdr.content, "#" .. hdr.identifier) | |
}) | |
}) | |
end | |
end | |
function incCounters(hdr) | |
local firstCounterLevel | |
if hdr.level == PART_LEVEL then | |
-- Parts don’t affect subsequent levels | |
addZerosUntilAtLevel(hdr.level) | |
firstCounterLevel = PART_LEVEL | |
else | |
addZerosUntilAtLevel(hdr.level) | |
pruneToLevel(hdr.level) | |
firstCounterLevel = CHAPTER_LEVEL | |
end | |
counters[hdr.level] = counters[hdr.level] + 1 | |
return countersToString(firstCounterLevel, hdr.level) | |
end | |
function addZerosUntilAtLevel(level) | |
while #counters < level do | |
table.insert(counters, 0) | |
end | |
end | |
function pruneToLevel(level) | |
while #counters > level do | |
table.remove(counters) | |
end | |
end | |
function countersToString(firstCounterLevel, lastCounterLevel) | |
-- For parts, only the part number is used, subsequent levels are ignored | |
-- For chapters and lower, the part number is ignored | |
local result = "" | |
for k,v in pairs(counters) do | |
if k > lastCounterLevel then break end | |
if k >= firstCounterLevel then | |
if k == PART_LEVEL then | |
result = result .. pandoc.utils.to_roman_numeral(v) .. "." | |
else | |
result = result .. v .. "." | |
end | |
end | |
end | |
return result | |
end | |
-- ////////// Create chapter TOCs \\\\\\\\\\ | |
local currentChapterId | |
function TrackChapterId(hdr) | |
if hdr.level == CHAPTER_LEVEL then | |
currentChapterId = hdr.identifier | |
end | |
end | |
function ChapterTocBlock(el) | |
if (el.format == "tex") then | |
if (el.text == "\\ChapterToc" and currentChapterId ~= nil) then | |
local result = {} | |
table.insert(result, pandoc.HorizontalRule()) | |
local tocEntries = idToChapterToc[currentChapterId] | |
local bulletList = tocEntriesToBulletLists(tocEntries, 1, 0).bulletList | |
if bulletList ~= nil then | |
table.insert(result, pandoc.Div(bulletList, pandoc.Attr(nil, {"chapter-toc"}))) | |
end | |
table.insert(result, pandoc.HorizontalRule()) | |
return result | |
end | |
end | |
return nil | |
end | |
function tocEntriesToBulletLists(tocEntries, index, parentIndent) | |
if index > #tocEntries then | |
return { nextIndex = index, bulletList = nil } | |
end | |
local listIndent = tocEntries[index].indent | |
if listIndent <= parentIndent then | |
return { nextIndex = index, bulletList = nil } | |
end | |
local bulletListItems = {} | |
while true do | |
if index > #tocEntries then | |
break | |
end | |
local tocEntry = tocEntries[index] | |
if tocEntry.indent < listIndent then | |
break | |
end | |
if tocEntry.indent > listIndent then | |
-- Should have been collected as a child | |
error() | |
end | |
local children = tocEntriesToBulletLists(tocEntries, index+1, listIndent) | |
if children.bulletList ~= nil then | |
table.insert(bulletListItems, {tocEntry.content, children.bulletList}) | |
else | |
table.insert(bulletListItems, {tocEntry.content}) | |
end | |
index = children.nextIndex | |
end | |
return { nextIndex = index, bulletList = pandoc.BulletList(bulletListItems) } | |
end | |
function ChapterTocBlockLatex(el) | |
if (el.format == "tex") then | |
if (el.text == "\\ChapterToc") then | |
return pandoc.RawBlock("latex", "\\minitoc") | |
end | |
end | |
return nil | |
end | |
-- ////////// Set up everything \\\\\\\\\\ | |
function setup(meta) | |
-- Command line: --metadata=header_tools_add_numbers:true | |
if meta.header_tools_add_numbers == true or meta.header_tools_add_numbers == false then | |
DO_NUMBER_HEADERS = meta.header_tools_add_numbers | |
else | |
error("Illegal value for 'header_tools_add_numbers': " .. meta.header_tools_add_numbers) | |
end | |
-- Command line: --metadata=header_tools_has_parts:true | |
if meta.header_tools_has_parts == true then | |
PART_LEVEL = 1 | |
CHAPTER_LEVEL = 2 | |
elseif meta.header_tools_has_parts == false then | |
PART_LEVEL = -1 | |
CHAPTER_LEVEL = 1 | |
else | |
error("Illegal value for 'header_tools_has_parts': " .. meta.header_tools_has_parts) | |
end | |
end | |
-- ////////// Tool functions \\\\\\\\\\ | |
function insertInto(source, target) | |
for _,v in pairs(source) do | |
table.insert(target, v) | |
end | |
return target | |
end | |
function append(t1, t2) | |
local result = {}; | |
insertInto(t1, result) | |
insertInto(t2, result) | |
return result | |
end | |
function printTable(tbl) | |
for k,v in pairs(tbl) do | |
print('KEY', k, 'VALUE', v) | |
end | |
end | |
-- ////////// Specify passes \\\\\\\\\\ | |
if FORMAT == "latex" then | |
return {{RawBlock = ChapterTocBlockLatex}} | |
else | |
return { | |
{Meta = setup}, {Header = VisitHeader}, -- setup, number Headers | |
{Header = TrackChapterId, RawBlock = ChapterTocBlock} -- create chapter TOCs | |
} | |
end |
<!DOCTYPE html> | |
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang=""> | |
<head> | |
<meta charset="utf-8" /> | |
<meta name="generator" content="pandoc" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> | |
<title>Demo</title> | |
<style type="text/css"> | |
code{white-space: pre-wrap;} | |
span.smallcaps{font-variant: small-caps;} | |
span.underline{text-decoration: underline;} | |
div.column{display: inline-block; vertical-align: top; width: 50%;} | |
</style> | |
<!--[if lt IE 9]> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> | |
<![endif]--> | |
</head> | |
<body> | |
<header id="title-block-header"> | |
<h1 class="title">Demo</h1> | |
</header> | |
<nav id="TOC"> | |
<ul> | |
<li><a href="#background">I. Background</a><ul> | |
<li><a href="#history">1. History</a></li> | |
<li><a href="#tips">2. Tips</a></li> | |
</ul></li> | |
<li><a href="#main">II. Main</a><ul> | |
<li><a href="#syntax">3. Syntax</a><ul> | |
<li><a href="#expressions">3.1. Expressions</a></li> | |
<li><a href="#statements">3.2. Statements</a></li> | |
</ul></li> | |
<li><a href="#library">4. Library</a></li> | |
</ul></li> | |
<li><a href="#appendix">III. Appendix</a><ul> | |
<li><a href="#acknowledgements">5. Acknowledgements</a></li> | |
</ul></li> | |
</ul> | |
</nav> | |
<h1 id="background">I. Background</h1> | |
<h2 id="history">1. History</h2> | |
<h2 id="tips">2. Tips</h2> | |
<h1 id="main">II. Main</h1> | |
<h2 id="syntax">3. Syntax</h2> | |
<p>BEFORE</p> | |
<hr /> | |
<div class="chapter-toc"> | |
<ul> | |
<li>3.1. <a href="#expressions">Expressions</a> | |
<ul> | |
<li>3.1.1. <a href="#special-expressions">Special expressions</a></li> | |
</ul></li> | |
<li>3.2. <a href="#statements">Statements</a></li> | |
</ul> | |
</div> | |
<hr /> | |
<p>AFTER</p> | |
<h3 id="expressions">3.1. Expressions</h3> | |
<h4 id="special-expressions">3.1.1. Special expressions</h4> | |
<h3 id="statements">3.2. Statements</h3> | |
<h2 id="library">4. Library</h2> | |
<h1 id="appendix">III. Appendix</h1> | |
<h2 id="acknowledgements">5. Acknowledgements</h2> | |
<!-- | |
pandoc -t html --standalone --toc --lua-filter=header-tools.lua --metadata=header_tools_has_parts:true --metadata=header_tools_add_numbers:true header-tools-demo.md | |
--> | |
</body> | |
</html> |