├── README.md ├── STERA-Std ├── plugin.png ├── plugin.py └── plugin.xml ├── STERA-Upload.zip └── STERA-X ├── STERAEPUB ├── __init__.py ├── bookenv_core.py ├── bookenv_dict.py ├── build_core.py ├── config │ ├── mime.json │ ├── regexp.json │ ├── t2s.json │ └── tag.json ├── cpsimg_core.py ├── gui_core.py ├── materials │ ├── ch1.ttf │ ├── ch2.ttf │ ├── ch3.ttf │ ├── cont.ttf │ ├── ctt1.ttf │ ├── ctt2.ttf │ ├── default.css │ ├── emoji.ttf │ ├── int.ttf │ ├── logo.png │ ├── main.ttf │ ├── mes.ttf │ ├── necessary.css │ ├── note.png │ ├── note.ttf │ ├── script.js │ ├── sum.ttf │ └── sym.ttf ├── regrex_core.py ├── regrex_dict.py ├── subfont_core.py ├── t2stext_core.py ├── t2stext_dict.py ├── userpanel.ui └── userpanel_ui.py ├── fontTools ├── __init__.py ├── __main__.py ├── afmLib.py ├── agl.py ├── cffLib │ ├── __init__.py │ ├── specializer.py │ └── width.py ├── colorLib │ ├── __init__.py │ ├── builder.py │ ├── errors.py │ ├── geometry.py │ ├── table_builder.py │ └── unbuilder.py ├── config │ └── __init__.py ├── cu2qu │ ├── __init__.py │ ├── __main__.py │ ├── benchmark.py │ ├── cli.py │ ├── cu2qu.py │ ├── errors.py │ └── ufo.py ├── designspaceLib │ ├── __init__.py │ ├── split.py │ ├── statNames.py │ └── types.py ├── encodings │ ├── MacRoman.py │ ├── StandardEncoding.py │ ├── __init__.py │ └── codecs.py ├── feaLib │ ├── __init__.py │ ├── __main__.py │ ├── ast.py │ ├── builder.py │ ├── error.py │ ├── lexer.py │ ├── location.py │ ├── lookupDebugInfo.py │ ├── parser.py │ └── variableScalar.py ├── fontBuilder.py ├── help.py ├── merge │ ├── __init__.py │ ├── __main__.py │ ├── base.py │ ├── cmap.py │ ├── layout.py │ ├── options.py │ ├── tables.py │ ├── unicode.py │ └── util.py ├── misc │ ├── __init__.py │ ├── arrayTools.py │ ├── bezierTools.py │ ├── classifyTools.py │ ├── cliTools.py │ ├── configTools.py │ ├── cython.py │ ├── dictTools.py │ ├── eexec.py │ ├── encodingTools.py │ ├── etree.py │ ├── filenames.py │ ├── fixedTools.py │ ├── intTools.py │ ├── loggingTools.py │ ├── macCreatorType.py │ ├── macRes.py │ ├── plistlib │ │ ├── __init__.py │ │ └── py.typed │ ├── psCharStrings.py │ ├── psLib.py │ ├── psOperators.py │ ├── py23.py │ ├── roundTools.py │ ├── sstruct.py │ ├── symfont.py │ ├── testTools.py │ ├── textTools.py │ ├── timeTools.py │ ├── transform.py │ ├── treeTools.py │ ├── vector.py │ ├── visitor.py │ ├── xmlReader.py │ └── xmlWriter.py ├── mtiLib │ ├── __init__.py │ └── __main__.py ├── otlLib │ ├── __init__.py │ ├── builder.py │ ├── error.py │ ├── maxContextCalc.py │ └── optimize │ │ ├── __init__.py │ │ ├── __main__.py │ │ └── gpos.py ├── pens │ ├── __init__.py │ ├── areaPen.py │ ├── basePen.py │ ├── boundsPen.py │ ├── cairoPen.py │ ├── cocoaPen.py │ ├── cu2quPen.py │ ├── filterPen.py │ ├── freetypePen.py │ ├── hashPointPen.py │ ├── momentsPen.py │ ├── perimeterPen.py │ ├── pointInsidePen.py │ ├── pointPen.py │ ├── qtPen.py │ ├── qu2cuPen.py │ ├── quartzPen.py │ ├── recordingPen.py │ ├── reportLabPen.py │ ├── reverseContourPen.py │ ├── roundingPen.py │ ├── statisticsPen.py │ ├── svgPathPen.py │ ├── t2CharStringPen.py │ ├── teePen.py │ ├── transformPen.py │ ├── ttGlyphPen.py │ └── wxPen.py ├── qu2cu │ ├── __init__.py │ ├── __main__.py │ ├── benchmark.py │ ├── cli.py │ └── qu2cu.py ├── subset │ ├── __init__.py │ ├── __main__.py │ ├── cff.py │ ├── svg.py │ └── util.py ├── svgLib │ ├── __init__.py │ └── path │ │ ├── __init__.py │ │ ├── arc.py │ │ ├── parser.py │ │ └── shapes.py ├── t1Lib │ └── __init__.py ├── tfmLib.py ├── ttLib │ ├── __init__.py │ ├── macUtils.py │ ├── removeOverlaps.py │ ├── scaleUpem.py │ ├── sfnt.py │ ├── standardGlyphOrder.py │ ├── tables │ │ ├── B_A_S_E_.py │ │ ├── BitmapGlyphMetrics.py │ │ ├── C_B_D_T_.py │ │ ├── C_B_L_C_.py │ │ ├── C_F_F_.py │ │ ├── C_F_F__2.py │ │ ├── C_O_L_R_.py │ │ ├── C_P_A_L_.py │ │ ├── D_S_I_G_.py │ │ ├── D__e_b_g.py │ │ ├── DefaultTable.py │ │ ├── E_B_D_T_.py │ │ ├── E_B_L_C_.py │ │ ├── F_F_T_M_.py │ │ ├── F__e_a_t.py │ │ ├── G_D_E_F_.py │ │ ├── G_M_A_P_.py │ │ ├── G_P_K_G_.py │ │ ├── G_P_O_S_.py │ │ ├── G_S_U_B_.py │ │ ├── G__l_a_t.py │ │ ├── G__l_o_c.py │ │ ├── H_V_A_R_.py │ │ ├── J_S_T_F_.py │ │ ├── L_T_S_H_.py │ │ ├── M_A_T_H_.py │ │ ├── M_E_T_A_.py │ │ ├── M_V_A_R_.py │ │ ├── O_S_2f_2.py │ │ ├── S_I_N_G_.py │ │ ├── S_T_A_T_.py │ │ ├── S_V_G_.py │ │ ├── S__i_l_f.py │ │ ├── S__i_l_l.py │ │ ├── T_S_I_B_.py │ │ ├── T_S_I_C_.py │ │ ├── T_S_I_D_.py │ │ ├── T_S_I_J_.py │ │ ├── T_S_I_P_.py │ │ ├── T_S_I_S_.py │ │ ├── T_S_I_V_.py │ │ ├── T_S_I__0.py │ │ ├── T_S_I__1.py │ │ ├── T_S_I__2.py │ │ ├── T_S_I__3.py │ │ ├── T_S_I__5.py │ │ ├── T_T_F_A_.py │ │ ├── TupleVariation.py │ │ ├── V_D_M_X_.py │ │ ├── V_O_R_G_.py │ │ ├── V_V_A_R_.py │ │ ├── __init__.py │ │ ├── _a_n_k_r.py │ │ ├── _a_v_a_r.py │ │ ├── _b_s_l_n.py │ │ ├── _c_i_d_g.py │ │ ├── _c_m_a_p.py │ │ ├── _c_v_a_r.py │ │ ├── _c_v_t.py │ │ ├── _f_e_a_t.py │ │ ├── _f_p_g_m.py │ │ ├── _f_v_a_r.py │ │ ├── _g_a_s_p.py │ │ ├── _g_c_i_d.py │ │ ├── _g_l_y_f.py │ │ ├── _g_v_a_r.py │ │ ├── _h_d_m_x.py │ │ ├── _h_e_a_d.py │ │ ├── _h_h_e_a.py │ │ ├── _h_m_t_x.py │ │ ├── _k_e_r_n.py │ │ ├── _l_c_a_r.py │ │ ├── _l_o_c_a.py │ │ ├── _l_t_a_g.py │ │ ├── _m_a_x_p.py │ │ ├── _m_e_t_a.py │ │ ├── _m_o_r_t.py │ │ ├── _m_o_r_x.py │ │ ├── _n_a_m_e.py │ │ ├── _o_p_b_d.py │ │ ├── _p_o_s_t.py │ │ ├── _p_r_e_p.py │ │ ├── _p_r_o_p.py │ │ ├── _s_b_i_x.py │ │ ├── _t_r_a_k.py │ │ ├── _v_h_e_a.py │ │ ├── _v_m_t_x.py │ │ ├── asciiTable.py │ │ ├── grUtils.py │ │ ├── otBase.py │ │ ├── otConverters.py │ │ ├── otData.py │ │ ├── otTables.py │ │ ├── otTraverse.py │ │ ├── sbixGlyph.py │ │ ├── sbixStrike.py │ │ ├── table_API_readme.txt │ │ └── ttProgram.py │ ├── ttCollection.py │ ├── ttFont.py │ ├── ttGlyphSet.py │ ├── ttVisitor.py │ └── woff2.py ├── ttx.py ├── ufoLib │ ├── __init__.py │ ├── converters.py │ ├── errors.py │ ├── etree.py │ ├── filenames.py │ ├── glifLib.py │ ├── kerning.py │ ├── plistlib.py │ ├── pointPen.py │ ├── utils.py │ └── validators.py ├── unicode.py ├── unicodedata │ ├── Blocks.py │ ├── OTTags.py │ ├── ScriptExtensions.py │ ├── Scripts.py │ └── __init__.py ├── varLib │ ├── __init__.py │ ├── __main__.py │ ├── builder.py │ ├── cff.py │ ├── errors.py │ ├── featureVars.py │ ├── instancer │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── featureVars.py │ │ ├── names.py │ │ └── solver.py │ ├── interpolatable.py │ ├── interpolate_layout.py │ ├── iup.py │ ├── merger.py │ ├── models.py │ ├── mutator.py │ ├── mvar.py │ ├── plot.py │ ├── stat.py │ └── varStore.py └── voltLib │ ├── __init__.py │ ├── ast.py │ ├── error.py │ ├── lexer.py │ └── parser.py ├── plugin.png ├── plugin.py └── plugin.xml /README.md: -------------------------------------------------------------------------------- 1 | # STERAePub++标准化模板
2 | ~ePub自动化快速精排 ver 1.3.1+~ 3 |

