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,163 @@
|
|
|
1
|
+
"""Methods for traversing trees of otData-driven OpenType tables."""
|
|
2
|
+
|
|
3
|
+
from collections import deque
|
|
4
|
+
from typing import Callable, Deque, Iterable, List, Optional, Tuple
|
|
5
|
+
from .otBase import BaseTable
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"bfs_base_table",
|
|
10
|
+
"dfs_base_table",
|
|
11
|
+
"SubTablePath",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SubTablePath(Tuple[BaseTable.SubTableEntry, ...]):
|
|
16
|
+
def __str__(self) -> str:
|
|
17
|
+
path_parts = []
|
|
18
|
+
for entry in self:
|
|
19
|
+
path_part = entry.name
|
|
20
|
+
if entry.index is not None:
|
|
21
|
+
path_part += f"[{entry.index}]"
|
|
22
|
+
path_parts.append(path_part)
|
|
23
|
+
return ".".join(path_parts)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Given f(current frontier, new entries) add new entries to frontier
|
|
27
|
+
AddToFrontierFn = Callable[[Deque[SubTablePath], List[SubTablePath]], None]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def dfs_base_table(
|
|
31
|
+
root: BaseTable,
|
|
32
|
+
root_accessor: Optional[str] = None,
|
|
33
|
+
skip_root: bool = False,
|
|
34
|
+
predicate: Optional[Callable[[SubTablePath], bool]] = None,
|
|
35
|
+
iter_subtables_fn: Optional[
|
|
36
|
+
Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]
|
|
37
|
+
] = None,
|
|
38
|
+
) -> Iterable[SubTablePath]:
|
|
39
|
+
"""Depth-first search tree of BaseTables.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
root (BaseTable): the root of the tree.
|
|
43
|
+
root_accessor (Optional[str]): attribute name for the root table, if any (mostly
|
|
44
|
+
useful for debugging).
|
|
45
|
+
skip_root (Optional[bool]): if True, the root itself is not visited, only its
|
|
46
|
+
children.
|
|
47
|
+
predicate (Optional[Callable[[SubTablePath], bool]]): function to filter out
|
|
48
|
+
paths. If True, the path is yielded and its subtables are added to the
|
|
49
|
+
queue. If False, the path is skipped and its subtables are not traversed.
|
|
50
|
+
iter_subtables_fn (Optional[Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]]):
|
|
51
|
+
function to iterate over subtables of a table. If None, the default
|
|
52
|
+
BaseTable.iterSubTables() is used.
|
|
53
|
+
|
|
54
|
+
Yields:
|
|
55
|
+
SubTablePath: tuples of BaseTable.SubTableEntry(name, table, index) namedtuples
|
|
56
|
+
for each of the nodes in the tree. The last entry in a path is the current
|
|
57
|
+
subtable, whereas preceding ones refer to its parent tables all the way up to
|
|
58
|
+
the root.
|
|
59
|
+
"""
|
|
60
|
+
yield from _traverse_ot_data(
|
|
61
|
+
root,
|
|
62
|
+
root_accessor,
|
|
63
|
+
skip_root,
|
|
64
|
+
predicate,
|
|
65
|
+
lambda frontier, new: frontier.extendleft(reversed(new)),
|
|
66
|
+
iter_subtables_fn,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def bfs_base_table(
|
|
71
|
+
root: BaseTable,
|
|
72
|
+
root_accessor: Optional[str] = None,
|
|
73
|
+
skip_root: bool = False,
|
|
74
|
+
predicate: Optional[Callable[[SubTablePath], bool]] = None,
|
|
75
|
+
iter_subtables_fn: Optional[
|
|
76
|
+
Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]
|
|
77
|
+
] = None,
|
|
78
|
+
) -> Iterable[SubTablePath]:
|
|
79
|
+
"""Breadth-first search tree of BaseTables.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
root
|
|
83
|
+
the root of the tree.
|
|
84
|
+
root_accessor (Optional[str]): attribute name for the root table, if any (mostly
|
|
85
|
+
useful for debugging).
|
|
86
|
+
skip_root (Optional[bool]): if True, the root itself is not visited, only its
|
|
87
|
+
children.
|
|
88
|
+
predicate (Optional[Callable[[SubTablePath], bool]]): function to filter out
|
|
89
|
+
paths. If True, the path is yielded and its subtables are added to the
|
|
90
|
+
queue. If False, the path is skipped and its subtables are not traversed.
|
|
91
|
+
iter_subtables_fn (Optional[Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]]):
|
|
92
|
+
function to iterate over subtables of a table. If None, the default
|
|
93
|
+
BaseTable.iterSubTables() is used.
|
|
94
|
+
|
|
95
|
+
Yields:
|
|
96
|
+
SubTablePath: tuples of BaseTable.SubTableEntry(name, table, index) namedtuples
|
|
97
|
+
for each of the nodes in the tree. The last entry in a path is the current
|
|
98
|
+
subtable, whereas preceding ones refer to its parent tables all the way up to
|
|
99
|
+
the root.
|
|
100
|
+
"""
|
|
101
|
+
yield from _traverse_ot_data(
|
|
102
|
+
root,
|
|
103
|
+
root_accessor,
|
|
104
|
+
skip_root,
|
|
105
|
+
predicate,
|
|
106
|
+
lambda frontier, new: frontier.extend(new),
|
|
107
|
+
iter_subtables_fn,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _traverse_ot_data(
|
|
112
|
+
root: BaseTable,
|
|
113
|
+
root_accessor: Optional[str],
|
|
114
|
+
skip_root: bool,
|
|
115
|
+
predicate: Optional[Callable[[SubTablePath], bool]],
|
|
116
|
+
add_to_frontier_fn: AddToFrontierFn,
|
|
117
|
+
iter_subtables_fn: Optional[
|
|
118
|
+
Callable[[BaseTable], Iterable[BaseTable.SubTableEntry]]
|
|
119
|
+
] = None,
|
|
120
|
+
) -> Iterable[SubTablePath]:
|
|
121
|
+
# no visited because general otData cannot cycle (forward-offset only)
|
|
122
|
+
if root_accessor is None:
|
|
123
|
+
root_accessor = type(root).__name__
|
|
124
|
+
|
|
125
|
+
if predicate is None:
|
|
126
|
+
|
|
127
|
+
def predicate(path):
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
if iter_subtables_fn is None:
|
|
131
|
+
|
|
132
|
+
def iter_subtables_fn(table):
|
|
133
|
+
return table.iterSubTables()
|
|
134
|
+
|
|
135
|
+
frontier: Deque[SubTablePath] = deque()
|
|
136
|
+
|
|
137
|
+
root_entry = BaseTable.SubTableEntry(root_accessor, root)
|
|
138
|
+
if not skip_root:
|
|
139
|
+
frontier.append((root_entry,))
|
|
140
|
+
else:
|
|
141
|
+
add_to_frontier_fn(
|
|
142
|
+
frontier,
|
|
143
|
+
[
|
|
144
|
+
(root_entry, subtable_entry)
|
|
145
|
+
for subtable_entry in iter_subtables_fn(root)
|
|
146
|
+
],
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
while frontier:
|
|
150
|
+
# path is (value, attr_name) tuples. attr_name is attr of parent to get value
|
|
151
|
+
path = frontier.popleft()
|
|
152
|
+
current = path[-1].value
|
|
153
|
+
|
|
154
|
+
if not predicate(path):
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
yield SubTablePath(path)
|
|
158
|
+
|
|
159
|
+
new_entries = [
|
|
160
|
+
path + (subtable_entry,) for subtable_entry in iter_subtables_fn(current)
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
add_to_frontier_fn(frontier, new_entries)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from fontTools.misc import sstruct
|
|
2
|
+
from fontTools.misc.textTools import readHex, safeEval
|
|
3
|
+
import struct
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
sbixGlyphHeaderFormat = """
|
|
7
|
+
>
|
|
8
|
+
originOffsetX: h # The x-value of the point in the glyph relative to its
|
|
9
|
+
# lower-left corner which corresponds to the origin of
|
|
10
|
+
# the glyph on the screen, that is the point on the
|
|
11
|
+
# baseline at the left edge of the glyph.
|
|
12
|
+
originOffsetY: h # The y-value of the point in the glyph relative to its
|
|
13
|
+
# lower-left corner which corresponds to the origin of
|
|
14
|
+
# the glyph on the screen, that is the point on the
|
|
15
|
+
# baseline at the left edge of the glyph.
|
|
16
|
+
graphicType: 4s # e.g. "png "
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
sbixGlyphHeaderFormatSize = sstruct.calcsize(sbixGlyphHeaderFormat)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Glyph(object):
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
glyphName=None,
|
|
26
|
+
referenceGlyphName=None,
|
|
27
|
+
originOffsetX=0,
|
|
28
|
+
originOffsetY=0,
|
|
29
|
+
graphicType=None,
|
|
30
|
+
imageData=None,
|
|
31
|
+
rawdata=None,
|
|
32
|
+
gid=0,
|
|
33
|
+
):
|
|
34
|
+
self.gid = gid
|
|
35
|
+
self.glyphName = glyphName
|
|
36
|
+
self.referenceGlyphName = referenceGlyphName
|
|
37
|
+
self.originOffsetX = originOffsetX
|
|
38
|
+
self.originOffsetY = originOffsetY
|
|
39
|
+
self.rawdata = rawdata
|
|
40
|
+
self.graphicType = graphicType
|
|
41
|
+
self.imageData = imageData
|
|
42
|
+
|
|
43
|
+
# fix self.graphicType if it is null terminated or too short
|
|
44
|
+
if self.graphicType is not None:
|
|
45
|
+
if self.graphicType[-1] == "\0":
|
|
46
|
+
self.graphicType = self.graphicType[:-1]
|
|
47
|
+
if len(self.graphicType) > 4:
|
|
48
|
+
from fontTools import ttLib
|
|
49
|
+
|
|
50
|
+
raise ttLib.TTLibError(
|
|
51
|
+
"Glyph.graphicType must not be longer than 4 characters."
|
|
52
|
+
)
|
|
53
|
+
elif len(self.graphicType) < 4:
|
|
54
|
+
# pad with spaces
|
|
55
|
+
self.graphicType += " "[: (4 - len(self.graphicType))]
|
|
56
|
+
|
|
57
|
+
def is_reference_type(self):
|
|
58
|
+
"""Returns True if this glyph is a reference to another glyph's image data."""
|
|
59
|
+
return self.graphicType == "dupe" or self.graphicType == "flip"
|
|
60
|
+
|
|
61
|
+
def decompile(self, ttFont):
|
|
62
|
+
self.glyphName = ttFont.getGlyphName(self.gid)
|
|
63
|
+
if self.rawdata is None:
|
|
64
|
+
from fontTools import ttLib
|
|
65
|
+
|
|
66
|
+
raise ttLib.TTLibError("No table data to decompile")
|
|
67
|
+
if len(self.rawdata) > 0:
|
|
68
|
+
if len(self.rawdata) < sbixGlyphHeaderFormatSize:
|
|
69
|
+
from fontTools import ttLib
|
|
70
|
+
|
|
71
|
+
# print "Glyph %i header too short: Expected %x, got %x." % (self.gid, sbixGlyphHeaderFormatSize, len(self.rawdata))
|
|
72
|
+
raise ttLib.TTLibError("Glyph header too short.")
|
|
73
|
+
|
|
74
|
+
sstruct.unpack(
|
|
75
|
+
sbixGlyphHeaderFormat, self.rawdata[:sbixGlyphHeaderFormatSize], self
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if self.is_reference_type():
|
|
79
|
+
# this glyph is a reference to another glyph's image data
|
|
80
|
+
(gid,) = struct.unpack(">H", self.rawdata[sbixGlyphHeaderFormatSize:])
|
|
81
|
+
self.referenceGlyphName = ttFont.getGlyphName(gid)
|
|
82
|
+
else:
|
|
83
|
+
self.imageData = self.rawdata[sbixGlyphHeaderFormatSize:]
|
|
84
|
+
self.referenceGlyphName = None
|
|
85
|
+
# clean up
|
|
86
|
+
del self.rawdata
|
|
87
|
+
del self.gid
|
|
88
|
+
|
|
89
|
+
def compile(self, ttFont):
|
|
90
|
+
if self.glyphName is None:
|
|
91
|
+
from fontTools import ttLib
|
|
92
|
+
|
|
93
|
+
raise ttLib.TTLibError("Can't compile Glyph without glyph name")
|
|
94
|
+
# TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index?
|
|
95
|
+
# (needed if you just want to compile the sbix table on its own)
|
|
96
|
+
self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName))
|
|
97
|
+
if self.graphicType is None:
|
|
98
|
+
rawdata = b""
|
|
99
|
+
else:
|
|
100
|
+
rawdata = sstruct.pack(sbixGlyphHeaderFormat, self)
|
|
101
|
+
if self.is_reference_type():
|
|
102
|
+
rawdata += struct.pack(">H", ttFont.getGlyphID(self.referenceGlyphName))
|
|
103
|
+
else:
|
|
104
|
+
assert self.imageData is not None
|
|
105
|
+
rawdata += self.imageData
|
|
106
|
+
self.rawdata = rawdata
|
|
107
|
+
|
|
108
|
+
def toXML(self, xmlWriter, ttFont):
|
|
109
|
+
if self.graphicType is None:
|
|
110
|
+
# TODO: ignore empty glyphs?
|
|
111
|
+
# a glyph data entry is required for each glyph,
|
|
112
|
+
# but empty ones can be calculated at compile time
|
|
113
|
+
xmlWriter.simpletag("glyph", name=self.glyphName)
|
|
114
|
+
xmlWriter.newline()
|
|
115
|
+
return
|
|
116
|
+
xmlWriter.begintag(
|
|
117
|
+
"glyph",
|
|
118
|
+
graphicType=self.graphicType,
|
|
119
|
+
name=self.glyphName,
|
|
120
|
+
originOffsetX=self.originOffsetX,
|
|
121
|
+
originOffsetY=self.originOffsetY,
|
|
122
|
+
)
|
|
123
|
+
xmlWriter.newline()
|
|
124
|
+
if self.is_reference_type():
|
|
125
|
+
# this glyph is a reference to another glyph id.
|
|
126
|
+
xmlWriter.simpletag("ref", glyphname=self.referenceGlyphName)
|
|
127
|
+
else:
|
|
128
|
+
xmlWriter.begintag("hexdata")
|
|
129
|
+
xmlWriter.newline()
|
|
130
|
+
xmlWriter.dumphex(self.imageData)
|
|
131
|
+
xmlWriter.endtag("hexdata")
|
|
132
|
+
xmlWriter.newline()
|
|
133
|
+
xmlWriter.endtag("glyph")
|
|
134
|
+
xmlWriter.newline()
|
|
135
|
+
|
|
136
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
137
|
+
if name == "ref":
|
|
138
|
+
# this glyph i.e. a reference to another glyph's image data.
|
|
139
|
+
# in this case imageData contains the glyph id of the reference glyph
|
|
140
|
+
# get glyph id from glyphname
|
|
141
|
+
glyphname = safeEval("'''" + attrs["glyphname"] + "'''")
|
|
142
|
+
self.imageData = struct.pack(">H", ttFont.getGlyphID(glyphname))
|
|
143
|
+
self.referenceGlyphName = glyphname
|
|
144
|
+
elif name == "hexdata":
|
|
145
|
+
self.imageData = readHex(content)
|
|
146
|
+
else:
|
|
147
|
+
from fontTools import ttLib
|
|
148
|
+
|
|
149
|
+
raise ttLib.TTLibError("can't handle '%s' element" % name)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from fontTools.misc import sstruct
|
|
2
|
+
from fontTools.misc.textTools import safeEval
|
|
3
|
+
from .sbixGlyph import Glyph
|
|
4
|
+
import struct
|
|
5
|
+
|
|
6
|
+
sbixStrikeHeaderFormat = """
|
|
7
|
+
>
|
|
8
|
+
ppem: H # The PPEM for which this strike was designed (e.g., 9,
|
|
9
|
+
# 12, 24)
|
|
10
|
+
resolution: H # The screen resolution (in dpi) for which this strike
|
|
11
|
+
# was designed (e.g., 72)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
sbixGlyphDataOffsetFormat = """
|
|
15
|
+
>
|
|
16
|
+
glyphDataOffset: L # Offset from the beginning of the strike data record
|
|
17
|
+
# to data for the individual glyph
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
sbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat)
|
|
21
|
+
sbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Strike(object):
|
|
25
|
+
def __init__(self, rawdata=None, ppem=0, resolution=72):
|
|
26
|
+
self.data = rawdata
|
|
27
|
+
self.ppem = ppem
|
|
28
|
+
self.resolution = resolution
|
|
29
|
+
self.glyphs = {}
|
|
30
|
+
|
|
31
|
+
def decompile(self, ttFont):
|
|
32
|
+
if self.data is None:
|
|
33
|
+
from fontTools import ttLib
|
|
34
|
+
|
|
35
|
+
raise ttLib.TTLibError
|
|
36
|
+
if len(self.data) < sbixStrikeHeaderFormatSize:
|
|
37
|
+
from fontTools import ttLib
|
|
38
|
+
|
|
39
|
+
raise (
|
|
40
|
+
ttLib.TTLibError,
|
|
41
|
+
"Strike header too short: Expected %x, got %x.",
|
|
42
|
+
) % (sbixStrikeHeaderFormatSize, len(self.data))
|
|
43
|
+
|
|
44
|
+
# read Strike header from raw data
|
|
45
|
+
sstruct.unpack(
|
|
46
|
+
sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# calculate number of glyphs
|
|
50
|
+
(firstGlyphDataOffset,) = struct.unpack(
|
|
51
|
+
">L",
|
|
52
|
+
self.data[
|
|
53
|
+
sbixStrikeHeaderFormatSize : sbixStrikeHeaderFormatSize
|
|
54
|
+
+ sbixGlyphDataOffsetFormatSize
|
|
55
|
+
],
|
|
56
|
+
)
|
|
57
|
+
self.numGlyphs = (
|
|
58
|
+
firstGlyphDataOffset - sbixStrikeHeaderFormatSize
|
|
59
|
+
) // sbixGlyphDataOffsetFormatSize - 1
|
|
60
|
+
# ^ -1 because there's one more offset than glyphs
|
|
61
|
+
|
|
62
|
+
# build offset list for single glyph data offsets
|
|
63
|
+
self.glyphDataOffsets = []
|
|
64
|
+
for i in range(
|
|
65
|
+
self.numGlyphs + 1
|
|
66
|
+
): # + 1 because there's one more offset than glyphs
|
|
67
|
+
start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize
|
|
68
|
+
(current_offset,) = struct.unpack(
|
|
69
|
+
">L", self.data[start : start + sbixGlyphDataOffsetFormatSize]
|
|
70
|
+
)
|
|
71
|
+
self.glyphDataOffsets.append(current_offset)
|
|
72
|
+
|
|
73
|
+
# iterate through offset list and slice raw data into glyph data records
|
|
74
|
+
for i in range(self.numGlyphs):
|
|
75
|
+
current_glyph = Glyph(
|
|
76
|
+
rawdata=self.data[
|
|
77
|
+
self.glyphDataOffsets[i] : self.glyphDataOffsets[i + 1]
|
|
78
|
+
],
|
|
79
|
+
gid=i,
|
|
80
|
+
)
|
|
81
|
+
current_glyph.decompile(ttFont)
|
|
82
|
+
self.glyphs[current_glyph.glyphName] = current_glyph
|
|
83
|
+
del self.glyphDataOffsets
|
|
84
|
+
del self.numGlyphs
|
|
85
|
+
del self.data
|
|
86
|
+
|
|
87
|
+
def compile(self, ttFont):
|
|
88
|
+
self.glyphDataOffsets = b""
|
|
89
|
+
self.bitmapData = b""
|
|
90
|
+
|
|
91
|
+
glyphOrder = ttFont.getGlyphOrder()
|
|
92
|
+
|
|
93
|
+
# first glyph starts right after the header
|
|
94
|
+
currentGlyphDataOffset = (
|
|
95
|
+
sbixStrikeHeaderFormatSize
|
|
96
|
+
+ sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1)
|
|
97
|
+
)
|
|
98
|
+
for glyphName in glyphOrder:
|
|
99
|
+
if glyphName in self.glyphs:
|
|
100
|
+
# we have glyph data for this glyph
|
|
101
|
+
current_glyph = self.glyphs[glyphName]
|
|
102
|
+
else:
|
|
103
|
+
# must add empty glyph data record for this glyph
|
|
104
|
+
current_glyph = Glyph(glyphName=glyphName)
|
|
105
|
+
current_glyph.compile(ttFont)
|
|
106
|
+
current_glyph.glyphDataOffset = currentGlyphDataOffset
|
|
107
|
+
self.bitmapData += current_glyph.rawdata
|
|
108
|
+
currentGlyphDataOffset += len(current_glyph.rawdata)
|
|
109
|
+
self.glyphDataOffsets += sstruct.pack(
|
|
110
|
+
sbixGlyphDataOffsetFormat, current_glyph
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# add last "offset", really the end address of the last glyph data record
|
|
114
|
+
dummy = Glyph()
|
|
115
|
+
dummy.glyphDataOffset = currentGlyphDataOffset
|
|
116
|
+
self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy)
|
|
117
|
+
|
|
118
|
+
# pack header
|
|
119
|
+
self.data = sstruct.pack(sbixStrikeHeaderFormat, self)
|
|
120
|
+
# add offsets and image data after header
|
|
121
|
+
self.data += self.glyphDataOffsets + self.bitmapData
|
|
122
|
+
|
|
123
|
+
def toXML(self, xmlWriter, ttFont):
|
|
124
|
+
xmlWriter.begintag("strike")
|
|
125
|
+
xmlWriter.newline()
|
|
126
|
+
xmlWriter.simpletag("ppem", value=self.ppem)
|
|
127
|
+
xmlWriter.newline()
|
|
128
|
+
xmlWriter.simpletag("resolution", value=self.resolution)
|
|
129
|
+
xmlWriter.newline()
|
|
130
|
+
glyphOrder = ttFont.getGlyphOrder()
|
|
131
|
+
for glyphName in glyphOrder:
|
|
132
|
+
if glyphName in self.glyphs:
|
|
133
|
+
self.glyphs[glyphName].toXML(xmlWriter, ttFont)
|
|
134
|
+
# TODO: what if there are more glyph data records than (glyf table) glyphs?
|
|
135
|
+
xmlWriter.endtag("strike")
|
|
136
|
+
xmlWriter.newline()
|
|
137
|
+
|
|
138
|
+
def fromXML(self, name, attrs, content, ttFont):
|
|
139
|
+
if name in ["ppem", "resolution"]:
|
|
140
|
+
setattr(self, name, safeEval(attrs["value"]))
|
|
141
|
+
elif name == "glyph":
|
|
142
|
+
if "graphicType" in attrs:
|
|
143
|
+
myFormat = safeEval("'''" + attrs["graphicType"] + "'''")
|
|
144
|
+
else:
|
|
145
|
+
myFormat = None
|
|
146
|
+
if "glyphname" in attrs:
|
|
147
|
+
myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''")
|
|
148
|
+
elif "name" in attrs:
|
|
149
|
+
myGlyphName = safeEval("'''" + attrs["name"] + "'''")
|
|
150
|
+
else:
|
|
151
|
+
from fontTools import ttLib
|
|
152
|
+
|
|
153
|
+
raise ttLib.TTLibError("Glyph must have a glyph name.")
|
|
154
|
+
if "originOffsetX" in attrs:
|
|
155
|
+
myOffsetX = safeEval(attrs["originOffsetX"])
|
|
156
|
+
else:
|
|
157
|
+
myOffsetX = 0
|
|
158
|
+
if "originOffsetY" in attrs:
|
|
159
|
+
myOffsetY = safeEval(attrs["originOffsetY"])
|
|
160
|
+
else:
|
|
161
|
+
myOffsetY = 0
|
|
162
|
+
current_glyph = Glyph(
|
|
163
|
+
glyphName=myGlyphName,
|
|
164
|
+
graphicType=myFormat,
|
|
165
|
+
originOffsetX=myOffsetX,
|
|
166
|
+
originOffsetY=myOffsetY,
|
|
167
|
+
)
|
|
168
|
+
for element in content:
|
|
169
|
+
if isinstance(element, tuple):
|
|
170
|
+
name, attrs, content = element
|
|
171
|
+
current_glyph.fromXML(name, attrs, content, ttFont)
|
|
172
|
+
current_glyph.compile(ttFont)
|
|
173
|
+
self.glyphs[current_glyph.glyphName] = current_glyph
|
|
174
|
+
else:
|
|
175
|
+
from fontTools import ttLib
|
|
176
|
+
|
|
177
|
+
raise ttLib.TTLibError("can't handle '%s' element" % name)
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
<module 'ttLib.tables._g_l_y_f'>
|
|
55
|
+
>>> ttLib.getTableClass('glyf')
|
|
56
|
+
<class ttLib.tables._g_l_y_f.table__g_l_y_f at 645f400>
|
|
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
|
+
|