中文网页排版优化之字距调整和首尾字符控制

已于2015年9月2日14:00重新修订了源代码

难以标准的中文排版

在编写 WordPress 主题的时候,我重新认识到网络标准对中文排版的轻视,几乎每个细节都会出现各种问题。这对于略有强逼症的我来说,简直不能忍啊。

起初我只是想实现首行缩进,但发现只有部分段落缩进了,查询得知是 br 标签不支持缩进。好吧,这个问题很好解决,~~把 br 标签全局替换成 p 标签可以解决掉。~~用 li 标签来实现零行距的缩进。但接踵而来的是标点换行,标点会在换行后出现在行首,这明显不符合中文的排版标准,在 CSS3 里找到个似乎相关的属性,结果只有 IE 才支持。最后是行对齐,理想的排版应该是两端对齐,但 CSS 里只支持英文两端对齐。

来回研究,在排版方面,居然是最为人唾弃的 IE 做得最好,标点能正确换行,也可动态调整中文的字距来实现两端对齐,只能说,不愧是做了 Office 的微软啊……

网上搜索了一通,没发现有什么好的解决办法,还是大神都藏了一手?但看了好些网站的排版,其中不乏知名文学类的,确实也没解决掉我发现的问题。

直到前几天在浏览 HTML 5 标准的时候,无意中看到了个标签 wbr,忽然灵机一动——或许这就是解决问题的一个关键。没想到接着脑洞大开,把问题一个接一个地解决了。我的方法可能不是独创,但确实有其价值,值得单独开一篇文章,以下是我的独家分享。

字体大小和行距

正文的字体大小,一般从 12~16px 中选择。我选择在文章正文中使用 16px,摘要则是 14px,并设置了 1.5 倍行距。

这只是全凭我个人喜好的选择,具体要选择怎样的字体大小,还要看内容和网站定位而定。

段首缩进和段间距

曾经看到一个说法,段首缩进在网页中并不是必要的。段首缩进和段间距都是用来区分段落,在空一行和缩进两字宽之间对比,似乎空一行的视觉效果更好。

翻开我手上不少的图书,有很多是不设置段间距,只靠段首缩进来区分段落。而同样的版面,在网页上居然会感觉有点密密麻麻,没那么舒服。

排除媒体方面的作用,这可能是因为我们看网页是快速阅读,而阅读书本是一种慢阅读,心态决定一切,我们更愿意在网页上直观地看到清晰明确的逻辑关系。

当然,段首缩进和段间距并不是二选一的关系,而是可以同时采用,这样既有清晰的段落逻辑,也兼顾出版的中文标准。

实现段首缩进的方式有几种,一是打两个全格空格,这个方法的好处是对 br 标签也生效,缺点是有些编辑器会吞掉这些空格,比如 WordPress。二是在 CSS 里实现,好处是兼容性高,缺点是可能把一下不想缩进的标题或图片也缩进了,也不对 br 标签起作用。第三种是手动在需要缩进的段落插入设置了缩进的 div 或 p 标签,好处是排版准确度高,缺点就是需要手动。本人采用的是第三种方法。

~~前面多次提及,br 标签,即硬回车换行是不会对缩进起作用的,所以我通过代码把它替换成 p 标签,又因为 br 本身是没有段间距的,所以我又给它添加了一个 间距为 0 的 CSS 类。~~改变了想法,br 还是有保留它的必要,不能这样把它替换掉,改用 li 标签来实现无行距的换行,效果会更好。问题是,br 可以由编辑器自己生成,li 就需要你去手动插入了。

在 css 里添加样式


/* 段首缩进两个字 */
.indent{text-indent:2em}

WordPress 也可以把以下代码添加到主题的函数模板 functions.php 里来添加按钮。

// 替换 br 为 无段间距的 p
function Replace_Html($html) {
  $html=str_replace('<br />','</p><p class="br">',$html);
  return $html;
}
add_filter('the_content','Replace_Html');

//在文本编辑器中添加按钮
add_action('admin_print_footer_scripts','wpjam_add_quicktags');
function wpjam_add_quicktags() {
  if(wp_script_is('quicktags')) {
?>
<script type="text/javascript">
QTags.addButton('indent2em','缩进','\n\n','\n\n');
</script>
<?php
}}

旧方法,已弃用展开

WordPress 请在 jQuery 和函数模板两种方式中任选一个,两者有本质区别,函数模板里的代码是在服务器段上运行,会将修改直接输出到 HTML 源代码中;而 jQuery 是在本地浏览器中执行,jQuery 修改的内容不会体现在 HTML 源代码中,只能在浏览器的开发者工具中查看。如果是增量修改的话,前者会增加 HTML 的体积,增加传输成本,而 jQuery 则不会对传输有任何影响。后面优化排版的代码可能会使 HTML 的体积成倍增加,因此全部改由 jQuery 实现。

中文标点

首先,在 Windows 里,相信不少网站都是使用了微软雅黑,但雅黑的部分全角标点很奇怪,一点也不适合中文排版,所以我选择调用另一个字体来做标点。为节省资源,我们需要使用一些工具对字体进行压缩,使它只保留我们需要使用的标点,网上有很多网站能做到这种效果,这里推荐一些。

阿里妈妈webfont平台 和图标字体一样,推荐它的原因是完全免费以及提供了字体托管,操作简单,甚至连注册也不需要,但阿里这个站的缺点也很明显,就是只有思源黑体这个字体,没有其他选择。

