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,158 @@
|
|
|
1
|
+
"""Generic visitor pattern implementation for Python objects."""
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
import weakref
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Visitor(object):
|
|
8
|
+
defaultStop = False
|
|
9
|
+
|
|
10
|
+
_visitors = {
|
|
11
|
+
# By default we skip visiting weak references to avoid recursion
|
|
12
|
+
# issues. Users can override this by registering a visit
|
|
13
|
+
# function for weakref.ProxyType.
|
|
14
|
+
weakref.ProxyType: {None: lambda self, obj, *args, **kwargs: False}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def _register(celf, clazzes_attrs):
|
|
19
|
+
assert celf != Visitor, "Subclass Visitor instead."
|
|
20
|
+
if "_visitors" not in celf.__dict__:
|
|
21
|
+
celf._visitors = {}
|
|
22
|
+
|
|
23
|
+
def wrapper(method):
|
|
24
|
+
assert method.__name__ == "visit"
|
|
25
|
+
for clazzes, attrs in clazzes_attrs:
|
|
26
|
+
if type(clazzes) != tuple:
|
|
27
|
+
clazzes = (clazzes,)
|
|
28
|
+
if type(attrs) == str:
|
|
29
|
+
attrs = (attrs,)
|
|
30
|
+
for clazz in clazzes:
|
|
31
|
+
_visitors = celf._visitors.setdefault(clazz, {})
|
|
32
|
+
for attr in attrs:
|
|
33
|
+
assert attr not in _visitors, (
|
|
34
|
+
"Oops, class '%s' has visitor function for '%s' defined already."
|
|
35
|
+
% (clazz.__name__, attr)
|
|
36
|
+
)
|
|
37
|
+
_visitors[attr] = method
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
return wrapper
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def register(celf, clazzes):
|
|
44
|
+
if type(clazzes) != tuple:
|
|
45
|
+
clazzes = (clazzes,)
|
|
46
|
+
return celf._register([(clazzes, (None,))])
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def register_attr(celf, clazzes, attrs):
|
|
50
|
+
clazzes_attrs = []
|
|
51
|
+
if type(clazzes) != tuple:
|
|
52
|
+
clazzes = (clazzes,)
|
|
53
|
+
if type(attrs) == str:
|
|
54
|
+
attrs = (attrs,)
|
|
55
|
+
for clazz in clazzes:
|
|
56
|
+
clazzes_attrs.append((clazz, attrs))
|
|
57
|
+
return celf._register(clazzes_attrs)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def register_attrs(celf, clazzes_attrs):
|
|
61
|
+
return celf._register(clazzes_attrs)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def _visitorsFor(celf, thing, _default={}):
|
|
65
|
+
typ = type(thing)
|
|
66
|
+
|
|
67
|
+
for celf in celf.mro():
|
|
68
|
+
_visitors = getattr(celf, "_visitors", None)
|
|
69
|
+
if _visitors is None:
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
for base in typ.mro():
|
|
73
|
+
m = celf._visitors.get(base, None)
|
|
74
|
+
if m is not None:
|
|
75
|
+
return m
|
|
76
|
+
|
|
77
|
+
return _default
|
|
78
|
+
|
|
79
|
+
def visitObject(self, obj, *args, **kwargs):
|
|
80
|
+
"""Called to visit an object. This function loops over all non-private
|
|
81
|
+
attributes of the objects and calls any user-registered (via
|
|
82
|
+
``@register_attr()`` or ``@register_attrs()``) ``visit()`` functions.
|
|
83
|
+
|
|
84
|
+
The visitor will proceed to call ``self.visitAttr()``, unless there is a
|
|
85
|
+
user-registered visit function and:
|
|
86
|
+
|
|
87
|
+
* It returns ``False``; or
|
|
88
|
+
* It returns ``None`` (or doesn't return anything) and
|
|
89
|
+
``visitor.defaultStop`` is ``True`` (non-default).
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
keys = sorted(vars(obj).keys())
|
|
93
|
+
_visitors = self._visitorsFor(obj)
|
|
94
|
+
defaultVisitor = _visitors.get("*", None)
|
|
95
|
+
for key in keys:
|
|
96
|
+
if key[0] == "_":
|
|
97
|
+
continue
|
|
98
|
+
value = getattr(obj, key)
|
|
99
|
+
visitorFunc = _visitors.get(key, defaultVisitor)
|
|
100
|
+
if visitorFunc is not None:
|
|
101
|
+
ret = visitorFunc(self, obj, key, value, *args, **kwargs)
|
|
102
|
+
if ret == False or (ret is None and self.defaultStop):
|
|
103
|
+
continue
|
|
104
|
+
self.visitAttr(obj, key, value, *args, **kwargs)
|
|
105
|
+
|
|
106
|
+
def visitAttr(self, obj, attr, value, *args, **kwargs):
|
|
107
|
+
"""Called to visit an attribute of an object."""
|
|
108
|
+
self.visit(value, *args, **kwargs)
|
|
109
|
+
|
|
110
|
+
def visitList(self, obj, *args, **kwargs):
|
|
111
|
+
"""Called to visit any value that is a list."""
|
|
112
|
+
for value in obj:
|
|
113
|
+
self.visit(value, *args, **kwargs)
|
|
114
|
+
|
|
115
|
+
def visitDict(self, obj, *args, **kwargs):
|
|
116
|
+
"""Called to visit any value that is a dictionary."""
|
|
117
|
+
for value in obj.values():
|
|
118
|
+
self.visit(value, *args, **kwargs)
|
|
119
|
+
|
|
120
|
+
def visitLeaf(self, obj, *args, **kwargs):
|
|
121
|
+
"""Called to visit any value that is not an object, list,
|
|
122
|
+
or dictionary."""
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
def visit(self, obj, *args, **kwargs):
|
|
126
|
+
"""This is the main entry to the visitor. The visitor will visit object
|
|
127
|
+
``obj``.
|
|
128
|
+
|
|
129
|
+
The visitor will first determine if there is a registered (via
|
|
130
|
+
``@register()``) visit function for the type of object. If there is, it
|
|
131
|
+
will be called, and ``(visitor, obj, *args, **kwargs)`` will be passed
|
|
132
|
+
to the user visit function.
|
|
133
|
+
|
|
134
|
+
The visitor will not recurse if there is a user-registered visit
|
|
135
|
+
function and:
|
|
136
|
+
|
|
137
|
+
* It returns ``False``; or
|
|
138
|
+
* It returns ``None`` (or doesn't return anything) and
|
|
139
|
+
``visitor.defaultStop`` is ``True`` (non-default)
|
|
140
|
+
|
|
141
|
+
Otherwise, the visitor will proceed to dispatch to one of
|
|
142
|
+
``self.visitObject()``, ``self.visitList()``, ``self.visitDict()``, or
|
|
143
|
+
``self.visitLeaf()`` (any of which can be overriden in a subclass).
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
visitorFunc = self._visitorsFor(obj).get(None, None)
|
|
147
|
+
if visitorFunc is not None:
|
|
148
|
+
ret = visitorFunc(self, obj, *args, **kwargs)
|
|
149
|
+
if ret == False or (ret is None and self.defaultStop):
|
|
150
|
+
return
|
|
151
|
+
if hasattr(obj, "__dict__") and not isinstance(obj, enum.Enum):
|
|
152
|
+
self.visitObject(obj, *args, **kwargs)
|
|
153
|
+
elif isinstance(obj, list):
|
|
154
|
+
self.visitList(obj, *args, **kwargs)
|
|
155
|
+
elif isinstance(obj, dict):
|
|
156
|
+
self.visitDict(obj, *args, **kwargs)
|
|
157
|
+
else:
|
|
158
|
+
self.visitLeaf(obj, *args, **kwargs)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
from fontTools import ttLib
|
|
2
|
+
from fontTools.misc.textTools import safeEval
|
|
3
|
+
from fontTools.ttLib.tables.DefaultTable import DefaultTable
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
log = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TTXParseError(Exception):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
BUFSIZE = 0x4000
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class XMLReader(object):
|
|
20
|
+
def __init__(
|
|
21
|
+
self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False
|
|
22
|
+
):
|
|
23
|
+
if fileOrPath == "-":
|
|
24
|
+
fileOrPath = sys.stdin
|
|
25
|
+
if not hasattr(fileOrPath, "read"):
|
|
26
|
+
self.file = open(fileOrPath, "rb")
|
|
27
|
+
self._closeStream = True
|
|
28
|
+
else:
|
|
29
|
+
# assume readable file object
|
|
30
|
+
self.file = fileOrPath
|
|
31
|
+
self._closeStream = False
|
|
32
|
+
self.ttFont = ttFont
|
|
33
|
+
self.progress = progress
|
|
34
|
+
if quiet is not None:
|
|
35
|
+
from fontTools.misc.loggingTools import deprecateArgument
|
|
36
|
+
|
|
37
|
+
deprecateArgument("quiet", "configure logging instead")
|
|
38
|
+
self.quiet = quiet
|
|
39
|
+
self.root = None
|
|
40
|
+
self.contentStack = []
|
|
41
|
+
self.contentOnly = contentOnly
|
|
42
|
+
self.stackSize = 0
|
|
43
|
+
|
|
44
|
+
def read(self, rootless=False):
|
|
45
|
+
if rootless:
|
|
46
|
+
self.stackSize += 1
|
|
47
|
+
if self.progress:
|
|
48
|
+
self.file.seek(0, 2)
|
|
49
|
+
fileSize = self.file.tell()
|
|
50
|
+
self.progress.set(0, fileSize // 100 or 1)
|
|
51
|
+
self.file.seek(0)
|
|
52
|
+
self._parseFile(self.file)
|
|
53
|
+
if self._closeStream:
|
|
54
|
+
self.close()
|
|
55
|
+
if rootless:
|
|
56
|
+
self.stackSize -= 1
|
|
57
|
+
|
|
58
|
+
def close(self):
|
|
59
|
+
self.file.close()
|
|
60
|
+
|
|
61
|
+
def _parseFile(self, file):
|
|
62
|
+
from xml.parsers.expat import ParserCreate
|
|
63
|
+
|
|
64
|
+
parser = ParserCreate()
|
|
65
|
+
parser.StartElementHandler = self._startElementHandler
|
|
66
|
+
parser.EndElementHandler = self._endElementHandler
|
|
67
|
+
parser.CharacterDataHandler = self._characterDataHandler
|
|
68
|
+
|
|
69
|
+
pos = 0
|
|
70
|
+
while True:
|
|
71
|
+
chunk = file.read(BUFSIZE)
|
|
72
|
+
if not chunk:
|
|
73
|
+
parser.Parse(chunk, 1)
|
|
74
|
+
break
|
|
75
|
+
pos = pos + len(chunk)
|
|
76
|
+
if self.progress:
|
|
77
|
+
self.progress.set(pos // 100)
|
|
78
|
+
parser.Parse(chunk, 0)
|
|
79
|
+
|
|
80
|
+
def _startElementHandler(self, name, attrs):
|
|
81
|
+
if self.stackSize == 1 and self.contentOnly:
|
|
82
|
+
# We already know the table we're parsing, skip
|
|
83
|
+
# parsing the table tag and continue to
|
|
84
|
+
# stack '2' which begins parsing content
|
|
85
|
+
self.contentStack.append([])
|
|
86
|
+
self.stackSize = 2
|
|
87
|
+
return
|
|
88
|
+
stackSize = self.stackSize
|
|
89
|
+
self.stackSize = stackSize + 1
|
|
90
|
+
subFile = attrs.get("src")
|
|
91
|
+
if subFile is not None:
|
|
92
|
+
if hasattr(self.file, "name"):
|
|
93
|
+
# if file has a name, get its parent directory
|
|
94
|
+
dirname = os.path.dirname(self.file.name)
|
|
95
|
+
else:
|
|
96
|
+
# else fall back to using the current working directory
|
|
97
|
+
dirname = os.getcwd()
|
|
98
|
+
subFile = os.path.join(dirname, subFile)
|
|
99
|
+
if not stackSize:
|
|
100
|
+
if name != "ttFont":
|
|
101
|
+
raise TTXParseError("illegal root tag: %s" % name)
|
|
102
|
+
if self.ttFont.reader is None and not self.ttFont.tables:
|
|
103
|
+
sfntVersion = attrs.get("sfntVersion")
|
|
104
|
+
if sfntVersion is not None:
|
|
105
|
+
if len(sfntVersion) != 4:
|
|
106
|
+
sfntVersion = safeEval('"' + sfntVersion + '"')
|
|
107
|
+
self.ttFont.sfntVersion = sfntVersion
|
|
108
|
+
self.contentStack.append([])
|
|
109
|
+
elif stackSize == 1:
|
|
110
|
+
if subFile is not None:
|
|
111
|
+
subReader = XMLReader(subFile, self.ttFont, self.progress)
|
|
112
|
+
subReader.read()
|
|
113
|
+
self.contentStack.append([])
|
|
114
|
+
return
|
|
115
|
+
tag = ttLib.xmlToTag(name)
|
|
116
|
+
msg = "Parsing '%s' table..." % tag
|
|
117
|
+
if self.progress:
|
|
118
|
+
self.progress.setLabel(msg)
|
|
119
|
+
log.info(msg)
|
|
120
|
+
if tag == "GlyphOrder":
|
|
121
|
+
tableClass = ttLib.GlyphOrder
|
|
122
|
+
elif "ERROR" in attrs or ("raw" in attrs and safeEval(attrs["raw"])):
|
|
123
|
+
tableClass = DefaultTable
|
|
124
|
+
else:
|
|
125
|
+
tableClass = ttLib.getTableClass(tag)
|
|
126
|
+
if tableClass is None:
|
|
127
|
+
tableClass = DefaultTable
|
|
128
|
+
if tag == "loca" and tag in self.ttFont:
|
|
129
|
+
# Special-case the 'loca' table as we need the
|
|
130
|
+
# original if the 'glyf' table isn't recompiled.
|
|
131
|
+
self.currentTable = self.ttFont[tag]
|
|
132
|
+
else:
|
|
133
|
+
self.currentTable = tableClass(tag)
|
|
134
|
+
self.ttFont[tag] = self.currentTable
|
|
135
|
+
self.contentStack.append([])
|
|
136
|
+
elif stackSize == 2 and subFile is not None:
|
|
137
|
+
subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True)
|
|
138
|
+
subReader.read()
|
|
139
|
+
self.contentStack.append([])
|
|
140
|
+
self.root = subReader.root
|
|
141
|
+
elif stackSize == 2:
|
|
142
|
+
self.contentStack.append([])
|
|
143
|
+
self.root = (name, attrs, self.contentStack[-1])
|
|
144
|
+
else:
|
|
145
|
+
l = []
|
|
146
|
+
self.contentStack[-1].append((name, attrs, l))
|
|
147
|
+
self.contentStack.append(l)
|
|
148
|
+
|
|
149
|
+
def _characterDataHandler(self, data):
|
|
150
|
+
if self.stackSize > 1:
|
|
151
|
+
# parser parses in chunks, so we may get multiple calls
|
|
152
|
+
# for the same text node; thus we need to append the data
|
|
153
|
+
# to the last item in the content stack:
|
|
154
|
+
# https://github.com/fonttools/fonttools/issues/2614
|
|
155
|
+
if (
|
|
156
|
+
data != "\n"
|
|
157
|
+
and self.contentStack[-1]
|
|
158
|
+
and isinstance(self.contentStack[-1][-1], str)
|
|
159
|
+
and self.contentStack[-1][-1] != "\n"
|
|
160
|
+
):
|
|
161
|
+
self.contentStack[-1][-1] += data
|
|
162
|
+
else:
|
|
163
|
+
self.contentStack[-1].append(data)
|
|
164
|
+
|
|
165
|
+
def _endElementHandler(self, name):
|
|
166
|
+
self.stackSize = self.stackSize - 1
|
|
167
|
+
del self.contentStack[-1]
|
|
168
|
+
if not self.contentOnly:
|
|
169
|
+
if self.stackSize == 1:
|
|
170
|
+
self.root = None
|
|
171
|
+
elif self.stackSize == 2:
|
|
172
|
+
name, attrs, content = self.root
|
|
173
|
+
self.currentTable.fromXML(name, attrs, content, self.ttFont)
|
|
174
|
+
self.root = None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class ProgressPrinter(object):
|
|
178
|
+
def __init__(self, title, maxval=100):
|
|
179
|
+
print(title)
|
|
180
|
+
|
|
181
|
+
def set(self, val, maxval=None):
|
|
182
|
+
pass
|
|
183
|
+
|
|
184
|
+
def increment(self, val=1):
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
def setLabel(self, text):
|
|
188
|
+
print(text)
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""xmlWriter.py -- Simple XML authoring class"""
|
|
2
|
+
|
|
3
|
+
from fontTools.misc.textTools import byteord, strjoin, tobytes, tostr
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
import string
|
|
7
|
+
import logging
|
|
8
|
+
import itertools
|
|
9
|
+
|
|
10
|
+
INDENT = " "
|
|
11
|
+
TTX_LOG = logging.getLogger("fontTools.ttx")
|
|
12
|
+
REPLACEMENT = "?"
|
|
13
|
+
ILLEGAL_XML_CHARS = dict.fromkeys(
|
|
14
|
+
itertools.chain(
|
|
15
|
+
range(0x00, 0x09),
|
|
16
|
+
(0x0B, 0x0C),
|
|
17
|
+
range(0x0E, 0x20),
|
|
18
|
+
range(0xD800, 0xE000),
|
|
19
|
+
(0xFFFE, 0xFFFF),
|
|
20
|
+
),
|
|
21
|
+
REPLACEMENT,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class XMLWriter(object):
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
fileOrPath,
|
|
29
|
+
indentwhite=INDENT,
|
|
30
|
+
idlefunc=None,
|
|
31
|
+
encoding="utf_8",
|
|
32
|
+
newlinestr="\n",
|
|
33
|
+
):
|
|
34
|
+
if encoding.lower().replace("-", "").replace("_", "") != "utf8":
|
|
35
|
+
raise Exception("Only UTF-8 encoding is supported.")
|
|
36
|
+
if fileOrPath == "-":
|
|
37
|
+
fileOrPath = sys.stdout
|
|
38
|
+
if not hasattr(fileOrPath, "write"):
|
|
39
|
+
self.filename = fileOrPath
|
|
40
|
+
self.file = open(fileOrPath, "wb")
|
|
41
|
+
self._closeStream = True
|
|
42
|
+
else:
|
|
43
|
+
self.filename = None
|
|
44
|
+
# assume writable file object
|
|
45
|
+
self.file = fileOrPath
|
|
46
|
+
self._closeStream = False
|
|
47
|
+
|
|
48
|
+
# Figure out if writer expects bytes or unicodes
|
|
49
|
+
try:
|
|
50
|
+
# The bytes check should be first. See:
|
|
51
|
+
# https://github.com/fonttools/fonttools/pull/233
|
|
52
|
+
self.file.write(b"")
|
|
53
|
+
self.totype = tobytes
|
|
54
|
+
except TypeError:
|
|
55
|
+
# This better not fail.
|
|
56
|
+
self.file.write("")
|
|
57
|
+
self.totype = tostr
|
|
58
|
+
self.indentwhite = self.totype(indentwhite)
|
|
59
|
+
if newlinestr is None:
|
|
60
|
+
self.newlinestr = self.totype(os.linesep)
|
|
61
|
+
else:
|
|
62
|
+
self.newlinestr = self.totype(newlinestr)
|
|
63
|
+
self.indentlevel = 0
|
|
64
|
+
self.stack = []
|
|
65
|
+
self.needindent = 1
|
|
66
|
+
self.idlefunc = idlefunc
|
|
67
|
+
self.idlecounter = 0
|
|
68
|
+
self._writeraw('<?xml version="1.0" encoding="UTF-8"?>')
|
|
69
|
+
self.newline()
|
|
70
|
+
|
|
71
|
+
def __enter__(self):
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
def __exit__(self, exception_type, exception_value, traceback):
|
|
75
|
+
self.close()
|
|
76
|
+
|
|
77
|
+
def close(self):
|
|
78
|
+
if self._closeStream:
|
|
79
|
+
self.file.close()
|
|
80
|
+
|
|
81
|
+
def write(self, string, indent=True):
|
|
82
|
+
"""Writes text."""
|
|
83
|
+
self._writeraw(escape(string), indent=indent)
|
|
84
|
+
|
|
85
|
+
def writecdata(self, string):
|
|
86
|
+
"""Writes text in a CDATA section."""
|
|
87
|
+
self._writeraw("<![CDATA[" + string + "]]>")
|
|
88
|
+
|
|
89
|
+
def write8bit(self, data, strip=False):
|
|
90
|
+
"""Writes a bytes() sequence into the XML, escaping
|
|
91
|
+
non-ASCII bytes. When this is read in xmlReader,
|
|
92
|
+
the original bytes can be recovered by encoding to
|
|
93
|
+
'latin-1'."""
|
|
94
|
+
self._writeraw(escape8bit(data), strip=strip)
|
|
95
|
+
|
|
96
|
+
def write_noindent(self, string):
|
|
97
|
+
"""Writes text without indentation."""
|
|
98
|
+
self._writeraw(escape(string), indent=False)
|
|
99
|
+
|
|
100
|
+
def _writeraw(self, data, indent=True, strip=False):
|
|
101
|
+
"""Writes bytes, possibly indented."""
|
|
102
|
+
if indent and self.needindent:
|
|
103
|
+
self.file.write(self.indentlevel * self.indentwhite)
|
|
104
|
+
self.needindent = 0
|
|
105
|
+
s = self.totype(data, encoding="utf_8")
|
|
106
|
+
if strip:
|
|
107
|
+
s = s.strip()
|
|
108
|
+
self.file.write(s)
|
|
109
|
+
|
|
110
|
+
def newline(self):
|
|
111
|
+
self.file.write(self.newlinestr)
|
|
112
|
+
self.needindent = 1
|
|
113
|
+
idlecounter = self.idlecounter
|
|
114
|
+
if not idlecounter % 100 and self.idlefunc is not None:
|
|
115
|
+
self.idlefunc()
|
|
116
|
+
self.idlecounter = idlecounter + 1
|
|
117
|
+
|
|
118
|
+
def comment(self, data):
|
|
119
|
+
data = escape(data)
|
|
120
|
+
lines = data.split("\n")
|
|
121
|
+
self._writeraw("<!-- " + lines[0])
|
|
122
|
+
for line in lines[1:]:
|
|
123
|
+
self.newline()
|
|
124
|
+
self._writeraw(" " + line)
|
|
125
|
+
self._writeraw(" -->")
|
|
126
|
+
|
|
127
|
+
def simpletag(self, _TAG_, *args, **kwargs):
|
|
128
|
+
attrdata = self.stringifyattrs(*args, **kwargs)
|
|
129
|
+
data = "<%s%s/>" % (_TAG_, attrdata)
|
|
130
|
+
self._writeraw(data)
|
|
131
|
+
|
|
132
|
+
def begintag(self, _TAG_, *args, **kwargs):
|
|
133
|
+
attrdata = self.stringifyattrs(*args, **kwargs)
|
|
134
|
+
data = "<%s%s>" % (_TAG_, attrdata)
|
|
135
|
+
self._writeraw(data)
|
|
136
|
+
self.stack.append(_TAG_)
|
|
137
|
+
self.indent()
|
|
138
|
+
|
|
139
|
+
def endtag(self, _TAG_):
|
|
140
|
+
assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
|
|
141
|
+
del self.stack[-1]
|
|
142
|
+
self.dedent()
|
|
143
|
+
data = "</%s>" % _TAG_
|
|
144
|
+
self._writeraw(data)
|
|
145
|
+
|
|
146
|
+
def dumphex(self, data):
|
|
147
|
+
linelength = 16
|
|
148
|
+
hexlinelength = linelength * 2
|
|
149
|
+
chunksize = 8
|
|
150
|
+
for i in range(0, len(data), linelength):
|
|
151
|
+
hexline = hexStr(data[i : i + linelength])
|
|
152
|
+
line = ""
|
|
153
|
+
white = ""
|
|
154
|
+
for j in range(0, hexlinelength, chunksize):
|
|
155
|
+
line = line + white + hexline[j : j + chunksize]
|
|
156
|
+
white = " "
|
|
157
|
+
self._writeraw(line)
|
|
158
|
+
self.newline()
|
|
159
|
+
|
|
160
|
+
def indent(self):
|
|
161
|
+
self.indentlevel = self.indentlevel + 1
|
|
162
|
+
|
|
163
|
+
def dedent(self):
|
|
164
|
+
assert self.indentlevel > 0
|
|
165
|
+
self.indentlevel = self.indentlevel - 1
|
|
166
|
+
|
|
167
|
+
def stringifyattrs(self, *args, **kwargs):
|
|
168
|
+
if kwargs:
|
|
169
|
+
assert not args
|
|
170
|
+
attributes = sorted(kwargs.items())
|
|
171
|
+
elif args:
|
|
172
|
+
assert len(args) == 1
|
|
173
|
+
attributes = args[0]
|
|
174
|
+
else:
|
|
175
|
+
return ""
|
|
176
|
+
data = ""
|
|
177
|
+
for attr, value in attributes:
|
|
178
|
+
if not isinstance(value, (bytes, str)):
|
|
179
|
+
value = str(value)
|
|
180
|
+
data = data + ' %s="%s"' % (attr, escapeattr(value))
|
|
181
|
+
return data
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def escape(data):
|
|
185
|
+
"""Escape characters not allowed in `XML 1.0 <https://www.w3.org/TR/xml/#NT-Char>`_."""
|
|
186
|
+
data = tostr(data, "utf_8")
|
|
187
|
+
data = data.replace("&", "&")
|
|
188
|
+
data = data.replace("<", "<")
|
|
189
|
+
data = data.replace(">", ">")
|
|
190
|
+
data = data.replace("\r", " ")
|
|
191
|
+
|
|
192
|
+
newData = data.translate(ILLEGAL_XML_CHARS)
|
|
193
|
+
if newData != data:
|
|
194
|
+
maxLen = 10
|
|
195
|
+
preview = repr(data)
|
|
196
|
+
if len(data) > maxLen:
|
|
197
|
+
preview = repr(data[:maxLen])[1:-1] + "..."
|
|
198
|
+
TTX_LOG.warning(
|
|
199
|
+
"Illegal XML character(s) found; replacing offending " "string %r with %r",
|
|
200
|
+
preview,
|
|
201
|
+
REPLACEMENT,
|
|
202
|
+
)
|
|
203
|
+
return newData
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def escapeattr(data):
|
|
207
|
+
data = escape(data)
|
|
208
|
+
data = data.replace('"', """)
|
|
209
|
+
return data
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def escape8bit(data):
|
|
213
|
+
"""Input is Unicode string."""
|
|
214
|
+
|
|
215
|
+
def escapechar(c):
|
|
216
|
+
n = ord(c)
|
|
217
|
+
if 32 <= n <= 127 and c not in "<&>":
|
|
218
|
+
return c
|
|
219
|
+
else:
|
|
220
|
+
return "&#" + repr(n) + ";"
|
|
221
|
+
|
|
222
|
+
return strjoin(map(escapechar, data.decode("latin-1")))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def hexStr(s):
|
|
226
|
+
h = string.hexdigits
|
|
227
|
+
r = ""
|
|
228
|
+
for c in s:
|
|
229
|
+
i = byteord(c)
|
|
230
|
+
r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
|
|
231
|
+
return r
|