平台

CJK 字体渲染:跨平台一致性方案

如何处理中文、日文和韩文字体环境,实现跨不同平台和区域的一致配置文件输出。

文档中心

想直接看维护中的产品文档?

这篇文章对应的主题已经有文档中心页面。需要规范流程、当前参数和长期参考时,优先看 docs。

简介

中文、日文和韩文(CJK)文本渲染是浏览器行为中最具平台特异性的方面之一。每个操作系统附带不同的 CJK 字体、不同的回退链和不同的渲染引擎,产生可见的不同输出。Windows 简体中文使用微软雅黑,日文使用 Meiryo,韩文使用 Malgun Gothic。macOS 中文使用苹方,日文使用 Hiragino Sans,韩文使用 Apple SD Gothic。Linux 发行版通常依赖 Noto Sans CJK 或文泉驿系列。

这些差异对浏览器指纹很重要,因为字体可用性和渲染输出是强烈的平台信号。BotBrowser 配置文件从源系统捕获完整的字体环境,确保 CJK 文本的一致渲染,不受主机操作系统和本地安装字体的影响。

隐私影响:为什么 CJK 字体很重要

字体指纹是识别浏览器平台和配置的最可靠方法之一。通过 CSS 或 Canvas 操作查询特定字体的可用性,追踪系统可以确定操作系统、语言配置,甚至特定的软件安装。

CJK 字体特别具有揭示性,因为:

  • 它们体积大且平台特定。Windows CJK 字体在 macOS 或 Linux 上不可用,反之亦然。
  • 支持 CJK 的系统比仅西文系统多出数十种额外字体,使字体列表成为强烈的区分信号。
  • 字体渲染输出(文本的实际像素级外观)在 DirectWrite(Windows)、Core Text(macOS)和 FreeType(Linux)之间存在可测量的差异,即使使用相同的字体家族名称。

对于面向 CJK 市场的隐私研究,准确的字体环境至关重要。声称是在中国运行的 Windows 系统但缺少微软雅黑或宋体的浏览器呈现出明显的不一致。同样,面向日本的 macOS 配置文件没有 Hiragino Sans 会在字体一致性检查中失败。

对于服务 CJK 地区的多账户管理,每个身份需要与其假定的平台和区域组合匹配的字体环境。

技术背景

各平台的 CJK 字体家族

Windows CJK 字体包括:

  • 简体中文:微软雅黑、宋体、黑体、仿宋、楷体、新宋体
  • 繁体中文:微软正黑体、细明体、新细明体
  • 日文:Yu Gothic、Meiryo、MS Gothic、MS Mincho、MS PGothic
  • 韩文:Malgun Gothic、Batang、Dotum、Gulim、Gungsuh

macOS CJK 字体包括:

  • 简体中文:苹方-简、华文黑体、华文宋体、华文楷体、华文仿宋
  • 繁体中文:苹方-繁、苹方-港、丽宋 Pro
  • 日文:Hiragino Sans、Hiragino Kaku Gothic、Hiragino Mincho
  • 韩文:Apple SD Gothic Neo、AppleMyungjo、Nanum Gothic

Linux CJK 字体常见包括:

  • 全语言:Noto Sans CJK(SC、TC、JP、KR 变体)、Noto Serif CJK
  • 中文:文泉驿微米黑、文泉驿正黑
  • 日文:IPAGothic、IPAMincho、Takao Gothic
  • 韩文:UnBatang、UnDotum

字体回退链

当网页指定的字体不可用时,浏览器遵循回退链。此链是平台特定的:

  • 在 Windows 上,中文文本的回退通常是微软雅黑,然后是宋体
  • 在 macOS 上,回退到苹方-简,然后是华文黑体
  • 在 Linux 上,回退到 Noto Sans CJK 或文泉驿系列

特定的回退行为是指纹信号。如果页面指定 font-family: "NonExistent Font", sans-serif,渲染的文本在每个平台上看起来不同,因为最终的回退字体不同。

字体度量和渲染

除了字体可用性外,CJK 字体产生不同的度量:

  • 字形宽度:字体家族之间各个字符的宽度不同
  • 行高:不同 CJK 字体的默认行距不同
  • 字距和间距:日文、中文和韩文排版约定之间的字符间距规则不同
  • 提示:小尺寸字符的像素级渲染取决于字体的提示表和渲染引擎

这些度量影响布局计算,进而影响追踪系统使用的 getBoundingClientRect()getComputedStyle() 和其他测量 API。

常见方法及其局限性

在主机上安装 CJK 字体

