summaryrefslogtreecommitdiff
path: root/misc/pylib/robofab/tools/toolsFL.py
blob: c8aff19a1c259d73463c4634e31fb245c181f748 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
"""
T.O.O.L.S.: Things Other Objects Lack (Sometimes)
-assorted raw tools.

This is an assorted colection of raw tools that do
things inside of FontLab. Many of these functions
form the bedrock of objectsFL. In short, use these
tools only if you need the raw functions and they are
not supported by the objects.

Object model:
Most of these tools were written before
objectsFL. Some of these tools are used by
objectsFL. That means that if you want to
use functions from robofab.tools you can always
feed them FontLab objects (like Font, Glyps,
etc.). If the functions also accept Robjects from
robofab.objects it is usually mentioned in the
doc string.

This is a simple way to convert a robofab Font
object back to a FL Font object. Even if you don't
know which particular faith an object belongs to
you can use this:

font = unwrapFont(font)
"""


from FL import *
from warnings import warn

try:
	from fl_cmd import *
except ImportError:
	print "The fl_cmd module is not available here. toolsFL.py"
	
import os

from robofab import RoboFabError

# local encoding
if os.name == "mac":
	LOCAL_ENCODING = "macroman"
else:
	LOCAL_ENCODING = "latin-1"
	
	
#
#
#
#	stuff for fontlab app
#
#
#

def AppFolderRenamer():
	"""This function will rename the folder that contains the
	FontLab application to a more specific name that includes
	the version of the application
	Warning: it messes with the paths of your app, if you have
	items that hardwired to this path you'd be in trouble.
	"""
	if fl.count > 0:
		warn("Close all fonts before running AppFolderRenamer")
		return
	old = fl.path[:-1]
	root = os.path.dirname(old)
	new = "FontLab " + fl.version.replace('/', '_')
	path = os.path.join(root, new)
	if path != old:
		try:
			os.rename(old, path)
		except OSError:
			pass
		warn("Please quit and restart FontLab")
			
#
#
#
#	stuff for fonts
#
#
#

def GetFont(full_name):
	"""Return fontobjects which match full_name.
	Note: result is a list.
	Returns: a list of FL Font objects
	"""
	found = []
	for f in AllFonts():
		if f.full_name == full_name:
			found.append(f)
	return found

def AllFonts():
	"""Collect a list of all open fonts.
	Returns: a list of FL Font objects.
	"""
	fontcount = len(fl)
	af = []
	for i in range(fontcount):
		af.append(fl[i])
	return af

def FontIndex(font):
	"""return the index of a specified FL Font"""
	font = unwrapFont(font)
	a = AllFonts()
	p = []
	for f in a:
		p.append(f.file_name)
	if font.file_name in p:
		return p.index(font.file_name)
	else:
		return None
		
def unwrapFont(font):
	"""Unwrap the font if it happens to be a RoboFab Font"""
	if hasattr(font, 'isRobofab'):
		return font.naked()
	return font

def MakeTempFont(font, dupemark=None, removeOverlap=True, decompose=True):
	"""Save the current FL Font,
	- close the file,
	- duplicate the file in the finder (icon looks weird, but it works)
	- open the duplicate
	- decompose the glyphs
	- remove overlaps
	- return the fontobject

	font  is either a FL Font or RF RFont object.
	
	Problems: doesn't check if the filename is getting too long.
	Note: it will overwrite older files with the same name.
	"""	
	import string
	f = unwrapFont(font)
	if not dupemark or dupemark == "":
		dupemark = "_tmp_"
	path = f.file_name
	a = f.file_name.split('.')
	a.insert(len(a)-1, dupemark)
	newpath = string.join(a, '.')
	f.Save(path)
	fl.Close(FontIndex(f))
	file = open(path, 'rb')
	data = file.read()
	file.close()
	file = open(newpath, 'wb')
	file.write(data)
	file.close()
	fl.Open(newpath, 1)
	nf = fl.font
	if nf is None:
		print 'uh oh, sup?'
		return None
	else:
		for g in nf.glyphs:
			if decompose:
				g.Decompose()
			if removeOverlap:
				g.RemoveOverlap()
		return nf

def makePSFontName(name):
	"""Create a postscript filename out of a regular postscript fontname,
	using the old fashioned macintosh 5:3:3 convention.
	"""
	import string
	parts = []
	current = []
	final = []
	notAllowed = '-_+=,-'
	index = 0
	for c in name:
		if c in notAllowed:
			continue
		if c in string.uppercase or index == 0:
			c = string.upper(c)
			if current:
				parts.append("".join(current))
			current = [c]
		else:
			current.append(c)
		index = index + 1
	if current:
		parts.append("".join(current))
	final.append(parts[0][:5])
	for p in parts[1:]:
		final.append(p[:3])
	return "".join(final)

