觀此回答有感https://www.zhihu.com/answer/1215083944

比如在電腦上按照某種字體把現在常用的漢字疊著寫出來,最後是否會是一個黑方塊


這個實驗我和 colourphilosophy 探討過,雖然看起來非常無厘頭,但其實對中文字體設計是有一些幫助的。

中文字體設計中有兩個概念:字身框字面框。字身框是外側的方框,大致相當于田字格的外框,也相當於鉛活字那一個金屬方塊的大小;字面框則是漢字字元實際分布的空間,很大程度上能夠反應字體的特性。

來源:justfont - 字型設計自己來——中文字型設計的第一課

所以,一般來說,即使把所有漢字疊加起來,字面框有可能會被填滿,但字身框中無論如何都還會留有一點空間,否則所有的漢字在排版時都會連在一起,沒有「呼吸的感覺」(笑)。

接下來我們看看真實字體的疊加情況。

中易宋體

思源宋體 Regular

思源黑體 Regular

微軟雅黑 Regular

我們把 GB2312 裡面的漢字隨機分了 10 組,然後分別疊加(全疊一起就是一坨黑,當然也可以後期加工)。在大樣本下每組基本沒什麼差別,換句話說疊加 600 字和 6000 字差別並不大。By the way,題目問的是「所有漢字」疊加,現在 Unicode 裡面的所謂漢字已經積累到了 92856 個[1],考慮到手邊機器的算力以及實際字體的支持情況,這裡只拿 GB2312 裡面的 6763 個漢字來玩。

圖中的藍色邊框大致就是字身框,而字面框並不是字體文件本身帶有的數據,只能通過後期分析(或者私底下問設計師)【實際上 OpenType 規範中允許在BASE表中寫入字面框信息(icfticfb基線標籤)[2],但大多數國內廠商的字體中並沒有提供BASE表,思源和很多日本字體則是有的。感謝 Celestial Phineas!】。上面挑了四種字體,兩宋兩黑。大致可以看出,字面框的大小從上往下遞增,這也符合我們日常的感覺:中易宋體字面最小,結構緊湊;微軟雅黑字面最大,結構寬鬆,但密排時感覺過於擁擠、喘不過氣而來;思源宋體、黑體介於其間,觀感現代而又不顯慵懶(好能扯)。

另一方面,除了分析字面率,疊加實驗還可以幫助我們理解筆畫的分布。上面的圖其實並不是一片均勻的黑色,而是有濃淡分布的。顯然,黑色較深的地方就對應著筆畫的集中分布。我們看那兩個宋體,都是左側有明顯的一豎,右側下面有一橫,而且右下角有一處格外明顯的黑色區域;右側還有一些不太明顯的橫豎筆畫分布。這就可以得出以下結論:

  • 漢字中左右結構的形聲字佔有絕對的統治地位
  • 左側的形旁很多以一豎作為主筆
  • 半包圍、全包圍結構的字也很多,而且大都有著相同尺寸的外框
  • 漢字筆畫中,橫和豎的比例較高