最直接的方法是在主机系统上安装目标平台的 CJK 字体。对于 Linux 服务器,这意味着安装微软核心字体、CJK 字体包或手动添加字体文件。但是:

  • 这改变了操作系统级别的字体可用性,影响所有浏览器实例,而不仅仅是特定配置文件
  • 同时安装 Windows 和 macOS CJK 字体创建了不匹配任何真实平台的字体列表
  • 字体渲染仍然使用主机系统的渲染引擎(Linux 上的 FreeType),而不是源平台的引擎
  • 跨多个服务器管理字体安装增加了运维复杂性

Web 字体注入

一些方法通过 CSS 注入 web 字体来覆盖系统字体。这可以控制文本的视觉外观,但不影响字体枚举查询。基于 JavaScript 的字体检测通过测量指定字体时的文本宽度变化来判断字体是否可用。注入的 web 字体出现在渲染中,但可能无法在这些枚举检查中正确注册。

字体伪装扩展

拦截字体相关 API 调用的浏览器扩展可以修改报告的字体列表。但是,它们无法改变实际的渲染输出。如果报告的字体列表说微软雅黑可用,但实际渲染使用 FreeType 和不同的字体,这种不一致可以通过 Canvas 文本渲染来测量。

BotBrowser 的方案

BotBrowser 配置文件从源系统捕获完整的字体环境。这包括字体可用性、渲染特征和度量。当在任何主机操作系统上加载 CJK 配置文件时,字体相关信号与捕获的环境完全匹配。

基于配置文件的字体环境

每个配置文件携带:

  • 源系统的完整可用字体列表
  • 与源渲染引擎匹配的字体度量
  • 源平台的 Canvas 文本渲染特征
  • 与源操作系统匹配的 CSS 字体回退行为

这意味着具有中文区域设置的 Windows 配置文件正确报告微软雅黑、宋体和其他 Windows CJK 字体,无论主机上安装了什么字体。

字体配置选项

BotBrowser 提供 --bot-config-fonts 标志来控制字体行为:

# Use the profile's embedded font environment (default)
--bot-config-fonts=profile

# Use profile fonts with additional fallback fonts
--bot-config-fonts=expand

# Use the host system's real fonts
--bot-config-fonts=real

默认的 profile 模式确保与捕获环境的完全一致性。expand 模式添加回退字体来处理配置文件字体集不包含特定字符的边缘情况。real 模式使用主机系统的实际字体,这在测试或需要主机字体行为时很有用。

渲染一致性

BotBrowser 不仅控制字体可用性,还控制文本的渲染方式。Canvas 文本渲染和文本测量操作产生与配置文件源平台一致的输出。这涵盖了字体指纹所依赖的视觉外观和编程度量(边界框、文本宽度)。

配置与使用

中文区域设置(简体)

chrome --bot-profile="/profiles/win11-zh-cn.enc" \
       --bot-config-locale=zh-CN \
       --bot-config-languages=zh-CN,zh,en \
       --bot-config-timezone=Asia/Shanghai

日文区域设置

chrome --bot-profile="/profiles/win11-ja.enc" \
       --bot-config-locale=ja-JP \
       --bot-config-languages=ja,en \
       --bot-config-timezone=Asia/Tokyo

韩文区域设置

chrome --bot-profile="/profiles/win11-ko.enc" \
       --bot-config-locale=ko-KR \
       --bot-config-languages=ko,en \
       --bot-config-timezone=Asia/Seoul

使用 CJK 配置文件的 Playwright 示例

const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      '--bot-profile=/profiles/win11-zh-cn.enc',
      '--bot-config-locale=zh-CN',
      '--bot-config-languages=zh-CN,zh,en',
      '--bot-config-timezone=Asia/Shanghai',
    ],
    headless: true,
  });

  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://www.baidu.com');

  // Verify CJK font rendering
  const fontCheck = await page.evaluate(() => {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    ctx.font = '16px "Microsoft YaHei"';
    const width = ctx.measureText('\u4F60\u597D').width; // "Hello" in Chinese
    return { width, font: ctx.font };
  });
  console.log('CJK font check:', fontCheck);
  await browser.close();
})();

完整的 CJK 身份

chrome --bot-profile="/profiles/win11-zh-cn.enc" \
       --bot-config-locale=zh-CN \
       --bot-config-languages=zh-CN,zh,en \
       --bot-config-timezone=Asia/Shanghai \
       --proxy-server=socks5://user:pass@cn-proxy:1080 \
       --bot-inject-random-history \
       --bot-bookmarks='[{"title":"\u767E\u5EA6","type":"url","url":"https://www.baidu.com"},{"title":"\u6DD8\u5B9D","type":"url","url":"https://www.taobao.com"}]'

