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.