Home | History | Annotate | Download | only in tables
      1 from __future__ import print_function, division, absolute_import
      2 from fontTools.misc.py23 import *
      3 from fontTools.misc import sstruct
      4 from fontTools.misc.textTools import readHex
      5 from .sbixBitmap import *
      6 import struct
      7 
      8 sbixBitmapSetHeaderFormat = """
      9 	>
     10 	size:            H    # 00 28
     11 	resolution:      H    #       00 48
     12 """
     13 
     14 sbixBitmapOffsetEntryFormat = """
     15 	>
     16 	ulOffset:        L    # 00 00 07 E0 # Offset from start of first offset entry to each bitmap
     17 """
     18 
     19 sbixBitmapSetHeaderFormatSize = sstruct.calcsize(sbixBitmapSetHeaderFormat)
     20 sbixBitmapOffsetEntryFormatSize = sstruct.calcsize(sbixBitmapOffsetEntryFormat)
     21 
     22 
     23 class 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