而對於字體設計師,還可以考慮下面這些問題:

  • 左側形旁與右側聲旁應該保持多大的距離?(參考謝培元先生和徐學成先生的第二中心線理論[3]
  • 筆畫的布局是否應當遵循統一的規律?是不是應該把不同部件的筆畫盡量放在相同的位置上?
  • 怎樣控制不同偏旁、部首的統一性?
  • 怎樣控制所謂「中宮」的大小?
  • 左右邊距應當如何控制?橫排或者豎排對此又有怎樣的影響?

這些問題歡迎大家(尤其是設計師朋友們)思考一下。


上面實驗的都是宋體和黑體,我們可以再來試一下其他風格:

康熙字典體(@厲向晨 別打我)

魏碑

行楷

這裡只用 400 字疊加,差不多也夠了。從上到下,書寫的感覺越來越強烈,體現在字面率越來越小,重疊的筆畫越來越少,曲線的感覺越來越明顯。但無論是哪種字體,我們都可以看出十分明顯的左右結構——畢竟這是漢字本身的特點。

還有一些別的東西可以玩,比如疊加獨體、左右結構、上下結構、包圍結構的漢字:

思源宋體

我似乎已經看到了 biang 字 2333。

漢字還有相當數量的衍生文字,在中國少數民族地區和泛東亞文化圈都被廣泛使用。包括日文平假名、片假名(評論區:假名像個球 2333):

ヒラキ?ノ明朝 ProN

諺文音節:

???(也就是思源宋體)

相比漢字,留下的空白就要多很多了。

應評論區要求補上注音符號:

思源黑體

以及女書、契丹小字和西夏文:

左:契丹小字(BabelStone Font);右:女書(Noto Sans Nushu,來自設計師 Lisa Huang,剛剛發布還熱乎著)

西夏文。左:Tangut Yinchuan,右:Noto Serif Tangut

這些文字保留了漢字的一些特徵,但又有所不同。比如女書整體造型呈菱形,完全不同於漢字的方塊造型[4]。西夏文和漢字類似,存在大量肉眼可見的左右結構字,但和漢字相比,左右部分大小基本相同,結構更加對稱(不過 Noto 這個西夏文字體被噴得很慘[5])。


最後, @韓泳思 提到了點陣字體的問題,我們也來欣賞一下:

GNU Unifont(這裡不是 GB2312 了!)

Unifont 中的漢字基本是 16×15 像素,為了防止密排時連在一起留了一個像素,但仍然有不少字會落到最右側的一列中。不過圖中可以看到,右上角還有最後一個空白沒有被佔領,所以我們可以得出結論:即使是點陣字體,所有漢字全部疊加起來也還是會留下空白(只考慮 Unicode 統一表意文字的範圍)。


實驗用的是 XeTeX,並用pdftopng導出圖片。附上代碼:

documentclass{standalone}
usepackage{tikz,xeCJK}

% 字體
setCJKmainfont{Source Han Serif SC}

% 透明度、顏色
defOption{opacity=0.1, color=black}

egin{document}

fontsize{200}{200} selectfont

ExplSyntaxOn

% 漢字列表
clist_set:Nn c_han_gb_clist
{
一丁七萬丈三上下丌不,
與丐丑專且丕世丘丙業,
今國意我永然警轉酬隨風鷹,
一乍丘中乎主串互予乃亂丫丐且丈三雲上丹了並乘乩乏丁世乖久井不丕乾乞五乙丸之事丞於下七乳二也丑九丟么丙,
交仁丐且仲仔丟之乙些主伏什丸予九介丹亮亨事仞也仿任井伐伯付伍上仆伉亙份乞亟企仍丑丕仟亦五以丘仗仄並雲仰一丫久他乘享了下不中乖世乏七仕么乳丞互串亂乎亭京件仇丈今仃伕於丁乍二休亥亡仙丙人伊亞代三令乩乃仳乾,
}

clist_map_inline:Nn c_han_gb_clist
{
egin{tikzpicture}[every node/.style={inner sep=0,outer sep=0}]

ode [color=blue!60!black] at (0,0)
{
makebox [0pt]
{
dim_set:Nn fboxsep {0pt}
dim_set:Nn fboxrule {2pt}
fbox
{
color {white}

ule {1em} {1em}
}
}
};
exp_args:NNo
ode [Option] at (0,0)
{
kern50pt
l_map_inline:nn {#1} { makebox [0pt] {##1} }
kern50pt
};

ode at (0,-4.2) { tfamily huge l_count:n {#1} };
end{tikzpicture}
}

ExplSyntaxOff

end{document}

參考

  1. ^2019 「State of the Unification」 Report https://blogs.adobe.com/CCJKType/2019/07/2019-sotu.html
  2. ^Baseline tags https://docs.microsoft.com/en-us/typography/opentype/otspec160/baselinetags
  3. ^厲致謙. 漢字設計中的度量標準(二) https://zhuanlan.zhihu.com/p/101183974
  4. ^Noto Sans Nushu https://github.com/LisaHuang2017/noto-sans-nushu
  5. ^https://twitter.com/BabelStone/status/1106903314690686986


用Windows自帶的中易宋體,用JS將Unicode的CJK基本區的所有漢字疊起來是這樣的。代碼相當簡單,放後頭了。反正中間是填滿了,但是邊邊角角還是乾乾巴巴、麻麻賴賴的。

&&
&

@韓泳思 說點陣字會,但拿中易宋體的點陣來說,其實還是沒完全填滿的。

@蒼原雪 用目前市面上最細的字體漢儀旗黑25S的話是這個效果(白邊部分是只出現過幾次的情況)。同樣一點兒空當也沒有。


五更了,這一次應評論區小夥伴要求,畫漢字的熱力圖。 @諸葛不亮 @夕雪 @polarlion。這回不再隨機選,而是依次畫Unicode列表裡漢字的前「1,10,100,1k,10k,20k」疊加的熱力圖效果。很有意思的是前1000個漢字情形能明顯的看出左右結構,存在大量單人旁的字。一直到畫完所有的2萬字左右依然可隱約看出左右結構。@王贇 Maigo


四更下,有小夥伴 @興趣寶貝 提出將漢字諺文與平假名片假名橫向比較有失公允。下面直接比較下漢字偏旁部首,平假名/片假名,韓語字母。三者總數量很接近,隨機取相同數目展示如下:


再更下,多謝 @王贇 Maigo 指正,韓語Unicode只到D7A3,共計11172字「少了11個」。

多謝 @王贇 Maigo @Wayne 提出日語字體的問題。最後面補上不同日語字體的比較。


「二更」受高贊回答啟發,一併把日語和韓語的動圖也畫了。日語當然僅僅是平假名和片假名。最終看起來漢字的確是方塊字。韓語排列組合拼湊的痕迹太明顯,疊加字數不太多的時候具有明顯的上下結構。日語平假名和片假名合起來像個球。哈哈。


雖然不懂語言學,但是用matplotlib簡單的畫了下,效果如下:

漢字:

漢字疊加效果

畫圖的時候刻意給漢字設置了5%的透明度,當總數到200基本就是一團黑了。但是一直畫到2萬字依然感覺黑色區域總面積還在增加。

韓語:

韓語疊加效果

日語平假名/片假名

日語平假名/片假名疊加效果

或許是因為平假名/片假名一共只有90幾個字元,或許是其採用了草書效果,真的看起來像個球。

橫向比較下,中日韓三語各隨機取90個字元「日語可取的太少了」如下:

補充下不同日語字體的比較,這些字體是Mac上matplotlib自帶的。畫圖的時候採用了相同的字體大小,那個像個球的中易黑體明顯比其他小很多。


matplotlib畫圖使用的是Unicode:

  • 中文:u4E00-u9FD5, 共20949個漢字
  • 韓文:uAC00-uD7AF,共11183個字
  • 日文平假名 u3040 - u309f,共46字
  • 日文片假名 u30a0 - u30ff,共46字

代碼如下:

import random
def chinese(num):
隨機選取num個中文字元
chineselist = [chr(i) for i in range(0x4E00,0x9FD5+1)]
return random.sample(chineselist, num)

def japanese(num):
隨機選取num個日文總平假名/片假名數字元
japaneselist = [chr(i) for i in range(0x3041, 0x309f+1) if not (i == 0x3097 or i == 0x3098 )] + [chr(i) for i in range(0x30a0, 0x30ff+1)]
return random.sample(japaneselist, num)

def korean(num):
隨機選取num個韓文字元
koreanlist = [chr(i) for i in range(0xAC00, 0xD7AF+1)]
return random.sample(koreanlist, num)

示例圖:

import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib import font_manager
fontP = font_manager.FontProperties()
fontP.set_family(SimHei)
fontP.set_size(200)
fontP1 = font_manager.FontProperties()
fontP1.set_family(SimHei)
fontP3 = font_manager.FontProperties()
fontP3.set_family(AppleGothic)
fontP3.set_size(200)

fig, axs = plt.subplots(1,3, figsize = (12,4), dpi = 100)
plt.subplots_adjust(wspace = 0.1)
num = 90

labels = [漢字,日語, 韓語]

for i in range(len(axs)):
axs[i].axis(off)
rect = patches.Rectangle((0.05,0.05),0.9,0.9,linewidth_=1,edgecolor=r,facecolor=none)
axs[i].add_patch(rect)
axs[i].set_title(labels[i], fontproperties=fontP1, fontsize = 20 )

for t in chinese(num):
axs[0].text(0.03,0.16, r%s%(t), fontproperties=fontP, transform = axs[0].transAxes, alpha = 0.05)
for t in japanese(num):
axs[1].text(0.03,0.16, r%s%(t), fontproperties=fontP, transform = axs[1].transAxes, alpha = 0.05)
for t in korean(num):
axs[2].text(0.03,0.16, r%s%(t), fontproperties=fontP3, transform = axs[2].transAxes, alpha = 0.05)

plt.savefig(cjk.png, dpi = 300, bbox_inches = tight)


紙上得來終覺淺,絕知此事要宮刑!

背景畫布:1500x1500;

字體: 思源宋體-超細(專門用了超細減輕筆劃遮蓋);

大小:1100;

字數: 20901個,全中文;

200個漢字和20000個漢字疊加的效果差不多

效果如下:

實際上:

20個就這樣了:

一丁丂七丄丅丆萬丈三上下丌不與丏丐丑丒專

50個就這樣了:

一丁丂七丄丅丆萬丈三上下丌不與丏丐丑丒專且丕世丗丘丙業叢東絲丞丟丠両丟丣兩嚴並喪丨丩個丫丬中丮丯豐丱

100個就這樣了:

一丁丂七丄丅丆萬丈三上下丌不與丏丐丑丒專且丕世丗丘丙業叢東絲丞丟丠両丟丣兩嚴並喪丨丩個丫丬中丮丯豐丱串丳臨丵丶丷丸丹為主丼麗舉丿乀乁乂乃乄久乆乇么義乊之烏乍乎乏樂乑乒乓喬乕乖乗乘乙乚乛乜九乞也習鄉乢乣


我也來個python代碼:

from PIL import Image, ImageDraw,ImageFont

str_hineses=[]
start,end = (0x4E00, 0x9FD5) #漢字編碼的範圍,20950個漢字

for codepoint in range(int(start),int(end)+1):
str_hineses.append(chr(codepoint))

img = Image.new(RGB, (1024, 1024), (255, 255, 255))
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(simsun.ttc, 1024)

for str_hinese in str_hineses:
draw.text((0, 0), str_hinese, fill=(255, 0, 0), font=font)

img.show()


推薦閱讀:
相关文章