root/packages/fontAppTools/trunk/Lib/fontAppTools.py

Revision 86, 3.3 kB (checked in by tal, 1 year ago)
Properly handle lists of glyphs names mapped to a single Unicode value.
Line 
1 def characterToGlyphName(c, cmap):
2     try:
3         c = unicode(c)
4         v = ord(c)
5         v = cmap.get(v)
6         if isinstance(v, list):
7             v = v[0]
8         return v
9     except UnicodeDecodeError:
10         return None
11
12 def splitText(text, cmap, fallback=".notdef"):
13     """
14     Break a string of characters or / delimited glyph names
15     into a list.
16
17     - Test name compiling
18     >>> splitText("/a", {})
19     ['a']
20     >>> splitText("/aacute/bbreve", {})
21     ['aacute', 'bbreve']
22     >>> splitText("/aacute /bbreve", {})
23     ['aacute', 'bbreve']
24
25     - Test character input
26     >>> splitText("*.", {})
27     ['.notdef', '.notdef']
28     >>> splitText("*.", {42:"asterisk", 46:"period"})
29     ['asterisk', 'period']
30
31     - Test slash escaping
32     >>> splitText("//", {})
33     ['slash']
34     >>> splitText("///", {})
35     ['slash']
36     >>> splitText("////", {})
37     ['slash', 'slash']
38     >>> splitText("/ /", {})
39     []
40     >>> splitText("/ /", {})
41     []
42     >>> splitText("1//2", {49:"one", 50:"two"})
43     ['one', 'slash', 'two']
44
45     - Test mixture
46     >>> splitText("*/aacute .%//", {42:"asterisk", 46:"period"})
47     ['asterisk', 'aacute', 'period', '.notdef', 'slash']
48     """
49     # escape //
50     text = text.replace("//", "/slash ")
51     #
52     glyphNames = []
53     compileStack = None
54     for c in text:
55         # start a glyph name compile.
56         if c == "/":
57             # finishing a previous compile.
58             if compileStack is not None:
59                 # only add the compile if something has been added to the stack.
60                 if compileStack:
61                     glyphNames.append("".join(compileStack))
62             # reset the stack.
63             compileStack = []
64         # adding to or ending a glyph name compile.
65         elif compileStack is not None:
66             # space. conclude the glyph name compile.
67             if c == " ":
68                 # only add the compile if something has been added to the stack.
69                 if compileStack:
70                     glyphNames.append("".join(compileStack))
71                 compileStack = None
72             # add the character to the stack.
73             else:
74                 compileStack.append(c)
75         # adding a character that needs to be converted to a glyph name.
76         else:
77             glyphName = characterToGlyphName(c, cmap)
78             if glyphName is None:
79                 glyphName = fallback
80             glyphNames.append(glyphName)
81     # catch remaining compile.
82     if compileStack is not None and compileStack:
83         glyphNames.append("".join(compileStack))
84     return glyphNames
85
86 def hyperCMAP(font):
87     """
88     Very quickly extract a CMAP style dict from a UFO.
89     """
90     import re
91     import os
92     unicode_RE = re.compile(
93             "<unicode\s+hex\s*=\s*[\"\"]"
94             "(\w*)"
95             "[\"\"]\s*/\s*>"
96             )
97     namesAndUnicodes = []
98     glyphsPath = os.path.join(font.path, "glyphs")
99     for glyphName, fileName in font._glyphSet.contents.items():
100         glyphPath = os.path.join(glyphsPath, fileName)
101         f = open(glyphPath, "rb")
102         data = f.read()
103         f.close()
104         unicodes = unicode_RE.findall(data)
105         if unicodes:
106             firstValue = int(unicodes[0], 16)
107         else:
108             firstValue = None
109         namesAndUnicodes.append((glyphName, firstValue))
110     return namesAndUnicodes
111
112
113 if __name__ =="__main__":
114     import doctest
115     doctest.testmod()
Note: See TracBrowser for help on using the browser.