1from __future__ import print_function, division, absolute_import
2from fontTools.misc.py23 import *
3from fontTools.misc import sstruct
4from fontTools.misc.textTools import readHex
5from .sbixBitmap import *
6import struct
7
8sbixBitmapSetHeaderFormat = """
9	>
10	size:            H    # 00 28
11	resolution:      H    #       00 48
12"""
13
14sbixBitmapOffsetEntryFormat = """
15	>
16	ulOffset:        L    # 00 00 07 E0 # Offset from start of first offset entry to each bitmap
17"""
18
19sbixBitmapSetHeaderFormatSize = sstruct.calcsize(sbixBitmapSetHeaderFormat)
20sbixBitmapOffsetEntryFormatSize = sstruct.calcsize(sbixBitmapOffsetEntryFormat)
21
22
23class BitmapSet(object):
24	def __init__(self, rawdata=None, size=0, resolution=72):
25		self.data = rawdata
26		self.size = size
27		self.resolution = resolution
28		self.bitmaps = {}
29
30	def decompile(self, ttFont):
31		if self.data is None:
32			from fontTools import ttLib
33			raise ttLib.TTLibError
34		if len(self.data) < sbixBitmapSetHeaderFormatSize:
35			from fontTools import ttLib
36			raise(ttLib.TTLibError, "BitmapSet header too short: Expected %x, got %x.") \
37				% (sbixBitmapSetHeaderFormatSize, len(self.data))
38
39		# read BitmapSet header from raw data
40		sstruct.unpack(sbixBitmapSetHeaderFormat, self.data[:sbixBitmapSetHeaderFormatSize], self)
41
42		# calculate number of bitmaps
43		firstBitmapOffset, = struct.unpack(">L", \
44			self.data[sbixBitmapSetHeaderFormatSize : sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize])
45		self.numBitmaps = (firstBitmapOffset - sbixBitmapSetHeaderFormatSize) // sbixBitmapOffsetEntryFormatSize - 1
46		# ^ -1 because there's one more offset than bitmaps
47
48		# build offset list for single bitmap offsets
49		self.bitmapOffsets = []
50		for i in range(self.numBitmaps + 1): # + 1 because there's one more offset than bitmaps
51			start = i * sbixBitmapOffsetEntryFormatSize + sbixBitmapSetHeaderFormatSize
52			myOffset, = struct.unpack(">L", self.data[start : start + sbixBitmapOffsetEntryFormatSize])
53			self.bitmapOffsets.append(myOffset)
54
55		# iterate through offset list and slice raw data into bitmaps
56		for i in range(self.numBitmaps):
57			myBitmap = Bitmap(rawdata=self.data[self.bitmapOffsets[i] : self.bitmapOffsets[i+1]], gid=i)
58			myBitmap.decompile(ttFont)
59			self.bitmaps[myBitmap.glyphName] = myBitmap
60		del self.bitmapOffsets
61		del self.data
62
63	def compile(self, ttFont):
64		self.bitmapOffsets = ""
65		self.bitmapData = ""
66
67		glyphOrder = ttFont.getGlyphOrder()
68
69		# first bitmap starts right after the header
70		bitmapOffset = sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize * (len(glyphOrder) + 1)
71		for glyphName in glyphOrder:
72			if glyphName in self.bitmaps:
73				# we have a bitmap for this glyph
74				myBitmap = self.bitmaps[glyphName]
75			else:
76				# must add empty bitmap for this glyph
77				myBitmap = Bitmap(glyphName=glyphName)
78			myBitmap.compile(ttFont)
79			myBitmap.ulOffset = bitmapOffset
80			self.bitmapData += myBitmap.rawdata
81			bitmapOffset += len(myBitmap.rawdata)
82			self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, myBitmap)
83
84		# add last "offset", really the end address of the last bitmap
85		dummy = Bitmap()
86		dummy.ulOffset = bitmapOffset
87		self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, dummy)
88
89		# bitmap sets are padded to 4 byte boundaries
90		dataLength = len(self.bitmapOffsets) + len(self.bitmapData)
91		if dataLength % 4 != 0:
92			padding = 4 - (dataLength % 4)
93		else:
94			padding = 0
95
96		# pack header
97		self.data = sstruct.pack(sbixBitmapSetHeaderFormat, self)
98		# add offset, image data and padding after header
99		self.data += self.bitmapOffsets + self.bitmapData + "\0" * padding
100
101	def toXML(self, xmlWriter, ttFont):
102		xmlWriter.begintag("bitmapSet")
103		xmlWriter.newline()
104		xmlWriter.simpletag("size", value=self.size)
105		xmlWriter.newline()
106		xmlWriter.simpletag("resolution", value=self.resolution)
107		xmlWriter.newline()
108		glyphOrder = ttFont.getGlyphOrder()
109		for i in range(len(glyphOrder)):
110			if glyphOrder[i] in self.bitmaps:
111				self.bitmaps[glyphOrder[i]].toXML(xmlWriter, ttFont)
112				# TODO: what if there are more bitmaps than glyphs?
113		xmlWriter.endtag("bitmapSet")
114		xmlWriter.newline()
115
116	def fromXML(self, name, attrs, content, ttFont):
117		if name in ["size", "resolution"]:
118			setattr(self, name, int(attrs["value"]))
119		elif name == "bitmap":
120			if "format" in attrs:
121				myFormat = attrs["format"]
122			else:
123				myFormat = None
124			if "glyphname" in attrs:
125				myGlyphName = attrs["glyphname"]
126			else:
127				from fontTools import ttLib
128				raise ttLib.TTLibError("Bitmap must have a glyph name.")
129			myBitmap = Bitmap(glyphName=myGlyphName, imageFormatTag=myFormat)
130			for element in content:
131				if isinstance(element, tuple):
132					name, attrs, content = element
133					myBitmap.fromXML(name, attrs, content, ttFont)
134					myBitmap.compile(ttFont)
135			self.bitmaps[myBitmap.glyphName] = myBitmap
136		else:
137			from fontTools import ttLib
138			raise ttLib.TTLibError("can't handle '%s' element" % name)
139