#
#
#
#	stuff for glyphs
#
#
#

def NewGlyph(font, glyphName, clear=False, updateFont=True):
	"""Make a new glyph if it doesn't already exist, return the glyph.
	font is either a FL Font or RF RFont object. If updateFont is True
	the (very slow) fl.UpdateFont function will be called.
	"""
	font = unwrapFont(font)
	if isinstance(glyphName, unicode):
		glyphName = glyphName.encode(LOCAL_ENCODING)
	glyph = font[glyphName]
	if glyph is None:
		new = Glyph()
		new.name = glyphName
		font.glyphs.append(new)
		if updateFont:
			fl.UpdateFont(FontIndex(font))
		glyph = font[glyphName]
	elif clear:
		glyph.Clear()
		glyph.anchors.clean()
		glyph.components.clean()
		glyph.note = ""
	return glyph


def AddToAlias(additions, sep='+'):
	"""additions is a dict with glyphnames as keys
	and glyphConstruction as values. In order to make
	a bunch of additions in one go rather than open
	and close the file for each name.	Add a glyph
	to the alias.dat file if it doesn't already exist.
	additions = {'Gcircumflex': ['G','circumflex'], }
	Returns a list of only the added glyphnames."""
	import string
	glyphs = {}
	data = []
	new = []
	path = os.path.join(fl.path, 'Mapping', 'alias.dat')
	if os.path.exists(path):
		file = open(path, 'r')
		data = file.read().split('\n')
		file.close()
	for i in data:
		if len(i) == 0: continue
		if i[0] != '%':
			glyphs[i.split(' ')[0]] = i.split(' ')[1]
	for glyphName, glyphConstruction in additions.items():
		if glyphName not in glyphs.keys():
			new.append(glyphName)
			glyphs[glyphName] = string.join(glyphConstruction, sep)
	newNames = ['%%FONTLAB ALIASES']
	l = glyphs.keys()
	l.sort()
	for i in l:
		newNames.append(string.join([i, glyphs[i]], ' '))
		file = open(path, 'w')
		file.write(string.join(newNames, '\n'))
		file.close()
	return new


def GlyphIndexTable(font):
	"""Make a glyph index table for font"""
	font = unwrapFont(font)
	idx = {}
	for i in range(len(font)):
		g = font.glyphs[i]
		idx[g.name] = i
	return idx

def MakeReverseCompoMapping(font):
	"""Return a dict that maps glyph names to lists containing tuples
	of the form:
	   (clientGlyphName, componentIndex)
	"""
	font = unwrapFont(font)
	reverseCompoMapping = {}
	for g in font.glyphs:
		for i, c in zip(range(len(g.components)), g.components):
			base = font[c.index].name
			if not base in reverseCompoMapping:
				reverseCompoMapping[base] = []
			reverseCompoMapping[base].append((g.name, i))
	return reverseCompoMapping


#
#
#
#	stuff for text files
#
#
#

def textPrinter(text, name=None, path=None):
	"""Write a string to a text file. If no name is given it becomes
	Untitled_hour_minute_second.txt . If no path is given it goes
	into the FontLab/RoboFab Data directory."""
	if not name:
		import time
		tm_year,tm_mon,tm_day,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst = time.localtime()
		now = '_'.join((`tm_hour`, `tm_min`, `tm_sec`))
		name = 'Untitled_%s.txt'%now
	if not path:
		path = os.path.join(makeDataFolder(), name)
	f = open(path, 'wb')
	f.write(text)
	f.close()

def makeDataFolder():
	"""Make the RoboFab data folder"""
	folderPath = os.path.join(fl.path, "RoboFab Data")
	if not os.path.exists(folderPath):
		try:
			os.makedirs(folderPath)
		except:
			pass
	return folderPath
	

def Log(text=None):
	"""Make an entry in the default log file."""
	now = str(time.asctime(time.localtime(time.time())))
	if not text:
		text = "-"
	entry = "%s: %s\r"%(now, text)
	path = os.path.join(os.getcwd(), "Logs")
	new = 0
	if not os.path.exists(path):
		os.makedirs(path)
		new = 1
	log = os.path.join(path, "log.txt")
	f = open(log, 'a')
	if new:
		f.write("# log file for FL\r")
	f.write(entry)
	f.close()