Files
2026-05-21 13:37:53 +08:00

181 lines
5.2 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Prepare LaTeX headers so quotation marks render correctly in the PDF body
-- while keeping proper Unicode text in PDF bookmarks.
--- Copyright: © 2025Present Tom Ben
--- License: MIT License
local function format_is(name)
return FORMAT:match(name) ~= nil
end
local function to_macro_block(inlines)
return pandoc.walk_block(pandoc.Plain(inlines), {
Quoted = function(el)
local opening, closing
if el.quotetype == "SingleQuote" then
opening = pandoc.RawInline('latex', "`")
closing = pandoc.RawInline('latex', "'")
else
opening = pandoc.RawInline('latex', "``")
closing = pandoc.RawInline('latex', "''")
end
local result = pandoc.Inlines { opening }
for _, inline in ipairs(el.content) do
result:insert(inline)
end
result:insert(closing)
return result
end
})
end
local function to_unicode_block(inlines)
return pandoc.walk_block(pandoc.Plain(inlines), {
Quoted = function(el)
local opening, closing
if el.quotetype == "SingleQuote" then
opening, closing = '', ''
else
opening, closing = '', ''
end
local result = pandoc.Inlines { pandoc.Str(opening) }
for _, inline in ipairs(el.content) do
result:insert(inline)
end
result:insert(pandoc.Str(closing))
return result
end,
Str = function(el)
-- Replace guillemets with curly quotes for PDF bookmarks
local text = el.text
text = text:gsub('«', '')
text = text:gsub('»', '')
text = text:gsub('', '')
text = text:gsub('', '')
if text ~= el.text then
return pandoc.Str(text)
end
return el
end
})
end
function Header(header)
if not format_is('latex') then
return header
end
local attr = header.attr
local macro_block = to_macro_block(header.content)
local unicode_block = to_unicode_block(header.content)
local latex_doc = pandoc.Pandoc({ macro_block }, pandoc.Meta {})
local latex_body = pandoc.write(latex_doc, 'latex')
:gsub('%s+$', '')
:gsub('\n', ' ')
local bookmark_text = pandoc.utils.stringify(unicode_block)
:gsub('\n', ' ')
attr.attributes['data-tex-body'] = latex_body
attr.attributes['data-bookmark'] = bookmark_text
return header
end
local latex_heading_levels = {
"section",
"subsection",
"subsubsection",
"paragraph",
"subparagraph",
"subparagraph"
}
local function escape_tex_argument(text)
local map = {
['\\'] = '\\\\',
['{'] = '\\{',
['}'] = '\\}',
['%'] = '\\%',
['#'] = '\\#',
['&'] = '\\&',
['_'] = '\\_',
['^'] = '\\^{}',
['~'] = '\\~{}'
}
return text:gsub('[\\%%#&_{}%^~]', map)
end
local function heading_to_raw(header)
local tex_body = header.attr.attributes['data-tex-body']
if not tex_body then
return nil
end
local bookmark = header.attr.attributes['data-bookmark'] or pandoc.utils.stringify(header)
header.attr.attributes['data-tex-body'] = nil
header.attr.attributes['data-bookmark'] = nil
local short_attr = header.attr.attributes['short-title'] or header.attr.attributes['short']
if short_attr and short_attr ~= '' then
bookmark = short_attr
end
bookmark = tostring(bookmark):gsub('\n', ' ')
local level_index = math.min(header.level, #latex_heading_levels)
local command = latex_heading_levels[level_index]
local classes = header.attr.classes or {}
local unnumbered = false
for _, class in ipairs(classes) do
if class == 'unnumbered' then
unnumbered = true
break
end
end
local number_attr = header.attr.attributes['number']
if number_attr == 'no' or number_attr == 'false' or number_attr == '0' then
unnumbered = true
end
local star = unnumbered and '*' or ''
local bookmark_escaped = escape_tex_argument(bookmark)
local texorpdfstring = '\\texorpdfstring{' .. tex_body .. '}{' .. bookmark_escaped .. '}'
local pieces = { '\\' .. command .. star .. '{' .. texorpdfstring .. '}' }
local identifier = header.attr.identifier
if identifier and identifier ~= '' then
table.insert(pieces, '\\label{' .. identifier .. '}')
end
if unnumbered then
table.insert(pieces, '\\addcontentsline{toc}{' .. command .. '}{' .. bookmark_escaped .. '}')
end
return pandoc.RawBlock('latex', table.concat(pieces, '') .. '\n')
end
function Pandoc(doc)
if not format_is('latex') then
return doc
end
local blocks = pandoc.List:new()
for _, block in ipairs(doc.blocks) do
if block.t == 'Header' then
blocks:insert(heading_to_raw(block) or block)
else
blocks:insert(block)
end
end
doc.blocks = blocks
return doc
end
return {
{ Header = Header },
{ Pandoc = Pandoc }
}