验证

通过检查字体可用性和渲染来验证 CJK 字体处理:

const page = await context.newPage();
await page.goto('about:blank');

const cjkVerification = await page.evaluate(() => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');

  // Test font availability through rendering width comparison
  const testFonts = [
    'Microsoft YaHei', 'SimSun', 'SimHei',      // Windows CN
    'PingFang SC', 'STHeiti',                     // macOS CN
    'Meiryo', 'Yu Gothic', 'MS Gothic',          // Windows JP
    'Hiragino Sans',                              // macOS JP
    'Malgun Gothic',                              // Windows KR
  ];

  const results = {};
  const fallbackWidth = (() => {
    ctx.font = '16px monospace';
    return ctx.measureText('\u4F60\u597D').width;
  })();

  for (const font of testFonts) {
    ctx.font = `16px "${font}", monospace`;
    const width = ctx.measureText('\u4F60\u597D').width;
    results[font] = width !== fallbackWidth;
  }

  return results;
});

console.log('CJK font availability:', cjkVerification);
CJK Font Families by Platform Windows CN: Microsoft YaHei CN: SimSun, SimHei JP: Yu Gothic, Meiryo JP: MS Gothic KR: Malgun Gothic KR: Batang, Gulim macOS CN: PingFang SC CN: STHeiti, STSong JP: Hiragino Sans JP: Hiragino Mincho KR: Apple SD Gothic KR: AppleMyungjo Linux All: Noto Sans CJK All: Noto Serif CJK CN: WenQuanYi JP: IPAGothic KR: UnBatang KR: UnDotum

最佳实践

  • 使用从匹配区域安装中捕获的配置文件。 从中文区域安装中捕获的 Windows 配置文件将包含该特定 Windows 配置附带的所有 CJK 字体。
  • 始终配对区域设置、语言和时区标志。 CJK 配置文件应包含匹配的 --bot-config-locale--bot-config-languages--bot-config-timezone 以获得完整的身份。
  • 使用区域代理。 中文配置文件配合中国代理,日文配置文件配合日本代理,韩文配置文件配合韩国代理。
  • 优先使用 --bot-config-fonts=profile(默认值)。 这确保配置文件捕获的字体环境被完全按照捕获的状态使用。
  • 使用 CJK 内容丰富的页面进行测试。 在具有大量 CJK 内容的页面上验证字体渲染以确认字体环境工作正常。

常见问题

我需要在 Linux 服务器上安装 CJK 字体吗?

不需要。BotBrowser 配置文件携带来自源系统的字体环境。配置文件决定哪些字体被报告为可用,无论主机上安装了什么。

可以在单个配置文件中混合 CJK 和西文字体吗?

可以。如果源系统上存在 CJK 和西文字体,所有配置文件都包含两者。具有中文区域设置的 Windows 配置文件包含中文字体和标准西文字体如 Arial、Segoe UI 等。

--bot-config-fonts=expand 选项如何工作?

expand 模式使用配置文件的字体集作为主要来源,但添加来自主机系统的回退字体。当遇到配置文件字体集未覆盖的字符时很有用,例如源系统中不存在的稀有 Unicode 符号或文字。

Canvas 中的字体渲染是否匹配配置文件平台?

是的。BotBrowser 在引擎级别控制 Canvas 文本渲染。使用 CJK 字体在 Canvas 上绘制的文本产生与配置文件源平台一致的输出,包括抗锯齿模式和字形形状。

可以创建同时包含日文和中文字体的配置文件吗?

配置文件反映源系统的配置。安装了日文和中文语言包的 Windows 系统将在其配置文件中包含两套字体。对此用例使用从多语言安装中捕获的配置文件。

如何验证使用了正确的 CJK 字体?

使用 Canvas 文本测量来比较指定 CJK 字体与回退字体之间的渲染宽度。如果指定字体产生的宽度与回退不同,则该字体可用。参见上面验证部分的代码示例。

总结

BotBrowser 中的 CJK 字体渲染通过从真实系统捕获的基于配置文件的字体环境来处理。每个配置文件携带其源平台的完整字体可用性和渲染特征,确保 CJK 文本输出的一致性,不受主机操作系统及其本地安装字体的影响。

有关相关主题,请参阅 字体指纹 了解更广泛的字体指纹概述,跨平台配置文件 了解跨操作系统运行配置文件,以及 时区、区域设置和语言 了解将区域设置与 CJK 配置文件匹配。

#Cjk#Fonts#Chinese#Japanese#Rendering

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。