├── .gitignore ├── LICENSE ├── README.md ├── data ├── glyph_complexities.json └── ids_simple.json ├── index.html └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Celestial Phineas 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 第二中心线计算器 2 | 3 | Demo: https://welai.github.io/secondary-axes/ 4 | 5 | 这是一个用第二中心线理论计算活字字体辅助线的简易工具,该工具未做移动端适配,请在PC上使用。 6 | 7 | 其中:深灰色虚线与数值代表定位部件的第二中心线;浅灰色虚线与数值是不定位的第二中心线,仅作为中宫的参考与提示;淡灰色点线代表部件交界线。 8 | 9 | ## 本工具的使用 10 | 在“字”输入框中输入您要计算的字,在“设定”窗格中输入字体的参数设定,单击“更新”按钮,下方预览窗格中就会出现计算所得的两组第二中心线。“设定”窗格中的内容对同一字体而言应当是固定的。预览底图的“参考字”与计算过程无关,仅供协助用户设定参数与验证计算有效性之用。 11 | 12 | 其中,“参考字体”输入框中可键入系统字体的名称,用以更改预览底图的参考字字体。去掉“显示参考字”的勾选可隐藏预览底图的参考字。 13 | 14 | UPM(units per em)是该字体的全身单位数,也就是字身框宽度或高度所对应的数值。“基线高度”为字体降部(descender)的绝对值,在一个OpenType字体中,纵坐标零点通常是基线,汉字字身框底边常对应于降部。这两个数值不影响第二中心线的位置,只体现在参考线的数值换算。 15 | 16 | 字面率为字体字面框相对字身框(日:仮想ボディ)的比值。 17 | 18 | 中宫率定义为两组第二中心线相对字身框的比值。中宫设定越大,该值应越大。该设定一般不超过字面率的一半。“补偿缩放”考虑了第二中心线理论中的“互差”效果,会依字的结构疏密适当缩放第二中心线间距。 19 | 20 | “灰度优先/笔画均匀优先”、“左紧右松”、“上紧下松”滑块会影响部件视觉重量估计,进而影响第二中心线的定位。“灰度优先”会在经营左右位置时适当考虑上下位置的灰度,经营上下亦然;“笔画均匀优先”倾向于仅考虑单方向笔画分布。“左紧右松”程度高的字体会低估左旁视觉重量,例如对楷体等字体可适当调高。“上紧下松”程度高的字体则会低估上冠视觉重量,导致重心稍高的效果。 21 | 22 | 对“互差”、“视觉重量”的解释,可参见下文对原理论的概述。 23 | 24 | ## 何谓第二中心线? 25 | 第二中心线理论由上海印刷技术研究所字体设计师谢培元、陈初伏(化名:平野、初伏)于1962年提出,原载于《印刷活字研究参考资料·第五辑》中《经营位置之一——第二中心线的运用》一篇。第二中心线理论探究了印刷活字中合体字的部件定位与结构统一问题。 26 | 27 | 文中将经过字身几何中心的水平与竖直参考线定义为“第一中心线”,而“第二中心线”定义为合体字部件中心所形成的参考线;举例来说,“林”字的第二中心线在其两竖附近,是一组两根竖直的第二中心线。与第一中心线所不同的是,第二中心线在不同字之间是游移的,而非固定的;举例来说,“纲”的第二中心线必然要比“刚”的靠左。 28 | 29 | 第二中心线理论认为,在同一字体中,第二中心线间距相对恒定,但容有±5%的互差,且该差距有特定趋势:内白大的字第二中心线间距会偏小,内白小的字第二中心线间距会偏大。文中经过测量验证了该结论。 30 | 31 | 理论还提出了确定第二中心线位置的方法:第二中心线到第一中心线的距离与其对应部件的视觉重量呈反比,即“杠杆原理”。但文中并未建模如何估计汉字部件的视觉重量。 32 | 33 | 为实现该工具从质量检测工具到生产辅助工具的转变,徐学成设计了在水平与竖直方向分别预设3组第二中心线的字稿格子,成文《漫话第二中心线》,发表于《印刷活字研究参考资料·第六辑》。 34 | 35 | 徐学成的文章同样指出了第二中心线理论的局限性:(1) 只适用于两部件构成的合体字,不适用于独体字、三合结构、包围结构等;(2) 第二中心线理论不解决重心定位问题;(3) 第二中心线互差的难以解释。 36 | 37 | ## 对第二中心线理论的定量处理 38 | 将“第二中心线间距”视作中文字体的内生参数,结合对汉字部件视觉重量的估计方法,就可以做到第二中心线位置的定量计算。 39 | 40 | 我们采用了一种朴素的图像处理方法估计汉字部件的视觉重量,并且对整体、横向笔画、纵向笔画做了分别估计,同时对数值做了规范化处理。尽管视觉重量计算使用思源黑体作为字形来源,但是在理论上,这种估计方法在任何宋体或黑体字上应用,所得到的结果都是接近的,对第二中心线计算结果影响不大。 41 | 42 | 在“互差”的处理上,当前建模仍较为随意(arbitrary),并非基于严格统计,但基于若干合理假设与经验。 43 | 44 | 该工具当前只考虑一级部件拆分的计算结果。对⿰结构的字,计算结果只有两根竖直方向的线定位部件;同理,对⿱结构的字,计算结果只有水平方向线定位部件;这两种情况中,另一组不定位部件的第二中心线仅间距有参考价值,位置仅仅是置中而不具备参考价值。对半包围结构的字,计算结果中水平与垂直方向的第二中心线都定位部件。 45 | 46 | ## 部件交界线的处理 47 | 1995年,Wong和Hsu提出了一种[基于物理建模的动态组字算法](https://ieeexplore.ieee.org/document/524809),认为部件间隙处的假想微粒受到各部件的引力是平衡的,该引力与部件的视觉质量成正比,与距离平方成反比。 48 | 49 | 本计算器将这个结论与第二中心线相结合起来,用于计算部件交界线。经多字试验,视觉上比较经得住检验。Wong与Hsu的方法是否蕴含了第二中心线的结论,尚待数学证明。 50 | 51 | ## 讨论 52 | 第二中心线理论提供了一种定量方法,可快速定位汉字部件位置、控制字体结构统一性。可以认为,它提供了传统书法“中宫”概念的一种良定义,尽管这一定义尚无法适用于全部汉字。 53 | 54 | 实践上,部件几何中心易受穿插特点影响,将本工具预测的参考线作为部件的视觉重心参考线去使用会更为合理。 55 | 56 | 在将本工具应用于您自己的字体项目时,您需要花费些时间调整设定参数,多试验一些字,特别是结构复杂的字,来摸清“中宫率”、“灰度优先/笔画均匀优先”、“左紧右松”、“上紧下松”这些参数的设定,因为这些参数对不同字体来说会非常不一样。 57 | 58 | 经试验,在充分调整参数后,该工具的预测结果基本合乎直觉与视觉规律,尽管在很多字的结果只能说差强人意,但足以辅助快速起稿、协助您发现部分字可能存在的结构问题。 59 | 60 | 适用性上,除宋体、黑体,该工具在正文用楷体、仿宋也可以取得不错的效果。对个别字而言,第二中心线的预测结果会明显失效,因为这些字背后有不浅的习惯因素。例如“仁”,“二”要写得扁宽一些,不得不占据理论上所不应该占据的大空间。或如“町”,从“丁”的字都会释放出一块便于识别的内白。但有趣的是,现代圆体的划时代之作,Naru(日:ナール),其“町”字造型,经测量就几乎完美契合第二中心线规律,格外挤压了“丁”的占据空间,不仅相较于中文字体、相较于其他日文字体,Naru在“町”字的处理也更为夸张、几何化(感谢韩泳思发现了这点)。从这些例子看,第二中心线在活字字形个性的维度上,存有相当的便宜空间。 61 | 62 | 总地来说,第二中心线的实际适用范围和效果较我们原本预期要好得多。能够将这样一种规律建模出来,不得不让人佩服上海老一辈设计师敏锐的直觉。 63 | 64 | ## 联系方式 65 | 如果您有任何想法、建议、问题、心得,都很欢迎和我们交流,我们很期待可以获得反馈。欢迎访问我们[实验室的主页](http://www.next.zju.edu.cn/),也欢迎联系作者:celestialphineas [AT] outlook [DOT] com -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 第二中心线测试 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |

