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,356 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Convert user-provided internal UFO names to spec-compliant filenames.
|
|
7
|
+
|
|
8
|
+
This module implements the algorithm for converting between a "user name" -
|
|
9
|
+
something that a user can choose arbitrarily inside a font editor - and a file
|
|
10
|
+
name suitable for use in a wide range of operating systems and filesystems.
|
|
11
|
+
|
|
12
|
+
The `UFO 3 specification <http://unifiedfontobject.org/versions/ufo3/conventions/>`_
|
|
13
|
+
provides an example of an algorithm for such conversion, which avoids illegal
|
|
14
|
+
characters, reserved file names, ambiguity between upper- and lower-case
|
|
15
|
+
characters, and clashes with existing files.
|
|
16
|
+
|
|
17
|
+
This code was originally copied from
|
|
18
|
+
`ufoLib <https://github.com/unified-font-object/ufoLib/blob/8747da7/Lib/ufoLib/filenames.py>`_
|
|
19
|
+
by Tal Leming and is copyright (c) 2005-2016, The RoboFab Developers:
|
|
20
|
+
|
|
21
|
+
- Erik van Blokland
|
|
22
|
+
- Tal Leming
|
|
23
|
+
- Just van Rossum
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Restrictions are taken mostly from
|
|
27
|
+
# https://docs.microsoft.com/en-gb/windows/win32/fileio/naming-a-file#naming-conventions.
|
|
28
|
+
#
|
|
29
|
+
# 1. Integer value zero, sometimes referred to as the ASCII NUL character.
|
|
30
|
+
# 2. Characters whose integer representations are in the range 1 to 31,
|
|
31
|
+
# inclusive.
|
|
32
|
+
# 3. Various characters that (mostly) Windows and POSIX-y filesystems don't
|
|
33
|
+
# allow, plus "(" and ")", as per the specification.
|
|
34
|
+
illegalCharacters: set[str] = {
|
|
35
|
+
"\x00",
|
|
36
|
+
"\x01",
|
|
37
|
+
"\x02",
|
|
38
|
+
"\x03",
|
|
39
|
+
"\x04",
|
|
40
|
+
"\x05",
|
|
41
|
+
"\x06",
|
|
42
|
+
"\x07",
|
|
43
|
+
"\x08",
|
|
44
|
+
"\t",
|
|
45
|
+
"\n",
|
|
46
|
+
"\x0b",
|
|
47
|
+
"\x0c",
|
|
48
|
+
"\r",
|
|
49
|
+
"\x0e",
|
|
50
|
+
"\x0f",
|
|
51
|
+
"\x10",
|
|
52
|
+
"\x11",
|
|
53
|
+
"\x12",
|
|
54
|
+
"\x13",
|
|
55
|
+
"\x14",
|
|
56
|
+
"\x15",
|
|
57
|
+
"\x16",
|
|
58
|
+
"\x17",
|
|
59
|
+
"\x18",
|
|
60
|
+
"\x19",
|
|
61
|
+
"\x1a",
|
|
62
|
+
"\x1b",
|
|
63
|
+
"\x1c",
|
|
64
|
+
"\x1d",
|
|
65
|
+
"\x1e",
|
|
66
|
+
"\x1f",
|
|
67
|
+
'"',
|
|
68
|
+
"*",
|
|
69
|
+
"+",
|
|
70
|
+
"/",
|
|
71
|
+
":",
|
|
72
|
+
"<",
|
|
73
|
+
">",
|
|
74
|
+
"?",
|
|
75
|
+
"[",
|
|
76
|
+
"\\",
|
|
77
|
+
"]",
|
|
78
|
+
"(",
|
|
79
|
+
")",
|
|
80
|
+
"|",
|
|
81
|
+
"\x7f",
|
|
82
|
+
}
|
|
83
|
+
reservedFileNames: set[str] = {
|
|
84
|
+
"aux",
|
|
85
|
+
"clock$",
|
|
86
|
+
"com1",
|
|
87
|
+
"com2",
|
|
88
|
+
"com3",
|
|
89
|
+
"com4",
|
|
90
|
+
"com5",
|
|
91
|
+
"com6",
|
|
92
|
+
"com7",
|
|
93
|
+
"com8",
|
|
94
|
+
"com9",
|
|
95
|
+
"con",
|
|
96
|
+
"lpt1",
|
|
97
|
+
"lpt2",
|
|
98
|
+
"lpt3",
|
|
99
|
+
"lpt4",
|
|
100
|
+
"lpt5",
|
|
101
|
+
"lpt6",
|
|
102
|
+
"lpt7",
|
|
103
|
+
"lpt8",
|
|
104
|
+
"lpt9",
|
|
105
|
+
"nul",
|
|
106
|
+
"prn",
|
|
107
|
+
}
|
|
108
|
+
maxFileNameLength: int = 255
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class NameTranslationError(Exception):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def userNameToFileName(
|
|
116
|
+
userName: str, existing: Iterable[str] = (), prefix: str = "", suffix: str = ""
|
|
117
|
+
) -> str:
|
|
118
|
+
"""Converts from a user name to a file name.
|
|
119
|
+
|
|
120
|
+
Takes care to avoid illegal characters, reserved file names, ambiguity between
|
|
121
|
+
upper- and lower-case characters, and clashes with existing files.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
userName (str): The input file name.
|
|
125
|
+
existing: A case-insensitive list of all existing file names.
|
|
126
|
+
prefix: Prefix to be prepended to the file name.
|
|
127
|
+
suffix: Suffix to be appended to the file name.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
A suitable filename.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
NameTranslationError: If no suitable name could be generated.
|
|
134
|
+
|
|
135
|
+
Examples::
|
|
136
|
+
|
|
137
|
+
>>> userNameToFileName("a") == "a"
|
|
138
|
+
True
|
|
139
|
+
>>> userNameToFileName("A") == "A_"
|
|
140
|
+
True
|
|
141
|
+
>>> userNameToFileName("AE") == "A_E_"
|
|
142
|
+
True
|
|
143
|
+
>>> userNameToFileName("Ae") == "A_e"
|
|
144
|
+
True
|
|
145
|
+
>>> userNameToFileName("ae") == "ae"
|
|
146
|
+
True
|
|
147
|
+
>>> userNameToFileName("aE") == "aE_"
|
|
148
|
+
True
|
|
149
|
+
>>> userNameToFileName("a.alt") == "a.alt"
|
|
150
|
+
True
|
|
151
|
+
>>> userNameToFileName("A.alt") == "A_.alt"
|
|
152
|
+
True
|
|
153
|
+
>>> userNameToFileName("A.Alt") == "A_.A_lt"
|
|
154
|
+
True
|
|
155
|
+
>>> userNameToFileName("A.aLt") == "A_.aL_t"
|
|
156
|
+
True
|
|
157
|
+
>>> userNameToFileName(u"A.alT") == "A_.alT_"
|
|
158
|
+
True
|
|
159
|
+
>>> userNameToFileName("T_H") == "T__H_"
|
|
160
|
+
True
|
|
161
|
+
>>> userNameToFileName("T_h") == "T__h"
|
|
162
|
+
True
|
|
163
|
+
>>> userNameToFileName("t_h") == "t_h"
|
|
164
|
+
True
|
|
165
|
+
>>> userNameToFileName("F_F_I") == "F__F__I_"
|
|
166
|
+
True
|
|
167
|
+
>>> userNameToFileName("f_f_i") == "f_f_i"
|
|
168
|
+
True
|
|
169
|
+
>>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
|
|
170
|
+
True
|
|
171
|
+
>>> userNameToFileName(".notdef") == "_notdef"
|
|
172
|
+
True
|
|
173
|
+
>>> userNameToFileName("con") == "_con"
|
|
174
|
+
True
|
|
175
|
+
>>> userNameToFileName("CON") == "C_O_N_"
|
|
176
|
+
True
|
|
177
|
+
>>> userNameToFileName("con.alt") == "_con.alt"
|
|
178
|
+
True
|
|
179
|
+
>>> userNameToFileName("alt.con") == "alt._con"
|
|
180
|
+
True
|
|
181
|
+
"""
|
|
182
|
+
# the incoming name must be a string
|
|
183
|
+
if not isinstance(userName, str):
|
|
184
|
+
raise ValueError("The value for userName must be a string.")
|
|
185
|
+
# establish the prefix and suffix lengths
|
|
186
|
+
prefixLength = len(prefix)
|
|
187
|
+
suffixLength = len(suffix)
|
|
188
|
+
# replace an initial period with an _
|
|
189
|
+
# if no prefix is to be added
|
|
190
|
+
if not prefix and userName[0] == ".":
|
|
191
|
+
userName = "_" + userName[1:]
|
|
192
|
+
# filter the user name
|
|
193
|
+
filteredUserName = []
|
|
194
|
+
for character in userName:
|
|
195
|
+
# replace illegal characters with _
|
|
196
|
+
if character in illegalCharacters:
|
|
197
|
+
character = "_"
|
|
198
|
+
# add _ to all non-lower characters
|
|
199
|
+
elif character != character.lower():
|
|
200
|
+
character += "_"
|
|
201
|
+
filteredUserName.append(character)
|
|
202
|
+
userName = "".join(filteredUserName)
|
|
203
|
+
# clip to 255
|
|
204
|
+
sliceLength = maxFileNameLength - prefixLength - suffixLength
|
|
205
|
+
userName = userName[:sliceLength]
|
|
206
|
+
# test for illegal files names
|
|
207
|
+
parts = []
|
|
208
|
+
for part in userName.split("."):
|
|
209
|
+
if part.lower() in reservedFileNames:
|
|
210
|
+
part = "_" + part
|
|
211
|
+
parts.append(part)
|
|
212
|
+
userName = ".".join(parts)
|
|
213
|
+
# test for clash
|
|
214
|
+
fullName = prefix + userName + suffix
|
|
215
|
+
if fullName.lower() in existing:
|
|
216
|
+
fullName = handleClash1(userName, existing, prefix, suffix)
|
|
217
|
+
# finished
|
|
218
|
+
return fullName
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def handleClash1(
|
|
222
|
+
userName: str, existing: Iterable[str] = [], prefix: str = "", suffix: str = ""
|
|
223
|
+
) -> str:
|
|
224
|
+
"""A helper function that resolves collisions with existing names when choosing a filename.
|
|
225
|
+
|
|
226
|
+
This function attempts to append an unused integer counter to the filename.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
userName (str): The input file name.
|
|
230
|
+
existing: A case-insensitive list of all existing file names.
|
|
231
|
+
prefix: Prefix to be prepended to the file name.
|
|
232
|
+
suffix: Suffix to be appended to the file name.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
A suitable filename.
|
|
236
|
+
|
|
237
|
+
>>> prefix = ("0" * 5) + "."
|
|
238
|
+
>>> suffix = "." + ("0" * 10)
|
|
239
|
+
>>> existing = ["a" * 5]
|
|
240
|
+
|
|
241
|
+
>>> e = list(existing)
|
|
242
|
+
>>> handleClash1(userName="A" * 5, existing=e,
|
|
243
|
+
... prefix=prefix, suffix=suffix) == (
|
|
244
|
+
... '00000.AAAAA000000000000001.0000000000')
|
|
245
|
+
True
|
|
246
|
+
|
|
247
|
+
>>> e = list(existing)
|
|
248
|
+
>>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
|
|
249
|
+
>>> handleClash1(userName="A" * 5, existing=e,
|
|
250
|
+
... prefix=prefix, suffix=suffix) == (
|
|
251
|
+
... '00000.AAAAA000000000000002.0000000000')
|
|
252
|
+
True
|
|
253
|
+
|
|
254
|
+
>>> e = list(existing)
|
|
255
|
+
>>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
|
|
256
|
+
>>> handleClash1(userName="A" * 5, existing=e,
|
|
257
|
+
... prefix=prefix, suffix=suffix) == (
|
|
258
|
+
... '00000.AAAAA000000000000001.0000000000')
|
|
259
|
+
True
|
|
260
|
+
"""
|
|
261
|
+
# if the prefix length + user name length + suffix length + 15 is at
|
|
262
|
+
# or past the maximum length, silce 15 characters off of the user name
|
|
263
|
+
prefixLength = len(prefix)
|
|
264
|
+
suffixLength = len(suffix)
|
|
265
|
+
if prefixLength + len(userName) + suffixLength + 15 > maxFileNameLength:
|
|
266
|
+
l = prefixLength + len(userName) + suffixLength + 15
|
|
267
|
+
sliceLength = maxFileNameLength - l
|
|
268
|
+
userName = userName[:sliceLength]
|
|
269
|
+
finalName = None
|
|
270
|
+
# try to add numbers to create a unique name
|
|
271
|
+
counter = 1
|
|
272
|
+
while finalName is None:
|
|
273
|
+
name = userName + str(counter).zfill(15)
|
|
274
|
+
fullName = prefix + name + suffix
|
|
275
|
+
if fullName.lower() not in existing:
|
|
276
|
+
finalName = fullName
|
|
277
|
+
break
|
|
278
|
+
else:
|
|
279
|
+
counter += 1
|
|
280
|
+
if counter >= 999999999999999:
|
|
281
|
+
break
|
|
282
|
+
# if there is a clash, go to the next fallback
|
|
283
|
+
if finalName is None:
|
|
284
|
+
finalName = handleClash2(existing, prefix, suffix)
|
|
285
|
+
# finished
|
|
286
|
+
return finalName
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def handleClash2(
|
|
290
|
+
existing: Iterable[str] = [], prefix: str = "", suffix: str = ""
|
|
291
|
+
) -> str:
|
|
292
|
+
"""A helper function that resolves collisions with existing names when choosing a filename.
|
|
293
|
+
|
|
294
|
+
This function is a fallback to :func:`handleClash1`. It attempts to append an unused integer counter to the filename.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
userName (str): The input file name.
|
|
298
|
+
existing: A case-insensitive list of all existing file names.
|
|
299
|
+
prefix: Prefix to be prepended to the file name.
|
|
300
|
+
suffix: Suffix to be appended to the file name.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
A suitable filename.
|
|
304
|
+
|
|
305
|
+
Raises:
|
|
306
|
+
NameTranslationError: If no suitable name could be generated.
|
|
307
|
+
|
|
308
|
+
Examples::
|
|
309
|
+
|
|
310
|
+
>>> prefix = ("0" * 5) + "."
|
|
311
|
+
>>> suffix = "." + ("0" * 10)
|
|
312
|
+
>>> existing = [prefix + str(i) + suffix for i in range(100)]
|
|
313
|
+
|
|
314
|
+
>>> e = list(existing)
|
|
315
|
+
>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
|
|
316
|
+
... '00000.100.0000000000')
|
|
317
|
+
True
|
|
318
|
+
|
|
319
|
+
>>> e = list(existing)
|
|
320
|
+
>>> e.remove(prefix + "1" + suffix)
|
|
321
|
+
>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
|
|
322
|
+
... '00000.1.0000000000')
|
|
323
|
+
True
|
|
324
|
+
|
|
325
|
+
>>> e = list(existing)
|
|
326
|
+
>>> e.remove(prefix + "2" + suffix)
|
|
327
|
+
>>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
|
|
328
|
+
... '00000.2.0000000000')
|
|
329
|
+
True
|
|
330
|
+
"""
|
|
331
|
+
# calculate the longest possible string
|
|
332
|
+
maxLength = maxFileNameLength - len(prefix) - len(suffix)
|
|
333
|
+
maxValue = int("9" * maxLength)
|
|
334
|
+
# try to find a number
|
|
335
|
+
finalName = None
|
|
336
|
+
counter = 1
|
|
337
|
+
while finalName is None:
|
|
338
|
+
fullName = prefix + str(counter) + suffix
|
|
339
|
+
if fullName.lower() not in existing:
|
|
340
|
+
finalName = fullName
|
|
341
|
+
break
|
|
342
|
+
else:
|
|
343
|
+
counter += 1
|
|
344
|
+
if counter >= maxValue:
|
|
345
|
+
break
|
|
346
|
+
# raise an error if nothing has been found
|
|
347
|
+
if finalName is None:
|
|
348
|
+
raise NameTranslationError("No unique name could be found.")
|
|
349
|
+
# finished
|
|
350
|
+
return finalName
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
if __name__ == "__main__":
|
|
354
|
+
import doctest
|
|
355
|
+
|
|
356
|
+
doctest.testmod()
|