Little Project:標點擠壓

作為一個(不那麼嚴謹的)排版控,看到自己博客的排版效果不佳總會心癢癢。前一陣子看得我最心亂的就是主頁上引號後接句號的超大標點間距:

這是一個名為 soli/tango 的獨立博客,其意思為「孤獨探戈

這已經算是不那麼極端的情況了,舉一個比較極端的例子:

四大名著指《三國演義》《水滸傳》《西游記》和《紅樓夢

這真的看得我都要暈厥過去了,尤其我還用 text-align: justify 對齊文本兩端——要是你的屏幕寬度夠還好,不然由於瀏覽器渲染時的標點行首/行尾禁則,整個網頁的排版更是一場災難。

為了改善這個情況,我們可以做一些「標點擠壓」的額外處理,大致的思路就是在使用連續全寬標點的時候,把一些視覺間距過寬的標點「擠壓」掉,變成半寬。還是上面那個例子,如果啓用標點擠壓的話,整體視覺效果將會顯得更加合理一些:

四大名著指《三國演義水滸傳西游記》和《紅樓夢

在高版本的 Chromium 中,有一個 text-spacing-trim 的 CSS 屬性可以在瀏覽器層面實現標點擠壓,但這種小範圍限定的實驗特性的兼容性終究不太好。幸好在這方面倒是有不少前人造過的輪子,如漢字標準格式,但它們大多都是利用網頁端的 JavaScript 代碼現場排版,比較「動態,與 Astro 相性實在不符。

就標點擠壓這單一的功能來說,如果我們能提前標記好需要被擠壓的標點符號,在網頁中我們就可以使用 font-feature-settings: "halt"; 啓用 OpenType 字體的半寬特性來擠壓標點,這種純 CSS 的方案可以避免在網頁端加載 JavaScript。

由於需要在 Astro 裏面用,這個標點的預標記過程最好能由 rehype 插件實現,但搜了一圈 GitHub 似乎沒有什麼結果,於是乎自己手搓了一個 rehype-chinese-punctuation-compression,原理大概就是掃描 HTML AST 中所有 Text 節點,然後利用 .compressed-punctutationspan 標記需要被擠壓的標點。

在實現的過程中,還要區分一下簡體中文和繁體中文的標點,它們有一些標點的 Unicode 點位是同一個,但在顯示效果上是不同的。比如說繁體的逗號是居中的,但在簡體中是靠左下的,那麼它們留空的位置是不同的,同樣它們擠壓的方式也不同的。

目前簡體和繁體的擠壓都已經實現了,可以通過改變 script 來指定不同的文字系統,快來和我一起擠壓標點吧