Just van Rossum feb0046176 Moved Data, Lib and install.py one level down the tree
git-svn-id: http://svn.robofab.com/trunk@67 b5fa9d6c-a76f-4ffd-b3cb-f825fc41095c
2008-03-11 09:18:32 +00:00

359 lines
9.6 KiB
Python
Executable File

"""This module has been deprecated."""
from warnings import warn
warn("family.py is deprecated.", DeprecationWarning)
"""
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
W A R N I N G
This is work in progress, a fast moving target.
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
"""
import os
from robofab import RoboFabError
from robofab.plistlib import readPlist, writePlist
from robofab.objects.objectsRF import RInfo, RFont, RLib, RGlyph, OpenFont
from robofab.ufoLib import UFOReader, writePlistAtomically, fontInfoAttrs
from robofab.objects.objectsBase import RBaseObject, BaseGroups
from robofab.glifLib import GlyphSet
import weakref
"""
RoboFab family planning
A font family is a group of UFO fonts that are related when
- they share a common source
- they are masters in various interpolations
- they are weights in a section of design space
Some family infrastructure is needed because there is some
information that transcends font or glyph. It is also a tool
to track fonts which are part of a larger structure, make sure
all resources needed for a project are present, up to date etc.
Structure:
MyFamilyProjectName.uff/
# the file and folder structure of the extended UFO family.
lib.plist
# a lib for the family
fonts/
# a folder with ufo's
masterA.ufo/
masterB.ufo/
# any number of fonts
...
contents.plist
# a contents.plist to track resources
shared/
# a .ufo containing capable of containing all
# data that a .ufo can contain. this data is
# shared with all members of the family
metainfo.plist
fontinfo.plist
lib.plist
groups.plist
kerning.plist
glyphs/
# any number of common or sharable glifs.
foundryLogo.glif
genericStuff.glif
# location for inbetween masters, interpolation exceptions:
# glyphs that don't fit in any specific font
a_lightBold_028.glif
a_lightBold_102.glif
a_lightBold_103.glif
...
contents.plist
"""
FONTS_DIRNAME = 'fonts'
FONTSCONTENTS_FILENAME = "contents.plist"
METAINFO_FILENAME = 'metainfo.plist'
LIB_FILENAME = 'lib.plist'
FAMILY_EXTENSION = ".uff"
FONT_EXTENSION = ".ufo"
SHARED_DIRNAME = "shared"
def makeUFFName(familyName):
return ''.join([familyName, FAMILY_EXTENSION])
def _scanContentsDirectory(path, forceRebuild=False):
contentsPath = os.path.join(path, FONTSCONTENTS_FILENAME)
if forceRebuild or not os.path.exists(contentsPath):
ext = FONT_EXTENSION
fileNames = os.listdir(path)
fileNames = [n for n in fileNames if n.endswith(ext)]
contents = {}
for n in fileNames:
contents[n[:-len(ext)]] = n
else:
contents = readPlist(contentsPath)
return contents
class FamilyReader(object):
"""A reader that reads all info from a .uff"""
def __init__(self, path):
self._path = path
def _checkForFile(self, path):
if not os.path.exists(path):
return False
else:
return True
def readMetaInfo(self):
path = os.path.join(self._path, METAINFO_FILENAME)
if not self._checkForFile(path):
return
def readLib(self):
path = os.path.join(self._path, LIB_FILENAME)
if not self._checkForFile(path):
return {}
return readPlist(path)
def readFontsContents(self):
contentsPath = os.path.join(self._path, FONTS_DIRNAME)
contents = _scanContentsDirectory(contentsPath)
return contents
def getSharedPath(self):
"""Return the path of all shared values in the family,
rather then create a new instance for it."""
return os.path.join(self._path, SHARED_DIRNAME)
class FamilyWriter(object):
"""a writer that builds all the necessary family stuff."""
fileCreator = 'org.robofab.uffLib'
formatVersion = 1
def __init__(self, path):
self._path = path
def _makeDirectory(self, subDirectory=None):
path = self._path
if subDirectory:
path = os.path.join(self._path, subDirectory)
if not os.path.exists(path):
os.makedirs(path)
if not os.path.exists(os.path.join(path, METAINFO_FILENAME)):
self._writeMetaInfo()
return path
def _writeMetaInfo(self):
path = os.path.join(self._path, METAINFO_FILENAME)
metaInfo = {
'creator': self.fileCreator,
'formatVersion': self.formatVersion,
}
writePlistAtomically(metaInfo, path)
def writeLib(self, libDict):
self._makeDirectory()
path = os.path.join(self._path, LIB_FILENAME)
if libDict:
writePlistAtomically(libDict, path)
elif os.path.exists(path):
os.remove(path)
def writeFontsContents(self):
path = self.makeFontsPath()
contents = _scanContentsDirectory(path)
contentsPath = os.path.join(path, FONTSCONTENTS_FILENAME)
writePlistAtomically(contents, contentsPath)
def makeFontsPath(self):
fontDir = self._makeDirectory(FONTS_DIRNAME)
return fontDir
def makeSharedPath(self):
sharedDir = self._makeDirectory(SHARED_DIRNAME)
return sharedDir
def getSharedGlyphSet(self):
path = self.makeSharedGlyphsPath()
return GlyphSet(path)
class RFamily(RBaseObject):
"""
Sketch for Family, the font superstructure.
This should ultimately move to objectsRF
The shared fontinfo and glyphset is just another font, named 'shared',
this avoids duplication of a lot of functionality in maintaining
the shared glyphset, reading, writing etc.
"""
def __init__(self, path=None):
self._path = path
self.info = RInfo() # this should go away. it is part of shared.info.
self.lib = RLib()
self.shared = RFont()
self._fontsContents = {} # font name: path
self._fonts = {} # fontName: object
self.lib.setParent(self)
if self._path:
self._loadData()
def __repr__(self):
if self.info.familyName:
name = self.info.familyName
else:
name = 'UnnamedFamily'
return "<RFont family for %s>" %(name)
def __len__(self):
return len(self._fontsContents.keys())
def __getitem__(self, fontKey):
if self._fontsContents.has_key(fontKey):
if not self._fonts.has_key(fontKey):
fontPath = os.path.join(self._path, FONTS_DIRNAME, self._fontsContents[fontKey])
font = RFont(fontPath)
font.setParent(self)
self._fonts[fontKey] = font
# uh, is returning a proxy the right thing to do here?
return weakref.proxy(self._fonts[fontKey])
raise IndexError
def __setitem__(self, key, fontObject):
if not key:
key = 'None'
key = self._makeKey(key)
self._fontsContents[key] = None
self._fonts[key] = fontObject
fontObject._path = None
def keys(self):
return self._fontsContents.keys()
def has_key(self, key):
return self._fontsContents.has_key(key)
__contains__ = has_key
def _loadData(self):
fr = FamilyReader(self._path)
self._fontsContents = fr.readFontsContents()
self.shared = RFont(fr.getSharedPath())
self.lib.update(fr.readLib())
def _hasChanged(self):
#mark the object as changed
self.setChanged(True)
def _makeKey(self, key):
# add a numerical extension to the key if it already exists
if self._fontsContents.has_key(key):
if key[-2] == '.':
try:
key = key[:-1] + `int(key[-1]) + 1`
except ValueError:
key = key + '.1'
else:
key = key + 1
self_makeKey(key)
return key
def save(self, destDir=None, doProgress=False):
if not destDir:
saveAs = False
destDir = self._path
else:
saveAs = True
fw = FamilyWriter(destDir)
for fontName, fontPath in self._fontsContents.items():
if saveAs and not self._fonts.has_key(fontName):
font = self[fontName]
if self._fonts.has_key(fontName):
if not fontPath or saveAs:
fontPath = os.path.join(path, fw.makeFontsPath(), ''.join([fontName, FONT_EXTENSION]))
self._fontsContents[fontName] = fontPath
self._fonts[fontName].save(fontPath, doProgress=False)
fw.writeFontsContents()
fw.writeLib(self.lib)
sharedPath = fw.makeSharedPath()
self.shared.save(sharedPath, doProgress=False)
self._path = destDir
#def sharedGlyphNames(self):
# """a list of all shared glyphs"""
# keys = self.sharedGlyphs.keys()
# if self.sharedGlyphSet is not None:
# keys.extend(self.sharedGlyphSet.keys())
# d = dict.fromkeys(keys)
# return d.keys()
#
#def getGlyph(self, glyphName, fontName=None):
# """retrieve a glyph from fontName, or from shared if no font is given."""
# if fontName is None or fontName =="shared":
# # ask for a shared glyph
# return self.shared[glyphName]
# if self.has_key(fontName):
# return self[fontName].getGlyph(glyphName)
# return None
#
#def newGlyph(self, glyphName):
# """add a new shared glyph"""
# return self.shared.newGlyph(glyphName)
#
#def removeGlyph(self, glyphName):
# """remove a shared glyph"""
# self.shared.removeGlyph(glyphName)
if __name__ == "__main__":
from robofab.world import OpenFont
from robofab.interface.all.dialogs import GetFolder
from PyBrowser import Browser
#open and test
font = OpenFont()
family = RFamily()
family['aFont'] = font
family.lib['org.robofab.uffLibTest'] = 'TestOneTwo!'
family.shared.info.familyName = 'ThisIsAFamilyName'
family.shared.newGlyph('xxx')
path = GetFolder('where do you want to store this new .uff?')
path = os.path.join(path, makeUFFName('MyBigFamily'))
family.save(path)
#family = RFamily(path)
#Browser(family.getGlyph('ASharedGlyph_Yay'))
## save as test
#path = GetFolder('select a .uff directory')
#family = RFamily(path)
#family.newGlyph('xxx')
#family.name = 'YeOldFamily'
#newPath = os.path.join(os.path.split(path)[0], 'xxx'+os.path.split(path)[1])
#family.save(newPath)