├── 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 |
--------------------------------------------------------------------------------