# OnlyOffice xlsx 渲染自查 + 行高 409.5 上限真相

> 用途：交付前用 **Maggie 实际看的引擎**（OnlyOffice，非 LibreOffice）把汇总表 xlsx 渲染成 PDF/图片做视觉自查；并厘清"长内容行高调不上去"的根因，避免重复踩坑浪费时间。
> 来源：2026-06-17 万达行高验证 + 世茂重做渲染自查。

## 一、x2t 渲染 xlsx → PDF（用 OnlyOffice 引擎，保真度最高）

OnlyOffice 文档转换器 `x2t` 在容器 `nextcloud-onlyoffice-1` 内，是 Maggie 在线编辑/查看时的同款引擎。比 `libreoffice --headless --convert-to pdf` 更能反映她看到的真实效果。

**format code**：513 = PDF。

**完整配方（已验证可用）**：
```bash
# 1. 容器内建可写目录（默认root建后chmod 777，让ds用户能写pdf输出）
docker exec nextcloud-onlyoffice-1 bash -c 'rm -rf /tmp/sx; mkdir -p /tmp/sx; chmod 777 /tmp/sx'

# 2. 主机预先写好转换配置 xml（关键：不要在容器内用 > 重定向，会因权限失败），cp进去
#    /tmp/s_convert.xml 内容：
#    <?xml version="1.0" encoding="utf-8"?>
#    <TaskQueueDataConvert xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
#    <m_sFileFrom>/tmp/sx/s.xlsx</m_sFileFrom>
#    <m_sFileTo>/tmp/sx/s.pdf</m_sFileTo>
#    <m_nFormatTo>513</m_nFormatTo>
#    <m_bIsNoBase64>true</m_bIsNoBase64>
#    </TaskQueueDataConvert>
docker cp /path/to/target.xlsx nextcloud-onlyoffice-1:/tmp/sx/s.xlsx
docker cp /tmp/s_convert.xml      nextcloud-onlyoffice-1:/tmp/sx/s.xml
docker exec nextcloud-onlyoffice-1 bash -c 'chmod 666 /tmp/sx/s.xlsx /tmp/sx/s.xml'

# 3. 以 ds 用户身份跑 x2t（x2t 本身就是 ds 进程，必须 -u ds）
X2T=/var/www/onlyoffice/documentserver/server/FileConverter/bin/x2t
docker exec -u ds nextcloud-onlyoffice-1 bash -c \
  "LD_LIBRARY_PATH=/var/www/onlyoffice/documentserver/server/FileConverter/bin $X2T /tmp/sx/s.xml 2>&1; echo x2t=\$?"

# 4. 取回 + 转图片自查
docker cp nextcloud-onlyoffice-1:/tmp/sx/s.pdf /tmp/out.pdf
pdftoppm -png -r 100 /tmp/out.pdf /tmp/out_img/s
```

### ⚠️ 权限坑（踩过3次才通）
- `x2t` 以 **ds 用户**运行，ds 的 HOME（`/var/www/onlyoffice/documentserver`）**不可写**。
- ds 能写 `/tmp` 和 `/var/lib/onlyoffice/documentserver/App_Data`。
- **致命点**：若用 root 建 `/tmp/xxx` 目录，ds 写不进 → `Permission denied` + `x2t=126/2`。修复=建目录后 `chmod 777`。
- **第二个坑**：在容器内用 `cat > s.xml <<EOF` 重定向写 xml 也会因 ds 权限失败。**正确做法：主机写好 xml，docker cp 进去**，不在容器内重定向。
- 成功标志：`x2t=0` 且 `/tmp/sx/s.pdf` 由 `ds ds` 所有、size>0。

### 截断检测用文本提取，不靠肉眼
渲染图片若无 vision 工具看不了内容时，用 `pdftotext` 抽全文，grep 各板块**末尾独特锚点句**是否都在（在=数据完整无截断）。比看图更可靠。
> ⚠️ pdftotext 会按列宽把长句**折行**，直接 grep 完整句会假阴性——先 `tr -d '\n' | tr -d ' '` 把整页拍平再 grep（见主 SKILL Pitfall 1b 末条）。

