123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- from fontTools.misc import sstruct
- from fontTools.misc.textTools import safeEval
- from .sbixGlyph import Glyph
- 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)
|