Core API
来自 @imposia/core 的框架无关页面文档与 Publication 控制器。
@imposia/core 是权威的页面文档运行时:分页、生命周期、解析器边界、扩展、打印和导出,全部不依赖 React。这里的一切都只在浏览器中运行。
Core 页面文档
mountPageDocument
mountPageDocument(
container: HTMLElement,
source: PageSource,
options?: PageDocumentOptions,
): PageDocumentController;该函数立即追加一个 canonical iframe,启动一个暂存世代,并同步返回。读取第一份文档前,请先等待 ready。
const controller = mountPageDocument(host, { html: "<article><h1>你好</h1></article>" });
const pageDocument = await controller.ready;PageSource 为 { html: string; baseUrl?: string } 或 { lightDom: Element | DocumentFragment; baseUrl?: string }。
PageDocumentOptions 字段 | 类型 / 用途 |
|---|---|
css | readonly string[];附加的作者 CSS。 |
assetResolver | 处理字体、图片、媒体和样式表请求的异步解析器。 |
page | { size?, orientation?, margin? };size 接受 A3、A4、A5、B4、B5、Letter、Legal、Ledger 或自定义宽高。 |
limits | 可选的输入、节点、资源、期限、页数、布局轮次和生成产物上限。 |
headerTemplate、footerTemplate | 页面装饰标记。 |
decorateBlankPages | 空白页是否接受装饰。 |
experimental | { footnotes?: boolean; pageFloats?: boolean };两者都是实验性特性,超出各自限制时分别以 FOOTNOTE_DEFERRED 或 PAGE_FLOAT_FALLBACK 警告回退。 |
extensions | 有序的 PageExtension 或 PublicationExtension 值。 |
compose | { yieldBudgetMs?: number; scheduler?: () => Promise<void> };主线程协作式分页,默认预算 8 ms。传 Infinity 可关闭向调度器让出。 |
signal | 取消初始世代。 |
onProgress | staging 每分配一页就收到轮次内的 { completedPages, pass, provisional: true }。之后的收敛轮次会重置计数。 |
调度器、字体和图片的等待都计入按实际时间计算的 limits.resourceDeadlineMs。进度描述的是暂存中的临时工作;最新已提交的世代只从 controller.current 读取。
AssetResolver 接收 { url, kind, baseUrl, signal },解析为 { status: "resolved", bytes, mimeType, resolvedUrl? } 或 { status: "blocked", reason? }。实现方法见加载图片与字体。
扩展
PageExtension 和 PublicationExtension 还可以实现同步的 finalizePage(page, context)。它在 Core 解析完装饰和 margin box 之后、提交之前,对每个被接受的页面运行一次,因此 page.element 是可实际测量的页面元素。page.tableFragments 列出续排的表格片段,带原始表格与从 1 开始的续排索引。修改会保留进已提交的 iframe。无论 decorateBlankPages 如何设置,它都会在每个已分配页面上运行,包括有意插入的空白页。
finalizePage 必须返回 undefined
finalizePage 返回任何非 undefined 的值都会让整个世代被拒绝。该钩子通过修改活动页面元素来传达结果,绝不通过返回替代值。
拆分表格需要在续排片段中冻结实测像素列宽时,将 createTableColgroupExtension() 作为可选扩展启用。Core 始终携带作者的 <colgroup> 元素,但默认从不合成列宽。
PageDocumentController
| 成员 | 返回值 | 就绪与错误 |
|---|---|---|
ready | Promise<PageDocument> | 首次提交;无法提交时拒绝。 |
current | PageDocument | undefined | 更新失败期间和之后保留上一次提交;销毁后清空。 |
update(source, options?) | Promise<PageDocument> | 启动一个世代;更新的 update 会中止进行中的那个。支持 options.signal。 |
print() | Promise<void> | 等待最新工作完成,然后通过顶层文档中的隔离快照打印最近一次成功提交。无提交或销毁后拒绝。 |
destroy() | Promise<void> | 中止生成与导出工作,移除 iframe,释放资源,并等待被跟踪的工作。幂等。 |
PageDocument
| 成员 | 类型 / 返回值 | 含义 |
|---|---|---|
iframe | HTMLIFrameElement | Core 拥有的 canonical 框架。 |
generation、pageCount | number | 提交的世代号与页数。 |
pages | readonly PageMetadata[] | 页码、左右页、命名、空白状态、几何、尺寸和正文文本。 |
warnings | readonly PageWarning[] | 当前世代的诊断。 |
timings | { totalMs; resourceMs; paginationMs } | 该世代的耗时,单位毫秒。 |
exportEpub(options) | Promise<Blob> | 等待最新活动工作完成,把最近一次提交的源内容导出为可重排 EPUB 3.3 Blob。 |
EPUB 选项要求 metadata: { title, language, identifier, modified? },并可选接受 signal 与 { maxEntries?, maxBytes? } 上限。导出是语义性的:页面包装、页边装饰、生成的计数器和只存在于页面上的实验性产物都被排除。
警告
每个已提交的 PageWarning 携带 code、message 和冻结的 location(含 generation、entryId 与 page,未知时各自为 undefined)。扩展诊断使用带命名空间的 EXTENSION_${string} 代码,并注明其 extension。
Core 辅助函数
prepareDocument(html: string, options?: PrepareDocumentOptions): PreparedDocument;
pageWarningTargetBounds(document: PageDocument, warning: PageWarning): PageWarningTargetBounds | undefined;
hasPageDocumentFrameSandbox(iframe: HTMLIFrameElement): boolean;
committedFrameGeneration(frameDocument: Document): number | undefined;
selectBlankMarkers(markers: PageSideConstraint[], pages: Map<number, number>): number[];prepareDocument同步规范化并清理 HTML,返回{ html, headerTemplate?, footerTemplate?, warnings }。其选项为headerTemplate、footerTemplate和allowRemoteResources。API 装饰选项会覆盖内嵌模板。它使用浏览器原生 HTML 解析器,因此与其他所有 Core API 一样仅限浏览器运行,警告按错误恢复后的文档顺序排列。pageWarningTargetBounds返回 iframe 视口坐标下的实时{ left, top, width, height }。对来自其他文档或无法定位的警告返回undefined。hasPageDocumentFrameSandbox仅在 sandbox 令牌恰好是公开集合(allow-same-origin与allow-modals)时为 true。committedFrameGeneration返回 Core 在提交时刻印在 canonical frame 上的世代;若该帧尚未提交带标记的世代,则为undefined。用于区分尚未送达的较新提交与真正的损坏。它不读取也不校验页面 marker。selectBlankMarkers返回为满足左右页奇偶而需要空白页的 marker ID,并考虑之前的选择。marker 没有页面映射时抛出。
Core Publication
mountPublication
mountPublication(
container: HTMLElement,
snapshot: PublicationSnapshot,
options?: PublicationOptions,
): PublicationController;它把有序条目组合成一个页面序列,并同步返回。无效的快照或 Publication 扩展可能在挂载期间抛出。
const controller = mountPublication(host, {
metadata: { title: "手册", language: "zh-CN" },
entries: [
{ id: "cover", title: "封面", html: "<h1>手册</h1>" },
{ id: "start", title: "开始", html: "<h1>开始</h1><p>第一步。</p>" },
],
});
const publication = await controller.ready;PublicationSnapshot 包含 metadata: { title, language?, identifier? } 和有序条目。每个条目要求一个唯一且不含空白或控制字符的 id、一个 title、可选的 baseUrl,以及 html 或 lightDom 中恰好一个。PublicationOptions 与 PageDocumentOptions 相同,只是 extensions 只接受 PublicationExtension[],并增加 pageNumbering。设为 pageNumbering: "entry" 时,每个条目从新页开始,counter(page)、counter(pages) 和 pageNumber/totalPages 模板令牌在条目内计数,因此批量打印发票时每张发票都显示“Page 1 of 2”。页面元数据、导航、搜索、条目页码范围、扩展的 decoratePage 输入和 target 引用仍按全局计数。默认值 "publication" 对整个 Publication 编号。
PublicationController
| 成员 | 返回值 | 就绪与错误 |
|---|---|---|
ready | Promise<PublicationDocument> | 首个已提交的 Publication。 |
current | PublicationDocument | undefined | 当前提交。 |
resolveDestination(id) | PublicationDestination | undefined | 在当前世代中解析大纲或搜索 ID。 |
search(query) | readonly PublicationSearchResult[] | 提交前为空;之后搜索当前索引。 |
navigate(destination) | void | 除非完整的导航目标匹配且存在于当前世代,否则抛出。 |
update(snapshot, options?) | Promise<PublicationDocument> | 校验并暂存一份完整快照;支持 options.signal。 |
print() | Promise<void> | 使用页面控制器打印最近一次成功提交的行为。 |
destroy() | Promise<void> | 销毁底层的页面与 Publication 资源。 |
PublicationDocument 在 PageDocument 基础上增加已提交的 metadata、entries 和嵌套的 outline。每个条目有一段闭区间的全局 pageRange。PublicationDestination 为 { id, entryId, page, generation };它绑定到世代,对过期值 navigate 抛出类型化 ImposiaError(STALE_PUBLICATION_DESTINATION)。搜索结果为 { entry, page, excerpt, destination }。