原地踏步 发表于 2024-1-1 12:21:19

浅谈如何制作矢量字体文件

本帖最后由 原地踏步 于 2024-1-3 21:20 编辑

本人原本对矢量字体文件一无所知,后来在明经看到wharan及szhorse等网友的贴子,颇受启发,于是试着自己研究了一下。中间也经历了一些曲折,最后总算有点收获,不敢藏私,在这里与各位朋友分享。若有错误,欢迎大家批评指正。Wharan的帖子:http://bbs.mjtd.com/thread-186534-1-1.htmlszhorse的帖子:http://bbs.mjtd.com/thread-186226-1-1.html

原地踏步 发表于 2024-1-1 13:11:29

制作文字
1)准备先将编译后的形文件,如HZTXT.SHX,反编译成形定义文件HZTXT.SHP。反编译可采用SHX2SHP等软件,网上有,搜一下就可以。再开一张新图,绘制一个网格。网格间距为1mm,尺寸大小根据需要确定。尺寸越大,字体精度越高,但字节数可能会增加。由于形定义文件X、Y的位移取值范围为-128~+127,因此建议网格不要超过127×127。这样只要线段均在网格内,就不必担心超出范围的问题了。将网格制成图块,块名为“GRID”(后面的程序要用到),原点为网格左下角。现在,就可以开始写字了。 2)制作写字的方式很多,高手可以直接绘制,一般人可以先找几个与待写字符有相同偏旁的文字,用CAD的ET自带程序TXTEXP炸开,再来拼凑组合,也可以找一个喜欢的TrueType或其它字体来描(推荐)。若采用描字法,可将底字设置适当的透明度,以避免遮住光标。有效的图元为 LINE、LWPOLYLINE、POLYLINE、ARC、CIRCLE。将写好的字置于网格内,运行程序LINSHP,即可生成该字符的代码。程序会删除网格中的原线段,按数据内容重新绘制在网格内,并生成代码及字节数。如果拾取了文字,还会生成它的GBK或UNICODE码。由于形文件所要求的矢量均为整数,程序又仅使用代码12和13来绘制圆弧(整圆采用代码10绘制),因此所有的点均会落在网格上(使用代码11绘制时,终点不一定落在网格上)。但写字时不可能这么精确,这会造成一些意料之外的结果,因此可能还需要做一些微调,特别是涉及到弧的情况下。调整可就在网格内进行,待满意后可再次运行LINSHP。程序可将生成的数据直接写入形定义文件中,但不知什么原因有时候会出错,建议选择不写入,而将打印在屏幕上的代码手动复制至形定义文件中。形定义文件的最后应有至少一个空行。程序默认字体格式为BIGFONT,字宽固定,也可选择UNIFONT,或字宽不固定。当选择固定字宽时,BIGFONT字体采用十进制编码,数据始终以 “7,1” 开头,以 “7,2” 结束;UNIFONT字体采用十六进制编码,始终以 “7,00001” 开头,以 “7,00002” 结束。当选择可变字宽时,不使用代码7,用户需自己输入字宽。通常BIGFONT字体均为全角字符,采用固定字宽即可,但UNIFONT字体可能包含有半角的英文字母、数字及符号,此时就会用到可变字宽。程序可支持所有平面的UNICODE字符编码,但位于平面0以外的字符编码,目前的CAD(我用的是2014及2021)无法通过编译。程序不进行坐标转换,不检查图块是否正确(只读取图块名及插入点),也不检查图层是否锁定。最后,使用CAD的命令COMPILE编译字形定义文件,即可得到字形文件。若字符大小为127×127,则绘制的文字会与字高为127,宽度因子为1的写出来的文字完全吻合。

原地踏步 发表于 2024-1-1 12:54:09

原地踏步 发表于 2024-1-1 12:36
程序共4个
linshp == 将线段生成 shp 所需代码
shplin == 读取 shp 文件,生成线段


hztxt.shx 主要是美化了一些字,并增加了一些字。基本上一二三级字能写的都写了。

原地踏步 发表于 2024-1-1 13:12:57

最后,在这里祝大家新年快乐,心想事成!

原地踏步 发表于 2024-1-1 12:36:52

程序共4个
linshp == 将线段生成 shp 所需代码
shplin == 读取 shp 文件,生成线段
shpcom == 以上两个程序的共用子程序,不可单独执行
yb_gbkuni == GBK 码转 UNICODE 码映射表

原地踏步 发表于 2024-1-1 13:00:50

本帖最后由 原地踏步 于 2024-1-1 13:08 编辑

下面简单介绍一下如何制作矢量字体,结合一个例子来说明。详细内容可参考1楼的pdf文件。
例子中网格尺寸为127×127,字符的大小也为127×127。


