Hotdry.
application-security

PNG 解码器后备解析 EXIF 方向标签,支持 image-orientation: from-image 无 JS 修复旋转

浏览器 PNG 解码器 fallback 解析 EXIF orientation,支持 CSS image-orientation: from-image,修复旋转 bug,无需客户端 JS 干预。

在 Web 开发中,用户上传的 PNG 图像经常出现旋转方向错误的问题,尤其是手机拍摄的竖屏照片在浏览器中显示为横屏。这并非图像本身损坏,而是因为 PNG 文件虽可嵌入 EXIF 元数据(包括 orientation 标签),但浏览器 PNG 解码器默认忽略它,导致 CSS image-orientation: from-image 无法生效。Mozilla Bug 1759087 提出解决方案:在 PNG 解码器中添加后备解析 EXIF orientation 标签,实现自动旋转修复,无需依赖客户端 JavaScript。

PNG 中的 EXIF:非标准但常见

PNG 格式(ISO/IEC 15948:2004)标准未定义 EXIF 支持,但实践中,许多工具如 Photoshop、GIMP 或手机相机会将 EXIF chunk(以 'eXIf' 开头)嵌入 PNG 文件中。其中,orientation 标签(TIFF tag 0x0112)记录拍摄方向,值为 1-8,表示不同旋转 / 翻转组合:

  • 1: 正常
  • 2: 水平翻转
  • 3: 180° 旋转
  • 4: 垂直翻转
  • 5: 顺时针 90° + 水平翻转
  • 6: 顺时针 90°
  • 7: 顺时针 90° + 垂直翻转
  • 8: 逆时针 90°

浏览器如 Chrome 和 Safari 对 JPEG EXIF 支持良好,但 PNG 解码器通常跳过非标准 chunk,导致 img { image-orientation: from-image; } 失效。结果是图像像素数据未旋转,用户看到颠倒图片。

Hacker News 近期热议此问题:“EXIF orientation info in PNGs isn't used for image-orientation”,链接 Mozilla Bug 1759087。该 bug 报告指出,Firefox PNG 解码器未解析 EXIF,导致旋转 bug,尤其在 from-image 模式下。

CSS image-orientation 的潜力与局限

CSS 属性 image-orientation(MDN 文档)允许指定替换图像方向:

img {
  image-orientation: from-image; /* 根据 EXIF 自动旋转 */
}
  • from-image: 读取 EXIF orientation 并应用。
  • 支持浏览器:Firefox 26+、Chrome 81+、Safari 13.1+ 逐步完善,但 PNG EXIF 解析缺失。

当前 workaround 多依赖 JS:使用 exif-js 库读取 orientation,然后 Canvas 旋转重绘。这引入性能开销(大图解析慢)、隐私风险(EXIF 含 GPS),且不适用于 <img> 标签静态显示。

工程化实现:PNG 解码器 fallback 解析

提案:在浏览器 PNG 解码器(如 Skia 或 libpng 基础上)添加轻量 EXIF 解析,仅针对 orientation tag,无需完整 EXIF 库。

1. 解析流程参数

  • Chunk 检测:扫描 PNG chunks,定位 eXIf (0x65 58 49 66),偏移后验证 TIFF header (0x4949 或 0x4D4D)。
  • Tag 定位:IFD0 中寻 tag 0x0112 (Orientation),短整数值。
  • 安全阈值
    参数 说明
    Max chunk size 64KB 防 OOM
    EXIF parse depth 1 (仅 IFD0) 避嵌套
    Valid orientation 1-8 异常设为 1
  • 性能:异步解析或懒加载,仅 from-image 时触发。基准:1MB PNG 解析 <5ms。

2. 旋转变换矩阵

根据 orientation 应用仿射变换(Canvas 风格):

// 示例变换(浏览器内部实现)
const transforms = {
  2: [-1, 0, 0, 1, width, 0],    // Flip X
  3: [-1, 0, 0, -1, width, height],
  4: [1, 0, 0, -1, 0, height],   // Flip Y
  5: [0, 1, 1, 0, 0, 0],
  6: [0, 1, -1, 0, height, 0],
  7: [0, -1, -1, 0, height, width],
  8: [0, -1, 1, 0, 0, width]
};
ctx.transform(...transforms[orientation]);
ctx.drawImage(rawImage, 0, 0);

浏览器解码时预旋转像素数据,输出规范方向(orientation=1),尺寸调整(90° 旋转交换宽高)。

3. 回滚与监控清单

  • Fallback:解析失败 → 默认 orientation=1。
  • 禁用开关:CSS image-orientation: none 覆盖。
  • 监控点
    指标 阈值 告警
    Parse success rate >99% <95% 调查 chunk 异常
    Decode latency <10ms 性能回归
    Memory peak <2x raw GC 调优
  • 安全:沙箱解析,忽略未知 tag,限长字符串。

4. 跨浏览器落地

  • Firefox:集成到 Gecko PNGDecoder。
  • Chromium:Skia PNG 模块扩展。
  • 测试集:1000+ 手机 PNG(iOS/Android),覆盖 orientation 1-8。
  • 兼容:不影响无 EXIF PNG,渐进增强。

此方案零 JS 开销,纯 CSS 驱动,提升 UX。相比服务器预处理(ImageMagick -auto-orient),浏览器端更实时。

风险与限制

  • 非标准:PNG EXIF 变体多(如 iTXt 伪装),解析需鲁棒。
  • 隐私:仅读 orientation,不暴露 GPS 等。
  • 性能:移动端大图,限解析大小。

实施后,用户上传 PNG 即正确显示,简化 Web 图像管道。

资料来源

查看归档