calibre 电子书编辑工具 API 深度指南:Container 容器模型与 Polish 模块实战
2026/9/23 20:27:35 网站建设 项目流程

calibre 电子书编辑工具 API 深度指南:Container 容器模型与 Polish 模块实战

【免费下载链接】calibreThe official source code repository for the calibre ebook manager项目地址: https://gitcode.com/GitHub_Trending/ca/calibre

本指南以 calibre 官方仓库 manual/polish.rst 的 API 文档为骨架,系统讲解电子书编辑工具(E-book editing tools / Edit Book)的核心架构:Container容器对象如何把一本书表示为“HTML + 资源文件”的集合,以及calibre.ebooks.oeb.polish.*各模块提供的文件管理、HTML 修复、封面处理、CSS 清理、目录(ToC)生成、文件拆分合并等可编程能力。读完本文,你将掌握如何用几行 Python 代码对 EPUB/AZW3 书执行批量编辑,并能在 E-book editor 插件中直接操作当前打开的书籍。

概述:编辑工具的两大组成

calibre 的电子书编辑工具由两部分构成(见 manual/polish.rst):

  1. Container容器对象(定义于calibre.ebooks.oeb.polish.container):将一本书表示为一个文件夹里的 HTML 与资源文件集合,是所有编辑操作的数据中枢;
  2. 一组模块级函数工具:分布在calibre.ebooks.oeb.polish.*的各个子模块中,例如replaceprettyjacketsplitcovercsstocfonts,用于在容器上执行具体操作。

两者配合,即可对书籍做无副作用的程序化编辑:所有操作都作用于容器,最后统一commit回磁盘。

获取 Container 对象

命令行 / 脚本场景

对一个位于某路径的书文件(EPUB、AZW3、MOBI 等)获取容器:

from calibre.ebooks.oeb.polish.container import get_container container = get_container('Path to book file', tweak_mode=True)

get_container的原型见 container.py:

def get_container(path, log=None, tdir=None, tweak_mode=False, ebook_cls=None) -> Container:

其行为要点:

  • 根据文件扩展名自动选择容器实现:.azw3.mobi.original_azw3.original_mobi使用AZW3Container.kepub.original_kepub使用KEPUBContainer,其余(含目录)默认使用EpubContainer
  • EPUB 会被解压到临时目录(PersistentTemporaryDirectory('_epub_container'))再解析,is_dir为 True 时按目录方式复制;
  • tweak_mode是解析模式开关:True表示“编辑模式”(HTML/CSS 解析更宽容,保留原始格式细节,供编辑器使用),False表示“打磨模式”(polish,可对解析做预处理)。源码中ContainerBase明确注释:tweak_mode = False(polishing 使用),编辑器则使用tweak_mode=True(见 container.py)。

插件场景:获取正在编辑的书

如果你正在为E-book editor(Edit Book)编写插件,可用模块级函数获取当前编辑中的容器:

from calibre.gui2.tweak_book import current_container container = current_container() if container is None: report_error # No book has been opened yet

该函数的实现位于 src/calibre/gui2/tweak_book/init.py,内部就是一个模块级全局变量_current_container,由set_current_container()在打开书籍时设置。因此在编辑器未打开任何书时,current_container()返回None,插件代码必须做判空处理。

此外,在插件Tool子类内部,self.current_container属性直接封装了上面的调用(见 plugin.py)。

Container 对象:电子书的统一视图

Container类完整定义于 src/calibre/ebooks/oeb/polish/container.py。其文档字符串定义了三个核心概念:

  • 根目录(root folder):电子书的基准目录,书内所有文件都在该目录或其子目录下;
  • Names:相对于根目录的文件路径,永远使用 POSIX 分隔符(/)、不带 URL 引号、且处于 NFC Unicode 规范化形式。Names 是容器内文件的“规范标识符”,容器的大部分方法都以 name 为参数;
  • Clones:容器支持高效的磁盘克隆(clone_data/data_for_clone),这是 E-book editor 实现检查点(checkpoint)/ 撤销功能的基础。正因如此,永远不要直接访问文件系统,应通过raw_data()open()读写书内文件。

