Skip to content

Improve PAGX SVG export coverage and add shared modifier resolver utilities.#3427

Open
OnionsYu wants to merge 209 commits into
Tencent:mainfrom
OnionsYu:feature/onionsyu_export_svg2
Open

Improve PAGX SVG export coverage and add shared modifier resolver utilities.#3427
OnionsYu wants to merge 209 commits into
Tencent:mainfrom
OnionsYu:feature/onionsyu_export_svg2

Conversation

@OnionsYu
Copy link
Copy Markdown
Contributor

完善 PAGX 的 SVG 导出能力,并抽取多个导出器共享的辅助模块。

主要变更

  • SVG 导出器增强:修复垂直文本布局、textAlign End 与 overflow hidden 的换行行为,支持 textLength/lengthAdjust 让非末尾列填满容器;按布局解析的缩放对齐 PAGX 渲染器,正确处理 RTL 段落与字形 run;将 bold/italic 字体样式编码进 font-family,便于浏览器与 CoreText 解析正确字面。
  • ImagePattern:仅有 filePath 时跳过 bake 路径并内联图像字节,正确处理 Decal 等 scaleMode;修复 fitsToGeometry 坐标空间下的渐变导出。
  • 新增 SVGFeatureProbe,用于检测目标渲染器对 SVG 特性的支持情况。
  • 抽取 ExporterUtils 与 ModifierResolver 作为 SVG/PPT 导出器共享的工具与修饰符求解层,去重原本散落在两个导出器中的逻辑。
  • TextLayout 调整以配合上述共享工具的接口。
  • 新增 PAGXSVGTest 覆盖上述新增/修复行为。

不影响 PPT 导出的对外行为,PPTWriter.h 的清理仅为同步共享头的迁移。

OnionsYu added 30 commits April 2, 2026 10:28
…tch when tileModeX or tileModeY is Repeat or Mirror.
OnionsYu added 16 commits May 9, 2026 16:01
…ks, modifier edges, and text alignment paths.
…ort_svg2

# Conflicts:
#	src/pagx/ppt/PPTExporter.cpp   resolved by feature/onionsyu_export_ppt2 version
…oving the duplicated ApplyStrokeBoxInset definition.
…resolved scales RTL paragraphs and glyph runs.
…on that misuses baselineY as a Y coordinate.
…d column so wrapping textAlign End and overflow hidden render correctly.
…t spacing so non-last columns stretch to fill the box like the PAGX renderer.
…o browsers and CoreText viewers use the real bold face.
…rt_svg2

# Conflicts:
#	.codebuddy/skills/pagx/references/cli.md
#	CMakeLists.txt
#	include/pagx/PPTExporter.h
#	include/pagx/SVGExporter.h
#	src/cli/CommandExport.cpp
#	src/pagx/TextLayout.cpp
#	src/pagx/TextLayout.h
#	src/pagx/ppt/PPTExporter.cpp
#	src/pagx/ppt/PPTFeatureProbe.h
#	src/pagx/ppt/PPTStyleEmitter.cpp
#	src/pagx/ppt/PPTTextWriter.cpp
#	src/pagx/ppt/PPTWriter.h
#	src/pagx/svg/SVGExporter.cpp
#	src/pagx/utils/ExporterUtils.cpp
#	src/pagx/utils/ExporterUtils.h
#	test/src/PAGXCliTest.cpp
#	test/src/PAGXPPTTest.cpp
OnionsYu added 5 commits May 13, 2026 11:33
…tting CSS Compositing Level 2 mix-blend-mode values and guarding feBlend against the unsupported Porter-Duff modes.
…ss SVG and PPT exporters to remove mirrored implementations.
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 13, 2026

Codecov Report

❌ Patch coverage is 96.33827% with 84 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.30%. Comparing base (d3454c1) to head (a046ed8).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/pagx/utils/ModifierResolver.cpp 90.35% 18 Missing and 12 partials ⚠️
src/pagx/utils/ExporterUtils.cpp 74.35% 19 Missing and 1 partial ⚠️
src/pagx/utils/ExporterUtils.h 73.52% 9 Missing and 9 partials ⚠️
src/pagx/PAGXOptimizer.cpp 42.10% 4 Missing and 7 partials ⚠️
src/pagx/svg/SVGBlendMode.h 40.00% 1 Missing and 2 partials ⚠️
src/pagx/ppt/PPTTextWriter.cpp 92.59% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3427      +/-   ##
==========================================
+ Coverage   79.69%   80.30%   +0.61%     
==========================================
  Files         580      584       +4     
  Lines       60823    63580    +2757     
  Branches    18920    19304     +384     
==========================================
+ Hits        48470    51057    +2587     
- Misses       8686     8799     +113     
- Partials     3667     3724      +57     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Collaborator

@shlzxjp shlzxjp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

对 PR 进行了多维度代码评审(架构、API、实现、健壮性、安全、规范、测试),所有发现均经实际代码交叉验证。整体质量较高(注释充分、严守项目禁用项),主要问题集中在共享重构未真正完成、光栅化兜底路径与公开 API 边界保护几处。具体见行级评论。

@@ -0,0 +1,601 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[H1] 共享重构未真正生效:本文件 601 行与 src/pagx/ppt/PPTModifierResolver.cpp diff -u 仅差 28 行(类名 + 2 处注释)。PPTWriter.h:51,703 仍持有 PPTModifierResolver,二进制中并存两份等价实现,PR 标题 "shared modifier resolver utilities" 目标未达成。建议让 PPT 迁移到 pagx::ModifierResolver 并删除 src/pagx/ppt/PPTModifierResolver.{h,cpp},或至少 using PPTModifierResolver = ModifierResolver;

