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.
Files changed (353) hide show
  1. fontTools/__init__.py +8 -0
  2. fontTools/__main__.py +35 -0
  3. fontTools/afmLib.py +439 -0
  4. fontTools/agl.py +5233 -0
  5. fontTools/annotations.py +30 -0
  6. fontTools/cffLib/CFF2ToCFF.py +258 -0
  7. fontTools/cffLib/CFFToCFF2.py +305 -0
  8. fontTools/cffLib/__init__.py +3694 -0
  9. fontTools/cffLib/specializer.py +927 -0
  10. fontTools/cffLib/transforms.py +495 -0
  11. fontTools/cffLib/width.py +210 -0
  12. fontTools/colorLib/__init__.py +0 -0
  13. fontTools/colorLib/builder.py +664 -0
  14. fontTools/colorLib/errors.py +2 -0
  15. fontTools/colorLib/geometry.py +143 -0
  16. fontTools/colorLib/table_builder.py +223 -0
  17. fontTools/colorLib/unbuilder.py +81 -0
  18. fontTools/config/__init__.py +90 -0
  19. fontTools/cu2qu/__init__.py +15 -0
  20. fontTools/cu2qu/__main__.py +6 -0
  21. fontTools/cu2qu/benchmark.py +54 -0
  22. fontTools/cu2qu/cli.py +198 -0
  23. fontTools/cu2qu/cu2qu.c +15817 -0
  24. fontTools/cu2qu/cu2qu.cp311-win32.pyd +0 -0
  25. fontTools/cu2qu/cu2qu.py +563 -0
  26. fontTools/cu2qu/errors.py +77 -0
  27. fontTools/cu2qu/ufo.py +363 -0
  28. fontTools/designspaceLib/__init__.py +3343 -0
  29. fontTools/designspaceLib/__main__.py +6 -0
  30. fontTools/designspaceLib/split.py +475 -0
  31. fontTools/designspaceLib/statNames.py +260 -0
  32. fontTools/designspaceLib/types.py +147 -0
  33. fontTools/encodings/MacRoman.py +258 -0
  34. fontTools/encodings/StandardEncoding.py +258 -0
  35. fontTools/encodings/__init__.py +1 -0
  36. fontTools/encodings/codecs.py +135 -0
  37. fontTools/feaLib/__init__.py +4 -0
  38. fontTools/feaLib/__main__.py +78 -0
  39. fontTools/feaLib/ast.py +2143 -0
  40. fontTools/feaLib/builder.py +1814 -0
  41. fontTools/feaLib/error.py +22 -0
  42. fontTools/feaLib/lexer.c +17029 -0
  43. fontTools/feaLib/lexer.cp311-win32.pyd +0 -0
  44. fontTools/feaLib/lexer.py +287 -0
  45. fontTools/feaLib/location.py +12 -0
  46. fontTools/feaLib/lookupDebugInfo.py +12 -0
  47. fontTools/feaLib/parser.py +2394 -0
  48. fontTools/feaLib/variableScalar.py +118 -0
  49. fontTools/fontBuilder.py +1014 -0
  50. fontTools/help.py +36 -0
  51. fontTools/merge/__init__.py +248 -0
  52. fontTools/merge/__main__.py +6 -0
  53. fontTools/merge/base.py +81 -0
  54. fontTools/merge/cmap.py +173 -0
  55. fontTools/merge/layout.py +526 -0
  56. fontTools/merge/options.py +85 -0
  57. fontTools/merge/tables.py +352 -0
  58. fontTools/merge/unicode.py +78 -0
  59. fontTools/merge/util.py +143 -0
  60. fontTools/misc/__init__.py +1 -0
  61. fontTools/misc/arrayTools.py +424 -0
  62. fontTools/misc/bezierTools.c +39731 -0
  63. fontTools/misc/bezierTools.cp311-win32.pyd +0 -0
  64. fontTools/misc/bezierTools.py +1500 -0
  65. fontTools/misc/classifyTools.py +170 -0
  66. fontTools/misc/cliTools.py +53 -0
  67. fontTools/misc/configTools.py +349 -0
  68. fontTools/misc/cython.py +27 -0
  69. fontTools/misc/dictTools.py +83 -0
  70. fontTools/misc/eexec.py +119 -0
  71. fontTools/misc/encodingTools.py +72 -0
  72. fontTools/misc/enumTools.py +23 -0
  73. fontTools/misc/etree.py +456 -0
  74. fontTools/misc/filenames.py +245 -0
  75. fontTools/misc/filesystem/__init__.py +68 -0
  76. fontTools/misc/filesystem/_base.py +134 -0
  77. fontTools/misc/filesystem/_copy.py +45 -0
  78. fontTools/misc/filesystem/_errors.py +54 -0
  79. fontTools/misc/filesystem/_info.py +75 -0
  80. fontTools/misc/filesystem/_osfs.py +164 -0
  81. fontTools/misc/filesystem/_path.py +67 -0
  82. fontTools/misc/filesystem/_subfs.py +92 -0
  83. fontTools/misc/filesystem/_tempfs.py +34 -0
  84. fontTools/misc/filesystem/_tools.py +34 -0
  85. fontTools/misc/filesystem/_walk.py +55 -0
  86. fontTools/misc/filesystem/_zipfs.py +204 -0
  87. fontTools/misc/fixedTools.py +253 -0
  88. fontTools/misc/intTools.py +25 -0
  89. fontTools/misc/iterTools.py +12 -0
  90. fontTools/misc/lazyTools.py +42 -0
  91. fontTools/misc/loggingTools.py +543 -0
  92. fontTools/misc/macCreatorType.py +56 -0
  93. fontTools/misc/macRes.py +261 -0
  94. fontTools/misc/plistlib/__init__.py +681 -0
  95. fontTools/misc/plistlib/py.typed +0 -0
  96. fontTools/misc/psCharStrings.py +1511 -0
  97. fontTools/misc/psLib.py +398 -0
  98. fontTools/misc/psOperators.py +572 -0
  99. fontTools/misc/py23.py +96 -0
  100. fontTools/misc/roundTools.py +110 -0
  101. fontTools/misc/sstruct.py +227 -0
  102. fontTools/misc/symfont.py +242 -0
  103. fontTools/misc/testTools.py +233 -0
  104. fontTools/misc/textTools.py +156 -0
  105. fontTools/misc/timeTools.py +88 -0
  106. fontTools/misc/transform.py +516 -0
  107. fontTools/misc/treeTools.py +45 -0
  108. fontTools/misc/vector.py +147 -0
  109. fontTools/misc/visitor.py +158 -0
  110. fontTools/misc/xmlReader.py +188 -0
  111. fontTools/misc/xmlWriter.py +231 -0
  112. fontTools/mtiLib/__init__.py +1400 -0
  113. fontTools/mtiLib/__main__.py +5 -0
  114. fontTools/otlLib/__init__.py +1 -0
  115. fontTools/otlLib/builder.py +3465 -0
  116. fontTools/otlLib/error.py +11 -0
  117. fontTools/otlLib/maxContextCalc.py +96 -0
  118. fontTools/otlLib/optimize/__init__.py +53 -0
  119. fontTools/otlLib/optimize/__main__.py +6 -0
  120. fontTools/otlLib/optimize/gpos.py +439 -0
  121. fontTools/pens/__init__.py +1 -0
  122. fontTools/pens/areaPen.py +52 -0
  123. fontTools/pens/basePen.py +475 -0
  124. fontTools/pens/boundsPen.py +98 -0
  125. fontTools/pens/cairoPen.py +26 -0
  126. fontTools/pens/cocoaPen.py +26 -0
  127. fontTools/pens/cu2quPen.py +325 -0
  128. fontTools/pens/explicitClosingLinePen.py +101 -0
  129. fontTools/pens/filterPen.py +433 -0
  130. fontTools/pens/freetypePen.py +462 -0
  131. fontTools/pens/hashPointPen.py +89 -0
  132. fontTools/pens/momentsPen.c +13378 -0
  133. fontTools/pens/momentsPen.cp311-win32.pyd +0 -0
  134. fontTools/pens/momentsPen.py +879 -0
  135. fontTools/pens/perimeterPen.py +69 -0
  136. fontTools/pens/pointInsidePen.py +192 -0
  137. fontTools/pens/pointPen.py +643 -0
  138. fontTools/pens/qtPen.py +29 -0
  139. fontTools/pens/qu2cuPen.py +105 -0
  140. fontTools/pens/quartzPen.py +43 -0
  141. fontTools/pens/recordingPen.py +335 -0
  142. fontTools/pens/reportLabPen.py +79 -0
  143. fontTools/pens/reverseContourPen.py +96 -0
  144. fontTools/pens/roundingPen.py +130 -0
  145. fontTools/pens/statisticsPen.py +312 -0
  146. fontTools/pens/svgPathPen.py +310 -0
  147. fontTools/pens/t2CharStringPen.py +88 -0
  148. fontTools/pens/teePen.py +55 -0
  149. fontTools/pens/transformPen.py +115 -0
  150. fontTools/pens/ttGlyphPen.py +335 -0
  151. fontTools/pens/wxPen.py +29 -0
  152. fontTools/qu2cu/__init__.py +15 -0
  153. fontTools/qu2cu/__main__.py +7 -0
  154. fontTools/qu2cu/benchmark.py +56 -0
  155. fontTools/qu2cu/cli.py +125 -0
  156. fontTools/qu2cu/qu2cu.c +16682 -0
  157. fontTools/qu2cu/qu2cu.cp311-win32.pyd +0 -0
  158. fontTools/qu2cu/qu2cu.py +405 -0
  159. fontTools/subset/__init__.py +4096 -0
  160. fontTools/subset/__main__.py +6 -0
  161. fontTools/subset/cff.py +184 -0
  162. fontTools/subset/svg.py +253 -0
  163. fontTools/subset/util.py +25 -0
  164. fontTools/svgLib/__init__.py +3 -0
  165. fontTools/svgLib/path/__init__.py +65 -0
  166. fontTools/svgLib/path/arc.py +154 -0
  167. fontTools/svgLib/path/parser.py +322 -0
  168. fontTools/svgLib/path/shapes.py +183 -0
  169. fontTools/t1Lib/__init__.py +648 -0
  170. fontTools/tfmLib.py +460 -0
  171. fontTools/ttLib/__init__.py +30 -0
  172. fontTools/ttLib/__main__.py +148 -0
  173. fontTools/ttLib/macUtils.py +54 -0
  174. fontTools/ttLib/removeOverlaps.py +395 -0
  175. fontTools/ttLib/reorderGlyphs.py +285 -0
  176. fontTools/ttLib/scaleUpem.py +436 -0
  177. fontTools/ttLib/sfnt.py +661 -0
  178. fontTools/ttLib/standardGlyphOrder.py +271 -0
  179. fontTools/ttLib/tables/B_A_S_E_.py +14 -0
  180. fontTools/ttLib/tables/BitmapGlyphMetrics.py +64 -0
  181. fontTools/ttLib/tables/C_B_D_T_.py +113 -0
  182. fontTools/ttLib/tables/C_B_L_C_.py +19 -0
  183. fontTools/ttLib/tables/C_F_F_.py +61 -0
  184. fontTools/ttLib/tables/C_F_F__2.py +26 -0
  185. fontTools/ttLib/tables/C_O_L_R_.py +165 -0
  186. fontTools/ttLib/tables/C_P_A_L_.py +305 -0
  187. fontTools/ttLib/tables/D_S_I_G_.py +158 -0
  188. fontTools/ttLib/tables/D__e_b_g.py +35 -0
  189. fontTools/ttLib/tables/DefaultTable.py +49 -0
  190. fontTools/ttLib/tables/E_B_D_T_.py +835 -0
  191. fontTools/ttLib/tables/E_B_L_C_.py +718 -0
  192. fontTools/ttLib/tables/F_F_T_M_.py +52 -0
  193. fontTools/ttLib/tables/F__e_a_t.py +149 -0
  194. fontTools/ttLib/tables/G_D_E_F_.py +13 -0
  195. fontTools/ttLib/tables/G_M_A_P_.py +148 -0
  196. fontTools/ttLib/tables/G_P_K_G_.py +133 -0
  197. fontTools/ttLib/tables/G_P_O_S_.py +14 -0
  198. fontTools/ttLib/tables/G_S_U_B_.py +13 -0
  199. fontTools/ttLib/tables/G_V_A_R_.py +5 -0
  200. fontTools/ttLib/tables/G__l_a_t.py +235 -0
  201. fontTools/ttLib/tables/G__l_o_c.py +85 -0
  202. fontTools/ttLib/tables/H_V_A_R_.py +13 -0
  203. fontTools/ttLib/tables/J_S_T_F_.py +13 -0
  204. fontTools/ttLib/tables/L_T_S_H_.py +58 -0
  205. fontTools/ttLib/tables/M_A_T_H_.py +13 -0
  206. fontTools/ttLib/tables/M_E_T_A_.py +352 -0
  207. fontTools/ttLib/tables/M_V_A_R_.py +13 -0
  208. fontTools/ttLib/tables/O_S_2f_2.py +752 -0
  209. fontTools/ttLib/tables/S_I_N_G_.py +99 -0
  210. fontTools/ttLib/tables/S_T_A_T_.py +15 -0
  211. fontTools/ttLib/tables/S_V_G_.py +223 -0
  212. fontTools/ttLib/tables/S__i_l_f.py +1040 -0
  213. fontTools/ttLib/tables/S__i_l_l.py +92 -0
  214. fontTools/ttLib/tables/T_S_I_B_.py +13 -0
  215. fontTools/ttLib/tables/T_S_I_C_.py +14 -0
  216. fontTools/ttLib/tables/T_S_I_D_.py +13 -0
  217. fontTools/ttLib/tables/T_S_I_J_.py +13 -0
  218. fontTools/ttLib/tables/T_S_I_P_.py +13 -0
  219. fontTools/ttLib/tables/T_S_I_S_.py +13 -0
  220. fontTools/ttLib/tables/T_S_I_V_.py +26 -0
  221. fontTools/ttLib/tables/T_S_I__0.py +70 -0
  222. fontTools/ttLib/tables/T_S_I__1.py +163 -0
  223. fontTools/ttLib/tables/T_S_I__2.py +17 -0
  224. fontTools/ttLib/tables/T_S_I__3.py +22 -0
  225. fontTools/ttLib/tables/T_S_I__5.py +60 -0
  226. fontTools/ttLib/tables/T_T_F_A_.py +14 -0
  227. fontTools/ttLib/tables/TupleVariation.py +884 -0
  228. fontTools/ttLib/tables/V_A_R_C_.py +12 -0
  229. fontTools/ttLib/tables/V_D_M_X_.py +249 -0
  230. fontTools/ttLib/tables/V_O_R_G_.py +165 -0
  231. fontTools/ttLib/tables/V_V_A_R_.py +13 -0
  232. fontTools/ttLib/tables/__init__.py +98 -0
  233. fontTools/ttLib/tables/_a_n_k_r.py +15 -0
  234. fontTools/ttLib/tables/_a_v_a_r.py +193 -0
  235. fontTools/ttLib/tables/_b_s_l_n.py +15 -0
  236. fontTools/ttLib/tables/_c_i_d_g.py +24 -0
  237. fontTools/ttLib/tables/_c_m_a_p.py +1591 -0
  238. fontTools/ttLib/tables/_c_v_a_r.py +94 -0
  239. fontTools/ttLib/tables/_c_v_t.py +56 -0
  240. fontTools/ttLib/tables/_f_e_a_t.py +15 -0
  241. fontTools/ttLib/tables/_f_p_g_m.py +62 -0
  242. fontTools/ttLib/tables/_f_v_a_r.py +261 -0
  243. fontTools/ttLib/tables/_g_a_s_p.py +63 -0
  244. fontTools/ttLib/tables/_g_c_i_d.py +13 -0
  245. fontTools/ttLib/tables/_g_l_y_f.py +2311 -0
  246. fontTools/ttLib/tables/_g_v_a_r.py +340 -0
  247. fontTools/ttLib/tables/_h_d_m_x.py +127 -0
  248. fontTools/ttLib/tables/_h_e_a_d.py +130 -0
  249. fontTools/ttLib/tables/_h_h_e_a.py +147 -0
  250. fontTools/ttLib/tables/_h_m_t_x.py +164 -0
  251. fontTools/ttLib/tables/_k_e_r_n.py +289 -0
  252. fontTools/ttLib/tables/_l_c_a_r.py +13 -0
  253. fontTools/ttLib/tables/_l_o_c_a.py +70 -0
  254. fontTools/ttLib/tables/_l_t_a_g.py +72 -0
  255. fontTools/ttLib/tables/_m_a_x_p.py +147 -0
  256. fontTools/ttLib/tables/_m_e_t_a.py +112 -0
  257. fontTools/ttLib/tables/_m_o_r_t.py +14 -0
  258. fontTools/ttLib/tables/_m_o_r_x.py +15 -0
  259. fontTools/ttLib/tables/_n_a_m_e.py +1242 -0
  260. fontTools/ttLib/tables/_o_p_b_d.py +14 -0
  261. fontTools/ttLib/tables/_p_o_s_t.py +319 -0
  262. fontTools/ttLib/tables/_p_r_e_p.py +16 -0
  263. fontTools/ttLib/tables/_p_r_o_p.py +12 -0
  264. fontTools/ttLib/tables/_s_b_i_x.py +129 -0
  265. fontTools/ttLib/tables/_t_r_a_k.py +332 -0
  266. fontTools/ttLib/tables/_v_h_e_a.py +139 -0
  267. fontTools/ttLib/tables/_v_m_t_x.py +19 -0
  268. fontTools/ttLib/tables/asciiTable.py +20 -0
  269. fontTools/ttLib/tables/grUtils.py +92 -0
  270. fontTools/ttLib/tables/otBase.py +1458 -0
  271. fontTools/ttLib/tables/otConverters.py +2068 -0
  272. fontTools/ttLib/tables/otData.py +6400 -0
  273. fontTools/ttLib/tables/otTables.py +2703 -0
  274. fontTools/ttLib/tables/otTraverse.py +163 -0
  275. fontTools/ttLib/tables/sbixGlyph.py +149 -0
  276. fontTools/ttLib/tables/sbixStrike.py +177 -0
  277. fontTools/ttLib/tables/table_API_readme.txt +91 -0
  278. fontTools/ttLib/tables/ttProgram.py +594 -0
  279. fontTools/ttLib/ttCollection.py +125 -0
  280. fontTools/ttLib/ttFont.py +1148 -0
  281. fontTools/ttLib/ttGlyphSet.py +490 -0
  282. fontTools/ttLib/ttVisitor.py +32 -0
  283. fontTools/ttLib/woff2.py +1680 -0
  284. fontTools/ttx.py +479 -0
  285. fontTools/ufoLib/__init__.py +2575 -0
  286. fontTools/ufoLib/converters.py +407 -0
  287. fontTools/ufoLib/errors.py +30 -0
  288. fontTools/ufoLib/etree.py +6 -0
  289. fontTools/ufoLib/filenames.py +356 -0
  290. fontTools/ufoLib/glifLib.py +2120 -0
  291. fontTools/ufoLib/kerning.py +141 -0
  292. fontTools/ufoLib/plistlib.py +47 -0
  293. fontTools/ufoLib/pointPen.py +6 -0
  294. fontTools/ufoLib/utils.py +107 -0
  295. fontTools/ufoLib/validators.py +1208 -0
  296. fontTools/unicode.py +50 -0
  297. fontTools/unicodedata/Blocks.py +817 -0
  298. fontTools/unicodedata/Mirrored.py +446 -0
  299. fontTools/unicodedata/OTTags.py +50 -0
  300. fontTools/unicodedata/ScriptExtensions.py +832 -0
  301. fontTools/unicodedata/Scripts.py +3639 -0
  302. fontTools/unicodedata/__init__.py +306 -0
  303. fontTools/varLib/__init__.py +1600 -0
  304. fontTools/varLib/__main__.py +6 -0
  305. fontTools/varLib/avar/__init__.py +0 -0
  306. fontTools/varLib/avar/__main__.py +72 -0
  307. fontTools/varLib/avar/build.py +79 -0
  308. fontTools/varLib/avar/map.py +108 -0
  309. fontTools/varLib/avar/plan.py +1004 -0
  310. fontTools/varLib/avar/unbuild.py +271 -0
  311. fontTools/varLib/avarPlanner.py +8 -0
  312. fontTools/varLib/builder.py +215 -0
  313. fontTools/varLib/cff.py +631 -0
  314. fontTools/varLib/errors.py +219 -0
  315. fontTools/varLib/featureVars.py +703 -0
  316. fontTools/varLib/hvar.py +113 -0
  317. fontTools/varLib/instancer/__init__.py +2052 -0
  318. fontTools/varLib/instancer/__main__.py +5 -0
  319. fontTools/varLib/instancer/featureVars.py +190 -0
  320. fontTools/varLib/instancer/names.py +388 -0
  321. fontTools/varLib/instancer/solver.py +309 -0
  322. fontTools/varLib/interpolatable.py +1209 -0
  323. fontTools/varLib/interpolatableHelpers.py +399 -0
  324. fontTools/varLib/interpolatablePlot.py +1269 -0
  325. fontTools/varLib/interpolatableTestContourOrder.py +82 -0
  326. fontTools/varLib/interpolatableTestStartingPoint.py +107 -0
  327. fontTools/varLib/interpolate_layout.py +124 -0
  328. fontTools/varLib/iup.c +19815 -0
  329. fontTools/varLib/iup.cp311-win32.pyd +0 -0
  330. fontTools/varLib/iup.py +490 -0
  331. fontTools/varLib/merger.py +1717 -0
  332. fontTools/varLib/models.py +642 -0
  333. fontTools/varLib/multiVarStore.py +253 -0
  334. fontTools/varLib/mutator.py +529 -0
  335. fontTools/varLib/mvar.py +40 -0
  336. fontTools/varLib/plot.py +238 -0
  337. fontTools/varLib/stat.py +149 -0
  338. fontTools/varLib/varStore.py +739 -0
  339. fontTools/voltLib/__init__.py +5 -0
  340. fontTools/voltLib/__main__.py +206 -0
  341. fontTools/voltLib/ast.py +452 -0
  342. fontTools/voltLib/error.py +12 -0
  343. fontTools/voltLib/lexer.py +99 -0
  344. fontTools/voltLib/parser.py +664 -0
  345. fontTools/voltLib/voltToFea.py +911 -0
  346. fonttools-4.60.2.data/data/share/man/man1/ttx.1 +225 -0
  347. fonttools-4.60.2.dist-info/METADATA +2250 -0
  348. fonttools-4.60.2.dist-info/RECORD +353 -0
  349. fonttools-4.60.2.dist-info/WHEEL +5 -0
  350. fonttools-4.60.2.dist-info/entry_points.txt +5 -0
  351. fonttools-4.60.2.dist-info/licenses/LICENSE +21 -0
  352. fonttools-4.60.2.dist-info/licenses/LICENSE.external +388 -0
  353. fonttools-4.60.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,835 @@
