跳到正文
QuireKit MSFE
开发者文档
v0.0.0 · 实验版本

自定义节点导出为 Word、PDF 与 OFD

共用原生内容转换和图片渲染,处理模板、资源失败、取消与 Word 元数据恢复。

导出适配器读取 Markdown 源码快照,与你的 Vue/React 节点视图分开。节点的屏幕外观不会自动 进入文件;需要明确把它转换为可打印内容。Word、PDF 与 OFD 可以复用同一份 extensions。 模板的选择方式见 MD Editor。

Quire Render 与多格式导出

Quire Render 是 QuireKit 的统一渲染引擎。Word、PDF 与 OFD 先复用节点的语义转换和 模板;Word 将结果编译为可编辑 DOCX,PDF/OFD 则通过 Classic 完成分页,再把逐页内容 交给 Quire Render 的对应后端。

名称职责
qkView声明式组合组件,处理组件内部的局部布局
Quire Display List用 QuireDisplayList 表达文字、路径、图片、裁剪等绘制指令
Render Backend把绘制指令输出到 Canvas、SVG、PDF 或 OFD

文档分页由专门的排版引擎负责,qkView 是生成绘制指令的一种入口。当前 Markdown 导出接入点仍是下面的原生内容或图片转换;Vue/DOM 视图、qkView 交互控件不会自动转换为 Word 或固定版式文件。各后端的字体、资源和损失报告也需要分别处理。

选择输出方式

需求转换器输出WordPDF / OFD
提示块、业务卡片、可阅读的业务字段原生段落、列表、表格、底纹和边框可编辑文字可选择文字,使用 Classic 分页
图表或复杂图形宿主生成的 PNG/JPEG图片,可附恢复元数据图片,保持比例并限制宽高
下次导入恢复业务数据带版本的 Custom XML 元数据和可见内容校验通过后恢复不保存这套恢复数据
自定义 PDF/OFD 矢量绘制或分页算法当前没有专用公开接口—需要另行扩展共享排版能力

PDF/OFD 消费现有 Classic 能识别的可见 LitAST;它不会执行 DOCX 的 package pass,也不加载 任意第三方 Classic schema。只写私有 XML 或返回一个未知 LitAST 类型,不能让 PDF/OFD 显示节点。

转换生命周期

  1. parse(source, next):仅当需要新 Markdown 语法时扩展解析。已有代码围栏可直接复用。
  2. prepare(root, context):校验业务数据、获取资源,把匹配节点投影为临时节点。异步工作在此完成。
  3. addDocx():返回与 Classic 相同的 docx.codec.node() 贡献。convertChildren 复用已有转换, resolveService(markdownExportTemplateService) 读取本次已解析模板。
  4. 公共呈现阶段约束表格、图片和页边距;Word 编译为 DOCX,PDF/OFD 转入临时 Classic 文档并分页编码。

每个 modelType 只有一个转换所有者。不要直接认领所有 code 节点后丢弃不认识的语言。 下面先把验证成功的 approval-card 围栏改成独立的 exampleApprovalCard,其他代码继续使用默认转换。

原生业务卡片

这是独立的导出接入示例,没有给编辑器新增卡片视图或插入菜单。业务数据保存在普通围栏中:

markdown

完整实现使用公开包入口。TypeScript 消费者需要 @types/mdast;通过 declare module 'mdast' 声明临时节点。卡片复用模板的 containers.note 外观,单元格内距使用 Word 的 twips, 边线宽度使用八分之一磅;正文样式、字体和分页仍由公共导出链决定。

export-nodes/native.tsts

title 和 paragraphs 都是纯文本。示例限制字段、长度和数量,无效数据保留代码并产生诊断。 长卡片允许跨页,不设置固定行高。需要富文本正文时应定义清晰的子节点合同,使用 convertChildren;不要把用户 HTML 直接拼进 XML。

ts

这段最小调用使用拉丁文字。中文与自定义字体必须按 PDF 与 OFD 导出 准备可嵌入字节,并在度量前加载相同字体。word.data / pdf.data 是下载内容,诊断通过 diagnostics 返回;代码本身不触发下载或修改编辑器。

使用同一转换器导出 OFD

