纸上得来终觉浅,绝知此事要宫刑!
背景画布:1500x1500;
字体: 思源宋体-超细(专门用了超细减轻笔划遮盖);
大小:1100;
字数: 20901个,全中文;
200个汉字和20000个汉字叠加的效果差不多
效果如下:
实际上:
20个就这样了:
50个就这样了:
100个就这样了:
五更了,这一次应评论区小伙伴要求,画汉字的热力图。 @诸葛不亮 @夕雪 @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:
代码如下:
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')
这个实验我和 colourphilosophy 探讨过,虽然看起来非常无厘头,但其实对中文字体设计是有一些帮助的。
中文字体设计中有两个概念:字身框和字面框。字身框是外侧的方框,大致相当于田字格的外框,也相当于铅活字那一个金属方块的大小;字面框则是汉字字符实际分布的空间,很大程度上能够反应字体的特性。
所以,一般来说,即使把所有汉字叠加起来,字面框有可能会被填满,但字身框中无论如何都还会留有一点空间,否则所有的汉字在排版时都会连在一起,没有「呼吸的感觉」(笑)。
接下来我们看看真实字体的叠加情况。
我们把 GB2312 里面的汉字随机分了 10 组,然后分别叠加(全叠一起就是一坨黑,当然也可以后期加工)。在大样本下每组基本没什么差别,换句话说叠加 600 字和 6000 字差别并不大。By the way,题目问的是「所有汉字」叠加,现在 Unicode 里面的所谓汉字已经积累到了 92856 个[1],考虑到手边机器的算力以及实际字体的支持情况,这里只拿 GB2312 里面的 6763 个汉字来玩。
图中的蓝色边框大致就是字身框,而字面框并不是字体文件本身带有的数据,只能通过后期分析(或者私底下问设计师)【实际上 OpenType 规范中允许在BASE
表中写入字面框信息(icft
和icfb
基线标签)[2],但大多数国内厂商的字体中并没有提供BASE
表,思源和很多日本字体则是有的。感谢 Celestial Phineas!】。上面挑了四种字体,两宋两黑。大致可以看出,字面框的大小从上往下递增,这也符合我们日常的感觉:中易宋体字面最小,结构紧凑;微软雅黑字面最大,结构宽松,但密排时感觉过于拥挤、喘不过气而来;思源宋体、黑体介于其间,观感现代而又不显慵懒(好能扯)。
另一方面,除了分析字面率,叠加实验还可以帮助我们理解笔画的分布。上面的图其实并不是一片均匀的黑色,而是有浓淡分布的。显然,黑色较深的地方就对应着笔画的集中分布。我们看那两个宋体,都是左侧有明显的一竖,右侧下面有一横,而且右下角有一处格外明显的黑色区域;右侧还有一些不太明显的横竖笔画分布。这就可以得出以下结论:
而对于字体设计师,还可以考虑下面这些问题:
这些问题欢迎大家(尤其是设计师朋友们)思考一下。
上面实验的都是宋体和黑体,我们可以再来试一下其他风格:
这里只用 400 字叠加,差不多也够了。从上到下,书写的感觉越来越强烈,体现在字面率越来越小,重叠的笔画越来越少,曲线的感觉越来越明显。但无论是哪种字体,我们都可以看出十分明显的左右结构——毕竟这是汉字本身的特点。
还有一些别的东西可以玩,比如叠加独体、左右结构、上下结构、包围结构的汉字:
我似乎已经看到了 biang 字 2333。
汉字还有相当数量的衍生文字,在中国少数民族地区和泛东亚文化圈都被广泛使用。包括日文平假名、片假名(评论区:假名像个球 2333):
谚文音节:
相比汉字,留下的空白就要多很多了。
应评论区要求补上注音符号:
以及女书、契丹小字和西夏文:
这些文字保留了汉字的一些特征,但又有所不同。比如女书整体造型呈菱形,完全不同于汉字的方块造型[4]。西夏文和汉字类似,存在大量肉眼可见的左右结构字,但和汉字相比,左右部分大小基本相同,结构更加对称(不过 Noto 这个西夏文字体被喷得很惨[5])。
最后, @韓泳思 提到了点阵字体的问题,我们也来欣赏一下:
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}
用Windows自带的中易宋体,用JS将Unicode的CJK基本区的所有汉字叠起来是这样的。代码相当简单,放后头了。反正中间是填满了,但是边边角角还是干干巴巴、麻麻赖赖的。
<canvas width="255px" height="255px" id="c"></canvas> <script>var c=document.getElementById("c");var ctx=c.getContext("2d");ctx.fillStyle="black";ctx.font="200px 宋体";for(var i=0x4e00;i<0x9ff0;i++){ctx.fillText( String.fromCharCode(i),0,200);}</script>
@韓泳思 说点阵字会,但拿中易宋体的点阵来说,其实还是没完全填满的。
@苍原雪 用目前市面上最细的字体汉仪旗黑25S的话是这个效果(白边部分是只出现过几次的情况)。同样一点儿空当也没有。