2.6 KiB
2.6 KiB
分享卡(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 = 150(37 模块 → 4px);箱子卡QRCodeSize = 165(37 模块 → 148px)。 - 卡内垂直间距在 4px 栅格上:
24 / 17 / 20 / 1 / 20 / 16,总高按链接行数算,整块垂直居中。
实测上限
960 宽下 4px/模块是当前结构的上限;缩到 480 宽(微信/QQ 常见展示尺寸)模块只剩 2px, gozxing 解不出。要真正抗二次压缩只能 2x 渲染(1920×1080),1C1G 要先测 CPU。
验证手法
- 守门测试
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 加随机参数。