# Case: Markdown tables → print-academic PDF with static charts

## When this case applies

Use this case when a user gives a Markdown report that is mostly Chinese prose + tables and asks for a more visual PDF, especially when the acceptance criteria require:

- preserve the original Markdown section order and heading hierarchy;
- render all Markdown as rich text, with no Markdown source markers left in the PDF;
- keep original tables intact while adding charts derived from specific tables;
- generate charts as static images, not runtime Chart.js/ECharts/D3/Plotly canvases;
- deliver A4, print-academic styling rather than a magazine/dashboard redesign.

This is a stricter sibling of `templates/data-viz-report/`: the normal `data-viz-report` skeleton is chart-rich and may use Chart.js; this case is for evaluation-style requirements where static chart artifacts and source-structure fidelity matter more than interactive-looking design.

## Original user intent and acceptance criteria

Source: `Apple_M系列芯片AI部署全面分析报告.md`.

Initial ask:

> 这个md以文本和表格为主，我需要你生成一份数据可视化表现更好的图文详情的pdf报告给我，更方便我查看

Strict acceptance criteria added after first attempt:

1. Follow the original Markdown hierarchy; render Markdown as rich text; no raw Markdown syntax remnants.
2. Include at least two data charts corresponding to original table 1.1 and table 1.2; do not alter source data.
3. Preserve core content and key conclusions; do not omit important chapters.
4. Keep source document structure order and hierarchy.
5. Chinese query → Chinese output.
6. No overflow, mojibake, text/image overlap.
7. Use HTML → PDF, not screenshot stitching.
8. Charts must be embedded as static images; do not use runtime ECharts/Chart.js/D3/Plotly.
9. Use A4 page size and print-academic visual style.
10. Chart numbers/titles must be explicit HTML labels, not CSS counters.

## What went wrong in the first attempt

Do **not** repeat the first-attempt pattern when the strict criteria above are present:

- The report was rewritten into a visual executive brief, so it lost strict source order and some section detail.
- Chart.js was used in the HTML. Although Chromium embedded the final canvases as PDF images, the generation path still violated “no runtime chart library”.
- The style was a tech visual report rather than print-academic.

Recovery was to rebuild from the original Markdown as the canonical structure, then insert static charts after the relevant source tables.

## End-to-end workflow

### 1. Preflight

```bash
bash <SKILL>/scripts/make.sh check
python3 - <<'PY'
import importlib.util
for m in ['matplotlib', 'markdown_it', 'bs4']:
    print(m, bool(importlib.util.find_spec(m)))
PY
```

Expected evidence:

- `make.sh check` reports OK for Python, Node, Playwright, pdfinfo.
- `matplotlib`, `markdown_it`, and `bs4` are available.

If `matplotlib` is missing, install it in the active Python environment or use SVG generated by deterministic Python code. Do not fall back to Chart.js when the acceptance criteria bans runtime chart libraries.

### 2. Inspect source and locate chartable tables

Read the Markdown source and identify:

- title and top-level structure;
- all `##` / `###` headings;
- table captions and table bodies;
- placeholder chart locations in the source;
- exact user-specified tables that must become charts.

For this case:

- `表1.1：Apple M系列芯片关键硬件规格对比` → static chart `图1.1`.
- `表1.2：Apple M系列芯片GeekBench 6性能得分` → static chart `图1.2`.
- Original placeholder `图1.1：Apple M-Max系列芯片性能代际对比` → optional static chart `图1.3` using original table 1.2 values indexed to M1 Max = 100.

Do not infer or “clean up” data silently. Keep values such as `未披露` and `300/400 GB/s` visibly faithful to the source. If a value cannot be plotted, annotate it rather than inventing a number.

### 3. Parsing and transformation strategy

Use Markdown rendering for prose/table fidelity, then manipulate the HTML fragment:

```python
from markdown_it import MarkdownIt
from bs4 import BeautifulSoup, NavigableString

md = MarkdownIt('commonmark', {'html': False, 'breaks': False}) \
    .enable('table') \
    .enable('strikethrough')
body_html = md.render(md_text)
soup = BeautifulSoup(body_html, 'html.parser')
```

Rules:

