• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from __future__ import print_function, division, absolute_import
2from fontTools.misc.py23 import *
3from fontTools.misc import sstruct
4from fontTools.misc.textTools import safeEval
5from . import DefaultTable
6import pdb
7import struct
8
9
10METAHeaderFormat = """
11		>	# big endian
12		tableVersionMajor:			H
13		tableVersionMinor:			H
14		metaEntriesVersionMajor:	H
15		metaEntriesVersionMinor:	H
16		unicodeVersion:				L
17		metaFlags:					H
18		nMetaRecs:					H
19"""
20# This record is followed by nMetaRecs of METAGlyphRecordFormat.
21# This in turn is followd by as many METAStringRecordFormat entries
22# as specified by the METAGlyphRecordFormat entries
23# this is followed by the strings specifried in the  METAStringRecordFormat
24METAGlyphRecordFormat = """
25		>	# big endian
26		glyphID:			H
27		nMetaEntry:			H
28"""
29# This record is followd by a variable data length field:
30# 	USHORT or ULONG	hdrOffset
31# Offset from start of META table to the beginning
32# of this glyphs array of ns Metadata string entries.
33# Size determined by metaFlags field
34# METAGlyphRecordFormat entries must be sorted by glyph ID
35
36METAStringRecordFormat = """
37		>	# big endian
38		labelID:			H
39		stringLen:			H
40"""
41# This record is followd by a variable data length field:
42# 	USHORT or ULONG	stringOffset
43# METAStringRecordFormat entries must be sorted in order of labelID
44# There may be more than one entry with the same labelID
45# There may be more than one strign with the same content.
46
47# Strings shall be Unicode UTF-8 encoded, and null-terminated.
48
49METALabelDict = {
50	0: "MojikumiX4051", # An integer in the range 1-20
51	1: "UNIUnifiedBaseChars",
52	2: "BaseFontName",
53	3: "Language",
54	4: "CreationDate",
55	5: "FoundryName",
56	6: "FoundryCopyright",
57	7: "OwnerURI",
58	8: "WritingScript",
59	10: "StrokeCount",
60	11: "IndexingRadical",
61}
62
63
64def getLabelString(labelID):
65	try:
66		label = METALabelDict[labelID]
67	except KeyError:
68		label = "Unknown label"
69	return str(label)
70
71
72class table_M_E_T_A_(DefaultTable.DefaultTable):
73
74	dependencies = []
75
76	def decompile(self, data, ttFont):
77		dummy, newData = sstruct.unpack2(METAHeaderFormat, data, self)
78		self.glyphRecords = []
79		for i in range(self.nMetaRecs):
80			glyphRecord, newData = sstruct.unpack2(METAGlyphRecordFormat, newData, GlyphRecord())
81			if self.metaFlags == 0:
82				[glyphRecord.offset] = struct.unpack(">H", newData[:2])
83				newData = newData[2:]
84			elif self.metaFlags == 1:
85				[glyphRecord.offset] = struct.unpack(">H", newData[:4])
86				newData = newData[4:]
87			else:
88				assert 0, "The metaFlags field in the META table header has a value other than 0 or 1 :" + str(self.metaFlags)
89			glyphRecord.stringRecs = []
90			newData = data[glyphRecord.offset:]
91			for j in range(glyphRecord.nMetaEntry):
92				stringRec, newData = sstruct.unpack2(METAStringRecordFormat, newData, StringRecord())
93				if self.metaFlags == 0:
94					[stringRec.offset] = struct.unpack(">H", newData[:2])
95					newData = newData[2:]
96				else:
97					[stringRec.offset] = struct.unpack(">H", newData[:4])
98					newData = newData[4:]
99				stringRec.string = data[stringRec.offset:stringRec.offset + stringRec.stringLen]
100				glyphRecord.stringRecs.append(stringRec)
101			self.glyphRecords.append(glyphRecord)
102
103	def compile(self, ttFont):
104		offsetOK = 0
105		self.nMetaRecs = len(self.glyphRecords)
106		count = 0
107		while (offsetOK != 1):
108			count = count + 1
109			if count > 4:
110				pdb.set_trace()
111			metaData = sstruct.pack(METAHeaderFormat, self)
112			stringRecsOffset = len(metaData) + self.nMetaRecs * (6 + 2*(self.metaFlags & 1))
113			stringRecSize = (6 + 2*(self.metaFlags & 1))
114			for glyphRec in self.glyphRecords:
115				glyphRec.offset = stringRecsOffset
116				if (glyphRec.offset > 65535) and ((self.metaFlags & 1) == 0):
117					self.metaFlags = self.metaFlags + 1
118					offsetOK = -1
119					break
120				metaData = metaData + glyphRec.compile(self)
121				stringRecsOffset = stringRecsOffset + (glyphRec.nMetaEntry * stringRecSize)
122				# this will be the String Record offset for the next GlyphRecord.
123			if offsetOK == -1:
124				offsetOK = 0
125				continue
126
127			# metaData now contains the header and all of the GlyphRecords. Its length should bw
128			# the offset to the first StringRecord.
129			stringOffset = stringRecsOffset
130			for glyphRec in self.glyphRecords:
131				assert (glyphRec.offset == len(metaData)), "Glyph record offset did not compile correctly! for rec:" + str(glyphRec)
132				for stringRec in glyphRec.stringRecs:
133					stringRec.offset = stringOffset
134					if (stringRec.offset > 65535) and ((self.metaFlags & 1) == 0):
135						self.metaFlags = self.metaFlags + 1
136						offsetOK = -1
137						break
138					metaData = metaData + stringRec.compile(self)
139					stringOffset = stringOffset + stringRec.stringLen
140			if 	offsetOK == -1:
141				offsetOK = 0
142				continue
143
144			if ((self.metaFlags & 1) == 1) and (stringOffset < 65536):
145				self.metaFlags = self.metaFlags - 1
146				continue
147			else:
148				offsetOK = 1
149
150			# metaData now contains the header and all of the GlyphRecords and all of the String Records.
151			# Its length should be the offset to the first string datum.
152			for glyphRec in self.glyphRecords:
153				for stringRec in glyphRec.stringRecs:
154					assert (stringRec.offset == len(metaData)), "String offset did not compile correctly! for string:" + str(stringRec.string)
155					metaData = metaData + stringRec.string
156
157		return metaData
158
159	def toXML(self, writer, ttFont):
160		writer.comment("Lengths and number of entries in this table will be recalculated by the compiler")
161		writer.newline()
162		formatstring, names, fixes = sstruct.getformat(METAHeaderFormat)
163		for name in names:
164			value = getattr(self, name)
165			writer.simpletag(name, value=value)
166			writer.newline()
167		for glyphRec in self.glyphRecords:
168			glyphRec.toXML(writer, ttFont)
169
170	def fromXML(self, name, attrs, content, ttFont):
171		if name == "GlyphRecord":
172			if not hasattr(self, "glyphRecords"):
173				self.glyphRecords = []
174			glyphRec = GlyphRecord()
175			self.glyphRecords.append(glyphRec)
176			for element in content:
177				if isinstance(element, basestring):
178					continue
179				name, attrs, content = element
180				glyphRec.fromXML(name, attrs, content, ttFont)
181			glyphRec.offset = -1
182			glyphRec.nMetaEntry = len(glyphRec.stringRecs)
183		else:
184			setattr(self, name, safeEval(attrs["value"]))
185
186
187class GlyphRecord(object):
188	def __init__(self):
189		self.glyphID = -1
190		self.nMetaEntry = -1
191		self.offset = -1
192		self.stringRecs = []
193
194	def toXML(self, writer, ttFont):
195		writer.begintag("GlyphRecord")
196		writer.newline()
197		writer.simpletag("glyphID", value=self.glyphID)
198		writer.newline()
199		writer.simpletag("nMetaEntry", value=self.nMetaEntry)
200		writer.newline()
201		for stringRec in self.stringRecs:
202			stringRec.toXML(writer, ttFont)
203		writer.endtag("GlyphRecord")
204		writer.newline()
205
206	def fromXML(self, name, attrs, content, ttFont):
207		if name == "StringRecord":
208			stringRec = StringRecord()
209			self.stringRecs.append(stringRec)
210			for element in content:
211				if isinstance(element, basestring):
212					continue
213				stringRec.fromXML(name, attrs, content, ttFont)
214			stringRec.stringLen = len(stringRec.string)
215		else:
216			setattr(self, name, safeEval(attrs["value"]))
217
218	def compile(self, parentTable):
219		data = sstruct.pack(METAGlyphRecordFormat, self)
220		if parentTable.metaFlags == 0:
221			datum = struct.pack(">H", self.offset)
222		elif parentTable.metaFlags == 1:
223			datum = struct.pack(">L", self.offset)
224		data = data + datum
225		return data
226
227	def __repr__(self):
228		return "GlyphRecord[ glyphID: " + str(self.glyphID) + ", nMetaEntry: " + str(self.nMetaEntry) + ", offset: " + str(self.offset) + " ]"
229
230# XXX The following two functions are really broken around UTF-8 vs Unicode
231
232def mapXMLToUTF8(string):
233	uString = unicode()
234	strLen = len(string)
235	i = 0
236	while i < strLen:
237		prefixLen = 0
238		if  (string[i:i+3] == "&#x"):
239			prefixLen = 3
240		elif  (string[i:i+7] == "&amp;#x"):
241			prefixLen = 7
242		if prefixLen:
243			i = i+prefixLen
244			j= i
245			while string[i] != ";":
246				i = i+1
247			valStr = string[j:i]
248
249			uString = uString + unichr(eval('0x' + valStr))
250		else:
251			uString = uString + unichr(byteord(string[i]))
252		i = i +1
253
254	return uString.encode('utf_8')
255
256
257def mapUTF8toXML(string):
258	uString = string.decode('utf_8')
259	string = ""
260	for uChar in uString:
261		i = ord(uChar)
262		if (i < 0x80) and (i > 0x1F):
263			string = string + uChar
264		else:
265			string = string + "&#x" + hex(i)[2:] + ";"
266	return string
267
268
269class StringRecord(object):
270
271	def toXML(self, writer, ttFont):
272		writer.begintag("StringRecord")
273		writer.newline()
274		writer.simpletag("labelID", value=self.labelID)
275		writer.comment(getLabelString(self.labelID))
276		writer.newline()
277		writer.newline()
278		writer.simpletag("string", value=mapUTF8toXML(self.string))
279		writer.newline()
280		writer.endtag("StringRecord")
281		writer.newline()
282
283	def fromXML(self, name, attrs, content, ttFont):
284		for element in content:
285			if isinstance(element, basestring):
286				continue
287			name, attrs, content = element
288			value = attrs["value"]
289			if name == "string":
290				self.string = mapXMLToUTF8(value)
291			else:
292				setattr(self, name, safeEval(value))
293
294	def compile(self, parentTable):
295		data = sstruct.pack(METAStringRecordFormat, self)
296		if parentTable.metaFlags == 0:
297			datum = struct.pack(">H", self.offset)
298		elif parentTable.metaFlags == 1:
299			datum = struct.pack(">L", self.offset)
300		data = data + datum
301		return data
302
303	def __repr__(self):
304		return "StringRecord [ labelID: " + str(self.labelID) + " aka " + getLabelString(self.labelID) \
305			+ ", offset: " + str(self.offset) + ", length: " + str(self.stringLen) + ", string: " +self.string + " ]"
306