We need to document the existing API and use that as a guide for developing the new API. It will also help us find things that can be removed/deprecated/deleted/forgotten.
Base
All objects have these methods, but they may be dummy methods or they may be application dependent.
# ----------- # Change State # ----------- dirty = obj.changed obj.setChanged(dirty) # maybe setChanged should be a property instead: # obj.changed = dirty # --------------- # Selection State # --------------- sel = obj.selected obj.selected = sel # -------- # Counting # -------- count = len(obj) # ------- # Copying # ------- c = obj.copy(aParent=None) # if aParent is not None, the copied object will have it's setParent method invoked with aParent. # ----- # Round # ----- obj.round() # ------------------------------- # Environment Specific Unwrapping # ------------------------------- v = obj.isRobofab() # what is this actually used for? # (erik) really early robofab / fontlab scripts could deal with both # fl and fab objects. This is no longer the case, method can go. naked = obj.naked() # --------- # Parenting # --------- parent = obj.getParent() obj.setParent(parent) # this parenting method is very broken in UFO 3. # we need to move to a more explicit parenting method that # is much more specific. defcon has this in place. copy it. # ---- # Junk # ---- # get rid of this unless their is a compelling reason to keep it obj._writeXML() d = obj.dump(private=False)
Font
Existing Base Behavior
# ---------------- # File Interaction # ---------------- # open a font font = RFont(path) # however, the RFont constructor in objectsFL takes a FL font object. # we should probably make the constrctor accept a path as well for consistency. font = RFont(nakedFont) # save the font font.save() # close the font (if it has UI) font.close() # ----------- # Sub-Objects # ----------- kerning = font.kerning info = font.info features = font.features groups = font.groups lib = font.lib psHints = font.psHints # ----------------- # Simple Attributes # ----------------- # get the file path path = font.path # ----------------- # Glyph Interaction # ----------------- # glyph names glyphNames = font.keys() # iteration for glyph in font: pass # get a glyph glyph = font["glyphName"] # test for a glyph exists = "glyphName" in font exists = font.has_key("glyphName") # insert a glyph font["glyphName"] = glyph # make a new glyph glyph = font.newGlyph("glyphName") # insert a glyph glyph = font.insertGlyph(glyph, name=None) # remove a glyph font.removeGlyph("glyphName") # ------------- # Misc. Mapping # ------------- # get a mapping of {unicode : [glyphNames]} mapping = font.getCharacterMapping() # get a mapping of {baseGlyph : [glyphsReferencingTheBaseGlyph]} mapping = font.getReverseComponentMapping() # ---------------- # UI Dummy Methods # ---------------- # refresh UI (this is a dummy method that can be used by app specific subclasses) font.update() # ------------------------ # Magic/Scripting Behavior # ------------------------ # compare same = font == otherFont # The comparison functionality should be deprecated unless # there is a compelling reason for extending it to all new sub-objects. # (erik) There were situations in which there were 2 objects representing the same file. I'm not sure how relevant it is now. # (tal) Let's get rid of it an reinstate it if there are problems. Updating it to work with layers is going to be a lot of work. # round all glyph coordinates font.round() # This is suspect. Should it apply this to kerning, info, etc.? # automatic unicode values font.autoUnicodes() # compile a glyph using arbitrary magic font.compileGlyph(self, glyphName, baseName, accentNames, adjustWidth=False, preflight=False, printErrors=True) # I think we should deprecate this. # compile glphs using arbitrary magic defined in a long abandoned text file font.generateGlyph(self, glyphName, replace=1, preflight=False, printErrors=True) # I think we should deprecate this. # (erik) These, or something new and better, have been useful tools to get folks # interested in scripting. But it doesn't have to be this, and it doesn't have to live here. # (tal) The text file that is used by this function hasn't been updated in at least four years: # http://code.robofab.com/log/trunk/Lib/robofab/tools/glyphConstruction.py # And, more likely, it hasn't been updated since I checked it into CVS eight or nine years ago. # We could leave it in, but it's just more stuff for us to maintain. # interpolate the font font.interpolate(self, factor, minFont, maxFont, suppressError=True, analyzeOnly=False, doProgress=False) # ------------------ # Legacy UFO Support # ------------------ # get the glyph name to file name function func = font.getGlyphNameToFileNameFunc()
In some of these, there are references to a progress bar. These automatically import ProgressBar? from robofab.interface.all.dialogs.
How relevant is glyphNameToFileNameFunc these days?
Existing NoneLab? Behavior
Existing FontLab? Behavior
# -------------- # UI Interaction # -------------- # get the names of the glyphs selected in the font window selection = font.selection # set the selected glyphs in the font window font.selection = ["glyphName"] # get the index of the naked FL object index = font.fontIndex # ---------------------- # Ancient OpenType Stuff # ---------------------- # These should be deprecated and, if they are really needed, # they should move to the font.features object classes = font.getOTClasses() font.setOTClasses(classesDict) class = font.getOTClass(name) font.setOTClass(name, glyphList) features = font.getOTFeatures() font.setOTFeatures(featuresDict) feature = font.getOTFeature(name) font.setOTFeature(name, text) # ------ # Guides # ------ # These should be deprecated since there are standard guides now. verticalGuides = font.getVGuides() font.appendVGuide(position, angle=0) font.removeVGuide(guide) font.clearVGuides() horizontalGuides = font.getHGuides() font.appendHGuide(position, angle=0) font.removeHGuide(guide) font.clearHGuides() # ----------------- # Binary Generation # ----------------- font.generate(outputType, path=None) # generate the font. outputType is the type of font to ouput. # --Ouput Types: # 'pctype1' : PC Type 1 font (binary/PFB) # 'pcmm' : PC MultipleMaster font (PFB) # 'pctype1ascii' : PC Type 1 font (ASCII/PFA) # 'pcmmascii' : PC MultipleMaster font (ASCII/PFA) # 'unixascii' : UNIX ASCII font (ASCII/PFA) # 'mactype1' : Mac Type 1 font (generates suitcase and LWFN file) # 'otfcff' : PS OpenType (CFF-based) font (OTF) # 'otfttf' : PC TrueType/TT OpenType font (TTF) # 'macttf' : Mac TrueType font (generates suitcase) # 'macttdfont' : Mac TrueType font (generates suitcase with resources in data fork) # (doc adapted from http://dev.fontlab.net/flpydoc/) # # path can be a directory or a directory file name combo: # path="DirectoryA/DirectoryB" # path="DirectoryA/DirectoryB/MyFontName" # if no path is given, the file will be output in the same directory # as the vfb file. if no file name is given, the filename will be the # vfb file name with the appropriate suffix. # ----------------------- # UFO Reading and Writing # ----------------------- font.writeUFO( path=None, doProgress=False, glyphNameToFileNameFunc=None, doHints=False, doInfo=True, doKerning=True, doGroups=True, doLib=True, doFeatures=True, glyphs=None, formatVersion=2) font.readUFO( path, doProgress=False, doHints=False, doInfo=True, doKerning=True, doGroups=True, doLib=True, doFeatures=True, glyphs=None) # ---------- # Odd Things # ---------- # get the file name. # this should be deprecated. fileName = font.fileName # get a list of glyphs. # what is the point of this? # (erik) No idea. glyphs = font.glyphs
Existing RoboFont? Behavior
# put any API that is unique to RoboFont or different from the base API here
UFO 3 Needs
# get layer order # set layer order # should guides be here or in the info? # the generateGLyph, compileGLyph and interpolate methods have been moved to BaseLayer, # bu they need to be reworked to deal with layers. not sure how that is going to be done.
Layers (New in UFO 3)
Layers are complicated in the file structure. defcon reflects the file structure because that's the general design of defcon. However, it may not be the most scripting fiendly API. This is how it works:
""" Layers are managed by a font level, list-like layers object. """ # make a layer layer = font.layers.newLayer(layerName) # get a layer layer = font.layers[layerName] # if layerName is None, this retrieves the default layer # remove a layer layers.removeLayer(layerName) # get a list of all layer names layerNames = layers.keys() # get the layer order layerOrder = font.layers.getLayerOrder() # set the layer order font.layers.setLayerOrder(layerOrder) # get the default layer layerName = font.layers.getDefaultLayer() # set the default layer font.layers.setDefaultLayerName(layerName) """ Individual layers have an API that is similar to the existing font API. """ # new glyph glyph = layer.newGlyph(glyphName) # get a glyph glyph = layer[glyphName] # remove a glyph layer.removeGlyph(glyphName) # get a list of all glyphs glyphNames = layer.keys() # etc. # get the layer lib lib = layer.lib # get the layer color color = layer.color # set the layer color layer.color = color
We could abstract this in the RoboFab API so that layers are dealt with at the glyph level. But, the layer info properties (color, lib) are going to require some sort of higher level layer object.
Glyph (UFO 3 Changes)
# ----- # Image # ----- # images are going to be a bit tricky. glifLib expects the image attribute # to return a dict object or an object that returns 0 for __len__. image = glyph.image glyph.image = None glyph.image = image # ---------- # Guidelines # ---------- glyph.appendGuideline(guideline) guidelines = glyph.guidelines # ---------- # Mark Color # ---------- # this is a convenience wrapper around the public.markColor lib entry. # in these cases, color is a color object as described below. color = glyph.markColor glyph.markColor = color # we have an existing mark attribute in objectsFL. # it uses a 0-255 int. we need to deprecate the # mark attribute and reroute anything set to it # to markColor. Why not just use mark and quietly # coerse the incoming value to the apprpriate format? # that would work for values coming in, but not # values going out. # ----------- # Identifiers # ----------- # There should probably be an attribute that returns the used attributes # in the glyph.
Image (New in UFO 3)
This needs to be a dict like object that has the required keys as defined in the UFO spec.
Contour (UFO 3 Changes)
identifier = contour.identifier contour.identifier = identifier
Component (UFO 3 Changes)
identifier = component.identifier component.identifier = identifier
Point (UFO 3 Changes)
identifier = point.identifier point.identifier = identifier
Anchor (New Structure in UFO 3)
This needs to be a dict like object that has the required keys as defined in the UFO spec.
Guideline (New in UFO 3)
This needs to be a dict like object that has the required keys as defined in the UFO spec.
# modify the guideline's list position i = guideline.index guideline.index = i
Color (New in UFO 3)
Colors are tricky. glifLib and ufoLib use a string format for reading and writing colors. This is very nice from a data storage standpoint, but for scripting it's not ideal. We don't want everyone to write color parsers. In defcon I made a special string subclass that follows the UFO color format, but offered some nicer API:
r, g, b, a = color r = color.r g = color.g b = color.b a = color.a
Additionally, color input can come in with the proper UFO syntax or as a sequence of numbers:
layer.color = "1,0,0,1" layer.color = (1, 0, 0, 1)
We should probably do something similar in RoboFab.
psHints
Not sure where to add this. The font level psHint data should all be available from the font.info so it might not be necessary to keep it here as well. It would be nice to be able to store and access glyph level ps hint data in some form. It is not esoteric.