字符“咸” 的代码为:*53196,88,咸7,1,8,(3,5),1,12,(14,39,16),12,(1,19,2),8,(0,38),8,(102,0),2,8,(-12,5),1,12,(-18,16,5),2,8,(-13,3),1,13,(6,-60,3),(14,-45,8),(9,-10,19),(9,3,61),(5,23,7),(0,0),2,8,(-12,48),1,12,(-45,-78,-24),2,8,(-29,18),1,9,(30,0),(0,32),(-30,0),(0,-32),(0,0),2,8,(-8,53),1,8,(47,0),7,2,0以下依次说明:★第一行:*53196:形编号,即字符“咸” 的十进制GBK码,以 “*” 开头。也可采用十六进制,十六进制编码前面要加0。同一个形定义文件中不能有重复的编号。88:后续行中数据的字节数。每个逗号分隔着一个字节,数一下就知道了。每个形最多可有2000个字节。咸:形名称,通常省略。★后续行:形定义的数据,可为一行或多行,每行最多可包含128个字符。代码7:绘制编号由下一个字节指定的子形。数据1:由代码7指定的子形编号,必须位于当前的形定义文件中。可能为:*1,75,2,14,8,(-63,-127),0也就是说,原数据也可写为(要去掉子形的结束符0):5,2,14,8,(-63,-127),8,(3,5),1,12,(14,39,16),……代码5:将当前位置入栈,即保存当前坐标,后面不跟数据。由于此前没有任何位移,故当前坐标为原点(0,0)。代码2:取消激活绘图模式(提笔),也就是笔悬空移动,不绘制。代码14:只有当文字是垂直的,才会处理下一个命令,水平文字会自动忽略此代码。垂直文字一般仅起点和终点与水平文字不同,其余都是一样的。代码14放在此处,与代码8结合,用于确定垂直文字的起点。当字符不可书写垂直文字时,就不要使用代码14。代码8:由后面两个数字指定 X-Y 的位移(不是坐标,位移换算成坐标值要与前面的坐标相加)。数据 (-63,-127):由代码8指定的位移,即向X方向位移 -63,向Y方向位移-127。这就是垂直文字中下一个字符的起点。其中的括号不是必需的,只是为了阅读方便。建议书写时均加上括号。数据8,(3,5),:与上一组数据类似,区别是代码14已结束了,此处的代码8用于水平文字。即从坐标 (0,0) 开始(代码14的位移在水平文字中被忽略),向X方向位移3,向Y方向位移5。从上图中可以看出,字符 “咸” 的起点坐标即为(3,5)。代码1:激活绘图模式(落笔)。代码12:绘制一段圆弧,后面跟三个数字。前两个数字的含义与代码8类似,分别代表X-Y方向的位移,第三个数字为凸度,其取值范围为-127~127,逆时针为正。凸度的计算公式为:(2*H/D)*127,可采用小数计算,但计算后得到的值应取整数。半圆的凸度为127(或-127),凸度为0时表示直线段。   
形说明中的凸度与群码表中LWPOLYLINE的群码42代表的凸度计算方法类似,但多了一个系数,形的凸度=群码凸度×127。数据“1,12,(14,39,16),12,(1,19,2),8,(0,38),8,(102,0),” 表示先绘制两段弧,再绘制两段直线,即“咸” 字中的 “厂”。该段数据也可写成:“1,12,(14,39,16),1,12,(1,19,2),1,8,(0,38),1,8,(102,0),”很显然,这种写法的字节数较多。数据2,8,(-12,5),1,12,(-18,16,5),2,8,(-13,3):抬笔位移,落笔绘制一段弧,即“咸” 字右上角的点,再抬笔位移。代码13:与代码12类似,但可绘制多段圆弧,以 (0,0) 结束。注意 “(0,0)” 不能省略,不要以为加了括号,电脑就会知道你在什么时候结束。前面说过,括号反而是可以省略的。数据“1,13,(6,-60,3),(14,-45,8),(9,-10,19),(9,3,61),(5,23,7),(0,0),”的含义为:落笔,连续绘制5段圆弧,即右侧的弯钩。前面的代码“12,(14,39,16),12,(1,19,2),” 也可写为:“13,(14,39,16),(1,19,2),(0,0),”同样,这种写法的字节数较多。很容易知道,当连续绘制3段弧时,代码12与代码13的字节数相同;超过3段时,使用代码13在字节数上就有优势了。数据2,8,(-12,48),1,12,(-45,-78,-24),2,8,(-29,18):抬笔位移,落笔绘制一段弧,即“咸”字右侧的撇,再抬笔位移。代码9:与代码8类似,但可绘制多段直线,以 (0,0) 结束。数据“1,9,(30,0),(0,32),(-30,0),(0,-32),(0,0),” 的含义为:落笔,连续绘制4段直线,即下方的 “口”。前面的数据“8,(0,38),8,(102,0),” 也可写为:“9,(0,38),(102,0),(0,0),”,但字节数较多。数据2,8,(-8,53),1,8,(47,0):抬笔位移,落笔绘制一段直线,即“口”上面的一横。数据7,2:引用编号为2的子形,子形必须位于当前的形定义文件中。编号2的定义可能为:*2,106,2,8,(127,0),14,8,(-127,-127),0则原数据也可写为:……2,8,(-8,53),1,8,(47,0), 6,2,8,(127,0),14,8,(-127,-127),0代码6:将当前位置出栈,即取出由前一个代码5储存的坐标。也就是说,无论此时坐标位于何处,均回到储存的坐标点上。代码5储存的坐标为(0,0),即当前的笔处于原点位置。数据2,8,(127,0):抬笔向X方向位移127,为本字符的终点,也是下一个字符的起点。本字符起点与终点的距离,即为该字符的宽度。由于字符一般都不会写满,因此它的宽度也包含了空余部分。字符的最后一点通常都不是终点,每个字符的终点都需要专门定义。数据14,8,(-127,-127):定义垂直文字的终点,不影响水平文字。代码0:结束形定义。

wangsr 发表于 2024-1-1 13:51:25

学习了谢谢分享

xiao1984 发表于 2024-1-1 13:59:08

不错新年的第一帖,硕果累累!

429014673 发表于 2024-1-1 14:11:10

录个视频学习下,主要步骤如何

彳余 发表于 2024-1-2 07:12:23

新年的第一好帖
页: [1] 2 3 4 5 6
查看完整版本: 浅谈如何制作矢量字体文件