有字库 功能非常强大,有多种字体可供选择,如果你要的字体没有授权,甚至可以自己上传,也有托管服务。缺点是这个网站的操作比较繁复,需要注册才能使用,只提供调用字体的代码,而不能下载压缩好的字体本身。总之因为太复杂,我没做过多的尝试。

字蛛 这个不是网站服务,更是一个工具,可以在本地压缩字体,优点是无限制。确定是无托管服务,虽然把字体上传到自己的空间或[七牛云存储](https://portal.qiniu.com/signup?code=3ln23sakgyv6a)等地方,使用起来也比较复杂。

百度字体编辑器 这是一个在线工具,可以上传字体进行编辑和转换格式,它的编辑功能相当强大,但它本身又不能简单地压缩字体,所以我是在需要修改已压缩的字体的时候使用它。

除了 Internet Explorer 之外,其他浏览器都不能正确地换行中文标点,常常出现把标点换行到行首的情况。我在随意翻阅 HTML 标准的时候,看到 wbr 这个标签。

如果单词太长,或者您担心浏览器会在错误的位置换行,那么您可以使用 wbr 元素来添加 Word Break Opportunity(单词换行时机)。

当我看到它的时候,第一时间想到,如果把 wbr 添加到中文标点后面,不就可以正确地换行吗?经过测试,它的确能达到我的要求,只是为什么我没看到有什么网站去使用它呢?从兼容性来说,只有 IE 浏览器不支持 wbr,但 IE 本身就能正确对标点换行,因此也就没关系了。

两端对齐和字距调整

网页默认的排版是靠左对齐,所以在中文网页里,经常会看到右边会有空缺。特别在中英混排中,如果设置了不在单词内换行,有时这空缺会更明显,影响观感。

虽然可以在 CSS 里改成两端对齐,然而并没有什么卵用。如果是纯中文,实际效果和靠左对齐是一样的,只有在中英混排时体现出不同,特别是段落中含有空格的时候,这些空格有时就会被拉伸得非常宽广,看起来奇怪极了。这虽然是实现了两端对齐,但无疑又带来了更奇怪的排版。

那么正确是怎样排版的呢,可以去看看 IE 或者 Word 的做法,它们是通过压缩或拉伸每个字符(包括中文、英文、标点等)的字距来实现两端对齐的,和只能拉伸空格的 Chrome、Firefox、Safari、Opera 有本质上的差别。

慢着,既然它们是只对空格进行处理,那么实际上并不是不支持中文,而是只对空格起作用呢?这时我脑海里又浮现出某些港台网站在每个字中间加空格的做法,这实际上除了不让繁体字看起来密密麻麻之外,也是为了实现两端对齐?

经过验证,确实就是这样的道理,在文字中间加空格就能解决问题了,但在简体中文中加空格并不好看,所以我又找来 kbd 标签来嵌套这些空格,并添加 CSS 样式来调整宽度和字距。经过一番修改,终于离我理想中的效果越来越近了。

最终代码

本文的 JavaScript 代码基于 jQuery 库,我不太懂 JavaScript,因此无法提供无需 jQuery 的代码。本文的全部代码已经在所有主流浏览器中测试通过,如有有异常,应该是你的浏览器版本太旧了。

CSS 样式展开

jQuery 代码展开

如果你不想替换标点的字体,可以把代码改成这样展开

该方法的不足之处是,在复制网页上的文字时会把空格一起复制,这必然会造成一定的困惑。我尝试通过 JavaScript 实现复制文字的时候,从剪贴板中移除文字间的空格,但我在网上抄了不少代码,都不起作用,原来是只有 IE 支持使用 clipboardData 访问系统剪贴板。希望谁能给我提供一个可行的跨浏览器代码。总算编写好可以实现的代码……

复制文本时去掉多余空格展开

此外,IE 浏览器应该(注意是“应该”,因为我未测试早期的 IE 版本,不敢打包票)是不需要在汉字中间插入空格也能两端对齐,所以有不少代码可以加入条件判断,如果是 IE 就不执行。所以部分代码可以改成以下的样子。

加入 IE 条件判断展开

总结

不直接在 CSS 里设置段首缩进,而是改用手动插入,是为了降低出错的机会。而最稳妥的做法是自己写好所有该出现的 HTML 标签,最大限度地避免由程序去自动生成标签,只是这样的效率太低下,不值得提倡。

替换标点字体的初衷是为了改善显示效果,如果部分标点替换后的表现反而更差,就应该把这些标点从字体中删除。另外,现在大部分 CSS 中都设置了多个字体,其中需要替换标点字体的可能只有微软雅黑,这个修改会将所有字体的标点替换掉,有点一刀切。

如果你的网站有使用图标字体,那还可以把标点字体和图标字体合并,我的方法是先压缩好标点字体,然后把它上传到 IcoMoon,再下载字体,里面会有一个图标一个文件的 SVG 格式,把这些 SVG 文件上传到 Iconfont,合并到原来的图标字体中,再把标点图标的 Unicode 码修改成正确的就可以了(可以用百度字体编辑器打开原来的标点字体来查询 Unicode 码)。

这个两端对齐的排版总算是搞掂了,我曾经考虑在英文字母之间也插入这些看不见的空格,但我最终连测试也没有做,因为现在看起来也足够了。提醒一下,写文章的时候记得在英文和汉字之间手动输入一个空格,这也是中文排版的要求。

这篇文章只是抛砖引玉,期望高手能扔给我更好的解决方案。

PS. 才发现 blink 内核的浏览器已经能很好地实现中文两端对齐和首尾字符控制。

作者:Xelloss
本博客文章采用 知识共享(Creative Commons) 署名-非商业性使用-禁止演绎 4.0 进行许可。