检测丢失的字符字形,以及 Firefox 的带有十六进制代码点的小盒子

Posted

技术标签:

【中文标题】检测丢失的字符字形,以及 Firefox 的带有十六进制代码点的小盒子【英文标题】:Detecting missing character glyphs, and Firefox's little boxes with hex codepoints inside them 【发布时间】:2020-06-21 17:54:26 【问题描述】:

我希望能够测试特定字符,例如 ????(这是一个雨滴表情符号,以防你看不到它),是否会在用户的网络浏览器中正确呈现,这样如果该字符不渲染我可以使用替代品。

下面的代码在 Chrome 中可以解决问题:

export function doesCharacterGlyphExist(elementOrFont: Element | string, charOrCodePoint: string | number): boolean 
  if (typeof charOrCodePoint === 'number')
    charOrCodePoint = String.fromCodePoint(charOrCodePoint);

  const metrics = getFontMetrics(elementOrFont);
  const PADDING = 8;
  const size = metrics.lineHeight + PADDING;

  const canvas0 = (doesCharacterGlyphExist as any).canvas0 || ((doesCharacterGlyphExist as any).canvas0 =
                  document.createElement('canvas') as htmlCanvasElement);
  const canvas1 = (doesCharacterGlyphExist as any).canvas1 || ((doesCharacterGlyphExist as any).canvas1 =
                  document.createElement('canvas') as HTMLCanvasElement);
  const canvases = [canvas0, canvas1];
  const pixmaps = [];

  for (let i = 0; i < 2; ++i) 
    const canvas = canvases[i];

    canvas.width = size;
    canvas.height = size;
    canvas.style.opacity = '1';

    const context = canvas.getContext('2d');

    context.fillStyle = 'white';
    context.fillRect(-1, -1, size + 2, size + 2);
    context.fillStyle = 'black';
    context.font = metrics.font;
    // Compare pixels for test character to pixels for known character without a glyph
    context.fillText(i === 0 ? charOrCodePoint : '\uFFFE', 0, metrics.ascent);

    pixmaps[i] = context.getImageData(0, 0, size, size).data;
  

  for (let i = 0; i < pixmaps[0].length; ++i) 
    if (pixmaps[0][i] !== pixmaps[1][i])
      return true;
  

  return false;

基本思想是在画布上绘制一个字符,看看它是否呈现与已知不存在的字形(通常呈现为空框)相同或不同的像素。

然而,Firefox 非常有用地将每个未知字符呈现为一个可单独区分的小盒子,其中包含一个十六进制 Unicode 代码点值。

虽然通常这很有帮助,但在这种特殊情况下,这是一个大问题。

有没有人知道如何关闭火狐的这个功能?我已经搜索了 Firefox 特定的 CSS 功能,但没有找到。

或者,如果不是这样,任何人都可以提出一种与我的画布比较技巧完全不同的方法来确定哪些字符代码点具有真正的字形,哪些没有?

【问题讨论】:

【参考方案1】:

虽然我仍然愿意寻求更好的答案,但至少我现在有一个解决方法。

事实证明,为丢失的字形显示的小框(无论是简单的空框还是内部带有十六进制代码点的 Firefox 变体)不受 CSS 斜体字的影响。然而,真实的渲染字符由斜体改变。因此,如果正常字符与其斜体变体相同,则它不是真正的字符。

不过,我更喜欢我在 Chrome 上使用的测试,所以我只在 Firefox 上使用这个替代测试。

function changeItalic(font: string): string 
  if (/\b(italic|oblique)\b/.test(font))
    return font.replace(/\b(italic|oblique)\b/, '');
  else
    return 'italic ' + font;


export function doesCharacterGlyphExist(elementOrFont: Element | string, charOrCodePoint: string | number): boolean 
  if (typeof charOrCodePoint === 'number')
    charOrCodePoint = String.fromCodePoint(charOrCodePoint);

  const metrics = getFontMetrics(elementOrFont);
  const PADDING = 8;
  const size = metrics.lineHeight + PADDING;

  const canvas0 = (doesCharacterGlyphExist as any).canvas0 || ((doesCharacterGlyphExist as any).canvas0 =
                  document.createElement('canvas') as HTMLCanvasElement);
  const canvas1 = (doesCharacterGlyphExist as any).canvas1 || ((doesCharacterGlyphExist as any).canvas1 =
                  document.createElement('canvas') as HTMLCanvasElement);
  const canvases = [canvas0, canvas1];
  const pixmaps = [];

  for (let i = 0; i < 2; ++i) 
    const canvas = canvases[i];

    canvas.width = size;
    canvas.height = size;
    canvas.style.opacity = '1';

    const context = canvas.getContext('2d');

    context.fillStyle = 'white';
    context.fillRect(-1, -1, size + 2, size + 2);
    context.fillStyle = 'black';
    // Compare pixels for test character to pixels for known character without a glyph.
    // For Firefox, which renders missing glyphs all differently, check if a character
    // looks the same as itself when rendered in italics -- the missing glyph boxes
    // remain straight when italicized.
    context.font = (i === 1 && isFirefox() ? changeItalic(metrics.font) : metrics.font);
    context.fillText(i === 0 || isFirefox() ? charOrCodePoint : '\uFFFE', 0, metrics.ascent);

    pixmaps[i] = context.getImageData(0, 0, size, size).data;
  

  for (let i = 0; i < pixmaps[0].length; ++i) 
    if (pixmaps[0][i] !== pixmaps[1][i])
      return true;
  

  return false;

【讨论】:

以上是关于检测丢失的字符字形,以及 Firefox 的带有十六进制代码点的小盒子的主要内容,如果未能解决你的问题,请参考以下文章

Firefox 中 bootstrap 3.3.5 中的字形图标存在一些问题

matplotlib:我可以使用辅助字体来丢失字形吗?

如何检测丢失的字体字符

为啥没有包含所有 Unicode 字形的字体? [关闭]

libcaca - 将 ascii 字形更改为片假名

带有字形图标和不同字体文本的 ASP.NET Actionlink