商业包 @quirekit/md-editor-export 的 /ofd 入口与 PDF 共享临时文档准备和 Classic 分页, 直接生成 OFD。保留上面的 markdown、extensions、template,显式传入宿主字体:

ts

OFD 没有 PDF 的标准字体,拉丁文字也必须提供完整 TTF/OTF 字节;fonts 的 family、 aliases 或 acceptedSubstitutions 应覆盖模板字体,并在调用前将相同物理字体加载到 浏览器用于度量。默认嵌入 Unicode 子集,fontEmbedding: 'full' 可选择完整字体。 无浏览器环境提供 textMeasurer,公式另提供 rasterizeFormula。OFD 同样返回 data、pageCount、diagnostics,支持 signal 和 lossPolicy。

原生卡片保持文字,图片节点和公式仍为图片;DOCX 的 Custom XML 恢复数据不会写入 OFD。 图片资源、模板宽度、失败降级与下面的取消约定都与 PDF 相同。OFD 字体和阅读器支持边界见 PDF 与 OFD 导出。

图片节点

下面把 progress-chart 围栏中的 JSON 数组(例如 [25,50,75])绘制为有界 PNG。 它只使用显式传入的 Document 创建离屏 canvas,不读取实时节点或请求外部资源。 图像颜色与文字由渲染器决定;若要跟随导出模板,宿主应把解析后的颜色、字体传给自己的渲染器。

export-nodes/image.tsts

为每次导出创建一个 AbortController,将它的 signal 同时传入资源回调和 PDF 选项。 把 createProgressChartExtension(document, controller.signal) 加入同一份 extensions, 并调用 exportMarkdownPdf(markdown, { extensions, ownerDocument: document, signal: controller.signal })。 关闭对话框或更换文档时调用 abort()。不支持 AbortSignal 的第三方渲染器可能继续计算, 宿主仍须在完成后检查信号并丢弃过期结果。Word 宿主也应在发布结果前检查信号。

render 返回有效 PNG/JPEG data URI;返回 null、无效内容或抛出异常时,内置转换器保留 原代码并报告 code-image-unavailable。PDF 中的图表文字不可单独选择。metadata: false 明确关闭 Word 源码恢复并产生损失诊断;PDF 即使接收开启元数据的同一转换器,也只保留可见图片。

与 Mermaid 组合

createMarkdownDocxCodeImageExtension 的多个实例会认领相同临时节点,不能各注册一个。 MD Editor 会用同名宿主扩展替换内置图片转换器,因此只注册 progress-chart 也会替换默认 Mermaid 配置。需要两种语言时,用一个 languages: ['mermaid', 'progress-chart'] 和一个分派回调, 由宿主同时提供 Mermaid 与业务图表渲染;不要改扩展名称来绕过所有权检查。

在 MD Editor 中接入

vue

导出插件收到的 options 已包含当前模板、内置与自定义转换器,自定义节点在 Word、PDF、OFD 中 使用同一份转换器。仅传 docx.extensions 不会安装商业 PDF/OFD 能力;未安装的格式不出现在菜单中。 非 Vue 宿主直接调用各格式的导出函数,显式传同一模板与转换器即可。

恢复、诊断与验收

Word 导入先验证内容指纹,再由扩展检查 payload 版本和字段。用户修改了 Word 中的文字、 图片或相关样式后,导入保留新内容,不恢复旧业务数据。未安装扩展、无效元数据或未知版本 同样使用可见回退。恢复的是校验后的业务数据,示例会规范化 JSON 排版,不保证逐字符恢复原围栏。

best-effort 返回结果和诊断,strict 遇到任意警告拒绝导出;圆角近似、图片化或缺字体 都可能使严格模式拒绝。PDF 本身不承诺源码恢复,调用方不能将 Word 的 metadata 信息当成 PDF 可逆性的证明。

交付自己的转换器前,至少验证:普通节点不受影响;缺资源时保留内容;实际 DOCX/PDF/OFD 有 可见文字或图像;长内容跨页及嵌套图片不越界;不同模板生效;取消不下载旧结果;修改 Word 后不会恢复过期数据。公开 recipe 的测试直接执行同一份源码,而不是另一份近似实现。

MSFE · QuireKitMarkdown 源码始终属于你。