@web-ppt/fonts 0.4.2 → 0.4.4
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 +31 -0
- package/README.md +35 -33
- package/README.zh-CN.md +95 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
本文件记录对使用者可见的变化。版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## 0.4.4
|
|
6
|
+
|
|
7
|
+
本版没有代码改动,只动文档——但 npm 上看到的东西全变了,所以值得单独发一版。
|
|
8
|
+
|
|
9
|
+
### 变更
|
|
10
|
+
|
|
11
|
+
- **npm 页面改英文**。三个包的 README 与 `description` 之前全是中文,
|
|
12
|
+
而 npm 的读者以国际开发者为主,等于最有说服力的能力矩阵对他们不存在。
|
|
13
|
+
现在 `@web-ppt/core` 的 npm 页取仓库的 `README.en.md`,另两个包各自
|
|
14
|
+
维护英文版,中文移到同目录的 `README.zh-CN.md`,两边顶部互链。
|
|
15
|
+
|
|
16
|
+
GitHub 首页仍是中文,两个入口各自面向自己的人群。
|
|
17
|
+
|
|
18
|
+
- **README 首屏补了演示 GIF 与定位段**。GIF 由 `npm run demo-gif` 录制,
|
|
19
|
+
录的是仓库自己生成的 `fixtures/showcase.pptx`,引擎改了重跑即可,
|
|
20
|
+
不是一次性的手工录屏。
|
|
21
|
+
|
|
22
|
+
## 0.4.3
|
|
23
|
+
|
|
24
|
+
### 变更
|
|
25
|
+
|
|
26
|
+
- **静态画面改回动画终态**,撤销 0.4.1 里改成初始态的那次调整。
|
|
27
|
+
|
|
28
|
+
对齐 PowerPoint 的模型:普通视图与缩略图看到的是「这一页演完的样子」,
|
|
29
|
+
按 F5 进入演示才从第一步开始建。所以**进全屏时画面跳一次是预期行为**,
|
|
30
|
+
不是 bug —— 0.4.1 当时是把这个跳变当成缺陷去消除的,方向错了。
|
|
31
|
+
|
|
32
|
+
「不能简单摊开全画」这条不变:一页里入场与退场的元素属于不同时刻,
|
|
33
|
+
全画出来等于把几帧叠在一起。全员退场的收尾页终态是空白,那种情况仍然
|
|
34
|
+
退回全部可见。
|
|
35
|
+
|
|
5
36
|
## 0.4.2
|
|
6
37
|
|
|
7
38
|
### 修复
|
package/README.md
CHANGED
|
@@ -1,51 +1,53 @@
|
|
|
1
1
|
# @web-ppt/fonts
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
|
|
7
|
+
There is **not one byte of font data in the package** (2.8 KB gzip). Slices point at already-published [fontsource](https://fontsource.org/) versions served by jsDelivr; nothing downloads until it's actually rendered.
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm i @web-ppt/fonts
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Usage
|
|
12
14
|
|
|
13
15
|
```ts
|
|
14
16
|
import { collectFonts } from '@web-ppt/core';
|
|
15
17
|
import { loadFontsFor } from '@web-ppt/fonts';
|
|
16
18
|
|
|
17
|
-
//
|
|
19
|
+
// Only the current slide: fill in on navigation, and slices already fetched are free
|
|
18
20
|
const usages = collectFonts([pres.slides[viewer.index]]);
|
|
19
21
|
await loadFontsFor(usages);
|
|
20
|
-
viewer.refresh(); //
|
|
22
|
+
viewer.refresh(); // layout is synchronous, loading is async — the first frame always breaks on the fallback
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
`loadFontsFor`
|
|
25
|
+
`loadFontsFor` does three things, none of them optional:
|
|
24
26
|
|
|
25
|
-
|
|
|
27
|
+
| Step | Why |
|
|
26
28
|
|---|---|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
29
|
+
| Skip families already installed locally | Zero download beats every loading strategy |
|
|
30
|
+
| Fetch the substitute's `@font-face` and **rewrite the family name to the original's** | The slide says "Calibri". CSS has no aliasing mechanism, so the only way that text picks up the substitute is for the `@font-face` to carry that exact name |
|
|
31
|
+
| Prepend `local()` to `src` | A same-named `@font-face` **shadows** the system font. Without this line, people who *do* have the original get dragged into downloading a substitute instead |
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
On-demand loading is left entirely to `unicode-range` — fontsource's CSS ships the slice boundaries, and the browser fetches only the slices it actually renders.
|
|
32
34
|
|
|
33
|
-
##
|
|
35
|
+
## Substitution table
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
**Every Latin entry is metric-compatible**: each character's advance width matches the original exactly, so line breaks land where PowerPoint puts them. This is the same set LibreOffice uses.
|
|
36
38
|
|
|
37
|
-
|
|
|
39
|
+
| Font in the deck | Substitute | Metric-compatible |
|
|
38
40
|
|---|---|---|
|
|
39
41
|
| Calibri | Carlito | ✓ |
|
|
40
42
|
| Cambria | Caladea | ✓ |
|
|
41
43
|
| Arial / Helvetica | Arimo | ✓ |
|
|
42
44
|
| Times New Roman | Tinos | ✓ |
|
|
43
45
|
| Courier New | Cousine | ✓ |
|
|
44
|
-
| Segoe UI / Tahoma / Verdana | Open Sans | ✗
|
|
46
|
+
| Segoe UI / Tahoma / Verdana | Open Sans | ✗ similar shapes only |
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
Chinese: Microsoft YaHei / PingFang / SimHei / DengXian → Noto Sans SC, the SimSun family → Noto Serif SC, KaiTi → LXGW WenKai.
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
Only **redistributable, subsettable** fonts are included (OFL / Apache). MiSans, HarmonyOS Sans and Alibaba PuHuiTi are "free for commercial use" but each restricts redistribution and modification, so they stay out of the built-in table — add them yourself through `overrides`:
|
|
49
51
|
|
|
50
52
|
```ts
|
|
51
53
|
await loadFontsFor(usages, {
|
|
@@ -53,41 +55,41 @@ await loadFontsFor(usages, {
|
|
|
53
55
|
});
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
##
|
|
58
|
+
## Know what CJK costs
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
CJK is substituted by default — the usage you pass in should all get handled, and silently dropping half of it would be the surprising behaviour. But the two sides have very different economics:
|
|
59
61
|
|
|
60
|
-
| |
|
|
62
|
+
| | Latin | CJK |
|
|
61
63
|
|---|---|---|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
64
|
+
| Cost for one slide | ~30 KB (one slice) | **553 KB** (22 distinct hanzi spread across 18 slices) |
|
|
65
|
+
| What you get | Metric compatibility; breaks match PowerPoint | A different glyph shape — hanzi are monospaced full-width, so breaks wouldn't move anyway |
|
|
66
|
+
| Cost of not doing it | Different advance widths, every line ending wrong | The system CJK font catches it and looks fine |
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
A slice is ~30 KB and holds ~160 codepoints; using one character out of it still costs the whole slice. A full Chinese deck converges around 1 MB. **Marginal cost is per slice, not per character** — "it only uses 22 hanzi" is the worst case, not the best.
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
Bandwidth-sensitive contexts can turn it off, or make it a user setting (which is what the project's own site does):
|
|
69
71
|
|
|
70
72
|
```ts
|
|
71
73
|
await loadFontsFor(usages, { cjk: false });
|
|
72
74
|
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
+
// When the user turns it off mid-session, withdraw the injected declarations and re-render.
|
|
76
|
+
// Only the declarations go — the downloaded bytes stay in the HTTP cache, so turning it back on is free
|
|
75
77
|
unloadFonts({ cjkOnly: true });
|
|
76
78
|
viewer.refresh();
|
|
77
79
|
```
|
|
78
80
|
|
|
79
|
-
##
|
|
81
|
+
## Self-hosting
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
Don't want to depend on jsDelivr? Point at your own base; the directory layout matches fontsource's npm packages:
|
|
82
84
|
|
|
83
85
|
```ts
|
|
84
86
|
await loadFontsFor(usages, { base: 'https://cdn.example.com/npm' });
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
##
|
|
89
|
+
## The other half: exports
|
|
88
90
|
|
|
89
|
-
`<img>`
|
|
91
|
+
An SVG loaded through `<img>` is an isolated document and can't see fonts registered on the page. Exporting to PNG or standalone SVG means inlining the slices you hit — the character sets `collectFonts` returns exist for exactly this.
|
|
90
92
|
|
|
91
|
-
##
|
|
93
|
+
## License
|
|
92
94
|
|
|
93
|
-
MIT
|
|
95
|
+
MIT. The substitute fonts remain under their own licenses (OFL-1.1 / Apache-2.0); this package distributes no font files.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
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),本包不分发字体文件。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@web-ppt/fonts",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.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",
|
|
7
7
|
"ppt",
|