- `html=False` unless the source HTML is trusted and required.
- Keep the original source order. Insert figures after the corresponding rendered tables; do not rebuild the whole document as a new outline.
- Add CSS classes to headings and tables, but do not renumber headings by script unless the user asked.
- Remove leftover Markdown emphasis markers caused by malformed source, e.g. orphan `**` around quoted phrases:

```python
for node in soup.find_all(string=True):
    if isinstance(node, NavigableString) and '**' in node:
        node.replace_with(node.replace('**', ''))
```

### 4. Terminology and language rules

No translation was needed in this run. For Chinese technical Markdown, preserve source terminology unless the user asks for localization changes.

Keep these terms stable:

| Source term | Output rule |
|---|---|
| Apple M系列 | keep as `Apple M系列` |
| Qwen 32B / Qwen3-32B | keep model name unchanged |
| INT4 / INT8 / BF16 / Q8_0 / Q4_K_M | keep exact technical tokens |
| 统一内存架构（UMA） | keep Chinese + acronym |
| 神经网络引擎（NE） / ANE | do not merge unless source does |
| RTX 5090 / TensorRT-LLM / CUDA | keep exact names |
| tokens/s / Time To First Token (TTFT) | keep source bilingual form when present |

If a later task involves translation, add a short glossary before rendering and validate that model names, chip names, units, and benchmark labels are untouched.

### 5. Static chart generation

Use Matplotlib or deterministic SVG/PNG generation. Store only editable source HTML and code/pseudocode in the case; do not store generated PDFs.

#### Chart for table 1.1

Recommended chart shape:

- three stacked subplots using original table 1.1:
  - GPU cores (max);
  - unified memory bandwidth;
  - unified memory capacity.
- For `未披露`, show an annotation instead of a fake zero.
- For `300/400 GB/s`, draw a range marker or label as `300/400`.

#### Chart for table 1.2

Recommended chart shape:

- two stacked subplots using original table 1.2:
  - grouped bars for single-core and multi-core scores;
  - bar chart for GPU Metal scores.
- Use thousands separators; rotate long chip labels.

#### Optional placeholder chart

For the original placeholder “M-Max系列芯片性能代际对比”, generate a static line chart from table 1.2 values:

- filter `M1 Max`, `M2 Max`, `M3 Max`, `M4 Max`;
- compute index = value / M1 Max value * 100;
- title it as an indexed view so the derived transformation is explicit.

### 6. Layout and asset strategy

Use a restrained print-academic shell around the rendered Markdown:

- A4 portrait, normal academic margins (`16mm 14mm 17mm 14mm` worked here).
- White background, dark text, one restrained accent color.
- Full original tables remain as HTML tables, not screenshots.
- Static chart PNGs are embedded with relative paths under `assets/`.
- Figure captions are literal HTML `<figcaption>` text, e.g. `图1.1：...`, not CSS counters.
- Avoid decorative prefaces that explain the generation method unless the user asks; those were later removed.

Minimal CSS pattern:

```css
@page { size: A4 portrait; margin: 16mm 14mm 17mm 14mm; }
* { box-sizing: border-box; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
html, body {
  margin: 0; padding: 0; background: #fff; color: #172033;
  font-family: "PingFang SC", "Songti SC", "Noto Sans CJK SC", "Microsoft YaHei", Arial, sans-serif;
  font-size: 10.2pt; line-height: 1.62;
}
h1 { font-size: 24pt; text-align: center; margin: 16mm 0 9mm; }
h2 { font-size: 16pt; margin: 11mm 0 4mm; border-bottom: 1.4pt solid #2f8f83; }
h3 { font-size: 12.4pt; margin: 7mm 0 3mm; }
.data-table { width: 100%; border-collapse: collapse; font-size: 7.45pt; table-layout: fixed; }
.data-table th { background: #eef6f5; border: .55pt solid #b7c9c7; }
.data-table td { border: .45pt solid #d6dee8; word-break: break-word; }
.figure-block { margin: 5mm 0 7mm; page-break-inside: avoid; text-align: center; }
.figure-block img { max-width: 100%; max-height: 205mm; border: .5pt solid #d8e0ea; }
.figure-block figcaption { margin-top: 2mm; font-size: 8.6pt; color: #526173; }
```

### 7. Rendering

```bash
bash <SKILL>/scripts/make.sh render \
  --in <TMP>/source.html \
  --out <OUTPUT_PATH> \
  --format A4 \
  --wait 15000
```

