O_S_2f_2.py revision 170fc9d40fb1abf5bf5582f603ba4e9a0a441a0c
1import DefaultTable
2from fontTools.misc import sstruct
3from fontTools.misc.textTools import safeEval, num2binary, binary2num
4from types import TupleType
5
6
7# panose classification
8
9panoseFormat = """
10	bFamilyType:        B
11	bSerifStyle:        B
12	bWeight:            B
13	bProportion:        B
14	bContrast:          B
15	bStrokeVariation:   B
16	bArmStyle:          B
17	bLetterForm:        B
18	bMidline:           B
19	bXHeight:           B
20"""
21
22class Panose:
23
24	def toXML(self, writer, ttFont):
25		formatstring, names, fixes = sstruct.getformat(panoseFormat)
26		for name in names:
27			writer.simpletag(name, value=getattr(self, name))
28			writer.newline()
29
30	def fromXML(self, (name, attrs, content), ttFont):
31		setattr(self, name, safeEval(attrs["value"]))
32
33
34# 'sfnt' OS/2 and Windows Metrics table - 'OS/2'
35
36OS2_format_0 = """
37	>   # big endian
38	version:                H       # version
39	xAvgCharWidth:          h       # average character width
40	usWeightClass:          H       # degree of thickness of strokes
41	usWidthClass:           H       # aspect ratio
42	fsType:                 h       # type flags
43	ySubscriptXSize:        h       # subscript horizontal font size
44	ySubscriptYSize:        h       # subscript vertical font size
45	ySubscriptXOffset:      h       # subscript x offset
46	ySubscriptYOffset:      h       # subscript y offset
47	ySuperscriptXSize:      h       # superscript horizontal font size
48	ySuperscriptYSize:      h       # superscript vertical font size
49	ySuperscriptXOffset:    h       # superscript x offset
50	ySuperscriptYOffset:    h       # superscript y offset
51	yStrikeoutSize:         h       # strikeout size
52	yStrikeoutPosition:     h       # strikeout position
53	sFamilyClass:           h       # font family class and subclass
54	panose:                 10s     # panose classification number
55	ulUnicodeRange1:        L       # character range
56	ulUnicodeRange2:        L       # character range
57	ulUnicodeRange3:        L       # character range
58	ulUnicodeRange4:        L       # character range
59	achVendID:              4s      # font vendor identification
60	fsSelection:            H       # font selection flags
61	fsFirstCharIndex:       H       # first unicode character index
62	fsLastCharIndex:        H       # last unicode character index
63	sTypoAscender:          h       # typographic ascender
64	sTypoDescender:         h       # typographic descender
65	sTypoLineGap:           h       # typographic line gap
66	usWinAscent:            H       # Windows ascender
67	usWinDescent:           H       # Windows descender
68"""
69
70OS2_format_1_addition =  """
71	ulCodePageRange1:   L
72	ulCodePageRange2:   L
73"""
74
75OS2_format_2_addition =  OS2_format_1_addition + """
76	sxHeight:           h
77	sCapHeight:         h
78	usDefaultChar:      H
79	usBreakChar:        H
80	usMaxContex:        H
81"""
82
83OS2_format_5_addition =  OS2_format_2_addition + """
84	usLowerOpticalPointSize:    H
85	usUpperOpticalPointSize:    H
86"""
87
88bigendian = "	>	# big endian\n"
89
90OS2_format_1 = OS2_format_0 + OS2_format_1_addition
91OS2_format_2 = OS2_format_0 + OS2_format_2_addition
92OS2_format_5 = OS2_format_0 + OS2_format_5_addition
93OS2_format_1_addition = bigendian + OS2_format_1_addition
94OS2_format_2_addition = bigendian + OS2_format_2_addition
95OS2_format_5_addition = bigendian + OS2_format_5_addition
96
97
98class table_O_S_2f_2(DefaultTable.DefaultTable):
99
100	"""the OS/2 table"""
101
102	def decompile(self, data, ttFont):
103		dummy, data = sstruct.unpack2(OS2_format_0, data, self)
104		# workarounds for buggy fonts (Apple, mona)
105		if not data:
106			self.version = 0
107		elif len(data) == sstruct.calcsize(OS2_format_1_addition):
108			self.version = 1
109		elif len(data) == sstruct.calcsize(OS2_format_2_addition):
110			if self.version not in (2, 3, 4):
111				self.version = 1
112		elif len(data) == sstruct.calcsize(OS2_format_5_addition):
113			self.version = 5
114		else:
115			from fontTools import ttLib
116			raise ttLib.TTLibError, "unknown format for OS/2 table (incorrect length): version %s" % (self.version, len(data))
117		if self.version == 1:
118			sstruct.unpack2(OS2_format_1_addition, data, self)
119		elif self.version in (2, 3, 4):
120			sstruct.unpack2(OS2_format_2_addition, data, self)
121		elif self.version == 5:
122			sstruct.unpack2(OS2_format_5_addition, data, self)
123			self.usLowerOpticalPointSize /= 20.
124			self.usUpperOpticalPointSize /= 20.
125		elif self.version <> 0:
126			from fontTools import ttLib
127			raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
128		self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
129
130	def compile(self, ttFont):
131		panose = self.panose
132		self.panose = sstruct.pack(panoseFormat, self.panose)
133		if self.version == 0:
134			data = sstruct.pack(OS2_format_0, self)
135		elif self.version == 1:
136			data = sstruct.pack(OS2_format_1, self)
137		elif self.version in (2, 3, 4):
138			data = sstruct.pack(OS2_format_2, self)
139		elif self.version == 5:
140			lower = self.usLowerOpticalPointSize
141			upper = self.usUpperOpticalPointSize
142			self.usLowerOpticalPointSize = int(round(self.usLowerOpticalPointSize * 20))
143			self.usUpperOpticalPointSize = int(round(self.usUpperOpticalPointSize * 20))
144			data = sstruct.pack(OS2_format_5, self)
145			self.usLowerOpticalPointSize = lower
146			self.usUpperOpticalPointSize = upper
147		else:
148			from fontTools import ttLib
149			raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
150		self.panose = panose
151		return data
152
153	def toXML(self, writer, ttFont):
154		if self.version == 1:
155			format = OS2_format_1
156		elif self.version in (2, 3, 4):
157			format = OS2_format_2
158		elif self.version == 5:
159			format = OS2_format_5
160		else:
161			format = OS2_format_0
162		formatstring, names, fixes = sstruct.getformat(format)
163		for name in names:
164			value = getattr(self, name)
165			if type(value) == type(0L):
166				value = int(value)
167			if name=="panose":
168				writer.begintag("panose")
169				writer.newline()
170				value.toXML(writer, ttFont)
171				writer.endtag("panose")
172			elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
173					"ulUnicodeRange3", "ulUnicodeRange4",
174					"ulCodePageRange1", "ulCodePageRange2"):
175				writer.simpletag(name, value=num2binary(value))
176			elif name in ("fsType", "fsSelection"):
177				writer.simpletag(name, value=num2binary(value, 16))
178			elif name == "achVendID":
179				writer.simpletag(name, value=repr(value)[1:-1])
180			else:
181				writer.simpletag(name, value=value)
182			writer.newline()
183
184	def fromXML(self, (name, attrs, content), ttFont):
185		if name == "panose":
186			self.panose = panose = Panose()
187			for element in content:
188				if type(element) == TupleType:
189					panose.fromXML(element, ttFont)
190		elif name in ("ulUnicodeRange1", "ulUnicodeRange2",
191				"ulUnicodeRange3", "ulUnicodeRange4",
192				"ulCodePageRange1", "ulCodePageRange2",
193				"fsType", "fsSelection"):
194			setattr(self, name, binary2num(attrs["value"]))
195		elif name == "achVendID":
196			setattr(self, name, safeEval("'''" + attrs["value"] + "'''"))
197		else:
198			setattr(self, name, safeEval(attrs["value"]))
199
200
201