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