96 lines
9.9 KiB
Markdown
96 lines
9.9 KiB
Markdown
# 可数据绑定的原生图表组件
|
||
|
||
`chart_component.py`把明确的数据生成一个独立 SVG `g`:其中包含原生 Chart metadata、同源的可见 SVG fallback,以及标题、单位、图例和来源文字。把它组合进所选 template 的页面;组件不复制母版、版式或页面 UI。它是技术组件接口,不替调用者决定报告应使用哪些图表。
|
||
|
||
## 支持范围
|
||
|
||
| type/示例数据 | 用途与适配 | 当前技术边界 |
|
||
|---|---|---|
|
||
| `column`/[column.json](../assets/components/charts/column.json) | 比较少量类别;多系列分组 | 数轴包含零,显式 min/max/major_unit;支持正、负、零 |
|
||
| `bar`/[bar.json](../assets/components/charts/bar.json) | 较长类别名称的横向数量比较 | 上游编译器不接受 bar 的 axes;原生应用自动选择范围与刻度,SVG 标尺只作预览;保留真实数值及原生数据标签 |
|
||
| `line`/[line.json](../assets/components/charts/line.json) | 按顺序比较变化 | 等间距类别轴、显式数轴;直线与圆点;不把类别轴当成连续日期轴 |
|
||
| `scatter`/[scatter.json](../assets/components/charts/scatter.json) | 展示成对的 X/Y 观测 | `points: [[x, y], …]`;两个显式数轴;圆点,无回归拟合 |
|
||
| `pie`/[pie.json](../assets/components/charts/pie.json) | 单组非负构成 | 一个系列,总和大于零;零值保留在数据与图例中,不绘制零面积扇形 |
|
||
| `doughnut`/[doughnut.json](../assets/components/charts/doughnut.json) | 单组非负构成 | 与 pie 相同;内圈比例75%,与锁定原生编译器一致 |
|
||
| `column-stacked`/[column-stacked.json](../assets/components/charts/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保存未缩放的实际数值。
|
||
|
||
遵循[文字与容器约束](text-and-containers.md):小号灰色/弱化说明只承载补充背景、来源或非关键口径。图表的实质性解释、关键条件和时机、结论,以及影响解释范围的限定或免责说明,必须进入可读的主体层级,不得塞进`source`、小字或仅存讲稿。关键单位和口径也不能因字段名被降级。图表标题、说明和正文承担不同作用时,用字号、字重和/或调色板颜色作出明确区分,不能只换字体。
|
||
|
||
## 样式来源
|
||
|
||
`style`和`theme`分别使用`template_ref.py export-style`的`style`、`theme_data`。每个文字角色通过`family_role`解析自己的字体、字号、字重和行高,按`canvas.pt_to_px`换算;不会把所有角色强制映射到body字体。图表标题使用`label`角色与`accent2`;没有`label`的旧样式沿用图表文字角色,通过颜色区分。单位、轴和图例使用显式`components.charts.text_role`(默认`caption`,缺少时为`label`)及`text_palette_index`;来源沿用标注角色的字体和字号,但颜色为`dk2`。组件没有组织、字体名或十六进制配色的固定默认值。
|
||
|
||
原生Chart轴当前只支持Regular(400),对需要其他轴字重的角色明确报错,不只改变SVG预览。pie/doughnut没有轴,独立标题、图例和来源可按角色保留加粗。普通原生文字/Table使用常规与加粗的字重语义;中间字重仍需目标字体与应用验证,不能仅据SVG的数值声明推断精确字重。
|
||
|
||
template可选择声明`style.components.charts`:
|
||
|
||
```json
|
||
{
|
||
"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环境,在任何目录执行:
|
||
|
||
```sh
|
||
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组合接口:
|
||
|
||
```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指纹和元数据一致。
|
||
|
||
同一种图表容器/变体共用内部排版实现,调用者只提供内容和整体bounds,不逐例移动标题、图例、图标或说明。需要新的说明区时定义共享组合规则,并重新生成整组;一个连续段落优先保持为一个原生文本框,必要拆分也必须共用一个逻辑文本块和换行规则。
|
||
|
||
图例、标题、单位及来源位于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检查](powerpoint-checks.md)核对字形、标尺、单位、图例、数据与页内分区;页面审阅由负责人按已有逐页流程组织。图表导出失败必须报告,不能用fallback截图宣称已得到原生Chart。
|