22
33'''
44GOADB writer -- write a GlyphOrderAndAliasDB file using a UFO as an input.
5- Relies on the agl glyph names which come in fontTools’ agl module.
65
76More reading on the GOADB format:
87https://github.com/adobe-type-tools/afdko/issues/1662
1312
1413import argparse
1514import re
15+ from afdko import agd , fdkutils
1616from pathlib import Path
17- from fontTools import agl
1817from defcon import Font , Glyph
1918
2019
@@ -56,6 +55,68 @@ def get_args(args=None):
5655 return parser .parse_args (args )
5756
5857
58+ def _load_agd_data ():
59+ '''
60+ read and load the AGD.txt file
61+ '''
62+ agd_txt = Path (fdkutils .get_resources_dir ()) / 'AGD.txt'
63+
64+ with open (agd_txt , "r" ) as agd_blob :
65+ agd_data = agd_blob .read ()
66+
67+ # This object is called “dictionary”, but it doesn’t have dict methods.
68+ # However, it contains two dicts -- .glyphs and .unicode.
69+ return agd .dictionary (agd_data )
70+
71+
72+ def _make_agd_dict ():
73+ '''
74+ AGD glyph name mapped to final name (often equivalent to the AGD name),
75+ and codepoint.
76+
77+ Mappings to private us codepoints (and mappings to esoteric final names)
78+ are deliberately omitted.
79+ '''
80+ agd_data = _load_agd_data ()
81+ rx_uni_name = r'(?:u|uni)?([0-9A-F]{4,5}).*' # ?: is non-capturing
82+ agd_name_dict = {}
83+
84+ private_use = (
85+ set (range (0xe000 , 0xf8ff + 1 )) |
86+ set (range (0xf0000 , 0xffffd + 1 )) |
87+ set (range (0x100000 , 0x10fffd + 1 )))
88+
89+ for gname , agdglyph in agd_data .glyphs .items ():
90+ if agdglyph .uni and not agdglyph .fin :
91+ gname_final = gname
92+ # The friendly name is equivalent to the final name.
93+ # makeotf will know which code point to assign
94+ # based on the name alone
95+ codepoint = int (agdglyph .uni , 16 )
96+ # The AGD contains a number of private_use code points.
97+ # Those are outdated, we do not need to consider them.
98+ if codepoint not in private_use :
99+ agd_name_dict [gname ] = gname_final , codepoint
100+
101+ elif agdglyph .uni and agdglyph .fin :
102+ gname_final = agdglyph .fin
103+ # makeotf knows that a specific name is associated with a certain
104+ # code point, but comparefamily complains about a “working name”
105+ # being assigned. This includes florin, for example.
106+ uni_match = re .match (rx_uni_name , agdglyph .fin )
107+ if uni_match :
108+ codepoint = int (uni_match .group (1 ), 16 )
109+ if codepoint not in private_use :
110+ agd_name_dict [gname ] = gname_final , codepoint
111+
112+ return agd_name_dict
113+
114+
115+ # inspired by
116+ # https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/agl.py#L5107
117+ AGD_DICT = _make_agd_dict ()
118+
119+
59120def get_glyph_order (f , include_template_glyphs = False ):
60121 '''
61122 Figure out the glyph order.
@@ -114,7 +175,8 @@ def get_glyph_order(f, include_template_glyphs=False):
114175 # then by the order of related names of encoded glyphs.
115176 gnames_alternates = sorted (
116177 [g .name for g in glyphs_alternates ],
117- key = lambda gn : (gn .split ('.' )[1 ], gnames_encoded .index (gn .split ('.' )[0 ])))
178+ key = lambda gn :
179+ (gn .split ('.' )[1 ], gnames_encoded .index (gn .split ('.' )[0 ])))
118180
119181 # glyphs not related to encoded glyphs are sorted alphabetically
120182 glyphs_rest = [
@@ -126,10 +188,6 @@ def get_glyph_order(f, include_template_glyphs=False):
126188 return order
127189
128190
129- def is_agl_name (input_name ):
130- return input_name in agl .AGL2UV .keys ()
131-
132-
133191def get_uni_name (cp ):
134192 '''
135193 convert codepoint to uniXXXX (or uXXXXX) glyph name
@@ -175,7 +233,7 @@ def make_unique_final_name(gname):
175233
176234def sanitize_final_name (gname ):
177235 '''
178- The following characters are allowed in friendly but not in final names:
236+ The following characters are allowed in friendly- but not final names:
179237 U+002A * asterisk
180238 U+002B + plus sign
181239 U+002D - hyphen-minus
@@ -253,27 +311,30 @@ def __init__(self, gn_friendly, g=None, gn_final=None, cp_override=None):
253311 # outside, and use this object for data storage only.
254312
255313 def assign_final_and_cp_override (self ):
314+ is_agd_name = self .gn_friendly in AGD_DICT .keys ()
256315 rx_uni_name = r'(?:u|uni)?([0-9A-F]{4,5}).*' # ?: is non-capturing
257316 uni_name_match = re .match (rx_uni_name , self .gn_friendly )
258317
259- # glyph name is in AGL
260- if is_agl_name (self .gn_friendly ):
318+ # glyph name is in AGD
319+ if is_agd_name :
320+ agd_final , agd_cp = AGD_DICT .get (self .gn_friendly )
261321 if self .glyph .unicodes == []:
262322 # no codepoint assigned to glyph, codepoint will be assigned
263- # through the glyph name only
264- self .gn_final = self .gn_friendly
323+ # through the glyph name only (or, in some cases, the AGD
324+ # dict has a different final name)
325+ self .gn_final = agd_final
265326 elif len (self .glyph .unicodes ) > 1 :
266- # glyph name is in AGL , but multiple code points attached;
327+ # glyph name is in AGD , but multiple code points attached;
267328 # override is needed
268329 self .gn_final = self .gn_friendly
269330 self .cp_override = get_uni_override (self .glyph .unicodes )
270331 else :
271332 # just one codepoint
272- expected_codepoint = agl . AGL2UV . get ( self . gn_friendly )
333+ expected_codepoint = agd_cp
273334 actual_codepoint = self .glyph .unicode
274335 if expected_codepoint == actual_codepoint :
275336 # codepoint is the expected one.
276- self .gn_final = self . gn_friendly
337+ self .gn_final = agd_final
277338 else :
278339 # codepoint is different from what we expect
279340 self .gn_final = get_uni_name (self .glyph .unicode )
@@ -333,7 +394,12 @@ def fill_gn_dict(gb, glyph_name_dict):
333394
334395
335396def get_glyph (f , gname ):
397+ '''
398+ make sure a glyph object is present -- no matter if it exists in the UFO
399+ or not.
400+ '''
336401 try :
402+ # glyph exists in the UFO
337403 glyph = f [gname ]
338404 except KeyError :
339405 # template glyph
0 commit comments