1
+ from fontTools.misc import sstruct
2
+ from fontTools.misc.textTools import (
3
+ bytechr,
4
+ byteord,
5
+ bytesjoin,
6
+ strjoin,
7
+ safeEval,
8
+ readHex,
9
+ hexStr,
10
+ deHexStr,
11
+ )
12
+ from .BitmapGlyphMetrics import (
13
+ BigGlyphMetrics,
14
+ bigGlyphMetricsFormat,
15
+ SmallGlyphMetrics,
16
+ smallGlyphMetricsFormat,
17
+ )
18
+ from . import DefaultTable
19
+ import itertools
20
+ import os
21
+ import struct
22
+ import logging
23
+
24
+
25
+ log = logging.getLogger(__name__)
26
+
27
+ ebdtTableVersionFormat = """
28
+ > # big endian
29
+ version: 16.16F
30
+ """
31
+
32
+ ebdtComponentFormat = """
33
+ > # big endian
34
+ glyphCode: H
35
+ xOffset: b
36
+ yOffset: b
37
+ """
38
+
39
+
40
+ class table_E_B_D_T_(DefaultTable.DefaultTable):
41
+ """Embedded Bitmap Data table
42
+
43
+ The ``EBDT`` table contains monochrome or grayscale bitmap data for
44
+ glyphs. It must be used in concert with the ``EBLC`` table.
45
+
46
+ See also https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt
47
+ """
48
+
49
+ # Keep a reference to the name of the data locator table.
50
+ locatorName = "EBLC"
51
+
52
+ # This method can be overridden in subclasses to support new formats
53
+ # without changing the other implementation. Also can be used as a
54
+ # convenience method for coverting a font file to an alternative format.
55
+ def getImageFormatClass(self, imageFormat):
56
+ return ebdt_bitmap_classes[imageFormat]
57
+
58
+ def decompile(self, data, ttFont):
59
+ # Get the version but don't advance the slice.
60
+ # Most of the lookup for this table is done relative
61
+ # to the begining so slice by the offsets provided
62
+ # in the EBLC table.
63
+ sstruct.unpack2(ebdtTableVersionFormat, data, self)
64
+
65
+ # Keep a dict of glyphs that have been seen so they aren't remade.
66
+ # This dict maps intervals of data to the BitmapGlyph.
67
+ glyphDict = {}
68
+
69
+ # Pull out the EBLC table and loop through glyphs.
70
+ # A strike is a concept that spans both tables.
71
+ # The actual bitmap data is stored in the EBDT.
72
+ locator = ttFont[self.__class__.locatorName]
73
+ self.strikeData = []
74
+ for curStrike in locator.strikes:
75
+ bitmapGlyphDict = {}
76
+ self.strikeData.append(bitmapGlyphDict)
77
+ for indexSubTable in curStrike.indexSubTables:
78
+ dataIter = zip(indexSubTable.names, indexSubTable.locations)
79
+ for curName, curLoc in dataIter:
80
+ # Don't create duplicate data entries for the same glyphs.
81
+ # Instead just use the structures that already exist if they exist.
82
+ if curLoc in glyphDict:
83
+ curGlyph = glyphDict[curLoc]
84
+ else:
85
+ curGlyphData = data[slice(*curLoc)]
86
+ imageFormatClass = self.getImageFormatClass(
87
+ indexSubTable.imageFormat
88
+ )
89
+ curGlyph = imageFormatClass(curGlyphData, ttFont)
90
+ glyphDict[curLoc] = curGlyph
91
+ bitmapGlyphDict[curName] = curGlyph
92
+
93
+ def compile(self, ttFont):
94
+ dataList = []
95
+ dataList.append(sstruct.pack(ebdtTableVersionFormat, self))
96
+ dataSize = len(dataList[0])
97
+
98
+ # Keep a dict of glyphs that have been seen so they aren't remade.
99
+ # This dict maps the id of the BitmapGlyph to the interval
100
+ # in the data.
101
+ glyphDict = {}
102
+
103
+ # Go through the bitmap glyph data. Just in case the data for a glyph
104
+ # changed the size metrics should be recalculated. There are a variety
105
+ # of formats and they get stored in the EBLC table. That is why
106
+ # recalculation is defered to the EblcIndexSubTable class and just
107
+ # pass what is known about bitmap glyphs from this particular table.
108
+ locator = ttFont[self.__class__.locatorName]
109
+ for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
110
+ for curIndexSubTable in curStrike.indexSubTables:
111
+ dataLocations = []
112
+ for curName in curIndexSubTable.names:
113
+ # Handle the data placement based on seeing the glyph or not.
114
+ # Just save a reference to the location if the glyph has already
115
+ # been saved in compile. This code assumes that glyphs will only
116
+ # be referenced multiple times from indexFormat5. By luck the
117
+ # code may still work when referencing poorly ordered fonts with
118
+ # duplicate references. If there is a font that is unlucky the
119
+ # respective compile methods for the indexSubTables will fail
120
+ # their assertions. All fonts seem to follow this assumption.
121
+ # More complicated packing may be needed if a counter-font exists.
122
+ glyph = curGlyphDict[curName]
123
+ objectId = id(glyph)
124
+ if objectId not in glyphDict:
125
+ data = glyph.compile(ttFont)
126
+ data = curIndexSubTable.padBitmapData(data)
127
+ startByte = dataSize
128
+ dataSize += len(data)
129
+ endByte = dataSize
130
+ dataList.append(data)
131
+ dataLoc = (startByte, endByte)
132
+ glyphDict[objectId] = dataLoc
133
+ else:
134
+ dataLoc = glyphDict[objectId]
135
+ dataLocations.append(dataLoc)
136
+ # Just use the new data locations in the indexSubTable.
137
+ # The respective compile implementations will take care
138
+ # of any of the problems in the convertion that may arise.
139
+ curIndexSubTable.locations = dataLocations
140
+
141
+ return bytesjoin(dataList)
142
+
143
+ def toXML(self, writer, ttFont):
144
+ # When exporting to XML if one of the data export formats
145
+ # requires metrics then those metrics may be in the locator.
146
+ # In this case populate the bitmaps with "export metrics".
147
+ if ttFont.bitmapGlyphDataFormat in ("row", "bitwise"):
148
+ locator = ttFont[self.__class__.locatorName]
149
+ for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
150
+ for curIndexSubTable in curStrike.indexSubTables:
151
+ for curName in curIndexSubTable.names:
152
+ glyph = curGlyphDict[curName]
153
+ # I'm not sure which metrics have priority here.
154
+ # For now if both metrics exist go with glyph metrics.
155
+ if hasattr(glyph, "metrics"):
156
+ glyph.exportMetrics = glyph.metrics
157
+ else:
158
+ glyph.exportMetrics = curIndexSubTable.metrics
159
+ glyph.exportBitDepth = curStrike.bitmapSizeTable.bitDepth
160
+
161
+ writer.simpletag("header", [("version", self.version)])
162
+ writer.newline()
163
+ locator = ttFont[self.__class__.locatorName]
164
+ for strikeIndex, bitmapGlyphDict in enumerate(self.strikeData):
165
+ writer.begintag("strikedata", [("index", strikeIndex)])
166
+ writer.newline()
167
+ for curName, curBitmap in bitmapGlyphDict.items():
168
+ curBitmap.toXML(strikeIndex, curName, writer, ttFont)
169
+ writer.endtag("strikedata")
170
+ writer.newline()
171
+
172
+ def fromXML(self, name, attrs, content, ttFont):
173
+ if name == "header":
174
+ self.version = safeEval(attrs["version"])
175
+ elif name == "strikedata":
176
+ if not hasattr(self, "strikeData"):
177
+ self.strikeData = []
178
+ strikeIndex = safeEval(attrs["index"])
179
+
180
+ bitmapGlyphDict = {}
181
+ for element in content:
182
+ if not isinstance(element, tuple):
183
+ continue
184
+ name, attrs, content = element
185
+ if name[4:].startswith(_bitmapGlyphSubclassPrefix[4:]):
186
+ imageFormat = safeEval(name[len(_bitmapGlyphSubclassPrefix) :])
187
+ glyphName = attrs["name"]
188
+ imageFormatClass = self.getImageFormatClass(imageFormat)
189
+ curGlyph = imageFormatClass(None, None)
190
+ curGlyph.fromXML(name, attrs, content, ttFont)
191
+ assert glyphName not in bitmapGlyphDict, (
192
+ "Duplicate glyphs with the same name '%s' in the same strike."
193
+ % glyphName
194
+ )
195
+ bitmapGlyphDict[glyphName] = curGlyph
196
+ else:
197
+ log.warning("%s being ignored by %s", name, self.__class__.__name__)
198
+
199
+ # Grow the strike data array to the appropriate size. The XML
200
+ # format allows the strike index value to be out of order.
201
+ if strikeIndex >= len(self.strikeData):
202
+ self.strikeData += [None] * (strikeIndex + 1 - len(self.strikeData))
203
+ assert (
204
+ self.strikeData[strikeIndex] is None
205
+ ), "Duplicate strike EBDT indices."
206
+ self.strikeData[strikeIndex] = bitmapGlyphDict
207
+
208
+
209
+ class EbdtComponent(object):
210
+ def toXML(self, writer, ttFont):
211
+ writer.begintag("ebdtComponent", [("name", self.name)])
212
+ writer.newline()
213
+ for componentName in sstruct.getformat(ebdtComponentFormat)[1][1:]:
214
+ writer.simpletag(componentName, value=getattr(self, componentName))
215
+ writer.newline()
216
+ writer.endtag("ebdtComponent")
217
+ writer.newline()
218
+
219
+ def fromXML(self, name, attrs, content, ttFont):
220
+ self.name = attrs["name"]
221
+ componentNames = set(sstruct.getformat(ebdtComponentFormat)[1][1:])
222
+ for element in content:
223
+ if not isinstance(element, tuple):
224
+ continue
225
+ name, attrs, content = element
226
+ if name in componentNames:
227
+ vars(self)[name] = safeEval(attrs["value"])
228
+ else:
229
+ log.warning("unknown name '%s' being ignored by EbdtComponent.", name)
230
+
231
+
232
+ # Helper functions for dealing with binary.
233
+
234
+
235
+ def _data2binary(data, numBits):
236
+ binaryList = []
237
+ for curByte in data:
238
+ value = byteord(curByte)
239
+ numBitsCut = min(8, numBits)
240
+ for i in range(numBitsCut):
241
+ if value & 0x1:
242
+ binaryList.append("1")
243
+ else:
244
+ binaryList.append("0")
245
+ value = value >> 1
246
+ numBits -= numBitsCut
247
+ return strjoin(binaryList)
248
+
249
+
250
+ def _binary2data(binary):
251
+ byteList = []
252
+ for bitLoc in range(0, len(binary), 8):
253
+ byteString = binary[bitLoc : bitLoc + 8]
254
+ curByte = 0
255
+ for curBit in reversed(byteString):
256
+ curByte = curByte << 1
257
+ if curBit == "1":
258
+ curByte |= 1
259
+ byteList.append(bytechr(curByte))
260
+ return bytesjoin(byteList)
261
+
262
+
263
+ def _memoize(f):
264
+ class memodict(dict):
265
+ def __missing__(self, key):
266
+ ret = f(key)
267
+ if isinstance(key, int) or len(key) == 1:
268
+ self[key] = ret
269
+ return ret
270
+
271
+ return memodict().__getitem__
272
+
273
+
274
+ # 00100111 -> 11100100 per byte, not to be confused with little/big endian.
275
+ # Bitmap data per byte is in the order that binary is written on the page
276
+ # with the least significant bit as far right as possible. This is the
277
+ # opposite of what makes sense algorithmically and hence this function.
278
+ @_memoize
279
+ def _reverseBytes(data):
280
+ r"""
281
+ >>> bin(ord(_reverseBytes(0b00100111)))
282
+ '0b11100100'
283
+ >>> _reverseBytes(b'\x00\xf0')
284
+ b'\x00\x0f'
285
+ """
286
+ if isinstance(data, bytes) and len(data) != 1:
287
+ return bytesjoin(map(_reverseBytes, data))
288
+ byte = byteord(data)
289
+ result = 0
290
+ for i in range(8):
291
+ result = result << 1
292
+ result |= byte & 1
293
+ byte = byte >> 1
294
+ return bytechr(result)
295
+
296
+
297
+ # This section of code is for reading and writing image data to/from XML.
298
+
299
+
300
+ def _writeRawImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
301
+ writer.begintag("rawimagedata")
302
+ writer.newline()
303
+ writer.dumphex(bitmapObject.imageData)
304
+ writer.endtag("rawimagedata")
305
+ writer.newline()
306
+
307
+
308
+ def _readRawImageData(bitmapObject, name, attrs, content, ttFont):
309
+ bitmapObject.imageData = readHex(content)
310
+
311
+
312
+ def _writeRowImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
313
+ metrics = bitmapObject.exportMetrics
314
+ del bitmapObject.exportMetrics
315
+ bitDepth = bitmapObject.exportBitDepth
316
+ del bitmapObject.exportBitDepth
317
+
318
+ writer.begintag(
319
+ "rowimagedata", bitDepth=bitDepth, width=metrics.width, height=metrics.height
320
+ )
321
+ writer.newline()
322
+ for curRow in range(metrics.height):
323
+ rowData = bitmapObject.getRow(curRow, bitDepth=bitDepth, metrics=metrics)
324
+ writer.simpletag("row", value=hexStr(rowData))
325
+ writer.newline()
326
+ writer.endtag("rowimagedata")
327
+ writer.newline()
328
+
329
+
330
+ def _readRowImageData(bitmapObject, name, attrs, content, ttFont):
331
+ bitDepth = safeEval(attrs["bitDepth"])
332
+ metrics = SmallGlyphMetrics()
333
+ metrics.width = safeEval(attrs["width"])
334
+ metrics.height = safeEval(attrs["height"])
335
+
336
+ dataRows = []
337
+ for element in content:
338
+ if not isinstance(element, tuple):
339
+ continue
340
+ name, attr, content = element
341
+ # Chop off 'imagedata' from the tag to get just the option.
342
+ if name == "row":
343
+ dataRows.append(deHexStr(attr["value"]))
344
+ bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics)
345
+
346
+
347
+ def _writeBitwiseImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
348
+ metrics = bitmapObject.exportMetrics
349
+ del bitmapObject.exportMetrics
350
+ bitDepth = bitmapObject.exportBitDepth
351
+ del bitmapObject.exportBitDepth
352
+
353
+ # A dict for mapping binary to more readable/artistic ASCII characters.
354
+ binaryConv = {"0": ".", "1": "@"}
355
+
356
+ writer.begintag(
357
+ "bitwiseimagedata",
358
+ bitDepth=bitDepth,
359
+ width=metrics.width,
360
+ height=metrics.height,
361
+ )
362
+ writer.newline()
363
+ for curRow in range(metrics.height):
364
+ rowData = bitmapObject.getRow(
365
+ curRow, bitDepth=1, metrics=metrics, reverseBytes=True
366
+ )
367
+ rowData = _data2binary(rowData, metrics.width)
368
+ # Make the output a readable ASCII art form.
369
+ rowData = strjoin(map(binaryConv.get, rowData))
370
+ writer.simpletag("row", value=rowData)
371
+ writer.newline()
372
+ writer.endtag("bitwiseimagedata")
373
+ writer.newline()
374
+
375
+
376
+ def _readBitwiseImageData(bitmapObject, name, attrs, content, ttFont):
377
+ bitDepth = safeEval(attrs["bitDepth"])
378
+ metrics = SmallGlyphMetrics()
379
+ metrics.width = safeEval(attrs["width"])
380
+ metrics.height = safeEval(attrs["height"])
381
+
382
+ # A dict for mapping from ASCII to binary. All characters are considered
383
+ # a '1' except space, period and '0' which maps to '0'.
384
+ binaryConv = {" ": "0", ".": "0", "0": "0"}
385
+
386
+ dataRows = []
387
+ for element in content:
388
+ if not isinstance(element, tuple):
389
+ continue
390
+ name, attr, content = element
391
+ if name == "row":
392
+ mapParams = zip(attr["value"], itertools.repeat("1"))
393
+ rowData = strjoin(itertools.starmap(binaryConv.get, mapParams))
394
+ dataRows.append(_binary2data(rowData))
395
+
396
+ bitmapObject.setRows(
397
+ dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True
398
+ )
399
+
400
+
401
+ def _writeExtFileImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
402
+ try:
403
+ folder = os.path.dirname(writer.file.name)
404
+ except AttributeError:
405
+ # fall back to current directory if output file's directory isn't found
406
+ folder = "."
407
+ folder = os.path.join(folder, "bitmaps")
408
+ filename = glyphName + bitmapObject.fileExtension
409
+ if not os.path.isdir(folder):
410
+ os.makedirs(folder)
411
+ folder = os.path.join(folder, "strike%d" % strikeIndex)
412
+ if not os.path.isdir(folder):
413
+ os.makedirs(folder)
414
+
415
+ fullPath = os.path.join(folder, filename)
416
+ writer.simpletag("extfileimagedata", value=fullPath)
417
+ writer.newline()
418
+
419
+ with open(fullPath, "wb") as file:
420
+ file.write(bitmapObject.imageData)
421
+
422
+
423
+ def _readExtFileImageData(bitmapObject, name, attrs, content, ttFont):
424
+ fullPath = attrs["value"]
425
+ with open(fullPath, "rb") as file:
426
+ bitmapObject.imageData = file.read()
427
+
428
+
429
+ # End of XML writing code.
430
+
431
+ # Important information about the naming scheme. Used for identifying formats
432
+ # in XML.
433
+ _bitmapGlyphSubclassPrefix = "ebdt_bitmap_format_"
434
+
435
+
436
+ class BitmapGlyph(object):
437
+ # For the external file format. This can be changed in subclasses. This way
438
+ # when the extfile option is turned on files have the form: glyphName.ext
439
+ # The default is just a flat binary file with no meaning.
440
+ fileExtension = ".bin"
441
+
442
+ # Keep track of reading and writing of various forms.
443
+ xmlDataFunctions = {
444
+ "raw": (_writeRawImageData, _readRawImageData),
445
+ "row": (_writeRowImageData, _readRowImageData),
446
+ "bitwise": (_writeBitwiseImageData, _readBitwiseImageData),
447
+ "extfile": (_writeExtFileImageData, _readExtFileImageData),
448
+ }
449
+
450
+ def __init__(self, data, ttFont):
451
+ self.data = data
452
+ self.ttFont = ttFont
453
+ # TODO Currently non-lazy decompilation is untested here...
454
+ # if not ttFont.lazy:
455
+ # self.decompile()
456
+ # del self.data
457
+
458
+ def __getattr__(self, attr):
459
+ # Allow lazy decompile.
460
+ if attr[:2] == "__":
461
+ raise AttributeError(attr)
462
+ if attr == "data":
463
+ raise AttributeError(attr)
464
+ self.decompile()
465
+ del self.data
466
+ return getattr(self, attr)
467
+
468
+ def ensureDecompiled(self, recurse=False):
469
+ if hasattr(self, "data"):
470
+ self.decompile()
471
+ del self.data
472
+
473
+ # Not a fan of this but it is needed for safer safety checking.
474
+ def getFormat(self):
475
+ return safeEval(self.__class__.__name__[len(_bitmapGlyphSubclassPrefix) :])
476
+
477
+ def toXML(self, strikeIndex, glyphName, writer, ttFont):
478
+ writer.begintag(self.__class__.__name__, [("name", glyphName)])
479
+ writer.newline()
480
+
481
+ self.writeMetrics(writer, ttFont)
482
+ # Use the internal write method to write using the correct output format.
483
+ self.writeData(strikeIndex, glyphName, writer, ttFont)
484
+
485
+ writer.endtag(self.__class__.__name__)
486
+ writer.newline()
487
+
488
+ def fromXML(self, name, attrs, content, ttFont):
489
+ self.readMetrics(name, attrs, content, ttFont)
490
+ for element in content:
491
+ if not isinstance(element, tuple):
492
+ continue
493
+ name, attr, content = element
494
+ if not name.endswith("imagedata"):
495
+ continue
496
+ # Chop off 'imagedata' from the tag to get just the option.
497
+ option = name[: -len("imagedata")]
498
+ assert option in self.__class__.xmlDataFunctions
499
+ self.readData(name, attr, content, ttFont)
500
+
501
+ # Some of the glyphs have the metrics. This allows for metrics to be
502
+ # added if the glyph format has them. Default behavior is to do nothing.
503
+ def writeMetrics(self, writer, ttFont):
504
+ pass
505
+
506
+ # The opposite of write metrics.
507
+ def readMetrics(self, name, attrs, content, ttFont):
508
+ pass
509
+
510
+ def writeData(self, strikeIndex, glyphName, writer, ttFont):
511
+ try:
512
+ writeFunc, readFunc = self.__class__.xmlDataFunctions[
513
+ ttFont.bitmapGlyphDataFormat
514
+ ]
515
+ except KeyError:
516
+ writeFunc = _writeRawImageData
517
+ writeFunc(strikeIndex, glyphName, self, writer, ttFont)
518
+
519
+ def readData(self, name, attrs, content, ttFont):
520
+ # Chop off 'imagedata' from the tag to get just the option.
521
+ option = name[: -len("imagedata")]
522
+ writeFunc, readFunc = self.__class__.xmlDataFunctions[option]
523
+ readFunc(self, name, attrs, content, ttFont)
524
+
525
+
526
+ # A closure for creating a mixin for the two types of metrics handling.
527
+ # Most of the code is very similar so its easier to deal with here.
528
+ # Everything works just by passing the class that the mixin is for.
529
+ def _createBitmapPlusMetricsMixin(metricsClass):
530
+ # Both metrics names are listed here to make meaningful error messages.
531
+ metricStrings = [BigGlyphMetrics.__name__, SmallGlyphMetrics.__name__]
532
+ curMetricsName = metricsClass.__name__
533
+ # Find which metrics this is for and determine the opposite name.
534
+ metricsId = metricStrings.index(curMetricsName)
535
+ oppositeMetricsName = metricStrings[1 - metricsId]
536
+
537
+ class BitmapPlusMetricsMixin(object):
538
+ def writeMetrics(self, writer, ttFont):
539
+ self.metrics.toXML(writer, ttFont)
540
+
541
+ def readMetrics(self, name, attrs, content, ttFont):
542
+ for element in content:
543
+ if not isinstance(element, tuple):
544
+ continue
545
+ name, attrs, content = element
546
+ if name == curMetricsName:
547
+ self.metrics = metricsClass()
548
+ self.metrics.fromXML(name, attrs, content, ttFont)
549
+ elif name == oppositeMetricsName:
550
+ log.warning(
551
+ "Warning: %s being ignored in format %d.",
552
+ oppositeMetricsName,
553
+ self.getFormat(),
554
+ )
555
+
556
+ return BitmapPlusMetricsMixin
557
+
558
+
559
+ # Since there are only two types of mixin's just create them here.
560
+ BitmapPlusBigMetricsMixin = _createBitmapPlusMetricsMixin(BigGlyphMetrics)
561
+ BitmapPlusSmallMetricsMixin = _createBitmapPlusMetricsMixin(SmallGlyphMetrics)
562
+
563
+
564
+ # Data that is bit aligned can be tricky to deal with. These classes implement
565
+ # helper functionality for dealing with the data and getting a particular row
566
+ # of bitwise data. Also helps implement fancy data export/import in XML.
567
+ class BitAlignedBitmapMixin(object):
568
+ def _getBitRange(self, row, bitDepth, metrics):
569
+ rowBits = bitDepth * metrics.width
570
+ bitOffset = row * rowBits
571
+ return (bitOffset, bitOffset + rowBits)
572
+
573
+ def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
574
+ if metrics is None:
575
+ metrics = self.metrics
576
+ assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
577
+
578
+ # Loop through each byte. This can cover two bytes in the original data or
579
+ # a single byte if things happen to be aligned. The very last entry might
580
+ # not be aligned so take care to trim the binary data to size and pad with
581
+ # zeros in the row data. Bit aligned data is somewhat tricky.
582
+ #
583
+ # Example of data cut. Data cut represented in x's.
584
+ # '|' represents byte boundary.
585
+ # data = ...0XX|XXXXXX00|000... => XXXXXXXX
586
+ # or
587
+ # data = ...0XX|XXXX0000|000... => XXXXXX00
588
+ # or
589
+ # data = ...000|XXXXXXXX|000... => XXXXXXXX
590
+ # or
591
+ # data = ...000|00XXXX00|000... => XXXX0000
592
+ #
593
+ dataList = []
594
+ bitRange = self._getBitRange(row, bitDepth, metrics)
595
+ stepRange = bitRange + (8,)
596
+ for curBit in range(*stepRange):
597
+ endBit = min(curBit + 8, bitRange[1])
598
+ numBits = endBit - curBit
599
+ cutPoint = curBit % 8
600
+ firstByteLoc = curBit // 8
601
+ secondByteLoc = endBit // 8
602
+ if firstByteLoc < secondByteLoc:
603
+ numBitsCut = 8 - cutPoint
604
+ else:
605
+ numBitsCut = endBit - curBit
606
+ curByte = _reverseBytes(self.imageData[firstByteLoc])
607
+ firstHalf = byteord(curByte) >> cutPoint
608
+ firstHalf = ((1 << numBitsCut) - 1) & firstHalf
609
+ newByte = firstHalf
610
+ if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
611
+ curByte = _reverseBytes(self.imageData[secondByteLoc])
612
+ secondHalf = byteord(curByte) << numBitsCut
613
+ newByte = (firstHalf | secondHalf) & ((1 << numBits) - 1)
614
+ dataList.append(bytechr(newByte))
615
+
616
+ # The way the data is kept is opposite the algorithm used.
617
+ data = bytesjoin(dataList)
618
+ if not reverseBytes:
619
+ data = _reverseBytes(data)
620
+ return data
621
+
622
+ def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
623
+ if metrics is None:
624
+ metrics = self.metrics
625
+ if not reverseBytes:
626
+ dataRows = list(map(_reverseBytes, dataRows))
627
+
628
+ # Keep track of a list of ordinal values as they are easier to modify
629
+ # than a list of strings. Map to actual strings later.
630
+ numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) // 8
631
+ ordDataList = [0] * numBytes
632
+ for row, data in enumerate(dataRows):
633
+ bitRange = self._getBitRange(row, bitDepth, metrics)
634
+ stepRange = bitRange + (8,)
635
+ for curBit, curByte in zip(range(*stepRange), data):
636
+ endBit = min(curBit + 8, bitRange[1])
637
+ cutPoint = curBit % 8
638
+ firstByteLoc = curBit // 8
639
+ secondByteLoc = endBit // 8
640
+ if firstByteLoc < secondByteLoc:
641
+ numBitsCut = 8 - cutPoint
642
+ else:
643
+ numBitsCut = endBit - curBit
644
+ curByte = byteord(curByte)
645
+ firstByte = curByte & ((1 << numBitsCut) - 1)
646
+ ordDataList[firstByteLoc] |= firstByte << cutPoint
647
+ if firstByteLoc < secondByteLoc and secondByteLoc < numBytes:
648
+ secondByte = (curByte >> numBitsCut) & ((1 << 8 - numBitsCut) - 1)
649
+ ordDataList[secondByteLoc] |= secondByte
650
+
651
+ # Save the image data with the bits going the correct way.
652
+ self.imageData = _reverseBytes(bytesjoin(map(bytechr, ordDataList)))
653
+
654
+
655
+ class ByteAlignedBitmapMixin(object):
656
+ def _getByteRange(self, row, bitDepth, metrics):
657
+ rowBytes = (bitDepth * metrics.width + 7) // 8
658
+ byteOffset = row * rowBytes
659
+ return (byteOffset, byteOffset + rowBytes)
660
+
661
+ def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
662
+ if metrics is None:
663
+ metrics = self.metrics
664
+ assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
665
+ byteRange = self._getByteRange(row, bitDepth, metrics)
666
+ data = self.imageData[slice(*byteRange)]
667
+ if reverseBytes:
668
+ data = _reverseBytes(data)
669
+ return data
670
+
671
+ def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
672
+ if metrics is None:
673
+ metrics = self.metrics
674
+ if reverseBytes:
675
+ dataRows = map(_reverseBytes, dataRows)
676
+ self.imageData = bytesjoin(dataRows)
677
+
678
+
679
+ class ebdt_bitmap_format_1(
680
+ ByteAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph
681
+ ):
682
+ def decompile(self):
683
+ self.metrics = SmallGlyphMetrics()
684
+ dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
685
+ self.imageData = data
686
+
687
+ def compile(self, ttFont):
688
+ data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
689
+ return data + self.imageData
690
+
691
+
692
+ class ebdt_bitmap_format_2(
693
+ BitAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph
694
+ ):
695
+ def decompile(self):
696
+ self.metrics = SmallGlyphMetrics()
697
+ dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
698
+ self.imageData = data
699
+
700
+ def compile(self, ttFont):
701
+ data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
702
+ return data + self.imageData
703
+
704
+
705
+ class ebdt_bitmap_format_5(BitAlignedBitmapMixin, BitmapGlyph):
706
+ def decompile(self):
707
+ self.imageData = self.data
708
+
709
+ def compile(self, ttFont):
710
+ return self.imageData
711
+
712
+
713
+ class ebdt_bitmap_format_6(
714
+ ByteAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph
715
+ ):
716
+ def decompile(self):
717
+ self.metrics = BigGlyphMetrics()
718
+ dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
719
+ self.imageData = data
720
+
721
+ def compile(self, ttFont):
722
+ data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
723
+ return data + self.imageData
724
+
725
+
726
+ class ebdt_bitmap_format_7(
727
+ BitAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph
728
+ ):
729
+ def decompile(self):
730
+ self.metrics = BigGlyphMetrics()
731
+ dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
732
+ self.imageData = data
733
+
734
+ def compile(self, ttFont):
735
+ data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
736
+ return data + self.imageData
737
+
738
+
739
+ class ComponentBitmapGlyph(BitmapGlyph):
740
+ def toXML(self, strikeIndex, glyphName, writer, ttFont):
741
+ writer.begintag(self.__class__.__name__, [("name", glyphName)])
742
+ writer.newline()
743
+
744
+ self.writeMetrics(writer, ttFont)
745
+
746
+ writer.begintag("components")
747
+ writer.newline()
748
+ for curComponent in self.componentArray:
749
+ curComponent.toXML(writer, ttFont)
750
+ writer.endtag("components")
751
+ writer.newline()
752
+
753
+ writer.endtag(self.__class__.__name__)
754
+ writer.newline()
755
+
756
+ def fromXML(self, name, attrs, content, ttFont):
757
+ self.readMetrics(name, attrs, content, ttFont)
758
+ for element in content:
759
+ if not isinstance(element, tuple):
760
+ continue
761
+ name, attr, content = element
762
+ if name == "components":
763
+ self.componentArray = []
764
+ for compElement in content:
765
+ if not isinstance(compElement, tuple):
766
+ continue
767
+ name, attrs, content = compElement
768
+ if name == "ebdtComponent":
769
+ curComponent = EbdtComponent()
770
+ curComponent.fromXML(name, attrs, content, ttFont)
771
+ self.componentArray.append(curComponent)
772
+ else:
773
+ log.warning("'%s' being ignored in component array.", name)
774
+
775
+
776
+ class ebdt_bitmap_format_8(BitmapPlusSmallMetricsMixin, ComponentBitmapGlyph):
777
+ def decompile(self):
778
+ self.metrics = SmallGlyphMetrics()
779
+ dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
780
+ data = data[1:]
781
+
782
+ (numComponents,) = struct.unpack(">H", data[:2])
783
+ data = data[2:]
784
+ self.componentArray = []
785
+ for i in range(numComponents):
786
+ curComponent = EbdtComponent()
787
+ dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
788
+ curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
789
+ self.componentArray.append(curComponent)
790
+
791
+ def compile(self, ttFont):
792
+ dataList = []
793
+ dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
794
+ dataList.append(b"\0")
795
+ dataList.append(struct.pack(">H", len(self.componentArray)))
796
+ for curComponent in self.componentArray:
797
+ curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
798
+ dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
799
+ return bytesjoin(dataList)
800
+
801
+
802
+ class ebdt_bitmap_format_9(BitmapPlusBigMetricsMixin, ComponentBitmapGlyph):
803
+ def decompile(self):
804
+ self.metrics = BigGlyphMetrics()
805
+ dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
806
+ (numComponents,) = struct.unpack(">H", data[:2])
807
+ data = data[2:]
808
+ self.componentArray = []
809
+ for i in range(numComponents):
810
+ curComponent = EbdtComponent()
811
+ dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
812
+ curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
813
+ self.componentArray.append(curComponent)
814
+
815
+ def compile(self, ttFont):
816
+ dataList = []
817
+ dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
818
+ dataList.append(struct.pack(">H", len(self.componentArray)))
819
+ for curComponent in self.componentArray:
820
+ curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
821
+ dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
822
+ return bytesjoin(dataList)
823
+
824
+
825
+ # Dictionary of bitmap formats to the class representing that format
826
+ # currently only the ones listed in this map are the ones supported.
827
+ ebdt_bitmap_classes = {
828
+ 1: ebdt_bitmap_format_1,
829
+ 2: ebdt_bitmap_format_2,
830
+ 5: ebdt_bitmap_format_5,
831
+ 6: ebdt_bitmap_format_6,
832
+ 7: ebdt_bitmap_format_7,
833
+ 8: ebdt_bitmap_format_8,
834
+ 9: ebdt_bitmap_format_9,
835
+ }