1import defcon
2from babelfont.font import Font
3from babelfont.layer import Layer
4from babelfont.lib import Lib
5from babelfont.glyph import Glyph
6from babelfont.point import Point
7from babelfont.features import Features
8from babelfont.contour import Contour
9from babelfont.component import Component
10from babelfont.anchor import Anchor
11from copy import copy
12
13
14def can_load(filename):
15    return filename.endswith(".ufo")
16
17def can_save(filename):
18    return filename.endswith(".ufo")
19
20
21def load(filename, **kwargs):
22    dcfont = defcon.Font(filename)
23    return _load_dcfont(dcfont)
24
25def save(font, filename):
26    dcfont = _save_dcfont(font)
27    dcfont.save(filename)
28
29# defcon -> babelfont
30
31
32def _load_dcfont(dcf):
33    bbf = Font(dcf)
34
35    # XXX Create: features
36    for k,v in dcf.info.getDataForSerialization().items():
37        bbf.info._setAttr(k, copy(v))
38    for k,v in dcf.lib.items():
39        bbf.lib._setItem(k, copy(v))
40    for dclayer in dcf.layers:
41        bblayer = _load_dclayer(dclayer)
42        bblayer.font = bbf
43        bbf._layers.append(bblayer)
44        bbf._layerOrder.append(bblayer.name)
45    for name, contents in dcf.groups.items():
46        bbf.groups[name] = contents
47    bbf.lib.glyphOrder = list(dcf.layers[None].keys())
48    _copy_kerning(dcf.kerning, bbf.kerning)
49    if dcf.features and dcf.features.text:
50        bbf.features.text = dcf.features.text
51    return bbf
52
53
54def _load_dclayer(dclayer):  # -> Layer
55    bblayer = Layer()
56    for k,v in dclayer.lib.items():
57        bblayer.lib._setItem(k, copy(v))
58    if dclayer.color:
59        bblayer.color = tuple(v for v in dclayer.color)
60    bblayer.name = dclayer.name
61    for k in dclayer.keys():
62        bblayer._glyphs[k] = _load_dcglyph(dclayer[k], bblayer)
63    return bblayer
64
65def _load_dcglyph(dcglyph, layer):
66    glyph = Glyph()
67    glyph._layer = layer
68    glyph._lib = Lib()
69    glyph._lib.glyph = glyph
70    for k,v in dcglyph.lib.items():
71        glyph._lib._setItem(k, copy(v))
72    glyph._name = dcglyph.name
73    glyph._unicodes = copy(dcglyph.unicodes)
74    glyph._width = dcglyph.width
75    glyph._height = dcglyph.height
76    # components, anchors, guidelines, image
77    glyph._components = [_load_dccomponent(c, glyph) for c in dcglyph.components]
78    glyph._contours = [_load_dccontour(p, glyph) for p in dcglyph]
79    glyph._anchors = [_load_dcanchor(a, glyph) for a in dcglyph.anchors]
80    return glyph
81
82
83def _load_dccontour(dccontour, glyph):
84    contour = Contour()
85    contour._glyph = glyph
86    contour._points = [_load_dcpoint(p, contour) for p in dccontour]
87    contour.clockwise = dccontour.clockwise
88    return contour
89
90
91def _load_dccomponent(dccomponent, glyph):
92    component = Component()
93    component._glyph = glyph
94    component._baseGlyph = dccomponent.baseGlyph
95    component.transformation = dccomponent.transformation
96    # XXX
97    return component
98
99
100def _load_dcanchor(dcanchor, glyph):
101    anchor = Anchor()
102    anchor._glyph = glyph
103    anchor.name = dcanchor.name
104    anchor.color = dcanchor.color
105    anchor.x = dcanchor.x
106    anchor.y = dcanchor.y
107    return anchor
108
109
110def _load_dcpoint(dcpoint, contour):
111    point = Point()
112    point._contour = contour
113    point._x = dcpoint.x
114    point._y = dcpoint.y
115    if not dcpoint.segmentType:
116        point.type = "offcurve"
117    else:
118        point.type = dcpoint.segmentType
119    point.smooth = dcpoint.smooth
120    return point
121
122def _copy_kerning(inkerning, outkerning):
123    for pair, value in inkerning.items():
124        outkerning[pair] = value
125
126# # babelfont -> defcon
127
128
129def _save_point(point):
130    if point.type == "offcurve":
131        dctype = None
132    else:
133        dctype = point.type
134    return defcon.Point ((point.x, point.y), dctype, point.smooth)
135
136
137def _save_contour(contour):
138    path = defcon.Contour()
139    for p in contour._points:
140        path.appendPoint(_save_point(p))
141    path.clockwise = contour.clockwise
142    return path
143
144
145def _save_component(component):
146    c = defcon.Component(None)
147    c.baseGlyph = component.baseGlyph
148    c.transformation = component.transformation
149    # XXX
150    return c
151
152def _save_anchor(anchor):
153    dcanchor = defcon.Anchor()
154    dcanchor.color = anchor.color
155    dcanchor.name = anchor.name
156    dcanchor.x = anchor.x
157    dcanchor.y = anchor.y
158    return dcanchor
159
160def _save_glyph(glyph):
161    dcglyph = defcon.Glyph()
162    dcglyph.unicodes = copy(glyph._unicodes)
163    for c in glyph.contours:
164        dccontour = _save_contour(c)
165        dcglyph.appendContour(dccontour)
166    # Anchors
167    for a in glyph.anchors:
168        dcglyph.appendAnchor(_save_anchor(a))
169
170    for k,v in glyph.lib._dict.items():
171        if k == "glyph":
172            continue
173        dcglyph.lib[k] = copy(v)
174    dcglyph.rightMargin = glyph.rightMargin
175    dcglyph.leftMargin = glyph.leftMargin
176    dcglyph.width = glyph.width
177    dcglyph.name = glyph.name
178    return dcglyph
179
180def _save_layer(layer, dclayer):
181    for g in layer:
182        dclayer.insertGlyph(_save_glyph(g))
183    # Handle components after all glyphs are in
184    for glyph in layer:
185        dcglyph = dclayer[glyph.name]
186        for c in glyph.components:
187            dcglyph.appendComponent(_save_component(c))
188
189    dclayer.color = layer.color
190
191def _save_dcfont(font):
192    dcf = defcon.Font()
193    for k,v in font.lib._dict.items():
194        dcf.lib[k] = copy(v)
195    dcf.info.setDataFromSerialization(font.info._dict)
196    if len(font.layers) == 1:
197        # Just put it in public.default
198        _save_layer(font.defaultLayer, dcf.layers.defaultLayer)
199    else:
200        for l in dcf.layers.layerOrder:
201            del(dcf.layers[l])
202
203        for l in font.layers:
204            newLayer = dcf.newLayer(l.name)
205            _save_layer(l, newLayer)
206    for name, contents in font.groups.items():
207        dcf.groups[name] = contents
208    _copy_kerning(font.kerning, dcf.kerning)
209    if font.features and font.features.text:
210        dcf.features.text = font.features.text
211    return dcf
212
213# # Random stuff
214
215# def _glyphs_date_to_ufo(d):
216#     return d.strftime('%Y/%m/%d %H:%M:%S')
217
218# def _ufo_date_to_glyphs(d):
219#     return datetime.strptime(d, '%Y/%m/%d %H:%M:%S').strftime('%Y-%m-%d %H:%M:%S +0000')
220