canvas->concat(targetLayer->getRelativeMatrix(coordinateSpace));
targetLayer->draw(canvas);
}
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[H2] MaskedLayerDrawerInSpace 缺少 scrollRect clip。同文件 826-844 的 MaskedLayerDrawer 注释明示 tgfx::Layer::draw 不应用 layer 自身 scrollRect,必须由 drawer 显式 clipRectSVGExporter::rasterizeLayerAsImage(SVGExporter.cpp:2077)走此 drawer,且 writeLayer 成功后 return(SVGExporter.cpp:2272)跳过后续 scrollRect 处理。当 layer 同时带 scrollRect 与 unsupported feature(TextPath/TextModifier/Conic/Diamond)时,导出的 <image> 含越界像素。建议在 concat 之后、draw 之前补上对称的 scrollRect clip。

auto data = GetImageData(image);
if (data && data->size() > 0) {
return EncodeImageDataURI(data->bytes(), data->size());
}
return image->filePath;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[M1] 加载失败回退为本地绝对路径,与上方注释 "SVG cross-environment usage cannot rely on absolute filesystem paths" 自相矛盾,且会泄露用户机器路径。建议失败时返回 {} 让上层走兜底,避免产出不可移植 SVG。

return "url(#" + defId + ")";
}
// If baking failed, fall through to try native tiling for Repeat modes
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[M2] Mirror/Clamp bake 失败时 fall through 到下方 non-tiling 单图分支(行 652),输出"单张图覆盖整 shape",与 Mirror/Clamp 语义不一致。建议 bake 失败且 mode 非 Repeat 时单独处理(保留 native pattern 或显式标错),不要静默落入 Repeat 路径。

* stretch the denser bitmap over the same visible extent, yielding a crisper result.
* The default value is 192.
*/
int rasterDPI = 192;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[M3+L4] rasterDPI 类型 int 且公开 API 文档无合法范围、入口无 clamp。
(1) 0/负数被 pixelWidth<=0 兜住但静默回退到 vector 路径,与 rasterizeUnsupportedFeatures=true 语义矛盾(用户期望保真);
(2) 极端大值在 ExporterUtils.cpp:809 static_cast<int>(std::ceil(width * pixelScale)) 触发 float→int UB(C++ 实现定义)。
对照 HTMLExportOptions::rasterScale 已声明 Valid range: (0, 4]. clamped,建议对齐:API 文档加合法区间,并在 ToSVG/ToFile 入口 clamp 到 [1, 4096] 之类。

Comment thread test/src/PAGXSVGTest.cpp
* WebP magic bytes should be detected and produce a data:image/webp URI.
*/
PAGX_TEST(PAGXSVGTest, SVGExport_WebpMimeDetection) {
static constexpr uint8_t kFakeWebp[] = {'R', 'I', 'F', 'F', 0x10, 0x00, 0x00, 0x00,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[L1] kFakeWebp 违反规范"不加 k 前缀"。

private:
PAGXDocument* _doc;

// Allocates a new pagx::Path whose data is a fresh PathData owned by _doc.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[L2] 私有方法 makePathFromData 上方多行注释违反规范"私有方法不加注释"。如属非显而易见的算法选择,应迁至 .cpp 实现处。同样的问题见 71、75 行(applyTrimToElementapplyRoundCornerToElement)。

Matrix groupMatrix = BuildGroupMatrix(group);
Matrix combined = transform * groupMatrix;
writeTextBoxGroup(out, group, group->elements, combined);
bool SVGWriter::rasterizeLayerAsImage(SVGBuilder& out, const Layer* layer) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[L3] 函数体内 4 处 return false 静默吞错(layerMap 缺失/!root、!tgfxLayer、!pngData、bounds 空)。建议接入项目日志或对应 [M5] warnings 通道,至少 Debug 构建中输出诊断。

Comment thread test/src/PAGXSVGTest.cpp
doc->layers.push_back(layer);

pagx::SVGExporter::Options opts;
opts.rasterizeUnsupportedFeatures = false;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[T1+T2] 测试覆盖缺口:
(1) 仅此处与 1982 行命中 rasterizeUnsupportedFeatures,且都设为 false无任何用例验证 rasterizeUnsupportedFeatures=true(默认)路径,即 conic/diamond/TextPath 实际生成 <image> 数据 URI 的端到端断言缺失。
(2) grep "rasterDPI" test/src/ 0 命中,新增 API 字段 rasterDPI(默认 192、边界 0/负/极大)零覆盖。

Comment thread test/src/PAGXSVGTest.cpp
* (rather than the legacy SVG 1.1 "tb") is used because text-anchor handling
* in the legacy keyword path is inconsistent across renderers.
*/
PAGX_TEST(PAGXSVGTest, SVGExport_WritingModeVertical) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[T3] WritingModeVertical 仅断言 vertical-rl 字符串。grep "textLength|lengthAdjust" test/src/ 0 命中,新增的 vertical justify 拉伸逻辑(SVGExporter.cpp:1773-1777)未被实测。建议补一个超界文本 + TextAlign::Justify 的 vertical 用例,断言 textLength=lengthAdjust="spacing" 实际写入 SVG。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants