你生成二维码时,有没有想过它会不会碰巧长成别的东西?二维码标准生成算法里有8种可互换的掩码,ISO/IEC 18004选哪个的依据只有一条:哪个更好扫描。至于扫出来的黑白格子会不会拼成一个钩十字,标准里根本没这一条。

一个刚上线的Rust包qr-swastika-avoider,把这个盲区补上了。它不改标准算法,只在标准输出之后加一道图案审查:检测钩十字,再从8种掩码里挑一个既好扫又干净的版本。给"只优化机器可读性"的技术流程,补上一层原本没人负责的人类语境检查——这才是它真正的意义。

机制:8种掩码里,它多看一眼

ISO/IEC 18004的惩罚分公式会给每种掩码打分:连续同色模块太多要扣分,类似指纹的规律图案太多也要扣分。分数越低,理论上越容易被扫描仪稳定识别。但这套公式只管机器视角的"干净",从不判断图案在人眼里像什么。

检测器扫描已生成二维码的模块矩阵,识别轴对齐、5×5或7×7大小、两种手性、黑白两种极性的钩十字图案。定位符、时序线、对齐图案、格式信息这些功能模块会被主动排除,避免误判。

生成端的encode_safe把8种掩码全部渲染一遍,先剔除命中图案的版本,再从剩下的里挑惩罚分最低的一个返回。找不到干净版本就直接报错,不会悄悄放行一个次优版本糊弄过去。

这个包本身零依赖、禁止unsafe,通过一个两方法的trait适配qrcodegen、qrcode、fast_qr和原始布尔矩阵,接入成本很低。

encode_safe 生成流程 输入内容 URL/文本 渲染8种掩码 全部生成 过滤含图案版本 剔除钩十字 选惩罚最低版本 ISO 18004 输出/报错 无干净版本即报错 零依赖 · 禁止 unsafe · 适配 qrcodegen/qrcode/fast_qr

边界:管得住什么,管不住什么

它的检测范围很具体,也很有限:

维度覆盖不覆盖
朝向轴对齐斜向45°、任意角度旋转
尺寸5×5、7×7其他尺寸变体
手性/极性左右镜像两种手性、黑白两种极性

"provably clean"要准确理解:它保证的是不包含检测器定义的那几种钩十字形态,不是保证二维码绝对不会出现任何视觉上引起联想的图案,更不是给二维码标准本身打了补丁。作者也没给出这类偶然图案的发生概率——目前能确认的只是标准从不检查这一维度,不是这类图案频繁出现。

卐字符号在不同文化里的含义也不统一,古印度教、佛教、耆那教都把它当作吉祥符号使用。这个包针对的是那个在特定语境下高度敏感、容易引发误解甚至现实冒犯的视觉形态,不是给所有形似图案一概定性。

谁该接入,谁不必

对象该怎么做
Rust二维码工具/库开发者零依赖、trait适配主流库,接入成本低;可加进模板化批量生成流程做一道防呆,不必重写现有生成算法
批量发布二维码的品牌/平台团队自动化+批量+公开展示的组合场景值得评估接入——命中概率虽低,一旦命中传播和公关代价不对称;人工核验、低频场景加这层意义不大

接下来值得盯住的,是这套检测思路能不能往外扩展。如果社区跟进覆盖更多敏感图案而不只是钩十字,才说明它是个能长期用的工具,而不只是一次性小修补。

锐评

ISO/IEC 18004写惩罚分公式的人,想的是噪声和扫描率,没人让它管人心。这不是标准的失职——需求里从没写过这一条。多数技术流程出岔子,不是算法算错了,是从没人问过"这算不算个问题"。

qr-swastika-avoider体量小,边界写得清楚,该报错时报错,不吹"绝对干净"。这种克制,比功能本身更值得说一句好。

它的意义不在二维码本身。批量生成图标、验证码、随机头像的系统,大多也只盯着"技术指标合格",没人问过"这东西摆出来像什么"。机器觉得合格,不代表能摆到人眼前。