普通文本  |  203行  |  5.67 KB

"""ttLib.macUtils.py -- Various Mac-specific stuff."""

from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
import sys
import os
if sys.platform not in ("mac", "darwin"):
	raise ImportError("This module is Mac-only!")
try:
	from Carbon import Res
except ImportError:
	import Res



def MyOpenResFile(path):
	mode = 1  # read only
	try:
		resref = Res.FSOpenResFile(path, mode)
	except Res.Error:
		# try data fork
		resref = Res.FSOpenResourceFile(path, unicode(), mode)
	return resref


def getSFNTResIndices(path):
	"""Determine whether a file has a resource fork or not."""
	try:
		resref = MyOpenResFile(path)
	except Res.Error:
		return []
	Res.UseResFile(resref)
	numSFNTs = Res.Count1Resources('sfnt')
	Res.CloseResFile(resref)
	return list(range(1, numSFNTs + 1))


def openTTFonts(path):
	"""Given a pathname, return a list of TTFont objects. In the case 
	of a flat TTF/OTF file, the list will contain just one font object;
	but in the case of a Mac font suitcase it will contain as many
	font objects as there are sfnt resources in the file.
	"""
	from fontTools import ttLib
	fonts = []
	sfnts = getSFNTResIndices(path)
	if not sfnts:
		fonts.append(ttLib.TTFont(path))
	else:
		for index in sfnts:
			fonts.append(ttLib.TTFont(path, index))
		if not fonts:
			raise ttLib.TTLibError("no fonts found in file '%s'" % path)
	return fonts


class SFNTResourceReader(object):
	
	"""Simple (Mac-only) read-only file wrapper for 'sfnt' resources."""
	
	def __init__(self, path, res_name_or_index):
		resref = MyOpenResFile(path)
		Res.UseResFile(resref)
		if isinstance(res_name_or_index, basestring):
			res = Res.Get1NamedResource('sfnt', res_name_or_index)
		else:
			res = Res.Get1IndResource('sfnt', res_name_or_index)
		self.file = StringIO(res.data)
		Res.CloseResFile(resref)
		self.name = path
	
	def __getattr__(self, attr):
		# cheap inheritance
		return getattr(self.file, attr)


class SFNTResourceWriter(object):
	
	"""Simple (Mac-only) file wrapper for 'sfnt' resources."""
	
	def __init__(self, path, ttFont, res_id=None):
		self.file = StringIO()
		self.name = path
		self.closed = 0
		fullname = ttFont['name'].getName(4, 1, 0) # Full name, mac, default encoding
		familyname = ttFont['name'].getName(1, 1, 0) # Fam. name, mac, default encoding
		psname = ttFont['name'].getName(6, 1, 0) # PostScript name, etc.
		if fullname is None or fullname is None or psname is None:
			from fontTools import ttLib
			raise ttLib.TTLibError("can't make 'sfnt' resource, no Macintosh 'name' table found")
		self.fullname = fullname.string
		self.familyname = familyname.string
		self.psname = psname.string
		if self.familyname != self.psname[:len(self.familyname)]:
			# ugh. force fam name to be the same as first part of ps name,
			# fondLib otherwise barfs.
			for i in range(min(len(self.psname), len(self.familyname))):
				if self.familyname[i] != self.psname[i]:
					break
			self.familyname = self.psname[:i]
		
		self.ttFont = ttFont
		self.res_id = res_id
		if os.path.exists(self.name):
			os.remove(self.name)
		# XXX datafork support
		Res.FSpCreateResFile(self.name, 'DMOV', 'FFIL', 0)
		self.resref = Res.FSOpenResFile(self.name, 3)  # exclusive read/write permission
	
	def close(self):
		if self.closed:
			return
		Res.UseResFile(self.resref)
		try:
			res = Res.Get1NamedResource('sfnt', self.fullname)
		except Res.Error:
			pass
		else:
			res.RemoveResource()
		res = Res.Resource(self.file.getvalue())
		if self.res_id is None:
			self.res_id = Res.Unique1ID('sfnt')
		res.AddResource('sfnt', self.res_id, self.fullname)
		res.ChangedResource()
		
		self.createFond()
		del self.ttFont
		Res.CloseResFile(self.resref)
		self.file.close()
		self.closed = 1
	
	def createFond(self):
		fond_res = Res.Resource("")
		fond_res.AddResource('FOND', self.res_id, self.fullname)
		
		from fontTools import fondLib
		fond = fondLib.FontFamily(fond_res, "w")
		
		fond.ffFirstChar = 0
		fond.ffLastChar = 255
		fond.fondClass = 0
		fond.fontAssoc = [(0, 0, self.res_id)]
		fond.ffFlags = 20480	# XXX ???
		fond.ffIntl = (0, 0)
		fond.ffLeading = 0
		fond.ffProperty = (0, 0, 0, 0, 0, 0, 0, 0, 0)
		fond.ffVersion = 0
		fond.glyphEncoding = {}
		if self.familyname == self.psname:
			fond.styleIndices = (1,) * 48  # uh-oh, fondLib is too dumb.
		else:
			fond.styleIndices = (2,) * 48
		fond.styleStrings = []
		fond.boundingBoxes = None
		fond.ffFamID = self.res_id
		fond.changed = 1
		fond.glyphTableOffset = 0
		fond.styleMappingReserved = 0
		
		# calc:
		scale = 4096 / self.ttFont['head'].unitsPerEm
		fond.ffAscent = scale * self.ttFont['hhea'].ascent
		fond.ffDescent = scale * self.ttFont['hhea'].descent
		fond.ffWidMax = scale * self.ttFont['hhea'].advanceWidthMax
		
		fond.ffFamilyName = self.familyname
		fond.psNames = {0: self.psname}
		
		fond.widthTables = {}
		fond.kernTables = {}
		cmap = self.ttFont['cmap'].getcmap(1, 0)
		if cmap:
			names = {}
			for code, name in cmap.cmap.items():
				names[name] = code
			if 'kern' in self.ttFont:
				kern = self.ttFont['kern'].getkern(0)
				if kern:
					fondkerning = []
					for (left, right), value in kern.kernTable.items():
						if left in names and right in names:
							fondkerning.append((names[left], names[right], scale * value))
					fondkerning.sort()
					fond.kernTables = {0: fondkerning}
			if 'hmtx' in self.ttFont:
				hmtx = self.ttFont['hmtx']
				fondwidths = [2048] * 256 + [0, 0]  # default width, + plus two zeros.
				for name, (width, lsb) in hmtx.metrics.items():
					if name in names:
						fondwidths[names[name]] = scale * width
				fond.widthTables = {0: fondwidths}
		fond.save()
	
	def __del__(self):
		if not self.closed:
			self.close()
	
	def __getattr__(self, attr):
		# cheap inheritance
		return getattr(self.file, attr)