Files
tamabox/.workbuddy/memory/topics/share.md
T

59 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 分享卡(share card)
两处入口都在 `route/question/share.go`:`ShareCard`(箱子卡,960×540)、`QuestionShareCard`(单题卡)。
单题卡出图条件:**已回答 + 非私密 + 无站方处置**(`db.Restrictions.ActiveLevel` 非空即拒绝,fail-closed)。
## 文字排版
- 居中/限宽**必须**用 `measureTextWidth`(逐字 `HMetric().AdvanceWidth` + `Kern`,`scale = fontSize*64`),
它与 `DrawString` 返回笔位逐位相等。绝不要用「中文 1em / 其余 em 系数」估算:URL 里的 `: / . _ -`
只有 0.25~0.35em,整句会虚高 55%,居中直接偏 40px。
- 链接展示走 `displayURL`(去 `https://`)→ `layoutLink`(单行 15→13px,放不下按 host/path 两行 13→11px,
host 超宽才中间省略,字号下限 `LinkMinSize`)→ `ellipsizeMiddle`。限宽恒定 `QRCardW-40`。
## 二维码(`generateQR`)
- **图像只输出符号**:`qr.DisableBorder = true`,静默区由承载它的白色卡片提供。
把 4 模块静默区画进图里 + 再居中留白,会让 165px 目标只剩 111px(3px/模块)——这是曾经的回归。
- `perModule = size / modules` 向下取整,符号 = `modules*perModule`;**调用方必须用 `qr.Bounds().Dx()`
排版与居中**,不能假设等于传入的 size。
- 单题卡 `QQRSize = 190`(37 模块 → 185px = 5px,卡片 236×300 居中于 679,120);
箱子卡 `QRCodeSize = 190`(37 模块 → 185px = 5px,卡片 270×335)。
- 单题卡的二维码卡片绘制在 `drawQuestionQRCard`(handler 只是调用),测试可直接出图。
- 卡内垂直间距在 4px 栅格上:`24 / 17 / 20 / 1 / 20 / 16`,总高按链接行数算,整块垂直居中。
## 模块像素:唯一的硬指标
| 模块 | 960 | 720 | 480 | 320 | 240 |
|---|---|---|---|---|---|
| 4px(148px,37 模块) | o | o | **x** | x | x |
| 5px(185px,37 模块) | o | o | o | o | x |
(go-qrcode 纠错 Q、真实 55 字符链接、含内嵌头像;用 gozxing 解码,它比手机严格,可当保守下限。)
→ **目标 5px/模块**;`TestGenerateQRModuleSizeAtLeast5` 锁住三种常见链接长度,改小 QRCodeSize 立刻 FAIL。
改大模块的路线排序(成本从低到高):
1. 同画布放大二维码(已做,0 CPU)——`generateQR` 只 0.8ms,与尺寸几乎无关。
2. 降纠错 Q→M:模块 37→33 只省 11%,容错 25%→15%,不划算,别单独用。
3. 换 PNG:只保住原图,缩到 480 一样解不出,体积还是 JPEG 的 5 倍。
4. 2x 渲染:+60~100ms(本地基准:JPEG 编码 12→48ms、背景 Lanczos 25→41ms、画布 3→13ms)。
只在「原图本身也要更精细(背景/文字抗锯齿)」时值得。
## 缓存命中率才是 2x 之外更值得看的地方
进程内 LRU 只有 16 条 + TTL 1h,站点箱子多时冷渲染频繁;把生成结果落盘 / 上 R2
能让「贵」只付一次。这是比 2x 更实在的优化方向。
## 验证手法
- 守门测试 `route/question/share_layout_test.go`:测量=笔位、链接限宽、按着墨边界判居中 ±3px、
二维码 `width == modules×perModule` 且吃掉目标尺寸 ≥80%、头像不在边缘裁切。改完都做变异验证。
- `TEST_PREVIEW=1 go test ./route/question/ -run TestShareCardLinkPreview` 出 270×315 的卡区域 PNG。
- 图片实测:PIL 数着墨 bbox(居中偏差 / 二维码宽度 / 各段间距);解码用
`github.com/makiuchi-d/gozxing` 起个临时 module 跑(`_ "image/jpeg"` 和 `_ "image/png"` 都要 import)。
- 单题卡要真实出图:`.workbuddy/local/qcard_probe.py`(发提问 → 登录 → 回答 → 拉图 → 删除,不入库)。
## 缓存
进程内 TTL 1 小时 + 响应头 `Cache-Control: public, max-age=300`。改完要复验:重启(清进程内缓存)+ URL 加随机参数。