各容器子类通过类属性区分能力:book_typeepub/azw3)、is_dirSUPPORTS_TITLEPAGESSUPPORTS_FILENAMESMAX_HTML_FILE_SIZE(EPUB 容器为 260 KiB)。

关键方法速览

方法作用
raw_data(name)/open(name, mode)读取/写入书内文件(推荐,兼容克隆机制)
parsed(name)返回文件的解析树(HTML/CSS/XML)
replace(name, obj)用新解析对象替换某文件内容并标记 dirty
dirty(name)标记文件已修改,等待提交
commit(outpath=None, keep_parsed=False)把所有 dirty 文件写回磁盘/写出书文件
add_file(...)添加文件,自动登记 OPF manifest 与 spine
rename(current_name, new_name)重命名文件并处理 OPF
remove_item(name)从容器删除文件(含 manifest/guide)
add_name_to_manifest(name)为文件创建 manifest 条目,返回 item id
manifest_has_name(name)/make_name_unique(name)manifest 检查与重名处理
opf()/mi()/opf_version()访问 OPF 解析树、元数据对象与版本
spine_items/spine_names()/set_spine(...)阅读与重排 spine
href_to_name(href, base)/name_to_href(name, base)href(带引号)与 name 互转
iterlinks(name)遍历文件中的链接(含行号)
compare_to(other)对比两个容器差异(测试用)

所有写操作(如add_filedirty)最终由commit()汇总:commit()遍历self.dirtied集合逐个调用commit_item()(见 container.py)。修改过的文件只有调用commit()才会真正落盘。

管理容器中的组件文件(replace 模块)

模块calibre.ebooks.oeb.polish.replace提供三类文件级操作(源码见 replace.py)。

replace_links —— 批量替换链接

def replace_links(container, link_map, frag_map=lambda name, frag: frag, replace_in_opf=False):