4 | 5 | >## 使用条件 6 | >- 适用工具:Sigil 1.9.0+ 7 | >- 适用对象:以[《台灣 EPUB 3 製作指引》](https://github.com/dpublishing/epub3guide)和[《電書協 EPUB 3 制作ガイド》](http://ebpaj.jp/counsel/guide)为标准,轻小说为主的ePub电子书源 8 | >- 目标类型:横排流式中文、日文(UTF-8)ePub电子书 9 | >- 靶向适配:WebKit内核浏览器/Reasily/多看阅读/Starrea/iBooks/Kindle/掌阅iReader 10 | >- 执行标准:EPUB 3.0.1/XHTML 1.1/CSS 3.0/ECMAScript 5/Python 3.6/Qt 5 11 | 12 |
13 | 14 | >## 安装方式 15 | >- Windows:运行*setup.exe*自动覆盖安装 16 | >- MacOS/Linux及其他:将*STERA-X*文件夹与*STERA-Std*文件夹分别放入同名父文件夹内后压缩为*zip*,通过`Sigil插件→插件管理→添加插件`安装压缩包,无需解压 17 | 18 |
19 | 20 | >## 功能简介 21 | >- STERA-X:集成自动化书源处理插件,包含*自动化处理*、*繁简转换*、*字体子集化*、*图片压缩*四大功能模块,其中*自动化处理*包含7个主流程与3个附加功能,皆可分离单独运行 22 | >- STERA-Std:书源规范化插件,可快速执行*EPUB规范化*与*EPUB 2转EPUB 3*操作 23 | >- STERAePub++:虚空文学旅团EPUB组标准化模板,具有广泛阅读器适配和优秀视觉效果,依赖JS可实现*互动式弹注*、*图片方向自适应*、*背景色渗透`body[bgcolor]`*,配合*STERA-X*自动化还可实现*自动样式检查与调试`[type="check"]`* -------------------------------------------------------------------------------- /STERA-Std/plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-Std/plugin.png -------------------------------------------------------------------------------- /STERA-Std/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | STERA-Std 4 | input 5 | 明日✿咲葉 6 | 虚空文学旅团STERAePub++标准化书源预处理 7 | python3.4 8 | 1.0 9 | osx,unx,win 10 | true 11 | true 12 | 13 | -------------------------------------------------------------------------------- /STERA-Upload.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-Upload.zip -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | from time import time 4 | from logging import disable 5 | from .bookenv_core import book 6 | from .gui_core import launch 7 | from .build_core import buildtoc, buildtem, clear 8 | from .regrex_core import dom 9 | from .t2stext_core import t2stext 10 | from .cpsimg_core import cpsimg 11 | from .subfont_core import subfont 12 | 13 | version = __version__ = '1.4.0beta1' 14 | __all__ = ['start'] 15 | 16 | 17 | def start(bk): 18 | disable(), print('【虚空文学旅团STERAePub++ ver', version, '】', sep='') 19 | para = launch(bk) 20 | if para: 21 | st = time() 22 | if para['auto']: 23 | if para['del']: 24 | clear(bk, mode='misc') 25 | if para['tem']: 26 | buildtem(bk) 27 | if para['flow_title']: 28 | buildtoc(bk, mode='ctt') 29 | del dom(bk, para['chk'])(para['flow_class'], 30 | para['flow_tag'], 31 | para['flow_text'], 32 | para['flow_title'], 33 | para['flow_note'], 34 | para['flow_image'], 35 | para['flow_page']) 36 | if para['del']: 37 | clear(bk, mode='page') 38 | if para['tem']: 39 | buildtem(bk, para) 40 | if para['flow_title']: 41 | buildtoc(bk, mode='nav') 42 | if para['del']: 43 | clear(bk) 44 | if para['t2s']: 45 | t2stext(bk) 46 | if para['sub']: 47 | subfont(bk) 48 | if para['cps']: 49 | cpsimg(bk) 50 | buildtoc(bk), print('\n【运行结束,共计耗时%.2f秒】' % (time()-st)) 51 | else: 52 | print('\n【运行中止】') 53 | -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/bookenv_dict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | extinfo = {'.bm': ('image/bmp', 'image', 'Images'), 4 | '.bmp': ('image/bmp', 'image', 'Images'), 5 | '.css': ('text/css', 'css', 'Styles'), 6 | '.dtb': ('application/x-dtbook+xml', 'text', 'Text'), 7 | '.gif': ('image/gif', 'image', 'Images'), 8 | '.htm': ('application/xhtml+xml', 'text', 'Text'), 9 | '.html': ('application/xhtml+xml', 'text', 'Text'), 10 | '.jpeg': ('image/jpeg', 'image', 'Images'), 11 | '.jpg': ('image/jpeg', 'image', 'Images'), 12 | '.js': ('application/javascript', 'misc', 'Misc'), 13 | '.m4a': ('audio/mp4', 'audio', 'Audio'), 14 | '.m4v': ('video/mp4', 'video', 'Video'), 15 | '.mp3': ('audio/mpeg', 'audio', 'Audio'), 16 | '.mp4': ('video/mp4', 'video', 'Video'), 17 | '.ncx': ('application/x-dtbncx+xml', 'ncx', ''), 18 | '.oga': ('audio/ogg', 'audio', 'Audio'), 19 | '.ogg': ('audio/ogg', 'audio', 'Audio'), 20 | '.ogv': ('video/ogg', 'video', 'Video'), 21 | '.otf': ('font/otf', 'font', 'Fonts'), 22 | '.pls': ('application/pls+xml', 'misc', 'Misc'), 23 | '.png': ('image/png', 'image', 'Images'), 24 | '.smil': ('application/smil+xml', 'misc', 'Misc'), 25 | '.svg': ('image/svg+xml', 'image', 'Images'), 26 | '.tif': ('image/tiff', 'image', 'Images'), 27 | '.tiff': ('image/tiff', 'image', 'Images'), 28 | '.ttc': ('font/collection', 'font', 'Fonts'), 29 | '.ttf': ('font/ttf', 'font', 'Fonts'), 30 | '.ttml': ('application/ttml+xml', 'video', 'Video'), 31 | '.txt': ('text/plain', 'misc', 'Misc'), 32 | '.vtt': ('text/vtt', 'video', 'Video'), 33 | '.webm': ('video/webm', 'video', 'Video'), 34 | '.webp': ('image/webp', 'image', 'Images'), 35 | '.woff': ('font/woff', 'font', 'Fonts'), 36 | '.woff2': ('font/woff2', 'font', 'Fonts'), 37 | '.xhtml': ('application/xhtml+xml', 'text', 'Text'), 38 | '.xpgt': ('application/vnd.adobe-page-template+xml', 'misc', 'Misc')} 39 | -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/config/mime.json: -------------------------------------------------------------------------------- 1 | { 2 | ".bm": [ 3 | "image/bmp", 4 | "image", 5 | "Images" 6 | ], 7 | ".bmp": [ 8 | "image/bmp", 9 | "image", 10 | "Images" 11 | ], 12 | ".css": [ 13 | "text/css", 14 | "css", 15 | "Styles" 16 | ], 17 | ".dtb": [ 18 | "application/x-dtbook+xml", 19 | "text", 20 | "Text" 21 | ], 22 | ".gif": [ 23 | "image/gif", 24 | "image", 25 | "Images" 26 | ], 27 | ".htm": [ 28 | "application/xhtml+xml", 29 | "text", 30 | "Text" 31 | ], 32 | ".html": [ 33 | "application/xhtml+xml", 34 | "text", 35 | "Text" 36 | ], 37 | ".jpeg": [ 38 | "image/jpeg", 39 | "image", 40 | "Images" 41 | ], 42 | ".jpg": [ 43 | "image/jpeg", 44 | "image", 45 | "Images" 46 | ], 47 | ".js": [ 48 | "application/javascript", 49 | "misc", 50 | "Misc" 51 | ], 52 | ".m4a": [ 53 | "audio/mp4", 54 | "audio", 55 | "Audio" 56 | ], 57 | ".m4v": [ 58 | "video/mp4", 59 | "video", 60 | "Video" 61 | ], 62 | ".mp3": [ 63 | "audio/mpeg", 64 | "audio", 65 | "Audio" 66 | ], 67 | ".mp4": [ 68 | "video/mp4", 69 | "video", 70 | "Video" 71 | ], 72 | ".ncx": [ 73 | "application/x-dtbncx+xml", 74 | "ncx", 75 | "" 76 | ], 77 | ".oga": [ 78 | "audio/ogg", 79 | "audio", 80 | "Audio" 81 | ], 82 | ".ogg": [ 83 | "audio/ogg", 84 | "audio", 85 | "Audio" 86 | ], 87 | ".ogv": [ 88 | "video/ogg", 89 | "video", 90 | "Video" 91 | ], 92 | ".otf": [ 93 | "font/otf", 94 | "font", 95 | "Fonts" 96 | ], 97 | ".pls": [ 98 | "application/pls+xml", 99 | "misc", 100 | "Misc" 101 | ], 102 | ".png": [ 103 | "image/png", 104 | "image", 105 | "Images" 106 | ], 107 | ".smil": [ 108 | "application/smil+xml", 109 | "misc", 110 | "Misc" 111 | ], 112 | ".svg": [ 113 | "image/svg+xml", 114 | "image", 115 | "Images" 116 | ], 117 | ".tif": [ 118 | "image/tiff", 119 | "image", 120 | "Images" 121 | ], 122 | ".tiff": [ 123 | "image/tiff", 124 | "image", 125 | "Images" 126 | ], 127 | ".ttc": [ 128 | "font/collection", 129 | "font", 130 | "Fonts" 131 | ], 132 | ".ttf": [ 133 | "font/ttf", 134 | "font", 135 | "Fonts" 136 | ], 137 | ".ttml": [ 138 | "application/ttml+xml", 139 | "video", 140 | "Video" 141 | ], 142 | ".txt": [ 143 | "text/plain", 144 | "misc", 145 | "Misc" 146 | ], 147 | ".vtt": [ 148 | "text/vtt", 149 | "video", 150 | "Video" 151 | ], 152 | ".webm": [ 153 | "video/webm", 154 | "video", 155 | "Video" 156 | ], 157 | ".webp": [ 158 | "image/webp", 159 | "image", 160 | "Images" 161 | ], 162 | ".woff": [ 163 | "font/woff", 164 | "font", 165 | "Fonts" 166 | ], 167 | ".woff2": [ 168 | "font/woff2", 169 | "font", 170 | "Fonts" 171 | ], 172 | ".xhtml": [ 173 | "application/xhtml+xml", 174 | "text", 175 | "Text" 176 | ], 177 | ".xpgt": [ 178 | "application/vnd.adobe-page-template+xml", 179 | "misc", 180 | "Misc" 181 | ] 182 | } -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/cpsimg_core.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | from regex import compile 4 | from PIL import Image 5 | from io import BytesIO 6 | from multiprocessing import Pool 7 | try: 8 | from .bookenv_core import book, InvalidEpubError, getbsn 9 | except ImportError: 10 | from bookenv_core import book, InvalidEpubError, getbsn 11 | 12 | # 图片压缩 13 | catchimg = compile( 14 | r']*?(?:[^-]src|href)="[^"]*?([^"/]+)"[^>]*?/>(?:(?!') 15 | 16 | 17 | def getpic(bk: book): 18 | ''' 19 | 传入book对象,返回包含HTML文档中所有图片独占页及对应图片的elem对象的生成器。\n 20 | bk -> EPUB的book对象 21 | ''' 22 | for ele in bk.iter('text'): 23 | catch = catchimg.search(ele.read()) 24 | if catch: 25 | pic = bk.get(bsn=getbsn(catch.group(1))) 26 | if pic: 27 | yield ele, pic 28 | 29 | 30 | def cpsimg(bk: book): 31 | ''' 32 | 传入book对象,对HTML文档中使用的图片文件进行压缩,将封面压缩为jpg格式,其余压缩为webp格式,并修改文中的对应链接。\n 33 | bk -> EPUB的book对象 34 | ''' 35 | print('\n图片压缩……') 36 | PIC, IMG, pool = {}, {}, Pool() 37 | for ele, pic in getpic(bk): 38 | cover = pic 39 | break 40 | else: 41 | raise InvalidEpubError('未找到有效的封面图片') 42 | for ele in bk.iter('image'): 43 | PIC[ele] = pool.apply_async(cps, args=( 44 | ele, 'jpeg' if ele is cover else 'webp')) 45 | pool.close(), pool.join() 46 | for ele, img in PIC.items(): 47 | img, bsn, stdext = img.get(), ele.bsn, '.jpg' if ele is cover else '.webp' 48 | if img: 49 | print(' +压缩:【', bsn, '】', sep='') 50 | else: 51 | print(' -未压缩:【', bsn, '】', sep='') 52 | continue 53 | if bsn.endswith(stdext): 54 | ele.write(img) 55 | else: 56 | stdbsn = ele.name+stdext 57 | IMG[bsn] = stdbsn 58 | bk.delete(ele) 59 | bk.set(bk.add(stdbsn, img)) 60 | changed = sorted(IMG, reverse=True, key=len) 61 | for ele in bk.iter('text', 'css'): 62 | data = ele.read() 63 | for i in changed: 64 | if i in data: 65 | ele.write(data.replace(i, IMG[i])) 66 | 67 | 68 | def cps(img: book.elem, tofm: str = 'webp'): 69 | ''' 70 | 通过传入图片的elem对象和目标格式对图片进行压缩,使图片长边不超过2048px,并根据体积是否超过100kb将图片质量压缩为80/90。\n 71 | img -> 目标图片的elem对象\n 72 | tofm -> 图片的目标格式,默认为webp 73 | ''' 74 | pic, fm, res = Image.open(img.fp), img.ext[1:], BytesIO() 75 | size, bsize = pic.size, len(img) 76 | if max(size) > 2048: 77 | pic.resize((2048, int(size[1]*2048/size[0])) if size[0] 78 | >= size[1] else (int(size[0]*2048/size[1]), 2048)) 79 | pic.save(res, tofm, optimize=True, quality=80 if bsize > 100000 else 90) 80 | image = res.getvalue() 81 | res.close() 82 | return image if bsize/len(image) >= 1.1 or (fm != 'webp' if tofm == 'webp' else fm != 'jpg') else None 83 | -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/ch1.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/ch1.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/ch2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/ch2.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/ch3.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/ch3.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/cont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/cont.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/ctt1.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/ctt1.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/ctt2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/ctt2.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/emoji.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/emoji.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/int.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/int.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/logo.png -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/main.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/main.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/mes.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/mes.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/note.png -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/note.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/note.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/sum.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/sum.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/materials/sym.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/STERAEPUB/materials/sym.ttf -------------------------------------------------------------------------------- /STERA-X/STERAEPUB/t2stext_core.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | from regex import compile 4 | try: 5 | from .bookenv_core import book 6 | from .t2stext_dict import zht2s 7 | except ImportError: 8 | from bookenv_core import book 9 | from t2stext_dict import zht2s 10 | 11 | # 繁简转换 12 | _decode = compile(r'\0(\d+)\0') 13 | tdic = tuple(sorted(zht2s, reverse=True, key=len)) 14 | 15 | 16 | def t2stext(bk: book): 17 | print('\n繁简转换……') 18 | if bk.metadata: 19 | bk.metadata = t2s(bk.metadata) 20 | for ele in bk.iter('text'): 21 | ele.write(t2s(ele.read())) 22 | print(' +繁简转换完成') 23 | 24 | 25 | def t2s(text: str) -> str: 26 | for i in range(len(tdic)): 27 | text = text.replace(tdic[i], str(i).join(('\0', '\0'))) 28 | return _decode.sub(lambda x: zht2s[tdic[int(x.group(1))]], text) 29 | -------------------------------------------------------------------------------- /STERA-X/fontTools/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from fontTools.misc.loggingTools import configLogger 3 | 4 | log = logging.getLogger(__name__) 5 | 6 | version = __version__ = "4.39.4" 7 | 8 | __all__ = ["version", "log", "configLogger"] 9 | -------------------------------------------------------------------------------- /STERA-X/fontTools/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | def main(args=None): 5 | if args is None: 6 | args = sys.argv[1:] 7 | 8 | # TODO Handle library-wide options. Eg.: 9 | # --unicodedata 10 | # --verbose / other logging stuff 11 | 12 | # TODO Allow a way to run arbitrary modules? Useful for setting 13 | # library-wide options and calling another library. Eg.: 14 | # 15 | # $ fonttools --unicodedata=... fontmake ... 16 | # 17 | # This allows for a git-like command where thirdparty commands 18 | # can be added. Should we just try importing the fonttools 19 | # module first and try without if it fails? 20 | 21 | if len(sys.argv) < 2: 22 | sys.argv.append("help") 23 | if sys.argv[1] == "-h" or sys.argv[1] == "--help": 24 | sys.argv[1] = "help" 25 | mod = "fontTools." + sys.argv[1] 26 | sys.argv[1] = sys.argv[0] + " " + sys.argv[1] 27 | del sys.argv[0] 28 | 29 | import runpy 30 | 31 | runpy.run_module(mod, run_name="__main__") 32 | 33 | 34 | if __name__ == "__main__": 35 | sys.exit(main()) 36 | -------------------------------------------------------------------------------- /STERA-X/fontTools/colorLib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/fontTools/colorLib/__init__.py -------------------------------------------------------------------------------- /STERA-X/fontTools/colorLib/errors.py: -------------------------------------------------------------------------------- 1 | class ColorLibError(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /STERA-X/fontTools/colorLib/unbuilder.py: -------------------------------------------------------------------------------- 1 | from fontTools.ttLib.tables import otTables as ot 2 | from .table_builder import TableUnbuilder 3 | 4 | 5 | def unbuildColrV1(layerList, baseGlyphList): 6 | layers = [] 7 | if layerList: 8 | layers = layerList.Paint 9 | unbuilder = LayerListUnbuilder(layers) 10 | return { 11 | rec.BaseGlyph: unbuilder.unbuildPaint(rec.Paint) 12 | for rec in baseGlyphList.BaseGlyphPaintRecord 13 | } 14 | 15 | 16 | def _flatten_layers(lst): 17 | for paint in lst: 18 | if paint["Format"] == ot.PaintFormat.PaintColrLayers: 19 | yield from _flatten_layers(paint["Layers"]) 20 | else: 21 | yield paint 22 | 23 | 24 | class LayerListUnbuilder: 25 | def __init__(self, layers): 26 | self.layers = layers 27 | 28 | callbacks = { 29 | ( 30 | ot.Paint, 31 | ot.PaintFormat.PaintColrLayers, 32 | ): self._unbuildPaintColrLayers, 33 | } 34 | self.tableUnbuilder = TableUnbuilder(callbacks) 35 | 36 | def unbuildPaint(self, paint): 37 | assert isinstance(paint, ot.Paint) 38 | return self.tableUnbuilder.unbuild(paint) 39 | 40 | def _unbuildPaintColrLayers(self, source): 41 | assert source["Format"] == ot.PaintFormat.PaintColrLayers 42 | 43 | layers = list( 44 | _flatten_layers( 45 | [ 46 | self.unbuildPaint(childPaint) 47 | for childPaint in self.layers[ 48 | source["FirstLayerIndex"] : source["FirstLayerIndex"] 49 | + source["NumLayers"] 50 | ] 51 | ] 52 | ) 53 | ) 54 | 55 | if len(layers) == 1: 56 | return layers[0] 57 | 58 | return {"Format": source["Format"], "Layers": layers} 59 | 60 | 61 | if __name__ == "__main__": 62 | from pprint import pprint 63 | import sys 64 | from fontTools.ttLib import TTFont 65 | 66 | try: 67 | fontfile = sys.argv[1] 68 | except IndexError: 69 | sys.exit("usage: fonttools colorLib.unbuilder FONTFILE") 70 | 71 | font = TTFont(fontfile) 72 | colr = font["COLR"] 73 | if colr.version < 1: 74 | sys.exit(f"error: No COLR table version=1 found in {fontfile}") 75 | 76 | colorGlyphs = unbuildColrV1( 77 | colr.table.LayerList, 78 | colr.table.BaseGlyphList, 79 | ) 80 | 81 | pprint(colorGlyphs) 82 | -------------------------------------------------------------------------------- /STERA-X/fontTools/config/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Define all configuration options that can affect the working of fontTools 3 | modules. E.g. optimization levels of varLib IUP, otlLib GPOS compression level, 4 | etc. If this file gets too big, split it into smaller files per-module. 5 | 6 | An instance of the Config class can be attached to a TTFont object, so that 7 | the various modules can access their configuration options from it. 8 | """ 9 | from textwrap import dedent 10 | 11 | from fontTools.misc.configTools import * 12 | 13 | 14 | class Config(AbstractConfig): 15 | options = Options() 16 | 17 | 18 | OPTIONS = Config.options 19 | 20 | 21 | Config.register_option( 22 | name="fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL", 23 | help=dedent( 24 | """\ 25 | GPOS Lookup type 2 (PairPos) compression level: 26 | 0 = do not attempt to compact PairPos lookups; 27 | 1 to 8 = create at most 1 to 8 new subtables for each existing 28 | subtable, provided that it would yield a 50%% file size saving; 29 | 9 = create as many new subtables as needed to yield a file size saving. 30 | Default: 0. 31 | 32 | This compaction aims to save file size, by splitting large class 33 | kerning subtables (Format 2) that contain many zero values into 34 | smaller and denser subtables. It's a trade-off between the overhead 35 | of several subtables versus the sparseness of one big subtable. 36 | 37 | See the pull request: https://github.com/fonttools/fonttools/pull/2326 38 | """ 39 | ), 40 | default=0, 41 | parse=int, 42 | validate=lambda v: v in range(10), 43 | ) 44 | 45 | Config.register_option( 46 | name="fontTools.ttLib.tables.otBase:USE_HARFBUZZ_REPACKER", 47 | help=dedent( 48 | """\ 49 | FontTools tries to use the HarfBuzz Repacker to serialize GPOS/GSUB tables 50 | if the uharfbuzz python bindings are importable, otherwise falls back to its 51 | slower, less efficient serializer. Set to False to always use the latter. 52 | Set to True to explicitly request the HarfBuzz Repacker (will raise an 53 | error if uharfbuzz cannot be imported). 54 | """ 55 | ), 56 | default=None, 57 | parse=Option.parse_optional_bool, 58 | validate=Option.validate_optional_bool, 59 | ) 60 | 61 | Config.register_option( 62 | name="fontTools.otlLib.builder:WRITE_GPOS7", 63 | help=dedent( 64 | """\ 65 | macOS before 13.2 didn’t support GPOS LookupType 7 (non-chaining 66 | ContextPos lookups), so FontTools.otlLib.builder disables a file size 67 | optimisation that would use LookupType 7 instead of 8 when there is no 68 | chaining (no prefix or suffix). Set to True to enable the optimization. 69 | """ 70 | ), 71 | default=False, 72 | parse=Option.parse_optional_bool, 73 | validate=Option.validate_optional_bool, 74 | ) 75 | -------------------------------------------------------------------------------- /STERA-X/fontTools/cu2qu/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. All Rights Reserved. 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. 14 | 15 | from .cu2qu import * 16 | -------------------------------------------------------------------------------- /STERA-X/fontTools/cu2qu/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from .cli import main 3 | 4 | 5 | if __name__ == "__main__": 6 | sys.exit(main()) 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/cu2qu/benchmark.py: -------------------------------------------------------------------------------- 1 | """Benchmark the cu2qu algorithm performance.""" 2 | 3 | from .cu2qu import * 4 | import random 5 | import timeit 6 | 7 | MAX_ERR = 0.05 8 | 9 | 10 | def generate_curve(): 11 | return [ 12 | tuple(float(random.randint(0, 2048)) for coord in range(2)) 13 | for point in range(4) 14 | ] 15 | 16 | 17 | def setup_curve_to_quadratic(): 18 | return generate_curve(), MAX_ERR 19 | 20 | 21 | def setup_curves_to_quadratic(): 22 | num_curves = 3 23 | return ([generate_curve() for curve in range(num_curves)], [MAX_ERR] * num_curves) 24 | 25 | 26 | def run_benchmark(module, function, setup_suffix="", repeat=5, number=1000): 27 | setup_func = "setup_" + function 28 | if setup_suffix: 29 | print("%s with %s:" % (function, setup_suffix), end="") 30 | setup_func += "_" + setup_suffix 31 | else: 32 | print("%s:" % function, end="") 33 | 34 | def wrapper(function, setup_func): 35 | function = globals()[function] 36 | setup_func = globals()[setup_func] 37 | 38 | def wrapped(): 39 | return function(*setup_func()) 40 | 41 | return wrapped 42 | 43 | results = timeit.repeat(wrapper(function, setup_func), repeat=repeat, number=number) 44 | print("\t%5.1fus" % (min(results) * 1000000.0 / number)) 45 | 46 | 47 | def main(): 48 | """Benchmark the cu2qu algorithm performance.""" 49 | run_benchmark("cu2qu", "curve_to_quadratic") 50 | run_benchmark("cu2qu", "curves_to_quadratic") 51 | 52 | 53 | if __name__ == "__main__": 54 | random.seed(1) 55 | main() 56 | -------------------------------------------------------------------------------- /STERA-X/fontTools/cu2qu/errors.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. All Rights Reserved. 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. 14 | 15 | 16 | class Error(Exception): 17 | """Base Cu2Qu exception class for all other errors.""" 18 | 19 | 20 | class ApproxNotFoundError(Error): 21 | def __init__(self, curve): 22 | message = "no approximation found: %s" % curve 23 | super().__init__(message) 24 | self.curve = curve 25 | 26 | 27 | class UnequalZipLengthsError(Error): 28 | pass 29 | 30 | 31 | class IncompatibleGlyphsError(Error): 32 | def __init__(self, glyphs): 33 | assert len(glyphs) > 1 34 | self.glyphs = glyphs 35 | names = set(repr(g.name) for g in glyphs) 36 | if len(names) > 1: 37 | self.combined_name = "{%s}" % ", ".join(sorted(names)) 38 | else: 39 | self.combined_name = names.pop() 40 | 41 | def __repr__(self): 42 | return "<%s %s>" % (type(self).__name__, self.combined_name) 43 | 44 | 45 | class IncompatibleSegmentNumberError(IncompatibleGlyphsError): 46 | def __str__(self): 47 | return "Glyphs named %s have different number of segments" % ( 48 | self.combined_name 49 | ) 50 | 51 | 52 | class IncompatibleSegmentTypesError(IncompatibleGlyphsError): 53 | def __init__(self, glyphs, segments): 54 | IncompatibleGlyphsError.__init__(self, glyphs) 55 | self.segments = segments 56 | 57 | def __str__(self): 58 | lines = [] 59 | ndigits = len(str(max(self.segments))) 60 | for i, tags in sorted(self.segments.items()): 61 | lines.append( 62 | "%s: (%s)" % (str(i).rjust(ndigits), ", ".join(repr(t) for t in tags)) 63 | ) 64 | return "Glyphs named %s have incompatible segment types:\n %s" % ( 65 | self.combined_name, 66 | "\n ".join(lines), 67 | ) 68 | 69 | 70 | class IncompatibleFontsError(Error): 71 | def __init__(self, glyph_errors): 72 | self.glyph_errors = glyph_errors 73 | 74 | def __str__(self): 75 | return "fonts contains incompatible glyphs: %s" % ( 76 | ", ".join(repr(g) for g in sorted(self.glyph_errors.keys())) 77 | ) 78 | -------------------------------------------------------------------------------- /STERA-X/fontTools/encodings/__init__.py: -------------------------------------------------------------------------------- 1 | """Empty __init__.py file to signal Python this directory is a package.""" 2 | -------------------------------------------------------------------------------- /STERA-X/fontTools/feaLib/__init__.py: -------------------------------------------------------------------------------- 1 | """fontTools.feaLib -- a package for dealing with OpenType feature files.""" 2 | 3 | # The structure of OpenType feature files is defined here: 4 | # http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html 5 | -------------------------------------------------------------------------------- /STERA-X/fontTools/feaLib/__main__.py: -------------------------------------------------------------------------------- 1 | from fontTools.ttLib import TTFont 2 | from fontTools.feaLib.builder import addOpenTypeFeatures, Builder 3 | from fontTools.feaLib.error import FeatureLibError 4 | from fontTools import configLogger 5 | from fontTools.misc.cliTools import makeOutputFileName 6 | import sys 7 | import argparse 8 | import logging 9 | 10 | 11 | log = logging.getLogger("fontTools.feaLib") 12 | 13 | 14 | def main(args=None): 15 | """Add features from a feature file (.fea) into an OTF font""" 16 | parser = argparse.ArgumentParser( 17 | description="Use fontTools to compile OpenType feature files (*.fea)." 18 | ) 19 | parser.add_argument( 20 | "input_fea", metavar="FEATURES", help="Path to the feature file" 21 | ) 22 | parser.add_argument( 23 | "input_font", metavar="INPUT_FONT", help="Path to the input font" 24 | ) 25 | parser.add_argument( 26 | "-o", 27 | "--output", 28 | dest="output_font", 29 | metavar="OUTPUT_FONT", 30 | help="Path to the output font.", 31 | ) 32 | parser.add_argument( 33 | "-t", 34 | "--tables", 35 | metavar="TABLE_TAG", 36 | choices=Builder.supportedTables, 37 | nargs="+", 38 | help="Specify the table(s) to be built.", 39 | ) 40 | parser.add_argument( 41 | "-d", 42 | "--debug", 43 | action="store_true", 44 | help="Add source-level debugging information to font.", 45 | ) 46 | parser.add_argument( 47 | "-v", 48 | "--verbose", 49 | help="Increase the logger verbosity. Multiple -v " "options are allowed.", 50 | action="count", 51 | default=0, 52 | ) 53 | parser.add_argument( 54 | "--traceback", help="show traceback for exceptions.", action="store_true" 55 | ) 56 | options = parser.parse_args(args) 57 | 58 | levels = ["WARNING", "INFO", "DEBUG"] 59 | configLogger(level=levels[min(len(levels) - 1, options.verbose)]) 60 | 61 | output_font = options.output_font or makeOutputFileName(options.input_font) 62 | log.info("Compiling features to '%s'" % (output_font)) 63 | 64 | font = TTFont(options.input_font) 65 | try: 66 | addOpenTypeFeatures( 67 | font, options.input_fea, tables=options.tables, debug=options.debug 68 | ) 69 | except FeatureLibError as e: 70 | if options.traceback: 71 | raise 72 | log.error(e) 73 | sys.exit(1) 74 | font.save(output_font) 75 | 76 | 77 | if __name__ == "__main__": 78 | sys.exit(main()) 79 | -------------------------------------------------------------------------------- /STERA-X/fontTools/feaLib/error.py: -------------------------------------------------------------------------------- 1 | class FeatureLibError(Exception): 2 | def __init__(self, message, location): 3 | Exception.__init__(self, message) 4 | self.location = location 5 | 6 | def __str__(self): 7 | message = Exception.__str__(self) 8 | if self.location: 9 | return f"{self.location}: {message}" 10 | else: 11 | return message 12 | 13 | 14 | class IncludedFeaNotFound(FeatureLibError): 15 | def __str__(self): 16 | assert self.location is not None 17 | 18 | message = ( 19 | "The following feature file should be included but cannot be found: " 20 | f"{Exception.__str__(self)}" 21 | ) 22 | return f"{self.location}: {message}" 23 | -------------------------------------------------------------------------------- /STERA-X/fontTools/feaLib/location.py: -------------------------------------------------------------------------------- 1 | from typing import NamedTuple 2 | 3 | 4 | class FeatureLibLocation(NamedTuple): 5 | """A location in a feature file""" 6 | 7 | file: str 8 | line: int 9 | column: int 10 | 11 | def __str__(self): 12 | return f"{self.file}:{self.line}:{self.column}" 13 | -------------------------------------------------------------------------------- /STERA-X/fontTools/feaLib/lookupDebugInfo.py: -------------------------------------------------------------------------------- 1 | from typing import NamedTuple 2 | 3 | LOOKUP_DEBUG_INFO_KEY = "com.github.fonttools.feaLib" 4 | LOOKUP_DEBUG_ENV_VAR = "FONTTOOLS_LOOKUP_DEBUGGING" 5 | 6 | 7 | class LookupDebugInfo(NamedTuple): 8 | """Information about where a lookup came from, to be embedded in a font""" 9 | 10 | location: str 11 | name: str 12 | feature: list 13 | -------------------------------------------------------------------------------- /STERA-X/fontTools/help.py: -------------------------------------------------------------------------------- 1 | import pkgutil 2 | import sys 3 | import fontTools 4 | import importlib 5 | import os 6 | from pathlib import Path 7 | 8 | 9 | def main(): 10 | """Show this help""" 11 | path = fontTools.__path__ 12 | descriptions = {} 13 | for pkg in sorted( 14 | mod.name 15 | for mod in pkgutil.walk_packages([fontTools.__path__[0]], prefix="fontTools.") 16 | ): 17 | try: 18 | imports = __import__(pkg, globals(), locals(), ["main"]) 19 | except ImportError as e: 20 | continue 21 | try: 22 | description = imports.main.__doc__ 23 | if description: 24 | pkg = pkg.replace("fontTools.", "").replace(".__main__", "") 25 | # show the docstring's first line only 26 | descriptions[pkg] = description.splitlines()[0] 27 | except AttributeError as e: 28 | pass 29 | for pkg, description in descriptions.items(): 30 | print("fonttools %-12s %s" % (pkg, description), file=sys.stderr) 31 | 32 | 33 | if __name__ == "__main__": 34 | print("fonttools v%s\n" % fontTools.__version__, file=sys.stderr) 35 | main() 36 | -------------------------------------------------------------------------------- /STERA-X/fontTools/merge/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from fontTools.merge import main 3 | 4 | 5 | if __name__ == "__main__": 6 | sys.exit(main()) 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/merge/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google, Inc. All Rights Reserved. 2 | # 3 | # Google Author(s): Behdad Esfahbod, Roozbeh Pournader 4 | 5 | from fontTools.ttLib.tables.DefaultTable import DefaultTable 6 | import logging 7 | 8 | 9 | log = logging.getLogger("fontTools.merge") 10 | 11 | 12 | def add_method(*clazzes, **kwargs): 13 | """Returns a decorator function that adds a new method to one or 14 | more classes.""" 15 | allowDefault = kwargs.get("allowDefaultTable", False) 16 | 17 | def wrapper(method): 18 | done = [] 19 | for clazz in clazzes: 20 | if clazz in done: 21 | continue # Support multiple names of a clazz 22 | done.append(clazz) 23 | assert allowDefault or clazz != DefaultTable, "Oops, table class not found." 24 | assert ( 25 | method.__name__ not in clazz.__dict__ 26 | ), "Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__) 27 | setattr(clazz, method.__name__, method) 28 | return None 29 | 30 | return wrapper 31 | 32 | 33 | def mergeObjects(lst): 34 | lst = [item for item in lst if item is not NotImplemented] 35 | if not lst: 36 | return NotImplemented 37 | lst = [item for item in lst if item is not None] 38 | if not lst: 39 | return None 40 | 41 | clazz = lst[0].__class__ 42 | assert all(type(item) == clazz for item in lst), lst 43 | 44 | logic = clazz.mergeMap 45 | returnTable = clazz() 46 | returnDict = {} 47 | 48 | allKeys = set.union(set(), *(vars(table).keys() for table in lst)) 49 | for key in allKeys: 50 | try: 51 | mergeLogic = logic[key] 52 | except KeyError: 53 | try: 54 | mergeLogic = logic["*"] 55 | except KeyError: 56 | raise Exception( 57 | "Don't know how to merge key %s of class %s" % (key, clazz.__name__) 58 | ) 59 | if mergeLogic is NotImplemented: 60 | continue 61 | value = mergeLogic(getattr(table, key, NotImplemented) for table in lst) 62 | if value is not NotImplemented: 63 | returnDict[key] = value 64 | 65 | returnTable.__dict__ = returnDict 66 | 67 | return returnTable 68 | 69 | 70 | @add_method(DefaultTable, allowDefaultTable=True) 71 | def merge(self, m, tables): 72 | if not hasattr(self, "mergeMap"): 73 | log.info("Don't know how to merge '%s'.", self.tableTag) 74 | return NotImplemented 75 | 76 | logic = self.mergeMap 77 | 78 | if isinstance(logic, dict): 79 | return m.mergeObjects(self, self.mergeMap, tables) 80 | else: 81 | return logic(tables) 82 | -------------------------------------------------------------------------------- /STERA-X/fontTools/merge/options.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google, Inc. All Rights Reserved. 2 | # 3 | # Google Author(s): Behdad Esfahbod, Roozbeh Pournader 4 | 5 | 6 | class Options(object): 7 | class UnknownOptionError(Exception): 8 | pass 9 | 10 | def __init__(self, **kwargs): 11 | 12 | self.verbose = False 13 | self.timing = False 14 | self.drop_tables = [] 15 | 16 | self.set(**kwargs) 17 | 18 | def set(self, **kwargs): 19 | for k, v in kwargs.items(): 20 | if not hasattr(self, k): 21 | raise self.UnknownOptionError("Unknown option '%s'" % k) 22 | setattr(self, k, v) 23 | 24 | def parse_opts(self, argv, ignore_unknown=[]): 25 | ret = [] 26 | opts = {} 27 | for a in argv: 28 | orig_a = a 29 | if not a.startswith("--"): 30 | ret.append(a) 31 | continue 32 | a = a[2:] 33 | i = a.find("=") 34 | op = "=" 35 | if i == -1: 36 | if a.startswith("no-"): 37 | k = a[3:] 38 | v = False 39 | else: 40 | k = a 41 | v = True 42 | else: 43 | k = a[:i] 44 | if k[-1] in "-+": 45 | op = k[-1] + "=" # Ops is '-=' or '+=' now. 46 | k = k[:-1] 47 | v = a[i + 1 :] 48 | ok = k 49 | k = k.replace("-", "_") 50 | if not hasattr(self, k): 51 | if ignore_unknown is True or ok in ignore_unknown: 52 | ret.append(orig_a) 53 | continue 54 | else: 55 | raise self.UnknownOptionError("Unknown option '%s'" % a) 56 | 57 | ov = getattr(self, k) 58 | if isinstance(ov, bool): 59 | v = bool(v) 60 | elif isinstance(ov, int): 61 | v = int(v) 62 | elif isinstance(ov, list): 63 | vv = v.split(",") 64 | if vv == [""]: 65 | vv = [] 66 | vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv] 67 | if op == "=": 68 | v = vv 69 | elif op == "+=": 70 | v = ov 71 | v.extend(vv) 72 | elif op == "-=": 73 | v = ov 74 | for x in vv: 75 | if x in v: 76 | v.remove(x) 77 | else: 78 | assert 0 79 | 80 | opts[k] = v 81 | self.set(**opts) 82 | 83 | return ret 84 | -------------------------------------------------------------------------------- /STERA-X/fontTools/merge/util.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google, Inc. All Rights Reserved. 2 | # 3 | # Google Author(s): Behdad Esfahbod, Roozbeh Pournader 4 | 5 | from fontTools.misc.timeTools import timestampNow 6 | from fontTools.ttLib.tables.DefaultTable import DefaultTable 7 | from functools import reduce 8 | import operator 9 | import logging 10 | 11 | 12 | log = logging.getLogger("fontTools.merge") 13 | 14 | 15 | # General utility functions for merging values from different fonts 16 | 17 | 18 | def equal(lst): 19 | lst = list(lst) 20 | t = iter(lst) 21 | first = next(t) 22 | assert all(item == first for item in t), "Expected all items to be equal: %s" % lst 23 | return first 24 | 25 | 26 | def first(lst): 27 | return next(iter(lst)) 28 | 29 | 30 | def recalculate(lst): 31 | return NotImplemented 32 | 33 | 34 | def current_time(lst): 35 | return timestampNow() 36 | 37 | 38 | def bitwise_and(lst): 39 | return reduce(operator.and_, lst) 40 | 41 | 42 | def bitwise_or(lst): 43 | return reduce(operator.or_, lst) 44 | 45 | 46 | def avg_int(lst): 47 | lst = list(lst) 48 | return sum(lst) // len(lst) 49 | 50 | 51 | def onlyExisting(func): 52 | """Returns a filter func that when called with a list, 53 | only calls func on the non-NotImplemented items of the list, 54 | and only so if there's at least one item remaining. 55 | Otherwise returns NotImplemented.""" 56 | 57 | def wrapper(lst): 58 | items = [item for item in lst if item is not NotImplemented] 59 | return func(items) if items else NotImplemented 60 | 61 | return wrapper 62 | 63 | 64 | def sumLists(lst): 65 | l = [] 66 | for item in lst: 67 | l.extend(item) 68 | return l 69 | 70 | 71 | def sumDicts(lst): 72 | d = {} 73 | for item in lst: 74 | d.update(item) 75 | return d 76 | 77 | 78 | def mergeBits(bitmap): 79 | def wrapper(lst): 80 | lst = list(lst) 81 | returnValue = 0 82 | for bitNumber in range(bitmap["size"]): 83 | try: 84 | mergeLogic = bitmap[bitNumber] 85 | except KeyError: 86 | try: 87 | mergeLogic = bitmap["*"] 88 | except KeyError: 89 | raise Exception("Don't know how to merge bit %s" % bitNumber) 90 | shiftedBit = 1 << bitNumber 91 | mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst) 92 | returnValue |= mergedValue << bitNumber 93 | return returnValue 94 | 95 | return wrapper 96 | 97 | 98 | class AttendanceRecordingIdentityDict(object): 99 | """A dictionary-like object that records indices of items actually accessed 100 | from a list.""" 101 | 102 | def __init__(self, lst): 103 | self.l = lst 104 | self.d = {id(v): i for i, v in enumerate(lst)} 105 | self.s = set() 106 | 107 | def __getitem__(self, v): 108 | self.s.add(self.d[id(v)]) 109 | return v 110 | 111 | 112 | class GregariousIdentityDict(object): 113 | """A dictionary-like object that welcomes guests without reservations and 114 | adds them to the end of the guest list.""" 115 | 116 | def __init__(self, lst): 117 | self.l = lst 118 | self.s = set(id(v) for v in lst) 119 | 120 | def __getitem__(self, v): 121 | if id(v) not in self.s: 122 | self.s.add(id(v)) 123 | self.l.append(v) 124 | return v 125 | 126 | 127 | class NonhashableDict(object): 128 | """A dictionary-like object mapping objects to values.""" 129 | 130 | def __init__(self, keys, values=None): 131 | if values is None: 132 | self.d = {id(v): i for i, v in enumerate(keys)} 133 | else: 134 | self.d = {id(k): v for k, v in zip(keys, values)} 135 | 136 | def __getitem__(self, k): 137 | return self.d[id(k)] 138 | 139 | def __setitem__(self, k, v): 140 | self.d[id(k)] = v 141 | 142 | def __delitem__(self, k): 143 | del self.d[id(k)] 144 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/__init__.py: -------------------------------------------------------------------------------- 1 | """Empty __init__.py file to signal Python this directory is a package.""" 2 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/cliTools.py: -------------------------------------------------------------------------------- 1 | """Collection of utilities for command-line interfaces and console scripts.""" 2 | import os 3 | import re 4 | 5 | 6 | numberAddedRE = re.compile(r"#\d+$") 7 | 8 | 9 | def makeOutputFileName( 10 | input, outputDir=None, extension=None, overWrite=False, suffix="" 11 | ): 12 | """Generates a suitable file name for writing output. 13 | 14 | Often tools will want to take a file, do some kind of transformation to it, 15 | and write it out again. This function determines an appropriate name for the 16 | output file, through one or more of the following steps: 17 | 18 | - changing the output directory 19 | - appending suffix before file extension 20 | - replacing the file extension 21 | - suffixing the filename with a number (``#1``, ``#2``, etc.) to avoid 22 | overwriting an existing file. 23 | 24 | Args: 25 | input: Name of input file. 26 | outputDir: Optionally, a new directory to write the file into. 27 | suffix: Optionally, a string suffix is appended to file name before 28 | the extension. 29 | extension: Optionally, a replacement for the current file extension. 30 | overWrite: Overwriting an existing file is permitted if true; if false 31 | and the proposed filename exists, a new name will be generated by 32 | adding an appropriate number suffix. 33 | 34 | Returns: 35 | str: Suitable output filename 36 | """ 37 | dirName, fileName = os.path.split(input) 38 | fileName, ext = os.path.splitext(fileName) 39 | if outputDir: 40 | dirName = outputDir 41 | fileName = numberAddedRE.split(fileName)[0] 42 | if extension is None: 43 | extension = os.path.splitext(input)[1] 44 | output = os.path.join(dirName, fileName + suffix + extension) 45 | n = 1 46 | if not overWrite: 47 | while os.path.exists(output): 48 | output = os.path.join( 49 | dirName, fileName + suffix + "#" + repr(n) + extension 50 | ) 51 | n += 1 52 | return output 53 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/cython.py: -------------------------------------------------------------------------------- 1 | """ Exports a no-op 'cython' namespace similar to 2 | https://github.com/cython/cython/blob/master/Cython/Shadow.py 3 | 4 | This allows to optionally compile @cython decorated functions 5 | (when cython is available at built time), or run the same code 6 | as pure-python, without runtime dependency on cython module. 7 | 8 | We only define the symbols that we use. E.g. see fontTools.cu2qu 9 | """ 10 | 11 | from types import SimpleNamespace 12 | 13 | 14 | def _empty_decorator(x): 15 | return x 16 | 17 | 18 | compiled = False 19 | 20 | for name in ("double", "complex", "int"): 21 | globals()[name] = None 22 | 23 | for name in ("cfunc", "inline"): 24 | globals()[name] = _empty_decorator 25 | 26 | locals = lambda **_: _empty_decorator 27 | returns = lambda _: _empty_decorator 28 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/dictTools.py: -------------------------------------------------------------------------------- 1 | """Misc dict tools.""" 2 | 3 | 4 | __all__ = ["hashdict"] 5 | 6 | # https://stackoverflow.com/questions/1151658/python-hashable-dicts 7 | class hashdict(dict): 8 | """ 9 | hashable dict implementation, suitable for use as a key into 10 | other dicts. 11 | 12 | >>> h1 = hashdict({"apples": 1, "bananas":2}) 13 | >>> h2 = hashdict({"bananas": 3, "mangoes": 5}) 14 | >>> h1+h2 15 | hashdict(apples=1, bananas=3, mangoes=5) 16 | >>> d1 = {} 17 | >>> d1[h1] = "salad" 18 | >>> d1[h1] 19 | 'salad' 20 | >>> d1[h2] 21 | Traceback (most recent call last): 22 | ... 23 | KeyError: hashdict(bananas=3, mangoes=5) 24 | 25 | based on answers from 26 | http://stackoverflow.com/questions/1151658/python-hashable-dicts 27 | 28 | """ 29 | 30 | def __key(self): 31 | return tuple(sorted(self.items())) 32 | 33 | def __repr__(self): 34 | return "{0}({1})".format( 35 | self.__class__.__name__, 36 | ", ".join("{0}={1}".format(str(i[0]), repr(i[1])) for i in self.__key()), 37 | ) 38 | 39 | def __hash__(self): 40 | return hash(self.__key()) 41 | 42 | def __setitem__(self, key, value): 43 | raise TypeError( 44 | "{0} does not support item assignment".format(self.__class__.__name__) 45 | ) 46 | 47 | def __delitem__(self, key): 48 | raise TypeError( 49 | "{0} does not support item assignment".format(self.__class__.__name__) 50 | ) 51 | 52 | def clear(self): 53 | raise TypeError( 54 | "{0} does not support item assignment".format(self.__class__.__name__) 55 | ) 56 | 57 | def pop(self, *args, **kwargs): 58 | raise TypeError( 59 | "{0} does not support item assignment".format(self.__class__.__name__) 60 | ) 61 | 62 | def popitem(self, *args, **kwargs): 63 | raise TypeError( 64 | "{0} does not support item assignment".format(self.__class__.__name__) 65 | ) 66 | 67 | def setdefault(self, *args, **kwargs): 68 | raise TypeError( 69 | "{0} does not support item assignment".format(self.__class__.__name__) 70 | ) 71 | 72 | def update(self, *args, **kwargs): 73 | raise TypeError( 74 | "{0} does not support item assignment".format(self.__class__.__name__) 75 | ) 76 | 77 | # update is not ok because it mutates the object 78 | # __add__ is ok because it creates a new object 79 | # while the new object is under construction, it's ok to mutate it 80 | def __add__(self, right): 81 | result = hashdict(self) 82 | dict.update(result, right) 83 | return result 84 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/eexec.py: -------------------------------------------------------------------------------- 1 | """ 2 | PostScript Type 1 fonts make use of two types of encryption: charstring 3 | encryption and ``eexec`` encryption. Charstring encryption is used for 4 | the charstrings themselves, while ``eexec`` is used to encrypt larger 5 | sections of the font program, such as the ``Private`` and ``CharStrings`` 6 | dictionaries. Despite the different names, the algorithm is the same, 7 | although ``eexec`` encryption uses a fixed initial key R=55665. 8 | 9 | The algorithm uses cipher feedback, meaning that the ciphertext is used 10 | to modify the key. Because of this, the routines in this module return 11 | the new key at the end of the operation. 12 | 13 | """ 14 | 15 | from fontTools.misc.textTools import bytechr, bytesjoin, byteord 16 | 17 | 18 | def _decryptChar(cipher, R): 19 | cipher = byteord(cipher) 20 | plain = ((cipher ^ (R >> 8))) & 0xFF 21 | R = ((cipher + R) * 52845 + 22719) & 0xFFFF 22 | return bytechr(plain), R 23 | 24 | 25 | def _encryptChar(plain, R): 26 | plain = byteord(plain) 27 | cipher = ((plain ^ (R >> 8))) & 0xFF 28 | R = ((cipher + R) * 52845 + 22719) & 0xFFFF 29 | return bytechr(cipher), R 30 | 31 | 32 | def decrypt(cipherstring, R): 33 | r""" 34 | Decrypts a string using the Type 1 encryption algorithm. 35 | 36 | Args: 37 | cipherstring: String of ciphertext. 38 | R: Initial key. 39 | 40 | Returns: 41 | decryptedStr: Plaintext string. 42 | R: Output key for subsequent decryptions. 43 | 44 | Examples:: 45 | 46 | >>> testStr = b"\0\0asdadads asds\265" 47 | >>> decryptedStr, R = decrypt(testStr, 12321) 48 | >>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1' 49 | True 50 | >>> R == 36142 51 | True 52 | """ 53 | plainList = [] 54 | for cipher in cipherstring: 55 | plain, R = _decryptChar(cipher, R) 56 | plainList.append(plain) 57 | plainstring = bytesjoin(plainList) 58 | return plainstring, int(R) 59 | 60 | 61 | def encrypt(plainstring, R): 62 | r""" 63 | Encrypts a string using the Type 1 encryption algorithm. 64 | 65 | Note that the algorithm as described in the Type 1 specification requires the 66 | plaintext to be prefixed with a number of random bytes. (For ``eexec`` the 67 | number of random bytes is set to 4.) This routine does *not* add the random 68 | prefix to its input. 69 | 70 | Args: 71 | plainstring: String of plaintext. 72 | R: Initial key. 73 | 74 | Returns: 75 | cipherstring: Ciphertext string. 76 | R: Output key for subsequent encryptions. 77 | 78 | Examples:: 79 | 80 | >>> testStr = b"\0\0asdadads asds\265" 81 | >>> decryptedStr, R = decrypt(testStr, 12321) 82 | >>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1' 83 | True 84 | >>> R == 36142 85 | True 86 | 87 | >>> testStr = b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1' 88 | >>> encryptedStr, R = encrypt(testStr, 12321) 89 | >>> encryptedStr == b"\0\0asdadads asds\265" 90 | True 91 | >>> R == 36142 92 | True 93 | """ 94 | cipherList = [] 95 | for plain in plainstring: 96 | cipher, R = _encryptChar(plain, R) 97 | cipherList.append(cipher) 98 | cipherstring = bytesjoin(cipherList) 99 | return cipherstring, int(R) 100 | 101 | 102 | def hexString(s): 103 | import binascii 104 | 105 | return binascii.hexlify(s) 106 | 107 | 108 | def deHexString(h): 109 | import binascii 110 | 111 | h = bytesjoin(h.split()) 112 | return binascii.unhexlify(h) 113 | 114 | 115 | if __name__ == "__main__": 116 | import sys 117 | import doctest 118 | 119 | sys.exit(doctest.testmod().failed) 120 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/encodingTools.py: -------------------------------------------------------------------------------- 1 | """fontTools.misc.encodingTools.py -- tools for working with OpenType encodings. 2 | """ 3 | 4 | import fontTools.encodings.codecs 5 | 6 | # Map keyed by platformID, then platEncID, then possibly langID 7 | _encodingMap = { 8 | 0: { # Unicode 9 | 0: "utf_16_be", 10 | 1: "utf_16_be", 11 | 2: "utf_16_be", 12 | 3: "utf_16_be", 13 | 4: "utf_16_be", 14 | 5: "utf_16_be", 15 | 6: "utf_16_be", 16 | }, 17 | 1: { # Macintosh 18 | # See 19 | # https://github.com/fonttools/fonttools/issues/236 20 | 0: { # Macintosh, platEncID==0, keyed by langID 21 | 15: "mac_iceland", 22 | 17: "mac_turkish", 23 | 18: "mac_croatian", 24 | 24: "mac_latin2", 25 | 25: "mac_latin2", 26 | 26: "mac_latin2", 27 | 27: "mac_latin2", 28 | 28: "mac_latin2", 29 | 36: "mac_latin2", 30 | 37: "mac_romanian", 31 | 38: "mac_latin2", 32 | 39: "mac_latin2", 33 | 40: "mac_latin2", 34 | Ellipsis: "mac_roman", # Other 35 | }, 36 | 1: "x_mac_japanese_ttx", 37 | 2: "x_mac_trad_chinese_ttx", 38 | 3: "x_mac_korean_ttx", 39 | 6: "mac_greek", 40 | 7: "mac_cyrillic", 41 | 25: "x_mac_simp_chinese_ttx", 42 | 29: "mac_latin2", 43 | 35: "mac_turkish", 44 | 37: "mac_iceland", 45 | }, 46 | 2: { # ISO 47 | 0: "ascii", 48 | 1: "utf_16_be", 49 | 2: "latin1", 50 | }, 51 | 3: { # Microsoft 52 | 0: "utf_16_be", 53 | 1: "utf_16_be", 54 | 2: "shift_jis", 55 | 3: "gb2312", 56 | 4: "big5", 57 | 5: "euc_kr", 58 | 6: "johab", 59 | 10: "utf_16_be", 60 | }, 61 | } 62 | 63 | 64 | def getEncoding(platformID, platEncID, langID, default=None): 65 | """Returns the Python encoding name for OpenType platformID/encodingID/langID 66 | triplet. If encoding for these values is not known, by default None is 67 | returned. That can be overriden by passing a value to the default argument. 68 | """ 69 | encoding = _encodingMap.get(platformID, {}).get(platEncID, default) 70 | if isinstance(encoding, dict): 71 | encoding = encoding.get(langID, encoding[Ellipsis]) 72 | return encoding 73 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/intTools.py: -------------------------------------------------------------------------------- 1 | __all__ = ["popCount"] 2 | 3 | 4 | try: 5 | bit_count = int.bit_count 6 | except AttributeError: 7 | 8 | def bit_count(v): 9 | return bin(v).count("1") 10 | 11 | 12 | """Return number of 1 bits (population count) of the absolute value of an integer. 13 | 14 | See https://docs.python.org/3.10/library/stdtypes.html#int.bit_count 15 | """ 16 | popCount = bit_count 17 | 18 | 19 | def bit_indices(v): 20 | """Return list of indices where bits are set, 0 being the index of the least significant bit. 21 | 22 | >>> bit_indices(0b101) 23 | [0, 2] 24 | """ 25 | return [i for i, b in enumerate(bin(v)[::-1]) if b == "1"] 26 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/macCreatorType.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.textTools import Tag, bytesjoin, strjoin 2 | 3 | try: 4 | import xattr 5 | except ImportError: 6 | xattr = None 7 | 8 | 9 | def _reverseString(s): 10 | s = list(s) 11 | s.reverse() 12 | return strjoin(s) 13 | 14 | 15 | def getMacCreatorAndType(path): 16 | """Returns file creator and file type codes for a path. 17 | 18 | Args: 19 | path (str): A file path. 20 | 21 | Returns: 22 | A tuple of two :py:class:`fontTools.textTools.Tag` objects, the first 23 | representing the file creator and the second representing the 24 | file type. 25 | """ 26 | if xattr is not None: 27 | try: 28 | finderInfo = xattr.getxattr(path, "com.apple.FinderInfo") 29 | except (KeyError, IOError): 30 | pass 31 | else: 32 | fileType = Tag(finderInfo[:4]) 33 | fileCreator = Tag(finderInfo[4:8]) 34 | return fileCreator, fileType 35 | return None, None 36 | 37 | 38 | def setMacCreatorAndType(path, fileCreator, fileType): 39 | """Set file creator and file type codes for a path. 40 | 41 | Note that if the ``xattr`` module is not installed, no action is 42 | taken but no error is raised. 43 | 44 | Args: 45 | path (str): A file path. 46 | fileCreator: A four-character file creator tag. 47 | fileType: A four-character file type tag. 48 | 49 | """ 50 | if xattr is not None: 51 | from fontTools.misc.textTools import pad 52 | 53 | if not all(len(s) == 4 for s in (fileCreator, fileType)): 54 | raise TypeError("arg must be string of 4 chars") 55 | finderInfo = pad(bytesjoin([fileType, fileCreator]), 32) 56 | xattr.setxattr(path, "com.apple.FinderInfo", finderInfo) 57 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/plistlib/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/fontTools/misc/plistlib/py.typed -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/py23.py: -------------------------------------------------------------------------------- 1 | """Python 2/3 compat layer leftovers.""" 2 | 3 | import decimal as _decimal 4 | import math as _math 5 | import warnings 6 | from contextlib import redirect_stderr, redirect_stdout 7 | from io import BytesIO 8 | from io import StringIO as UnicodeIO 9 | from types import SimpleNamespace 10 | 11 | from .textTools import Tag, bytechr, byteord, bytesjoin, strjoin, tobytes, tostr 12 | 13 | warnings.warn( 14 | "The py23 module has been deprecated and will be removed in a future release. " 15 | "Please update your code.", 16 | DeprecationWarning, 17 | ) 18 | 19 | __all__ = [ 20 | "basestring", 21 | "bytechr", 22 | "byteord", 23 | "BytesIO", 24 | "bytesjoin", 25 | "open", 26 | "Py23Error", 27 | "range", 28 | "RecursionError", 29 | "round", 30 | "SimpleNamespace", 31 | "StringIO", 32 | "strjoin", 33 | "Tag", 34 | "tobytes", 35 | "tostr", 36 | "tounicode", 37 | "unichr", 38 | "unicode", 39 | "UnicodeIO", 40 | "xrange", 41 | "zip", 42 | ] 43 | 44 | 45 | class Py23Error(NotImplementedError): 46 | pass 47 | 48 | 49 | RecursionError = RecursionError 50 | StringIO = UnicodeIO 51 | 52 | basestring = str 53 | isclose = _math.isclose 54 | isfinite = _math.isfinite 55 | open = open 56 | range = range 57 | round = round3 = round 58 | unichr = chr 59 | unicode = str 60 | zip = zip 61 | 62 | tounicode = tostr 63 | 64 | 65 | def xrange(*args, **kwargs): 66 | raise Py23Error("'xrange' is not defined. Use 'range' instead.") 67 | 68 | 69 | def round2(number, ndigits=None): 70 | """ 71 | Implementation of Python 2 built-in round() function. 72 | Rounds a number to a given precision in decimal digits (default 73 | 0 digits). The result is a floating point number. Values are rounded 74 | to the closest multiple of 10 to the power minus ndigits; if two 75 | multiples are equally close, rounding is done away from 0. 76 | ndigits may be negative. 77 | See Python 2 documentation: 78 | https://docs.python.org/2/library/functions.html?highlight=round#round 79 | """ 80 | if ndigits is None: 81 | ndigits = 0 82 | 83 | if ndigits < 0: 84 | exponent = 10 ** (-ndigits) 85 | quotient, remainder = divmod(number, exponent) 86 | if remainder >= exponent // 2 and number >= 0: 87 | quotient += 1 88 | return float(quotient * exponent) 89 | else: 90 | exponent = _decimal.Decimal("10") ** (-ndigits) 91 | 92 | d = _decimal.Decimal.from_float(number).quantize( 93 | exponent, rounding=_decimal.ROUND_HALF_UP 94 | ) 95 | 96 | return float(d) 97 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/roundTools.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various round-to-integer helpers. 3 | """ 4 | 5 | import math 6 | import functools 7 | import logging 8 | 9 | log = logging.getLogger(__name__) 10 | 11 | __all__ = [ 12 | "noRound", 13 | "otRound", 14 | "maybeRound", 15 | "roundFunc", 16 | ] 17 | 18 | 19 | def noRound(value): 20 | return value 21 | 22 | 23 | def otRound(value): 24 | """Round float value to nearest integer towards ``+Infinity``. 25 | 26 | The OpenType spec (in the section on `"normalization" of OpenType Font Variations `_) 27 | defines the required method for converting floating point values to 28 | fixed-point. In particular it specifies the following rounding strategy: 29 | 30 | for fractional values of 0.5 and higher, take the next higher integer; 31 | for other fractional values, truncate. 32 | 33 | This function rounds the floating-point value according to this strategy 34 | in preparation for conversion to fixed-point. 35 | 36 | Args: 37 | value (float): The input floating-point value. 38 | 39 | Returns 40 | float: The rounded value. 41 | """ 42 | # See this thread for how we ended up with this implementation: 43 | # https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166 44 | return int(math.floor(value + 0.5)) 45 | 46 | 47 | def maybeRound(v, tolerance, round=otRound): 48 | rounded = round(v) 49 | return rounded if abs(rounded - v) <= tolerance else v 50 | 51 | 52 | def roundFunc(tolerance, round=otRound): 53 | if tolerance < 0: 54 | raise ValueError("Rounding tolerance must be positive") 55 | 56 | if tolerance == 0: 57 | return noRound 58 | 59 | if tolerance >= 0.5: 60 | return round 61 | 62 | return functools.partial(maybeRound, tolerance=tolerance, round=round) 63 | 64 | 65 | def nearestMultipleShortestRepr(value: float, factor: float) -> str: 66 | """Round to nearest multiple of factor and return shortest decimal representation. 67 | 68 | This chooses the float that is closer to a multiple of the given factor while 69 | having the shortest decimal representation (the least number of fractional decimal 70 | digits). 71 | 72 | For example, given the following: 73 | 74 | >>> nearestMultipleShortestRepr(-0.61883544921875, 1.0/(1<<14)) 75 | '-0.61884' 76 | 77 | Useful when you need to serialize or print a fixed-point number (or multiples 78 | thereof, such as F2Dot14 fractions of 180 degrees in COLRv1 PaintRotate) in 79 | a human-readable form. 80 | 81 | Args: 82 | value (value): The value to be rounded and serialized. 83 | factor (float): The value which the result is a close multiple of. 84 | 85 | Returns: 86 | str: A compact string representation of the value. 87 | """ 88 | if not value: 89 | return "0.0" 90 | 91 | value = otRound(value / factor) * factor 92 | eps = 0.5 * factor 93 | lo = value - eps 94 | hi = value + eps 95 | # If the range of valid choices spans an integer, return the integer. 96 | if int(lo) != int(hi): 97 | return str(float(round(value))) 98 | 99 | fmt = "%.8f" 100 | lo = fmt % lo 101 | hi = fmt % hi 102 | assert len(lo) == len(hi) and lo != hi 103 | for i in range(len(lo)): 104 | if lo[i] != hi[i]: 105 | break 106 | period = lo.find(".") 107 | assert period < i 108 | fmt = "%%.%df" % (i - period) 109 | return fmt % value 110 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/textTools.py: -------------------------------------------------------------------------------- 1 | """fontTools.misc.textTools.py -- miscellaneous routines.""" 2 | 3 | 4 | import ast 5 | import string 6 | 7 | 8 | # alias kept for backward compatibility 9 | safeEval = ast.literal_eval 10 | 11 | 12 | class Tag(str): 13 | @staticmethod 14 | def transcode(blob): 15 | if isinstance(blob, bytes): 16 | blob = blob.decode("latin-1") 17 | return blob 18 | 19 | def __new__(self, content): 20 | return str.__new__(self, self.transcode(content)) 21 | 22 | def __ne__(self, other): 23 | return not self.__eq__(other) 24 | 25 | def __eq__(self, other): 26 | return str.__eq__(self, self.transcode(other)) 27 | 28 | def __hash__(self): 29 | return str.__hash__(self) 30 | 31 | def tobytes(self): 32 | return self.encode("latin-1") 33 | 34 | 35 | def readHex(content): 36 | """Convert a list of hex strings to binary data.""" 37 | return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, str))) 38 | 39 | 40 | def deHexStr(hexdata): 41 | """Convert a hex string to binary data.""" 42 | hexdata = strjoin(hexdata.split()) 43 | if len(hexdata) % 2: 44 | hexdata = hexdata + "0" 45 | data = [] 46 | for i in range(0, len(hexdata), 2): 47 | data.append(bytechr(int(hexdata[i : i + 2], 16))) 48 | return bytesjoin(data) 49 | 50 | 51 | def hexStr(data): 52 | """Convert binary data to a hex string.""" 53 | h = string.hexdigits 54 | r = "" 55 | for c in data: 56 | i = byteord(c) 57 | r = r + h[(i >> 4) & 0xF] + h[i & 0xF] 58 | return r 59 | 60 | 61 | def num2binary(l, bits=32): 62 | items = [] 63 | binary = "" 64 | for i in range(bits): 65 | if l & 0x1: 66 | binary = "1" + binary 67 | else: 68 | binary = "0" + binary 69 | l = l >> 1 70 | if not ((i + 1) % 8): 71 | items.append(binary) 72 | binary = "" 73 | if binary: 74 | items.append(binary) 75 | items.reverse() 76 | assert l in (0, -1), "number doesn't fit in number of bits" 77 | return " ".join(items) 78 | 79 | 80 | def binary2num(bin): 81 | bin = strjoin(bin.split()) 82 | l = 0 83 | for digit in bin: 84 | l = l << 1 85 | if digit != "0": 86 | l = l | 0x1 87 | return l 88 | 89 | 90 | def caselessSort(alist): 91 | """Return a sorted copy of a list. If there are only strings 92 | in the list, it will not consider case. 93 | """ 94 | 95 | try: 96 | return sorted(alist, key=lambda a: (a.lower(), a)) 97 | except TypeError: 98 | return sorted(alist) 99 | 100 | 101 | def pad(data, size): 102 | r"""Pad byte string 'data' with null bytes until its length is a 103 | multiple of 'size'. 104 | 105 | >>> len(pad(b'abcd', 4)) 106 | 4 107 | >>> len(pad(b'abcde', 2)) 108 | 6 109 | >>> len(pad(b'abcde', 4)) 110 | 8 111 | >>> pad(b'abcdef', 4) == b'abcdef\x00\x00' 112 | True 113 | """ 114 | data = tobytes(data) 115 | if size > 1: 116 | remainder = len(data) % size 117 | if remainder: 118 | data += b"\0" * (size - remainder) 119 | return data 120 | 121 | 122 | def tostr(s, encoding="ascii", errors="strict"): 123 | if not isinstance(s, str): 124 | return s.decode(encoding, errors) 125 | else: 126 | return s 127 | 128 | 129 | def tobytes(s, encoding="ascii", errors="strict"): 130 | if isinstance(s, str): 131 | return s.encode(encoding, errors) 132 | else: 133 | return bytes(s) 134 | 135 | 136 | def bytechr(n): 137 | return bytes([n]) 138 | 139 | 140 | def byteord(c): 141 | return c if isinstance(c, int) else ord(c) 142 | 143 | 144 | def strjoin(iterable, joiner=""): 145 | return tostr(joiner).join(iterable) 146 | 147 | 148 | def bytesjoin(iterable, joiner=b""): 149 | return tobytes(joiner).join(tobytes(item) for item in iterable) 150 | 151 | 152 | if __name__ == "__main__": 153 | import doctest, sys 154 | 155 | sys.exit(doctest.testmod().failed) 156 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/timeTools.py: -------------------------------------------------------------------------------- 1 | """fontTools.misc.timeTools.py -- tools for working with OpenType timestamps. 2 | """ 3 | 4 | import os 5 | import time 6 | from datetime import datetime, timezone 7 | import calendar 8 | 9 | 10 | epoch_diff = calendar.timegm((1904, 1, 1, 0, 0, 0, 0, 0, 0)) 11 | 12 | DAYNAMES = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 13 | MONTHNAMES = [ 14 | None, 15 | "Jan", 16 | "Feb", 17 | "Mar", 18 | "Apr", 19 | "May", 20 | "Jun", 21 | "Jul", 22 | "Aug", 23 | "Sep", 24 | "Oct", 25 | "Nov", 26 | "Dec", 27 | ] 28 | 29 | 30 | def asctime(t=None): 31 | """ 32 | Convert a tuple or struct_time representing a time as returned by gmtime() 33 | or localtime() to a 24-character string of the following form: 34 | 35 | >>> asctime(time.gmtime(0)) 36 | 'Thu Jan 1 00:00:00 1970' 37 | 38 | If t is not provided, the current time as returned by localtime() is used. 39 | Locale information is not used by asctime(). 40 | 41 | This is meant to normalise the output of the built-in time.asctime() across 42 | different platforms and Python versions. 43 | In Python 3.x, the day of the month is right-justified, whereas on Windows 44 | Python 2.7 it is padded with zeros. 45 | 46 | See https://github.com/fonttools/fonttools/issues/455 47 | """ 48 | if t is None: 49 | t = time.localtime() 50 | s = "%s %s %2s %s" % ( 51 | DAYNAMES[t.tm_wday], 52 | MONTHNAMES[t.tm_mon], 53 | t.tm_mday, 54 | time.strftime("%H:%M:%S %Y", t), 55 | ) 56 | return s 57 | 58 | 59 | def timestampToString(value): 60 | return asctime(time.gmtime(max(0, value + epoch_diff))) 61 | 62 | 63 | def timestampFromString(value): 64 | wkday, mnth = value[:7].split() 65 | t = datetime.strptime(value[7:], " %d %H:%M:%S %Y") 66 | t = t.replace(month=MONTHNAMES.index(mnth), tzinfo=timezone.utc) 67 | wkday_idx = DAYNAMES.index(wkday) 68 | assert t.weekday() == wkday_idx, '"' + value + '" has inconsistent weekday' 69 | return int(t.timestamp()) - epoch_diff 70 | 71 | 72 | def timestampNow(): 73 | # https://reproducible-builds.org/specs/source-date-epoch/ 74 | source_date_epoch = os.environ.get("SOURCE_DATE_EPOCH") 75 | if source_date_epoch is not None: 76 | return int(source_date_epoch) - epoch_diff 77 | return int(time.time() - epoch_diff) 78 | 79 | 80 | def timestampSinceEpoch(value): 81 | return int(value - epoch_diff) 82 | 83 | 84 | if __name__ == "__main__": 85 | import sys 86 | import doctest 87 | 88 | sys.exit(doctest.testmod().failed) 89 | -------------------------------------------------------------------------------- /STERA-X/fontTools/misc/treeTools.py: -------------------------------------------------------------------------------- 1 | """Generic tools for working with trees.""" 2 | 3 | from math import ceil, log 4 | 5 | 6 | def build_n_ary_tree(leaves, n): 7 | """Build N-ary tree from sequence of leaf nodes. 8 | 9 | Return a list of lists where each non-leaf node is a list containing 10 | max n nodes. 11 | """ 12 | if not leaves: 13 | return [] 14 | 15 | assert n > 1 16 | 17 | depth = ceil(log(len(leaves), n)) 18 | 19 | if depth <= 1: 20 | return list(leaves) 21 | 22 | # Fully populate complete subtrees of root until we have enough leaves left 23 | root = [] 24 | unassigned = None 25 | full_step = n ** (depth - 1) 26 | for i in range(0, len(leaves), full_step): 27 | subtree = leaves[i : i + full_step] 28 | if len(subtree) < full_step: 29 | unassigned = subtree 30 | break 31 | while len(subtree) > n: 32 | subtree = [subtree[k : k + n] for k in range(0, len(subtree), n)] 33 | root.append(subtree) 34 | 35 | if unassigned: 36 | # Recurse to fill the last subtree, which is the only partially populated one 37 | subtree = build_n_ary_tree(unassigned, n) 38 | if len(subtree) <= n - len(root): 39 | # replace last subtree with its children if they can still fit 40 | root.extend(subtree) 41 | else: 42 | root.append(subtree) 43 | assert len(root) <= n 44 | 45 | return root 46 | -------------------------------------------------------------------------------- /STERA-X/fontTools/mtiLib/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from fontTools.mtiLib import main 3 | 4 | if __name__ == "__main__": 5 | sys.exit(main()) 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/otlLib/__init__.py: -------------------------------------------------------------------------------- 1 | """OpenType Layout-related functionality.""" 2 | -------------------------------------------------------------------------------- /STERA-X/fontTools/otlLib/error.py: -------------------------------------------------------------------------------- 1 | class OpenTypeLibError(Exception): 2 | def __init__(self, message, location): 3 | Exception.__init__(self, message) 4 | self.location = location 5 | 6 | def __str__(self): 7 | message = Exception.__str__(self) 8 | if self.location: 9 | return f"{self.location}: {message}" 10 | else: 11 | return message 12 | -------------------------------------------------------------------------------- /STERA-X/fontTools/otlLib/maxContextCalc.py: -------------------------------------------------------------------------------- 1 | __all__ = ["maxCtxFont"] 2 | 3 | 4 | def maxCtxFont(font): 5 | """Calculate the usMaxContext value for an entire font.""" 6 | 7 | maxCtx = 0 8 | for tag in ("GSUB", "GPOS"): 9 | if tag not in font: 10 | continue 11 | table = font[tag].table 12 | if not table.LookupList: 13 | continue 14 | for lookup in table.LookupList.Lookup: 15 | for st in lookup.SubTable: 16 | maxCtx = maxCtxSubtable(maxCtx, tag, lookup.LookupType, st) 17 | return maxCtx 18 | 19 | 20 | def maxCtxSubtable(maxCtx, tag, lookupType, st): 21 | """Calculate usMaxContext based on a single lookup table (and an existing 22 | max value). 23 | """ 24 | 25 | # single positioning, single / multiple substitution 26 | if (tag == "GPOS" and lookupType == 1) or ( 27 | tag == "GSUB" and lookupType in (1, 2, 3) 28 | ): 29 | maxCtx = max(maxCtx, 1) 30 | 31 | # pair positioning 32 | elif tag == "GPOS" and lookupType == 2: 33 | maxCtx = max(maxCtx, 2) 34 | 35 | # ligatures 36 | elif tag == "GSUB" and lookupType == 4: 37 | for ligatures in st.ligatures.values(): 38 | for ligature in ligatures: 39 | maxCtx = max(maxCtx, ligature.CompCount) 40 | 41 | # context 42 | elif (tag == "GPOS" and lookupType == 7) or (tag == "GSUB" and lookupType == 5): 43 | maxCtx = maxCtxContextualSubtable(maxCtx, st, "Pos" if tag == "GPOS" else "Sub") 44 | 45 | # chained context 46 | elif (tag == "GPOS" and lookupType == 8) or (tag == "GSUB" and lookupType == 6): 47 | maxCtx = maxCtxContextualSubtable( 48 | maxCtx, st, "Pos" if tag == "GPOS" else "Sub", "Chain" 49 | ) 50 | 51 | # extensions 52 | elif (tag == "GPOS" and lookupType == 9) or (tag == "GSUB" and lookupType == 7): 53 | maxCtx = maxCtxSubtable(maxCtx, tag, st.ExtensionLookupType, st.ExtSubTable) 54 | 55 | # reverse-chained context 56 | elif tag == "GSUB" and lookupType == 8: 57 | maxCtx = maxCtxContextualRule(maxCtx, st, "Reverse") 58 | 59 | return maxCtx 60 | 61 | 62 | def maxCtxContextualSubtable(maxCtx, st, ruleType, chain=""): 63 | """Calculate usMaxContext based on a contextual feature subtable.""" 64 | 65 | if st.Format == 1: 66 | for ruleset in getattr(st, "%s%sRuleSet" % (chain, ruleType)): 67 | if ruleset is None: 68 | continue 69 | for rule in getattr(ruleset, "%s%sRule" % (chain, ruleType)): 70 | if rule is None: 71 | continue 72 | maxCtx = maxCtxContextualRule(maxCtx, rule, chain) 73 | 74 | elif st.Format == 2: 75 | for ruleset in getattr(st, "%s%sClassSet" % (chain, ruleType)): 76 | if ruleset is None: 77 | continue 78 | for rule in getattr(ruleset, "%s%sClassRule" % (chain, ruleType)): 79 | if rule is None: 80 | continue 81 | maxCtx = maxCtxContextualRule(maxCtx, rule, chain) 82 | 83 | elif st.Format == 3: 84 | maxCtx = maxCtxContextualRule(maxCtx, st, chain) 85 | 86 | return maxCtx 87 | 88 | 89 | def maxCtxContextualRule(maxCtx, st, chain): 90 | """Calculate usMaxContext based on a contextual feature rule.""" 91 | 92 | if not chain: 93 | return max(maxCtx, st.GlyphCount) 94 | elif chain == "Reverse": 95 | return max(maxCtx, st.GlyphCount + st.LookAheadGlyphCount) 96 | return max(maxCtx, st.InputGlyphCount + st.LookAheadGlyphCount) 97 | -------------------------------------------------------------------------------- /STERA-X/fontTools/otlLib/optimize/__init__.py: -------------------------------------------------------------------------------- 1 | from argparse import RawTextHelpFormatter 2 | from fontTools.otlLib.optimize.gpos import COMPRESSION_LEVEL, compact 3 | from fontTools.ttLib import TTFont 4 | 5 | 6 | def main(args=None): 7 | """Optimize the layout tables of an existing font""" 8 | from argparse import ArgumentParser 9 | 10 | from fontTools import configLogger 11 | 12 | parser = ArgumentParser( 13 | prog="otlLib.optimize", 14 | description=main.__doc__, 15 | formatter_class=RawTextHelpFormatter, 16 | ) 17 | parser.add_argument("font") 18 | parser.add_argument( 19 | "-o", metavar="OUTPUTFILE", dest="outfile", default=None, help="output file" 20 | ) 21 | parser.add_argument( 22 | "--gpos-compression-level", 23 | help=COMPRESSION_LEVEL.help, 24 | default=COMPRESSION_LEVEL.default, 25 | choices=list(range(10)), 26 | type=int, 27 | ) 28 | logging_group = parser.add_mutually_exclusive_group(required=False) 29 | logging_group.add_argument( 30 | "-v", "--verbose", action="store_true", help="Run more verbosely." 31 | ) 32 | logging_group.add_argument( 33 | "-q", "--quiet", action="store_true", help="Turn verbosity off." 34 | ) 35 | options = parser.parse_args(args) 36 | 37 | configLogger( 38 | level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO") 39 | ) 40 | 41 | font = TTFont(options.font) 42 | compact(font, options.gpos_compression_level) 43 | font.save(options.outfile or options.font) 44 | 45 | 46 | if __name__ == "__main__": 47 | import sys 48 | 49 | if len(sys.argv) > 1: 50 | sys.exit(main()) 51 | import doctest 52 | 53 | sys.exit(doctest.testmod().failed) 54 | -------------------------------------------------------------------------------- /STERA-X/fontTools/otlLib/optimize/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from fontTools.otlLib.optimize import main 3 | 4 | 5 | if __name__ == "__main__": 6 | sys.exit(main()) 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/__init__.py: -------------------------------------------------------------------------------- 1 | """Empty __init__.py file to signal Python this directory is a package.""" 2 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/areaPen.py: -------------------------------------------------------------------------------- 1 | """Calculate the area of a glyph.""" 2 | 3 | from fontTools.pens.basePen import BasePen 4 | 5 | 6 | __all__ = ["AreaPen"] 7 | 8 | 9 | class AreaPen(BasePen): 10 | def __init__(self, glyphset=None): 11 | BasePen.__init__(self, glyphset) 12 | self.value = 0 13 | 14 | def _moveTo(self, p0): 15 | self._p0 = self._startPoint = p0 16 | 17 | def _lineTo(self, p1): 18 | x0, y0 = self._p0 19 | x1, y1 = p1 20 | self.value -= (x1 - x0) * (y1 + y0) * 0.5 21 | self._p0 = p1 22 | 23 | def _qCurveToOne(self, p1, p2): 24 | # https://github.com/Pomax/bezierinfo/issues/44 25 | p0 = self._p0 26 | x0, y0 = p0[0], p0[1] 27 | x1, y1 = p1[0] - x0, p1[1] - y0 28 | x2, y2 = p2[0] - x0, p2[1] - y0 29 | self.value -= (x2 * y1 - x1 * y2) / 3 30 | self._lineTo(p2) 31 | self._p0 = p2 32 | 33 | def _curveToOne(self, p1, p2, p3): 34 | # https://github.com/Pomax/bezierinfo/issues/44 35 | p0 = self._p0 36 | x0, y0 = p0[0], p0[1] 37 | x1, y1 = p1[0] - x0, p1[1] - y0 38 | x2, y2 = p2[0] - x0, p2[1] - y0 39 | x3, y3 = p3[0] - x0, p3[1] - y0 40 | self.value -= (x1 * (-y2 - y3) + x2 * (y1 - 2 * y3) + x3 * (y1 + 2 * y2)) * 0.15 41 | self._lineTo(p3) 42 | self._p0 = p3 43 | 44 | def _closePath(self): 45 | self._lineTo(self._startPoint) 46 | del self._p0, self._startPoint 47 | 48 | def _endPath(self): 49 | if self._p0 != self._startPoint: 50 | # Area is not defined for open contours. 51 | raise NotImplementedError 52 | del self._p0, self._startPoint 53 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/boundsPen.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect 2 | from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds 3 | from fontTools.pens.basePen import BasePen 4 | 5 | 6 | __all__ = ["BoundsPen", "ControlBoundsPen"] 7 | 8 | 9 | class ControlBoundsPen(BasePen): 10 | 11 | """Pen to calculate the "control bounds" of a shape. This is the 12 | bounding box of all control points, so may be larger than the 13 | actual bounding box if there are curves that don't have points 14 | on their extremes. 15 | 16 | When the shape has been drawn, the bounds are available as the 17 | ``bounds`` attribute of the pen object. It's a 4-tuple:: 18 | 19 | (xMin, yMin, xMax, yMax). 20 | 21 | If ``ignoreSinglePoints`` is True, single points are ignored. 22 | """ 23 | 24 | def __init__(self, glyphSet, ignoreSinglePoints=False): 25 | BasePen.__init__(self, glyphSet) 26 | self.ignoreSinglePoints = ignoreSinglePoints 27 | self.init() 28 | 29 | def init(self): 30 | self.bounds = None 31 | self._start = None 32 | 33 | def _moveTo(self, pt): 34 | self._start = pt 35 | if not self.ignoreSinglePoints: 36 | self._addMoveTo() 37 | 38 | def _addMoveTo(self): 39 | if self._start is None: 40 | return 41 | bounds = self.bounds 42 | if bounds: 43 | self.bounds = updateBounds(bounds, self._start) 44 | else: 45 | x, y = self._start 46 | self.bounds = (x, y, x, y) 47 | self._start = None 48 | 49 | def _lineTo(self, pt): 50 | self._addMoveTo() 51 | self.bounds = updateBounds(self.bounds, pt) 52 | 53 | def _curveToOne(self, bcp1, bcp2, pt): 54 | self._addMoveTo() 55 | bounds = self.bounds 56 | bounds = updateBounds(bounds, bcp1) 57 | bounds = updateBounds(bounds, bcp2) 58 | bounds = updateBounds(bounds, pt) 59 | self.bounds = bounds 60 | 61 | def _qCurveToOne(self, bcp, pt): 62 | self._addMoveTo() 63 | bounds = self.bounds 64 | bounds = updateBounds(bounds, bcp) 65 | bounds = updateBounds(bounds, pt) 66 | self.bounds = bounds 67 | 68 | 69 | class BoundsPen(ControlBoundsPen): 70 | 71 | """Pen to calculate the bounds of a shape. It calculates the 72 | correct bounds even when the shape contains curves that don't 73 | have points on their extremes. This is somewhat slower to compute 74 | than the "control bounds". 75 | 76 | When the shape has been drawn, the bounds are available as the 77 | ``bounds`` attribute of the pen object. It's a 4-tuple:: 78 | 79 | (xMin, yMin, xMax, yMax) 80 | """ 81 | 82 | def _curveToOne(self, bcp1, bcp2, pt): 83 | self._addMoveTo() 84 | bounds = self.bounds 85 | bounds = updateBounds(bounds, pt) 86 | if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds): 87 | bounds = unionRect( 88 | bounds, calcCubicBounds(self._getCurrentPoint(), bcp1, bcp2, pt) 89 | ) 90 | self.bounds = bounds 91 | 92 | def _qCurveToOne(self, bcp, pt): 93 | self._addMoveTo() 94 | bounds = self.bounds 95 | bounds = updateBounds(bounds, pt) 96 | if not pointInRect(bcp, bounds): 97 | bounds = unionRect( 98 | bounds, calcQuadraticBounds(self._getCurrentPoint(), bcp, pt) 99 | ) 100 | self.bounds = bounds 101 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/cairoPen.py: -------------------------------------------------------------------------------- 1 | """Pen to draw to a Cairo graphics library context.""" 2 | 3 | from fontTools.pens.basePen import BasePen 4 | 5 | 6 | __all__ = ["CairoPen"] 7 | 8 | 9 | class CairoPen(BasePen): 10 | """Pen to draw to a Cairo graphics library context.""" 11 | 12 | def __init__(self, glyphSet, context): 13 | BasePen.__init__(self, glyphSet) 14 | self.context = context 15 | 16 | def _moveTo(self, p): 17 | self.context.move_to(*p) 18 | 19 | def _lineTo(self, p): 20 | self.context.line_to(*p) 21 | 22 | def _curveToOne(self, p1, p2, p3): 23 | self.context.curve_to(*p1, *p2, *p3) 24 | 25 | def _closePath(self): 26 | self.context.close_path() 27 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/cocoaPen.py: -------------------------------------------------------------------------------- 1 | from fontTools.pens.basePen import BasePen 2 | 3 | 4 | __all__ = ["CocoaPen"] 5 | 6 | 7 | class CocoaPen(BasePen): 8 | def __init__(self, glyphSet, path=None): 9 | BasePen.__init__(self, glyphSet) 10 | if path is None: 11 | from AppKit import NSBezierPath 12 | 13 | path = NSBezierPath.bezierPath() 14 | self.path = path 15 | 16 | def _moveTo(self, p): 17 | self.path.moveToPoint_(p) 18 | 19 | def _lineTo(self, p): 20 | self.path.lineToPoint_(p) 21 | 22 | def _curveToOne(self, p1, p2, p3): 23 | self.path.curveToPoint_controlPoint1_controlPoint2_(p3, p1, p2) 24 | 25 | def _closePath(self): 26 | self.path.closePath() 27 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/hashPointPen.py: -------------------------------------------------------------------------------- 1 | # Modified from https://github.com/adobe-type-tools/psautohint/blob/08b346865710ed3c172f1eb581d6ef243b203f99/python/psautohint/ufoFont.py#L800-L838 2 | import hashlib 3 | 4 | from fontTools.pens.basePen import MissingComponentError 5 | from fontTools.pens.pointPen import AbstractPointPen 6 | 7 | 8 | class HashPointPen(AbstractPointPen): 9 | """ 10 | This pen can be used to check if a glyph's contents (outlines plus 11 | components) have changed. 12 | 13 | Components are added as the original outline plus each composite's 14 | transformation. 15 | 16 | Example: You have some TrueType hinting code for a glyph which you want to 17 | compile. The hinting code specifies a hash value computed with HashPointPen 18 | that was valid for the glyph's outlines at the time the hinting code was 19 | written. Now you can calculate the hash for the glyph's current outlines to 20 | check if the outlines have changed, which would probably make the hinting 21 | code invalid. 22 | 23 | > glyph = ufo[name] 24 | > hash_pen = HashPointPen(glyph.width, ufo) 25 | > glyph.drawPoints(hash_pen) 26 | > ttdata = glyph.lib.get("public.truetype.instructions", None) 27 | > stored_hash = ttdata.get("id", None) # The hash is stored in the "id" key 28 | > if stored_hash is None or stored_hash != hash_pen.hash: 29 | > logger.error(f"Glyph hash mismatch, glyph '{name}' will have no instructions in font.") 30 | > else: 31 | > # The hash values are identical, the outline has not changed. 32 | > # Compile the hinting code ... 33 | > pass 34 | """ 35 | 36 | def __init__(self, glyphWidth=0, glyphSet=None): 37 | self.glyphset = glyphSet 38 | self.data = ["w%s" % round(glyphWidth, 9)] 39 | 40 | @property 41 | def hash(self): 42 | data = "".join(self.data) 43 | if len(data) >= 128: 44 | data = hashlib.sha512(data.encode("ascii")).hexdigest() 45 | return data 46 | 47 | def beginPath(self, identifier=None, **kwargs): 48 | pass 49 | 50 | def endPath(self): 51 | self.data.append("|") 52 | 53 | def addPoint( 54 | self, 55 | pt, 56 | segmentType=None, 57 | smooth=False, 58 | name=None, 59 | identifier=None, 60 | **kwargs, 61 | ): 62 | if segmentType is None: 63 | pt_type = "o" # offcurve 64 | else: 65 | pt_type = segmentType[0] 66 | self.data.append(f"{pt_type}{pt[0]:g}{pt[1]:+g}") 67 | 68 | def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs): 69 | tr = "".join([f"{t:+}" for t in transformation]) 70 | self.data.append("[") 71 | try: 72 | self.glyphset[baseGlyphName].drawPoints(self) 73 | except KeyError: 74 | raise MissingComponentError(baseGlyphName) 75 | self.data.append(f"({tr})]") 76 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/perimeterPen.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Calculate the perimeter of a glyph.""" 3 | 4 | from fontTools.pens.basePen import BasePen 5 | from fontTools.misc.bezierTools import ( 6 | approximateQuadraticArcLengthC, 7 | calcQuadraticArcLengthC, 8 | approximateCubicArcLengthC, 9 | calcCubicArcLengthC, 10 | ) 11 | import math 12 | 13 | 14 | __all__ = ["PerimeterPen"] 15 | 16 | 17 | def _distance(p0, p1): 18 | return math.hypot(p0[0] - p1[0], p0[1] - p1[1]) 19 | 20 | 21 | class PerimeterPen(BasePen): 22 | def __init__(self, glyphset=None, tolerance=0.005): 23 | BasePen.__init__(self, glyphset) 24 | self.value = 0 25 | self.tolerance = tolerance 26 | 27 | # Choose which algorithm to use for quadratic and for cubic. 28 | # Quadrature is faster but has fixed error characteristic with no strong 29 | # error bound. The cutoff points are derived empirically. 30 | self._addCubic = ( 31 | self._addCubicQuadrature if tolerance >= 0.0015 else self._addCubicRecursive 32 | ) 33 | self._addQuadratic = ( 34 | self._addQuadraticQuadrature 35 | if tolerance >= 0.00075 36 | else self._addQuadraticExact 37 | ) 38 | 39 | def _moveTo(self, p0): 40 | self.__startPoint = p0 41 | 42 | def _closePath(self): 43 | p0 = self._getCurrentPoint() 44 | if p0 != self.__startPoint: 45 | self._lineTo(self.__startPoint) 46 | 47 | def _lineTo(self, p1): 48 | p0 = self._getCurrentPoint() 49 | self.value += _distance(p0, p1) 50 | 51 | def _addQuadraticExact(self, c0, c1, c2): 52 | self.value += calcQuadraticArcLengthC(c0, c1, c2) 53 | 54 | def _addQuadraticQuadrature(self, c0, c1, c2): 55 | self.value += approximateQuadraticArcLengthC(c0, c1, c2) 56 | 57 | def _qCurveToOne(self, p1, p2): 58 | p0 = self._getCurrentPoint() 59 | self._addQuadratic(complex(*p0), complex(*p1), complex(*p2)) 60 | 61 | def _addCubicRecursive(self, c0, c1, c2, c3): 62 | self.value += calcCubicArcLengthC(c0, c1, c2, c3, self.tolerance) 63 | 64 | def _addCubicQuadrature(self, c0, c1, c2, c3): 65 | self.value += approximateCubicArcLengthC(c0, c1, c2, c3) 66 | 67 | def _curveToOne(self, p1, p2, p3): 68 | p0 = self._getCurrentPoint() 69 | self._addCubic(complex(*p0), complex(*p1), complex(*p2), complex(*p3)) 70 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/qtPen.py: -------------------------------------------------------------------------------- 1 | from fontTools.pens.basePen import BasePen 2 | 3 | 4 | __all__ = ["QtPen"] 5 | 6 | 7 | class QtPen(BasePen): 8 | def __init__(self, glyphSet, path=None): 9 | BasePen.__init__(self, glyphSet) 10 | if path is None: 11 | from PyQt5.QtGui import QPainterPath 12 | 13 | path = QPainterPath() 14 | self.path = path 15 | 16 | def _moveTo(self, p): 17 | self.path.moveTo(*p) 18 | 19 | def _lineTo(self, p): 20 | self.path.lineTo(*p) 21 | 22 | def _curveToOne(self, p1, p2, p3): 23 | self.path.cubicTo(*p1, *p2, *p3) 24 | 25 | def _qCurveToOne(self, p1, p2): 26 | self.path.quadTo(*p1, *p2) 27 | 28 | def _closePath(self): 29 | self.path.closeSubpath() 30 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/quartzPen.py: -------------------------------------------------------------------------------- 1 | from fontTools.pens.basePen import BasePen 2 | 3 | from Quartz.CoreGraphics import CGPathCreateMutable, CGPathMoveToPoint 4 | from Quartz.CoreGraphics import CGPathAddLineToPoint, CGPathAddCurveToPoint 5 | from Quartz.CoreGraphics import CGPathAddQuadCurveToPoint, CGPathCloseSubpath 6 | 7 | 8 | __all__ = ["QuartzPen"] 9 | 10 | 11 | class QuartzPen(BasePen): 12 | 13 | """A pen that creates a CGPath 14 | 15 | Parameters 16 | - path: an optional CGPath to add to 17 | - xform: an optional CGAffineTransform to apply to the path 18 | """ 19 | 20 | def __init__(self, glyphSet, path=None, xform=None): 21 | BasePen.__init__(self, glyphSet) 22 | if path is None: 23 | path = CGPathCreateMutable() 24 | self.path = path 25 | self.xform = xform 26 | 27 | def _moveTo(self, pt): 28 | x, y = pt 29 | CGPathMoveToPoint(self.path, self.xform, x, y) 30 | 31 | def _lineTo(self, pt): 32 | x, y = pt 33 | CGPathAddLineToPoint(self.path, self.xform, x, y) 34 | 35 | def _curveToOne(self, p1, p2, p3): 36 | (x1, y1), (x2, y2), (x3, y3) = p1, p2, p3 37 | CGPathAddCurveToPoint(self.path, self.xform, x1, y1, x2, y2, x3, y3) 38 | 39 | def _qCurveToOne(self, p1, p2): 40 | (x1, y1), (x2, y2) = p1, p2 41 | CGPathAddQuadCurveToPoint(self.path, self.xform, x1, y1, x2, y2) 42 | 43 | def _closePath(self): 44 | CGPathCloseSubpath(self.path) 45 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/reportLabPen.py: -------------------------------------------------------------------------------- 1 | from fontTools.pens.basePen import BasePen 2 | from reportlab.graphics.shapes import Path 3 | 4 | 5 | __all__ = ["ReportLabPen"] 6 | 7 | 8 | class ReportLabPen(BasePen): 9 | 10 | """A pen for drawing onto a ``reportlab.graphics.shapes.Path`` object.""" 11 | 12 | def __init__(self, glyphSet, path=None): 13 | BasePen.__init__(self, glyphSet) 14 | if path is None: 15 | path = Path() 16 | self.path = path 17 | 18 | def _moveTo(self, p): 19 | (x, y) = p 20 | self.path.moveTo(x, y) 21 | 22 | def _lineTo(self, p): 23 | (x, y) = p 24 | self.path.lineTo(x, y) 25 | 26 | def _curveToOne(self, p1, p2, p3): 27 | (x1, y1) = p1 28 | (x2, y2) = p2 29 | (x3, y3) = p3 30 | self.path.curveTo(x1, y1, x2, y2, x3, y3) 31 | 32 | def _closePath(self): 33 | self.path.closePath() 34 | 35 | 36 | if __name__ == "__main__": 37 | import sys 38 | 39 | if len(sys.argv) < 3: 40 | print( 41 | "Usage: reportLabPen.py []" 42 | ) 43 | print( 44 | " If no image file name is created, by default .png is created." 45 | ) 46 | print(" example: reportLabPen.py Arial.TTF R test.png") 47 | print( 48 | " (The file format will be PNG, regardless of the image file name supplied)" 49 | ) 50 | sys.exit(0) 51 | 52 | from fontTools.ttLib import TTFont 53 | from reportlab.lib import colors 54 | 55 | path = sys.argv[1] 56 | glyphName = sys.argv[2] 57 | if len(sys.argv) > 3: 58 | imageFile = sys.argv[3] 59 | else: 60 | imageFile = "%s.png" % glyphName 61 | 62 | font = TTFont(path) # it would work just as well with fontTools.t1Lib.T1Font 63 | gs = font.getGlyphSet() 64 | pen = ReportLabPen(gs, Path(fillColor=colors.red, strokeWidth=5)) 65 | g = gs[glyphName] 66 | g.draw(pen) 67 | 68 | w, h = g.width, 1000 69 | from reportlab.graphics import renderPM 70 | from reportlab.graphics.shapes import Group, Drawing, scale 71 | 72 | # Everything is wrapped in a group to allow transformations. 73 | g = Group(pen.path) 74 | g.translate(0, 200) 75 | g.scale(0.3, 0.3) 76 | 77 | d = Drawing(w, h) 78 | d.add(g) 79 | 80 | renderPM.drawToFile(d, imageFile, fmt="PNG") 81 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/roundingPen.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.roundTools import otRound 2 | from fontTools.misc.transform import Transform 3 | from fontTools.pens.filterPen import FilterPen, FilterPointPen 4 | 5 | 6 | __all__ = ["RoundingPen", "RoundingPointPen"] 7 | 8 | 9 | class RoundingPen(FilterPen): 10 | """ 11 | Filter pen that rounds point coordinates and component XY offsets to integer. 12 | 13 | >>> from fontTools.pens.recordingPen import RecordingPen 14 | >>> recpen = RecordingPen() 15 | >>> roundpen = RoundingPen(recpen) 16 | >>> roundpen.moveTo((0.4, 0.6)) 17 | >>> roundpen.lineTo((1.6, 2.5)) 18 | >>> roundpen.qCurveTo((2.4, 4.6), (3.3, 5.7), (4.9, 6.1)) 19 | >>> roundpen.curveTo((6.4, 8.6), (7.3, 9.7), (8.9, 10.1)) 20 | >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5)) 21 | >>> recpen.value == [ 22 | ... ('moveTo', ((0, 1),)), 23 | ... ('lineTo', ((2, 3),)), 24 | ... ('qCurveTo', ((2, 5), (3, 6), (5, 6))), 25 | ... ('curveTo', ((6, 9), (7, 10), (9, 10))), 26 | ... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10))), 27 | ... ] 28 | True 29 | """ 30 | 31 | def __init__(self, outPen, roundFunc=otRound): 32 | super().__init__(outPen) 33 | self.roundFunc = roundFunc 34 | 35 | def moveTo(self, pt): 36 | self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1]))) 37 | 38 | def lineTo(self, pt): 39 | self._outPen.lineTo((self.roundFunc(pt[0]), self.roundFunc(pt[1]))) 40 | 41 | def curveTo(self, *points): 42 | self._outPen.curveTo( 43 | *((self.roundFunc(x), self.roundFunc(y)) for x, y in points) 44 | ) 45 | 46 | def qCurveTo(self, *points): 47 | self._outPen.qCurveTo( 48 | *((self.roundFunc(x), self.roundFunc(y)) for x, y in points) 49 | ) 50 | 51 | def addComponent(self, glyphName, transformation): 52 | self._outPen.addComponent( 53 | glyphName, 54 | Transform( 55 | *transformation[:4], 56 | self.roundFunc(transformation[4]), 57 | self.roundFunc(transformation[5]), 58 | ), 59 | ) 60 | 61 | 62 | class RoundingPointPen(FilterPointPen): 63 | """ 64 | Filter point pen that rounds point coordinates and component XY offsets to integer. 65 | 66 | >>> from fontTools.pens.recordingPen import RecordingPointPen 67 | >>> recpen = RecordingPointPen() 68 | >>> roundpen = RoundingPointPen(recpen) 69 | >>> roundpen.beginPath() 70 | >>> roundpen.addPoint((0.4, 0.6), 'line') 71 | >>> roundpen.addPoint((1.6, 2.5), 'line') 72 | >>> roundpen.addPoint((2.4, 4.6)) 73 | >>> roundpen.addPoint((3.3, 5.7)) 74 | >>> roundpen.addPoint((4.9, 6.1), 'qcurve') 75 | >>> roundpen.endPath() 76 | >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5)) 77 | >>> recpen.value == [ 78 | ... ('beginPath', (), {}), 79 | ... ('addPoint', ((0, 1), 'line', False, None), {}), 80 | ... ('addPoint', ((2, 3), 'line', False, None), {}), 81 | ... ('addPoint', ((2, 5), None, False, None), {}), 82 | ... ('addPoint', ((3, 6), None, False, None), {}), 83 | ... ('addPoint', ((5, 6), 'qcurve', False, None), {}), 84 | ... ('endPath', (), {}), 85 | ... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10)), {}), 86 | ... ] 87 | True 88 | """ 89 | 90 | def __init__(self, outPen, roundFunc=otRound): 91 | super().__init__(outPen) 92 | self.roundFunc = roundFunc 93 | 94 | def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): 95 | self._outPen.addPoint( 96 | (self.roundFunc(pt[0]), self.roundFunc(pt[1])), 97 | segmentType=segmentType, 98 | smooth=smooth, 99 | name=name, 100 | **kwargs, 101 | ) 102 | 103 | def addComponent(self, baseGlyphName, transformation, **kwargs): 104 | self._outPen.addComponent( 105 | baseGlyphName, 106 | Transform( 107 | *transformation[:4], 108 | self.roundFunc(transformation[4]), 109 | self.roundFunc(transformation[5]), 110 | ), 111 | **kwargs, 112 | ) 113 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/statisticsPen.py: -------------------------------------------------------------------------------- 1 | """Pen calculating area, center of mass, variance and standard-deviation, 2 | covariance and correlation, and slant, of glyph shapes.""" 3 | import math 4 | from fontTools.pens.momentsPen import MomentsPen 5 | 6 | __all__ = ["StatisticsPen"] 7 | 8 | 9 | class StatisticsPen(MomentsPen): 10 | 11 | """Pen calculating area, center of mass, variance and 12 | standard-deviation, covariance and correlation, and slant, 13 | of glyph shapes. 14 | 15 | Note that all the calculated values are 'signed'. Ie. if the 16 | glyph shape is self-intersecting, the values are not correct 17 | (but well-defined). As such, area will be negative if contour 18 | directions are clockwise. Moreover, variance might be negative 19 | if the shapes are self-intersecting in certain ways.""" 20 | 21 | def __init__(self, glyphset=None): 22 | MomentsPen.__init__(self, glyphset=glyphset) 23 | self.__zero() 24 | 25 | def _closePath(self): 26 | MomentsPen._closePath(self) 27 | self.__update() 28 | 29 | def __zero(self): 30 | self.meanX = 0 31 | self.meanY = 0 32 | self.varianceX = 0 33 | self.varianceY = 0 34 | self.stddevX = 0 35 | self.stddevY = 0 36 | self.covariance = 0 37 | self.correlation = 0 38 | self.slant = 0 39 | 40 | def __update(self): 41 | 42 | area = self.area 43 | if not area: 44 | self.__zero() 45 | return 46 | 47 | # Center of mass 48 | # https://en.wikipedia.org/wiki/Center_of_mass#A_continuous_volume 49 | self.meanX = meanX = self.momentX / area 50 | self.meanY = meanY = self.momentY / area 51 | 52 | # Var(X) = E[X^2] - E[X]^2 53 | self.varianceX = varianceX = self.momentXX / area - meanX**2 54 | self.varianceY = varianceY = self.momentYY / area - meanY**2 55 | 56 | self.stddevX = stddevX = math.copysign(abs(varianceX) ** 0.5, varianceX) 57 | self.stddevY = stddevY = math.copysign(abs(varianceY) ** 0.5, varianceY) 58 | 59 | # Covariance(X,Y) = ( E[X.Y] - E[X]E[Y] ) 60 | self.covariance = covariance = self.momentXY / area - meanX * meanY 61 | 62 | # Correlation(X,Y) = Covariance(X,Y) / ( stddev(X) * stddev(Y) ) 63 | # https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient 64 | if stddevX * stddevY == 0: 65 | correlation = float("NaN") 66 | else: 67 | correlation = covariance / (stddevX * stddevY) 68 | self.correlation = correlation if abs(correlation) > 1e-3 else 0 69 | 70 | slant = covariance / varianceY if varianceY != 0 else float("NaN") 71 | self.slant = slant if abs(slant) > 1e-3 else 0 72 | 73 | 74 | def _test(glyphset, upem, glyphs): 75 | from fontTools.pens.transformPen import TransformPen 76 | from fontTools.misc.transform import Scale 77 | 78 | print("upem", upem) 79 | 80 | for glyph_name in glyphs: 81 | print() 82 | print("glyph:", glyph_name) 83 | glyph = glyphset[glyph_name] 84 | pen = StatisticsPen(glyphset=glyphset) 85 | transformer = TransformPen(pen, Scale(1.0 / upem)) 86 | glyph.draw(transformer) 87 | for item in [ 88 | "area", 89 | "momentX", 90 | "momentY", 91 | "momentXX", 92 | "momentYY", 93 | "momentXY", 94 | "meanX", 95 | "meanY", 96 | "varianceX", 97 | "varianceY", 98 | "stddevX", 99 | "stddevY", 100 | "covariance", 101 | "correlation", 102 | "slant", 103 | ]: 104 | print("%s: %g" % (item, getattr(pen, item))) 105 | 106 | 107 | def main(args): 108 | if not args: 109 | return 110 | filename, glyphs = args[0], args[1:] 111 | from fontTools.ttLib import TTFont 112 | 113 | font = TTFont(filename) 114 | if not glyphs: 115 | glyphs = font.getGlyphOrder() 116 | _test(font.getGlyphSet(), font["head"].unitsPerEm, glyphs) 117 | 118 | 119 | if __name__ == "__main__": 120 | import sys 121 | 122 | main(sys.argv[1:]) 123 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/t2CharStringPen.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009 Type Supply LLC 2 | # Author: Tal Leming 3 | 4 | from fontTools.misc.roundTools import otRound, roundFunc 5 | from fontTools.misc.psCharStrings import T2CharString 6 | from fontTools.pens.basePen import BasePen 7 | from fontTools.cffLib.specializer import specializeCommands, commandsToProgram 8 | 9 | 10 | class T2CharStringPen(BasePen): 11 | """Pen to draw Type 2 CharStrings. 12 | 13 | The 'roundTolerance' argument controls the rounding of point coordinates. 14 | It is defined as the maximum absolute difference between the original 15 | float and the rounded integer value. 16 | The default tolerance of 0.5 means that all floats are rounded to integer; 17 | a value of 0 disables rounding; values in between will only round floats 18 | which are close to their integral part within the tolerated range. 19 | """ 20 | 21 | def __init__(self, width, glyphSet, roundTolerance=0.5, CFF2=False): 22 | super(T2CharStringPen, self).__init__(glyphSet) 23 | self.round = roundFunc(roundTolerance) 24 | self._CFF2 = CFF2 25 | self._width = width 26 | self._commands = [] 27 | self._p0 = (0, 0) 28 | 29 | def _p(self, pt): 30 | p0 = self._p0 31 | pt = self._p0 = (self.round(pt[0]), self.round(pt[1])) 32 | return [pt[0] - p0[0], pt[1] - p0[1]] 33 | 34 | def _moveTo(self, pt): 35 | self._commands.append(("rmoveto", self._p(pt))) 36 | 37 | def _lineTo(self, pt): 38 | self._commands.append(("rlineto", self._p(pt))) 39 | 40 | def _curveToOne(self, pt1, pt2, pt3): 41 | _p = self._p 42 | self._commands.append(("rrcurveto", _p(pt1) + _p(pt2) + _p(pt3))) 43 | 44 | def _closePath(self): 45 | pass 46 | 47 | def _endPath(self): 48 | pass 49 | 50 | def getCharString(self, private=None, globalSubrs=None, optimize=True): 51 | commands = self._commands 52 | if optimize: 53 | maxstack = 48 if not self._CFF2 else 513 54 | commands = specializeCommands( 55 | commands, generalizeFirst=False, maxstack=maxstack 56 | ) 57 | program = commandsToProgram(commands) 58 | if self._width is not None: 59 | assert ( 60 | not self._CFF2 61 | ), "CFF2 does not allow encoding glyph width in CharString." 62 | program.insert(0, otRound(self._width)) 63 | if not self._CFF2: 64 | program.append("endchar") 65 | charString = T2CharString( 66 | program=program, private=private, globalSubrs=globalSubrs 67 | ) 68 | return charString 69 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/teePen.py: -------------------------------------------------------------------------------- 1 | """Pen multiplexing drawing to one or more pens.""" 2 | from fontTools.pens.basePen import AbstractPen 3 | 4 | 5 | __all__ = ["TeePen"] 6 | 7 | 8 | class TeePen(AbstractPen): 9 | """Pen multiplexing drawing to one or more pens. 10 | 11 | Use either as TeePen(pen1, pen2, ...) or TeePen(iterableOfPens).""" 12 | 13 | def __init__(self, *pens): 14 | if len(pens) == 1: 15 | pens = pens[0] 16 | self.pens = pens 17 | 18 | def moveTo(self, p0): 19 | for pen in self.pens: 20 | pen.moveTo(p0) 21 | 22 | def lineTo(self, p1): 23 | for pen in self.pens: 24 | pen.lineTo(p1) 25 | 26 | def qCurveTo(self, *points): 27 | for pen in self.pens: 28 | pen.qCurveTo(*points) 29 | 30 | def curveTo(self, *points): 31 | for pen in self.pens: 32 | pen.curveTo(*points) 33 | 34 | def closePath(self): 35 | for pen in self.pens: 36 | pen.closePath() 37 | 38 | def endPath(self): 39 | for pen in self.pens: 40 | pen.endPath() 41 | 42 | def addComponent(self, glyphName, transformation): 43 | for pen in self.pens: 44 | pen.addComponent(glyphName, transformation) 45 | 46 | 47 | if __name__ == "__main__": 48 | from fontTools.pens.basePen import _TestPen 49 | 50 | pen = TeePen(_TestPen(), _TestPen()) 51 | pen.moveTo((0, 0)) 52 | pen.lineTo((0, 100)) 53 | pen.curveTo((50, 75), (60, 50), (50, 25)) 54 | pen.closePath() 55 | -------------------------------------------------------------------------------- /STERA-X/fontTools/pens/wxPen.py: -------------------------------------------------------------------------------- 1 | from fontTools.pens.basePen import BasePen 2 | 3 | 4 | __all__ = ["WxPen"] 5 | 6 | 7 | class WxPen(BasePen): 8 | def __init__(self, glyphSet, path=None): 9 | BasePen.__init__(self, glyphSet) 10 | if path is None: 11 | import wx 12 | 13 | path = wx.GraphicsRenderer.GetDefaultRenderer().CreatePath() 14 | self.path = path 15 | 16 | def _moveTo(self, p): 17 | self.path.MoveToPoint(*p) 18 | 19 | def _lineTo(self, p): 20 | self.path.AddLineToPoint(*p) 21 | 22 | def _curveToOne(self, p1, p2, p3): 23 | self.path.AddCurveToPoint(*p1 + p2 + p3) 24 | 25 | def _qCurveToOne(self, p1, p2): 26 | self.path.AddQuadCurveToPoint(*p1 + p2) 27 | 28 | def _closePath(self): 29 | self.path.CloseSubpath() 30 | -------------------------------------------------------------------------------- /STERA-X/fontTools/qu2cu/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. All Rights Reserved. 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. 14 | 15 | from .qu2cu import * 16 | -------------------------------------------------------------------------------- /STERA-X/fontTools/qu2cu/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from .cli import main 4 | 5 | 6 | if __name__ == "__main__": 7 | sys.exit(main()) 8 | -------------------------------------------------------------------------------- /STERA-X/fontTools/qu2cu/benchmark.py: -------------------------------------------------------------------------------- 1 | """Benchmark the qu2cu algorithm performance.""" 2 | 3 | from .qu2cu import * 4 | from fontTools.cu2qu import curve_to_quadratic 5 | import random 6 | import timeit 7 | 8 | MAX_ERR = 0.5 9 | NUM_CURVES = 5 10 | 11 | 12 | def generate_curves(n): 13 | points = [ 14 | tuple(float(random.randint(0, 2048)) for coord in range(2)) 15 | for point in range(1 + 3 * n) 16 | ] 17 | curves = [] 18 | for i in range(n): 19 | curves.append(tuple(points[i * 3 : i * 3 + 4])) 20 | return curves 21 | 22 | 23 | def setup_quadratic_to_curves(): 24 | curves = generate_curves(NUM_CURVES) 25 | quadratics = [curve_to_quadratic(curve, MAX_ERR) for curve in curves] 26 | return quadratics, MAX_ERR 27 | 28 | 29 | def run_benchmark(module, function, setup_suffix="", repeat=25, number=1): 30 | setup_func = "setup_" + function 31 | if setup_suffix: 32 | print("%s with %s:" % (function, setup_suffix), end="") 33 | setup_func += "_" + setup_suffix 34 | else: 35 | print("%s:" % function, end="") 36 | 37 | def wrapper(function, setup_func): 38 | function = globals()[function] 39 | setup_func = globals()[setup_func] 40 | 41 | def wrapped(): 42 | return function(*setup_func()) 43 | 44 | return wrapped 45 | 46 | results = timeit.repeat(wrapper(function, setup_func), repeat=repeat, number=number) 47 | print("\t%5.1fus" % (min(results) * 1000000.0 / number)) 48 | 49 | 50 | def main(): 51 | """Benchmark the qu2cu algorithm performance.""" 52 | run_benchmark("qu2cu", "quadratic_to_curves") 53 | 54 | 55 | if __name__ == "__main__": 56 | random.seed(1) 57 | main() 58 | -------------------------------------------------------------------------------- /STERA-X/fontTools/qu2cu/cli.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | import logging 4 | from fontTools.misc.cliTools import makeOutputFileName 5 | from fontTools.ttLib import TTFont 6 | from fontTools.pens.qu2cuPen import Qu2CuPen 7 | from fontTools.pens.ttGlyphPen import TTGlyphPen 8 | import fontTools 9 | 10 | 11 | logger = logging.getLogger("fontTools.qu2cu") 12 | 13 | 14 | def _font_to_cubic(input_path, output_path=None, **kwargs): 15 | font = TTFont(input_path) 16 | logger.info("Converting curves for %s", input_path) 17 | 18 | stats = {} if kwargs["dump_stats"] else None 19 | qu2cu_kwargs = { 20 | "stats": stats, 21 | "max_err": kwargs["max_err_em"] * font["head"].unitsPerEm, 22 | "all_cubic": kwargs["all_cubic"], 23 | } 24 | 25 | assert "gvar" not in font, "Cannot convert variable font" 26 | glyphSet = font.getGlyphSet() 27 | glyphOrder = font.getGlyphOrder() 28 | glyf = font["glyf"] 29 | for glyphName in glyphOrder: 30 | glyph = glyphSet[glyphName] 31 | ttpen = TTGlyphPen(glyphSet) 32 | pen = Qu2CuPen(ttpen, **qu2cu_kwargs) 33 | glyph.draw(pen) 34 | glyf[glyphName] = ttpen.glyph(dropImpliedOnCurves=True) 35 | 36 | font["head"].glyphDataFormat = 1 37 | 38 | if kwargs["dump_stats"]: 39 | logger.info("Stats: %s", stats) 40 | 41 | logger.info("Saving %s", output_path) 42 | font.save(output_path) 43 | 44 | 45 | def main(args=None): 46 | parser = argparse.ArgumentParser(prog="qu2cu") 47 | parser.add_argument("--version", action="version", version=fontTools.__version__) 48 | parser.add_argument( 49 | "infiles", 50 | nargs="+", 51 | metavar="INPUT", 52 | help="one or more input TTF source file(s).", 53 | ) 54 | parser.add_argument("-v", "--verbose", action="count", default=0) 55 | parser.add_argument( 56 | "-e", 57 | "--conversion-error", 58 | type=float, 59 | metavar="ERROR", 60 | default=0.001, 61 | help="maxiumum approximation error measured in EM (default: 0.001)", 62 | ) 63 | parser.add_argument( 64 | "-c", 65 | "--all-cubic", 66 | default=False, 67 | action="store_true", 68 | help="whether to only use cubic curves", 69 | ) 70 | 71 | output_parser = parser.add_mutually_exclusive_group() 72 | output_parser.add_argument( 73 | "-o", 74 | "--output-file", 75 | default=None, 76 | metavar="OUTPUT", 77 | help=("output filename for the converted TTF."), 78 | ) 79 | output_parser.add_argument( 80 | "-d", 81 | "--output-dir", 82 | default=None, 83 | metavar="DIRECTORY", 84 | help="output directory where to save converted TTFs", 85 | ) 86 | 87 | options = parser.parse_args(args) 88 | 89 | if not options.verbose: 90 | level = "WARNING" 91 | elif options.verbose == 1: 92 | level = "INFO" 93 | else: 94 | level = "DEBUG" 95 | logging.basicConfig(level=level) 96 | 97 | if len(options.infiles) > 1 and options.output_file: 98 | parser.error("-o/--output-file can't be used with multile inputs") 99 | 100 | if options.output_dir: 101 | output_dir = options.output_dir 102 | if not os.path.exists(output_dir): 103 | os.mkdir(output_dir) 104 | elif not os.path.isdir(output_dir): 105 | parser.error("'%s' is not a directory" % output_dir) 106 | output_paths = [ 107 | os.path.join(output_dir, os.path.basename(p)) for p in options.infiles 108 | ] 109 | elif options.output_file: 110 | output_paths = [options.output_file] 111 | else: 112 | output_paths = [ 113 | makeOutputFileName(p, overWrite=True, suffix=".cubic") 114 | for p in options.infiles 115 | ] 116 | 117 | kwargs = dict( 118 | dump_stats=options.verbose > 0, 119 | max_err_em=options.conversion_error, 120 | all_cubic=options.all_cubic, 121 | ) 122 | 123 | for input_path, output_path in zip(options.infiles, output_paths): 124 | _font_to_cubic(input_path, output_path, **kwargs) 125 | -------------------------------------------------------------------------------- /STERA-X/fontTools/subset/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from fontTools.subset import main 3 | 4 | 5 | if __name__ == "__main__": 6 | sys.exit(main()) 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/subset/util.py: -------------------------------------------------------------------------------- 1 | """Private utility methods used by the subset modules""" 2 | 3 | 4 | def _add_method(*clazzes): 5 | """Returns a decorator function that adds a new method to one or 6 | more classes.""" 7 | 8 | def wrapper(method): 9 | done = [] 10 | for clazz in clazzes: 11 | if clazz in done: 12 | continue # Support multiple names of a clazz 13 | done.append(clazz) 14 | assert clazz.__name__ != "DefaultTable", "Oops, table class not found." 15 | assert not hasattr( 16 | clazz, method.__name__ 17 | ), "Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__) 18 | setattr(clazz, method.__name__, method) 19 | return None 20 | 21 | return wrapper 22 | 23 | 24 | def _uniq_sort(l): 25 | return sorted(set(l)) 26 | -------------------------------------------------------------------------------- /STERA-X/fontTools/svgLib/__init__.py: -------------------------------------------------------------------------------- 1 | from .path import SVGPath, parse_path 2 | 3 | __all__ = ["SVGPath", "parse_path"] 4 | -------------------------------------------------------------------------------- /STERA-X/fontTools/svgLib/path/__init__.py: -------------------------------------------------------------------------------- 1 | from fontTools.pens.transformPen import TransformPen 2 | from fontTools.misc import etree 3 | from fontTools.misc.textTools import tostr 4 | from .parser import parse_path 5 | from .shapes import PathBuilder 6 | 7 | 8 | __all__ = [tostr(s) for s in ("SVGPath", "parse_path")] 9 | 10 | 11 | class SVGPath(object): 12 | """Parse SVG ``path`` elements from a file or string, and draw them 13 | onto a glyph object that supports the FontTools Pen protocol. 14 | 15 | For example, reading from an SVG file and drawing to a Defcon Glyph: 16 | 17 | import defcon 18 | glyph = defcon.Glyph() 19 | pen = glyph.getPen() 20 | svg = SVGPath("path/to/a.svg") 21 | svg.draw(pen) 22 | 23 | Or reading from a string containing SVG data, using the alternative 24 | 'fromstring' (a class method): 25 | 26 | data = ' # big endian 12 | height: B 13 | width: B 14 | horiBearingX: b 15 | horiBearingY: b 16 | horiAdvance: B 17 | vertBearingX: b 18 | vertBearingY: b 19 | vertAdvance: B 20 | """ 21 | 22 | smallGlyphMetricsFormat = """ 23 | > # big endian 24 | height: B 25 | width: B 26 | BearingX: b 27 | BearingY: b 28 | Advance: B 29 | """ 30 | 31 | 32 | class BitmapGlyphMetrics(object): 33 | def toXML(self, writer, ttFont): 34 | writer.begintag(self.__class__.__name__) 35 | writer.newline() 36 | for metricName in sstruct.getformat(self.__class__.binaryFormat)[1]: 37 | writer.simpletag(metricName, value=getattr(self, metricName)) 38 | writer.newline() 39 | writer.endtag(self.__class__.__name__) 40 | writer.newline() 41 | 42 | def fromXML(self, name, attrs, content, ttFont): 43 | metricNames = set(sstruct.getformat(self.__class__.binaryFormat)[1]) 44 | for element in content: 45 | if not isinstance(element, tuple): 46 | continue 47 | name, attrs, content = element 48 | # Make sure this is a metric that is needed by GlyphMetrics. 49 | if name in metricNames: 50 | vars(self)[name] = safeEval(attrs["value"]) 51 | else: 52 | log.warning( 53 | "unknown name '%s' being ignored in %s.", 54 | name, 55 | self.__class__.__name__, 56 | ) 57 | 58 | 59 | class BigGlyphMetrics(BitmapGlyphMetrics): 60 | binaryFormat = bigGlyphMetricsFormat 61 | 62 | 63 | class SmallGlyphMetrics(BitmapGlyphMetrics): 64 | binaryFormat = smallGlyphMetricsFormat 65 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/C_B_D_T_.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google, Inc. All Rights Reserved. 2 | # 3 | # Google Author(s): Matt Fontaine 4 | 5 | 6 | from fontTools.misc.textTools import bytesjoin 7 | from fontTools.misc import sstruct 8 | from . import E_B_D_T_ 9 | from .BitmapGlyphMetrics import ( 10 | BigGlyphMetrics, 11 | bigGlyphMetricsFormat, 12 | SmallGlyphMetrics, 13 | smallGlyphMetricsFormat, 14 | ) 15 | from .E_B_D_T_ import ( 16 | BitmapGlyph, 17 | BitmapPlusSmallMetricsMixin, 18 | BitmapPlusBigMetricsMixin, 19 | ) 20 | import struct 21 | 22 | 23 | class table_C_B_D_T_(E_B_D_T_.table_E_B_D_T_): 24 | 25 | # Change the data locator table being referenced. 26 | locatorName = "CBLC" 27 | 28 | # Modify the format class accessor for color bitmap use. 29 | def getImageFormatClass(self, imageFormat): 30 | try: 31 | return E_B_D_T_.table_E_B_D_T_.getImageFormatClass(self, imageFormat) 32 | except KeyError: 33 | return cbdt_bitmap_classes[imageFormat] 34 | 35 | 36 | # Helper method for removing export features not supported by color bitmaps. 37 | # Write data in the parent class will default to raw if an option is unsupported. 38 | def _removeUnsupportedForColor(dataFunctions): 39 | dataFunctions = dict(dataFunctions) 40 | del dataFunctions["row"] 41 | return dataFunctions 42 | 43 | 44 | class ColorBitmapGlyph(BitmapGlyph): 45 | 46 | fileExtension = ".png" 47 | xmlDataFunctions = _removeUnsupportedForColor(BitmapGlyph.xmlDataFunctions) 48 | 49 | 50 | class cbdt_bitmap_format_17(BitmapPlusSmallMetricsMixin, ColorBitmapGlyph): 51 | def decompile(self): 52 | self.metrics = SmallGlyphMetrics() 53 | dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics) 54 | (dataLen,) = struct.unpack(">L", data[:4]) 55 | data = data[4:] 56 | 57 | # For the image data cut it to the size specified by dataLen. 58 | assert dataLen <= len(data), "Data overun in format 17" 59 | self.imageData = data[:dataLen] 60 | 61 | def compile(self, ttFont): 62 | dataList = [] 63 | dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics)) 64 | dataList.append(struct.pack(">L", len(self.imageData))) 65 | dataList.append(self.imageData) 66 | return bytesjoin(dataList) 67 | 68 | 69 | class cbdt_bitmap_format_18(BitmapPlusBigMetricsMixin, ColorBitmapGlyph): 70 | def decompile(self): 71 | self.metrics = BigGlyphMetrics() 72 | dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics) 73 | (dataLen,) = struct.unpack(">L", data[:4]) 74 | data = data[4:] 75 | 76 | # For the image data cut it to the size specified by dataLen. 77 | assert dataLen <= len(data), "Data overun in format 18" 78 | self.imageData = data[:dataLen] 79 | 80 | def compile(self, ttFont): 81 | dataList = [] 82 | dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics)) 83 | dataList.append(struct.pack(">L", len(self.imageData))) 84 | dataList.append(self.imageData) 85 | return bytesjoin(dataList) 86 | 87 | 88 | class cbdt_bitmap_format_19(ColorBitmapGlyph): 89 | def decompile(self): 90 | (dataLen,) = struct.unpack(">L", self.data[:4]) 91 | data = self.data[4:] 92 | 93 | assert dataLen <= len(data), "Data overun in format 19" 94 | self.imageData = data[:dataLen] 95 | 96 | def compile(self, ttFont): 97 | return struct.pack(">L", len(self.imageData)) + self.imageData 98 | 99 | 100 | # Dict for CBDT extended formats. 101 | cbdt_bitmap_classes = { 102 | 17: cbdt_bitmap_format_17, 103 | 18: cbdt_bitmap_format_18, 104 | 19: cbdt_bitmap_format_19, 105 | } 106 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/C_B_L_C_.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Google, Inc. All Rights Reserved. 2 | # 3 | # Google Author(s): Matt Fontaine 4 | 5 | from . import E_B_L_C_ 6 | 7 | 8 | class table_C_B_L_C_(E_B_L_C_.table_E_B_L_C_): 9 | 10 | dependencies = ["CBDT"] 11 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/C_F_F_.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from fontTools import cffLib 3 | from . import DefaultTable 4 | 5 | 6 | class table_C_F_F_(DefaultTable.DefaultTable): 7 | def __init__(self, tag=None): 8 | DefaultTable.DefaultTable.__init__(self, tag) 9 | self.cff = cffLib.CFFFontSet() 10 | self._gaveGlyphOrder = False 11 | 12 | def decompile(self, data, otFont): 13 | self.cff.decompile(BytesIO(data), otFont, isCFF2=False) 14 | assert len(self.cff) == 1, "can't deal with multi-font CFF tables." 15 | 16 | def compile(self, otFont): 17 | f = BytesIO() 18 | self.cff.compile(f, otFont, isCFF2=False) 19 | return f.getvalue() 20 | 21 | def haveGlyphNames(self): 22 | if hasattr(self.cff[self.cff.fontNames[0]], "ROS"): 23 | return False # CID-keyed font 24 | else: 25 | return True 26 | 27 | def getGlyphOrder(self): 28 | if self._gaveGlyphOrder: 29 | from fontTools import ttLib 30 | 31 | raise ttLib.TTLibError("illegal use of getGlyphOrder()") 32 | self._gaveGlyphOrder = True 33 | return self.cff[self.cff.fontNames[0]].getGlyphOrder() 34 | 35 | def setGlyphOrder(self, glyphOrder): 36 | pass 37 | # XXX 38 | # self.cff[self.cff.fontNames[0]].setGlyphOrder(glyphOrder) 39 | 40 | def toXML(self, writer, otFont): 41 | self.cff.toXML(writer) 42 | 43 | def fromXML(self, name, attrs, content, otFont): 44 | if not hasattr(self, "cff"): 45 | self.cff = cffLib.CFFFontSet() 46 | self.cff.fromXML(name, attrs, content, otFont) 47 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/C_F_F__2.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from fontTools.ttLib.tables.C_F_F_ import table_C_F_F_ 3 | 4 | 5 | class table_C_F_F__2(table_C_F_F_): 6 | def decompile(self, data, otFont): 7 | self.cff.decompile(BytesIO(data), otFont, isCFF2=True) 8 | assert len(self.cff) == 1, "can't deal with multi-font CFF tables." 9 | 10 | def compile(self, otFont): 11 | f = BytesIO() 12 | self.cff.compile(f, otFont, isCFF2=True) 13 | return f.getvalue() 14 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/D__e_b_g.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from . import DefaultTable 4 | 5 | 6 | class table_D__e_b_g(DefaultTable.DefaultTable): 7 | def decompile(self, data, ttFont): 8 | self.data = json.loads(data) 9 | 10 | def compile(self, ttFont): 11 | return json.dumps(self.data).encode("utf-8") 12 | 13 | def toXML(self, writer, ttFont): 14 | writer.writecdata(json.dumps(self.data)) 15 | 16 | def fromXML(self, name, attrs, content, ttFont): 17 | self.data = json.loads(content) 18 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/DefaultTable.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.textTools import Tag 2 | from fontTools.ttLib import getClassTag 3 | 4 | 5 | class DefaultTable(object): 6 | 7 | dependencies = [] 8 | 9 | def __init__(self, tag=None): 10 | if tag is None: 11 | tag = getClassTag(self.__class__) 12 | self.tableTag = Tag(tag) 13 | 14 | def decompile(self, data, ttFont): 15 | self.data = data 16 | 17 | def compile(self, ttFont): 18 | return self.data 19 | 20 | def toXML(self, writer, ttFont, **kwargs): 21 | if hasattr(self, "ERROR"): 22 | writer.comment("An error occurred during the decompilation of this table") 23 | writer.newline() 24 | writer.comment(self.ERROR) 25 | writer.newline() 26 | writer.begintag("hexdata") 27 | writer.newline() 28 | writer.dumphex(self.compile(ttFont)) 29 | writer.endtag("hexdata") 30 | writer.newline() 31 | 32 | def fromXML(self, name, attrs, content, ttFont): 33 | from fontTools.misc.textTools import readHex 34 | from fontTools import ttLib 35 | 36 | if name != "hexdata": 37 | raise ttLib.TTLibError("can't handle '%s' element" % name) 38 | self.decompile(readHex(content), ttFont) 39 | 40 | def __repr__(self): 41 | return "<'%s' table at %x>" % (self.tableTag, id(self)) 42 | 43 | def __eq__(self, other): 44 | if type(self) != type(other): 45 | return NotImplemented 46 | return self.__dict__ == other.__dict__ 47 | 48 | def __ne__(self, other): 49 | result = self.__eq__(other) 50 | return result if result is NotImplemented else not result 51 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/F_F_T_M_.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc import sstruct 2 | from fontTools.misc.textTools import safeEval 3 | from fontTools.misc.timeTools import timestampFromString, timestampToString 4 | from . import DefaultTable 5 | 6 | FFTMFormat = """ 7 | > # big endian 8 | version: I 9 | FFTimeStamp: Q 10 | sourceCreated: Q 11 | sourceModified: Q 12 | """ 13 | 14 | 15 | class table_F_F_T_M_(DefaultTable.DefaultTable): 16 | def decompile(self, data, ttFont): 17 | dummy, rest = sstruct.unpack2(FFTMFormat, data, self) 18 | 19 | def compile(self, ttFont): 20 | data = sstruct.pack(FFTMFormat, self) 21 | return data 22 | 23 | def toXML(self, writer, ttFont): 24 | writer.comment( 25 | "FontForge's timestamp, font source creation and modification dates" 26 | ) 27 | writer.newline() 28 | formatstring, names, fixes = sstruct.getformat(FFTMFormat) 29 | for name in names: 30 | value = getattr(self, name) 31 | if name in ("FFTimeStamp", "sourceCreated", "sourceModified"): 32 | value = timestampToString(value) 33 | writer.simpletag(name, value=value) 34 | writer.newline() 35 | 36 | def fromXML(self, name, attrs, content, ttFont): 37 | value = attrs["value"] 38 | if name in ("FFTimeStamp", "sourceCreated", "sourceModified"): 39 | value = timestampFromString(value) 40 | else: 41 | value = safeEval(value) 42 | setattr(self, name, value) 43 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/G_D_E_F_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_G_D_E_F_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/G_P_O_S_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_G_P_O_S_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/G_S_U_B_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_G_S_U_B_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/G__l_o_c.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc import sstruct 2 | from fontTools.misc.textTools import safeEval 3 | from . import DefaultTable 4 | import array 5 | import sys 6 | 7 | 8 | Gloc_header = """ 9 | > # big endian 10 | version: 16.16F # Table version 11 | flags: H # bit 0: 1=long format, 0=short format 12 | # bit 1: 1=attribute names, 0=no names 13 | numAttribs: H # NUmber of attributes 14 | """ 15 | 16 | 17 | class table_G__l_o_c(DefaultTable.DefaultTable): 18 | """ 19 | Support Graphite Gloc tables 20 | """ 21 | 22 | dependencies = ["Glat"] 23 | 24 | def __init__(self, tag=None): 25 | DefaultTable.DefaultTable.__init__(self, tag) 26 | self.attribIds = None 27 | self.numAttribs = 0 28 | 29 | def decompile(self, data, ttFont): 30 | _, data = sstruct.unpack2(Gloc_header, data, self) 31 | flags = self.flags 32 | del self.flags 33 | self.locations = array.array("I" if flags & 1 else "H") 34 | self.locations.frombytes(data[: len(data) - self.numAttribs * (flags & 2)]) 35 | if sys.byteorder != "big": 36 | self.locations.byteswap() 37 | self.attribIds = array.array("H") 38 | if flags & 2: 39 | self.attribIds.frombytes(data[-self.numAttribs * 2 :]) 40 | if sys.byteorder != "big": 41 | self.attribIds.byteswap() 42 | 43 | def compile(self, ttFont): 44 | data = sstruct.pack( 45 | Gloc_header, 46 | dict( 47 | version=1.0, 48 | flags=(bool(self.attribIds) << 1) + (self.locations.typecode == "I"), 49 | numAttribs=self.numAttribs, 50 | ), 51 | ) 52 | if sys.byteorder != "big": 53 | self.locations.byteswap() 54 | data += self.locations.tobytes() 55 | if sys.byteorder != "big": 56 | self.locations.byteswap() 57 | if self.attribIds: 58 | if sys.byteorder != "big": 59 | self.attribIds.byteswap() 60 | data += self.attribIds.tobytes() 61 | if sys.byteorder != "big": 62 | self.attribIds.byteswap() 63 | return data 64 | 65 | def set(self, locations): 66 | long_format = max(locations) >= 65536 67 | self.locations = array.array("I" if long_format else "H", locations) 68 | 69 | def toXML(self, writer, ttFont): 70 | writer.simpletag("attributes", number=self.numAttribs) 71 | writer.newline() 72 | 73 | def fromXML(self, name, attrs, content, ttFont): 74 | if name == "attributes": 75 | self.numAttribs = int(safeEval(attrs["number"])) 76 | 77 | def __getitem__(self, index): 78 | return self.locations[index] 79 | 80 | def __len__(self): 81 | return len(self.locations) 82 | 83 | def __iter__(self): 84 | return iter(self.locations) 85 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/H_V_A_R_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_H_V_A_R_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/J_S_T_F_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_J_S_T_F_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/L_T_S_H_.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.textTools import safeEval 2 | from . import DefaultTable 3 | import struct 4 | import array 5 | 6 | # XXX I've lowered the strictness, to make sure Apple's own Chicago 7 | # XXX gets through. They're looking into it, I hope to raise the standards 8 | # XXX back to normal eventually. 9 | 10 | 11 | class table_L_T_S_H_(DefaultTable.DefaultTable): 12 | def decompile(self, data, ttFont): 13 | version, numGlyphs = struct.unpack(">HH", data[:4]) 14 | data = data[4:] 15 | assert version == 0, "unknown version: %s" % version 16 | assert (len(data) % numGlyphs) < 4, "numGlyphs doesn't match data length" 17 | # ouch: the assertion is not true in Chicago! 18 | # assert numGlyphs == ttFont['maxp'].numGlyphs 19 | yPels = array.array("B") 20 | yPels.frombytes(data) 21 | self.yPels = {} 22 | for i in range(numGlyphs): 23 | self.yPels[ttFont.getGlyphName(i)] = yPels[i] 24 | 25 | def compile(self, ttFont): 26 | version = 0 27 | names = list(self.yPels.keys()) 28 | numGlyphs = len(names) 29 | yPels = [0] * numGlyphs 30 | # ouch: the assertion is not true in Chicago! 31 | # assert len(self.yPels) == ttFont['maxp'].numGlyphs == numGlyphs 32 | for name in names: 33 | yPels[ttFont.getGlyphID(name)] = self.yPels[name] 34 | yPels = array.array("B", yPels) 35 | return struct.pack(">HH", version, numGlyphs) + yPels.tobytes() 36 | 37 | def toXML(self, writer, ttFont): 38 | names = sorted(self.yPels.keys()) 39 | for name in names: 40 | writer.simpletag("yPel", name=name, value=self.yPels[name]) 41 | writer.newline() 42 | 43 | def fromXML(self, name, attrs, content, ttFont): 44 | if not hasattr(self, "yPels"): 45 | self.yPels = {} 46 | if name != "yPel": 47 | return # ignore unknown tags 48 | self.yPels[attrs["name"]] = safeEval(attrs["value"]) 49 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/M_A_T_H_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_M_A_T_H_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/M_V_A_R_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_M_V_A_R_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/S_I_N_G_.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc import sstruct 2 | from fontTools.misc.textTools import bytechr, byteord, tobytes, tostr, safeEval 3 | from . import DefaultTable 4 | 5 | SINGFormat = """ 6 | > # big endian 7 | tableVersionMajor: H 8 | tableVersionMinor: H 9 | glyphletVersion: H 10 | permissions: h 11 | mainGID: H 12 | unitsPerEm: H 13 | vertAdvance: h 14 | vertOrigin: h 15 | uniqueName: 28s 16 | METAMD5: 16s 17 | nameLength: 1s 18 | """ 19 | # baseGlyphName is a byte string which follows the record above. 20 | 21 | 22 | class table_S_I_N_G_(DefaultTable.DefaultTable): 23 | 24 | dependencies = [] 25 | 26 | def decompile(self, data, ttFont): 27 | dummy, rest = sstruct.unpack2(SINGFormat, data, self) 28 | self.uniqueName = self.decompileUniqueName(self.uniqueName) 29 | self.nameLength = byteord(self.nameLength) 30 | assert len(rest) == self.nameLength 31 | self.baseGlyphName = tostr(rest) 32 | 33 | rawMETAMD5 = self.METAMD5 34 | self.METAMD5 = "[" + hex(byteord(self.METAMD5[0])) 35 | for char in rawMETAMD5[1:]: 36 | self.METAMD5 = self.METAMD5 + ", " + hex(byteord(char)) 37 | self.METAMD5 = self.METAMD5 + "]" 38 | 39 | def decompileUniqueName(self, data): 40 | name = "" 41 | for char in data: 42 | val = byteord(char) 43 | if val == 0: 44 | break 45 | if (val > 31) or (val < 128): 46 | name += chr(val) 47 | else: 48 | octString = oct(val) 49 | if len(octString) > 3: 50 | octString = octString[1:] # chop off that leading zero. 51 | elif len(octString) < 3: 52 | octString.zfill(3) 53 | name += "\\" + octString 54 | return name 55 | 56 | def compile(self, ttFont): 57 | d = self.__dict__.copy() 58 | d["nameLength"] = bytechr(len(self.baseGlyphName)) 59 | d["uniqueName"] = self.compilecompileUniqueName(self.uniqueName, 28) 60 | METAMD5List = eval(self.METAMD5) 61 | d["METAMD5"] = b"" 62 | for val in METAMD5List: 63 | d["METAMD5"] += bytechr(val) 64 | assert len(d["METAMD5"]) == 16, "Failed to pack 16 byte MD5 hash in SING table" 65 | data = sstruct.pack(SINGFormat, d) 66 | data = data + tobytes(self.baseGlyphName) 67 | return data 68 | 69 | def compilecompileUniqueName(self, name, length): 70 | nameLen = len(name) 71 | if length <= nameLen: 72 | name = name[: length - 1] + "\000" 73 | else: 74 | name += (nameLen - length) * "\000" 75 | return name 76 | 77 | def toXML(self, writer, ttFont): 78 | writer.comment("Most of this table will be recalculated by the compiler") 79 | writer.newline() 80 | formatstring, names, fixes = sstruct.getformat(SINGFormat) 81 | for name in names: 82 | value = getattr(self, name) 83 | writer.simpletag(name, value=value) 84 | writer.newline() 85 | writer.simpletag("baseGlyphName", value=self.baseGlyphName) 86 | writer.newline() 87 | 88 | def fromXML(self, name, attrs, content, ttFont): 89 | value = attrs["value"] 90 | if name in ["uniqueName", "METAMD5", "baseGlyphName"]: 91 | setattr(self, name, value) 92 | else: 93 | setattr(self, name, safeEval(value)) 94 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/S_T_A_T_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_S_T_A_T_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/S__i_l_l.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc import sstruct 2 | from fontTools.misc.fixedTools import floatToFixedToStr 3 | from fontTools.misc.textTools import safeEval 4 | from . import DefaultTable 5 | from . import grUtils 6 | import struct 7 | 8 | Sill_hdr = """ 9 | > 10 | version: 16.16F 11 | """ 12 | 13 | 14 | class table_S__i_l_l(DefaultTable.DefaultTable): 15 | def __init__(self, tag=None): 16 | DefaultTable.DefaultTable.__init__(self, tag) 17 | self.langs = {} 18 | 19 | def decompile(self, data, ttFont): 20 | (_, data) = sstruct.unpack2(Sill_hdr, data, self) 21 | self.version = float(floatToFixedToStr(self.version, precisionBits=16)) 22 | (numLangs,) = struct.unpack(">H", data[:2]) 23 | data = data[8:] 24 | maxsetting = 0 25 | langinfo = [] 26 | for i in range(numLangs): 27 | (langcode, numsettings, offset) = struct.unpack( 28 | ">4sHH", data[i * 8 : (i + 1) * 8] 29 | ) 30 | offset = int(offset / 8) - (numLangs + 1) 31 | langcode = langcode.replace(b"\000", b"") 32 | langinfo.append((langcode.decode("utf-8"), numsettings, offset)) 33 | maxsetting = max(maxsetting, offset + numsettings) 34 | data = data[numLangs * 8 :] 35 | finfo = [] 36 | for i in range(maxsetting): 37 | (fid, val, _) = struct.unpack(">LHH", data[i * 8 : (i + 1) * 8]) 38 | finfo.append((fid, val)) 39 | self.langs = {} 40 | for c, n, o in langinfo: 41 | self.langs[c] = [] 42 | for i in range(o, o + n): 43 | self.langs[c].append(finfo[i]) 44 | 45 | def compile(self, ttFont): 46 | ldat = b"" 47 | fdat = b"" 48 | offset = len(self.langs) 49 | for c, inf in sorted(self.langs.items()): 50 | ldat += struct.pack(">4sHH", c.encode("utf8"), len(inf), 8 * offset + 20) 51 | for fid, val in inf: 52 | fdat += struct.pack(">LHH", fid, val, 0) 53 | offset += len(inf) 54 | ldat += struct.pack(">LHH", 0x80808080, 0, 8 * offset + 20) 55 | return ( 56 | sstruct.pack(Sill_hdr, self) 57 | + grUtils.bininfo(len(self.langs)) 58 | + ldat 59 | + fdat 60 | ) 61 | 62 | def toXML(self, writer, ttFont): 63 | writer.simpletag("version", version=self.version) 64 | writer.newline() 65 | for c, inf in sorted(self.langs.items()): 66 | writer.begintag("lang", name=c) 67 | writer.newline() 68 | for fid, val in inf: 69 | writer.simpletag("feature", fid=grUtils.num2tag(fid), val=val) 70 | writer.newline() 71 | writer.endtag("lang") 72 | writer.newline() 73 | 74 | def fromXML(self, name, attrs, content, ttFont): 75 | if name == "version": 76 | self.version = float(safeEval(attrs["version"])) 77 | elif name == "lang": 78 | c = attrs["name"] 79 | self.langs[c] = [] 80 | for element in content: 81 | if not isinstance(element, tuple): 82 | continue 83 | tag, a, subcontent = element 84 | if tag == "feature": 85 | self.langs[c].append( 86 | (grUtils.tag2num(a["fid"]), int(safeEval(a["val"]))) 87 | ) 88 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I_B_.py: -------------------------------------------------------------------------------- 1 | from .T_S_I_V_ import table_T_S_I_V_ 2 | 3 | 4 | class table_T_S_I_B_(table_T_S_I_V_): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I_C_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_T_S_I_C_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I_D_.py: -------------------------------------------------------------------------------- 1 | from .T_S_I_V_ import table_T_S_I_V_ 2 | 3 | 4 | class table_T_S_I_D_(table_T_S_I_V_): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I_J_.py: -------------------------------------------------------------------------------- 1 | from .T_S_I_V_ import table_T_S_I_V_ 2 | 3 | 4 | class table_T_S_I_J_(table_T_S_I_V_): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I_P_.py: -------------------------------------------------------------------------------- 1 | from .T_S_I_V_ import table_T_S_I_V_ 2 | 3 | 4 | class table_T_S_I_P_(table_T_S_I_V_): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I_S_.py: -------------------------------------------------------------------------------- 1 | from .T_S_I_V_ import table_T_S_I_V_ 2 | 3 | 4 | class table_T_S_I_S_(table_T_S_I_V_): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I_V_.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.textTools import strjoin, tobytes, tostr 2 | from . import asciiTable 3 | 4 | 5 | class table_T_S_I_V_(asciiTable.asciiTable): 6 | def toXML(self, writer, ttFont): 7 | data = tostr(self.data) 8 | # removing null bytes. XXX needed?? 9 | data = data.split("\0") 10 | data = strjoin(data) 11 | writer.begintag("source") 12 | writer.newline() 13 | writer.write_noindent(data.replace("\r", "\n")) 14 | writer.newline() 15 | writer.endtag("source") 16 | writer.newline() 17 | 18 | def fromXML(self, name, attrs, content, ttFont): 19 | lines = strjoin(content).split("\n") 20 | self.data = tobytes("\r".join(lines[1:-1])) 21 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I__0.py: -------------------------------------------------------------------------------- 1 | """ TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT) 2 | tool to store its hinting source data. 3 | 4 | TSI0 is the index table containing the lengths and offsets for the glyph 5 | programs and 'extra' programs ('fpgm', 'prep', and 'cvt') that are contained 6 | in the TSI1 table. 7 | """ 8 | from . import DefaultTable 9 | import struct 10 | 11 | tsi0Format = ">HHL" 12 | 13 | 14 | def fixlongs(glyphID, textLength, textOffset): 15 | return int(glyphID), int(textLength), textOffset 16 | 17 | 18 | class table_T_S_I__0(DefaultTable.DefaultTable): 19 | 20 | dependencies = ["TSI1"] 21 | 22 | def decompile(self, data, ttFont): 23 | numGlyphs = ttFont["maxp"].numGlyphs 24 | indices = [] 25 | size = struct.calcsize(tsi0Format) 26 | for i in range(numGlyphs + 5): 27 | glyphID, textLength, textOffset = fixlongs( 28 | *struct.unpack(tsi0Format, data[:size]) 29 | ) 30 | indices.append((glyphID, textLength, textOffset)) 31 | data = data[size:] 32 | assert len(data) == 0 33 | assert indices[-5] == (0xFFFE, 0, 0xABFC1F34), "bad magic number" 34 | self.indices = indices[:-5] 35 | self.extra_indices = indices[-4:] 36 | 37 | def compile(self, ttFont): 38 | if not hasattr(self, "indices"): 39 | # We have no corresponding table (TSI1 or TSI3); let's return 40 | # no data, which effectively means "ignore us". 41 | return b"" 42 | data = b"" 43 | for index, textLength, textOffset in self.indices: 44 | data = data + struct.pack(tsi0Format, index, textLength, textOffset) 45 | data = data + struct.pack(tsi0Format, 0xFFFE, 0, 0xABFC1F34) 46 | for index, textLength, textOffset in self.extra_indices: 47 | data = data + struct.pack(tsi0Format, index, textLength, textOffset) 48 | return data 49 | 50 | def set(self, indices, extra_indices): 51 | # gets called by 'TSI1' or 'TSI3' 52 | self.indices = indices 53 | self.extra_indices = extra_indices 54 | 55 | def toXML(self, writer, ttFont): 56 | writer.comment("This table will be calculated by the compiler") 57 | writer.newline() 58 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I__2.py: -------------------------------------------------------------------------------- 1 | """ TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT) 2 | tool to store its hinting source data. 3 | 4 | TSI2 is the index table containing the lengths and offsets for the glyph 5 | programs that are contained in the TSI3 table. It uses the same format as 6 | the TSI0 table. 7 | """ 8 | from fontTools import ttLib 9 | 10 | superclass = ttLib.getTableClass("TSI0") 11 | 12 | 13 | class table_T_S_I__2(superclass): 14 | 15 | dependencies = ["TSI3"] 16 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I__3.py: -------------------------------------------------------------------------------- 1 | """ TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT) 2 | tool to store its hinting source data. 3 | 4 | TSI3 contains the text of the glyph programs in the form of 'VTTTalk' code. 5 | """ 6 | from fontTools import ttLib 7 | 8 | superclass = ttLib.getTableClass("TSI1") 9 | 10 | 11 | class table_T_S_I__3(superclass): 12 | 13 | extras = { 14 | 0xFFFA: "reserved0", 15 | 0xFFFB: "reserved1", 16 | 0xFFFC: "reserved2", 17 | 0xFFFD: "reserved3", 18 | } 19 | 20 | indextable = "TSI2" 21 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_S_I__5.py: -------------------------------------------------------------------------------- 1 | """ TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT) 2 | tool to store its hinting source data. 3 | 4 | TSI5 contains the VTT character groups. 5 | """ 6 | from fontTools.misc.textTools import safeEval 7 | from . import DefaultTable 8 | import sys 9 | import array 10 | 11 | 12 | class table_T_S_I__5(DefaultTable.DefaultTable): 13 | def decompile(self, data, ttFont): 14 | numGlyphs = ttFont["maxp"].numGlyphs 15 | assert len(data) == 2 * numGlyphs 16 | a = array.array("H") 17 | a.frombytes(data) 18 | if sys.byteorder != "big": 19 | a.byteswap() 20 | self.glyphGrouping = {} 21 | for i in range(numGlyphs): 22 | self.glyphGrouping[ttFont.getGlyphName(i)] = a[i] 23 | 24 | def compile(self, ttFont): 25 | glyphNames = ttFont.getGlyphOrder() 26 | a = array.array("H") 27 | for i in range(len(glyphNames)): 28 | a.append(self.glyphGrouping.get(glyphNames[i], 0)) 29 | if sys.byteorder != "big": 30 | a.byteswap() 31 | return a.tobytes() 32 | 33 | def toXML(self, writer, ttFont): 34 | names = sorted(self.glyphGrouping.keys()) 35 | for glyphName in names: 36 | writer.simpletag( 37 | "glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName] 38 | ) 39 | writer.newline() 40 | 41 | def fromXML(self, name, attrs, content, ttFont): 42 | if not hasattr(self, "glyphGrouping"): 43 | self.glyphGrouping = {} 44 | if name != "glyphgroup": 45 | return 46 | self.glyphGrouping[attrs["name"]] = safeEval(attrs["value"]) 47 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/T_T_F_A_.py: -------------------------------------------------------------------------------- 1 | from . import asciiTable 2 | 3 | 4 | class table_T_T_F_A_(asciiTable.asciiTable): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/V_V_A_R_.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table_V_V_A_R_(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/__init__.py: -------------------------------------------------------------------------------- 1 | # DON'T EDIT! This file is generated by MetaTools/buildTableList.py. 2 | def _moduleFinderHint(): 3 | """Dummy function to let modulefinder know what tables may be 4 | dynamically imported. Generated by MetaTools/buildTableList.py. 5 | 6 | >>> _moduleFinderHint() 7 | """ 8 | from . import B_A_S_E_ 9 | from . import C_B_D_T_ 10 | from . import C_B_L_C_ 11 | from . import C_F_F_ 12 | from . import C_F_F__2 13 | from . import C_O_L_R_ 14 | from . import C_P_A_L_ 15 | from . import D_S_I_G_ 16 | from . import D__e_b_g 17 | from . import E_B_D_T_ 18 | from . import E_B_L_C_ 19 | from . import F_F_T_M_ 20 | from . import F__e_a_t 21 | from . import G_D_E_F_ 22 | from . import G_M_A_P_ 23 | from . import G_P_K_G_ 24 | from . import G_P_O_S_ 25 | from . import G_S_U_B_ 26 | from . import G__l_a_t 27 | from . import G__l_o_c 28 | from . import H_V_A_R_ 29 | from . import J_S_T_F_ 30 | from . import L_T_S_H_ 31 | from . import M_A_T_H_ 32 | from . import M_E_T_A_ 33 | from . import M_V_A_R_ 34 | from . import O_S_2f_2 35 | from . import S_I_N_G_ 36 | from . import S_T_A_T_ 37 | from . import S_V_G_ 38 | from . import S__i_l_f 39 | from . import S__i_l_l 40 | from . import T_S_I_B_ 41 | from . import T_S_I_C_ 42 | from . import T_S_I_D_ 43 | from . import T_S_I_J_ 44 | from . import T_S_I_P_ 45 | from . import T_S_I_S_ 46 | from . import T_S_I_V_ 47 | from . import T_S_I__0 48 | from . import T_S_I__1 49 | from . import T_S_I__2 50 | from . import T_S_I__3 51 | from . import T_S_I__5 52 | from . import T_T_F_A_ 53 | from . import V_D_M_X_ 54 | from . import V_O_R_G_ 55 | from . import V_V_A_R_ 56 | from . import _a_n_k_r 57 | from . import _a_v_a_r 58 | from . import _b_s_l_n 59 | from . import _c_i_d_g 60 | from . import _c_m_a_p 61 | from . import _c_v_a_r 62 | from . import _c_v_t 63 | from . import _f_e_a_t 64 | from . import _f_p_g_m 65 | from . import _f_v_a_r 66 | from . import _g_a_s_p 67 | from . import _g_c_i_d 68 | from . import _g_l_y_f 69 | from . import _g_v_a_r 70 | from . import _h_d_m_x 71 | from . import _h_e_a_d 72 | from . import _h_h_e_a 73 | from . import _h_m_t_x 74 | from . import _k_e_r_n 75 | from . import _l_c_a_r 76 | from . import _l_o_c_a 77 | from . import _l_t_a_g 78 | from . import _m_a_x_p 79 | from . import _m_e_t_a 80 | from . import _m_o_r_t 81 | from . import _m_o_r_x 82 | from . import _n_a_m_e 83 | from . import _o_p_b_d 84 | from . import _p_o_s_t 85 | from . import _p_r_e_p 86 | from . import _p_r_o_p 87 | from . import _s_b_i_x 88 | from . import _t_r_a_k 89 | from . import _v_h_e_a 90 | from . import _v_m_t_x 91 | 92 | 93 | if __name__ == "__main__": 94 | import doctest, sys 95 | 96 | sys.exit(doctest.testmod().failed) 97 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_a_n_k_r.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table__a_n_k_r(BaseTTXConverter): 5 | """ 6 | The anchor point table provides a way to define anchor points. 7 | These are points within the coordinate space of a given glyph, 8 | independent of the control points used to render the glyph. 9 | Anchor points are used in conjunction with the 'kerx' table. 10 | 11 | See also https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html 12 | """ 13 | 14 | pass 15 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_b_s_l_n.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html 5 | class table__b_s_l_n(BaseTTXConverter): 6 | pass 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_c_i_d_g.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from .otBase import BaseTTXConverter 3 | 4 | 5 | class table__c_i_d_g(BaseTTXConverter): 6 | """The AAT ``cidg`` table has almost the same structure as ``gidc``, 7 | just mapping CIDs to GlyphIDs instead of the reverse direction. 8 | 9 | It is useful for fonts that may be used by a PDF renderer in lieu of 10 | a font reference with a known glyph collection but no subsetted 11 | glyphs. For instance, a PDF can say “please use a font conforming 12 | to Adobe-Japan-1”; the ``cidg`` mapping is necessary if the font is, 13 | say, a TrueType font. ``gidc`` is lossy for this purpose and is 14 | obsoleted by ``cidg``. 15 | 16 | For example, the first font in ``/System/Library/Fonts/PingFang.ttc`` 17 | (which Apple ships pre-installed on MacOS 10.12.6) has a ``cidg`` table.""" 18 | 19 | pass 20 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_c_v_a_r.py: -------------------------------------------------------------------------------- 1 | from . import DefaultTable 2 | from fontTools.misc import sstruct 3 | from fontTools.misc.textTools import bytesjoin 4 | from fontTools.ttLib.tables.TupleVariation import ( 5 | compileTupleVariationStore, 6 | decompileTupleVariationStore, 7 | TupleVariation, 8 | ) 9 | 10 | 11 | # https://www.microsoft.com/typography/otspec/cvar.htm 12 | # https://www.microsoft.com/typography/otspec/otvarcommonformats.htm 13 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cvar.html 14 | 15 | CVAR_HEADER_FORMAT = """ 16 | > # big endian 17 | majorVersion: H 18 | minorVersion: H 19 | tupleVariationCount: H 20 | offsetToData: H 21 | """ 22 | 23 | CVAR_HEADER_SIZE = sstruct.calcsize(CVAR_HEADER_FORMAT) 24 | 25 | 26 | class table__c_v_a_r(DefaultTable.DefaultTable): 27 | dependencies = ["cvt ", "fvar"] 28 | 29 | def __init__(self, tag=None): 30 | DefaultTable.DefaultTable.__init__(self, tag) 31 | self.majorVersion, self.minorVersion = 1, 0 32 | self.variations = [] 33 | 34 | def compile(self, ttFont, useSharedPoints=False): 35 | tupleVariationCount, tuples, data = compileTupleVariationStore( 36 | variations=[v for v in self.variations if v.hasImpact()], 37 | pointCount=len(ttFont["cvt "].values), 38 | axisTags=[axis.axisTag for axis in ttFont["fvar"].axes], 39 | sharedTupleIndices={}, 40 | useSharedPoints=useSharedPoints, 41 | ) 42 | header = { 43 | "majorVersion": self.majorVersion, 44 | "minorVersion": self.minorVersion, 45 | "tupleVariationCount": tupleVariationCount, 46 | "offsetToData": CVAR_HEADER_SIZE + len(tuples), 47 | } 48 | return b"".join([sstruct.pack(CVAR_HEADER_FORMAT, header), tuples, data]) 49 | 50 | def decompile(self, data, ttFont): 51 | axisTags = [axis.axisTag for axis in ttFont["fvar"].axes] 52 | header = {} 53 | sstruct.unpack(CVAR_HEADER_FORMAT, data[0:CVAR_HEADER_SIZE], header) 54 | self.majorVersion = header["majorVersion"] 55 | self.minorVersion = header["minorVersion"] 56 | assert self.majorVersion == 1, self.majorVersion 57 | self.variations = decompileTupleVariationStore( 58 | tableTag=self.tableTag, 59 | axisTags=axisTags, 60 | tupleVariationCount=header["tupleVariationCount"], 61 | pointCount=len(ttFont["cvt "].values), 62 | sharedTuples=None, 63 | data=data, 64 | pos=CVAR_HEADER_SIZE, 65 | dataPos=header["offsetToData"], 66 | ) 67 | 68 | def fromXML(self, name, attrs, content, ttFont): 69 | if name == "version": 70 | self.majorVersion = int(attrs.get("major", "1")) 71 | self.minorVersion = int(attrs.get("minor", "0")) 72 | elif name == "tuple": 73 | valueCount = len(ttFont["cvt "].values) 74 | var = TupleVariation({}, [None] * valueCount) 75 | self.variations.append(var) 76 | for tupleElement in content: 77 | if isinstance(tupleElement, tuple): 78 | tupleName, tupleAttrs, tupleContent = tupleElement 79 | var.fromXML(tupleName, tupleAttrs, tupleContent) 80 | 81 | def toXML(self, writer, ttFont): 82 | axisTags = [axis.axisTag for axis in ttFont["fvar"].axes] 83 | writer.simpletag("version", major=self.majorVersion, minor=self.minorVersion) 84 | writer.newline() 85 | for var in self.variations: 86 | var.toXML(writer, axisTags) 87 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_c_v_t.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.textTools import safeEval 2 | from . import DefaultTable 3 | import sys 4 | import array 5 | 6 | 7 | class table__c_v_t(DefaultTable.DefaultTable): 8 | def decompile(self, data, ttFont): 9 | values = array.array("h") 10 | values.frombytes(data) 11 | if sys.byteorder != "big": 12 | values.byteswap() 13 | self.values = values 14 | 15 | def compile(self, ttFont): 16 | values = self.values[:] 17 | if sys.byteorder != "big": 18 | values.byteswap() 19 | return values.tobytes() 20 | 21 | def toXML(self, writer, ttFont): 22 | for i in range(len(self.values)): 23 | value = self.values[i] 24 | writer.simpletag("cv", value=value, index=i) 25 | writer.newline() 26 | 27 | def fromXML(self, name, attrs, content, ttFont): 28 | if not hasattr(self, "values"): 29 | self.values = array.array("h") 30 | if name == "cv": 31 | index = safeEval(attrs["index"]) 32 | value = safeEval(attrs["value"]) 33 | for i in range(1 + index - len(self.values)): 34 | self.values.append(0) 35 | self.values[index] = value 36 | 37 | def __len__(self): 38 | return len(self.values) 39 | 40 | def __getitem__(self, index): 41 | return self.values[index] 42 | 43 | def __setitem__(self, index, value): 44 | self.values[index] = value 45 | 46 | def __delitem__(self, index): 47 | del self.values[index] 48 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_f_e_a_t.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table__f_e_a_t(BaseTTXConverter): 5 | """The feature name table is an AAT (Apple Advanced Typography) table for 6 | storing font features, settings, and their human-readable names. It should 7 | not be confused with the ``Feat`` table or the OpenType Layout ``GSUB``/``GPOS`` 8 | tables. See `Feature Name Table `_ 9 | in the TrueType Reference Manual for more information on the structure and 10 | purpose of this table.""" 11 | 12 | pass 13 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_f_p_g_m.py: -------------------------------------------------------------------------------- 1 | from . import DefaultTable 2 | from . import ttProgram 3 | 4 | 5 | class table__f_p_g_m(DefaultTable.DefaultTable): 6 | def decompile(self, data, ttFont): 7 | program = ttProgram.Program() 8 | program.fromBytecode(data) 9 | self.program = program 10 | 11 | def compile(self, ttFont): 12 | return self.program.getBytecode() 13 | 14 | def toXML(self, writer, ttFont): 15 | self.program.toXML(writer, ttFont) 16 | 17 | def fromXML(self, name, attrs, content, ttFont): 18 | program = ttProgram.Program() 19 | program.fromXML(name, attrs, content, ttFont) 20 | self.program = program 21 | 22 | def __bool__(self): 23 | """ 24 | >>> fpgm = table__f_p_g_m() 25 | >>> bool(fpgm) 26 | False 27 | >>> p = ttProgram.Program() 28 | >>> fpgm.program = p 29 | >>> bool(fpgm) 30 | False 31 | >>> bc = bytearray([0]) 32 | >>> p.fromBytecode(bc) 33 | >>> bool(fpgm) 34 | True 35 | >>> p.bytecode.pop() 36 | 0 37 | >>> bool(fpgm) 38 | False 39 | """ 40 | return hasattr(self, "program") and bool(self.program) 41 | 42 | __nonzero__ = __bool__ 43 | 44 | 45 | if __name__ == "__main__": 46 | import sys 47 | import doctest 48 | 49 | sys.exit(doctest.testmod().failed) 50 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_g_a_s_p.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.textTools import safeEval 2 | from . import DefaultTable 3 | import struct 4 | 5 | 6 | GASP_SYMMETRIC_GRIDFIT = 0x0004 7 | GASP_SYMMETRIC_SMOOTHING = 0x0008 8 | GASP_DOGRAY = 0x0002 9 | GASP_GRIDFIT = 0x0001 10 | 11 | 12 | class table__g_a_s_p(DefaultTable.DefaultTable): 13 | def decompile(self, data, ttFont): 14 | self.version, numRanges = struct.unpack(">HH", data[:4]) 15 | assert 0 <= self.version <= 1, "unknown 'gasp' format: %s" % self.version 16 | data = data[4:] 17 | self.gaspRange = {} 18 | for i in range(numRanges): 19 | rangeMaxPPEM, rangeGaspBehavior = struct.unpack(">HH", data[:4]) 20 | self.gaspRange[int(rangeMaxPPEM)] = int(rangeGaspBehavior) 21 | data = data[4:] 22 | assert not data, "too much data" 23 | 24 | def compile(self, ttFont): 25 | version = 0 # ignore self.version 26 | numRanges = len(self.gaspRange) 27 | data = b"" 28 | items = sorted(self.gaspRange.items()) 29 | for rangeMaxPPEM, rangeGaspBehavior in items: 30 | data = data + struct.pack(">HH", rangeMaxPPEM, rangeGaspBehavior) 31 | if rangeGaspBehavior & ~(GASP_GRIDFIT | GASP_DOGRAY): 32 | version = 1 33 | data = struct.pack(">HH", version, numRanges) + data 34 | return data 35 | 36 | def toXML(self, writer, ttFont): 37 | items = sorted(self.gaspRange.items()) 38 | for rangeMaxPPEM, rangeGaspBehavior in items: 39 | writer.simpletag( 40 | "gaspRange", 41 | [ 42 | ("rangeMaxPPEM", rangeMaxPPEM), 43 | ("rangeGaspBehavior", rangeGaspBehavior), 44 | ], 45 | ) 46 | writer.newline() 47 | 48 | def fromXML(self, name, attrs, content, ttFont): 49 | if name != "gaspRange": 50 | return 51 | if not hasattr(self, "gaspRange"): 52 | self.gaspRange = {} 53 | self.gaspRange[safeEval(attrs["rangeMaxPPEM"])] = safeEval( 54 | attrs["rangeGaspBehavior"] 55 | ) 56 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_g_c_i_d.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html 5 | class table__g_c_i_d(BaseTTXConverter): 6 | pass 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_l_c_a_r.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | class table__l_c_a_r(BaseTTXConverter): 5 | pass 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_l_o_c_a.py: -------------------------------------------------------------------------------- 1 | from . import DefaultTable 2 | import sys 3 | import array 4 | import logging 5 | 6 | 7 | log = logging.getLogger(__name__) 8 | 9 | 10 | class table__l_o_c_a(DefaultTable.DefaultTable): 11 | 12 | dependencies = ["glyf"] 13 | 14 | def decompile(self, data, ttFont): 15 | longFormat = ttFont["head"].indexToLocFormat 16 | if longFormat: 17 | format = "I" 18 | else: 19 | format = "H" 20 | locations = array.array(format) 21 | locations.frombytes(data) 22 | if sys.byteorder != "big": 23 | locations.byteswap() 24 | if not longFormat: 25 | l = array.array("I") 26 | for i in range(len(locations)): 27 | l.append(locations[i] * 2) 28 | locations = l 29 | if len(locations) < (ttFont["maxp"].numGlyphs + 1): 30 | log.warning( 31 | "corrupt 'loca' table, or wrong numGlyphs in 'maxp': %d %d", 32 | len(locations) - 1, 33 | ttFont["maxp"].numGlyphs, 34 | ) 35 | self.locations = locations 36 | 37 | def compile(self, ttFont): 38 | try: 39 | max_location = max(self.locations) 40 | except AttributeError: 41 | self.set([]) 42 | max_location = 0 43 | if max_location < 0x20000 and all(l % 2 == 0 for l in self.locations): 44 | locations = array.array("H") 45 | for i in range(len(self.locations)): 46 | locations.append(self.locations[i] // 2) 47 | ttFont["head"].indexToLocFormat = 0 48 | else: 49 | locations = array.array("I", self.locations) 50 | ttFont["head"].indexToLocFormat = 1 51 | if sys.byteorder != "big": 52 | locations.byteswap() 53 | return locations.tobytes() 54 | 55 | def set(self, locations): 56 | self.locations = array.array("I", locations) 57 | 58 | def toXML(self, writer, ttFont): 59 | writer.comment("The 'loca' table will be calculated by the compiler") 60 | writer.newline() 61 | 62 | def __getitem__(self, index): 63 | return self.locations[index] 64 | 65 | def __len__(self): 66 | return len(self.locations) 67 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_l_t_a_g.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.textTools import bytesjoin, tobytes, safeEval 2 | from . import DefaultTable 3 | import struct 4 | 5 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html 6 | 7 | 8 | class table__l_t_a_g(DefaultTable.DefaultTable): 9 | def __init__(self, tag=None): 10 | DefaultTable.DefaultTable.__init__(self, tag) 11 | self.version, self.flags = 1, 0 12 | self.tags = [] 13 | 14 | def addTag(self, tag): 15 | """Add 'tag' to the list of langauge tags if not already there. 16 | 17 | Returns the integer index of 'tag' in the list of all tags. 18 | """ 19 | try: 20 | return self.tags.index(tag) 21 | except ValueError: 22 | self.tags.append(tag) 23 | return len(self.tags) - 1 24 | 25 | def decompile(self, data, ttFont): 26 | self.version, self.flags, numTags = struct.unpack(">LLL", data[:12]) 27 | assert self.version == 1 28 | self.tags = [] 29 | for i in range(numTags): 30 | pos = 12 + i * 4 31 | offset, length = struct.unpack(">HH", data[pos : pos + 4]) 32 | tag = data[offset : offset + length].decode("ascii") 33 | self.tags.append(tag) 34 | 35 | def compile(self, ttFont): 36 | dataList = [struct.pack(">LLL", self.version, self.flags, len(self.tags))] 37 | stringPool = "" 38 | for tag in self.tags: 39 | offset = stringPool.find(tag) 40 | if offset < 0: 41 | offset = len(stringPool) 42 | stringPool = stringPool + tag 43 | offset = offset + 12 + len(self.tags) * 4 44 | dataList.append(struct.pack(">HH", offset, len(tag))) 45 | dataList.append(tobytes(stringPool)) 46 | return bytesjoin(dataList) 47 | 48 | def toXML(self, writer, ttFont): 49 | writer.simpletag("version", value=self.version) 50 | writer.newline() 51 | writer.simpletag("flags", value=self.flags) 52 | writer.newline() 53 | for tag in self.tags: 54 | writer.simpletag("LanguageTag", tag=tag) 55 | writer.newline() 56 | 57 | def fromXML(self, name, attrs, content, ttFont): 58 | if not hasattr(self, "tags"): 59 | self.tags = [] 60 | if name == "LanguageTag": 61 | self.tags.append(attrs["tag"]) 62 | elif "value" in attrs: 63 | value = safeEval(attrs["value"]) 64 | setattr(self, name, value) 65 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_m_e_t_a.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc import sstruct 2 | from fontTools.misc.textTools import bytesjoin, strjoin, readHex 3 | from fontTools.ttLib import TTLibError 4 | from . import DefaultTable 5 | 6 | # Apple's documentation of 'meta': 7 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html 8 | 9 | META_HEADER_FORMAT = """ 10 | > # big endian 11 | version: L 12 | flags: L 13 | dataOffset: L 14 | numDataMaps: L 15 | """ 16 | 17 | 18 | DATA_MAP_FORMAT = """ 19 | > # big endian 20 | tag: 4s 21 | dataOffset: L 22 | dataLength: L 23 | """ 24 | 25 | 26 | class table__m_e_t_a(DefaultTable.DefaultTable): 27 | def __init__(self, tag=None): 28 | DefaultTable.DefaultTable.__init__(self, tag) 29 | self.data = {} 30 | 31 | def decompile(self, data, ttFont): 32 | headerSize = sstruct.calcsize(META_HEADER_FORMAT) 33 | header = sstruct.unpack(META_HEADER_FORMAT, data[0:headerSize]) 34 | if header["version"] != 1: 35 | raise TTLibError("unsupported 'meta' version %d" % header["version"]) 36 | dataMapSize = sstruct.calcsize(DATA_MAP_FORMAT) 37 | for i in range(header["numDataMaps"]): 38 | dataMapOffset = headerSize + i * dataMapSize 39 | dataMap = sstruct.unpack( 40 | DATA_MAP_FORMAT, data[dataMapOffset : dataMapOffset + dataMapSize] 41 | ) 42 | tag = dataMap["tag"] 43 | offset = dataMap["dataOffset"] 44 | self.data[tag] = data[offset : offset + dataMap["dataLength"]] 45 | if tag in ["dlng", "slng"]: 46 | self.data[tag] = self.data[tag].decode("utf-8") 47 | 48 | def compile(self, ttFont): 49 | keys = sorted(self.data.keys()) 50 | headerSize = sstruct.calcsize(META_HEADER_FORMAT) 51 | dataOffset = headerSize + len(keys) * sstruct.calcsize(DATA_MAP_FORMAT) 52 | header = sstruct.pack( 53 | META_HEADER_FORMAT, 54 | { 55 | "version": 1, 56 | "flags": 0, 57 | "dataOffset": dataOffset, 58 | "numDataMaps": len(keys), 59 | }, 60 | ) 61 | dataMaps = [] 62 | dataBlocks = [] 63 | for tag in keys: 64 | if tag in ["dlng", "slng"]: 65 | data = self.data[tag].encode("utf-8") 66 | else: 67 | data = self.data[tag] 68 | dataMaps.append( 69 | sstruct.pack( 70 | DATA_MAP_FORMAT, 71 | {"tag": tag, "dataOffset": dataOffset, "dataLength": len(data)}, 72 | ) 73 | ) 74 | dataBlocks.append(data) 75 | dataOffset += len(data) 76 | return bytesjoin([header] + dataMaps + dataBlocks) 77 | 78 | def toXML(self, writer, ttFont): 79 | for tag in sorted(self.data.keys()): 80 | if tag in ["dlng", "slng"]: 81 | writer.begintag("text", tag=tag) 82 | writer.newline() 83 | writer.write(self.data[tag]) 84 | writer.newline() 85 | writer.endtag("text") 86 | writer.newline() 87 | else: 88 | writer.begintag("hexdata", tag=tag) 89 | writer.newline() 90 | data = self.data[tag] 91 | if min(data) >= 0x20 and max(data) <= 0x7E: 92 | writer.comment("ascii: " + data.decode("ascii")) 93 | writer.newline() 94 | writer.dumphex(data) 95 | writer.endtag("hexdata") 96 | writer.newline() 97 | 98 | def fromXML(self, name, attrs, content, ttFont): 99 | if name == "hexdata": 100 | self.data[attrs["tag"]] = readHex(content) 101 | elif name == "text" and attrs["tag"] in ["dlng", "slng"]: 102 | self.data[attrs["tag"]] = strjoin(content).strip() 103 | else: 104 | raise TTLibError("can't handle '%s' element" % name) 105 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_m_o_r_t.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html 5 | class table__m_o_r_t(BaseTTXConverter): 6 | pass 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_m_o_r_x.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html 5 | class table__m_o_r_x(BaseTTXConverter): 6 | pass 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_o_p_b_d.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html 5 | class table__o_p_b_d(BaseTTXConverter): 6 | pass 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_p_r_e_p.py: -------------------------------------------------------------------------------- 1 | from fontTools import ttLib 2 | 3 | superclass = ttLib.getTableClass("fpgm") 4 | 5 | 6 | class table__p_r_e_p(superclass): 7 | pass 8 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_p_r_o_p.py: -------------------------------------------------------------------------------- 1 | from .otBase import BaseTTXConverter 2 | 3 | 4 | # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6prop.html 5 | class table__p_r_o_p(BaseTTXConverter): 6 | pass 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/_v_m_t_x.py: -------------------------------------------------------------------------------- 1 | from fontTools import ttLib 2 | 3 | superclass = ttLib.getTableClass("hmtx") 4 | 5 | 6 | class table__v_m_t_x(superclass): 7 | 8 | headerTag = "vhea" 9 | advanceName = "height" 10 | sideBearingName = "tsb" 11 | numberOfMetricsName = "numberOfVMetrics" 12 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/asciiTable.py: -------------------------------------------------------------------------------- 1 | from fontTools.misc.textTools import strjoin, tobytes, tostr 2 | from . import DefaultTable 3 | 4 | 5 | class asciiTable(DefaultTable.DefaultTable): 6 | def toXML(self, writer, ttFont): 7 | data = tostr(self.data) 8 | # removing null bytes. XXX needed?? 9 | data = data.split("\0") 10 | data = strjoin(data) 11 | writer.begintag("source") 12 | writer.newline() 13 | writer.write_noindent(data) 14 | writer.newline() 15 | writer.endtag("source") 16 | writer.newline() 17 | 18 | def fromXML(self, name, attrs, content, ttFont): 19 | lines = strjoin(content).split("\n") 20 | self.data = tobytes("\n".join(lines[1:-1])) 21 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/grUtils.py: -------------------------------------------------------------------------------- 1 | import struct, warnings 2 | 3 | try: 4 | import lz4 5 | except ImportError: 6 | lz4 = None 7 | else: 8 | import lz4.block 9 | 10 | # old scheme for VERSION < 0.9 otherwise use lz4.block 11 | 12 | 13 | def decompress(data): 14 | (compression,) = struct.unpack(">L", data[4:8]) 15 | scheme = compression >> 27 16 | size = compression & 0x07FFFFFF 17 | if scheme == 0: 18 | pass 19 | elif scheme == 1 and lz4: 20 | res = lz4.block.decompress(struct.pack("L", (scheme << 27) + (len(data) & 0x07FFFFFF)) 32 | if scheme == 0: 33 | return data 34 | elif scheme == 1 and lz4: 35 | res = lz4.block.compress( 36 | data, mode="high_compression", compression=16, store_size=False 37 | ) 38 | return hdr + res 39 | else: 40 | warnings.warn("Table failed to compress by unsupported compression scheme") 41 | return data 42 | 43 | 44 | def _entries(attrs, sameval): 45 | ak = 0 46 | vals = [] 47 | lastv = 0 48 | for k, v in attrs: 49 | if len(vals) and (k != ak + 1 or (sameval and v != lastv)): 50 | yield (ak - len(vals) + 1, len(vals), vals) 51 | vals = [] 52 | ak = k 53 | vals.append(v) 54 | lastv = v 55 | yield (ak - len(vals) + 1, len(vals), vals) 56 | 57 | 58 | def entries(attributes, sameval=False): 59 | g = _entries(sorted(attributes.items(), key=lambda x: int(x[0])), sameval) 60 | return g 61 | 62 | 63 | def bininfo(num, size=1): 64 | if num == 0: 65 | return struct.pack(">4H", 0, 0, 0, 0) 66 | srange = 1 67 | select = 0 68 | while srange <= num: 69 | srange *= 2 70 | select += 1 71 | select -= 1 72 | srange //= 2 73 | srange *= size 74 | shift = num * size - srange 75 | return struct.pack(">4H", num, srange, select, shift) 76 | 77 | 78 | def num2tag(n): 79 | if n < 0x200000: 80 | return str(n) 81 | else: 82 | return ( 83 | struct.unpack("4s", struct.pack(">L", n))[0].replace(b"\000", b"").decode() 84 | ) 85 | 86 | 87 | def tag2num(n): 88 | try: 89 | return int(n) 90 | except ValueError: 91 | n = (n + " ")[:4] 92 | return struct.unpack(">L", n.encode("ascii"))[0] 93 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/tables/table_API_readme.txt: -------------------------------------------------------------------------------- 1 | This folder is a subpackage of ttLib. Each module here is a 2 | specialized TT/OT table converter: they can convert raw data 3 | to Python objects and vice versa. Usually you don't need to 4 | use the modules directly: they are imported and used 5 | automatically when needed by ttLib. 6 | 7 | If you are writing you own table converter the following is 8 | important. 9 | 10 | The modules here have pretty strange names: this is due to the 11 | fact that we need to map TT table tags (which are case sensitive) 12 | to filenames (which on Mac and Win aren't case sensitive) as well 13 | as to Python identifiers. The latter means it can only contain 14 | [A-Za-z0-9_] and cannot start with a number. 15 | 16 | ttLib provides functions to expand a tag into the format used here: 17 | 18 | >>> from fontTools import ttLib 19 | >>> ttLib.tagToIdentifier("FOO ") 20 | 'F_O_O_' 21 | >>> ttLib.tagToIdentifier("cvt ") 22 | '_c_v_t' 23 | >>> ttLib.tagToIdentifier("OS/2") 24 | 'O_S_2f_2' 25 | >>> ttLib.tagToIdentifier("glyf") 26 | '_g_l_y_f' 27 | >>> 28 | 29 | And vice versa: 30 | 31 | >>> ttLib.identifierToTag("F_O_O_") 32 | 'FOO ' 33 | >>> ttLib.identifierToTag("_c_v_t") 34 | 'cvt ' 35 | >>> ttLib.identifierToTag("O_S_2f_2") 36 | 'OS/2' 37 | >>> ttLib.identifierToTag("_g_l_y_f") 38 | 'glyf' 39 | >>> 40 | 41 | Eg. the 'glyf' table converter lives in a Python file called: 42 | 43 | _g_l_y_f.py 44 | 45 | The converter itself is a class, named "table_" + expandedtag. Eg: 46 | 47 | class table__g_l_y_f: 48 | etc. 49 | 50 | Note that if you _do_ need to use such modules or classes manually, 51 | there are two convenient API functions that let you find them by tag: 52 | 53 | >>> ttLib.getTableModule('glyf') 54 | 55 | >>> ttLib.getTableClass('glyf') 56 | 57 | >>> 58 | 59 | You must subclass from DefaultTable.DefaultTable. It provides some default 60 | behavior, as well as a constructor method (__init__) that you don't need to 61 | override. 62 | 63 | Your converter should minimally provide two methods: 64 | 65 | class table_F_O_O_(DefaultTable.DefaultTable): # converter for table 'FOO ' 66 | 67 | def decompile(self, data, ttFont): 68 | # 'data' is the raw table data. Unpack it into a 69 | # Python data structure. 70 | # 'ttFont' is a ttLib.TTfile instance, enabling you to 71 | # refer to other tables. Do ***not*** keep a reference to 72 | # it: it will cause a circular reference (ttFont saves 73 | # a reference to us), and that means we'll be leaking 74 | # memory. If you need to use it in other methods, just 75 | # pass it around as a method argument. 76 | 77 | def compile(self, ttFont): 78 | # Return the raw data, as converted from the Python 79 | # data structure. 80 | # Again, 'ttFont' is there so you can access other tables. 81 | # Same warning applies. 82 | 83 | If you want to support TTX import/export as well, you need to provide two 84 | additional methods: 85 | 86 | def toXML(self, writer, ttFont): 87 | # XXX 88 | 89 | def fromXML(self, (name, attrs, content), ttFont): 90 | # XXX 91 | 92 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ttLib/ttVisitor.py: -------------------------------------------------------------------------------- 1 | """Specialization of fontTools.misc.visitor to work with TTFont.""" 2 | 3 | from fontTools.misc.visitor import Visitor 4 | from fontTools.ttLib import TTFont 5 | 6 | 7 | class TTVisitor(Visitor): 8 | def visitAttr(self, obj, attr, value, *args, **kwargs): 9 | if isinstance(value, TTFont): 10 | return False 11 | super().visitAttr(obj, attr, value, *args, **kwargs) 12 | 13 | def visit(self, obj, *args, **kwargs): 14 | if hasattr(obj, "ensureDecompiled"): 15 | obj.ensureDecompiled(recurse=False) 16 | super().visit(obj, *args, **kwargs) 17 | 18 | 19 | @TTVisitor.register(TTFont) 20 | def visit(visitor, font, *args, **kwargs): 21 | # Some objects have links back to TTFont; even though we 22 | # have a check in visitAttr to stop them from recursing 23 | # onto TTFont, sometimes they still do, for example when 24 | # someone overrides visitAttr. 25 | if hasattr(visitor, "font"): 26 | return False 27 | 28 | visitor.font = font 29 | for tag in font.keys(): 30 | visitor.visit(font[tag], *args, **kwargs) 31 | del visitor.font 32 | return False 33 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ufoLib/errors.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | 4 | class UFOLibError(Exception): 5 | pass 6 | 7 | 8 | class UnsupportedUFOFormat(UFOLibError): 9 | pass 10 | 11 | 12 | class GlifLibError(UFOLibError): 13 | def _add_note(self, note: str) -> None: 14 | # Loose backport of PEP 678 until we only support Python 3.11+, used for 15 | # adding additional context to errors. 16 | # TODO: Replace with https://docs.python.org/3.11/library/exceptions.html#BaseException.add_note 17 | (message, *rest) = self.args 18 | self.args = ((message + "\n" + note), *rest) 19 | 20 | 21 | class UnsupportedGLIFFormat(GlifLibError): 22 | pass 23 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ufoLib/etree.py: -------------------------------------------------------------------------------- 1 | """DEPRECATED - This module is kept here only as a backward compatibility shim 2 | for the old ufoLib.etree module, which was moved to fontTools.misc.etree. 3 | Please use the latter instead. 4 | """ 5 | from fontTools.misc.etree import * 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ufoLib/kerning.py: -------------------------------------------------------------------------------- 1 | def lookupKerningValue( 2 | pair, kerning, groups, fallback=0, glyphToFirstGroup=None, glyphToSecondGroup=None 3 | ): 4 | """ 5 | Note: This expects kerning to be a flat dictionary 6 | of kerning pairs, not the nested structure used 7 | in kerning.plist. 8 | 9 | >>> groups = { 10 | ... "public.kern1.O" : ["O", "D", "Q"], 11 | ... "public.kern2.E" : ["E", "F"] 12 | ... } 13 | >>> kerning = { 14 | ... ("public.kern1.O", "public.kern2.E") : -100, 15 | ... ("public.kern1.O", "F") : -200, 16 | ... ("D", "F") : -300 17 | ... } 18 | >>> lookupKerningValue(("D", "F"), kerning, groups) 19 | -300 20 | >>> lookupKerningValue(("O", "F"), kerning, groups) 21 | -200 22 | >>> lookupKerningValue(("O", "E"), kerning, groups) 23 | -100 24 | >>> lookupKerningValue(("O", "O"), kerning, groups) 25 | 0 26 | >>> lookupKerningValue(("E", "E"), kerning, groups) 27 | 0 28 | >>> lookupKerningValue(("E", "O"), kerning, groups) 29 | 0 30 | >>> lookupKerningValue(("X", "X"), kerning, groups) 31 | 0 32 | >>> lookupKerningValue(("public.kern1.O", "public.kern2.E"), 33 | ... kerning, groups) 34 | -100 35 | >>> lookupKerningValue(("public.kern1.O", "F"), kerning, groups) 36 | -200 37 | >>> lookupKerningValue(("O", "public.kern2.E"), kerning, groups) 38 | -100 39 | >>> lookupKerningValue(("public.kern1.X", "public.kern2.X"), kerning, groups) 40 | 0 41 | """ 42 | # quickly check to see if the pair is in the kerning dictionary 43 | if pair in kerning: 44 | return kerning[pair] 45 | # create glyph to group mapping 46 | if glyphToFirstGroup is not None: 47 | assert glyphToSecondGroup is not None 48 | if glyphToSecondGroup is not None: 49 | assert glyphToFirstGroup is not None 50 | if glyphToFirstGroup is None: 51 | glyphToFirstGroup = {} 52 | glyphToSecondGroup = {} 53 | for group, groupMembers in groups.items(): 54 | if group.startswith("public.kern1."): 55 | for glyph in groupMembers: 56 | glyphToFirstGroup[glyph] = group 57 | elif group.startswith("public.kern2."): 58 | for glyph in groupMembers: 59 | glyphToSecondGroup[glyph] = group 60 | # get group names and make sure first and second are glyph names 61 | first, second = pair 62 | firstGroup = secondGroup = None 63 | if first.startswith("public.kern1."): 64 | firstGroup = first 65 | first = None 66 | else: 67 | firstGroup = glyphToFirstGroup.get(first) 68 | if second.startswith("public.kern2."): 69 | secondGroup = second 70 | second = None 71 | else: 72 | secondGroup = glyphToSecondGroup.get(second) 73 | # make an ordered list of pairs to look up 74 | pairs = [ 75 | (first, second), 76 | (first, secondGroup), 77 | (firstGroup, second), 78 | (firstGroup, secondGroup), 79 | ] 80 | # look up the pairs and return any matches 81 | for pair in pairs: 82 | if pair in kerning: 83 | return kerning[pair] 84 | # use the fallback value 85 | return fallback 86 | 87 | 88 | if __name__ == "__main__": 89 | import doctest 90 | 91 | doctest.testmod() 92 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ufoLib/plistlib.py: -------------------------------------------------------------------------------- 1 | """DEPRECATED - This module is kept here only as a backward compatibility shim 2 | for the old ufoLib.plistlib module, which was moved to fontTools.misc.plistlib. 3 | Please use the latter instead. 4 | """ 5 | from fontTools.misc.plistlib import dump, dumps, load, loads 6 | from fontTools.misc.textTools import tobytes 7 | 8 | # The following functions were part of the old py2-like ufoLib.plistlib API. 9 | # They are kept only for backward compatiblity. 10 | from fontTools.ufoLib.utils import deprecated 11 | 12 | 13 | @deprecated("Use 'fontTools.misc.plistlib.load' instead") 14 | def readPlist(path_or_file): 15 | did_open = False 16 | if isinstance(path_or_file, str): 17 | path_or_file = open(path_or_file, "rb") 18 | did_open = True 19 | try: 20 | return load(path_or_file, use_builtin_types=False) 21 | finally: 22 | if did_open: 23 | path_or_file.close() 24 | 25 | 26 | @deprecated("Use 'fontTools.misc.plistlib.dump' instead") 27 | def writePlist(value, path_or_file): 28 | did_open = False 29 | if isinstance(path_or_file, str): 30 | path_or_file = open(path_or_file, "wb") 31 | did_open = True 32 | try: 33 | dump(value, path_or_file, use_builtin_types=False) 34 | finally: 35 | if did_open: 36 | path_or_file.close() 37 | 38 | 39 | @deprecated("Use 'fontTools.misc.plistlib.loads' instead") 40 | def readPlistFromString(data): 41 | return loads(tobytes(data, encoding="utf-8"), use_builtin_types=False) 42 | 43 | 44 | @deprecated("Use 'fontTools.misc.plistlib.dumps' instead") 45 | def writePlistToString(value): 46 | return dumps(value, use_builtin_types=False) 47 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ufoLib/pointPen.py: -------------------------------------------------------------------------------- 1 | """DEPRECATED - This module is kept here only as a backward compatibility shim 2 | for the old ufoLib.pointPen module, which was moved to fontTools.pens.pointPen. 3 | Please use the latter instead. 4 | """ 5 | from fontTools.pens.pointPen import * 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/ufoLib/utils.py: -------------------------------------------------------------------------------- 1 | """The module contains miscellaneous helpers. 2 | It's not considered part of the public ufoLib API. 3 | """ 4 | import warnings 5 | import functools 6 | 7 | 8 | numberTypes = (int, float) 9 | 10 | 11 | def deprecated(msg=""): 12 | """Decorator factory to mark functions as deprecated with given message. 13 | 14 | >>> @deprecated("Enough!") 15 | ... def some_function(): 16 | ... "I just print 'hello world'." 17 | ... print("hello world") 18 | >>> some_function() 19 | hello world 20 | >>> some_function.__doc__ == "I just print 'hello world'." 21 | True 22 | """ 23 | 24 | def deprecated_decorator(func): 25 | @functools.wraps(func) 26 | def wrapper(*args, **kwargs): 27 | warnings.warn( 28 | f"{func.__name__} function is a deprecated. {msg}", 29 | category=DeprecationWarning, 30 | stacklevel=2, 31 | ) 32 | return func(*args, **kwargs) 33 | 34 | return wrapper 35 | 36 | return deprecated_decorator 37 | 38 | 39 | # To be mixed with enum.Enum in UFOFormatVersion and GLIFFormatVersion 40 | class _VersionTupleEnumMixin: 41 | @property 42 | def major(self): 43 | return self.value[0] 44 | 45 | @property 46 | def minor(self): 47 | return self.value[1] 48 | 49 | @classmethod 50 | def _missing_(cls, value): 51 | # allow to initialize a version enum from a single (major) integer 52 | if isinstance(value, int): 53 | return cls((value, 0)) 54 | # or from None to obtain the current default version 55 | if value is None: 56 | return cls.default() 57 | return super()._missing_(value) 58 | 59 | def __str__(self): 60 | return f"{self.major}.{self.minor}" 61 | 62 | @classmethod 63 | def default(cls): 64 | # get the latest defined version (i.e. the max of all versions) 65 | return max(cls.__members__.values()) 66 | 67 | @classmethod 68 | def supported_versions(cls): 69 | return frozenset(cls.__members__.values()) 70 | 71 | 72 | if __name__ == "__main__": 73 | import doctest 74 | 75 | doctest.testmod() 76 | -------------------------------------------------------------------------------- /STERA-X/fontTools/unicode.py: -------------------------------------------------------------------------------- 1 | def _makeunicodes(f): 2 | lines = iter(f.readlines()) 3 | unicodes = {} 4 | for line in lines: 5 | if not line: 6 | continue 7 | num, name = line.split(";")[:2] 8 | if name[0] == "<": 9 | continue # "", etc. 10 | num = int(num, 16) 11 | unicodes[num] = name 12 | return unicodes 13 | 14 | 15 | class _UnicodeCustom(object): 16 | def __init__(self, f): 17 | if isinstance(f, str): 18 | with open(f) as fd: 19 | codes = _makeunicodes(fd) 20 | else: 21 | codes = _makeunicodes(f) 22 | self.codes = codes 23 | 24 | def __getitem__(self, charCode): 25 | try: 26 | return self.codes[charCode] 27 | except KeyError: 28 | return "????" 29 | 30 | 31 | class _UnicodeBuiltin(object): 32 | def __getitem__(self, charCode): 33 | try: 34 | # use unicodedata backport to python2, if available: 35 | # https://github.com/mikekap/unicodedata2 36 | import unicodedata2 as unicodedata 37 | except ImportError: 38 | import unicodedata 39 | try: 40 | return unicodedata.name(chr(charCode)) 41 | except ValueError: 42 | return "????" 43 | 44 | 45 | Unicode = _UnicodeBuiltin() 46 | 47 | 48 | def setUnicodeData(f): 49 | global Unicode 50 | Unicode = _UnicodeCustom(f) 51 | -------------------------------------------------------------------------------- /STERA-X/fontTools/unicodedata/OTTags.py: -------------------------------------------------------------------------------- 1 | # Data updated to OpenType 1.8.2 as of January 2018. 2 | 3 | # Complete list of OpenType script tags at: 4 | # https://www.microsoft.com/typography/otspec/scripttags.htm 5 | 6 | # Most of the script tags are the same as the ISO 15924 tag but lowercased, 7 | # so we only have to handle the exceptional cases: 8 | # - KATAKANA and HIRAGANA both map to 'kana'; 9 | # - spaces at the end are preserved, unlike ISO 15924; 10 | # - we map special script codes for Inherited, Common and Unknown to DFLT. 11 | 12 | DEFAULT_SCRIPT = "DFLT" 13 | 14 | SCRIPT_ALIASES = { 15 | "jamo": "hang", 16 | } 17 | 18 | SCRIPT_EXCEPTIONS = { 19 | "Hira": "kana", 20 | "Hrkt": "kana", 21 | "Laoo": "lao ", 22 | "Yiii": "yi ", 23 | "Nkoo": "nko ", 24 | "Vaii": "vai ", 25 | "Zmth": "math", 26 | "Zinh": DEFAULT_SCRIPT, 27 | "Zyyy": DEFAULT_SCRIPT, 28 | "Zzzz": DEFAULT_SCRIPT, 29 | } 30 | 31 | SCRIPT_EXCEPTIONS_REVERSED = { 32 | "math": "Zmth", 33 | } 34 | 35 | NEW_SCRIPT_TAGS = { 36 | "Beng": ("bng2",), 37 | "Deva": ("dev2",), 38 | "Gujr": ("gjr2",), 39 | "Guru": ("gur2",), 40 | "Knda": ("knd2",), 41 | "Mlym": ("mlm2",), 42 | "Orya": ("ory2",), 43 | "Taml": ("tml2",), 44 | "Telu": ("tel2",), 45 | "Mymr": ("mym2",), 46 | } 47 | 48 | NEW_SCRIPT_TAGS_REVERSED = { 49 | value: key for key, values in NEW_SCRIPT_TAGS.items() for value in values 50 | } 51 | -------------------------------------------------------------------------------- /STERA-X/fontTools/varLib/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from fontTools.varLib import main 3 | 4 | 5 | if __name__ == "__main__": 6 | sys.exit(main()) 7 | -------------------------------------------------------------------------------- /STERA-X/fontTools/varLib/instancer/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from fontTools.varLib.instancer import main 3 | 4 | if __name__ == "__main__": 5 | sys.exit(main()) 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/varLib/interpolate_layout.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interpolate OpenType Layout tables (GDEF / GPOS / GSUB). 3 | """ 4 | from fontTools.ttLib import TTFont 5 | from fontTools.varLib import models, VarLibError, load_designspace, load_masters 6 | from fontTools.varLib.merger import InstancerMerger 7 | import os.path 8 | import logging 9 | from copy import deepcopy 10 | from pprint import pformat 11 | 12 | log = logging.getLogger("fontTools.varLib.interpolate_layout") 13 | 14 | 15 | def interpolate_layout(designspace, loc, master_finder=lambda s: s, mapped=False): 16 | """ 17 | Interpolate GPOS from a designspace file and location. 18 | 19 | If master_finder is set, it should be a callable that takes master 20 | filename as found in designspace file and map it to master font 21 | binary as to be opened (eg. .ttf or .otf). 22 | 23 | If mapped is False (default), then location is mapped using the 24 | map element of the axes in designspace file. If mapped is True, 25 | it is assumed that location is in designspace's internal space and 26 | no mapping is performed. 27 | """ 28 | if hasattr(designspace, "sources"): # Assume a DesignspaceDocument 29 | pass 30 | else: # Assume a file path 31 | from fontTools.designspaceLib import DesignSpaceDocument 32 | 33 | designspace = DesignSpaceDocument.fromfile(designspace) 34 | 35 | ds = load_designspace(designspace) 36 | log.info("Building interpolated font") 37 | 38 | log.info("Loading master fonts") 39 | master_fonts = load_masters(designspace, master_finder) 40 | font = deepcopy(master_fonts[ds.base_idx]) 41 | 42 | log.info("Location: %s", pformat(loc)) 43 | if not mapped: 44 | loc = {name: ds.axes[name].map_forward(v) for name, v in loc.items()} 45 | log.info("Internal location: %s", pformat(loc)) 46 | loc = models.normalizeLocation(loc, ds.internal_axis_supports) 47 | log.info("Normalized location: %s", pformat(loc)) 48 | 49 | # Assume single-model for now. 50 | model = models.VariationModel(ds.normalized_master_locs) 51 | assert 0 == model.mapping[ds.base_idx] 52 | 53 | merger = InstancerMerger(font, model, loc) 54 | 55 | log.info("Building interpolated tables") 56 | # TODO GSUB/GDEF 57 | merger.mergeTables(font, master_fonts, ["GPOS"]) 58 | return font 59 | 60 | 61 | def main(args=None): 62 | """Interpolate GDEF/GPOS/GSUB tables for a point on a designspace""" 63 | from fontTools import configLogger 64 | import argparse 65 | import sys 66 | 67 | parser = argparse.ArgumentParser( 68 | "fonttools varLib.interpolate_layout", 69 | description=main.__doc__, 70 | ) 71 | parser.add_argument( 72 | "designspace_filename", metavar="DESIGNSPACE", help="Input TTF files" 73 | ) 74 | parser.add_argument( 75 | "locations", 76 | metavar="LOCATION", 77 | type=str, 78 | nargs="+", 79 | help="Axis locations (e.g. wdth=120", 80 | ) 81 | parser.add_argument( 82 | "-o", 83 | "--output", 84 | metavar="OUTPUT", 85 | help="Output font file (defaults to -instance.ttf)", 86 | ) 87 | parser.add_argument( 88 | "-l", 89 | "--loglevel", 90 | metavar="LEVEL", 91 | default="INFO", 92 | help="Logging level (defaults to INFO)", 93 | ) 94 | 95 | args = parser.parse_args(args) 96 | 97 | if not args.output: 98 | args.output = os.path.splitext(args.designspace_filename)[0] + "-instance.ttf" 99 | 100 | configLogger(level=args.loglevel) 101 | 102 | finder = lambda s: s.replace("master_ufo", "master_ttf_interpolatable").replace( 103 | ".ufo", ".ttf" 104 | ) 105 | 106 | loc = {} 107 | for arg in args.locations: 108 | tag, val = arg.split("=") 109 | loc[tag] = float(val) 110 | 111 | font = interpolate_layout(args.designspace_filename, loc, finder) 112 | log.info("Saving font %s", args.output) 113 | font.save(args.output) 114 | 115 | 116 | if __name__ == "__main__": 117 | import sys 118 | 119 | if len(sys.argv) > 1: 120 | sys.exit(main()) 121 | import doctest 122 | 123 | sys.exit(doctest.testmod().failed) 124 | -------------------------------------------------------------------------------- /STERA-X/fontTools/varLib/mvar.py: -------------------------------------------------------------------------------- 1 | MVAR_ENTRIES = { 2 | "hasc": ("OS/2", "sTypoAscender"), # horizontal ascender 3 | "hdsc": ("OS/2", "sTypoDescender"), # horizontal descender 4 | "hlgp": ("OS/2", "sTypoLineGap"), # horizontal line gap 5 | "hcla": ("OS/2", "usWinAscent"), # horizontal clipping ascent 6 | "hcld": ("OS/2", "usWinDescent"), # horizontal clipping descent 7 | "vasc": ("vhea", "ascent"), # vertical ascender 8 | "vdsc": ("vhea", "descent"), # vertical descender 9 | "vlgp": ("vhea", "lineGap"), # vertical line gap 10 | "hcrs": ("hhea", "caretSlopeRise"), # horizontal caret rise 11 | "hcrn": ("hhea", "caretSlopeRun"), # horizontal caret run 12 | "hcof": ("hhea", "caretOffset"), # horizontal caret offset 13 | "vcrs": ("vhea", "caretSlopeRise"), # vertical caret rise 14 | "vcrn": ("vhea", "caretSlopeRun"), # vertical caret run 15 | "vcof": ("vhea", "caretOffset"), # vertical caret offset 16 | "xhgt": ("OS/2", "sxHeight"), # x height 17 | "cpht": ("OS/2", "sCapHeight"), # cap height 18 | "sbxs": ("OS/2", "ySubscriptXSize"), # subscript em x size 19 | "sbys": ("OS/2", "ySubscriptYSize"), # subscript em y size 20 | "sbxo": ("OS/2", "ySubscriptXOffset"), # subscript em x offset 21 | "sbyo": ("OS/2", "ySubscriptYOffset"), # subscript em y offset 22 | "spxs": ("OS/2", "ySuperscriptXSize"), # superscript em x size 23 | "spys": ("OS/2", "ySuperscriptYSize"), # superscript em y size 24 | "spxo": ("OS/2", "ySuperscriptXOffset"), # superscript em x offset 25 | "spyo": ("OS/2", "ySuperscriptYOffset"), # superscript em y offset 26 | "strs": ("OS/2", "yStrikeoutSize"), # strikeout size 27 | "stro": ("OS/2", "yStrikeoutPosition"), # strikeout offset 28 | "unds": ("post", "underlineThickness"), # underline size 29 | "undo": ("post", "underlinePosition"), # underline offset 30 | #'gsp0': ('gasp', 'gaspRange[0].rangeMaxPPEM'), # gaspRange[0] 31 | #'gsp1': ('gasp', 'gaspRange[1].rangeMaxPPEM'), # gaspRange[1] 32 | #'gsp2': ('gasp', 'gaspRange[2].rangeMaxPPEM'), # gaspRange[2] 33 | #'gsp3': ('gasp', 'gaspRange[3].rangeMaxPPEM'), # gaspRange[3] 34 | #'gsp4': ('gasp', 'gaspRange[4].rangeMaxPPEM'), # gaspRange[4] 35 | #'gsp5': ('gasp', 'gaspRange[5].rangeMaxPPEM'), # gaspRange[5] 36 | #'gsp6': ('gasp', 'gaspRange[6].rangeMaxPPEM'), # gaspRange[6] 37 | #'gsp7': ('gasp', 'gaspRange[7].rangeMaxPPEM'), # gaspRange[7] 38 | #'gsp8': ('gasp', 'gaspRange[8].rangeMaxPPEM'), # gaspRange[8] 39 | #'gsp9': ('gasp', 'gaspRange[9].rangeMaxPPEM'), # gaspRange[9] 40 | } 41 | -------------------------------------------------------------------------------- /STERA-X/fontTools/voltLib/__init__.py: -------------------------------------------------------------------------------- 1 | """fontTools.voltLib -- a package for dealing with Visual OpenType Layout Tool 2 | (VOLT) files.""" 3 | 4 | # See 5 | # http://www.microsoft.com/typography/VOLT.mspx 6 | -------------------------------------------------------------------------------- /STERA-X/fontTools/voltLib/error.py: -------------------------------------------------------------------------------- 1 | class VoltLibError(Exception): 2 | def __init__(self, message, location): 3 | Exception.__init__(self, message) 4 | self.location = location 5 | 6 | def __str__(self): 7 | message = Exception.__str__(self) 8 | if self.location: 9 | path, line, column = self.location 10 | return "%s:%d:%d: %s" % (path, line, column, message) 11 | else: 12 | return message 13 | -------------------------------------------------------------------------------- /STERA-X/fontTools/voltLib/lexer.py: -------------------------------------------------------------------------------- 1 | from fontTools.voltLib.error import VoltLibError 2 | 3 | 4 | class Lexer(object): 5 | NUMBER = "NUMBER" 6 | STRING = "STRING" 7 | NAME = "NAME" 8 | NEWLINE = "NEWLINE" 9 | 10 | CHAR_WHITESPACE_ = " \t" 11 | CHAR_NEWLINE_ = "\r\n" 12 | CHAR_DIGIT_ = "0123456789" 13 | CHAR_UC_LETTER_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 14 | CHAR_LC_LETTER_ = "abcdefghijklmnopqrstuvwxyz" 15 | CHAR_UNDERSCORE_ = "_" 16 | CHAR_PERIOD_ = "." 17 | CHAR_NAME_START_ = ( 18 | CHAR_UC_LETTER_ + CHAR_LC_LETTER_ + CHAR_PERIOD_ + CHAR_UNDERSCORE_ 19 | ) 20 | CHAR_NAME_CONTINUATION_ = CHAR_NAME_START_ + CHAR_DIGIT_ 21 | 22 | def __init__(self, text, filename): 23 | self.filename_ = filename 24 | self.line_ = 1 25 | self.pos_ = 0 26 | self.line_start_ = 0 27 | self.text_ = text 28 | self.text_length_ = len(text) 29 | 30 | def __iter__(self): 31 | return self 32 | 33 | def next(self): # Python 2 34 | return self.__next__() 35 | 36 | def __next__(self): # Python 3 37 | while True: 38 | token_type, token, location = self.next_() 39 | if token_type not in {Lexer.NEWLINE}: 40 | return (token_type, token, location) 41 | 42 | def location_(self): 43 | column = self.pos_ - self.line_start_ + 1 44 | return (self.filename_ or "", self.line_, column) 45 | 46 | def next_(self): 47 | self.scan_over_(Lexer.CHAR_WHITESPACE_) 48 | location = self.location_() 49 | start = self.pos_ 50 | text = self.text_ 51 | limit = len(text) 52 | if start >= limit: 53 | raise StopIteration() 54 | cur_char = text[start] 55 | next_char = text[start + 1] if start + 1 < limit else None 56 | 57 | if cur_char == "\n": 58 | self.pos_ += 1 59 | self.line_ += 1 60 | self.line_start_ = self.pos_ 61 | return (Lexer.NEWLINE, None, location) 62 | if cur_char == "\r": 63 | self.pos_ += 2 if next_char == "\n" else 1 64 | self.line_ += 1 65 | self.line_start_ = self.pos_ 66 | return (Lexer.NEWLINE, None, location) 67 | if cur_char == '"': 68 | self.pos_ += 1 69 | self.scan_until_('"\r\n') 70 | if self.pos_ < self.text_length_ and self.text_[self.pos_] == '"': 71 | self.pos_ += 1 72 | return (Lexer.STRING, text[start + 1 : self.pos_ - 1], location) 73 | else: 74 | raise VoltLibError("Expected '\"' to terminate string", location) 75 | if cur_char in Lexer.CHAR_NAME_START_: 76 | self.pos_ += 1 77 | self.scan_over_(Lexer.CHAR_NAME_CONTINUATION_) 78 | token = text[start : self.pos_] 79 | return (Lexer.NAME, token, location) 80 | if cur_char in Lexer.CHAR_DIGIT_: 81 | self.scan_over_(Lexer.CHAR_DIGIT_) 82 | return (Lexer.NUMBER, int(text[start : self.pos_], 10), location) 83 | if cur_char == "-" and next_char in Lexer.CHAR_DIGIT_: 84 | self.pos_ += 1 85 | self.scan_over_(Lexer.CHAR_DIGIT_) 86 | return (Lexer.NUMBER, int(text[start : self.pos_], 10), location) 87 | raise VoltLibError("Unexpected character: '%s'" % cur_char, location) 88 | 89 | def scan_over_(self, valid): 90 | p = self.pos_ 91 | while p < self.text_length_ and self.text_[p] in valid: 92 | p += 1 93 | self.pos_ = p 94 | 95 | def scan_until_(self, stop_at): 96 | p = self.pos_ 97 | while p < self.text_length_ and self.text_[p] not in stop_at: 98 | p += 1 99 | self.pos_ = p 100 | -------------------------------------------------------------------------------- /STERA-X/plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Minami926494/STERA/a335b3ee663b60b5f2717c41b72038c1145d5a7a/STERA-X/plugin.png -------------------------------------------------------------------------------- /STERA-X/plugin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | from STERAEPUB import start 4 | from sys import exit 5 | 6 | 7 | def run(bk): 8 | if bk.launcher_version() <= 20220101: 9 | print('Sigil版本过低,请更新至Sigil1.9.0+!') 10 | return -1 11 | elif not bk.epub_is_standard(): 12 | print('文档不符合Sigil规范,请先执行规范化!') 13 | return -1 14 | elif not bk.epub_version().startswith('3'): 15 | print('文件不是有效的EPUB3文档,请先转换至EPUB3!') 16 | return -1 17 | else: 18 | start(bk) 19 | return 0 20 | 21 | 22 | if __name__ == '__main__': 23 | print('程序运行环境异常!') 24 | exit(-1) 25 | -------------------------------------------------------------------------------- /STERA-X/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | STERA-X 4 | edit 5 | 明日✿咲葉 6 | 虚空文学旅团STERAePub++自动化书源处理 7 | python3.4 8 | 1.4.0beta1 9 | osx,unx,win 10 | true 11 | false 12 | 13 | --------------------------------------------------------------------------------