1from fontParts.base.base import BaseDict, dynamicProperty, reference 2from fontParts.base import normalizers 3from fontParts.base.deprecated import DeprecatedLib, RemovedLib 4 5 6class BaseLib(BaseDict, DeprecatedLib, RemovedLib): 7 8 """ 9 A Lib object. This object normally created as part of a 10 :class:`BaseFont`. An orphan Lib object can be created like this:: 11 12 >>> lib = RLib() 13 14 This object behaves like a Python dictionary. Most of the dictionary 15 functionality comes from :class:`BaseDict`, look at that object for the 16 required environment implementation details. 17 18 Lib uses :func:`normalizers.normalizeLibKey` to normalize the key of 19 the ``dict``, and :func:`normalizers.normalizeLibValue` to normalize the 20 value of the ``dict``. 21 """ 22 23 keyNormalizer = normalizers.normalizeLibKey 24 valueNormalizer = normalizers.normalizeLibValue 25 26 def _reprContents(self): 27 contents = [] 28 if self.glyph is not None: 29 contents.append("in glyph") 30 contents += self.glyph._reprContents() 31 if self.font: 32 contents.append("in font") 33 contents += self.font._reprContents() 34 return contents 35 36 # ------- 37 # Parents 38 # ------- 39 40 # Glyph 41 42 _glyph = None 43 44 glyph = dynamicProperty("glyph", "The lib's parent glyph.") 45 46 def _get_glyph(self): 47 if self._glyph is None: 48 return None 49 return self._glyph() 50 51 def _set_glyph(self, glyph): 52 if self._font is not None: 53 raise AssertionError("font for lib already set") 54 if self._glyph is not None and self._glyph() != glyph: 55 raise AssertionError("glyph for lib already set and is not same as glyph") 56 if glyph is not None: 57 glyph = reference(glyph) 58 self._glyph = glyph 59 60 # Font 61 62 _font = None 63 64 font = dynamicProperty("font", "The lib's parent font.") 65 66 def _get_font(self): 67 if self._font is not None: 68 return self._font() 69 elif self._glyph is not None: 70 return self.glyph.font 71 return None 72 73 def _set_font(self, font): 74 if self._font is not None and self._font() != font: 75 raise AssertionError("font for lib already set and is not same as font") 76 if self._glyph is not None: 77 raise AssertionError("glyph for lib already set") 78 if font is not None: 79 font = reference(font) 80 self._font = font 81 82 # Layer 83 84 layer = dynamicProperty("layer", "The lib's parent layer.") 85 86 def _get_layer(self): 87 if self._glyph is None: 88 return None 89 return self.glyph.layer 90 91 # --------------------- 92 # RoboFab Compatibility 93 # --------------------- 94 95 def remove(self, key): 96 """ 97 Removes a key from the Lib. **key** will be 98 a :ref:`type-string` that is the key to 99 be removed. 100 101 This is a backwards compatibility method. 102 """ 103 del self[key] 104 105 def asDict(self): 106 """ 107 Return the Lib as a ``dict``. 108 109 This is a backwards compatibility method. 110 """ 111 d = {} 112 for k, v in self.items(): 113 d[k] = v 114 return d 115 116 # ------------------- 117 # Inherited Functions 118 # ------------------- 119 120 def __contains__(self, key): 121 """ 122 Tests to see if a lib name is in the Lib. 123 **key** will be a :ref:`type-string`. 124 This returns a ``bool`` indicating if the **key** 125 is in the Lib. :: 126 127 >>> "public.glyphOrder" in font.lib 128 True 129 """ 130 return super(BaseLib, self).__contains__(key) 131 132 def __delitem__(self, key): 133 """ 134 Removes **key** from the Lib. **key** is a :ref:`type-string`.:: 135 136 >>> del font.lib["public.glyphOrder"] 137 """ 138 super(BaseLib, self).__delitem__(key) 139 140 def __getitem__(self, key): 141 """ 142 Returns the contents of the named lib. **key** is a 143 :ref:`type-string`. 144 The returned value will be a ``list`` of the lib contents.:: 145 146 >>> font.lib["public.glyphOrder"] 147 ["A", "B", "C"] 148 149 It is important to understand that any changes to the returned lib 150 contents will not be reflected in the Lib object. If one wants to 151 make a change to the lib contents, one should do the following:: 152 153 >>> lib = font.lib["public.glyphOrder"] 154 >>> lib.remove("A") 155 >>> font.lib["public.glyphOrder"] = lib 156 """ 157 return super(BaseLib, self).__getitem__(key) 158 159 def __iter__(self): 160 """ 161 Iterates through the Lib, giving the key for each iteration. The 162 order that the Lib will iterate though is not fixed nor is it 163 ordered.:: 164 165 >>> for key in font.lib: 166 >>> print key 167 "public.glyphOrder" 168 "org.robofab.scripts.SomeData" 169 "public.postscriptNames" 170 """ 171 return super(BaseLib, self).__iter__() 172 173 def __len__(self): 174 """ 175 Returns the number of keys in Lib as an ``int``.:: 176 177 >>> len(font.lib) 178 5 179 """ 180 return super(BaseLib, self).__len__() 181 182 def __setitem__(self, key, items): 183 """ 184 Sets the **key** to the list of **items**. **key** 185 is the lib name as a :ref:`type-string` and **items** is a 186 ``list`` of items as :ref:`type-string`. 187 188 >>> font.lib["public.glyphOrder"] = ["A", "B", "C"] 189 """ 190 super(BaseLib, self).__setitem__(key, items) 191 192 def clear(self): 193 """ 194 Removes all keys from Lib, 195 resetting the Lib to an empty dictionary. :: 196 197 >>> font.lib.clear() 198 """ 199 super(BaseLib, self).clear() 200 201 def get(self, key, default=None): 202 """ 203 Returns the contents of the named key. 204 **key** is a :ref:`type-string`, and the returned values will 205 either be ``list`` of key contents or ``None`` if no key was 206 found. :: 207 208 >>> font.lib["public.glyphOrder"] 209 ["A", "B", "C"] 210 211 It is important to understand that any changes to the returned key 212 contents will not be reflected in the Lib object. If one wants to 213 make a change to the key contents, one should do the following:: 214 215 >>> lib = font.lib["public.glyphOrder"] 216 >>> lib.remove("A") 217 >>> font.lib["public.glyphOrder"] = lib 218 """ 219 return super(BaseLib, self).get(key, default) 220 221 def items(self): 222 """ 223 Returns a list of ``tuple`` of each key name and key items. 224 Keys are :ref:`type-string` and key members are a ``list`` 225 of :ref:`type-string`. The initial list will be unordered. 226 227 >>> font.lib.items() 228 [("public.glyphOrder", ["A", "B", "C"]), 229 ("public.postscriptNames", {'be': 'uni0431', 'ze': 'uni0437'})] 230 """ 231 return super(BaseLib, self).items() 232 233 def keys(self): 234 """ 235 Returns a ``list`` of all the key names in Lib. This list will be 236 unordered.:: 237 238 >>> font.lib.keys() 239 ["public.glyphOrder", "org.robofab.scripts.SomeData", 240 "public.postscriptNames"] 241 """ 242 return super(BaseLib, self).keys() 243 244 def pop(self, key, default=None): 245 """ 246 Removes the **key** from the Lib and returns the ``list`` of 247 key members. If no key is found, **default** is returned. 248 **key** is a :ref:`type-string`. This must return either 249 **default** or a ``list`` of items as :ref:`type-string`. 250 251 >>> font.lib.pop("public.glyphOrder") 252 ["A", "B", "C"] 253 """ 254 return super(BaseLib, self).pop(key, default) 255 256 def update(self, otherLib): 257 """ 258 Updates the Lib based on **otherLib**. *otherLib** is a 259 ``dict`` of keys. If a key from **otherLib** is in Lib 260 the key members will be replaced by the key members from 261 **otherLib**. If a key from **otherLib** is not in the Lib, 262 it is added to the Lib. If Lib contain a key name that is not 263 in *otherLib**, it is not changed. 264 265 >>> font.lib.update(newLib) 266 """ 267 super(BaseLib, self).update(otherLib) 268 269 def values(self): 270 """ 271 Returns a ``list`` of each named key's members. This will be a list 272 of lists, the key members will be a ``list`` of :ref:`type-string`. 273 The initial list will be unordered. 274 275 >>> font.lib.items() 276 [["A", "B", "C"], {'be': 'uni0431', 'ze': 'uni0437'}] 277 """ 278 return super(BaseLib, self).values() 279