pandoc:用 docs 生成 epub

最近网上找了一本 epub 电子书,但目录结构紊乱,决定处理一下。

epub 本身很难处理。可以用 Calibre 来编辑 epub 文件,但这个不会。所以找了个网站 https://rare2pdf.com/epub-to-docx/ 转换成 docx 文件然后处理。

docx 编辑的注意事项

  1. 需要使用 Word/WPS 内置的标题 Headings 功能来处理书签和层级。
  2. 需要使用脚注来处理引用

使用 pandoc 转换文档

安装说明: https://zhuanlan.zhihu.com/p/682455380

转换命令

pandoc input.docx -o output.epub --epub-cover-image=cover.jpg --metadata title="书名"

通过上述命令设置封面图片和书名。默认情况下,pandoc 只会处理第一级 heading 并为其设置书签链接。

我们可以通过添加 --epub-chapter-level=5 参数的方式,来指定 heading 深度。

但请注意,不论标题层级大小,在 pandoc 转换的时候,都会被解析成一个 epub 章节,对应一个 xhtml 文件,然后这些 xhtml 文件顺序拼接,最后显示为 epub 电子书。

弹出式脚注

pandoc 的自动转化下,脚注会以超链接的形式跳转到文档章节尾部。然后再借助脚注最后的超链接返回原位。这样非常干扰阅读体验。

epub3 支持弹出式脚注。即可以生成一个小弹窗,显示脚注内容,而不需要跳转到章节尾部。

要想支持这个功能,需要添加额外的 lua 脚本,来识别转换原脚注文本。并添加 --lua-filter=epub_footnote.lua 参数。

完整的命令行脚本可以是:

pandoc input.docx -o output.epub --epub-cover-image=cover.jpg --metadata title="书名" --epub-chapter-level=5 --lua-filter=epub_footnote.lua

微信读书支持弹出式脚注。特别是通过微信读书APP内加载的书能够正常支持弹出式脚注,但通过 epub 格式导入的,以 aside 标签作为标注的脚注,似乎并不支持。也可能是我脚本写的不对。留待后续探索。

-- epub_footnote.lua

counter = 1

function Para(el)
  local blocks = {}
  local inlines = {}

  for _, inline in ipairs(el.content) do
    if inline.t == 'Note' then
      local id = "fn" .. counter
      local ref = pandoc.RawInline('html', '<a href="#' .. id .. '" epub:type="noteref">[' .. counter .. ']</a>')
      table.insert(inlines, ref)

      local footnote_content = pandoc.utils.stringify(inline.content)
      local aside_html = '<aside id="' .. id .. '" epub:type="footnote"><p>[' .. counter .. ']' .. footnote_content .. '</p></aside>'
      table.insert(blocks, pandoc.RawBlock('html', aside_html))

      counter = counter + 1
    else
      table.insert(inlines, inline)
    end
  end

  -- 返回一个段落 + 若干个脚注块
  local result = { pandoc.Para(inlines) }
  for _, b in ipairs(blocks) do
    table.insert(result, b)
  end
  return result
end


function Header(el)
  local blocks = {}
  local inlines = {}

  for _, inline in ipairs(el.content) do
    if inline.t == 'Note' then
      local id = "fn" .. counter
      local ref = pandoc.RawInline('html', '<a href="#' .. id .. '" epub:type="noteref">[' .. counter .. ']</a>')
      table.insert(inlines, ref)

      local footnote_content = pandoc.utils.stringify(inline.content)
      local aside_html = '<aside id="' .. id .. '" epub:type="footnote"><p>[' .. counter .. ']' .. footnote_content .. '</p></aside>'
      table.insert(blocks, pandoc.RawBlock('html', aside_html))

      counter = counter + 1
    else
      table.insert(inlines, inline)
    end
  end

  -- 新的标题块
  local new_heading = pandoc.Header(el.level, inlines, el.identifier, el.classes, el.attributes)
  local result = { new_heading }
  for _, b in ipairs(blocks) do
    table.insert(result, b)
  end
  return result
end

posted @ 2025-08-02 17:39  Crimson深红  阅读(166)  评论(0)    收藏  举报