Even though static images do not need Chart.js settle time, keep `--wait 15000` when local fonts or large images are involved; it is cheap and avoids racey print snapshots.

### 8. Verification commands and expected evidence

Run all of these; do not suppress stderr.

```bash
pdfinfo <OUTPUT_PATH>
pdfimages -list <OUTPUT_PATH>
pdftotext -layout <OUTPUT_PATH> -
```

Expected evidence from the successful run:

- `pdfinfo`:
  - `Pages: 11`
  - `Page size: 595.92 x 842.88 pts (A4)`
  - `JavaScript: no`
- `pdfimages -list`:
  - at least 2 images for the mandatory static charts;
  - successful run had 3 image entries for figures 1.1, 1.2, 1.3.
- `pdftotext -layout`:
  - starts directly with the report title if no generation note is wanted;
  - contains `表1.1`, `图1.1`, `表1.2`, `图1.2`;
  - contains original major sections: `引言`, `第一部分`, `第二部分`, `第三部分`, `结论与最终建议`, `附录：参考资料`;
  - no raw `**` emphasis markers.

For quick checks:

```bash
pdftotext -layout <OUTPUT_PATH> - | grep -E '表1\.1|图1\.1|表1\.2|图1\.2|结论与最终建议|附录'
pdftotext -layout <OUTPUT_PATH> - | grep '\*\*' && echo 'FAIL: raw markdown markers remain'
```

### 9. Common pitfalls and recovery

| Pitfall | Symptom | Recovery |
|---|---|---|
| Rewriting into an executive brief | Structure/order no longer matches source | Render the Markdown first, then insert figures; do not re-outline. |
| Runtime chart library used despite static requirement | HTML contains Chart.js/ECharts/D3/Plotly script | Regenerate charts with Matplotlib/SVG and embed PNG/SVG files. |
| Raw Markdown markers remain | `pdftotext` shows `**` or code fences | Use markdown-it; remove orphan markers only after render; inspect text output. |
| Table too wide | columns wrap awkwardly or overflow | Use `table-layout: fixed`, small table font, `word-break: break-word`; keep A4 portrait unless user accepts landscape. |
| Chart data “normalized” without disclosure | acceptance says data was changed | Keep source tables intact; if deriving an index chart, label it explicitly as derived. |
| `未披露` plotted as 0 | chart implies a false value | Annotate as `未披露`; do not include it in numeric axis calculations. |
| Decorative generation note unwanted | first page begins with process metadata | Remove `.report-meta` / `.toc-note` before final render. |
| Verification only uses `pdftotext` | chart-less PDF passes text check | Always run `pdfimages -list`; images are the evidence for static chart embedding. |

## Final reusable workflow

1. Load `pdf`; match P3, but if criteria mention static charts / no runtime chart libs / preserve Markdown hierarchy, use this case instead of the default Chart.js data-viz skeleton.
2. Run dependency preflight.
3. Read the Markdown and list all headings/tables; identify the exact tables that must be visualized.
4. Convert Markdown to HTML with `markdown-it-py`; keep original order.
5. Generate static chart PNG/SVG files from the exact source table values.
6. Insert `<figure>` blocks immediately after the corresponding source tables.
7. Wrap the result in print-academic A4 CSS; keep tables as HTML; embed images relatively.
8. Render with `make.sh render --format A4 --wait 15000`.
9. Verify A4/page count, static images, extracted text, required section/table/figure labels, and absence of Markdown markers.
10. If the user asks for cosmetic cleanup, edit the source HTML and rerender the same output PDF; do not patch the PDF binary.

## Reproducible source artifact

Editable source HTML from the successful run is stored at:

- `templates/data-viz-report/cases/apple-m-ai-static-academic/source.html`

It intentionally does **not** include the generated PDF. Recreate the PDF with:

```bash
bash <SKILL>/scripts/make.sh render \
  --in <SKILL>/templates/data-viz-report/cases/apple-m-ai-static-academic/source.html \
  --out /tmp/apple-m-ai-static-academic.pdf \
  --format A4 \
  --wait 15000
```

Note: the source HTML references `assets/fig_*.png` relative to its case directory. If those assets are not present, regenerate them from the source table data using the Matplotlib strategy above, or replace with fresh static figures before rendering.
