feat: 完成 DOCX 通用结构映射与严格收口
This commit is contained in:
@@ -1,18 +1,29 @@
|
||||
local map_path = os.getenv("MD_TO_PDF_DOCX_MEDIA_MAP")
|
||||
local structure_plan_path =
|
||||
os.getenv("MD_TO_PDF_DOCX_STRUCTURE_PLAN")
|
||||
|
||||
if map_path == nil or map_path == "" then
|
||||
error("DOCX media map path is missing")
|
||||
end
|
||||
|
||||
local map_file, open_error = io.open(map_path, "rb")
|
||||
if map_file == nil then
|
||||
error("DOCX media map cannot be opened: " .. tostring(open_error))
|
||||
if structure_plan_path == nil or structure_plan_path == "" then
|
||||
error("DOCX structure plan path is missing")
|
||||
end
|
||||
|
||||
local map_content = map_file:read("*a")
|
||||
map_file:close()
|
||||
local function read_json(path, description)
|
||||
local file, open_error = io.open(path, "rb")
|
||||
if file == nil then
|
||||
error(
|
||||
description .. " cannot be opened: " .. tostring(open_error)
|
||||
)
|
||||
end
|
||||
local content = file:read("*a")
|
||||
file:close()
|
||||
return pandoc.json.decode(content, false)
|
||||
end
|
||||
|
||||
local media_map = pandoc.json.decode(map_content, false)
|
||||
local media_map = read_json(map_path, "DOCX media map")
|
||||
local structure_plan =
|
||||
read_json(structure_plan_path, "DOCX structure plan")
|
||||
local counters = {
|
||||
image = 0,
|
||||
mermaid = 0,
|
||||
@@ -86,6 +97,69 @@ local function replace_code_block(block)
|
||||
return pandoc.Para { image }
|
||||
end
|
||||
|
||||
local function append_inlines(target, value)
|
||||
target:extend(pandoc.Inlines(value))
|
||||
end
|
||||
|
||||
local function structure_paragraph(block)
|
||||
local inlines = pandoc.Inlines {}
|
||||
for index, segment in ipairs(block.segments) do
|
||||
if index > 1 then
|
||||
if block.separator == "tab" then
|
||||
inlines:insert(
|
||||
pandoc.RawInline(
|
||||
"openxml",
|
||||
"<w:r><w:tab/></w:r>"
|
||||
)
|
||||
)
|
||||
else
|
||||
inlines:insert(pandoc.Space())
|
||||
end
|
||||
end
|
||||
append_inlines(inlines, segment)
|
||||
end
|
||||
local paragraph = pandoc.Para(inlines)
|
||||
if block.styleId == nil or block.styleId == "" then
|
||||
return paragraph
|
||||
end
|
||||
return pandoc.Div(
|
||||
{ paragraph },
|
||||
pandoc.Attr("", {}, { ["custom-style"] = block.styleId })
|
||||
)
|
||||
end
|
||||
|
||||
local function marker_paragraph(marker)
|
||||
return pandoc.Para { pandoc.Str(marker) }
|
||||
end
|
||||
|
||||
local function structure_blocks(blocks)
|
||||
local result = pandoc.Blocks {}
|
||||
for _, block in ipairs(blocks) do
|
||||
if block.kind == "paragraph" then
|
||||
result:insert(structure_paragraph(block))
|
||||
elseif block.kind == "container" then
|
||||
local children = structure_blocks(block.blocks)
|
||||
result:insert(marker_paragraph(block.startMarker))
|
||||
result:extend(children)
|
||||
result:insert(marker_paragraph(block.endMarker))
|
||||
elseif block.kind == "section-break" then
|
||||
result:insert(marker_paragraph(block.marker))
|
||||
else
|
||||
error("Unsupported DOCX structure block: " .. tostring(block.kind))
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function suppress_first_body_heading(blocks)
|
||||
for index, block in ipairs(blocks) do
|
||||
if block.t == "Header" and block.level == 1 then
|
||||
blocks:remove(index)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function validate_counts()
|
||||
for _, kind in ipairs({ "image", "mermaid", "echarts" }) do
|
||||
if counters[kind] ~= #media_map[kind] then
|
||||
@@ -104,6 +178,16 @@ return {
|
||||
Image = replace_image,
|
||||
CodeBlock = replace_code_block
|
||||
}
|
||||
if structure_plan.titlePolicy.metadataTitle == "suppress" then
|
||||
transformed.meta.title = nil
|
||||
end
|
||||
if structure_plan.titlePolicy.firstBodyHeading == "suppress" then
|
||||
suppress_first_body_heading(transformed.blocks)
|
||||
end
|
||||
local blocks = structure_blocks(structure_plan.prefix)
|
||||
blocks:extend(transformed.blocks)
|
||||
blocks:extend(structure_blocks(structure_plan.suffix))
|
||||
transformed.blocks = blocks
|
||||
validate_counts()
|
||||
return transformed
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user