lua major update
This commit is contained in:
parent
8eef9b492c
commit
4a0879e81c
7 changed files with 408 additions and 243 deletions
267
filter2.lua
Normal file
267
filter2.lua
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
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
|
||||
Loading…
Reference in a new issue