遍历容器内所有文件,把指向旧文件名的链接改为新文件名:

  • link_map{旧规范名: 新规范名}映射,例如{'images/old.png': 'images/new.png'}
  • frag_map:可调用对象,接收(name, anchor)返回新锚点,用于同时改写 HTML 内锚点(如#chapter2);
  • replace_in_opf:为False(默认)时跳过 OPF 文件,为True时连 OPF 里的 href 一并替换。

实现上由LinkReplacer类配合container.replace_links(name, repl)逐文件执行。

rename_files —— 重命名并自动修复链接

def rename_files(container, file_map):
  • file_map{旧规范名: 新规范名},例如{'text/chapter1.html': 'chapter1.html'}
  • 自动调用container.rename()并随后replace_links(container, link_map, replace_in_opf=True),因此所有指向旧文件名的链接会被同步更新
  • 安全性检查:拒绝循环重命名(目标同时也是源)、目标已存在、以及目标名重复;大小写变化在大小写不敏感文件系统上会被特殊处理。

get_recommended_folders —— 推荐文件夹

def get_recommended_folders(container, names):

根据容器内同类文件多数所在位置,为给定文件名推荐存放目录;若某种类型不存在,则推荐 OPF 所在文件夹。内部按 MIME 类型归类(text / style / font / opf / toc 等,见mt_to_category),同名工具被 Edit Book 的“Rationalize Folders”(整理文件夹)功能使用。

同模块还提供replace_ids(container, id_map)(批量改写 id 及指向它们的 idref)、smarten_punctuation(container, report)(智能标点转换)、replace_file(...)(用外部文件替换书内文件)、remove_links_to(container, predicate)(按谓词删除链接)等函数。

美化打印与自动修复解析错误(pretty 模块)

模块calibre.ebooks.oeb.polish.pretty提供对 HTML/CSS/XML 的美化与修复能力(源码见 pretty.py):

函数行为
fix_html(container, raw)HTML5 解析算法修复raw字符串中的解析错误,返回序列化后的 HTML
fix_all_html(container)对容器内所有 HTML 文档执行修复(标记 dirty 触发重写)
pretty_html(container, name, raw)美化单个 HTML 字符串
pretty_css(container, name, raw)美化单个 CSS 字符串
pretty_xml(container, name, raw)美化单个 XML 字符串;若name是 OPF,还会执行 OPF 专属美化(pretty_opf
pretty_all(container)美化容器内全部 HTML/CSS/XML 文件

内部实现要点:

  • pretty_html先经parse_xhtml解析成树,再通过pretty_html_tree对块级元素重新缩进;pretty_script_or_style专门处理<script>/<style>内的内容;
  • pretty_cssparse_css解析后序列化,实现 CSS 规则的统一缩进;
  • fix_all_html并不逐个比较新旧内容,而是直接对每个 OEB 文档调用parsed(name)+dirty(name),让 HTML5 解析器在重写时自动纠正错误。

这两个函数集正是 Edit Book 中Fix HTMLPretty Print工具的后端:GUI 菜单Tools → Fix HTML/Tools → Pretty Print(见Boss.fix_htmlBoss.pretty_print,boss.py)。

管理书籍封套(jacket 模块)

模块calibre.ebooks.oeb.polish.jacket用于管理 EPUB 的封套页(jacket,书籍开头的装饰页)(源码见 jacket.py):

  • remove_jacket(container):删除书中已存在的封套页及其图片资源(remove_jacket_images);
  • add_or_replace_jacket(container):为书添加封套页;若已存在旧式/现式封套则先替换。

实现细节:find_existing_jacket在 spine 中定位封套文档,is_legacy_jacket/is_current_jacket用于识别不同版本的封套结构,render_jacket根据书籍元数据渲染封套 HTML,replace_jacket完成内容替换并处理 OPF 条目。

文件的拆分与合并(split 模块)

模块calibre.ebooks.oeb.polish.split提供对 HTML 文档的拆分与合并(源码见 split.py)。

split —— 单点拆分

def split(container, name, loc_or_xpath, before=True, totals=None):

name指定的文件在loc_or_xpath处一分为二,自动迁移所有受影响的链接与引用

  • loc_or_xpath:XPath 表达式,如//h:div[@id="split_here"];也可传内部使用的loc(预览面板拆分时使用);
  • before=True表示在命中元素之前拆分,否则在之后;
  • 失败保护:若定位节点在<table>内或为<body>标签则抛出AbortError;若 HTML 解析计数不一致,会强制用 HTML5 解析器重试,并提示“Try running the Fix HTML tool before splitting”。

multisplit —— 多点拆分

def multisplit(container, name, xpath, before=True):

按 XPath 命中的所有元素将文件拆成多份,逻辑基于split的循环调用。

merge —— 合并文件

def merge(container, category, names, master):

把同类别(category)的一组文件合并进master

  • HTML 合并由merge_html实现,insert_page_breaks=False时按顺序拼接各文件 body 内容,并把锚点改名以保证唯一性(unique_anchorremove_name_attributes);
  • CSS 合并由merge_css实现,把各样式表规则并入主样式表;
  • 合并后调用remove_names_from_toc等清理工作,确保 ToC 不再指向被合并掉的旧文件。

管理封面(cover 模块)

模块calibre.ebooks.oeb.polish.cover负责书籍封面的设置与识别(源码见 cover.py):

函数行为
set_cover(container, cover_path, report=None, options=None)把外部图片设为书籍封面:写入封面图、生成/替换封面页(EPUB 走set_epub_cover,AZW3 走set_azw3_cover),并清理旧封面残留
mark_as_cover(container, name)把容器内已有文件标记为封面(EPUB 走mark_as_cover_epub,AZW3 走mark_as_cover_azw3
mark_as_titlepage(container, name, move_to_start=True)把指定文件标记为书名页(titlepage),默认移到 spine 开头
find_cover_image(container, strict=False)智能探测书籍封面图
has_epub_cover(container)判断 EPUB 是否已有封面

EPUB 封面处理会同时维护三处状态:OPF 的metadatameta name="cover")、manifest中封面图条目、以及 spine 中的封面页;create_epub_cover负责生成包含封面图的书名页 HTML,remove_cover_image_in_page在更换封面时清理旧图。

处理 CSS(css 模块与 fonts 模块)

remove_unused_css —— 清理无用样式

def remove_unused_css( container, report=None, remove_unused_classes=False, merge_rules=False, merge_rules_with_identical_properties=False, remove_unreferenced_sheets=False, ):

删除书中所有不匹配任何实际内容的 CSS 规则(源码见 css.py):

  • remove_unused_classes=True:同时移除 HTML 中不匹配任何 CSS 规则的class属性;
  • merge_rules=True:合并选择器相同的规则(merge_identical_selectors);
  • merge_rules_with_identical_properties=True:合并属性完全相同的规则;
  • remove_unreferenced_sheets=True:移除未被任何内容引用的样式表文件;
  • 内部通过mark_used_selectors用 CSS 选择器引擎(select)对全部文档做使用标记,get_imported_sheets处理@import链(默认递归深度 10),未命中的选择器与规则进入removal_stats被删除。

filter_css —— 过滤 CSS 属性

def filter_css(container, properties, names=()):

从样式表中删除指定 CSS 属性(如filter_css(container, {'color'})),可限定只处理names列出的文件;transform_css是更通用的“CSS 变换”入口(可传入transform_sheet/transform_style回调,支持把font-size转成pt/em这类整体重写),二者实现均在 css.py。

change_font —— 全局更换字体

def change_font(container, old_name, new_name=None):

把字体族old_name全局替换为new_name(源码见 fonts.py):

  • 作用于样式表、<style>标签与内联style属性三处;
  • old_name内嵌字体,替换时会被一并移除;
  • new_name=None表示只移除该字体族而非替换;
  • 底层由font_family_data汇总所有字体声明,change_font_in_sheet/change_font_in_declaration分别处理样式表与声明。

处理目录 ToC(toc 模块)

模块calibre.ebooks.oeb.polish.toc提供目录的生成与提交能力(源码见 toc.py)。

从 XPath 生成 ToC

def from_xpaths(container, xpaths, prefer_title=False):

用一组 XPath 表达式生成目录,每个表达式对应一级:['//h:h1', '//h:h2', '//h:h3']会从<h1>/<h2>/<h3>生成三级目录。实现会自动剔除在所有 spine 文档中均无匹配的“空层级”,并用node_level_map维护父子关系;prefer_title=True时优先取title属性作为条目文本。

从链接与文件生成 ToC

  • from_links(container):把 spine 文档中已有的<a>链接结构转换为目录(适用于从 HTML 链接推断章节结构);
  • from_files(container):把每个 spine 文件作为目录的一个条目(文件级目录)。

提交与内联目录

  • commit_toc(container, toc, lang=None, uid=None):根据 EPUB 版本写入 NCX 或 EPUB3nav文档——commit_ncx_toc负责 EPUB2 的toc.ncxcommit_nav_toc负责 EPUB3 的nav.xhtml,并同步ensure_container_has_nav/set_landmarks(landmarks 导航);
  • create_inline_toc(container, title=None):生成内联目录页toc_to_html),把当前 ToC 渲染成书内一个 HTML 页面并插入 spine。

Edit Book 插件工具类(Tool)

为 E-book editor 编写插件时,工具类继承:

class Tool: # calibre.gui2.tweak_book.plugin.Tool

定义于 plugin.py,常用成员:

  • self.plugin:所属calibre.customize.Plugin对象;
  • self.boss:全局Boss对象,用于控制用户界面(get_boss());
  • self.gui:编辑器主窗口;
  • self.current_container:当前编辑书籍的Container(即current_container());
  • self.name:工具的唯一名称(作 key 使用);
  • allowed_in_toolbar/allowed_in_menu:用户是否可把该工具放入插件工具栏/插件菜单;
  • 必须覆写的方法:create_actionregister_shortcut

register_shortcut(qaction, unique_name, default_keys=(), ...)用于注册快捷键,例如default_keys=('Ctrl+J', 'F9');注册后用户可在编辑器的快捷键偏好设置中自定义,若与内置快捷键或用户配置冲突则自动忽略。

控制编辑器用户界面(Boss)

编辑器的用户界面由一个全局Boss对象统一控制(calibre.gui2.tweak_book.boss.Boss,见 boss.py),插件代码可通过get_boss()获取它来执行常见任务,常用方法包括:

方法用途
currently_editing()当前是否处于编辑状态
open_book(path, ...)/new_book()/import_book(path)打开/新建/导入书籍
book_opened(job)书籍打开完成的回调(可在此刷新插件 UI)
add_file()/add_files()/add_cover()添加文件/封面
edit_toc()/insert_inline_toc()编辑 ToC / 插入内联目录
polish(action, name, parent=None)调用 polish 工具(Editor Polish 菜单入口)
transform_html()/transform_styles()打开 HTML/CSS 变换对话框
manage_fonts()/manage_fonts_embed()/manage_fonts_subset()字体管理(内嵌/子集化)
rationalize_folders()整理文件夹结构
rename_requested(...)/bulk_rename_requested(...)文件重命名/批量重命名
do_global_undo()/do_global_redo()全局撤销/重做
add_savepoint(msg)/rewind_savepoint()保存点管理(撤销层级)
show_current_diff(...)/compare_book()显示当前改动差异 / 对比书籍
set_modified()标记书籍已修改
fix_html(current)/pretty_print(current)修复 HTML / 美化打印

完整示例:脚本化打磨一本书

把上述 API 串起来,一个典型的“打磨流程”脚本大致如下(脚本式使用需在 calibre 的 Python 环境运行,例如calibre-debug -e script.py,入口见 develop/calibre-debug):

from calibre.ebooks.oeb.polish.container import get_container from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all from calibre.ebooks.oeb.polish.css import remove_unused_css from calibre.ebooks.oeb.polish.toc import from_xpaths, commit_toc # 1. 以打磨模式打开书籍(tweak_mode=False,即 polish 模式) container = get_container('/path/to/book.epub') # 2. 修复所有 HTML 解析错误并美化 fix_all_html(container) pretty_all(container) # 3. 清理无用 CSS,合并重复规则 remove_unused_css(container, remove_unused_classes=True, merge_rules=True, remove_unreferenced_sheets=True) # 4. 按 h1/h2 重新生成三级目录 toc = from_xpaths(container, ['//h:h1', '//h:h2', '//h:h3']) commit_toc(container, toc) # 5. 写回磁盘 container.commit()

若是在E-book editor 插件里,则第 1 步改为from calibre.gui2.tweak_book import current_container,第 5 步改为依赖编辑器的“保存”流程,插件只需对容器做修改即可(编辑器通过克隆容器实现撤销/恢复)。

参考资源

  • API 文档原文:manual/polish.rst
  • 容器核心实现:src/calibre/ebooks/oeb/polish/container.py
  • 各工具模块:replace.py、pretty.py、jacket.py、split.py、cover.py、css.py、toc.py、fonts.py
  • 编辑器 GUI 集成:src/calibre/gui2/tweak_book/init.py 中的current_container、plugin.py 中的Tool、boss.py 中的Boss
  • 插件编写综合指南:manual/creating_plugins.rst(含 manual/plugin_examples/editor_demo/main.py 可运行的编辑器插件示例)
  • 单元测试:src/calibre/ebooks/oeb/polish/tests/(container、split、cascade、structure 等测试覆盖了上述多数 API)

【免费下载链接】calibreThe official source code repository for the calibre ebook manager项目地址: https://gitcode.com/GitHub_Trending/ca/calibre

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询