### ⚠️ LibreOffice headless 渲超高行 xlsx 会卡死/被 SIGKILL → 直接走 x2t，别耗重试（2026-06-22 悦拾光实证）
交付前想出 PDF 自查时，**别先试 `libreoffice --headless --convert-to pdf`**——当 xlsx 含**超高行 + 海量文本单元格**（如 K列法律风险 800字、行高 600–700pt）时，LibreOffice headless 转换阶段会**卡死**，前台 `timeout` 到点被 `-15`、加 `-env:UserInstallation` 独立 profile 重试仍 `-9`(SIGKILL)。诊断特征：`libreoffice --version` 秒回正常、`free -h` 内存充足（11G+ 可用），**唯独 `--convert-to` 这一步挂**——证明不是环境/内存问题，是 LibreOffice 对「超高行+超长单元格」headless 排版的已知缺陷。
- **别浪费在 LibreOffice 上反复试**（本次连耗 4 次 -9/-15 才转向）：内存够、版本正常却 `--convert-to` 挂，立即判定为超高行触发的 LO 缺陷，**直接切到本文上半「x2t 渲染」配方**。x2t 是 Maggie 实际看文件的引擎、对超高行无此问题，本就该优先用它，不该先绕 LibreOffice。
- 主 SKILL 已有铁律「LibreOffice 与 OnlyOffice 不同源、最终验收用 x2t」——本条补充其**故障模式**：LO 不只是「页数不准」，遇超高行会**直接转换失败**，更没有当 fallback 的价值。
- 清残留：转换挂掉常留 soffice 僵尸进程，重试前 `pkill -9 -f soffice; pkill -9 -f oosplash`。

### ⚠️ 程序化截断自检：用 skill 自带脚本，别每次手写行高估算（2026-06-22 悦拾光教训）
交付前判断「K列长文会不会被行高截断」，**直接跑 `scripts/xlsx-rowheight-analyze.py <文件.xlsx>`**（只读，按列宽折行估每行所需高度、标出「当前行高 < 建议行高」的行），不要在 `execute_code` 里临时手写一版行高估算函数——本次就因手写估算**严重偏低**（K列 800字实际需 43 行≈665pt，手写版只给了 21 行≈321pt），自检时才发现差一倍，白绕一圈。脚本已沉淀正确的 CJK 折行口径（中文按2宽、按 `\n` 切段逐段折行、向上取整），照用即可。设完行高用同口径复检 `可显行数 ≥ 需求行数` 全绿再渲染。

## 二、行高 409.5 pt 上限真相（别再浪费时间调高）

长内容单元格"行高调不上去"的根因已查清：

| 层 | 行为 |
|---|---|
| xlsx 文件格式 | **允许** >409.5pt（openpyxl 写 900 读回 900） |
| OnlyOffice **x2t 批量引擎** | **接受** 900pt，round-trip 不 clamp |
| OnlyOffice **网页版编辑器** | **存盘时 clamp 到 ~409.5pt** ← 这是 Maggie 编辑保存后高行变矮的真因 |

- 实测：给行设 760pt 交付，Maggie 在 OnlyOffice 网页版打开编辑保存后，被压回 409.6pt。**不是 xlsx 上限，是网页编辑器 clamp**。
- **Maggie 的决定（2026-06-17）**："行距搞不定就算了，就按照目前的最高行距就好。" → **统一用 409.6pt，不再折腾调更高**。
- 实务影响：单格约容纳 18–20 个折行；超长内容（如 K 列 700+ 字、第三部分 800+ 字）在网页**在线编辑视图可滚动看全、数据层完整**，只有导 PDF 给客户打印时才需另调版式（拆单元格/缩字号）。平时 409.6 即可，与万达定稿一致。
- 若某份确需更高且**不经网页编辑器存盘**（直接文件层交付），x2t 引擎会认 900pt——但 Maggie 已拍板用 409.6，除非她另有指示，不要自作主张调高。