第二中心线计算器

16 |
17 |
18 |
19 |
20 | 21 | 22 |
23 | 更新 24 |
25 |
26 | 103 |
104 |
105 |
106 |
107 |
108 |
109 |
h1
110 |
h2
111 |
v2
112 |
v1
113 |
v2
114 |
v2
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | 147 | 189 | 190 | 281 | 282 | 283 | 284 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | (function(){let t={hanzi:"林",font:"",upm:1e3,baseline:150,facew:96,faceh:96,medw:40,medh:40,overshoot:true,weightmix:.5,hweight:.2,vweight:.2};function e(){t.hanzi=$("#hanzi-input").val();t.upm=parseFloat($("#upm-input").val());t.baseline=parseFloat($("#baseline-input").val());t.facew=parseFloat($("#facew-input").val());t.faceh=parseFloat($("#faceh-input").val());t.medw=parseFloat($("#medw-input").val());t.medh=parseFloat($("#medh-input").val());t.overshoot=$("#overshoot-input").is(":checked");t.weightmix=parseFloat($("#weightmix-input").val());t.hweight=parseFloat($("#hweight-input").val());t.vweight=parseFloat($("#vweight-input").val());const a=$("#font-input").val();try{if(a===""||a===undefined){t.font="";$("#glyph-box").css({fontFamily:""})}else if(!document.fonts.check("12px "+a)){M.toast({html:`不支持的字体:${a}`});t.font="";$("#glyph-box").css({fontFamily:""})}else{t.font=a;$("#glyph-box").css({fontFamily:a})}}catch{M.toast({html:`不支持的字体:${a}`});$("#glyph-box").css({fontFamily:""})}$("#glyph-box").text(t.hanzi);if($("#showref-input").is(":checked")){$("#glyph-box").css({display:""})}else{$("#glyph-box").css({display:"none"})}o(t);localStorage.setItem("secondMedialData",JSON.stringify(t))}function s(a){$("#hanzi-input").val(a.hanzi);$("#font-input").val(a.font);$("#upm-input").val(a.upm);$("#baseline-input").val(a.baseline);$("#facew-input").val(a.facew);$("#faceh-input").val(a.faceh);$("#medw-input").val(a.medw);$("#medh-input").val(a.medh);$("#overshoot-input").prop("checked",a.overshoot);$("#weightmix-input").val(a.weightmix);$("#hweight-input").val(a.hweight);$("#vweight-input").val(a.vweight)}function o(a){const t=$("#em-box").width(),e=$("#em-box").height();const s=t*a.facew/100,o=e*a.faceh/100;$("#face-box").css({width:s+"px",height:o+"px",left:(t-s)/2+"px",top:(e-o)/2+"px"});const{axes:[h,i,l,n],bounds:[r,v,u,g],status:d}=p(a);switch(d){case"unknownIDS":{M.toast({html:"第二中心线理论不支持的构字,或拆字后部件过于生僻"});$("#h-axes").addClass("gray-out");$("#v-axes").addClass("gray-out");$("#h1-val").addClass("gray-out");$("#h2-val").addClass("gray-out");$("#v1-val").addClass("gray-out");$("#v2-val").addClass("gray-out");$("#bounds").addClass("gray-out");$("#hb-val").addClass("gray-out");$("#vb-val").addClass("gray-out")}break;case"unknownComplexity":{M.toast({html:"部分部件视觉重量未知,无法计算"});$("#h-axes").addClass("gray-out");$("#v-axes").addClass("gray-out");$("#h1-val").addClass("gray-out");$("#h2-val").addClass("gray-out");$("#v1-val").addClass("gray-out");$("#v2-val").addClass("gray-out");$("#bounds").addClass("gray-out");$("#hb-val").addClass("gray-out");$("#vb-val").addClass("gray-out")}break;case"horizontal":{$("#h-axes").removeClass("gray-out");$("#v-axes").addClass("gray-out");$("#h1-val").removeClass("gray-out");$("#h2-val").removeClass("gray-out");$("#v1-val").addClass("gray-out");$("#v2-val").addClass("gray-out");$("#bounds").removeClass("gray-out");$("#hb-val").removeClass("gray-out");$("#vb-val").addClass("gray-out")}break;case"vertical":{$("#h-axes").addClass("gray-out");$("#v-axes").removeClass("gray-out");$("#h1-val").addClass("gray-out");$("#h2-val").addClass("gray-out");$("#v1-val").removeClass("gray-out");$("#v2-val").removeClass("gray-out");$("#bounds").removeClass("gray-out");$("#hb-val").addClass("gray-out");$("#vb-val").removeClass("gray-out")}break;case"done":{$("#h-axes").removeClass("gray-out");$("#v-axes").removeClass("gray-out");$("#h1-val").removeClass("gray-out");$("#h2-val").removeClass("gray-out");$("#v1-val").removeClass("gray-out");$("#v2-val").removeClass("gray-out");$("#bounds").removeClass("gray-out");$("#hb-val").removeClass("gray-out");$("#vb-val").removeClass("gray-out")}break}$("#h-axes").css({width:`${s}px`,height:`${(i-h)*e}px`,left:`${(t-s)/2}px`,top:`${h*e}px`});$("#v-axes").css({width:`${(n-l)*t}px`,height:`${o}px`,left:`${l*t}px`,top:`${(e-o)/2}px`});$("#bounds").css({width:`${(v-r)*t}px`,height:`${(g-u)*e}px`,left:`${r*t}px`,top:`${u*t}px`});const m=.5-uq=a);const h=$.getJSON("./data/glyph_complexities.json").then(a=>k=a);$.when(a,h).then(()=>{const a=localStorage.getItem("secondMedialData");if(a!==null){t=JSON.parse(a);s(t)}e();$("#loading").css({display:"none"})});window.syncUI2Data=e})(); --------------------------------------------------------------------------------