mireapandoc/filter2.lua
2025-02-15 02:42:21 +03:00

267 lines
No EOL
10 KiB
Lua
Raw 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.

IMAGE, FORMULA, TABLE, CODE = 0, 0, 0, 0
UNNUMBERED = true
local hdlis = {
['АННОТАЦИЯ'] = true,
['ВВЕДЕНИЕ'] = true,
['ЗАКЛЮЧЕНИЕ'] = true,
['ВЫВОДЫ'] = true,
['СПИСОК-ИСПОЛЬЗОВАННЫХ-ИСТОЧНИКОВ'] = true
}
local section = {0, 0, 0}
local function remove_trailing_punctuation(text)
return text:gsub("[,;%.!?]+$", "")
end
local function capitalize_first_letter(text)
local first_char = pandoc.text.sub(text, 1, 1)
local rest = pandoc.text.sub(text, 2)
return pandoc.text.upper(first_char) .. rest
end
local function is_unnumbered(header_text)
return hdlis[pandoc.text.upper(header_text)] ~= nil
end
local function get_section_number(level)
if level == 1 then
section[1] = section[1] + 1
section[2] = 0
section[3] = 0
IMAGE = 0
return tostring(section[1])
elseif level == 2 then
if section[1] == 0 then
io.stderr:write("Ошибка: Второй уровень без первого запрещен\n")
return nil
end
section[2] = section[2] + 1
section[3] = 0
return section[1] .. "." .. section[2]
elseif level >= 3 then
if section[1] == 0 or section[2] == 0 then
io.stderr:write("Ошибка: Третий уровень без первого или второго запрещен\n")
return nil
end
section[3] = section[3] + 1
return section[1] .. "." .. section[2] .. "." .. section[3]
end
end
function Header(el)
local cleaned_text = capitalize_first_letter(remove_trailing_punctuation(pandoc.utils.stringify(el.content)))
local is_unnumbered_section = is_unnumbered(cleaned_text)
UNNUMBERED = is_unnumbered_section
local level = math.min(el.level, 3) -- Принудительно ограничиваем вложенность до 3
if level > 1 and section[1] == 0 then
io.stderr:write("Ошибка: Заголовки 2 и ниже без нумерованного заголовка 1 уровня\n")
return el
end
if not is_unnumbered_section then
local section_number = get_section_number(level)
cleaned_text = section_number .. " " .. capitalize_first_letter(cleaned_text)
elseif level > 1 then
io.stderr:write("Ошибка: В ненумеруемом разделе не должно быть вложенных заголовков\n")
return el
end
el.content = pandoc.Inlines(cleaned_text)
return el
end
function Figure(el)
IMAGE = IMAGE + 1
local str = pandoc.utils.stringify(el.caption)
str = capitalize_first_letter(str)
str = remove_trailing_punctuation(str)
if UNNUMBERED then
io.stderr:write("Ошибка: Рисунок " .. str .. " в ненумерованном разделе!")
return el
end
str = "Рисунок " .. section[1] .. "." .. IMAGE .. "" .. str
el.caption = pandoc.Blocks(str)
return el
end
function Table(el)
TABLE = TABLE + 1
local str = pandoc.utils.stringify(el.caption)
str = capitalize_first_letter(str)
str = remove_trailing_punctuation(str)
if UNNUMBERED then
io.stderr:write("Ошибка: Таблица " .. str .. " в ненумерованном разделе!")
return el
end
str = "Таблица " .. section[1] .. "." .. IMAGE .. "" .. str
el.caption = pandoc.Blocks(str)
return el
end
--[[
Предполагается, что ранее в фильтре уже определены:
IMAGE, FORMULA, TABLE, CODE = 0, 0, 0, 0
UNNUMBERED, hdlis, section
функции Header, Figure, Table (обрабатывающие подписи и прочее)
Этот блок добавляет модификацию соседних блоков (предыдущего Para для ссылки,
а также оборачивание следующего блока для Table и CodeBlock).
--]]
-- Функция для приведения первой буквы строки к заглавной
function capitalize_first_letter(text)
local first = pandoc.text.sub(text, 1, 1)
local rest = pandoc.text.sub(text, 2)
return pandoc.text.upper(first) .. rest
end
-- Функция оборачивает блок в Div с кастомным стилем "aftertable"
function wrap_aftertable(block)
return pandoc.Div(block, pandoc.Attr("", {}, {["custom-style"] = "aftertable"}))
end
-- Функция, которая ищет в списке блоков блок по индексу index и, если это Para (или вложенный Para в Div),
-- удаляет завершающую пунктуацию у последнего текстового элемента и дописывает ссылку в виде:
-- " (Тип section.num):"
function append_reference_to_block(blocks, index, ref_name, section_num, num)
if index < 1 or index > #blocks then
return false
end
local blk = blocks[index]
-- Если блок Div, пробуем взять последний вложенный блок из его содержимого
if blk.t == "Div" then
if blk.c and #blk.c >= 2 and type(blk.c[2]) == "table" and #blk.c[2] > 0 then
blk = blk.c[2][#blk.c[2]]
else
return false
end
end
if blk.t ~= "Para" then
return false
end
-- Удаляем завершающую пунктуацию у последнего Str
local inlines = blk.c
for j = #inlines, 1, -1 do
if inlines[j].t == "Str" then
inlines[j].text = inlines[j].text:gsub("[%.?!…%s]+$", "")
break
end
end
-- Формируем текст ссылки, например: "(Рисунок 1.2):"
local ref_text = "(" .. ref_name .. " " .. section_num .. "." .. num .. "):"
ref_text = capitalize_first_letter(ref_text)
table.insert(inlines, pandoc.Space())
table.insert(inlines, pandoc.Str(ref_text))
return true
end
-- Главная функция фильтра, которая проходит по всем блокам документа и в нужных местах
-- модифицирует соседние блоки.
function Pandoc(doc)
local blocks = doc.blocks
local i = 1
while i <= #blocks do
local blk = blocks[i]
if blk.t == "Table" then
-- Для таблиц: добавляем ссылку в предыдущий абзац и оборачиваем следующий блок
if not append_reference_to_block(blocks, i - 1, "Таблица", section[1], TABLE) then
io.stderr:write("ОШИБКА: перед таблицей нет абзаца для ссылки\n")
end
if i + 1 <= #blocks then
blocks[i + 1] = wrap_aftertable(blocks[i + 1])
end
elseif blk.t == "Figure" then
-- Для рисунков: добавляем ссылку в предыдущий абзац
if not append_reference_to_block(blocks, i - 1, "Рисунок", section[1], IMAGE) then
io.stderr:write("ОШИБКА: перед рисунком нет абзаца для ссылки\n")
end
elseif blk.t == "CodeBlock" then
-- Для листингов: проверяем наличие подписи (caption) в атрибутах
local attr = blk.attr or {"", {}, {}}
local attributes = attr[3] or {}
local caption = attributes.caption
if not caption then
io.stderr:write("ОШИБКА: Листинг без подписи!\nИспользуйте ключ caption в CodeBlock\n")
else
CODE = CODE + 1
local ref_text = "Листинг " .. section[1] .. "." .. CODE .. "" .. caption
if not append_reference_to_block(blocks, i - 1, "Листинг", section[1], CODE) then
io.stderr:write("ОШИБКА: перед листингом нет абзаца для ссылки\n")
end
-- Вставляем блок с подписью перед текущим CodeBlock.
local caption_block = pandoc.Div(
pandoc.Para({ pandoc.Str(capitalize_first_letter(ref_text)) }),
pandoc.Attr("", {}, {["custom-style"] = "Code Caption"})
)
table.insert(blocks, i, caption_block)
i = i + 1 -- пропускаем вставленный блок
if i + 1 <= #blocks then
blocks[i + 1] = wrap_aftertable(blocks[i + 1])
end
end
end
i = i + 1
end
return pandoc.Pandoc(blocks, doc.meta)
end
function Meta(m)
if m.date == nil then
m.date = os.date("%e «%m» %Y")
end
return m
end
local function remove_trailing_punctuation_li (li)
return pandoc.Blocks(remove_trailing_punctuation(pandoc.utils.stringify(li)))
end
local function add_trailing_semicolon_li (li)
return pandoc.Blocks(pandoc.utils.stringify(li) .. ";")
end
local function add_trailing_stop_li (li)
return pandoc.Blocks(pandoc.utils.stringify(li) .. ".")
end
local function capitalize_first_letter_li (li)
return pandoc.Blocks(capitalize_first_letter(pandoc.utils.stringify(li)))
end
local function warnCapitalizedStart (li)
local str = pandoc.utils.stringify(li)
local capitalized = capitalize_first_letter(str)
if str == capitalized
then
io.stderr:write("Предупреждение: необходимо исправить вручную. В маркированном списке предложение должно начинаться со строчной (маленькой) буквы. Если текст пункта стартует с аббревиатуры, проигнорируйте данное предупреждение, иначе - исправьте вручную. Текст пункта:\n" .. str)
end
return li
end
function BulletList(el)
local li = el.content
li = pandoc.List.map(li, remove_trailing_punctuation_li)
li = pandoc.List.map(li, add_trailing_semicolon_li)
local ending = pandoc.List.at(el.content, -1)
pandoc.List.remove(li)
ending = add_trailing_stop_li(remove_trailing_punctuation_li(ending))
pandoc.List.insert(li, ending)
li = pandoc.List.map(li, warnCapitalizedStart);
el.content = li
return el
end
function OrderedList(el)
local li = el.content
li = pandoc.List.map(li, remove_trailing_punctuation_li)
li = pandoc.List.map(li, add_trailing_stop_li)
li = pandoc.List.map(li, capitalize_first_letter_li)
el.content = li
return el
end