Conversation
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as outdated.
This comment was marked as outdated.
| - name: Build WASM | ||
| run: ./build-wasm.sh | ||
|
|
||
| - name: Run Vue TSC |
There was a problem hiding this comment.
这里调整顺序是因为 build (resvg) wasm 的过程会顺便生成用于调用 wasm 能力的 js & d.ts 文件,它们(尤其是 d.ts 文件)是前端代码静态检查的依赖
|
|
||
| ## Resvg dependency | ||
|
|
||
| The tool currently pins `nighca/resvg` at a specific commit. The fork carries the cluster-aware font-fallback patch from [linebender/resvg#1087](https://github.com/linebender/resvg/pull/1087), which is required for project-font sequences containing variation selectors or ZWJ emoji before a later fallback font. Once that patch ships in upstream Resvg, replace the fork with the corresponding upstream release or revision. |
There was a problem hiding this comment.
这里是晨会提到过的,目前使用的 resvg 是我 fork 的版本,等上游解决 cluster-aware font-fallback 的问题后换成非 fork 版本
| - **Node.js**: ^24.11.1 | ||
| - **pnpm**: ^11.9.0 | ||
| - **Go**: >= 1.25.0 | ||
| - **Rust**: >= 1.88 with the `wasm32-unknown-unknown` target |
There was a problem hiding this comment.
会要求 spx-gui 开发者本地有 rust 工具链
| const injectedSvgText = await injectFontsToSvgText(svgText, [], new Map()) | ||
| return new Blob([injectedSvgText ?? ab], { type: 'image/svg+xml' }) | ||
| const svgTextWithFontPreferences = applyFontPreferences(svgText, this.config.fontPreferences) | ||
| const png = (await this.getRenderer()).render(svgTextWithFontPreferences, { maxSize: maxSvgRenderSize }) |
There was a problem hiding this comment.
这里缩小 PNG 后,背景的逻辑尺寸也跟着变了。比如 2048×1024 的 SVG 转成 1024×512 后,StageViewer 和 MapViewer 的原尺寸、平铺模式都会把背景缩小一半。这里是符合预期的吗?
There was a problem hiding this comment.
这不是预期的,清晰度下降可以接受,但应该保持图片尺寸不变的,我来处理下
| } | ||
| } | ||
| } | ||
| default_select_fallback(character, used_fonts, fontdb) |
There was a problem hiding this comment.
按字体文档的约定,指定列表都缺字后应该显示缺字符号;已经匹配到项目字体的情况下,这里应该直接返回 None?
builder/docs/product/font.zh.md
Lines 138 to 159 in ded616c
There was a problem hiding this comment.
嗯是的这里逻辑有问题
还有一个回退记录覆盖的问题:同一个
<text>中,两个<tspan>分别指定default,B和default,C,因为主字体 ID 相同,第二次 insert 会覆盖第一次的记录,导致第一段缺字时也使用 C。
这也是个问题,是 resvg 的接口有缺陷;需要调整上游接口来支持
| }) | ||
| .collect::<Vec<_>>(); | ||
| if let Some((id, fallback_ids)) = candidate_ids.split_first() { | ||
| fallbacks.lock().unwrap().insert(*id, fallback_ids.to_vec()); |
There was a problem hiding this comment.
还有一个回退记录覆盖的问题:同一个 <text> 中,两个 <tspan> 分别指定 default,B 和 default,C,因为主字体 ID 相同,第二次 insert 会覆盖第一次的记录,导致第一段缺字时也使用 C。
| if (initialization == null) initialization = init() | ||
| await initialization |
There was a problem hiding this comment.
这里初始化失败会一直保留rejected Promise,或许可以考虑用已有的 memoizeAsync 来缓存 init()?
There was a problem hiding this comment.
可以考虑用已有的
memoizeAsync来缓存init()
嗯这样会好点
| let tree = usvg::Tree::from_str(svg, &options).map_err(error)?; | ||
| let size = tree.size().to_int_size(); | ||
| let (width, height, scale) = | ||
| render_size(size.width(), size.height(), render_options.max_size); |
There was a problem hiding this comment.
这里的 maxSize 只限制最终 PNG 的尺寸,但 SVG 里的 <pattern> 还会单独生成一张图片,这张图片没有受到限制。
复现:一个 64×64 的 SVG,里面用了 4096×4096 的 pattern。最终 PNG 虽然只有 64×64,渲染过程中仍然会为这个 pattern 分配 4096 × 4096 × 4 = 64 MiB 的内存。
Closes #3366.
Summary
fontPreferencesarray to the editor SVG rendering context;Fallback coverage
The fork retains the upstream compound/ZWJ emoji regression and adds a Builder-aligned regression: after a variation-selector/ZWJ emoji cluster, fallback continues to a later configured font. This protects the
A❤️B … 👩💻andABC ❤️ 中文class of project-font content.Cost and local measurements
The renderer initializes lazily: it is first needed only when a font-aware SVG image is rendered, not during normal application startup. The figures below are from the local production build and a Node 24.17.0 benchmark on the development MacBook Air. They are implementation costs, not a claim about end-to-end browser or mobile-device latency.
resvg_bg.wasmis 3.78 MiB uncompressed; 1.20 MiB gzip in the local production build (0.82 MiB when locally Brotli-compressed at quality 11). CDN compression, cache state, and device network determine actual transfer time.Files this can also cause all of those font resources to be fetched. The built-in default face adds another 58 KiB.WeakMapavoids repeated rasterization of the sameFile, but each distinct image has this initial cost.Other implications:
Verification
usvgparser suite (including both fallback regressions).nighca/ProjectFontsrenders the project backdrop with Chinese text and the final emoji/variation-selector cluster intact.The PR remains a draft while we obtain final product review. The fork is pinned by immutable commit; when upstream #1087 merges, we should replace it with the corresponding upstream release or revision.