fonttools 4.60.2__cp311-cp311-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fontTools/__init__.py +8 -0
- fontTools/__main__.py +35 -0
- fontTools/afmLib.py +439 -0
- fontTools/agl.py +5233 -0
- fontTools/annotations.py +30 -0
- fontTools/cffLib/CFF2ToCFF.py +258 -0
- fontTools/cffLib/CFFToCFF2.py +305 -0
- fontTools/cffLib/__init__.py +3694 -0
- fontTools/cffLib/specializer.py +927 -0
- fontTools/cffLib/transforms.py +495 -0
- fontTools/cffLib/width.py +210 -0
- fontTools/colorLib/__init__.py +0 -0
- fontTools/colorLib/builder.py +664 -0
- fontTools/colorLib/errors.py +2 -0
- fontTools/colorLib/geometry.py +143 -0
- fontTools/colorLib/table_builder.py +223 -0
- fontTools/colorLib/unbuilder.py +81 -0
- fontTools/config/__init__.py +90 -0
- fontTools/cu2qu/__init__.py +15 -0
- fontTools/cu2qu/__main__.py +6 -0
- fontTools/cu2qu/benchmark.py +54 -0
- fontTools/cu2qu/cli.py +198 -0
- fontTools/cu2qu/cu2qu.c +15817 -0
- fontTools/cu2qu/cu2qu.cp311-win32.pyd +0 -0
- fontTools/cu2qu/cu2qu.py +563 -0
- fontTools/cu2qu/errors.py +77 -0
- fontTools/cu2qu/ufo.py +363 -0
- fontTools/designspaceLib/__init__.py +3343 -0
- fontTools/designspaceLib/__main__.py +6 -0
- fontTools/designspaceLib/split.py +475 -0
- fontTools/designspaceLib/statNames.py +260 -0
- fontTools/designspaceLib/types.py +147 -0
- fontTools/encodings/MacRoman.py +258 -0
- fontTools/encodings/StandardEncoding.py +258 -0
- fontTools/encodings/__init__.py +1 -0
- fontTools/encodings/codecs.py +135 -0
- fontTools/feaLib/__init__.py +4 -0
- fontTools/feaLib/__main__.py +78 -0
- fontTools/feaLib/ast.py +2143 -0
- fontTools/feaLib/builder.py +1814 -0
- fontTools/feaLib/error.py +22 -0
- fontTools/feaLib/lexer.c +17029 -0
- fontTools/feaLib/lexer.cp311-win32.pyd +0 -0
- fontTools/feaLib/lexer.py +287 -0
- fontTools/feaLib/location.py +12 -0
- fontTools/feaLib/lookupDebugInfo.py +12 -0
- fontTools/feaLib/parser.py +2394 -0
- fontTools/feaLib/variableScalar.py +118 -0
- fontTools/fontBuilder.py +1014 -0
- fontTools/help.py +36 -0
- fontTools/merge/__init__.py +248 -0
- fontTools/merge/__main__.py +6 -0
- fontTools/merge/base.py +81 -0
- fontTools/merge/cmap.py +173 -0
- fontTools/merge/layout.py +526 -0
- fontTools/merge/options.py +85 -0
- fontTools/merge/tables.py +352 -0
- fontTools/merge/unicode.py +78 -0
- fontTools/merge/util.py +143 -0
- fontTools/misc/__init__.py +1 -0
- fontTools/misc/arrayTools.py +424 -0
- fontTools/misc/bezierTools.c +39731 -0
- fontTools/misc/bezierTools.cp311-win32.pyd +0 -0
- fontTools/misc/bezierTools.py +1500 -0
- fontTools/misc/classifyTools.py +170 -0
- fontTools/misc/cliTools.py +53 -0
- fontTools/misc/configTools.py +349 -0
- fontTools/misc/cython.py +27 -0
- fontTools/misc/dictTools.py +83 -0
- fontTools/misc/eexec.py +119 -0
- fontTools/misc/encodingTools.py +72 -0
- fontTools/misc/enumTools.py +23 -0
- fontTools/misc/etree.py +456 -0
- fontTools/misc/filenames.py +245 -0
- fontTools/misc/filesystem/__init__.py +68 -0
- fontTools/misc/filesystem/_base.py +134 -0
- fontTools/misc/filesystem/_copy.py +45 -0
- fontTools/misc/filesystem/_errors.py +54 -0
- fontTools/misc/filesystem/_info.py +75 -0
- fontTools/misc/filesystem/_osfs.py +164 -0
- fontTools/misc/filesystem/_path.py +67 -0
- fontTools/misc/filesystem/_subfs.py +92 -0
- fontTools/misc/filesystem/_tempfs.py +34 -0
- fontTools/misc/filesystem/_tools.py +34 -0
- fontTools/misc/filesystem/_walk.py +55 -0
- fontTools/misc/filesystem/_zipfs.py +204 -0
- fontTools/misc/fixedTools.py +253 -0
- fontTools/misc/intTools.py +25 -0
- fontTools/misc/iterTools.py +12 -0
- fontTools/misc/lazyTools.py +42 -0
- fontTools/misc/loggingTools.py +543 -0
- fontTools/misc/macCreatorType.py +56 -0
- fontTools/misc/macRes.py +261 -0
- fontTools/misc/plistlib/__init__.py +681 -0
- fontTools/misc/plistlib/py.typed +0 -0
- fontTools/misc/psCharStrings.py +1511 -0
- fontTools/misc/psLib.py +398 -0
- fontTools/misc/psOperators.py +572 -0
- fontTools/misc/py23.py +96 -0
- fontTools/misc/roundTools.py +110 -0
- fontTools/misc/sstruct.py +227 -0
- fontTools/misc/symfont.py +242 -0
- fontTools/misc/testTools.py +233 -0
- fontTools/misc/textTools.py +156 -0
- fontTools/misc/timeTools.py +88 -0
- fontTools/misc/transform.py +516 -0
- fontTools/misc/treeTools.py +45 -0
- fontTools/misc/vector.py +147 -0
- fontTools/misc/visitor.py +158 -0
- fontTools/misc/xmlReader.py +188 -0
- fontTools/misc/xmlWriter.py +231 -0
- fontTools/mtiLib/__init__.py +1400 -0
- fontTools/mtiLib/__main__.py +5 -0
- fontTools/otlLib/__init__.py +1 -0
- fontTools/otlLib/builder.py +3465 -0
- fontTools/otlLib/error.py +11 -0
- fontTools/otlLib/maxContextCalc.py +96 -0
- fontTools/otlLib/optimize/__init__.py +53 -0
- fontTools/otlLib/optimize/__main__.py +6 -0
- fontTools/otlLib/optimize/gpos.py +439 -0
- fontTools/pens/__init__.py +1 -0
- fontTools/pens/areaPen.py +52 -0
- fontTools/pens/basePen.py +475 -0
- fontTools/pens/boundsPen.py +98 -0
- fontTools/pens/cairoPen.py +26 -0
- fontTools/pens/cocoaPen.py +26 -0
- fontTools/pens/cu2quPen.py +325 -0
- fontTools/pens/explicitClosingLinePen.py +101 -0
- fontTools/pens/filterPen.py +433 -0
- fontTools/pens/freetypePen.py +462 -0
- fontTools/pens/hashPointPen.py +89 -0
- fontTools/pens/momentsPen.c +13378 -0
- fontTools/pens/momentsPen.cp311-win32.pyd +0 -0
- fontTools/pens/momentsPen.py +879 -0
- fontTools/pens/perimeterPen.py +69 -0
- fontTools/pens/pointInsidePen.py +192 -0
- fontTools/pens/pointPen.py +643 -0
- fontTools/pens/qtPen.py +29 -0
- fontTools/pens/qu2cuPen.py +105 -0
- fontTools/pens/quartzPen.py +43 -0
- fontTools/pens/recordingPen.py +335 -0
- fontTools/pens/reportLabPen.py +79 -0
- fontTools/pens/reverseContourPen.py +96 -0
- fontTools/pens/roundingPen.py +130 -0
- fontTools/pens/statisticsPen.py +312 -0
- fontTools/pens/svgPathPen.py +310 -0
- fontTools/pens/t2CharStringPen.py +88 -0
- fontTools/pens/teePen.py +55 -0
- fontTools/pens/transformPen.py +115 -0
- fontTools/pens/ttGlyphPen.py +335 -0
- fontTools/pens/wxPen.py +29 -0
- fontTools/qu2cu/__init__.py +15 -0
- fontTools/qu2cu/__main__.py +7 -0
- fontTools/qu2cu/benchmark.py +56 -0
- fontTools/qu2cu/cli.py +125 -0
- fontTools/qu2cu/qu2cu.c +16682 -0
- fontTools/qu2cu/qu2cu.cp311-win32.pyd +0 -0
- fontTools/qu2cu/qu2cu.py +405 -0
- fontTools/subset/__init__.py +4096 -0
- fontTools/subset/__main__.py +6 -0
- fontTools/subset/cff.py +184 -0
- fontTools/subset/svg.py +253 -0
- fontTools/subset/util.py +25 -0
- fontTools/svgLib/__init__.py +3 -0
- fontTools/svgLib/path/__init__.py +65 -0
- fontTools/svgLib/path/arc.py +154 -0
- fontTools/svgLib/path/parser.py +322 -0
- fontTools/svgLib/path/shapes.py +183 -0
- fontTools/t1Lib/__init__.py +648 -0
- fontTools/tfmLib.py +460 -0
- fontTools/ttLib/__init__.py +30 -0
- fontTools/ttLib/__main__.py +148 -0
- fontTools/ttLib/macUtils.py +54 -0
- fontTools/ttLib/removeOverlaps.py +395 -0
- fontTools/ttLib/reorderGlyphs.py +285 -0
- fontTools/ttLib/scaleUpem.py +436 -0
- fontTools/ttLib/sfnt.py +661 -0
- fontTools/ttLib/standardGlyphOrder.py +271 -0
- fontTools/ttLib/tables/B_A_S_E_.py +14 -0
- fontTools/ttLib/tables/BitmapGlyphMetrics.py +64 -0
- fontTools/ttLib/tables/C_B_D_T_.py +113 -0
- fontTools/ttLib/tables/C_B_L_C_.py +19 -0
- fontTools/ttLib/tables/C_F_F_.py +61 -0
- fontTools/ttLib/tables/C_F_F__2.py +26 -0
- fontTools/ttLib/tables/C_O_L_R_.py +165 -0
- fontTools/ttLib/tables/C_P_A_L_.py +305 -0
- fontTools/ttLib/tables/D_S_I_G_.py +158 -0
- fontTools/ttLib/tables/D__e_b_g.py +35 -0
- fontTools/ttLib/tables/DefaultTable.py +49 -0
- fontTools/ttLib/tables/E_B_D_T_.py +835 -0
- fontTools/ttLib/tables/E_B_L_C_.py +718 -0
- fontTools/ttLib/tables/F_F_T_M_.py +52 -0
- fontTools/ttLib/tables/F__e_a_t.py +149 -0
- fontTools/ttLib/tables/G_D_E_F_.py +13 -0
- fontTools/ttLib/tables/G_M_A_P_.py +148 -0
- fontTools/ttLib/tables/G_P_K_G_.py +133 -0
- fontTools/ttLib/tables/G_P_O_S_.py +14 -0
- fontTools/ttLib/tables/G_S_U_B_.py +13 -0
- fontTools/ttLib/tables/G_V_A_R_.py +5 -0
- fontTools/ttLib/tables/G__l_a_t.py +235 -0
- fontTools/ttLib/tables/G__l_o_c.py +85 -0
- fontTools/ttLib/tables/H_V_A_R_.py +13 -0
- fontTools/ttLib/tables/J_S_T_F_.py +13 -0
- fontTools/ttLib/tables/L_T_S_H_.py +58 -0
- fontTools/ttLib/tables/M_A_T_H_.py +13 -0
- fontTools/ttLib/tables/M_E_T_A_.py +352 -0
- fontTools/ttLib/tables/M_V_A_R_.py +13 -0
- fontTools/ttLib/tables/O_S_2f_2.py +752 -0
- fontTools/ttLib/tables/S_I_N_G_.py +99 -0
- fontTools/ttLib/tables/S_T_A_T_.py +15 -0
- fontTools/ttLib/tables/S_V_G_.py +223 -0
- fontTools/ttLib/tables/S__i_l_f.py +1040 -0
- fontTools/ttLib/tables/S__i_l_l.py +92 -0
- fontTools/ttLib/tables/T_S_I_B_.py +13 -0
- fontTools/ttLib/tables/T_S_I_C_.py +14 -0
- fontTools/ttLib/tables/T_S_I_D_.py +13 -0
- fontTools/ttLib/tables/T_S_I_J_.py +13 -0
- fontTools/ttLib/tables/T_S_I_P_.py +13 -0
- fontTools/ttLib/tables/T_S_I_S_.py +13 -0
- fontTools/ttLib/tables/T_S_I_V_.py +26 -0
- fontTools/ttLib/tables/T_S_I__0.py +70 -0
- fontTools/ttLib/tables/T_S_I__1.py +163 -0
- fontTools/ttLib/tables/T_S_I__2.py +17 -0
- fontTools/ttLib/tables/T_S_I__3.py +22 -0
- fontTools/ttLib/tables/T_S_I__5.py +60 -0
- fontTools/ttLib/tables/T_T_F_A_.py +14 -0
- fontTools/ttLib/tables/TupleVariation.py +884 -0
- fontTools/ttLib/tables/V_A_R_C_.py +12 -0
- fontTools/ttLib/tables/V_D_M_X_.py +249 -0
- fontTools/ttLib/tables/V_O_R_G_.py +165 -0
- fontTools/ttLib/tables/V_V_A_R_.py +13 -0
- fontTools/ttLib/tables/__init__.py +98 -0
- fontTools/ttLib/tables/_a_n_k_r.py +15 -0
- fontTools/ttLib/tables/_a_v_a_r.py +193 -0
- fontTools/ttLib/tables/_b_s_l_n.py +15 -0
- fontTools/ttLib/tables/_c_i_d_g.py +24 -0
- fontTools/ttLib/tables/_c_m_a_p.py +1591 -0
- fontTools/ttLib/tables/_c_v_a_r.py +94 -0
- fontTools/ttLib/tables/_c_v_t.py +56 -0
- fontTools/ttLib/tables/_f_e_a_t.py +15 -0
- fontTools/ttLib/tables/_f_p_g_m.py +62 -0
- fontTools/ttLib/tables/_f_v_a_r.py +261 -0
- fontTools/ttLib/tables/_g_a_s_p.py +63 -0
- fontTools/ttLib/tables/_g_c_i_d.py +13 -0
- fontTools/ttLib/tables/_g_l_y_f.py +2311 -0
- fontTools/ttLib/tables/_g_v_a_r.py +340 -0
- fontTools/ttLib/tables/_h_d_m_x.py +127 -0
- fontTools/ttLib/tables/_h_e_a_d.py +130 -0
- fontTools/ttLib/tables/_h_h_e_a.py +147 -0
- fontTools/ttLib/tables/_h_m_t_x.py +164 -0
- fontTools/ttLib/tables/_k_e_r_n.py +289 -0
- fontTools/ttLib/tables/_l_c_a_r.py +13 -0
- fontTools/ttLib/tables/_l_o_c_a.py +70 -0
- fontTools/ttLib/tables/_l_t_a_g.py +72 -0
- fontTools/ttLib/tables/_m_a_x_p.py +147 -0
- fontTools/ttLib/tables/_m_e_t_a.py +112 -0
- fontTools/ttLib/tables/_m_o_r_t.py +14 -0
- fontTools/ttLib/tables/_m_o_r_x.py +15 -0
- fontTools/ttLib/tables/_n_a_m_e.py +1242 -0
- fontTools/ttLib/tables/_o_p_b_d.py +14 -0
- fontTools/ttLib/tables/_p_o_s_t.py +319 -0
- fontTools/ttLib/tables/_p_r_e_p.py +16 -0
- fontTools/ttLib/tables/_p_r_o_p.py +12 -0
- fontTools/ttLib/tables/_s_b_i_x.py +129 -0
- fontTools/ttLib/tables/_t_r_a_k.py +332 -0
- fontTools/ttLib/tables/_v_h_e_a.py +139 -0
- fontTools/ttLib/tables/_v_m_t_x.py +19 -0
- fontTools/ttLib/tables/asciiTable.py +20 -0
- fontTools/ttLib/tables/grUtils.py +92 -0
- fontTools/ttLib/tables/otBase.py +1458 -0
- fontTools/ttLib/tables/otConverters.py +2068 -0
- fontTools/ttLib/tables/otData.py +6400 -0
- fontTools/ttLib/tables/otTables.py +2703 -0
- fontTools/ttLib/tables/otTraverse.py +163 -0
- fontTools/ttLib/tables/sbixGlyph.py +149 -0
- fontTools/ttLib/tables/sbixStrike.py +177 -0
- fontTools/ttLib/tables/table_API_readme.txt +91 -0
- fontTools/ttLib/tables/ttProgram.py +594 -0
- fontTools/ttLib/ttCollection.py +125 -0
- fontTools/ttLib/ttFont.py +1148 -0
- fontTools/ttLib/ttGlyphSet.py +490 -0
- fontTools/ttLib/ttVisitor.py +32 -0
- fontTools/ttLib/woff2.py +1680 -0
- fontTools/ttx.py +479 -0
- fontTools/ufoLib/__init__.py +2575 -0
- fontTools/ufoLib/converters.py +407 -0
- fontTools/ufoLib/errors.py +30 -0
- fontTools/ufoLib/etree.py +6 -0
- fontTools/ufoLib/filenames.py +356 -0
- fontTools/ufoLib/glifLib.py +2120 -0
- fontTools/ufoLib/kerning.py +141 -0
- fontTools/ufoLib/plistlib.py +47 -0
- fontTools/ufoLib/pointPen.py +6 -0
- fontTools/ufoLib/utils.py +107 -0
- fontTools/ufoLib/validators.py +1208 -0
- fontTools/unicode.py +50 -0
- fontTools/unicodedata/Blocks.py +817 -0
- fontTools/unicodedata/Mirrored.py +446 -0
- fontTools/unicodedata/OTTags.py +50 -0
- fontTools/unicodedata/ScriptExtensions.py +832 -0
- fontTools/unicodedata/Scripts.py +3639 -0
- fontTools/unicodedata/__init__.py +306 -0
- fontTools/varLib/__init__.py +1600 -0
- fontTools/varLib/__main__.py +6 -0
- fontTools/varLib/avar/__init__.py +0 -0
- fontTools/varLib/avar/__main__.py +72 -0
- fontTools/varLib/avar/build.py +79 -0
- fontTools/varLib/avar/map.py +108 -0
- fontTools/varLib/avar/plan.py +1004 -0
- fontTools/varLib/avar/unbuild.py +271 -0
- fontTools/varLib/avarPlanner.py +8 -0
- fontTools/varLib/builder.py +215 -0
- fontTools/varLib/cff.py +631 -0
- fontTools/varLib/errors.py +219 -0
- fontTools/varLib/featureVars.py +703 -0
- fontTools/varLib/hvar.py +113 -0
- fontTools/varLib/instancer/__init__.py +2052 -0
- fontTools/varLib/instancer/__main__.py +5 -0
- fontTools/varLib/instancer/featureVars.py +190 -0
- fontTools/varLib/instancer/names.py +388 -0
- fontTools/varLib/instancer/solver.py +309 -0
- fontTools/varLib/interpolatable.py +1209 -0
- fontTools/varLib/interpolatableHelpers.py +399 -0
- fontTools/varLib/interpolatablePlot.py +1269 -0
- fontTools/varLib/interpolatableTestContourOrder.py +82 -0
- fontTools/varLib/interpolatableTestStartingPoint.py +107 -0
- fontTools/varLib/interpolate_layout.py +124 -0
- fontTools/varLib/iup.c +19815 -0
- fontTools/varLib/iup.cp311-win32.pyd +0 -0
- fontTools/varLib/iup.py +490 -0
- fontTools/varLib/merger.py +1717 -0
- fontTools/varLib/models.py +642 -0
- fontTools/varLib/multiVarStore.py +253 -0
- fontTools/varLib/mutator.py +529 -0
- fontTools/varLib/mvar.py +40 -0
- fontTools/varLib/plot.py +238 -0
- fontTools/varLib/stat.py +149 -0
- fontTools/varLib/varStore.py +739 -0
- fontTools/voltLib/__init__.py +5 -0
- fontTools/voltLib/__main__.py +206 -0
- fontTools/voltLib/ast.py +452 -0
- fontTools/voltLib/error.py +12 -0
- fontTools/voltLib/lexer.py +99 -0
- fontTools/voltLib/parser.py +664 -0
- fontTools/voltLib/voltToFea.py +911 -0
- fonttools-4.60.2.data/data/share/man/man1/ttx.1 +225 -0
- fonttools-4.60.2.dist-info/METADATA +2250 -0
- fonttools-4.60.2.dist-info/RECORD +353 -0
- fonttools-4.60.2.dist-info/WHEEL +5 -0
- fonttools-4.60.2.dist-info/entry_points.txt +5 -0
- fonttools-4.60.2.dist-info/licenses/LICENSE +21 -0
- fonttools-4.60.2.dist-info/licenses/LICENSE.external +388 -0
- fonttools-4.60.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""Helpers for writing unit tests."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import shutil
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
from unittest import TestCase as _TestCase
|
|
11
|
+
from fontTools.config import Config
|
|
12
|
+
from fontTools.misc.textTools import tobytes
|
|
13
|
+
from fontTools.misc.xmlWriter import XMLWriter
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parseXML(xmlSnippet):
|
|
17
|
+
"""Parses a snippet of XML.
|
|
18
|
+
|
|
19
|
+
Input can be either a single string (unicode or UTF-8 bytes), or a
|
|
20
|
+
a sequence of strings.
|
|
21
|
+
|
|
22
|
+
The result is in the same format that would be returned by
|
|
23
|
+
XMLReader, but the parser imposes no constraints on the root
|
|
24
|
+
element so it can be called on small snippets of TTX files.
|
|
25
|
+
"""
|
|
26
|
+
# To support snippets with multiple elements, we add a fake root.
|
|
27
|
+
reader = TestXMLReader_()
|
|
28
|
+
xml = b"<root>"
|
|
29
|
+
if isinstance(xmlSnippet, bytes):
|
|
30
|
+
xml += xmlSnippet
|
|
31
|
+
elif isinstance(xmlSnippet, str):
|
|
32
|
+
xml += tobytes(xmlSnippet, "utf-8")
|
|
33
|
+
elif isinstance(xmlSnippet, Iterable):
|
|
34
|
+
xml += b"".join(tobytes(s, "utf-8") for s in xmlSnippet)
|
|
35
|
+
else:
|
|
36
|
+
raise TypeError(
|
|
37
|
+
"expected string or sequence of strings; found %r"
|
|
38
|
+
% type(xmlSnippet).__name__
|
|
39
|
+
)
|
|
40
|
+
xml += b"</root>"
|
|
41
|
+
reader.parser.Parse(xml, 1)
|
|
42
|
+
return reader.root[2]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def parseXmlInto(font, parseInto, xmlSnippet):
|
|
46
|
+
parsed_xml = [e for e in parseXML(xmlSnippet.strip()) if not isinstance(e, str)]
|
|
47
|
+
for name, attrs, content in parsed_xml:
|
|
48
|
+
parseInto.fromXML(name, attrs, content, font)
|
|
49
|
+
if hasattr(parseInto, "populateDefaults"):
|
|
50
|
+
parseInto.populateDefaults()
|
|
51
|
+
return parseInto
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class FakeFont:
|
|
55
|
+
def __init__(self, glyphs):
|
|
56
|
+
self.glyphOrder_ = glyphs
|
|
57
|
+
self.reverseGlyphOrderDict_ = {g: i for i, g in enumerate(glyphs)}
|
|
58
|
+
self.lazy = False
|
|
59
|
+
self.tables = {}
|
|
60
|
+
self.cfg = Config()
|
|
61
|
+
|
|
62
|
+
def __contains__(self, tag):
|
|
63
|
+
return tag in self.tables
|
|
64
|
+
|
|
65
|
+
def __getitem__(self, tag):
|
|
66
|
+
return self.tables[tag]
|
|
67
|
+
|
|
68
|
+
def __setitem__(self, tag, table):
|
|
69
|
+
self.tables[tag] = table
|
|
70
|
+
|
|
71
|
+
def get(self, tag, default=None):
|
|
72
|
+
return self.tables.get(tag, default)
|
|
73
|
+
|
|
74
|
+
def getGlyphID(self, name):
|
|
75
|
+
return self.reverseGlyphOrderDict_[name]
|
|
76
|
+
|
|
77
|
+
def getGlyphIDMany(self, lst):
|
|
78
|
+
return [self.getGlyphID(gid) for gid in lst]
|
|
79
|
+
|
|
80
|
+
def getGlyphName(self, glyphID):
|
|
81
|
+
if glyphID < len(self.glyphOrder_):
|
|
82
|
+
return self.glyphOrder_[glyphID]
|
|
83
|
+
else:
|
|
84
|
+
return "glyph%.5d" % glyphID
|
|
85
|
+
|
|
86
|
+
def getGlyphNameMany(self, lst):
|
|
87
|
+
return [self.getGlyphName(gid) for gid in lst]
|
|
88
|
+
|
|
89
|
+
def getGlyphOrder(self):
|
|
90
|
+
return self.glyphOrder_
|
|
91
|
+
|
|
92
|
+
def getReverseGlyphMap(self):
|
|
93
|
+
return self.reverseGlyphOrderDict_
|
|
94
|
+
|
|
95
|
+
def getGlyphNames(self):
|
|
96
|
+
return sorted(self.getGlyphOrder())
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TestXMLReader_(object):
|
|
100
|
+
def __init__(self):
|
|
101
|
+
from xml.parsers.expat import ParserCreate
|
|
102
|
+
|
|
103
|
+
self.parser = ParserCreate()
|
|
104
|
+
self.parser.StartElementHandler = self.startElement_
|
|
105
|
+
self.parser.EndElementHandler = self.endElement_
|
|
106
|
+
self.parser.CharacterDataHandler = self.addCharacterData_
|
|
107
|
+
self.root = None
|
|
108
|
+
self.stack = []
|
|
109
|
+
|
|
110
|
+
def startElement_(self, name, attrs):
|
|
111
|
+
element = (name, attrs, [])
|
|
112
|
+
if self.stack:
|
|
113
|
+
self.stack[-1][2].append(element)
|
|
114
|
+
else:
|
|
115
|
+
self.root = element
|
|
116
|
+
self.stack.append(element)
|
|
117
|
+
|
|
118
|
+
def endElement_(self, name):
|
|
119
|
+
self.stack.pop()
|
|
120
|
+
|
|
121
|
+
def addCharacterData_(self, data):
|
|
122
|
+
self.stack[-1][2].append(data)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def makeXMLWriter(newlinestr="\n"):
|
|
126
|
+
# don't write OS-specific new lines
|
|
127
|
+
writer = XMLWriter(BytesIO(), newlinestr=newlinestr)
|
|
128
|
+
# erase XML declaration
|
|
129
|
+
writer.file.seek(0)
|
|
130
|
+
writer.file.truncate()
|
|
131
|
+
return writer
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def getXML(func, ttFont=None):
|
|
135
|
+
"""Call the passed toXML function and return the written content as a
|
|
136
|
+
list of lines (unicode strings).
|
|
137
|
+
Result is stripped of XML declaration and OS-specific newline characters.
|
|
138
|
+
"""
|
|
139
|
+
writer = makeXMLWriter()
|
|
140
|
+
func(writer, ttFont)
|
|
141
|
+
xml = writer.file.getvalue().decode("utf-8")
|
|
142
|
+
# toXML methods must always end with a writer.newline()
|
|
143
|
+
assert xml.endswith("\n")
|
|
144
|
+
return xml.splitlines()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def stripVariableItemsFromTTX(
|
|
148
|
+
string: str,
|
|
149
|
+
ttLibVersion: bool = True,
|
|
150
|
+
checkSumAdjustment: bool = True,
|
|
151
|
+
modified: bool = True,
|
|
152
|
+
created: bool = True,
|
|
153
|
+
sfntVersion: bool = False, # opt-in only
|
|
154
|
+
) -> str:
|
|
155
|
+
"""Strip stuff like ttLibVersion, checksums, timestamps, etc. from TTX dumps."""
|
|
156
|
+
# ttlib changes with the fontTools version
|
|
157
|
+
if ttLibVersion:
|
|
158
|
+
string = re.sub(' ttLibVersion="[^"]+"', "", string)
|
|
159
|
+
# sometimes (e.g. some subsetter tests) we don't care whether it's OTF or TTF
|
|
160
|
+
if sfntVersion:
|
|
161
|
+
string = re.sub(' sfntVersion="[^"]+"', "", string)
|
|
162
|
+
# head table checksum and creation and mod date changes with each save.
|
|
163
|
+
if checkSumAdjustment:
|
|
164
|
+
string = re.sub('<checkSumAdjustment value="[^"]+"/>', "", string)
|
|
165
|
+
if modified:
|
|
166
|
+
string = re.sub('<modified value="[^"]+"/>', "", string)
|
|
167
|
+
if created:
|
|
168
|
+
string = re.sub('<created value="[^"]+"/>', "", string)
|
|
169
|
+
return string
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class MockFont(object):
|
|
173
|
+
"""A font-like object that automatically adds any looked up glyphname
|
|
174
|
+
to its glyphOrder."""
|
|
175
|
+
|
|
176
|
+
def __init__(self):
|
|
177
|
+
self._glyphOrder = [".notdef"]
|
|
178
|
+
|
|
179
|
+
class AllocatingDict(dict):
|
|
180
|
+
def __missing__(reverseDict, key):
|
|
181
|
+
self._glyphOrder.append(key)
|
|
182
|
+
gid = len(reverseDict)
|
|
183
|
+
reverseDict[key] = gid
|
|
184
|
+
return gid
|
|
185
|
+
|
|
186
|
+
self._reverseGlyphOrder = AllocatingDict({".notdef": 0})
|
|
187
|
+
self.lazy = False
|
|
188
|
+
|
|
189
|
+
def getGlyphID(self, glyph):
|
|
190
|
+
gid = self._reverseGlyphOrder[glyph]
|
|
191
|
+
return gid
|
|
192
|
+
|
|
193
|
+
def getReverseGlyphMap(self):
|
|
194
|
+
return self._reverseGlyphOrder
|
|
195
|
+
|
|
196
|
+
def getGlyphName(self, gid):
|
|
197
|
+
return self._glyphOrder[gid]
|
|
198
|
+
|
|
199
|
+
def getGlyphOrder(self):
|
|
200
|
+
return self._glyphOrder
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class TestCase(_TestCase):
|
|
204
|
+
def __init__(self, methodName):
|
|
205
|
+
_TestCase.__init__(self, methodName)
|
|
206
|
+
# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
|
|
207
|
+
# and fires deprecation warnings if a program uses the old name.
|
|
208
|
+
if not hasattr(self, "assertRaisesRegex"):
|
|
209
|
+
self.assertRaisesRegex = self.assertRaisesRegexp
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class DataFilesHandler(TestCase):
|
|
213
|
+
def setUp(self):
|
|
214
|
+
self.tempdir = None
|
|
215
|
+
self.num_tempfiles = 0
|
|
216
|
+
|
|
217
|
+
def tearDown(self):
|
|
218
|
+
if self.tempdir:
|
|
219
|
+
shutil.rmtree(self.tempdir)
|
|
220
|
+
|
|
221
|
+
def getpath(self, testfile):
|
|
222
|
+
folder = os.path.dirname(sys.modules[self.__module__].__file__)
|
|
223
|
+
return os.path.join(folder, "data", testfile)
|
|
224
|
+
|
|
225
|
+
def temp_dir(self):
|
|
226
|
+
if not self.tempdir:
|
|
227
|
+
self.tempdir = tempfile.mkdtemp()
|
|
228
|
+
|
|
229
|
+
def temp_font(self, font_path, file_name):
|
|
230
|
+
self.temp_dir()
|
|
231
|
+
temppath = os.path.join(self.tempdir, file_name)
|
|
232
|
+
shutil.copy2(font_path, temppath)
|
|
233
|
+
return temppath
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""fontTools.misc.textTools.py -- miscellaneous routines."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import string
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# alias kept for backward compatibility
|
|
10
|
+
safeEval = ast.literal_eval
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Tag(str):
|
|
14
|
+
@staticmethod
|
|
15
|
+
def transcode(blob):
|
|
16
|
+
if isinstance(blob, bytes):
|
|
17
|
+
blob = blob.decode("latin-1")
|
|
18
|
+
return blob
|
|
19
|
+
|
|
20
|
+
def __new__(self, content):
|
|
21
|
+
return str.__new__(self, self.transcode(content))
|
|
22
|
+
|
|
23
|
+
def __ne__(self, other):
|
|
24
|
+
return not self.__eq__(other)
|
|
25
|
+
|
|
26
|
+
def __eq__(self, other):
|
|
27
|
+
return str.__eq__(self, self.transcode(other))
|
|
28
|
+
|
|
29
|
+
def __hash__(self):
|
|
30
|
+
return str.__hash__(self)
|
|
31
|
+
|
|
32
|
+
def tobytes(self):
|
|
33
|
+
return self.encode("latin-1")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def readHex(content):
|
|
37
|
+
"""Convert a list of hex strings to binary data."""
|
|
38
|
+
return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, str)))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def deHexStr(hexdata):
|
|
42
|
+
"""Convert a hex string to binary data."""
|
|
43
|
+
hexdata = strjoin(hexdata.split())
|
|
44
|
+
if len(hexdata) % 2:
|
|
45
|
+
hexdata = hexdata + "0"
|
|
46
|
+
data = []
|
|
47
|
+
for i in range(0, len(hexdata), 2):
|
|
48
|
+
data.append(bytechr(int(hexdata[i : i + 2], 16)))
|
|
49
|
+
return bytesjoin(data)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def hexStr(data):
|
|
53
|
+
"""Convert binary data to a hex string."""
|
|
54
|
+
h = string.hexdigits
|
|
55
|
+
r = ""
|
|
56
|
+
for c in data:
|
|
57
|
+
i = byteord(c)
|
|
58
|
+
r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
|
|
59
|
+
return r
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def num2binary(l, bits=32):
|
|
63
|
+
items = []
|
|
64
|
+
binary = ""
|
|
65
|
+
for i in range(bits):
|
|
66
|
+
if l & 0x1:
|
|
67
|
+
binary = "1" + binary
|
|
68
|
+
else:
|
|
69
|
+
binary = "0" + binary
|
|
70
|
+
l = l >> 1
|
|
71
|
+
if not ((i + 1) % 8):
|
|
72
|
+
items.append(binary)
|
|
73
|
+
binary = ""
|
|
74
|
+
if binary:
|
|
75
|
+
items.append(binary)
|
|
76
|
+
items.reverse()
|
|
77
|
+
assert l in (0, -1), "number doesn't fit in number of bits"
|
|
78
|
+
return " ".join(items)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def binary2num(bin):
|
|
82
|
+
bin = strjoin(bin.split())
|
|
83
|
+
l = 0
|
|
84
|
+
for digit in bin:
|
|
85
|
+
l = l << 1
|
|
86
|
+
if digit != "0":
|
|
87
|
+
l = l | 0x1
|
|
88
|
+
return l
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def caselessSort(alist):
|
|
92
|
+
"""Return a sorted copy of a list. If there are only strings
|
|
93
|
+
in the list, it will not consider case.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
return sorted(alist, key=lambda a: (a.lower(), a))
|
|
98
|
+
except TypeError:
|
|
99
|
+
return sorted(alist)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def pad(data, size):
|
|
103
|
+
r"""Pad byte string 'data' with null bytes until its length is a
|
|
104
|
+
multiple of 'size'.
|
|
105
|
+
|
|
106
|
+
>>> len(pad(b'abcd', 4))
|
|
107
|
+
4
|
|
108
|
+
>>> len(pad(b'abcde', 2))
|
|
109
|
+
6
|
|
110
|
+
>>> len(pad(b'abcde', 4))
|
|
111
|
+
8
|
|
112
|
+
>>> pad(b'abcdef', 4) == b'abcdef\x00\x00'
|
|
113
|
+
True
|
|
114
|
+
"""
|
|
115
|
+
data = tobytes(data)
|
|
116
|
+
if size > 1:
|
|
117
|
+
remainder = len(data) % size
|
|
118
|
+
if remainder:
|
|
119
|
+
data += b"\0" * (size - remainder)
|
|
120
|
+
return data
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def tostr(s: str | bytes, encoding: str = "ascii", errors: str = "strict") -> str:
|
|
124
|
+
if not isinstance(s, str):
|
|
125
|
+
return s.decode(encoding, errors)
|
|
126
|
+
else:
|
|
127
|
+
return s
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def tobytes(s: str | bytes, encoding: str = "ascii", errors: str = "strict") -> bytes:
|
|
131
|
+
if isinstance(s, str):
|
|
132
|
+
return s.encode(encoding, errors)
|
|
133
|
+
else:
|
|
134
|
+
return bytes(s)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def bytechr(n):
|
|
138
|
+
return bytes([n])
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def byteord(c):
|
|
142
|
+
return c if isinstance(c, int) else ord(c)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def strjoin(iterable, joiner=""):
|
|
146
|
+
return tostr(joiner).join(iterable)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def bytesjoin(iterable, joiner=b""):
|
|
150
|
+
return tobytes(joiner).join(tobytes(item) for item in iterable)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if __name__ == "__main__":
|
|
154
|
+
import doctest, sys
|
|
155
|
+
|
|
156
|
+
sys.exit(doctest.testmod().failed)
|
|
@@ -0,0 +1,88 @@
|
|
|
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)
|