@sliiu/present-renderer 0.0.0-stage → 0.1.0
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/COPYRIGHT +5 -0
- package/LICENSE +661 -0
- package/THIRD-PARTY-NOTICES.md +221 -0
- package/dist/cjs/arrow-geom.js +38 -0
- package/dist/cjs/canvas-backend.js +1368 -0
- package/dist/cjs/cell-borders.js +25 -0
- package/dist/cjs/chart-canvas.js +1059 -0
- package/dist/cjs/chart-ex-render.js +275 -0
- package/dist/cjs/chart-geom.js +313 -0
- package/dist/cjs/chart-svg.js +1110 -0
- package/dist/cjs/compose.js +74 -0
- package/dist/cjs/effect-clip.js +652 -0
- package/dist/cjs/entrance-hide.js +63 -0
- package/dist/cjs/font-stack.js +103 -0
- package/dist/cjs/hit-geom.js +50 -0
- package/dist/cjs/hybrid-backend.js +93 -0
- package/dist/cjs/hybrid-segments.js +54 -0
- package/dist/cjs/index.js +70 -0
- package/dist/cjs/linked-src.js +116 -0
- package/dist/cjs/metafile-preview.js +142 -0
- package/dist/cjs/model3d-frame.js +199 -0
- package/dist/cjs/model3d-render.js +78 -0
- package/dist/cjs/morph-transition.js +57 -0
- package/dist/cjs/node-affine.js +79 -0
- package/dist/cjs/ole-render.js +291 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/paint-canvas.js +701 -0
- package/dist/cjs/paint-svg.js +674 -0
- package/dist/cjs/scene-node.js +762 -0
- package/dist/cjs/selection.js +130 -0
- package/dist/cjs/shape-effects.js +66 -0
- package/dist/cjs/shape-front.js +38 -0
- package/dist/cjs/stage-chrome.js +15 -0
- package/dist/cjs/svg-backend.js +2172 -0
- package/dist/cjs/text-autofit.js +48 -0
- package/dist/cjs/text-hit.js +284 -0
- package/dist/cjs/text-layout.js +408 -0
- package/dist/cjs/text-shared.js +238 -0
- package/dist/cjs/text-stacked.js +82 -0
- package/dist/cjs/text-warp.js +377 -0
- package/dist/cjs/text-wordart.js +33 -0
- package/dist/cjs/video-frame.js +113 -0
- package/dist/cjs/webgl-model3d-backend.js +285 -0
- package/dist/cjs/webgl-shape-backend.js +360 -0
- package/dist/esm/arrow-geom.js +33 -0
- package/dist/esm/canvas-backend.js +1363 -0
- package/dist/esm/cell-borders.js +21 -0
- package/dist/esm/chart-canvas.js +1056 -0
- package/dist/esm/chart-ex-render.js +261 -0
- package/dist/esm/chart-geom.js +296 -0
- package/dist/esm/chart-svg.js +1107 -0
- package/dist/esm/compose.js +71 -0
- package/dist/esm/effect-clip.js +648 -0
- package/dist/esm/entrance-hide.js +59 -0
- package/dist/esm/font-stack.js +100 -0
- package/dist/esm/hit-geom.js +45 -0
- package/dist/esm/hybrid-backend.js +89 -0
- package/dist/esm/hybrid-segments.js +51 -0
- package/dist/esm/index.js +24 -0
- package/dist/esm/linked-src.js +109 -0
- package/dist/esm/metafile-preview.js +135 -0
- package/dist/esm/model3d-frame.js +158 -0
- package/dist/esm/model3d-render.js +73 -0
- package/dist/esm/morph-transition.js +54 -0
- package/dist/esm/node-affine.js +71 -0
- package/dist/esm/ole-render.js +286 -0
- package/dist/esm/paint-canvas.js +693 -0
- package/dist/esm/paint-svg.js +670 -0
- package/dist/esm/scene-node.js +750 -0
- package/dist/esm/selection.js +122 -0
- package/dist/esm/shape-effects.js +60 -0
- package/dist/esm/shape-front.js +35 -0
- package/dist/esm/stage-chrome.js +12 -0
- package/dist/esm/svg-backend.js +2168 -0
- package/dist/esm/text-autofit.js +44 -0
- package/dist/esm/text-hit.js +280 -0
- package/dist/esm/text-layout.js +400 -0
- package/dist/esm/text-shared.js +221 -0
- package/dist/esm/text-stacked.js +78 -0
- package/dist/esm/text-warp.js +371 -0
- package/dist/esm/text-wordart.js +30 -0
- package/dist/esm/video-frame.js +109 -0
- package/dist/esm/webgl-model3d-backend.js +248 -0
- package/dist/esm/webgl-shape-backend.js +323 -0
- package/dist/types/arrow-geom.d.ts +10 -0
- package/dist/types/canvas-backend.d.ts +55 -0
- package/dist/types/cell-borders.d.ts +9 -0
- package/dist/types/chart-canvas.d.ts +2 -0
- package/dist/types/chart-ex-render.d.ts +79 -0
- package/dist/types/chart-geom.d.ts +112 -0
- package/dist/types/chart-svg.d.ts +2 -0
- package/dist/types/compose.d.ts +2 -0
- package/dist/types/effect-clip.d.ts +10 -0
- package/dist/types/entrance-hide.d.ts +16 -0
- package/dist/types/font-stack.d.ts +9 -0
- package/dist/types/hit-geom.d.ts +15 -0
- package/dist/types/hybrid-backend.d.ts +21 -0
- package/dist/types/hybrid-segments.d.ts +11 -0
- package/dist/types/index.d.ts +24 -0
- package/dist/types/linked-src.d.ts +13 -0
- package/dist/types/metafile-preview.d.ts +11 -0
- package/dist/types/model3d-frame.d.ts +8 -0
- package/dist/types/model3d-render.d.ts +4 -0
- package/dist/types/morph-transition.d.ts +15 -0
- package/dist/types/node-affine.d.ts +22 -0
- package/dist/types/ole-render.d.ts +7 -0
- package/dist/types/paint-canvas.d.ts +22 -0
- package/dist/types/paint-svg.d.ts +19 -0
- package/dist/types/scene-node.d.ts +128 -0
- package/dist/types/selection.d.ts +22 -0
- package/dist/types/shape-effects.d.ts +13 -0
- package/dist/types/shape-front.d.ts +4 -0
- package/dist/types/stage-chrome.d.ts +8 -0
- package/dist/types/svg-backend.d.ts +88 -0
- package/dist/types/text-autofit.d.ts +7 -0
- package/dist/types/text-hit.d.ts +7 -0
- package/dist/types/text-layout.d.ts +35 -0
- package/dist/types/text-shared.d.ts +86 -0
- package/dist/types/text-stacked.d.ts +11 -0
- package/dist/types/text-warp.d.ts +37 -0
- package/dist/types/text-wordart.d.ts +18 -0
- package/dist/types/video-frame.d.ts +2 -0
- package/dist/types/webgl-model3d-backend.d.ts +25 -0
- package/dist/types/webgl-shape-backend.d.ts +33 -0
- package/package.json +33 -4
- package/README.md +0 -3
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyEntranceHideToScene = applyEntranceHideToScene;
|
|
4
|
+
exports.revealDesignedAppearance = revealDesignedAppearance;
|
|
5
|
+
const core_1 = require("@sliiu/present-core");
|
|
6
|
+
/**
|
|
7
|
+
* 放映专用:按当前 timing 把 entrance 目标在 scene 上藏起来(opacity=0 / 段落级 hide)。
|
|
8
|
+
*
|
|
9
|
+
* 编辑器会 `forceVisibleForEdit`,让入场图形在编辑态全部可见;分享 / 导出 / 直播快照
|
|
10
|
+
* 打的是这份数据。播放器必须在这里重算 hidden-until-entrance,不能依赖 shape.opacity。
|
|
11
|
+
*/
|
|
12
|
+
function applyEntranceHideToScene(scene, slide) {
|
|
13
|
+
const st = (0, core_1.computeInitialEntranceState)(slide);
|
|
14
|
+
for (const s of st.hiddenShapes) {
|
|
15
|
+
const node = scene.root.findById(s.id);
|
|
16
|
+
if (node)
|
|
17
|
+
node.setProperty("opacity", 0);
|
|
18
|
+
}
|
|
19
|
+
for (const o of st.paraOverrides) {
|
|
20
|
+
const node = scene.root.findById(o.shapeId);
|
|
21
|
+
if (!node)
|
|
22
|
+
continue;
|
|
23
|
+
if (o.opacity !== undefined)
|
|
24
|
+
node.setParaProperty(o.paraIdx, "opacity", o.opacity);
|
|
25
|
+
}
|
|
26
|
+
for (const o of st.charOverrides) {
|
|
27
|
+
const node = scene.root.findById(o.shapeId);
|
|
28
|
+
if (!node)
|
|
29
|
+
continue;
|
|
30
|
+
if (o.opacity !== undefined) {
|
|
31
|
+
node.setCharRangeProperty(o.paraIdx, o.unit, o.start, o.end, "opacity", o.opacity);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 放映缩略图 / 下一张预览:画出设计稿,而不是「点击前隐藏」的初态。
|
|
37
|
+
*
|
|
38
|
+
* 进入放映时编辑器会把 entrance 目标的 opacity 写回 0。播放舞台要这个初态,
|
|
39
|
+
* 缩略图如果跟着藏,演讲者看到的是空白页。只改 scene,不改 slide 数据。
|
|
40
|
+
*/
|
|
41
|
+
function revealDesignedAppearance(scene, slide) {
|
|
42
|
+
const st = (0, core_1.computeInitialEntranceState)(slide);
|
|
43
|
+
for (const s of st.hiddenShapes) {
|
|
44
|
+
const node = scene.root.findById(s.id);
|
|
45
|
+
if (node)
|
|
46
|
+
node.setProperty("opacity", 1);
|
|
47
|
+
}
|
|
48
|
+
for (const o of st.paraOverrides) {
|
|
49
|
+
const node = scene.root.findById(o.shapeId);
|
|
50
|
+
if (!node)
|
|
51
|
+
continue;
|
|
52
|
+
if (o.opacity === 0)
|
|
53
|
+
node.setParaProperty(o.paraIdx, "opacity", 1);
|
|
54
|
+
}
|
|
55
|
+
for (const o of st.charOverrides) {
|
|
56
|
+
const node = scene.root.findById(o.shapeId);
|
|
57
|
+
if (!node)
|
|
58
|
+
continue;
|
|
59
|
+
if (o.opacity === 0) {
|
|
60
|
+
node.setCharRangeProperty(o.paraIdx, o.unit, o.start, o.end, "opacity", 1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Font fallback for the browser. OOXML names one typeface per run ("Inter", "Songti SC"); when the
|
|
4
|
+
* machine lacks it PowerPoint substitutes a face of the same class (sans / serif / mono), but a
|
|
5
|
+
* browser given a bare unknown family falls back to its default — usually a serif — so a sans deck
|
|
6
|
+
* comes up in Times. Append the class-matched generic (plus CJK faces so Han glyphs never hit a
|
|
7
|
+
* Latin-only fallback) to every family that is not already a stack.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.withFontFallback = withFontFallback;
|
|
11
|
+
const SERIF = /\b(serif|times|georgia|garamond|cambria|palatino|baskerville|didot|bodoni|playfair|fraunces|merriweather|lora|libre|spectral|songti|simsun|song|ming|mincho|kaiti|kai|fangsong|batang)\b|宋体|楷体|仿宋|书宋|明朝/i;
|
|
12
|
+
const MONO = /\b(mono|monospace|consolas|menlo|courier|code|inconsolata|fira code|source code|jetbrains|plex mono|space mono)\b/i;
|
|
13
|
+
const CJK_FALLBACK = `"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC"`;
|
|
14
|
+
const CJK_SERIF_FALLBACK = `"Songti SC", "SimSun", "Noto Serif CJK SC"`;
|
|
15
|
+
// OOXML 写的是 Windows 字体名。同一种字在 macOS 上登记成另一个 family,
|
|
16
|
+
// 名字对不上时浏览器会跳过它,后面的拉丁字体就把数字和英文抢走。
|
|
17
|
+
// 别名紧跟在原名后面,原名存在时仍优先用原名。
|
|
18
|
+
const CJK_ALIASES = {
|
|
19
|
+
黑体: ["SimHei", "Heiti SC", "STHeiti", "华文黑体"],
|
|
20
|
+
SimHei: ["黑体", "Heiti SC", "STHeiti"],
|
|
21
|
+
宋体: ["SimSun", "Songti SC", "STSong", "华文宋体"],
|
|
22
|
+
SimSun: ["宋体", "Songti SC", "STSong"],
|
|
23
|
+
新宋体: ["NSimSun", "Songti SC", "SimSun"],
|
|
24
|
+
NSimSun: ["新宋体", "Songti SC"],
|
|
25
|
+
微软雅黑: ["Microsoft YaHei", "PingFang SC", "Hiragino Sans GB"],
|
|
26
|
+
"Microsoft YaHei": ["微软雅黑", "PingFang SC", "Hiragino Sans GB"],
|
|
27
|
+
楷体: ["KaiTi", "Kaiti SC", "STKaiti", "华文楷体"],
|
|
28
|
+
KaiTi: ["楷体", "Kaiti SC", "STKaiti"],
|
|
29
|
+
仿宋: ["FangSong", "STFangsong", "华文仿宋"],
|
|
30
|
+
FangSong: ["仿宋", "STFangsong"],
|
|
31
|
+
等线: ["DengXian", "Microsoft YaHei", "PingFang SC"],
|
|
32
|
+
};
|
|
33
|
+
const cache = new Map();
|
|
34
|
+
/** `family` as a CSS font-family list with a class-matched generic fallback appended. */
|
|
35
|
+
function withFontFallback(family) {
|
|
36
|
+
if (!family)
|
|
37
|
+
return `${CJK_FALLBACK}, sans-serif`;
|
|
38
|
+
const hit = cache.get(family);
|
|
39
|
+
if (hit)
|
|
40
|
+
return hit;
|
|
41
|
+
const trimmed = expandCjkAliases(family.trim());
|
|
42
|
+
// Ends in a generic already: trust it. A stack of named faces (`Inter, "Noto Sans SC"`) still
|
|
43
|
+
// needs the generic, since with none of them installed the browser falls to its serif default.
|
|
44
|
+
// The class is judged from the lead family; the rest of the stack is kept as authored.
|
|
45
|
+
const lead = trimmed.split(",")[0].trim().replace(/^["']|["']$/g, "");
|
|
46
|
+
const head = trimmed.includes(",") ? trimmed : quote(trimmed);
|
|
47
|
+
const stack = /\b(sans-serif|serif|monospace|system-ui|cursive|fantasy)\s*$/i.test(trimmed)
|
|
48
|
+
? trimmed
|
|
49
|
+
: MONO.test(lead)
|
|
50
|
+
? `${head}, "Menlo", "Consolas", monospace`
|
|
51
|
+
: SERIF.test(lead)
|
|
52
|
+
? `${head}, ${CJK_SERIF_FALLBACK}, serif`
|
|
53
|
+
: `${head}, ${CJK_FALLBACK}, sans-serif`;
|
|
54
|
+
cache.set(family, stack);
|
|
55
|
+
return stack;
|
|
56
|
+
}
|
|
57
|
+
function quote(name) {
|
|
58
|
+
return /^["']/.test(name) || !/[\s\d]/.test(name) ? name : `"${name}"`;
|
|
59
|
+
}
|
|
60
|
+
function unquote(name) {
|
|
61
|
+
return name.trim().replace(/^["']|["']$/g, "");
|
|
62
|
+
}
|
|
63
|
+
function splitFamilies(stack) {
|
|
64
|
+
const out = [];
|
|
65
|
+
let cur = "";
|
|
66
|
+
let quoted = false;
|
|
67
|
+
for (const ch of stack) {
|
|
68
|
+
if (ch === '"') {
|
|
69
|
+
quoted = !quoted;
|
|
70
|
+
cur += ch;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (ch === "," && !quoted) {
|
|
74
|
+
if (cur.trim())
|
|
75
|
+
out.push(cur.trim());
|
|
76
|
+
cur = "";
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
cur += ch;
|
|
80
|
+
}
|
|
81
|
+
if (cur.trim())
|
|
82
|
+
out.push(cur.trim());
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
function expandCjkAliases(stack) {
|
|
86
|
+
const parts = splitFamilies(stack);
|
|
87
|
+
const seen = new Set(parts.map((part) => unquote(part).toLowerCase()));
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const part of parts) {
|
|
90
|
+
out.push(part);
|
|
91
|
+
const aliases = CJK_ALIASES[unquote(part)];
|
|
92
|
+
if (!aliases)
|
|
93
|
+
continue;
|
|
94
|
+
for (const alias of aliases) {
|
|
95
|
+
const key = alias.toLowerCase();
|
|
96
|
+
if (seen.has(key))
|
|
97
|
+
continue;
|
|
98
|
+
seen.add(key);
|
|
99
|
+
out.push(quote(alias));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return out.join(", ");
|
|
103
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inverseSceneNode = inverseSceneNode;
|
|
4
|
+
exports.worldToLocal = worldToLocal;
|
|
5
|
+
exports.hitTestNodeBBox = hitTestNodeBBox;
|
|
6
|
+
/**
|
|
7
|
+
* 撤销单个 SceneNode 的绘制变换,与 canvas/svg backend 对偶:
|
|
8
|
+
* translate(tx,ty) → rotate around bbox center → scale around center。
|
|
9
|
+
*/
|
|
10
|
+
function inverseSceneNode(n, x, y) {
|
|
11
|
+
const s = n.shape;
|
|
12
|
+
const cx = s.bbox.x + s.bbox.w / 2;
|
|
13
|
+
const cy = s.bbox.y + s.bbox.h / 2;
|
|
14
|
+
let lx = x - n.tx;
|
|
15
|
+
let ly = y - n.ty;
|
|
16
|
+
if (n.rotation) {
|
|
17
|
+
const a = (-n.rotation * Math.PI) / 180;
|
|
18
|
+
const cos = Math.cos(a);
|
|
19
|
+
const sin = Math.sin(a);
|
|
20
|
+
const dx = lx - cx;
|
|
21
|
+
const dy = ly - cy;
|
|
22
|
+
lx = cx + dx * cos - dy * sin;
|
|
23
|
+
ly = cy + dx * sin + dy * cos;
|
|
24
|
+
}
|
|
25
|
+
const flipX = s.transform?.scaleX ?? 1;
|
|
26
|
+
const flipY = s.transform?.scaleY ?? 1;
|
|
27
|
+
const sx = n.scale * n.scaleX * n.widthScale * (flipX < 0 ? -1 : 1);
|
|
28
|
+
const sy = n.scale * n.scaleY * n.heightScale * (flipY < 0 ? -1 : 1);
|
|
29
|
+
if (sx !== 1 && sx !== 0)
|
|
30
|
+
lx = (lx - cx) / sx + cx;
|
|
31
|
+
if (sy !== 1 && sy !== 0)
|
|
32
|
+
ly = (ly - cy) / sy + cy;
|
|
33
|
+
return { x: lx, y: ly };
|
|
34
|
+
}
|
|
35
|
+
/** 世界坐标 → 该节点未旋转 bbox 空间(含祖先 group 变换)。 */
|
|
36
|
+
function worldToLocal(node, x, y) {
|
|
37
|
+
const chain = [];
|
|
38
|
+
for (let n = node; n; n = n.parent)
|
|
39
|
+
chain.push(n);
|
|
40
|
+
chain.reverse();
|
|
41
|
+
let p = { x, y };
|
|
42
|
+
for (const n of chain)
|
|
43
|
+
p = inverseSceneNode(n, p.x, p.y);
|
|
44
|
+
return p;
|
|
45
|
+
}
|
|
46
|
+
function hitTestNodeBBox(node, worldX, worldY) {
|
|
47
|
+
const p = worldToLocal(node, worldX, worldY);
|
|
48
|
+
const b = node.shape.bbox;
|
|
49
|
+
return p.x >= b.x && p.x <= b.x + b.w && p.y >= b.y && p.y <= b.y + b.h;
|
|
50
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HybridBackend = void 0;
|
|
4
|
+
const svg_backend_1 = require("./svg-backend");
|
|
5
|
+
const canvas_backend_1 = require("./canvas-backend");
|
|
6
|
+
const webgl_shape_backend_1 = require("./webgl-shape-backend");
|
|
7
|
+
const webgl_model3d_backend_1 = require("./webgl-model3d-backend");
|
|
8
|
+
const hybrid_segments_1 = require("./hybrid-segments");
|
|
9
|
+
class HybridBackend {
|
|
10
|
+
host;
|
|
11
|
+
svg;
|
|
12
|
+
wrapper;
|
|
13
|
+
backends = [];
|
|
14
|
+
constructor(scene, container, opts = {}) {
|
|
15
|
+
this.wrapper = document.createElement("div");
|
|
16
|
+
this.wrapper.style.position = "relative";
|
|
17
|
+
this.wrapper.style.width = "100%";
|
|
18
|
+
this.wrapper.style.height = "100%";
|
|
19
|
+
this.wrapper.style.background = opts.background ?? "transparent";
|
|
20
|
+
container.appendChild(this.wrapper);
|
|
21
|
+
const segments = (0, hybrid_segments_1.buildHybridSegments)(scene.root.children);
|
|
22
|
+
let firstSvg = null;
|
|
23
|
+
for (const seg of segments) {
|
|
24
|
+
const layer = document.createElement("div");
|
|
25
|
+
layer.style.position = "absolute";
|
|
26
|
+
layer.style.inset = "0";
|
|
27
|
+
layer.style.width = "100%";
|
|
28
|
+
layer.style.height = "100%";
|
|
29
|
+
this.wrapper.appendChild(layer);
|
|
30
|
+
const ids = new Set(seg.ids);
|
|
31
|
+
const filter = (s) => ids.has(s.id);
|
|
32
|
+
if (seg.kind === "canvas") {
|
|
33
|
+
this.backends.push(new canvas_backend_1.CanvasBackend(scene, layer, { shapeFilter: filter, transparent: true }));
|
|
34
|
+
}
|
|
35
|
+
else if (seg.kind === "webgl") {
|
|
36
|
+
layer.style.pointerEvents = "none";
|
|
37
|
+
this.backends.push(new webgl_shape_backend_1.WebGLShapeBackend(scene, layer, { shapeFilter: filter }));
|
|
38
|
+
}
|
|
39
|
+
else if (seg.kind === "webglm3d") {
|
|
40
|
+
layer.style.pointerEvents = "none";
|
|
41
|
+
this.backends.push(new webgl_model3d_backend_1.WebGLModel3DBackend(scene, layer, { shapeFilter: filter, interactive: opts.interactive }));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// 这一层必须能接到指针。HTML 祖先一旦 pointer-events:none,foreignObject
|
|
45
|
+
// 里的 <video> 控件就点不到,点击会穿透到 stage,被放映的翻页接走。
|
|
46
|
+
const be = new svg_backend_1.SvgBackend(scene, layer, {
|
|
47
|
+
shapeFilter: filter,
|
|
48
|
+
transparent: true,
|
|
49
|
+
clipToSlide: opts.clipToSlide,
|
|
50
|
+
});
|
|
51
|
+
this.backends.push(be);
|
|
52
|
+
if (!firstSvg)
|
|
53
|
+
firstSvg = be;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!firstSvg) {
|
|
57
|
+
const layer = document.createElement("div");
|
|
58
|
+
layer.style.position = "absolute";
|
|
59
|
+
layer.style.inset = "0";
|
|
60
|
+
layer.style.width = "100%";
|
|
61
|
+
layer.style.height = "100%";
|
|
62
|
+
layer.style.pointerEvents = "none";
|
|
63
|
+
this.wrapper.appendChild(layer);
|
|
64
|
+
firstSvg = new svg_backend_1.SvgBackend(scene, layer, {
|
|
65
|
+
shapeFilter: () => false,
|
|
66
|
+
transparent: true,
|
|
67
|
+
clipToSlide: opts.clipToSlide,
|
|
68
|
+
});
|
|
69
|
+
this.backends.push(firstSvg);
|
|
70
|
+
}
|
|
71
|
+
this.svg = firstSvg;
|
|
72
|
+
this.host = this.wrapper;
|
|
73
|
+
}
|
|
74
|
+
get svgRoot() {
|
|
75
|
+
return this.svg.root;
|
|
76
|
+
}
|
|
77
|
+
/** 同步刷新所有子 backend;详见 SvgBackend.flush。 */
|
|
78
|
+
flush() {
|
|
79
|
+
for (const b of this.backends)
|
|
80
|
+
b.flush?.();
|
|
81
|
+
}
|
|
82
|
+
/** 等待所有 live 3D(WebGLShapeBackend)首帧就绪——截图 / visual-compare 前调。 */
|
|
83
|
+
async waitForReady() {
|
|
84
|
+
await Promise.all(this.backends.map((b) => b.whenReady?.() ?? Promise.resolve()));
|
|
85
|
+
}
|
|
86
|
+
destroy() {
|
|
87
|
+
for (const b of this.backends)
|
|
88
|
+
b.destroy();
|
|
89
|
+
this.backends = [];
|
|
90
|
+
this.wrapper.remove();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.HybridBackend = HybridBackend;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildHybridSegments = buildHybridSegments;
|
|
4
|
+
const three_bake_1 = require("@sliiu/present-three-bake");
|
|
5
|
+
const BITMAP_KINDS = new Set(["image", "chart"]);
|
|
6
|
+
function shapeIsLive3D(s) {
|
|
7
|
+
return (0, three_bake_1.shapeSupports3D)(s) && (0, three_bake_1.needs3DRender)((0, three_bake_1.shape3DInfo)(s));
|
|
8
|
+
}
|
|
9
|
+
function shapeIsLiveModel3D(s) {
|
|
10
|
+
return s.kind === "model3d" && Boolean(s.glbSrc);
|
|
11
|
+
}
|
|
12
|
+
function classifyBucket(node) {
|
|
13
|
+
if (shapeIsLive3D(node.shape))
|
|
14
|
+
return "webgl";
|
|
15
|
+
if (shapeIsLiveModel3D(node.shape))
|
|
16
|
+
return "webglm3d";
|
|
17
|
+
if (node.shape.kind === "model3d")
|
|
18
|
+
return "canvas";
|
|
19
|
+
return BITMAP_KINDS.has(node.shape.kind) ? "canvas" : "svg";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 按绘制顺序切段。组合不单独成段:非 3D 子形状留在组合被归入的那一类(图片在组合里
|
|
23
|
+
* 仍走 SVG),3D 子形状在它自己的 z 序位置拆成 webgl 段,这样字可以盖在挤出体上面。
|
|
24
|
+
*/
|
|
25
|
+
function buildHybridSegments(children) {
|
|
26
|
+
const segments = [];
|
|
27
|
+
for (const child of children)
|
|
28
|
+
walk(child, segments, classifyBucket(child));
|
|
29
|
+
return segments;
|
|
30
|
+
}
|
|
31
|
+
function walk(node, segments, bucket) {
|
|
32
|
+
if (shapeIsLive3D(node.shape)) {
|
|
33
|
+
push(segments, "webgl", node.id);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (shapeIsLiveModel3D(node.shape)) {
|
|
37
|
+
push(segments, "webglm3d", node.id);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (node.shape.kind === "group") {
|
|
41
|
+
for (const c of node.children)
|
|
42
|
+
walk(c, segments, bucket);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
push(segments, bucket, node.id);
|
|
46
|
+
}
|
|
47
|
+
function push(segments, kind, id) {
|
|
48
|
+
const prev = segments[segments.length - 1];
|
|
49
|
+
if (prev && prev.kind === kind && kind !== "webgl" && kind !== "webglm3d") {
|
|
50
|
+
prev.ids.push(id);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
segments.push({ kind, ids: [id] });
|
|
54
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.placeGlyphsOnWarp = exports.sampleWarpPath = exports.warpActive = exports.shapeFrontImageSrc = exports.inverseSceneNode = exports.hitTestNodeBBox = exports.worldToLocal = exports.hitTestTextRunAt = exports.runLinkAt = exports.effectClipUsesEvenOdd = exports.effectClipPathD = exports.drawChartCanvas = exports.renderChart = exports.waitForModel3DFrames = exports.getModel3DPreviewFrame = exports.waitForVideoFrames = exports.getVideoPosterFrame = exports.waitForImagesLoaded = exports.getImage = exports.applyCanvasStroke = exports.paintToCanvasStyle = exports.applySvgStroke = exports.renderPaint = exports.mountMorphTransitionLayers = exports.stageChromeBackground = exports.WebGLModel3DBackend = exports.WebGLShapeBackend = exports.revealDesignedAppearance = exports.applyEntranceHideToScene = void 0;
|
|
18
|
+
__exportStar(require("./scene-node"), exports);
|
|
19
|
+
var entrance_hide_1 = require("./entrance-hide");
|
|
20
|
+
Object.defineProperty(exports, "applyEntranceHideToScene", { enumerable: true, get: function () { return entrance_hide_1.applyEntranceHideToScene; } });
|
|
21
|
+
Object.defineProperty(exports, "revealDesignedAppearance", { enumerable: true, get: function () { return entrance_hide_1.revealDesignedAppearance; } });
|
|
22
|
+
__exportStar(require("./compose"), exports);
|
|
23
|
+
__exportStar(require("./svg-backend"), exports);
|
|
24
|
+
__exportStar(require("./canvas-backend"), exports);
|
|
25
|
+
__exportStar(require("./hybrid-backend"), exports);
|
|
26
|
+
var webgl_shape_backend_1 = require("./webgl-shape-backend");
|
|
27
|
+
Object.defineProperty(exports, "WebGLShapeBackend", { enumerable: true, get: function () { return webgl_shape_backend_1.WebGLShapeBackend; } });
|
|
28
|
+
var webgl_model3d_backend_1 = require("./webgl-model3d-backend");
|
|
29
|
+
Object.defineProperty(exports, "WebGLModel3DBackend", { enumerable: true, get: function () { return webgl_model3d_backend_1.WebGLModel3DBackend; } });
|
|
30
|
+
var stage_chrome_1 = require("./stage-chrome");
|
|
31
|
+
Object.defineProperty(exports, "stageChromeBackground", { enumerable: true, get: function () { return stage_chrome_1.stageChromeBackground; } });
|
|
32
|
+
var morph_transition_1 = require("./morph-transition");
|
|
33
|
+
Object.defineProperty(exports, "mountMorphTransitionLayers", { enumerable: true, get: function () { return morph_transition_1.mountMorphTransitionLayers; } });
|
|
34
|
+
__exportStar(require("./selection"), exports);
|
|
35
|
+
var paint_svg_1 = require("./paint-svg");
|
|
36
|
+
Object.defineProperty(exports, "renderPaint", { enumerable: true, get: function () { return paint_svg_1.renderPaint; } });
|
|
37
|
+
Object.defineProperty(exports, "applySvgStroke", { enumerable: true, get: function () { return paint_svg_1.applyStroke; } });
|
|
38
|
+
var paint_canvas_1 = require("./paint-canvas");
|
|
39
|
+
Object.defineProperty(exports, "paintToCanvasStyle", { enumerable: true, get: function () { return paint_canvas_1.paintToCanvasStyle; } });
|
|
40
|
+
Object.defineProperty(exports, "applyCanvasStroke", { enumerable: true, get: function () { return paint_canvas_1.applyStroke; } });
|
|
41
|
+
Object.defineProperty(exports, "getImage", { enumerable: true, get: function () { return paint_canvas_1.getImage; } });
|
|
42
|
+
Object.defineProperty(exports, "waitForImagesLoaded", { enumerable: true, get: function () { return paint_canvas_1.waitForImagesLoaded; } });
|
|
43
|
+
var video_frame_1 = require("./video-frame");
|
|
44
|
+
Object.defineProperty(exports, "getVideoPosterFrame", { enumerable: true, get: function () { return video_frame_1.getVideoPosterFrame; } });
|
|
45
|
+
Object.defineProperty(exports, "waitForVideoFrames", { enumerable: true, get: function () { return video_frame_1.waitForVideoFrames; } });
|
|
46
|
+
var model3d_frame_1 = require("./model3d-frame");
|
|
47
|
+
Object.defineProperty(exports, "getModel3DPreviewFrame", { enumerable: true, get: function () { return model3d_frame_1.getModel3DPreviewFrame; } });
|
|
48
|
+
Object.defineProperty(exports, "waitForModel3DFrames", { enumerable: true, get: function () { return model3d_frame_1.waitForModel3DFrames; } });
|
|
49
|
+
var chart_svg_1 = require("./chart-svg");
|
|
50
|
+
Object.defineProperty(exports, "renderChart", { enumerable: true, get: function () { return chart_svg_1.renderChart; } });
|
|
51
|
+
var chart_canvas_1 = require("./chart-canvas");
|
|
52
|
+
Object.defineProperty(exports, "drawChartCanvas", { enumerable: true, get: function () { return chart_canvas_1.drawChartCanvas; } });
|
|
53
|
+
var effect_clip_1 = require("./effect-clip");
|
|
54
|
+
Object.defineProperty(exports, "effectClipPathD", { enumerable: true, get: function () { return effect_clip_1.effectClipPathD; } });
|
|
55
|
+
Object.defineProperty(exports, "effectClipUsesEvenOdd", { enumerable: true, get: function () { return effect_clip_1.effectClipUsesEvenOdd; } });
|
|
56
|
+
__exportStar(require("./text-shared"), exports);
|
|
57
|
+
__exportStar(require("./text-stacked"), exports);
|
|
58
|
+
var text_hit_1 = require("./text-hit");
|
|
59
|
+
Object.defineProperty(exports, "runLinkAt", { enumerable: true, get: function () { return text_hit_1.runLinkAt; } });
|
|
60
|
+
Object.defineProperty(exports, "hitTestTextRunAt", { enumerable: true, get: function () { return text_hit_1.hitTestTextRunAt; } });
|
|
61
|
+
var hit_geom_1 = require("./hit-geom");
|
|
62
|
+
Object.defineProperty(exports, "worldToLocal", { enumerable: true, get: function () { return hit_geom_1.worldToLocal; } });
|
|
63
|
+
Object.defineProperty(exports, "hitTestNodeBBox", { enumerable: true, get: function () { return hit_geom_1.hitTestNodeBBox; } });
|
|
64
|
+
Object.defineProperty(exports, "inverseSceneNode", { enumerable: true, get: function () { return hit_geom_1.inverseSceneNode; } });
|
|
65
|
+
var shape_front_1 = require("./shape-front");
|
|
66
|
+
Object.defineProperty(exports, "shapeFrontImageSrc", { enumerable: true, get: function () { return shape_front_1.shapeFrontImageSrc; } });
|
|
67
|
+
var text_warp_1 = require("./text-warp");
|
|
68
|
+
Object.defineProperty(exports, "warpActive", { enumerable: true, get: function () { return text_warp_1.warpActive; } });
|
|
69
|
+
Object.defineProperty(exports, "sampleWarpPath", { enumerable: true, get: function () { return text_warp_1.sampleWarpPath; } });
|
|
70
|
+
Object.defineProperty(exports, "placeGlyphsOnWarp", { enumerable: true, get: function () { return text_warp_1.placeGlyphsOnWarp; } });
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** 浏览器无法加载 `file:` 外链图;画带文件名的占位,避免空白碎图。
|
|
3
|
+
* Node 下若本地文件仍在,读成 data URL 仅供预览,**不改 IR 的 src**(否则 round-trip 会丢掉 r:link)。
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isFileLinkedSrc = isFileLinkedSrc;
|
|
7
|
+
exports.linkedImageLabel = linkedImageLabel;
|
|
8
|
+
exports.linkedImagePlaceholderDataUri = linkedImagePlaceholderDataUri;
|
|
9
|
+
exports.displayImageSrc = displayImageSrc;
|
|
10
|
+
exports.drawLinkedImagePlaceholder = drawLinkedImagePlaceholder;
|
|
11
|
+
function isFileLinkedSrc(src) {
|
|
12
|
+
return !!src && /^file:/i.test(src);
|
|
13
|
+
}
|
|
14
|
+
function linkedImageLabel(src) {
|
|
15
|
+
const stripped = src.replace(/^file:\/\//i, "").replace(/^\/\//, "");
|
|
16
|
+
const name = stripped.split(/[/\\]/).pop()?.trim();
|
|
17
|
+
return name && name.length > 0 && name.length < 80 ? name : "Linked image";
|
|
18
|
+
}
|
|
19
|
+
function escapeXml(s) {
|
|
20
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
21
|
+
}
|
|
22
|
+
const FILE_PREVIEW_CACHE = new Map();
|
|
23
|
+
function fileUrlToFsPath(src) {
|
|
24
|
+
try {
|
|
25
|
+
const u = new URL(src);
|
|
26
|
+
if (u.protocol !== "file:")
|
|
27
|
+
return undefined;
|
|
28
|
+
let p = decodeURIComponent(u.pathname);
|
|
29
|
+
if (/^\/[A-Za-z]:\//.test(p))
|
|
30
|
+
p = p.slice(1);
|
|
31
|
+
return p;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function mimeFromFsPath(p) {
|
|
38
|
+
const ext = p.slice(p.lastIndexOf(".") + 1).toLowerCase();
|
|
39
|
+
if (ext === "jpg" || ext === "jpeg")
|
|
40
|
+
return "image/jpeg";
|
|
41
|
+
if (ext === "gif")
|
|
42
|
+
return "image/gif";
|
|
43
|
+
if (ext === "webp")
|
|
44
|
+
return "image/webp";
|
|
45
|
+
if (ext === "svg")
|
|
46
|
+
return "image/svg+xml";
|
|
47
|
+
if (ext === "bmp")
|
|
48
|
+
return "image/bmp";
|
|
49
|
+
return "image/png";
|
|
50
|
+
}
|
|
51
|
+
function tryReadFileLinkedDataUrl(src) {
|
|
52
|
+
const cached = FILE_PREVIEW_CACHE.get(src);
|
|
53
|
+
if (cached === null)
|
|
54
|
+
return undefined;
|
|
55
|
+
if (cached)
|
|
56
|
+
return cached;
|
|
57
|
+
const proc = globalThis;
|
|
58
|
+
if (!proc.process?.versions?.node) {
|
|
59
|
+
FILE_PREVIEW_CACHE.set(src, null);
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const fsPath = fileUrlToFsPath(src);
|
|
63
|
+
if (!fsPath) {
|
|
64
|
+
FILE_PREVIEW_CACHE.set(src, null);
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const fs = proc.process.getBuiltinModule?.("fs");
|
|
69
|
+
if (!fs?.existsSync || !fs.readFileSync || !fs.existsSync(fsPath)) {
|
|
70
|
+
FILE_PREVIEW_CACHE.set(src, null);
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const b64 = fs.readFileSync(fsPath).toString("base64");
|
|
74
|
+
const dataUrl = `data:${mimeFromFsPath(fsPath)};base64,${b64}`;
|
|
75
|
+
FILE_PREVIEW_CACHE.set(src, dataUrl);
|
|
76
|
+
return dataUrl;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
FILE_PREVIEW_CACHE.set(src, null);
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function linkedImagePlaceholderDataUri(src, w = 320, h = 180) {
|
|
84
|
+
const label = escapeXml(linkedImageLabel(src));
|
|
85
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${Math.max(1, Math.round(w))}" height="${Math.max(1, Math.round(h))}">` +
|
|
86
|
+
`<rect width="100%" height="100%" fill="#f3f4f6"/>` +
|
|
87
|
+
`<rect x="0.5" y="0.5" width="99%" height="99%" fill="none" stroke="#d1d5db" stroke-dasharray="6 4"/>` +
|
|
88
|
+
`<text x="50%" y="46%" text-anchor="middle" font-size="14" fill="#6b7280" font-family="sans-serif">Linked image</text>` +
|
|
89
|
+
`<text x="50%" y="60%" text-anchor="middle" font-size="12" fill="#6b7280" font-family="sans-serif">${label}</text>` +
|
|
90
|
+
`</svg>`;
|
|
91
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
92
|
+
}
|
|
93
|
+
function displayImageSrc(src, w = 320, h = 180) {
|
|
94
|
+
if (!isFileLinkedSrc(src))
|
|
95
|
+
return src;
|
|
96
|
+
return tryReadFileLinkedDataUrl(src) ?? linkedImagePlaceholderDataUri(src, w, h);
|
|
97
|
+
}
|
|
98
|
+
function drawLinkedImagePlaceholder(ctx, bbox, src) {
|
|
99
|
+
ctx.save();
|
|
100
|
+
ctx.fillStyle = "#f3f4f6";
|
|
101
|
+
ctx.fillRect(bbox.x, bbox.y, bbox.w, bbox.h);
|
|
102
|
+
ctx.strokeStyle = "#d1d5db";
|
|
103
|
+
ctx.setLineDash([6, 4]);
|
|
104
|
+
ctx.lineWidth = 1;
|
|
105
|
+
ctx.strokeRect(bbox.x + 0.5, bbox.y + 0.5, bbox.w - 1, bbox.h - 1);
|
|
106
|
+
ctx.setLineDash([]);
|
|
107
|
+
ctx.fillStyle = "#6b7280";
|
|
108
|
+
ctx.font = "12px sans-serif";
|
|
109
|
+
ctx.textAlign = "center";
|
|
110
|
+
ctx.textBaseline = "middle";
|
|
111
|
+
const label = linkedImageLabel(src);
|
|
112
|
+
ctx.fillText("Linked image", bbox.x + bbox.w / 2, bbox.y + bbox.h / 2 - 8);
|
|
113
|
+
ctx.font = "11px sans-serif";
|
|
114
|
+
ctx.fillText(label, bbox.x + bbox.w / 2, bbox.y + bbox.h / 2 + 10, Math.max(8, bbox.w - 12));
|
|
115
|
+
ctx.restore();
|
|
116
|
+
}
|