├── pictures ├── pic01.png ├── pic02.png └── pic03.png ├── codes ├── configs │ ├── config.json │ ├── sourcehan.json │ └── uvs.json ├── step02.py ├── hpsh.py └── step01.py ├── README-SC.md ├── README.md └── LICENSE.txt /pictures/pic01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuiWonder/EarlySummerSerif/HEAD/pictures/pic01.png -------------------------------------------------------------------------------- /pictures/pic02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuiWonder/EarlySummerSerif/HEAD/pictures/pic02.png -------------------------------------------------------------------------------- /pictures/pic03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuiWonder/EarlySummerSerif/HEAD/pictures/pic03.png -------------------------------------------------------------------------------- /codes/configs/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "fontName": "Early Summer", 3 | "fontNameTC": "初夏", 4 | "fontNameSC": "初夏", 5 | "fontNameJP": "初夏", 6 | "fontVersion": "1.006", 7 | "fontID": "GUIW", 8 | "fontVURL": "https://github.com/GuiWonder/EarlySummerSerif", 9 | "fontCopyright": "© 2023-2025 Early Summer Fonts.", 10 | "fontDesigner": "GuiWonder", 11 | "fontDiscript": "Serif font" 12 | } 13 | -------------------------------------------------------------------------------- /codes/configs/sourcehan.json: -------------------------------------------------------------------------------- 1 | { 2 | "scgl": "䏰懜矒名茗壓懕賲", 3 | "tcgl": "帮㈾㊮㕙㖗㗴㘚㙇㧻㨘㫵㵆㵟㵪㶏㹃㻇㻐㻗㻰㻴䁓䃺䄲䅮䅼䈑䉠䌊䕡䠷䤑䤵䬙䮐䱻䱽丸丽伨偺傦兝凐刏剨咓喒喼嗖嘬埧埾堔填塭尜幵弅归恮憌挈掰揝撮撯斳昋朂柭栃栥棎椕楶檨櫘歅汵沷洯洰浻湱滘潖澬濸牄獊眔瞂矏砉称穨篫篬籈糌聣肏芲蘳蚐蛧蛩蛪蝂蝆螥螩螶蠀觢赹跫躹輙鄋銏鋷闉靟鞏頙駏驖骺鮁鱋黇黫鼃趵玤甏靮玓虳扚魡尦畃圴垹挷梆聛均鈞朓檝濈真顛鷏", 4 | "hcgl": "䶜儥唦尳愲挱撗敹樇櫜渠潃猀獚皾硰穔篎粈粣糔聽螖蟝蠽贕赻鎥陻飺栔棸猐琷鯓羴羼", 5 | "krgl": "燏踛迅邦劘徘摯杓汋灼爟瞬舛虁誹豹非馰龜龝丈乖乳交伴使侮侵便偉偏健偰傑僧全公具兼再冒冓冬刃分判券削勇勉勝勤包化匾半卑博厖叉及史吏吸呀咬咲唐唳啄啓喝喫嘆器圍坪堙塀塌塘墜墨壄复契奓妥姬姿娜娵婦媛媾嫌宵寐寒寧尊尋導尨屣層岈峯巡巨巽帽幃幣平庭廉廊廠延廷建廻弊弱彩忍忭急恝悔情愉慧憊憎懲房所扇扈扉扱抱拒拳挺捨捩掃採援搆搔搨摩撐敏敷文斐斧斲旅晴暑暖曙曜更朗杖松柊栓校梅梗梛梢榻構欄次殺泡派浮海浸消渚港湧湮溝滕滬漢潔澘濦濯灰煙煢煮燿爵父爺牙牚狡猪猶琢環璵甄甦甩畔癒癤盆益矩砑砲硝硬碑磨社祈祉祐祖祝神祢祥禍禎福稧稱穀穢穴穿突竄筵簿籐籾粉精糖紋納級紛終絞絣絳綮総編緩緯練縛縢縫繁繩纛缾署羽翁翌習翔翠翩翫翻翼耀者耕耗聖聡肇肖肩肺胞脈腱臭舜舞艇花苒苣菜著蓮蓴蕣薄藤蘭虐虔虜虞蚊蚌蚤蝇蝙螽衂術衛褊褐視覯訝訟評認誕誤請諏諞諭諮諸謁謄謙講謹負貧貨賆資賓購贈距躍較輸迎近迓返迪迫迭述迷追退送逃逆透逐途通逝速逢連逮進逸遂遇遊運遍過道達違遘遠遣適遭遮遵選遺遼避邃還那邨都鄕鄰采釜鉼銎鍵鎌鎖门降陬隆隊隙隣隻雇雙難雪雰靖静靭靴鞭頌頑頒頻類顧食飢飯飼飽飾養餓館駁駩騰驎驟鬣鬮魔鮗鮫鴉鶹鶾麗麟麻麿黛鼂鼇鼈鼬鼻廊朗𰻞衋蔑骪靡喞" 6 | } -------------------------------------------------------------------------------- /README-SC.md: -------------------------------------------------------------------------------- 1 | [繁體中文](../../) **简体中文** 2 | 3 | # Early Summer Serif 初夏明朝体 4 | 一款接近传统印刷体的中文字体,基于[思源宋体](https://github.com/adobe-fonts/source-han-serif)。 5 | 6 | ## 预览 7 | ![image](./pictures/pic01.png) 8 | ![image](./pictures/pic02.png) 9 | > NOTE: 本字体可通过 locl 特性改变不同的标点符号。 10 | 11 | ## 字重与格式 12 | 包含可变字体,以及 7 种粗细的静态字体,使用 TrueType 和 OpenType 格式。本项目还提供了适合屏幕显示的“屏阅初夏明朝体”。 13 | ![image](./pictures/pic03.png) 14 | 15 | ## 下载字体 16 | 可从本站 [Releases](https://github.com/GuiWonder/EarlySummerSerif/releases) 页面下载字体。 17 | 18 | ## 授权 19 | 遵循 [SIL Open Font License 1.1](./LICENSE.txt)。 20 | 21 | ## 鸣谢 22 | - [思源宋体](https://github.com/adobe-fonts/source-han-serif) 23 | - [Noto CJK fonts](https://github.com/notofonts/noto-cjk) 24 | - [FontTools](https://github.com/fonttools/fonttools) 25 | - [AFDKO](https://github.com/adobe-type-tools/afdko/) 26 | - [FontForge](https://github.com/fontforge/fontforge) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **繁體中文** [简体中文](README-SC.md) 2 | 3 | # Early Summer Serif 初夏明朝體 4 | 一款接近傳統印刷體的中文字型,基於[思源宋體](https://github.com/adobe-fonts/source-han-serif)。 5 | 6 | ## 預覽 7 | ![image](./pictures/pic01.png) 8 | ![image](./pictures/pic02.png) 9 | > NOTE: 本字型可通過 locl 特性改變不同的標點符號。 10 | 11 | ## 字重與格式 12 | 包含可變字體,以及 7 種粗細的靜態字體,使用 TrueType 和 OpenType 格式。本專案還提供了適合螢幕顯示的「屏閱初夏明朝體」。 13 | ![image](./pictures/pic03.png) 14 | 15 | ## 下載字型 16 | 可从本站 [Releases](https://github.com/GuiWonder/EarlySummerSerif/releases) 頁面下載字型。 17 | 18 | ## 授權 19 | 遵循 [SIL Open Font License 1.1](./LICENSE.txt)。 20 | 21 | ## 鳴謝 22 | - [思源宋體](https://github.com/adobe-fonts/source-han-serif) 23 | - [Noto CJK fonts](https://github.com/notofonts/noto-cjk) 24 | - [FontTools](https://github.com/fonttools/fonttools) 25 | - [AFDKO](https://github.com/adobe-type-tools/afdko/) 26 | - [FontForge](https://github.com/fontforge/fontforge) 27 | -------------------------------------------------------------------------------- /codes/step02.py: -------------------------------------------------------------------------------- 1 | from fontTools.varLib.instancer import * 2 | from fontTools.ttLib import newTable 3 | import sys 4 | 5 | def run(infile, outfile, wt): 6 | ttft=TTFont(infile) 7 | insname=dict() 8 | for ins in ttft['fvar'].instances: 9 | name=ttft['name'].getDebugName(ins.subfamilyNameID) 10 | if 'wght' not in ins.coordinates: continue 11 | wght=ins.coordinates['wght'] 12 | insname[name]=wght 13 | ttft.close() 14 | 15 | args=['--no-recalc-timestamp', 16 | '--remove-overlaps', 17 | #'--update-name-table', 18 | '-o', outfile, 19 | infile, 20 | f'wght={insname[wt]}'] 21 | 22 | """Partially instantiate a variable font""" 23 | infile_notuse, axisLimits, options = parseArgs(args) 24 | 25 | log.info("Restricting axes: %s", axisLimits) 26 | 27 | log.info("Loading variable font") 28 | varfont = TTFont( 29 | infile, 30 | recalcTimestamp=options.recalc_timestamp, 31 | recalcBBoxes=options.recalc_bounds, 32 | ) 33 | 34 | isFullInstance = { 35 | axisTag for axisTag, limit in axisLimits.items() if not isinstance(limit, tuple) 36 | }.issuperset(axis.axisTag for axis in varfont["fvar"].axes) 37 | 38 | varfont = instantiateVariableFont( 39 | varfont, 40 | axisLimits, 41 | inplace=True, 42 | optimize=options.optimize, 43 | overlap=options.overlap, 44 | updateFontNames=options.update_name_table, 45 | downgradeCFF2=options.downgrade_cff2, 46 | ) 47 | 48 | suffix = "-instance" if isFullInstance else "-partial" 49 | 50 | log.info("Updating name table") 51 | varfont['name']=fixname(varfont['name'], wt) 52 | if wt in ['Regular', 'Bold']:setrbbb(varfont, wt.lower()) 53 | else:setrbbb(varfont, 'other') 54 | del varfont['STAT'] 55 | 56 | log.info( 57 | "Saving %s font %s", 58 | "instance" if isFullInstance else "partial variable", 59 | outfile, 60 | ) 61 | varfont["head"].yMax = varfont["hhea"].ascender 62 | varfont["head"].yMin = varfont["hhea"].descender 63 | varfont.save(outfile) 64 | 65 | def setrbbb(font, stylename): 66 | assert stylename in {"regular", "bold", "italic", "bold italic", 'other'} 67 | if stylename == "bold": 68 | font["head"].macStyle = 0b01 69 | elif stylename == "bold italic": 70 | font["head"].macStyle = 0b11 71 | elif stylename == "italic": 72 | font["head"].macStyle = 0b10 73 | else: 74 | font["head"].macStyle = 0b00 75 | selection = font["OS/2"].fsSelection 76 | # First clear... 77 | selection &= ~(1 << 0) 78 | selection &= ~(1 << 5) 79 | selection &= ~(1 << 6) 80 | # ...then re-set the bits. 81 | if stylename == "regular": 82 | selection |= 1 << 6 83 | elif stylename == "bold": 84 | selection |= 1 << 5 85 | elif stylename == "italic": 86 | selection |= 1 << 0 87 | elif stylename == "bold italic": 88 | selection |= 1 << 0 89 | selection |= 1 << 5 90 | font["OS/2"].fsSelection = selection 91 | 92 | def fixname(nameobj, wt): 93 | newnane=newTable('name') 94 | for n1 in nameobj.names: 95 | if n1.nameID>128: continue 96 | nstr=str(n1) 97 | if n1.nameID==2 and wt=='Bold': 98 | nstr='Bold' 99 | if n1.nameID in (1, 4, 16):nstr=nstr.replace(' VF', '') 100 | if n1.nameID in (3, 6):nstr=nstr.replace('VF', '') 101 | 102 | if n1.nameID==17 and wt in ['Regular', 'Bold']: continue 103 | if n1.nameID==1 and wt not in ['Regular', 'Bold']: 104 | newnane.setName(nstr, 16, n1.platformID, n1.platEncID, n1.langID) 105 | if n1.nameID==4 or (n1.nameID==1 and wt not in ['Regular', 'Bold']): 106 | nstr+=' '+wt 107 | if wt!='ExtraLight': 108 | if n1.nameID==17: 109 | nstr=wt 110 | if n1.nameID in (3, 6): 111 | nstr=nstr.replace('ExtraLight', wt) 112 | newnane.setName(nstr, n1.nameID, n1.platformID, n1.platEncID, n1.langID) 113 | return newnane 114 | 115 | if __name__=="__main__": 116 | run(sys.argv[1], sys.argv[2], sys.argv[3]) 117 | 118 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 2 | This license is copied below, and is also available with a FAQ at: 3 | http://scripts.sil.org/OFL 4 | 5 | 6 | ----------------------------------------------------------- 7 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 8 | ----------------------------------------------------------- 9 | 10 | PREAMBLE 11 | The goals of the Open Font License (OFL) are to stimulate worldwide 12 | development of collaborative font projects, to support the font creation 13 | efforts of academic and linguistic communities, and to provide a free and 14 | open framework in which fonts may be shared and improved in partnership 15 | with others. 16 | 17 | The OFL allows the licensed fonts to be used, studied, modified and 18 | redistributed freely as long as they are not sold by themselves. The 19 | fonts, including any derivative works, can be bundled, embedded, 20 | redistributed and/or sold with any software provided that any reserved 21 | names are not used by derivative works. The fonts and derivatives, 22 | however, cannot be released under any other type of license. The 23 | requirement for fonts to remain under this license does not apply 24 | to any document created using the fonts or their derivatives. 25 | 26 | DEFINITIONS 27 | "Font Software" refers to the set of files released by the Copyright 28 | Holder(s) under this license and clearly marked as such. This may 29 | include source files, build scripts and documentation. 30 | 31 | "Reserved Font Name" refers to any names specified as such after the 32 | copyright statement(s). 33 | 34 | "Original Version" refers to the collection of Font Software components as 35 | distributed by the Copyright Holder(s). 36 | 37 | "Modified Version" refers to any derivative made by adding to, deleting, 38 | or substituting -- in part or in whole -- any of the components of the 39 | Original Version, by changing formats or by porting the Font Software to a 40 | new environment. 41 | 42 | "Author" refers to any designer, engineer, programmer, technical 43 | writer or other person who contributed to the Font Software. 44 | 45 | PERMISSION & CONDITIONS 46 | Permission is hereby granted, free of charge, to any person obtaining 47 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 48 | redistribute, and sell modified and unmodified copies of the Font 49 | Software, subject to the following conditions: 50 | 51 | 1) Neither the Font Software nor any of its individual components, 52 | in Original or Modified Versions, may be sold by itself. 53 | 54 | 2) Original or Modified Versions of the Font Software may be bundled, 55 | redistributed and/or sold with any software, provided that each copy 56 | contains the above copyright notice and this license. These can be 57 | included either as stand-alone text files, human-readable headers or 58 | in the appropriate machine-readable metadata fields within text or 59 | binary files as long as those fields can be easily viewed by the user. 60 | 61 | 3) No Modified Version of the Font Software may use the Reserved Font 62 | Name(s) unless explicit written permission is granted by the corresponding 63 | Copyright Holder. This restriction only applies to the primary font name as 64 | presented to the users. 65 | 66 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 67 | Software shall not be used to promote, endorse or advertise any 68 | Modified Version, except to acknowledge the contribution(s) of the 69 | Copyright Holder(s) and the Author(s) or with their explicit written 70 | permission. 71 | 72 | 5) The Font Software, modified or unmodified, in part or in whole, 73 | must be distributed entirely under this license, and must not be 74 | distributed under any other license. The requirement for fonts to 75 | remain under this license does not apply to any document created 76 | using the Font Software. 77 | 78 | TERMINATION 79 | This license becomes null and void if any of the above conditions are 80 | not met. 81 | 82 | DISCLAIMER 83 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 84 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 85 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 86 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 87 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 88 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 89 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 90 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 91 | OTHER DEALINGS IN THE FONT SOFTWARE. 92 | -------------------------------------------------------------------------------- /codes/hpsh.py: -------------------------------------------------------------------------------- 1 | from fontTools.ttLib import TTFont, newTable 2 | 3 | def setcg(cmap, code, glyf): 4 | for table in cmap.tables: 5 | if (table.format==4 and code<=0xFFFF) or table.format==12 or code in table.cmap: 6 | table.cmap[code]=glyf 7 | 8 | def mkname(cfg, oldname, locn, ithw='', isvf=False): 9 | fpsn=oldname.getDebugName(6) 10 | if locn: locn=' '+locn 11 | if 'Screen' in fpsn: return scstname(oldname) 12 | else: return nfname(cfg, oldname, locn, ithw, isvf) 13 | 14 | def nfname(cfg, oldname, locn, ithw='', isvf=False): 15 | fpsn=oldname.getDebugName(6) 16 | wt=oldname.getDebugName(17) 17 | if not wt: wt=oldname.getDebugName(2) 18 | isit='Italic' in wt or 'it' in ithw.lower() 19 | wt=wt.replace('Italic', '').strip() 20 | if not wt: wt='Regular' 21 | ishw='HW' in fpsn or 'hw' in ithw.lower() 22 | itml, itm, hwm=str(), str(), str() 23 | if ishw: hwm=' HW' 24 | if isit: itml, itm=' Italic', 'It' 25 | locadd=locn.strip() 26 | if locadd=='ST': 27 | loctc=' 簡轉繁' 28 | locsc=' 简转繁' 29 | else: 30 | loctc=locsc=locadd 31 | vfn=str() 32 | if 'VF' in fpsn: vfn=' VF' 33 | if 'Sans' in fpsn: 34 | fmlen=cfg['fontName']+' Sans'+hwm+locn+vfn 35 | fmlsc=cfg['fontNameSC']+'黑体'+locsc+hwm+vfn 36 | fmltc=cfg['fontNameTC']+'黑體'+loctc+hwm+vfn 37 | fmlja=cfg['fontNameJP']+'ゴシック'+loctc+hwm+vfn 38 | elif 'Serif' in fpsn: 39 | fmlen=cfg['fontName']+' Serif'+hwm+locn+vfn 40 | fmlsc=cfg['fontNameSC']+'明朝体'+locsc+hwm+vfn 41 | fmltc=cfg['fontNameTC']+'明朝體'+loctc+hwm+vfn 42 | fmlja=cfg['fontNameJP']+'明朝'+loctc+hwm+vfn 43 | elif 'Mono' in fpsn: 44 | fmlen=cfg['fontName']+' Mono'+hwm+locn+vfn 45 | fmlsc=cfg['fontNameSC']+'等宽'+locsc+hwm+vfn 46 | fmltc=cfg['fontNameTC']+'等寬'+loctc+hwm+vfn 47 | fmlja=cfg['fontNameJP']+'等幅'+loctc+hwm+vfn 48 | #elif 'Rounded' in fpsn: 49 | # fmlen=cfg['fontName']+' Rounded'+hwm+locn+vfn 50 | # fmlsc=cfg['fontNameSC']+'圆角'+locsc+hwm+vfn 51 | # fmltc=cfg['fontNameTC']+'圓角'+loctc+hwm+vfn 52 | else: raise 53 | nameen, namesc, nametc, nameja=fmlen, fmlsc, fmltc, fmlja 54 | if not isvf and wt not in ('Regular', 'Bold'): 55 | nameen+=' '+wt 56 | namesc+=' '+wt 57 | nametc+=' '+wt 58 | nameja+=' '+wt 59 | #if wt=='Bold': 60 | if wt in ('Regular', 'Bold') and not (isit and wt=='Regular'): 61 | fullen=nameen+' '+wt+itml 62 | fullsc=namesc+' '+wt+itml 63 | fulltc=nametc+' '+wt+itml 64 | fullja=nameja+' '+wt+itml 65 | else: 66 | fullen=nameen+itml 67 | fullsc=namesc+itml 68 | fulltc=nametc+itml 69 | fullja=nameja+itml 70 | if isit: 71 | if wt=='Bold': subfml='Bold Italic' 72 | else: subfml='Italic' 73 | elif wt=='Bold': 74 | subfml='Bold' 75 | else: 76 | subfml='Regular' 77 | psName=fmlen.replace(' ', '')+'-'+fpsn.split('-')[-1].replace('It', '')+itm 78 | uniqID=cfg['fontVersion']+';'+cfg['fontID'].strip()+';'+psName 79 | 80 | newnane=newTable('name') 81 | newnane.setName(cfg['fontCopyright'], 0, 3, 1, 1033) 82 | newnane.setName(uniqID, 3, 3, 1, 1033) 83 | newnane.setName('Version '+cfg['fontVersion'], 5, 3, 1, 1033) 84 | newnane.setName(psName, 6, 3, 1, 1033) 85 | newnane.setName(cfg['fontDesigner'], 9, 3, 1, 1033) 86 | newnane.setName(cfg['fontDiscript'], 10, 3, 1, 1033) 87 | newnane.setName(cfg['fontVURL'], 11, 3, 1, 1033) 88 | newnane.setName(oldname.getDebugName(13), 13, 3, 1, 1033) 89 | newnane.setName(oldname.getDebugName(14), 14, 3, 1, 1033) 90 | 91 | newnane.setName(nameen, 1, 3, 1, 1033) 92 | newnane.setName(fullen, 4, 3, 1, 1033) 93 | if not isvf and not wt in ('Regular', 'Bold'): 94 | newnane.setName(fmlen, 16, 3, 1, 1033) 95 | 96 | for lanid in (1028, 3076, 5124): 97 | newnane.setName(nametc, 1, 3, 1, lanid) 98 | newnane.setName(fulltc, 4, 3, 1, lanid) 99 | if not isvf and not wt in ('Regular', 'Bold'): 100 | newnane.setName(fmltc, 16, 3, 1, lanid) 101 | 102 | for lanid in (2052, 4100): 103 | newnane.setName(namesc, 1, 3, 1, lanid) 104 | newnane.setName(fullsc, 4, 3, 1, lanid) 105 | if not isvf and not wt in ('Regular', 'Bold'): 106 | newnane.setName(fmlsc, 16, 3, 1, lanid) 107 | 108 | newnane.setName(nameja, 1, 3, 1, 1041) 109 | newnane.setName(fullja, 4, 3, 1, 1041) 110 | if not isvf and not wt in ('Regular', 'Bold'): 111 | newnane.setName(fmlja, 16, 3, 1, 1041) 112 | 113 | for lanid in (1033, 1028, 3076, 5124, 2052, 4100, 1041): 114 | newnane.setName(subfml, 2, 3, 1, lanid) 115 | if wt not in ('Regular', 'Bold'): 116 | newnane.setName(wt+itml, 17, 3, 1, lanid) 117 | 118 | if isvf: 119 | oldnm=fpsn.split('-')[0] 120 | newnm=fmlen.replace(' ', '') 121 | for n1 in oldname.names: 122 | if n1.nameID<255: continue 123 | nstr=str(n1).replace(oldnm, newnm) 124 | newnane.setName(nstr, n1.nameID, n1.platformID, n1.platEncID, n1.langID) 125 | 126 | return newnane 127 | 128 | def scstname(oldname): 129 | newnane=newTable('name') 130 | for nm in oldname.names: 131 | nstr=str(nm) 132 | if 'Serif Screen' in nstr: 133 | nstr=nstr.replace('Serif Screen', 'Serif ST Screen') 134 | elif 'Serif-Screen' in nstr: 135 | nstr=nstr.replace('Serif-Screen', 'SerifST-Screen') 136 | elif '屏閲' in nstr: 137 | nstr+=' 簡轉繁' 138 | elif '屏阅' in nstr: 139 | nstr+=' 简转繁' 140 | newnane.setName(nstr, nm.nameID, nm.platformID, nm.platEncID, nm.langID) 141 | return newnane 142 | -------------------------------------------------------------------------------- /codes/configs/uvs.json: -------------------------------------------------------------------------------- 1 | { 2 | "丈": "E0101", 3 | "与": "E0101", 4 | "丰": "E0101", 5 | "主": "E0101", 6 | "乳": "E0101", 7 | "亡": "E0101", 8 | "交": "E0101", 9 | "伴": "E0101", 10 | "住": "E0101", 11 | "併": "E0101", 12 | "使": "E0101", 13 | "侮": "E0101", 14 | "侵": "E0101", 15 | "便": "E0101", 16 | "偉": "E0101", 17 | "偏": "E0101", 18 | "健": "E0101", 19 | "傑": "E0101", 20 | "僊": "E0102", 21 | "僧": "E0101", 22 | "免": "E0101", 23 | "兎": "E0101", 24 | "兔": "E0100", 25 | "全": "E0101", 26 | "公": "E0101", 27 | "具": "E0101", 28 | "兼": "E0101", 29 | "冉": "E0101", 30 | "再": "E0100", 31 | "冤": "E0101", 32 | "冬": "E0100", 33 | "凋": "E0101", 34 | "刃": "E0101", 35 | "分": "E0101", 36 | "判": "E0101", 37 | "券": "E0101", 38 | "削": "E0101", 39 | "前": "E0101", 40 | "剤": "E0101", 41 | "割": "E0100", 42 | "勉": "E0100", 43 | "勗": "E0101", 44 | "勝": "E0101", 45 | "勤": "E0101", 46 | "包": "E0101", 47 | "化": "E0101", 48 | "半": "E0101", 49 | "卑": "E0100", 50 | "博": "E0101", 51 | "危": "E0101", 52 | "叉": "E0100", 53 | "及": "E0101", 54 | "叠": "E0101", 55 | "史": "E0101", 56 | "吏": "E0101", 57 | "吸": "E0101", 58 | "呈": "E0101", 59 | "周": "E0100", 60 | "咬": "E0100", 61 | "咲": "E0101", 62 | "唐": "E0101", 63 | "唳": "E0102", 64 | "啄": "E0101", 65 | "啓": "E0101", 66 | "啡": "E0101", 67 | "喝": "E0101", 68 | "喫": "E0101", 69 | "嗤": "E0102", 70 | "嘆": "E0100", 71 | "器": "E0101", 72 | "嚮": "E0101", 73 | "囮": "E0101", 74 | "坪": "E0101", 75 | "城": "E0100", 76 | "堋": "E0101", 77 | "堙": "E0101", 78 | "塀": "E0101", 79 | "塘": "E0101", 80 | "塚": "E0101", 81 | "墜": "E0101", 82 | "墨": "E0100", 83 | "契": "E0101", 84 | "奓": "E0101", 85 | "妄": "E0101", 86 | "妥": "E0101", 87 | "姬": "E0101", 88 | "姿": "E0101", 89 | "娜": "E0101", 90 | "婦": "E0101", 91 | "媛": "E0101", 92 | "媾": "E0101", 93 | "嫂": "E0101", 94 | "嫌": "E0101", 95 | "孳": "E0100", 96 | "孼": "E0100", 97 | "害": "E0102", 98 | "宵": "E0101", 99 | "寃": "E0101", 100 | "寒": "E0101", 101 | "寧": "E0100", 102 | "尊": "E0102", 103 | "尋": "E0101", 104 | "導": "E0101", 105 | "尨": "E0101", 106 | "層": "E0101", 107 | "屮": "E0100", 108 | "崩": "E0101", 109 | "巡": "E0101", 110 | "巨": "E0101", 111 | "巽": "E0100", 112 | "帽": "E0101", 113 | "幃": "E0101", 114 | "幣": "E0101", 115 | "平": "E0101", 116 | "庭": "E0101", 117 | "廉": "E0100", 118 | "廊": "E0102", 119 | "廋": "E0100", 120 | "廐": "E0101", 121 | "廠": "E0101", 122 | "延": "E0102", 123 | "廷": "E0101", 124 | "建": "E0101", 125 | "廻": "E0100", 126 | "弊": "E0101", 127 | "弭": "E0100", 128 | "弱": "E0101", 129 | "弸": "E0101", 130 | "彐": "E0100", 131 | "形": "E0101", 132 | "彩": "E0101", 133 | "彫": "E0101", 134 | "往": "E0101", 135 | "徘": "E0100", 136 | "忍": "E0101", 137 | "忘": "E0101", 138 | "忙": "E0101", 139 | "急": "E0101", 140 | "恐": "E0101", 141 | "悔": "E0100", 142 | "悗": "E0101", 143 | "情": "E0101", 144 | "愉": "E0101", 145 | "慌": "E0101", 146 | "慎": "E0101", 147 | "慧": "E0101", 148 | "慨": "E0101", 149 | "憎": "E0100", 150 | "憲": "E0101", 151 | "懲": "E0102", 152 | "成": "E0101", 153 | "房": "E0101", 154 | "所": "E0101", 155 | "扇": "E0101", 156 | "扈": "E0101", 157 | "扨": "E0100", 158 | "扱": "E0101", 159 | "抱": "E0101", 160 | "拒": "E0101", 161 | "拳": "E0101", 162 | "挺": "E0100", 163 | "捨": "E0101", 164 | "捩": "E0101", 165 | "掃": "E0101", 166 | "採": "E0101", 167 | "援": "E0101", 168 | "搆": "E0101", 169 | "搜": "E0101", 170 | "搨": "E0101", 171 | "摩": "E0101", 172 | "擶": "E0101", 173 | "敏": "E0100", 174 | "敞": "E0100", 175 | "文": "E0101", 176 | "斉": "E0101", 177 | "斎": "E0101", 178 | "斐": "E0101", 179 | "斜": "E0101", 180 | "斧": "E0100", 181 | "旅": "E0101", 182 | "旡": "E0101", 183 | "既": "E0100", 184 | "晟": "E0103", 185 | "晩": "E0101", 186 | "晴": "E0100", 187 | "暑": "E0101", 188 | "暖": "E0101", 189 | "曁": "E0102", 190 | "曙": "E0101", 191 | "曜": "E0101", 192 | "更": "E0101", 193 | "曵": "E0101", 194 | "最": "E0101", 195 | "朋": "E0101", 196 | "服": "E0101", 197 | "朕": "E0102", 198 | "朗": "E0102", 199 | "朝": "E0101", 200 | "杓": "E0100", 201 | "杖": "E0100", 202 | "松": "E0101", 203 | "枦": "E0101", 204 | "柊": "E0101", 205 | "柱": "E0101", 206 | "栓": "E0101", 207 | "栟": "E0101", 208 | "校": "E0101", 209 | "桒": "E0100", 210 | "梅": "E0100", 211 | "梗": "E0100", 212 | "梛": "E0101", 213 | "梢": "E0101", 214 | "棚": "E0101", 215 | "楞": "E0101", 216 | "榻": "E0101", 217 | "構": "E0101", 218 | "槪": "E0101", 219 | "欝": "E0101", 220 | "次": "E0101", 221 | "欤": "E0101", 222 | "殺": "E0100", 223 | "永": "E0101", 224 | "氺": "E0101", 225 | "沪": "E0101", 226 | "沿": "E0102", 227 | "泡": "E0101", 228 | "注": "E0101", 229 | "派": "E0102", 230 | "浩": "E0101", 231 | "浮": "E0101", 232 | "海": "E0100", 233 | "浸": "E0101", 234 | "消": "E0101", 235 | "済": "E0101", 236 | "渚": "E0100", 237 | "港": "E0101", 238 | "湮": "E0101", 239 | "溝": "E0101", 240 | "滕": "E0101", 241 | "滛": "E0101", 242 | "滬": "E0101", 243 | "漢": "E0101", 244 | "漾": "E0101", 245 | "潑": "E0101", 246 | "潤": "E0100", 247 | "潮": "E0100", 248 | "澘": "E0101", 249 | "濯": "E0101", 250 | "瀛": "E0101", 251 | "灰": "E0101", 252 | "灼": "E0100", 253 | "炭": "E0101", 254 | "煎": "E0101", 255 | "煙": "E0101", 256 | "煢": "E0101", 257 | "煮": "E0100", 258 | "燿": "E0101", 259 | "爵": "E0102", 260 | "父": "E0101", 261 | "爺": "E0100", 262 | "狡": "E0100", 263 | "猪": "E0100", 264 | "猶": "E0102", 265 | "率": "E0101", 266 | "珎": "E0101", 267 | "琢": "E0100", 268 | "環": "E0101", 269 | "甄": "E0101", 270 | "甕": "E0100", 271 | "甦": "E0100", 272 | "畔": "E0101", 273 | "癒": "E0101", 274 | "皓": "E0101", 275 | "盆": "E0101", 276 | "益": "E0101", 277 | "盛": "E0101", 278 | "盲": "E0101", 279 | "瞍": "E0101", 280 | "矩": "E0101", 281 | "砲": "E0101", 282 | "硝": "E0101", 283 | "硬": "E0101", 284 | "硼": "E0101", 285 | "碑": "E0100", 286 | "磨": "E0101", 287 | "礼": "E0101", 288 | "社": "E0101", 289 | "祈": "E0100", 290 | "祉": "E0101", 291 | "祐": "E0100", 292 | "祖": "E0101", 293 | "祝": "E0100", 294 | "神": "E0100", 295 | "祥": "E0100", 296 | "禍": "E0100", 297 | "禎": "E0101", 298 | "福": "E0101", 299 | "程": "E0101", 300 | "穀": "E0100", 301 | "穠": "E0101", 302 | "穴": "E0101", 303 | "突": "E0101", 304 | "窕": "E0100", 305 | "筑": "E0101", 306 | "筵": "E0100", 307 | "箙": "E0101", 308 | "節": "E0101", 309 | "篇": "E0101", 310 | "築": "E0101", 311 | "籍": "E0101", 312 | "籐": "E0101", 313 | "籘": "E0101", 314 | "籩": "E0100", 315 | "籾": "E0101", 316 | "粉": "E0101", 317 | "粐": "E0101", 318 | "精": "E0100", 319 | "糖": "E0100", 320 | "紋": "E0101", 321 | "納": "E0101", 322 | "級": "E0101", 323 | "紛": "E0101", 324 | "終": "E0101", 325 | "絞": "E0101", 326 | "絣": "E0101", 327 | "綟": "E0101", 328 | "綮": "E0101", 329 | "網": "E0101", 330 | "総": "E0101", 331 | "編": "E0101", 332 | "緩": "E0101", 333 | "緯": "E0101", 334 | "練": "E0100", 335 | "縉": "E0100", 336 | "縛": "E0101", 337 | "縢": "E0102", 338 | "縫": "E0101", 339 | "繁": "E0101", 340 | "繃": "E0101", 341 | "缾": "E0101", 342 | "署": "E0100", 343 | "羀": "E0101", 344 | "羽": "E0100", 345 | "翁": "E0102", 346 | "翔": "E0101", 347 | "翠": "E0101", 348 | "翩": "E0101", 349 | "翻": "E0101", 350 | "耀": "E0101", 351 | "者": "E0101", 352 | "耕": "E0101", 353 | "耗": "E0101", 354 | "聡": "E0101", 355 | "肇": "E0101", 356 | "肖": "E0101", 357 | "肞": "E0101", 358 | "肩": "E0101", 359 | "肺": "E0101", 360 | "胞": "E0101", 361 | "脆": "E0101", 362 | "脈": "E0102", 363 | "脉": "E0101", 364 | "腰": "E0101", 365 | "腱": "E0100", 366 | "臭": "E0101", 367 | "舛": "E0101", 368 | "舮": "E0101", 369 | "船": "E0101", 370 | "艇": "E0101", 371 | "花": "E0101", 372 | "苒": "E0101", 373 | "苣": "E0101", 374 | "荒": "E0101", 375 | "荵": "E0101", 376 | "莽": "E0101", 377 | "菜": "E0101", 378 | "菟": "E0102", 379 | "萢": "E0101", 380 | "著": "E0101", 381 | "蓴": "E0101", 382 | "蕝": "E0101", 383 | "蕣": "E0101", 384 | "薇": "E0101", 385 | "藤": "E0101", 386 | "蘒": "E0101", 387 | "蘭": "E0101", 388 | "虐": "E0101", 389 | "虔": "E0100", 390 | "虜": "E0100", 391 | "虞": "E0101", 392 | "蚊": "E0101", 393 | "蝙": "E0101", 394 | "螽": "E0101", 395 | "蟒": "E0102", 396 | "蠎": "E0101", 397 | "衂": "E0101", 398 | "術": "E0101", 399 | "衛": "E0101", 400 | "衷": "E0101", 401 | "褊": "E0101", 402 | "褐": "E0101", 403 | "要": "E0101", 404 | "覆": "E0101", 405 | "覇": "E0101", 406 | "視": "E0101", 407 | "覯": "E0101", 408 | "訟": "E0101", 409 | "評": "E0101", 410 | "認": "E0101", 411 | "誕": "E0102", 412 | "誠": "E0101", 413 | "誤": "E0101", 414 | "誹": "E0100", 415 | "調": "E0101", 416 | "請": "E0101", 417 | "諛": "E0101", 418 | "諞": "E0101", 419 | "諭": "E0100", 420 | "諮": "E0101", 421 | "諸": "E0100", 422 | "謁": "E0100", 423 | "謄": "E0101", 424 | "謙": "E0101", 425 | "講": "E0101", 426 | "謹": "E0100", 427 | "譚": "E0101", 428 | "譜": "E0101", 429 | "豹": "E0100", 430 | "負": "E0102", 431 | "貧": "E0101", 432 | "貨": "E0101", 433 | "資": "E0101", 434 | "賊": "E0102", 435 | "賓": "E0100", 436 | "購": "E0102", 437 | "贈": "E0101", 438 | "赧": "E0100", 439 | "起": "E0101", 440 | "距": "E0101", 441 | "躍": "E0101", 442 | "較": "E0101", 443 | "輸": "E0101", 444 | "轄": "E0101", 445 | "込": "E0101", 446 | "迎": "E0101", 447 | "近": "E0101", 448 | "返": "E0101", 449 | "迪": "E0101", 450 | "迫": "E0101", 451 | "迭": "E0101", 452 | "迯": "E0101", 453 | "述": "E0101", 454 | "迷": "E0101", 455 | "追": "E0101", 456 | "退": "E0101", 457 | "送": "E0101", 458 | "逃": "E0102", 459 | "逆": "E0101", 460 | "透": "E0101", 461 | "逐": "E0101", 462 | "途": "E0101", 463 | "通": "E0101", 464 | "逝": "E0101", 465 | "速": "E0101", 466 | "造": "E0101", 467 | "連": "E0100", 468 | "逮": "E0101", 469 | "週": "E0101", 470 | "進": "E0101", 471 | "逸": "E0101", 472 | "遂": "E0102", 473 | "遇": "E0101", 474 | "遊": "E0101", 475 | "運": "E0101", 476 | "遍": "E0101", 477 | "過": "E0101", 478 | "道": "E0101", 479 | "達": "E0101", 480 | "遘": "E0101", 481 | "遠": "E0101", 482 | "遣": "E0101", 483 | "遭": "E0101", 484 | "遮": "E0101", 485 | "遵": "E0101", 486 | "遷": "E0103", 487 | "選": "E0101", 488 | "遺": "E0101", 489 | "遼": "E0101", 490 | "避": "E0101", 491 | "邃": "E0101", 492 | "還": "E0101", 493 | "那": "E0101", 494 | "邨": "E0101", 495 | "都": "E0100", 496 | "酷": "E0101", 497 | "醱": "E0101", 498 | "采": "E0101", 499 | "釜": "E0100", 500 | "釼": "E0100", 501 | "鉛": "E0101", 502 | "鋩": "E0101", 503 | "錵": "E0101", 504 | "鍵": "E0100", 505 | "鎌": "E0101", 506 | "鎖": "E0101", 507 | "鎮": "E0101", 508 | "降": "E0101", 509 | "隆": "E0101", 510 | "隊": "E0101", 511 | "隙": "E0100", 512 | "隣": "E0101", 513 | "隧": "E0100", 514 | "隲": "E0100", 515 | "隻": "E0101", 516 | "雅": "E0100", 517 | "雇": "E0101", 518 | "雙": "E0101", 519 | "雚": "E0100", 520 | "難": "E0100", 521 | "雪": "E0101", 522 | "雰": "E0101", 523 | "霡": "E0101", 524 | "靖": "E0101", 525 | "静": "E0101", 526 | "靭": "E0102", 527 | "靱": "E0100", 528 | "靴": "E0101", 529 | "鞭": "E0100", 530 | "響": "E0103", 531 | "頌": "E0101", 532 | "頑": "E0101", 533 | "頒": "E0101", 534 | "頻": "E0100", 535 | "類": "E0100", 536 | "顧": "E0101", 537 | "顳": "E0100", 538 | "食": "E0101", 539 | "飢": "E0101", 540 | "飯": "E0100", 541 | "飼": "E0101", 542 | "飽": "E0101", 543 | "飾": "E0101", 544 | "養": "E0101", 545 | "餓": "E0101", 546 | "館": "E0101", 547 | "駁": "E0100", 548 | "駐": "E0101", 549 | "騰": "E0101", 550 | "髯": "E0101", 551 | "鬘": "E0101", 552 | "鬮": "E0101", 553 | "魔": "E0101", 554 | "魘": "E0101", 555 | "鮗": "E0101", 556 | "鮫": "E0100", 557 | "鯛": "E0101", 558 | "鱈": "E0101", 559 | "鵬": "E0101", 560 | "麗": "E0101", 561 | "麪": "E0101", 562 | "麭": "E0101", 563 | "麻": "E0101", 564 | "麿": "E0101", 565 | "黛": "E0101", 566 | "鼈": "E0102", 567 | "鼻": "E0101", 568 | "龜": "E0101", 569 | "龝": "E0101" 570 | } -------------------------------------------------------------------------------- /codes/step01.py: -------------------------------------------------------------------------------- 1 | import os, json, sys 2 | from fontTools.ttLib import TTFont, newTable 3 | from hpsh import * 4 | 5 | pydir=os.path.abspath(os.path.dirname(__file__)) 6 | cfg=json.load(open(os.path.join(pydir, 'configs/config.json'), 'r', encoding='utf-8')) 7 | 8 | def glfrtxt(txt): 9 | cmap=font.getBestCmap() 10 | glys=list() 11 | for ch in txt: 12 | if ord(ch) in cmap and cmap[ord(ch)] not in glys: 13 | glys.append(cmap[ord(ch)]) 14 | return glys 15 | def glyrepl(repdic): 16 | for table in font["cmap"].tables: 17 | for cd in table.cmap: 18 | if table.cmap[cd] in repdic: 19 | table.cmap[cd]=repdic[table.cmap[cd]] 20 | print('Remapping', chr(cd)) 21 | def locllki(ftgsub, lan): 22 | ftl, lkl=list(), list() 23 | for sr in ftgsub.table.ScriptList.ScriptRecord: 24 | for lsr in sr.Script.LangSysRecord: 25 | if lsr.LangSysTag.strip()==lan: 26 | ftl+=lsr.LangSys.FeatureIndex 27 | for ki in ftl: 28 | ftg=ftgsub.table.FeatureList.FeatureRecord[ki].FeatureTag 29 | if ftg=='locl': 30 | lkl+=ftgsub.table.FeatureList.FeatureRecord[ki].Feature.LookupListIndex 31 | return list(dict.fromkeys(lkl)) 32 | def getloclk(ckfont, lan): 33 | locdics=list() 34 | for lki in locllki(ckfont["GSUB"], lan): 35 | locrpl=dict() 36 | for st in ckfont["GSUB"].table.LookupList.Lookup[lki].SubTable: 37 | if st.LookupType==7 and st.ExtSubTable.LookupType==1: 38 | tabl=st.ExtSubTable.mapping 39 | elif st.LookupType==1: 40 | tabl=st.mapping 41 | for g1 in tabl: 42 | locrpl[g1]=tabl[g1] 43 | locdics.append(locrpl) 44 | return locdics 45 | def glfrloc(gl, loclk): 46 | for dc in loclk: 47 | if gl in dc: return dc[gl] 48 | 49 | def locglrpl(newmap): 50 | locgls=dict() 51 | shset=json.load(open(os.path.join(pydir, 'configs/sourcehan.json'), 'r', encoding='utf-8')) 52 | krgl, scgl, tcgl, hcgl=glfrtxt(shset['krgl']), glfrtxt(shset['scgl']), glfrtxt(shset['tcgl']), glfrtxt(shset['hcgl']) 53 | for glloc in ((krgl, lockor), (scgl, loczhs), (tcgl, loczht), (hcgl, loczhh)): 54 | for g1 in glloc[0]: 55 | assert g1 not in locgls, g1 56 | g2=glfrloc(g1, glloc[1]) 57 | if g2: locgls[g1]=g2 58 | cmap=font.getBestCmap() 59 | for cd in cmap: 60 | if cmap[cd] in locgls: 61 | assert cd not in newmap, chr(cd) 62 | newmap[cd]=locgls[cmap[cd]] 63 | 64 | def setuvs(newmap, uvdic): 65 | uvcfg=json.load(open(os.path.join(pydir, 'configs/uvs.json'), 'r', encoding='utf-8')) 66 | tv=dict() 67 | for ch in uvcfg.keys(): 68 | tv[ord(ch)]=int(uvcfg[ch], 16) 69 | for k in uvdic.keys(): 70 | if k in tv and tv[k] in uvdic[k]: 71 | g=uvdic[k][tv[k]] 72 | if k in newmap: 73 | if newmap[k]==g: continue 74 | else: raise RuntimeError(chr(k), newmap[k], g) 75 | newmap[k]=g 76 | def glfruv(fontob, ch, uv): 77 | cmapob=fontob.getBestCmap() 78 | for table in fontob["cmap"].tables: 79 | if table.format==14 and uv in table.uvsDict: 80 | for cg in table.uvsDict[uv]: 81 | if cg[0]==ord(ch): 82 | if cg[1]==None: return cmapob[cg[0]] 83 | else: return cg[1] 84 | raise RuntimeError(ch) 85 | def ckdlg(): 86 | rplg=dict() 87 | for ch in '成': 88 | rplg[glfruv(font, ch, 0xE0100)]=glfruv(font, ch, 0xE0101) 89 | dllk=set() 90 | for ki in font["GSUB"].table.FeatureList.FeatureRecord: 91 | if ki.FeatureTag=='dlig': dllk.update(ki.Feature.LookupListIndex) 92 | for i in dllk: 93 | for st in font["GSUB"].table.LookupList.Lookup[i].SubTable: 94 | if st.LookupType==7: stbl=st.ExtSubTable 95 | else: stbl=st 96 | if stbl.LookupType!=4: continue 97 | for lgg in list(stbl.ligatures): 98 | for lg in list(stbl.ligatures[lgg]): 99 | for ilin in range(len(lg.Component)): 100 | if lg.Component[ilin] in rplg: 101 | lg.Component[ilin]=rplg[lg.Component[ilin]] 102 | def locvar(newmap): 103 | cmap=font.getBestCmap() 104 | jpvch=[('𰰨', '芲'), ('𩑠', '頙'), ('鄉', '鄕'), ('唧', '喞'), ('概', '槪'), ('𥄳', '眔')] 105 | radic=[('⽉', '月'), ('⻁', '虎'), ('⾳', '音'), ('⿓', '龍'), ('⼾', '戶')] 106 | for chs in jpvch+radic: 107 | if ord(chs[1]) in cmap: 108 | c=ord(chs[0]) 109 | assert c not in newmap, chr(c) 110 | newmap[c]=cmap[ord(chs[1])] 111 | locscv=[('𫜹', '彐'), ('𣽽', '潸')] 112 | for lv1 in locscv: 113 | if ord(lv1[1]) in cmap: 114 | c=ord(lv1[0]) 115 | assert c not in newmap, chr(c) 116 | gv2=glfrloc(cmap[ord(lv1[1])], loczhs) 117 | if gv2: 118 | newmap[c]=gv2 119 | 120 | def uvsvar(newmap, uvdic): 121 | uvsmul=[('⺼', '月', 'E0100'), ('𱍐', '示', 'E0100'), ('䶹', '屮', 'E0101'), ('𠾖', '器', 'E0100'), ('𡐨', '壄', 'E0100'), ('𤥨', '琢', 'E0101'), ('𦤀', '臭', 'E0100'), ('𨺓', '隆', 'E0100'), ('𫜸', '叱', 'E0101'), ('廄', '廏', 'E0101'), ('溉', '漑', 'E0102')] 122 | for uvm in uvsmul: 123 | u1, u2, usel=ord(uvm[0]), ord(uvm[1]), int(uvm[2], 16) 124 | if u2 in uvdic and usel in uvdic[u2]: 125 | assert u1 not in newmap, chr(u1) 126 | newmap[u1]=uvdic[u2][usel] 127 | 128 | def rmlk(font, tbnm, i): 129 | font[tbnm].table.LookupList.Lookup.pop(i) 130 | for ki in font[tbnm].table.FeatureList.FeatureRecord: 131 | newft=list() 132 | for j in ki.Feature.LookupListIndex: 133 | if j>i: newft.append(j-1) 134 | elif ji: 142 | sbrcd.LookupListIndex-=1 143 | def rmft(font, tbnm, i): 144 | font[tbnm].table.FeatureList.FeatureRecord.pop(i) 145 | for sr in font[tbnm].table.ScriptList.ScriptRecord: 146 | newdl=list() 147 | for j in sr.Script.DefaultLangSys.FeatureIndex: 148 | if j>i: newdl.append(j-1) 149 | elif ji: newln.append(j-1) 155 | elif j