8.3 KiB
可数据绑定的原生图表组件
chart_component.py把明确的数据生成一个独立 SVG g:其中包含原生 Chart metadata、同源的可见 SVG fallback,以及标题、单位、图例和来源文字。把它组合进所选 template 的页面;组件不复制母版、版式或页面 UI。它是技术组件接口,不替调用者决定报告应使用哪些图表。
支持范围
| type/示例数据 | 用途与适配 | 当前技术边界 |
|---|---|---|
column/column.json |
比较少量类别;多系列分组 | 数轴包含零,显式 min/max/major_unit;支持正、负、零 |
bar/bar.json |
较长类别名称的横向数量比较 | 上游编译器不接受 bar 的 axes;原生应用自动选择范围与刻度,SVG 标尺只作预览;保留真实数值及原生数据标签 |
line/line.json |
按顺序比较变化 | 等间距类别轴、显式数轴;直线与圆点;不把类别轴当成连续日期轴 |
scatter/scatter.json |
展示成对的 X/Y 观测 | points: [[x, y], …];两个显式数轴;圆点,无回归拟合 |
pie/pie.json |
单组非负构成 | 一个系列,总和大于零;零值保留在数据与图例中,不绘制零面积扇形 |
doughnut/doughnut.json |
单组非负构成 | 与 pie 相同;内圈比例75%,与锁定原生编译器一致 |
column-stacked/column-stacked.json |
比较各类别的总量及组成 | 初版只支持非负堆叠;范围基于各类别总量,不把单个系列最大值当堆叠上界 |
示例全部明确标记 synthetic-demo,使用564×464区域,保留单位与来源。它们只演示技术能力,不能作为真实报告证据。图例、文字和轴预留空间不足时工具报错,不自动缩字;改写标签或使用更宽区域后再生成。宽度估算是保守的字符检查,仍需查看目标应用的实际渲染。
这层封装不接受混合图、双轴、百分比堆叠、3D、气泡、时间轴、缺失值、平滑曲线、任意自定坐标轴或额外格式字段。上游可能另有能力,但不能经本组件接口静默转入。需要这些能力时先确认作者路线与支持范围。
数据契约
JSON顶层字段:schema_version: 1,data_kind(synthetic-demo/observed/derived),type、非空 title、unit(允许空字符串)、series。source可选。scatter可另写x_unit,并省略categories;其余类型必须提供非空、唯一的categories标签。
每个系列有稳定、唯一的id、非空name,以及与类别同长的values,或scatter的非空points数组。ID使用[A-Za-z][A-Za-z0-9_-]{0,63}。数值必须是JSON数字且有限;布尔值、数字字符串、NaN和无穷均拒绝。为避免SVG/OOXML标量计算溢出或失去有效标签,当前非零数值的绝对值支持1e-9至1e12,零独立支持。不会补齐长度、删掉零值、改写负数或按百分比重算原始数据。CLI拒绝重复JSON键和未知字段。
source只作为显示文字与元数据保留,工具不会读取链接、补充出处或证明其真实性。所有原始数据同时放入marker的component_data,供工程审计;原生图表cache及内嵌XLSX保存未缩放的实际数值。
样式来源
style和theme分别使用template_ref.py export-style的style、theme_data。字体从typography.families.body取得,字号默认取caption角色,缺少时取label;按canvas.pt_to_px换算。组件没有组织、字体名或十六进制配色的固定默认值。
template可选择声明style.components.charts:
{
"series_palette_indices": ["accent1", "accent3", "accent4"],
"text_role": "caption",
"text_palette_index": "dk1",
"grid_palette_index": "line"
}
未声明时,系列依次使用已有的accent1、accent3、accent4、accent5、accent6、accent2;文字使用dk1,网格使用line(缺少时dk2),图表背景使用lt1。颜色必须为调色板中已存在的六位hex值。系列按数组顺序绑定索引,pie按类别顺序绑定。需要的系列或扇形数超过不同索引数量时明确拒绝,不循环复用颜色。组件不会从系列名称推断状态或改动数据语义。
保持简洁的精细化优先处理信息层次:保留轻网格、统一标签与单位、适度留白,并按比较目的分配颜色。平行系列可用两个主题索引;当作者明确选择一个重点系列时,可在该页风格副本中设置 series_palette_indices: ["dk2", "accent1"],用中性参照配合主题色重点。颜色顺序必须对应明确系列,不从名称或数值自动选择“更好”的一组。
axis_palette_index 可将轴线绑定到现有调色板索引,默认跟随文字。value_labels: true 为非负分组 column 同时生成原生数值标签及同源 fallback;标签使用已声明字号,放不下时报错。默认关闭,不给每一种图表都加数字。bar 本身保留数值标签;其他类型或负值柱图需要按其信息特点另行设计。改动页面风格副本不改变全工程的系列语义;同一系列跨页需由作者保持绑定。
column、line、scatter与非负堆叠的显式数轴和SVG fallback使用同一组范围与步长;全零数轴为0至1。bar受锁定编译器限制,由原生应用自动选轴,并带有data-native-axis-policy="automatic-bar-preview-scale-may-differ"声明。显式数轴一致不代表原生应用绘图区、轴文字位置与SVG像素级一致。
生成与组合
使用安装方准备的Python环境,在任何目录执行:
python /path/to/skill/scripts/chart_component.py \
--manifest /absolute/path/to/template/manifest.json \
--theme selected-theme \
--data /absolute/path/to/column.json \
--bounds 64 156 564 464 --id results-chart \
--output /absolute/path/to/new-component.svg
CLI选择一个外部template/theme,输出对应canvas的独立组件SVG,拒绝覆盖文件及经符号链接写出。它没有页面的Master/Layout契约,不能直接冒充完整模板页面交给结构化原生导出器。
Python组合接口:
from chart_component import render_chart
component = render_chart(data, exported["style"], exported["theme_data"],
(64, 156, 564, 464), "results-chart")
# page_root是已有模板页,保留其Master/Layout、固定UI、占位符与素材引用。
slot = next(node for node in page_root if node.get("id") == "slot-left")
for child in list(slot):
slot.remove(child)
slot.append(component)
render_chart(data, style, theme, bounds, component_id) -> xml.etree.ElementTree.Element返回SVG g。使用当前页真实槽位的bounds;确认当前template确实有该槽位,不能把示例中的ID、坐标当通用约定。component_id在整页必须唯一。生成后不要单独改fallback图形或marker数据;修改JSON并重新生成,保持fallback指纹和元数据一致。
图例、标题、单位及来源位于marker外,编译为独立可编辑对象;marker内转换为真正的Chart和内嵌XLSX。此安排保持页面文字位置受模板控制。若在PowerPoint内改图表数据,外部图例或构成数量文字不会自动更新,需要一起修改,或从工程JSON重建。将图表JSON和生成工具声明为工程输入并锁定,可在迁移后重建;不能只交付生成的SVG与PPTX。
验证与限制
本项目的check_chart_components.py验证两theme的7类组件SVG、错误输入拒绝和已有文件保护;实际通过锁定编译器生成7类Chart及正负/全零边界案例,检查原生Chart类型、显式轴、自动bar轴、数值cache、内嵌XLSX、独立标题与合成数据声明。Chart对象的存在以PPTX关系和XML为证据。
这些检查不代表PowerPoint已实际打开、渲染、编辑或完成投影验收。进入真实页面后需按PowerPoint检查核对字形、标尺、单位、图例、数据与页内分区;页面审阅由负责人按已有逐页流程组织。图表导出失败必须报告,不能用fallback截图宣称已得到原生Chart。