@web-ppt/fonts 0.4.4 → 0.5.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,115 @@
2
2
 
3
3
  本文件记录对使用者可见的变化。版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## 未发布
6
+
7
+ ## 0.5.0-beta.1 - 2026-08-27
8
+
9
+ ### 新增
10
+
11
+ - `@web-ppt/edit-core`、`@web-ppt/editor` 与框架适配层新增元素动画编辑闭环:稳定
12
+ `SlideId` / `ElementId` 时间线支持来源恢复、显式清空、撤销恢复、删除/复制页联动,以及
13
+ view/edit 共用的无副作用即时预览。首个安全目录覆盖入场/退场的 appear、fade、fly、wipe、zoom、
14
+ dissolve,强调 spin/grow 和 2–256 顶点运动路径;复杂 MCE、build、扩展或条件时间树会标记
15
+ `sourceReadonly`,只有调用方明确整条替换才写回;公开 `MAX_ANIMATION_STEPS=128`,保证允许写入的
16
+ 最重时间线仍能在有界解析预算内完整回读。
17
+ - 元素动画保存按 OOXML 官方 preset/filter 重建规范 `p:tnLst`,保留 timing 根属性、`bldLst`、
18
+ `extLst` 与无关 MCE,并精确回读 trigger、delay、duration、方向和路径。真实 Chrome 实测 60 元素
19
+ 启动、200 页批量提交、单页反馈 p95 为 0.8/3.5/0.4ms;确定性 5 页固件及 LibreOffice 打开验证通过。
20
+
21
+ - `@web-ppt/editor` 新增显式启用的纯浏览器自动保存:完整源字节 SHA-256 把同内容输入绑定到同一日志,
22
+ IndexedDB 以串行批次原子追加恢复帧,并按分块数、总估算字节、记录数和保留期压缩清理。`openEditor`
23
+ 在解析和挂载前完成 restore/discard/cancel 决策,clean 尾帧不提示;adapter 与 React/Vue 共用
24
+ `recovering + onRecovery` seam;原子代际占位拒绝旧页面在 discard 后复活日志。真实 Chrome 中 50MB
25
+ 指纹为 42.2ms,1,000 帧写入/关闭重开恢复为 131.5/14.9ms,压缩后 10 个分块,同步提交增量 0.000ms。
26
+
27
+ - `@web-ppt/edit-core` 新增版本化 `RecoveryFrame`、`Editor.subscribeRecovery()` 与
28
+ `restoreRecoveryFrames()`:事务、撤销/重做、非历史写入、选区、保存点、图片资源和身份水位都能按
29
+ 严格序号写成纯 JSON。会话图片 URL 在 patch 中先变成保留 token,资源闭包缺失会原子拒绝;回放先在
30
+ 写时复制模型上完整验真,并拒绝身份水位倒退,坏尾帧不会污染目标;恢复保留选区与脏状态,
31
+ 但不伪造旧历史。未订阅时普通编辑零日志深拷贝。210 页 / 50MB 基准中,1,000 帧为 316.3KB,
32
+ JSON 往返 1.6ms、回放 269.9ms;`@web-ppt/editor.openEditor()` 可在挂载前透传同一日志。
33
+
34
+ - `renderSlideToSvg` 新增 `RenderOptions.idPrefix`。指定后,同一页与同一前缀会生成
35
+ 逐字节相同的 SVG,供编辑器增量更新和确定性比较使用;省略时仍维持全局唯一 id,
36
+ 主视图与缩略图同时挂载不会发生 defs 冲突。
37
+ - 新增 `renderElementToSvg(element, options) → { markup, defs }`。编辑器可用每元素唯一的
38
+ 稳定 `idPrefix` 只重渲脏元素,并在同一次 DOM 更新中替换它自己的节点与 defs;
39
+ 整页与元素入口复用同一分发和两条文本路径,不存在第二套近似渲染器。
40
+ - 新增无 DOM 的 `renderTextBodyToHtml(text, width, height, options)`。屏幕预览和后续
41
+ contenteditable 覆盖层共用同一份段落、run、CJK 标点、分栏与 autofit 排版;默认输出
42
+ 可反解的编辑标记,并隔离危险链接协议,原生 SVG 文本导出路径保持独立。
43
+ - 新增纯函数 `layoutText(text, width, height, options)`。它与原生 `<text>` 输出共用断行、
44
+ CJK 挤压、分栏、行距及 autofit,并公开段落/run 身份、UTF-16 字符区间与光标停靠点,
45
+ 供 Safari engine 模式命中选区;竖排返回仿射变换,公式保持原子,测量器可注入。
46
+ - `renderTextBodyToHtml` 新增 `{ layout: 'engine' }`:直接把 `layoutText()` 的视觉行与源 UTF-16
47
+ 半开区间序列化为绝对定位、禁止二次断行的可编辑 HTML。`@web-ppt/editor` 的 `textMode: 'svg'`
48
+ 以及 `auto` Safari 降级现在让静态 SVG 与 `contenteditable` 共用这组行盒;硬换行、空 run/段、
49
+ 项目符号、RTL、竖排、分栏、公式和裸 autofit 在进入、输入、提交与退出时保持同一布局。真实
50
+ Chrome 的 2,000 字符完整上屏多轮 p95 为 8.3–17.8ms、行盒最大偏差 0.009px;editor 入口为
51
+ 27.46KB gzip。
52
+ - `parse` 新增完全可选的 `edit` / `keepPackage`:前者保留页与元素的 OOXML 回写锚点、
53
+ 占位符身份,后者通过 `Presentation.package` 保留原始 ZIP 与解压 part。默认解析的
54
+ 对象形状和资源生命周期不变;`dispose()` 会同时释放保留的原包。
55
+ - `.pptx` 与 `.ppt` 的编辑解析会额外保留预设形状的 `preset + adj` 语义(OOXML 含
56
+ 版式/母版继承),并新增纯函数 `resolveGeomPath`;编辑投影改变宽高后可重新求值路径,
57
+ 不再复用解析期烘焙的几何。
58
+ - 新增无 DOM、无框架的 `@web-ppt/edit-core`:把解析结果转换为带稳定身份与分数 z 序的
59
+ 扁平 `EditDoc`,以 `src` / `ovr` 分离源值和用户改动,并通过 `toSlide` 投影回现有
60
+ 渲染 Schema。元素、组祖先和页面缓存按修改路径精确失效;图表、SmartArt、OLE、
61
+ 墨迹与媒体只开放框架级变换,内部派生节点不可误编辑。
62
+ - `@web-ppt/edit-core` 新增无 DOM 的 `Editor`、可 JSON 序列化的 `SetXfrm` 与双向 patch。
63
+ 事务失败整体回滚;撤销/重做恢复选区,同路径改动按 500ms 分组,远端 origin 不进入本地历史,
64
+ 默认保留 200 组 / 8MB。固定种子 200 条命令通过撤销、重做与 JSON 回放全等,500 条非法命令
65
+ 不污染文档;210 页实测 200 组撤销/重做含脏页重渲均约 0.5ms/次,历史约 64KB。
66
+ - 新增按需入口 `@web-ppt/edit-core/xml`:无 DOM 的保留型 XML 树支持 UTF-8 / UTF-16
67
+ 字节回环、限定名与 namespace URI 查询、属性最小改写和 OOXML sequence 有序插入;声明、
68
+ 注释、PI、前缀、属性顺序、自闭合形态及 `AlternateContent` 不被重建。默认编辑模型入口为
69
+ 8.28KB gzip,保存期 XML 入口独立为 7.14KB gzip。
70
+ - 新增按需入口 `@web-ppt/edit-core/opc`:无修改保存复用原始字节;脏 part 定点重压,净条目的
71
+ 本地头、extra field 与压缩流逐字直通。新增/删除 part、重复保存和确定性输出均有契约;zip64、
72
+ 数据描述符、存档注释、加密条目、未知压缩与多磁盘格式会给出原因并整包重压。入口为 4.27KB gzip,
73
+ 210 页 / 50.6MB 修改并序列化 3 页后的实测保存为 84.0ms;保存结果可随文档或独立释放。
74
+ - 新增 M0 自动门禁:29 份固件(含吸附、加密 OOXML、RC4 `.ppt` 与 hardcases)的只读链路和
75
+ 编辑投影分别在独立进程渲染,两条文本路径共 232 对原始 SVG 指纹必须完全一致;
76
+ 210 页基准同时强制检查默认路径零编辑状态、编辑常驻内存增量、文本行盒与提交重渲预算。
77
+ - `@web-ppt/editor` 在编辑模式直接消费浏览器 SVG 命中:组默认选外层,双击 / Escape
78
+ 逐层进退,Alt+点击按 `elementsFromPoint` z 序循环;选择只更新交互层,多视图各自拥有监听器。
79
+ - `@web-ppt/editor` 公开元素本地 / 幻灯片 / 屏幕坐标变换,并为普通、旋转翻转和嵌套组元素绘制
80
+ OBB、8 个缩放柄与旋转柄。手柄尺寸不随 zoom 变化;真实 Chrome 三档缩放的最大矩阵偏差为
81
+ 0.000px,9 个手柄的位置与尺寸最大偏差同为 0.000px,60 元素页完整选择框上屏 p95 为 0.100ms。
82
+ - `@web-ppt/editor` 支持主指针拖动单选与多选:3px 阈值后以 pointer capture 和 rAF 合并幽灵帧,
83
+ 松手只提交一个可撤销 `SetXfrm` 事务;嵌套旋转翻转组按各自父空间换算。取消、丢失捕获、切页、
84
+ view 模式和销毁均不写模型。真实 Chrome 嵌套幽灵偏差 0.000px,60 元素拖动帧 p95 0.100ms。
85
+ - `@web-ppt/editor` 绑定 8 个恒屏幕尺寸缩放柄,支持单选/多选、旋转翻转与嵌套组、Shift 等比、
86
+ Alt 中心缩放及过锚翻面;移动与缩放共用可回收的 pointer capture/rAF 状态机,松手一次提交并可
87
+ 精确保存。真实 Chrome 三档 zoom 的手柄/锚点最大偏差 0.009px,缩放帧 p95 0.100ms;60 个
88
+ 45° 元素的近奇异多选缩放 p95 0.600ms。
89
+ - `@web-ppt/editor` 绑定旋转柄:单选按元素父空间处理自身翻转与多层旋转/翻转组,多选围绕共同 AABB
90
+ 中心同步旋转;角度跨 ±180° 连续累计,Shift 可动态吸附 15°,松手以一个事务精确保存。真实 Chrome
91
+ 三档 zoom 的嵌套/多选幽灵最大偏差为 0.000/0.009px,60 元素旋转帧 p95 0.300ms,可信 pointer
92
+ capture 与撤销门禁通过。
93
+ - `@web-ppt/editor` 的移动手势新增屏幕 6px 吸附:画布中线/边缘、显式页边距、同组兄弟边缘/中线与
94
+ 横纵等距按稳定优先级逐轴裁决;`Ctrl` 可动态关闭,视图也可用 `snapping` / `setSnapping()` 全局切换。
95
+ 对齐线和双向等距箭头只进入 interaction SVG,取消零提交。真实 Chrome 三档 zoom、缩放组和等距线
96
+ 最大偏差均为 0.000px,60 元素吸附帧 p95 0.200ms,可信 pointer capture 与撤销门禁通过;editor
97
+ 发布入口实测为 15.87KB gzip。
98
+
99
+ ## 0.4.5
100
+
101
+ ### 修复
102
+
103
+ - **`@web-ppt/viewer-core` 与 `@web-ppt/fonts` 的 npm 页面在 0.4.4 里仍是中文**。
104
+ 包内容是对的(tarball 里躺着英文版),错的是 npm 用来渲染页面的那份元数据。
105
+
106
+ 原因是 npm 认哪个文件当 README 并不看「是不是叫 README.md」:
107
+ `@npmcli/package-json` 用 `{README,README.*}` 去 glob 再取第一个像 markdown
108
+ 的命中,而 0.4.4 引入的 `README.zh-CN.md` 同样匹配 `README.*`,实测还排在
109
+ `README.md` 前面。`files` 只挡住了 tarball,挡不住元数据。
110
+
111
+ 本地化 README 改用连字符(`README-zh-CN.md`),并在 `sync-package-docs.mjs`
112
+ 里加了守卫:包目录里出现 `README.*` 旁支就直接让发布失败。
113
+
5
114
  ## 0.4.4
6
115
 
7
116
  本版没有代码改动,只动文档——但 npm 上看到的东西全变了,所以值得单独发一版。
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @web-ppt/fonts
2
2
 
3
- **English** · [简体中文](https://github.com/unStone/web-ppt/blob/master/packages/fonts/README.zh-CN.md)
3
+ **English** · [简体中文](https://github.com/unStone/web-ppt/blob/master/packages/fonts/README-zh-CN.md)
4
4
 
5
5
  The fonts a deck asks for usually aren't installed on the machine viewing it. This package swaps in a **free substitute that downloads on demand**, so the text still lays out close to the original.
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-ppt/fonts",
3
- "version": "0.4.4",
3
+ "version": "0.5.0-beta.1",
4
4
  "description": "Font substitution and on-demand web-font loading for PowerPoint decks: metric-compatible free substitutes plus a unicode-range slice loader. Zero font bytes in the package.",
5
5
  "keywords": [
6
6
  "pptx",
@@ -48,7 +48,7 @@
48
48
  "prepack": "node ../../tooling/sync-package-docs.mjs"
49
49
  },
50
50
  "peerDependencies": {
51
- "@web-ppt/core": "^0.4.0"
51
+ "@web-ppt/core": "^0.5.0-beta.1"
52
52
  },
53
53
  "publishConfig": {
54
54
  "access": "public"
package/README.zh-CN.md DELETED
@@ -1,95 +0,0 @@
1
- # @web-ppt/fonts
2
-
3
- [English](README.md) · **简体中文**
4
-
5
- PPT 里指定的字体,本机往往没有。这个包负责换一份**免费的、能自动按需下载的**替代字体,让文本仍然按接近原样的方式排版。
6
-
7
- 包里**一个字节的字体都没有**(gzip 2.8KB)。切片指向 [fontsource](https://fontsource.org/) 已发布的版本,由 jsDelivr 分发,用不到就不下载。
8
-
9
- ```bash
10
- npm i @web-ppt/fonts
11
- ```
12
-
13
- ## 用法
14
-
15
- ```ts
16
- import { collectFonts } from '@web-ppt/core';
17
- import { loadFontsFor } from '@web-ppt/fonts';
18
-
19
- // 只统计当前页:翻到了再补,已下过的切片是免费的
20
- const usages = collectFonts([pres.slides[viewer.index]]);
21
- await loadFontsFor(usages);
22
- viewer.refresh(); // 排版是同步的、加载是异步的,首帧一定按回退字体断的行
23
- ```
24
-
25
- `loadFontsFor` 做三件事,缺一不可:
26
-
27
- | 步骤 | 为什么 |
28
- |---|---|
29
- | 本机装了原字体就跳过 | 零下载永远优于任何加载策略 |
30
- | 取替代字体的 `@font-face`,**把家族名改写成原字体名** | 幻灯片里写的是「Calibri」,CSS 没有别名机制,只有让 `@font-face` 顶着这个名字,那段文字才会用上它 |
31
- | `src` 开头补一条 `local()` | 同名 `@font-face` 会**盖掉**系统里的同名字体,不补这条,装了原字体的人反而被拖去下载替代品 |
32
-
33
- 按需下载完全交给 `unicode-range`——fontsource 的 CSS 自带切片划分,浏览器只取真正渲染到的那几片。
34
-
35
- ## 替换表
36
-
37
- **拉丁一栏全是度量兼容字体**:每个字符的前进宽度与原字体逐一相等,断行位置因此与 PowerPoint 逐字对齐。LibreOffice 用的就是这一套。
38
-
39
- | PPT 里的字体 | 替代 | 度量兼容 |
40
- |---|---|---|
41
- | Calibri | Carlito | ✓ |
42
- | Cambria | Caladea | ✓ |
43
- | Arial / Helvetica | Arimo | ✓ |
44
- | Times New Roman | Tinos | ✓ |
45
- | Courier New | Cousine | ✓ |
46
- | Segoe UI / Tahoma / Verdana | Open Sans | ✗ 仅形近 |
47
-
48
- 中文:微软雅黑 / 苹方 / 黑体 / 等线 → Noto Sans SC,宋体系 → Noto Serif SC,楷体 → 霞鹜文楷。
49
-
50
- 只收**可再分发、可子集化**的字体(OFL / Apache)。MiSans、HarmonyOS Sans、阿里普惠这些「免费商用」的许可各自限制再分发与改字,不进内置表——要用就自己往 `overrides` 里加:
51
-
52
- ```ts
53
- await loadFontsFor(usages, {
54
- overrides: { 思源黑体: { family: 'Noto Sans SC', metricCompatible: false, cjk: true } },
55
- });
56
- ```
57
-
58
- ## 中文的代价要心里有数
59
-
60
- 中文默认也换——传进来的用量本来就该全部处理。但两边的账完全不同:
61
-
62
- | | 拉丁 | 中文 |
63
- |---|---|---|
64
- | 一页的代价 | ~30KB(一个切片) | **553KB**(22 个不同汉字跨 18 个切片) |
65
- | 换来什么 | 度量兼容,断行对齐 PowerPoint | 换个字形——汉字全角等宽,断行本来就不会变 |
66
- | 不换的后果 | 字宽不同,行尾全错 | 系统自带中文字体接住,看着没问题 |
67
-
68
- 切片一片约 30KB、装约 160 个码位,用掉一两个也得整片下,整份中文文件收敛下来在 1MB 上下。**边际成本按切片算,不按字算**——「只用了 22 个汉字」反而是最亏的情形。
69
-
70
- 流量敏感的场景可以关掉,或者做成用户可选(官网就是这么做的):
71
-
72
- ```ts
73
- await loadFontsFor(usages, { cjk: false });
74
-
75
- // 用户中途关掉时把已注入的声明撤回,然后重渲当前页。
76
- // 只撤声明,已下载的字节留在 HTTP 缓存里,再打开是免费的
77
- unloadFonts({ cjkOnly: true });
78
- viewer.refresh();
79
- ```
80
-
81
- ## 自托管
82
-
83
- 不想依赖 jsDelivr 就换基址,目录结构与 fontsource 的 npm 包一致:
84
-
85
- ```ts
86
- await loadFontsFor(usages, { base: 'https://cdn.example.com/npm' });
87
- ```
88
-
89
- ## 导出的另一半
90
-
91
- `<img>` 加载的 SVG 是隔离文档,拿不到页面注册的字体。导出 PNG / 独立 SVG 时必须把命中的切片内联进去——`collectFonts` 给出的字符集就是为这个准备的。
92
-
93
- ## 许可
94
-
95
- MIT。替代字体本身各自遵循其原始许可(OFL-1.1 / Apache-2.0),本包不分发字体文件。