| 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) |