#!/usr/bin/python3 ''' Unicode または文字を指定したテキストから、フォントのグリフ名(CID/GID)のリストを取得。 $ char2name.py [option] デフォルトで、カンマ区切りのリストを出力。 ginfo は、make-ginfo.py で出力したグリフ情報ファイル。 -l: Unicode とグリフ名のリストを出力。 -cid: カンマ区切りのリスト出力時、CID 指定用に、'/'+番号 として出力する。 -gid: -l または、カンマ区切りのリスト出力時、CID の代わりに GID を出力。 -r : rotatefont -rtf 用の対象グリフの出力。 xoffset,yoffset はグリフ座標の移動幅。 -m : mergefonts 用のマッピングファイルを出力。 dst-ginfo を変換先として、同じ Unicode または GSUB 置き換え先のグリフに割り当てる。 -g 指定がある場合、GSUB 置き換えのグリフも対象。 -g : 指定グリフから、GSUB の指定 feature で置き換えられるグリフも含める。 feature の名前は、複数の場合、カンマで区切る。(例: "vert,vrt2") text: ※ UTF-8 テキストであること。 "u:" で始まる行は、Unicode 値の指定。 カンマで区切って、16進数の Unicode 値を指定する。 '-' が含まれる場合、範囲の値。 "c:" で始まる行は、文字での指定。 ':' 以降、改行までのすべての各文字を、UTF-8 -> Unicode 変換して指定する。 ただし、半角空白はスキップされる。 それ以外の行は無視。 ------- u:20,3041-309f c:ABCDabcd123 ------- ''' import sys import re if len(sys.argv) < 2: print('[usage] ' + sys.argv[0] + ' [option] ') sys.exit(0) args = [] proc = None optval = {} # オプション i = 1 while i < len(sys.argv): l = sys.argv[i] i += 1 if l[0] != '-': args.append(l) else: l = l[1:] if l == 'l': proc = 'list' elif l == 'cid': optval['cid'] = True elif l == 'gid': optval['gid'] = True elif l == 'r': proc = 'rotate' optval['rotate'] = [sys.argv[i], sys.argv[i+1]] i += 2 elif l == 'g': optval['gsub'] = sys.argv[i] i += 1 elif l == 'm': proc = 'merge' optval['merge'] = sys.argv[i] i += 1 def sort_int(l): return l[0] class ConvName: # エラー def err(self,mes): print(mes, file=sys.stderr) sys.exit(1) # ginfo 読み込み def read_ginfo(self,fname,valname): f = open(fname, 'rt') lines = f.readlines() f.close() namemap = {} unimap = {} gsubmap = {} for l in lines: l = l.rstrip('\n') sp = l.split('\t') name = sp[0] if name.isnumeric(): name = int(name) gid = int(sp[1]) ulist = [] gsub = {} if sp[2]: for uc in sp[2].split('/'): uc = int(uc,base=16) ulist.append(uc) unimap[uc] = name if sp[3]: for tmp in sp[3].split('/'): fea,dst = tmp.split(':') if dst.isnumeric(): dst = int(dst) gsub[fea] = dst if fea not in gsubmap: gsubmap[fea] = {} gsubmap[fea][name] = dst namemap[name] = [gid, ulist, gsub] if valname == 'src': self.srclist = namemap self.srcumap = unimap else: self.dstlist = namemap self.dstumap = unimap self.dstgsub = gsubmap # Unicode から追加 def _add_unicode(self,uc): if uc not in self.srcumap: return name = self.srcumap[uc] # すでにある if name in self.out: return self.out[name] = [uc, None] # GSUB 置換グリフも含める for fea,dst in self.srclist[name][2].items(): if (fea in self.gsubfea) and (dst not in self.out): self.out[dst] = [uc, fea] # Unicode 値の入力処理 def _proc_unicode(self,l): for sp in l.split(','): if '-' in sp: # 範囲 sp = sp.split('-') u1 = int(sp[0],base=16) u2 = int(sp[1],base=16) if u1 > u2: self.err('error: ' + l) while u1 <= u2: self._add_unicode(u1) u1 += 1 else: self._add_unicode(int(sp,base=16)) # 入力テキストを読み込み、Unicode -> グリフ名 def _read_text(self,fname): f = open(fname, 'rt') lines = f.readlines() f.close() self.out = {} for l in lines: l = l.rstrip('\n') if not l: continue if l.startswith('u:'): self._proc_unicode(l[2:].strip()) elif l.startswith('c:'): # 各文字 for ch in l[2:]: if ch != ' ': self._add_unicode(ord(ch)) # 辞書 -> 配列 out = [] for name,dat in self.out.items(): out.append([name,dat]) # タイプ別 if out: if type(out[0][0]) is int: self.gtype = 'id' self.out = sorted(out, key=sort_int) else: self.gtype = 'name' self.out = out # カンマ区切り出力 (数値) def _output_list_int(self,optval): is_cid = ('cid' in optval) # GID のリストにする if 'gid' in optval: out = [] for name,dat in self.out: out.append([self.srclist[name][0], None]) self.out = sorted(out, key=sort_int) # 連続した数値は範囲にする out = [] st = None for name,dat in self.out: if st is None: st = name ed = name else: if name == ed + 1: ed = name else: out.append([st,ed]) st = name ed = name out.append([st,ed]) # 出力 o = '' for st,ed in out: if is_cid: st = '/{0}'.format(st) ed = '/{0}'.format(ed) if st == ed: o += "{0},".format(st) else: o += "{0}-{1},".format(st, ed) print(o[:-1]) # Unicode とグリフ名のリストを出力 def _output_list(self,optval): is_gid = ('gid' in optval) for name,dat in self.out: uc = dat[0] fea = '' if dat[1]: fea = " <" + dat[1] + ">" if is_gid: name = self.srclist[name][0] print('U+{0:X}({1}){2}: {3}'.format(uc, chr(uc), fea, name)) # rotatefont 用の出力 def _output_rotate(self,optval): val = " None {0} {1}".format(optval[0], optval[1]) if self.gtype == 'id': print("0 0 None 0 0") else: print(".notdef .notdef None 0 0") for name,dat in self.out: uc = dat[0] fea = '' if dat[1]: fea = " <" + dat[1] + ">" o = "# U+{0:X} ({1}){2}\n".format(uc, chr(uc), fea) o += "{0} {0}".format(name) o += val print(o) # mergefonts 用のマッピングファイル出力 def _output_merge(self): print('mergefonts') for sname,sdat in self.out: suc = sdat[0] fea = sdat[1] if not fea: # Unicode から if suc in self.dstumap: o = '# U+{0:X} ({1})\n{2} {3}'.format(suc, chr(suc), self.dstumap[suc], sname) print(o) else: print('no mapping: U+{0:X}'.format(suc), file=sys.stderr) else: # GSUB 置換 if (fea in self.dstgsub) and (suc in self.dstumap): dname = self.dstumap[suc] if dname in self.dstgsub[fea]: o = '# U+{0:X} ({1}) <{2}>\n{3} {4}'.format(suc, chr(suc), fea, self.dstgsub[fea][dname], sname) print(o) # 出力 def output(self,fname,proc,optval): # 対象 feature self.gsubfea = [] if 'gsub' in optval: self.gsubfea = optval['gsub'].split(',') # テキスト読み込み self._read_text(fname) if not self.out: return if proc == 'list': self._output_list(optval) elif proc == 'rotate': self._output_rotate(optval['rotate']) elif proc == 'merge': self.read_ginfo(optval['merge'], 'dst') self._output_merge() else: # カンマ区切り if self.gtype == 'id': self._output_list_int(optval) else: o = '' for name,dat in self.out: o += "{0},".format(name) print(o[:-1]) #----- c = ConvName() c.read_ginfo(args[1],'src') c.output(args[0], proc, optval)