当使用非等宽字体制作等宽字体时,如何使替换字符的宽度正确显示?

6

网络浏览器会用另一个字体的字符代替缺失的字体(通常是箭头、图形符号和特定使用情况下的Unicode字符“≫”(U+226B))。如果您使用的是等宽字体,则替换字符(大多数情况下)来自比例字体,这将破坏在许多情况下使用等宽字体时所期望的严格网格模式。

例如,在我的浏览器中,这个问题被搞砸了:

<pre>
012345
 1 |
 a |
 Ö |
 % |
 ≫ |
 2 |
 b |
 X |
 & |
</pre>

“≫”后面的管道符号与其他符号不对齐(在我的计算机上安装的字体中是这样的)。

是否有一种方法可以确保浏览器将替换字符缩放到正确的宽度?

如果必要,JavaScript解决方案也可以。


如果您提供标记以重现问题(您正在使用的字体和那些特定字符),那将是很好的。 - Salman A
只是字符“≫”造成了所有的问题吗?如果是这样,我注意到在许多字体中,如果您在HTML中写入&raquo;,它将自动解决问题。或者,如果您无法更改HTML代码,您可以使用一个小脚本来替换该字符,即document.body.innerHTML = document.body.innerHTML.replace('≫', '&raquo;'); - Niffler
1
@Salman:我提供了一个例子,请看我的编辑。 - Roland Seuhs
@Niffler:»是不同的字符:»与≫。 - Roland Seuhs
3个回答

5
这只是一个部分答案,但或许对某些人有所帮助。在你的CSS字体堆栈中,可以包括一个等宽字体,该字体具有良好的Unicode覆盖范围。例如,在OS X上,Courier New包含1200多个字形,其中我猜测包括大部分有用的Unicode字符。如果在主字体后列出它,并在字体堆栈中使用它,那么至少对于那些不在主字体中的字符,你将拥有一致的宽度。
当然,如果主字体的宽度与备用字体的宽度不同,这并不能真正解决问题。如果你很幸运,你可能能找到一个备用字体,它具有足够的Unicode覆盖范围并具有相同的宽度,那么你就可以直接使用它。否则,我想你就没有简单的解决方案了。在这种情况下,我能想到几个替代方案:
  1. 如果许可证允许,你可以修改备用字体的宽度,使其与主字体匹配。
  2. 如果你事先知道要显示哪些Unicode字符,你可以将它们包装在 <span> 标签中,并调整样式以匹配主字体的宽度。
无论哪种情况,这都不是一个简单的解决方案。
附注:你可能想编辑一下标题。我认为你想说“等宽字体(mono-spaced)”,而不是“单排字体(monotype)”,因为后者是一家字体铸造公司的名字。

@Stephen Thomas:包含大部分有用的Unicode字符...除非你是中国人:p - frasnian

3

你愿意变得多么专业?
这是一个代码,它将给定宽度的span包裹文本中的每个字母:

var container = document.getElementsByClassName('mymonotext');
if (!container.length) {
    throw new Error('.mymonotext is not found');
}

container = container[0];
container.normalize(); //Merge all the adjacent text nodes inside a container (see https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize)

var currentNode = container.firstChild;
for (var i = 0, len = currentNode.textContent.length; i < len - 1; i++) {  
    var range = new Range(); //Create a range

    //Insert a first char into a range
    range.setStart(currentNode, 0); 
    range.setEnd(currentNode, 1);

    var wrapper = document.createElement('span');
    wrapper.className = 'letter';

    //Wrap the range with a span tag
    wrapper.appendChild(range.extractContents())
    range.insertNode(wrapper);

    //Detach the previous range and shift current node
    range.detach();
    currentNode = currentNode.nextSibling.nextSibling;
}

包装标签的样式:

.letter {
    width: .5rem;
    display: inline-block;
    overflow: hidden;
}

在jsfiddle上同样的事情: http://jsfiddle.net/enwyoy4y/
(使用有风险,不支持IE8等)。


它截断字母而不是拉伸它们,有可能修复吗?此外,我想仅将其应用于符号列表(而不是每个字母)-有可能确保“宽度”等于普通等宽字符的宽度吗? - Roland Seuhs
你可以尝试将跨度宽度设置为1em,这应该接近字母宽度。根据你的实际情况进行调整。 - Arseny Smoogly
我不知道任何好的方法来拉伸字符。如果您知道字体支持的所有字符的完整列表,那么可以从换行中排除已知符号。 - Arseny Smoogly
减小字体大小直到字符适合为止。字母宽度应该通过 JavaScript 计算,并且您必须记住字母宽度可能是有小数的(例如,8.4 像素而不是 8 像素)。 - Salman A

1
在等宽字体中,字符的进阶宽度是针对特定字体固定的,但不同的等宽字体可能会有所不同。如果你限制自己只使用某个等宽字体(在网页上,这仅在使用 @font-face 时才有意义,因为没有一种等宽字体可以在所有计算机上使用),你可以找出它的进阶宽度(例如使用字体检查程序或进行一些测试),然后将元素缩放到该宽度。
但是,除非使用一些 JavaScript 编程,否则无法使字体大小依赖于元素的宽度,因此如果你只需要一个字符,最简单的方法是使用图像。用某个字体以大号写出字符,截取屏幕截图,然后使用 img 元素缩放图像,只设置其宽度。或者你可以创建恰当大小的图像,但如果稍后更改字体(因此宽度也可能会改变),这将不够灵活。

JavaScript可以,这就是为什么我在问题的标签列表中包含了JavaScript。 - Roland Seuhs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接