aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/ttLib/tables/sbixStrike.py
blob: 7e3e17e8b4e68902b036f10ecd0f3d159d28bee8 (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
from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import readHex
from .sbixGlyph import *
import struct

sbixStrikeHeaderFormat = """
	>
	ppem:          H	# The PPEM for which this strike was designed (e.g., 9,
						# 12, 24)
	resolution:    H	# The screen resolution (in dpi) for which this strike
						# was designed (e.g., 72)
"""

sbixGlyphDataOffsetFormat = """
	>
	glyphDataOffset:   L	# Offset from the beginning of the strike data record
							# to data for the individual glyph
"""

sbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat)
sbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat)


class Strike(object):
	def __init__(self, rawdata=None, ppem=0, resolution=72):
		self.data = rawdata
		self.ppem = ppem
		self.resolution = resolution
		self.glyphs = {}

	def decompile(self, ttFont):
		if self.data is None:
			from fontTools import ttLib
			raise ttLib.TTLibError
		if len(self.data) < sbixStrikeHeaderFormatSize:
			from fontTools import ttLib
			raise(ttLib.TTLibError, "Strike header too short: Expected %x, got %x.") \
				% (sbixStrikeHeaderFormatSize, len(self.data))

		# read Strike header from raw data
		sstruct.unpack(sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self)

		# calculate number of glyphs
		firstGlyphDataOffset, = struct.unpack(">L", \
			self.data[sbixStrikeHeaderFormatSize:sbixStrikeHeaderFormatSize + sbixGlyphDataOffsetFormatSize])
		self.numGlyphs = (firstGlyphDataOffset - sbixStrikeHeaderFormatSize) // sbixGlyphDataOffsetFormatSize - 1
		# ^ -1 because there's one more offset than glyphs

		# build offset list for single glyph data offsets
		self.glyphDataOffsets = []
		for i in range(self.numGlyphs + 1): # + 1 because there's one more offset than glyphs
			start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize
			current_offset, = struct.unpack(">L", self.data[start:start + sbixGlyphDataOffsetFormatSize])
			self.glyphDataOffsets.append(current_offset)

		# iterate through offset list and slice raw data into glyph data records
		for i in range(self.numGlyphs):
			current_glyph = Glyph(rawdata=self.data[self.glyphDataOffsets[i]:self.glyphDataOffsets[i+1]], gid=i)
			current_glyph.decompile(ttFont)
			self.glyphs[current_glyph.glyphName] = current_glyph
		del self.glyphDataOffsets
		del self.numGlyphs
		del self.data

	def compile(self, ttFont):
		self.glyphDataOffsets = b""
		self.bitmapData = b""

		glyphOrder = ttFont.getGlyphOrder()

		# first glyph starts right after the header
		currentGlyphDataOffset = sbixStrikeHeaderFormatSize + sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1)
		for glyphName in glyphOrder:
			if glyphName in self.glyphs:
				# we have glyph data for this glyph
				current_glyph = self.glyphs[glyphName]
			else:
				# must add empty glyph data record for this glyph
				current_glyph = Glyph(glyphName=glyphName)
			current_glyph.compile(ttFont)
			current_glyph.glyphDataOffset = currentGlyphDataOffset
			self.bitmapData += current_glyph.rawdata
			currentGlyphDataOffset += len(current_glyph.rawdata)
			self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, current_glyph)

		# add last "offset", really the end address of the last glyph data record
		dummy = Glyph()
		dummy.glyphDataOffset = currentGlyphDataOffset
		self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy)

		# pack header
		self.data = sstruct.pack(sbixStrikeHeaderFormat, self)
		# add offsets and image data after header
		self.data += self.glyphDataOffsets + self.bitmapData

	def toXML(self, xmlWriter, ttFont):
		xmlWriter.begintag("strike")
		xmlWriter.newline()
		xmlWriter.simpletag("ppem", value=self.ppem)
		xmlWriter.newline()
		xmlWriter.simpletag("resolution", value=self.resolution)
		xmlWriter.newline()
		glyphOrder = ttFont.getGlyphOrder()
		for i in range(len(glyphOrder)):
			if glyphOrder[i] in self.glyphs:
				self.glyphs[glyphOrder[i]].toXML(xmlWriter, ttFont)
				# TODO: what if there are more glyph data records than (glyf table) glyphs?
		xmlWriter.endtag("strike")
		xmlWriter.newline()

	def fromXML(self, name, attrs, content, ttFont):
		if name in ["ppem", "resolution"]:
			setattr(self, name, safeEval(attrs["value"]))
		elif name == "glyph":
			if "graphicType" in attrs:
				myFormat = safeEval("'''" + attrs["graphicType"] + "'''")
			else:
				myFormat = None
			if "glyphname" in attrs:
				myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''")
			elif "name" in attrs:
				myGlyphName = safeEval("'''" + attrs["name"] + "'''")
			else:
				from fontTools import ttLib
				raise ttLib.TTLibError("Glyph must have a glyph name.")
			if "originOffsetX" in attrs:
				myOffsetX = safeEval(attrs["originOffsetX"])
			else:
				myOffsetX = 0
			if "originOffsetY" in attrs:
				myOffsetY = safeEval(attrs["originOffsetY"])
			else:
				myOffsetY = 0
			current_glyph = Glyph(
				glyphName=myGlyphName,
				graphicType=myFormat,
				originOffsetX=myOffsetX,
				originOffsetY=myOffsetY,
			)
			for element in content:
				if isinstance(element, tuple):
					name, attrs, content = element
					current_glyph.fromXML(name, attrs, content, ttFont)
					current_glyph.compile(ttFont)
			self.glyphs[current_glyph.glyphName] = current_glyph
		else:
			from fontTools import ttLib
			raise ttLib.TTLibError("can't handle '%s' element" % name)