Example #1
0
	def setUp(self):
		self.font = font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
		font['head'] = ttLib.getTableClass('head')()
		font['maxp'] = ttLib.getTableClass('maxp')()
		font['loca'] = WOFF2LocaTable()
		font['glyf'] = WOFF2GlyfTable()
		for tag in self.transformedTags:
			font[tag].decompile(self.tables[tag], font)
Example #2
0
 def setUp(self):
     self.font = font = ttLib.TTFont(recalcBBoxes=False,
                                     recalcTimestamp=False)
     font['head'] = ttLib.getTableClass('head')()
     font['maxp'] = ttLib.getTableClass('maxp')()
     font['loca'] = WOFF2LocaTable()
     font['glyf'] = WOFF2GlyfTable()
     for tag in self.transformedTags:
         font[tag].decompile(self.tables[tag], font)
Example #3
0
class WOFF2LocaTable(getTableClass('loca')):
	"""Same as parent class. The only difference is that it attempts to preserve
	the 'indexFormat' as encoded in the WOFF2 glyf table.
	"""

	def __init__(self, tag=None):
		self.tableTag = Tag(tag or 'loca')

	def compile(self, ttFont):
		try:
			max_location = max(self.locations)
		except AttributeError:
			self.set([])
			max_location = 0
		if 'glyf' in ttFont and hasattr(ttFont['glyf'], 'indexFormat'):
			# copile loca using the indexFormat specified in the WOFF2 glyf table
			indexFormat = ttFont['glyf'].indexFormat
			if indexFormat == 0:
				if max_location >= 0x20000:
					raise TTLibError("indexFormat is 0 but local offsets > 0x20000")
				if not all(l % 2 == 0 for l in self.locations):
					raise TTLibError("indexFormat is 0 but local offsets not multiples of 2")
				locations = array.array("H")
				for i in range(len(self.locations)):
					locations.append(self.locations[i] // 2)
			else:
				locations = array.array("I", self.locations)
			if sys.byteorder != "big": locations.byteswap()
			data = locations.tobytes()
		else:
			# use the most compact indexFormat given the current glyph offsets
			data = super(WOFF2LocaTable, self).compile(ttFont)
		return data
Example #4
0
def normalise_table(font, tag, padding=4):
	""" Return normalised table data. Keep 'font' instance unmodified. """
	assert tag in ('glyf', 'loca', 'head')
	assert tag in font
	if tag == 'head':
		origHeadFlags = font['head'].flags
		font['head'].flags |= (1 << 11)
		tableData = font['head'].compile(font)
	if font.sfntVersion in ("\x00\x01\x00\x00", "true"):
		assert {'glyf', 'loca', 'head'}.issubset(font.keys())
		origIndexFormat = font['head'].indexToLocFormat
		if hasattr(font['loca'], 'locations'):
			origLocations = font['loca'].locations[:]
		else:
			origLocations = []
		glyfTable = ttLib.getTableClass('glyf')()
		glyfTable.decompile(font.getTableData('glyf'), font)
		glyfTable.padding = padding
		if tag == 'glyf':
			tableData = glyfTable.compile(font)
		elif tag == 'loca':
			glyfTable.compile(font)
			tableData = font['loca'].compile(font)
		if tag == 'head':
			glyfTable.compile(font)
			font['loca'].compile(font)
			tableData = font['head'].compile(font)
		font['head'].indexToLocFormat = origIndexFormat
		font['loca'].set(origLocations)
	if tag == 'head':
		font['head'].flags = origHeadFlags
	return tableData
def doit(args):
    font = args.ifont
    args.type = args.type.upper()

    for tag in ('GSUB', 'GPOS'):
        if tag == args.type or args.type == 'BOTH':
            table = ttLib.getTableClass(tag)()
            t = getattr(otTables, tag, None)()
            t.Version = 1.0
            t.ScriptList = otTables.ScriptList()
            t.ScriptList.ScriptRecord = []
            t.FeatureList = otTables.FeatureList()
            t.FeatureList.FeatureRecord = []
            t.LookupList = otTables.LookupList()
            t.LookupList.Lookup = []
            srec = otTables.ScriptRecord()
            srec.ScriptTag = args.script
            srec.Script = otTables.Script()
            srec.Script.DefaultLangSys = None
            srec.Script.LangSysRecord = []
            t.ScriptList.ScriptRecord.append(srec)
            t.ScriptList.ScriptCount = 1
            t.FeatureList.FeatureCount = 0
            t.LookupList.LookupCount = 0
            table.table = t
            font[tag] = table

    return font
Example #6
0
 def _startElementHandler(self, name, attrs):
     stackSize = self.stackSize
     self.stackSize = stackSize + 1
     if not stackSize:
         if name != "ttFont":
             raise TTXParseError("illegal root tag: %s" % name)
         sfntVersion = attrs.get("sfntVersion")
         if sfntVersion is not None:
             if len(sfntVersion) != 4:
                 sfntVersion = safeEval('"' + sfntVersion + '"')
             self.ttFont.sfntVersion = sfntVersion
         self.contentStack.append([])
     elif stackSize == 1:
         subFile = attrs.get("src")
         if subFile is not None:
             if hasattr(self.file, 'name'):
                 # if file has a name, get its parent directory
                 dirname = os.path.dirname(self.file.name)
             else:
                 # else fall back to using the current working directory
                 dirname = os.getcwd()
             subFile = os.path.join(dirname, subFile)
             subReader = XMLReader(subFile, self.ttFont, self.progress,
                                   self.quiet)
             subReader.read()
             self.contentStack.append([])
             return
         tag = ttLib.xmlToTag(name)
         msg = "Parsing '%s' table..." % tag
         if self.progress:
             self.progress.setLabel(msg)
         elif self.ttFont.verbose:
             ttLib.debugmsg(msg)
         else:
             if not self.quiet:
                 print(msg)
         if tag == "GlyphOrder":
             tableClass = ttLib.GlyphOrder
         elif "ERROR" in attrs or ('raw' in attrs
                                   and safeEval(attrs['raw'])):
             tableClass = DefaultTable
         else:
             tableClass = ttLib.getTableClass(tag)
             if tableClass is None:
                 tableClass = DefaultTable
         if tag == 'loca' and tag in self.ttFont:
             # Special-case the 'loca' table as we need the
             #    original if the 'glyf' table isn't recompiled.
             self.currentTable = self.ttFont[tag]
         else:
             self.currentTable = tableClass(tag)
             self.ttFont[tag] = self.currentTable
         self.contentStack.append([])
     elif stackSize == 2:
         self.contentStack.append([])
         self.root = (name, attrs, self.contentStack[-1])
     else:
         l = []
         self.contentStack[-1].append((name, attrs, l))
         self.contentStack.append(l)
Example #7
0
def normalise_table(font, tag, padding=4):
    """ Return normalised table data. Keep 'font' instance unmodified. """
    assert tag in ('glyf', 'loca', 'head')
    assert tag in font
    if tag == 'head':
        origHeadFlags = font['head'].flags
        font['head'].flags |= (1 << 11)
        tableData = font['head'].compile(font)
    if font.sfntVersion in ("\x00\x01\x00\x00", "true"):
        assert {'glyf', 'loca', 'head'}.issubset(font.keys())
        origIndexFormat = font['head'].indexToLocFormat
        if hasattr(font['loca'], 'locations'):
            origLocations = font['loca'].locations[:]
        else:
            origLocations = []
        glyfTable = ttLib.getTableClass('glyf')()
        glyfTable.decompile(font.getTableData('glyf'), font)
        glyfTable.padding = padding
        if tag == 'glyf':
            tableData = glyfTable.compile(font)
        elif tag == 'loca':
            glyfTable.compile(font)
            tableData = font['loca'].compile(font)
        if tag == 'head':
            glyfTable.compile(font)
            font['loca'].compile(font)
            tableData = font['head'].compile(font)
        font['head'].indexToLocFormat = origIndexFormat
        font['loca'].set(origLocations)
    if tag == 'head':
        font['head'].flags = origHeadFlags
    return tableData
Example #8
0
def add_gsub_to_font(fontfile):
    """Adds an empty GSUB table to a font."""
    font = ttLib.TTFont(fontfile)
    gsub_table = ttLib.getTableClass('GSUB')('GSUB')
    gsub_table.table = otTables.GSUB()
    gsub_table.table.Version = 1.0
    gsub_table.table.ScriptList = otTables.ScriptList()
    gsub_table.table.ScriptCount = 1
    gsub_table.table.LookupList = otTables.LookupList()
    gsub_table.table.LookupList.LookupCount = 0
    gsub_table.table.LookupList.Lookup = []
    gsub_table.table.FeatureList = otTables.FeatureList()
    gsub_table.table.FeatureList.FeatureCount = 0
    gsub_table.table.LookupList.FeatureRecord = []

    script_record = otTables.ScriptRecord()
    script_record.ScriptTag = get_opentype_script_tag(fontfile)
    script_record.Script = otTables.Script()
    script_record.Script.LangSysCount = 0
    script_record.Script.LangSysRecord = []

    default_lang_sys = otTables.DefaultLangSys()
    default_lang_sys.FeatureIndex = []
    default_lang_sys.FeatureCount = 0
    default_lang_sys.LookupOrder = None
    default_lang_sys.ReqFeatureIndex = 65535
    script_record.Script.DefaultLangSys = default_lang_sys

    gsub_table.table.ScriptList.ScriptRecord = [script_record]

    font['GSUB'] = gsub_table

    target_file = tempfile.gettempdir() + '/' + os.path.basename(fontfile)
    font.save(target_file)
    return target_file
Example #9
0
def add_gsub_to_font(fontfile):
    """Adds an empty GSUB table to a font."""
    font = ttLib.TTFont(fontfile)
    gsub_table = ttLib.getTableClass('GSUB')('GSUB')
    gsub_table.table = otTables.GSUB()
    gsub_table.table.Version = 1.0
    gsub_table.table.ScriptList = otTables.ScriptList()
    gsub_table.table.ScriptCount = 1
    gsub_table.table.LookupList = otTables.LookupList()
    gsub_table.table.LookupList.LookupCount = 0
    gsub_table.table.LookupList.Lookup = []
    gsub_table.table.FeatureList = otTables.FeatureList()
    gsub_table.table.FeatureList.FeatureCount = 0
    gsub_table.table.LookupList.FeatureRecord = []

    script_record = otTables.ScriptRecord()
    script_record.ScriptTag = get_opentype_script_tag(fontfile)
    script_record.Script = otTables.Script()
    script_record.Script.LangSysCount = 0
    script_record.Script.LangSysRecord = []

    default_lang_sys = otTables.DefaultLangSys()
    default_lang_sys.FeatureIndex = []
    default_lang_sys.FeatureCount = 0
    default_lang_sys.LookupOrder = None
    default_lang_sys.ReqFeatureIndex = 65535
    script_record.Script.DefaultLangSys = default_lang_sys

    gsub_table.table.ScriptList.ScriptRecord = [script_record]

    font['GSUB'] = gsub_table

    target_file = tempfile.gettempdir() + '/' + os.path.basename(fontfile)
    font.save(target_file)
    return target_file
Example #10
0
    def makeGDEF(self):
        gdef = otTables.GDEF()
        gdef.Version = 1.0
        gdef.GlyphClassDef = otTables.GlyphClassDef()
        gdef.GlyphClassDef.classDefs = {}

        glyphMarkClass = {}  # glyph --> markClass
        for markClass in self.parseTree.markClasses.values():
            for glyph in markClass.anchors.keys():
                if glyph in glyphMarkClass:
                    other = glyphMarkClass[glyph]
                    name1, name2 = sorted([markClass.name, other.name])
                    raise FeatureLibError(
                        'glyph %s cannot be both in markClass @%s and @%s' %
                        (glyph, name1, name2), markClass.location)
                glyphMarkClass[glyph] = markClass
                gdef.GlyphClassDef.classDefs[glyph] = 3
        gdef.AttachList = None
        gdef.LigCaretList = None
        gdef.MarkAttachClassDef = None
        if len(gdef.GlyphClassDef.classDefs) == 0:
            return None
        result = getTableClass("GDEF")()
        result.table = gdef
        return result
Example #11
0
def parseGDEF(lines, font):
	container = ttLib.getTableClass('GDEF')()
	log.debug("Parsing GDEF")
	self = ot.GDEF()
	fields = {
		'class definition begin':
			('GlyphClassDef',
			 lambda lines, font: parseClassDef(lines, font, klass=ot.GlyphClassDef)),
		'attachment list begin':
			('AttachList', parseAttachList),
		'carets begin':
			('LigCaretList', parseCaretList),
		'mark attachment class definition begin':
			('MarkAttachClassDef',
			 lambda lines, font: parseClassDef(lines, font, klass=ot.MarkAttachClassDef)),
		'markfilter set definition begin':
			('MarkGlyphSetsDef', parseMarkFilteringSets),
	}
	for attr,parser in fields.values():
		setattr(self, attr, None)
	while lines.peek() is not None:
		typ = lines.peek()[0].lower()
		if typ not in fields:
			log.debug('Skipping %s', typ)
			next(lines)
			continue
		attr,parser = fields[typ]
		assert getattr(self, attr) is None, attr
		setattr(self, attr, parser(lines, font))
	self.Version = 0x00010000 if self.MarkGlyphSetsDef is None else 0x00010002
	container.table = self
	return container
Example #12
0
def parseGDEF(lines, font):
	container = ttLib.getTableClass('GDEF')()
	log.debug("Parsing GDEF")
	self = ot.GDEF()
	fields = {
		'class definition begin':
			('GlyphClassDef',
			 lambda lines, font: parseClassDef(lines, font, klass=ot.GlyphClassDef)),
		'attachment list begin':
			('AttachList', parseAttachList),
		'carets begin':
			('LigCaretList', parseCaretList),
		'mark attachment class definition begin':
			('MarkAttachClassDef',
			 lambda lines, font: parseClassDef(lines, font, klass=ot.MarkAttachClassDef)),
		'markfilter set definition begin':
			('MarkGlyphSetsDef', parseMarkFilteringSets),
	}
	for attr,parser in fields.values():
		setattr(self, attr, None)
	while lines.peek() is not None:
		typ = lines.peek()[0].lower()
		if typ not in fields:
			log.debug('Skipping %s', typ)
			next(lines)
			continue
		attr,parser = fields[typ]
		assert getattr(self, attr) is None, attr
		setattr(self, attr, parser(lines, font))
	self.Version = 0x00010000 if self.MarkGlyphSetsDef is None else 0x00010002
	container.table = self
	return container
Example #13
0
	def _startElementHandler(self, name, attrs):
		stackSize = self.stackSize
		self.stackSize = stackSize + 1
		if not stackSize:
			if name != "ttFont":
				raise TTXParseError("illegal root tag: %s" % name)
			sfntVersion = attrs.get("sfntVersion")
			if sfntVersion is not None:
				if len(sfntVersion) != 4:
					sfntVersion = safeEval('"' + sfntVersion + '"')
				self.ttFont.sfntVersion = sfntVersion
			self.contentStack.append([])
		elif stackSize == 1:
			subFile = attrs.get("src")
			if subFile is not None:
				if hasattr(self.file, 'name'):
					# if file has a name, get its parent directory
					dirname = os.path.dirname(self.file.name)
				else:
					# else fall back to using the current working directory
					dirname = os.getcwd()
				subFile = os.path.join(dirname, subFile)
				subReader = XMLReader(subFile, self.ttFont, self.progress, self.quiet)
				subReader.read()
				self.contentStack.append([])
				return
			tag = ttLib.xmlToTag(name)
			msg = "Parsing '%s' table..." % tag
			if self.progress:
				self.progress.setLabel(msg)
			elif self.ttFont.verbose:
				ttLib.debugmsg(msg)
			else:
				if not self.quiet:
					print(msg)
			if tag == "GlyphOrder":
				tableClass = ttLib.GlyphOrder
			elif "ERROR" in attrs or ('raw' in attrs and safeEval(attrs['raw'])):
				tableClass = DefaultTable
			else:
				tableClass = ttLib.getTableClass(tag)
				if tableClass is None:
					tableClass = DefaultTable
			if tag == 'loca' and tag in self.ttFont:
				# Special-case the 'loca' table as we need the
				#    original if the 'glyf' table isn't recompiled.
				self.currentTable = self.ttFont[tag]
			else:
				self.currentTable = tableClass(tag)
				self.ttFont[tag] = self.currentTable
			self.contentStack.append([])
		elif stackSize == 2:
			self.contentStack.append([])
			self.root = (name, attrs, self.contentStack[-1])
		else:
			l = []
			self.contentStack[-1].append((name, attrs, l))
			self.contentStack.append(l)
Example #14
0
 def build_name(self):
     if not self.names_:
         return
     table = self.font.get("name")
     if not table:  # this only happens for unit tests
         table = self.font["name"] = getTableClass("name")()
     for name in self.names_:
         nameID, platformID, platEncID, langID, string = name
         table.setName(string, nameID, platformID, platEncID, langID)
Example #15
0
    def merge(self, fontfiles):

        mega = ttLib.TTFont()

        #
        # Settle on a mega glyph order.
        #
        fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
        glyphOrders = [font.getGlyphOrder() for font in fonts]
        megaGlyphOrder = self._mergeGlyphOrders(glyphOrders)
        # Reload fonts and set new glyph names on them.
        # TODO Is it necessary to reload font?  I think it is.  At least
        # it's safer, in case tables were loaded to provide glyph names.
        fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
        for font, glyphOrder in zip(fonts, glyphOrders):
            font.setGlyphOrder(glyphOrder)
        mega.setGlyphOrder(megaGlyphOrder)

        for font in fonts:
            self._preMerge(font)

        self.fonts = fonts
        self.duplicateGlyphsPerFont = [{} for f in fonts]

        allTags = reduce(set.union, (list(font.keys()) for font in fonts),
                         set())
        allTags.remove('GlyphOrder')

        # Make sure we process cmap before GSUB as we have a dependency there.
        if 'GSUB' in allTags:
            allTags.remove('GSUB')
            allTags = ['GSUB'] + list(allTags)
        if 'cmap' in allTags:
            allTags.remove('cmap')
            allTags = ['cmap'] + list(allTags)

        for tag in allTags:
            with timer("merge '%s'" % tag):
                tables = [font.get(tag, NotImplemented) for font in fonts]

                log.info("Merging '%s'.", tag)
                clazz = ttLib.getTableClass(tag)
                table = clazz(tag).merge(self, tables)
                # XXX Clean this up and use:  table = mergeObjects(tables)

                if table is not NotImplemented and table is not False:
                    mega[tag] = table
                    log.info("Merged '%s'.", tag)
                else:
                    log.info("Dropped '%s'.", tag)

        del self.duplicateGlyphsPerFont
        del self.fonts

        self._postMerge(mega)

        return mega
Example #16
0
	def _decompileTable(self, tag):
		"""Decompile table data and store it inside self.ttFont."""
		data = self[tag]
		if self.ttFont.isLoaded(tag):
			return self.ttFont[tag]
		tableClass = getTableClass(tag)
		table = tableClass(tag)
		self.ttFont.tables[tag] = table
		table.decompile(data, self.ttFont)
Example #17
0
	def merge(self, fontfiles):

		mega = ttLib.TTFont()

		#
		# Settle on a mega glyph order.
		#
		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
		glyphOrders = [font.getGlyphOrder() for font in fonts]
		megaGlyphOrder = self._mergeGlyphOrders(glyphOrders)
		# Reload fonts and set new glyph names on them.
		# TODO Is it necessary to reload font?  I think it is.  At least
		# it's safer, in case tables were loaded to provide glyph names.
		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
		for font,glyphOrder in zip(fonts, glyphOrders):
			font.setGlyphOrder(glyphOrder)
		mega.setGlyphOrder(megaGlyphOrder)

		for font in fonts:
			self._preMerge(font)

		self.fonts = fonts
		self.duplicateGlyphsPerFont = [{} for f in fonts]

		allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
		allTags.remove('GlyphOrder')

		# Make sure we process cmap before GSUB as we have a dependency there.
		if 'GSUB' in allTags:
			allTags.remove('GSUB')
			allTags = ['GSUB'] + list(allTags)
		if 'cmap' in allTags:
			allTags.remove('cmap')
			allTags = ['cmap'] + list(allTags)

		for tag in allTags:
			with timer("merge '%s'" % tag):
				tables = [font.get(tag, NotImplemented) for font in fonts]

				log.info("Merging '%s'.", tag)
				clazz = ttLib.getTableClass(tag)
				table = clazz(tag).merge(self, tables)
				# XXX Clean this up and use:  table = mergeObjects(tables)

				if table is not NotImplemented and table is not False:
					mega[tag] = table
					log.info("Merged '%s'.", tag)
				else:
					log.info("Dropped '%s'.", tag)

		del self.duplicateGlyphsPerFont
		del self.fonts

		self._postMerge(mega)

		return mega
Example #18
0
 def build_head(self):
     if not self.fontRevision_:
         return
     table = self.font.get("head")
     if not table:  # this only happens for unit tests
         table = self.font["head"] = getTableClass("head")()
         table.decompile(b"\0" * 54, self.font)
         table.tableVersion = 1.0
         table.created = table.modified = 3406620153  # 2011-12-13 11:22:33
     table.fontRevision = self.fontRevision_
Example #19
0
 def build_head(self):
     if not self.fontRevision_:
         return
     table = self.font.get("head")
     if not table:  # this only happens for unit tests
         table = self.font["head"] = getTableClass("head")()
         table.decompile(b"\0" * 54, self.font)
         table.tableVersion = 1.0
         table.created = table.modified = 3406620153  # 2011-12-13 11:22:33
     table.fontRevision = self.fontRevision_
Example #20
0
def inject_meta_into_font(ttf, flatbuffer_bin_filename):
    """inject metadata binary into font"""
    if not 'meta' in ttf:
        ttf['meta'] = ttLib.getTableClass('meta')()
    meta = ttf['meta']
    with open(flatbuffer_bin_filename) as flatbuffer_bin_file:
        meta.data[EMOJI_META_TAG_NAME] = flatbuffer_bin_file.read()

    # sort meta tables for faster access
    update_ttlib_orig_sort()
def inject_meta_into_font(ttf, flatbuffer_bin_filename):
    """inject metadata binary into font"""
    if not 'meta' in ttf:
        ttf['meta'] = ttLib.getTableClass('meta')()
    meta = ttf['meta']
    with open(flatbuffer_bin_filename) as flatbuffer_bin_file:
        meta.data[EMOJI_META_TAG_NAME] = flatbuffer_bin_file.read()

    # sort meta tables for faster access
    update_ttlib_orig_sort()
Example #22
0
def create_simple_gsub(lookups, script='DFLT', feature='ccmp'):
    """Create a simple GSUB table."""
    gsub_class = ttLib.getTableClass('GSUB')
    gsub = gsub_class('GSUB')

    gsub.table = otTables.GSUB()
    gsub.table.Version = 1.0
    gsub.table.ScriptList = create_script_list(script)
    gsub.table.FeatureList = create_feature_list(feature, len(lookups))
    gsub.table.LookupList = create_lookup_list(lookups)
    return gsub
Example #23
0
def create_simple_gsub(lookups, script='DFLT', feature='ccmp'):
    """Create a simple GSUB table."""
    gsub_class = ttLib.getTableClass('GSUB')
    gsub = gsub_class('GSUB')

    gsub.table = otTables.GSUB()
    gsub.table.Version = 1.0
    gsub.table.ScriptList = create_script_list(script)
    gsub.table.FeatureList = create_feature_list(feature, len(lookups))
    gsub.table.LookupList = create_lookup_list(lookups)
    return gsub
Example #24
0
    def makeGDEF(self):
        gdef = otTables.GDEF()
        gdef.Version = 1.0
        gdef.GlyphClassDef = otTables.GlyphClassDef()

        inferredGlyphClass = {}
        for lookup in self.lookups_:
            inferredGlyphClass.update(lookup.inferGlyphClasses())

        marks = {}  # glyph --> markClass
        for markClass in self.parseTree.markClasses.values():
            for markClassDef in markClass.definitions:
                for glyph in markClassDef.glyphSet():
                    other = marks.get(glyph)
                    if other not in (None, markClass):
                        name1, name2 = sorted([markClass.name, other.name])
                        raise FeatureLibError(
                            'Glyph %s cannot be both in '
                            'markClass @%s and @%s' %
                            (glyph, name1, name2), markClassDef.location)
                    marks[glyph] = markClass
                    inferredGlyphClass[glyph] = 3

        gdef.GlyphClassDef.classDefs = inferredGlyphClass
        gdef.AttachList = None
        gdef.LigCaretList = None

        markAttachClass = {g: c for g, (c, _) in self.markAttach_.items()}
        if markAttachClass:
            gdef.MarkAttachClassDef = otTables.MarkAttachClassDef()
            gdef.MarkAttachClassDef.classDefs = markAttachClass
        else:
            gdef.MarkAttachClassDef = None

        if self.markFilterSets_:
            gdef.Version = 0x00010002
            m = gdef.MarkGlyphSetsDef = otTables.MarkGlyphSetsDef()
            m.MarkSetTableFormat = 1
            m.MarkSetCount = len(self.markFilterSets_)
            m.Coverage = []
            filterSets = [(id, glyphs)
                          for (glyphs, id) in self.markFilterSets_.items()]
            for i, glyphs in sorted(filterSets):
                coverage = otTables.Coverage()
                coverage.glyphs = sorted(glyphs, key=self.font.getGlyphID)
                m.Coverage.append(coverage)

        if (len(gdef.GlyphClassDef.classDefs) == 0 and
                gdef.MarkAttachClassDef is None):
            return None
        result = getTableClass("GDEF")()
        result.table = gdef
        return result
Example #25
0
    def makeGDEF(self):
        gdef = otTables.GDEF()
        gdef.Version = 1.0
        gdef.GlyphClassDef = otTables.GlyphClassDef()

        inferredGlyphClass = {}
        for lookup in self.lookups_:
            inferredGlyphClass.update(lookup.inferGlyphClasses())

        marks = {}  # glyph --> markClass
        for markClass in self.parseTree.markClasses.values():
            for markClassDef in markClass.definitions:
                for glyph in markClassDef.glyphSet():
                    other = marks.get(glyph)
                    if other not in (None, markClass):
                        name1, name2 = sorted([markClass.name, other.name])
                        raise FeatureLibError(
                            'Glyph %s cannot be both in '
                            'markClass @%s and @%s' % (glyph, name1, name2),
                            markClassDef.location)
                    marks[glyph] = markClass
                    inferredGlyphClass[glyph] = 3

        gdef.GlyphClassDef.classDefs = inferredGlyphClass
        gdef.AttachList = None
        gdef.LigCaretList = None

        markAttachClass = {g: c for g, (c, _) in self.markAttach_.items()}
        if markAttachClass:
            gdef.MarkAttachClassDef = otTables.MarkAttachClassDef()
            gdef.MarkAttachClassDef.classDefs = markAttachClass
        else:
            gdef.MarkAttachClassDef = None

        if self.markFilterSets_:
            gdef.Version = 0x00010002
            m = gdef.MarkGlyphSetsDef = otTables.MarkGlyphSetsDef()
            m.MarkSetTableFormat = 1
            m.MarkSetCount = len(self.markFilterSets_)
            m.Coverage = []
            filterSets = [(id, glyphs)
                          for (glyphs, id) in self.markFilterSets_.items()]
            for i, glyphs in sorted(filterSets):
                coverage = otTables.Coverage()
                coverage.glyphs = sorted(glyphs, key=self.font.getGlyphID)
                m.Coverage.append(coverage)

        if (len(gdef.GlyphClassDef.classDefs) == 0
                and gdef.MarkAttachClassDef is None):
            return None
        result = getTableClass("GDEF")()
        result.table = gdef
        return result
Example #26
0
 def test_getVersion(self):
     # no version
     self.assertEqual((0, 0), self.writer._getVersion())
     # version from head.fontRevision
     fontRevision = self.font['head'].fontRevision
     versionTuple = tuple(int(i) for i in str(fontRevision).split("."))
     entry = self.writer.tables['head'] = ttLib.getTableClass('head')()
     entry.data = self.font.getTableData('head')
     self.assertEqual(versionTuple, self.writer._getVersion())
     # version from writer.flavorData
     flavorData = self.writer.flavorData = WOFF2FlavorData()
     flavorData.majorVersion, flavorData.minorVersion = (10, 11)
     self.assertEqual((10, 11), self.writer._getVersion())
Example #27
0
	def test_getVersion(self):
		# no version
		self.assertEqual((0, 0), self.writer._getVersion())
		# version from head.fontRevision
		fontRevision = self.font['head'].fontRevision
		versionTuple = tuple(int(i) for i in str(fontRevision).split("."))
		entry = self.writer.tables['head'] = ttLib.getTableClass('head')()
		entry.data = self.font.getTableData('head')
		self.assertEqual(versionTuple, self.writer._getVersion())
		# version from writer.flavorData
		flavorData = self.writer.flavorData = WOFF2FlavorData()
		flavorData.majorVersion, flavorData.minorVersion = (10, 11)
		self.assertEqual((10, 11), self.writer._getVersion())
Example #28
0
	def startElementHandler(self, name, attrs):
		stackSize = self.stackSize
		self.stackSize = stackSize + 1
		if not stackSize:
			if name <> "ttFont":
				raise TTXParseError, "illegal root tag: %s" % name
			sfntVersion = attrs.get("sfntVersion")
			if sfntVersion is not None:
				if len(sfntVersion) <> 4:
					sfntVersion = safeEval('"' + sfntVersion + '"')
				self.ttFont.sfntVersion = sfntVersion
			self.contentStack.append([])
		elif stackSize == 1:
			subFile = attrs.get("src")
			if subFile is not None:
				subFile = os.path.join(os.path.dirname(self.fileName), subFile)
				importXML(self.ttFont, subFile, self.progress)
				self.contentStack.append([])
				return
			tag = ttLib.xmlToTag(name)
			msg = "Parsing '%s' table..." % tag
			if self.progress:
				self.progress.setlabel(msg)
			elif self.ttFont.verbose:
				ttLib.debugmsg(msg)
			else:
				if not self.quiet:
					print msg
			if tag == "GlyphOrder":
				tableClass = ttLib.GlyphOrder
			elif attrs.has_key("ERROR"):
				tableClass = DefaultTable
			else:
				tableClass = ttLib.getTableClass(tag)
				if tableClass is None:
					tableClass = DefaultTable
			if tag == 'loca' and self.ttFont.has_key(tag):
				# Special-case the 'loca' table as we need the
				#    original if the 'glyf' table isn't recompiled.
				self.currentTable = self.ttFont[tag]
			else:
				self.currentTable = tableClass(tag)
				self.ttFont[tag] = self.currentTable
			self.contentStack.append([])
		elif stackSize == 2:
			self.contentStack.append([])
			self.root = (name, attrs, self.contentStack[-1])
		else:
			list = []
			self.contentStack[-1].append((name, attrs, list))
			self.contentStack.append(list)
Example #29
0
def parseGSUBGPOS(lines, font, tableTag):
	container = ttLib.getTableClass(tableTag)()
	lookupMap = DeferredMapping()
	featureMap = DeferredMapping()
	assert tableTag in ('GSUB', 'GPOS')
	log.debug("Parsing %s", tableTag)
	self = getattr(ot, tableTag)()
	self.Version = 0x00010000
	fields = {
		'script table begin':
		('ScriptList',
		 lambda lines: parseScriptList (lines, featureMap)),
		'feature table begin':
		('FeatureList',
		 lambda lines: parseFeatureList (lines, lookupMap, featureMap)),
		'lookup':
		('LookupList',
		 None),
	}
	for attr,parser in fields.values():
		setattr(self, attr, None)
	while lines.peek() is not None:
		typ = lines.peek()[0].lower()
		if typ not in fields:
			log.debug('Skipping %s', lines.peek())
			next(lines)
			continue
		attr,parser = fields[typ]
		if typ == 'lookup':
			if self.LookupList is None:
				self.LookupList = ot.LookupList()
				self.LookupList.Lookup = []
			_, name, _ = lines.peek()
			lookup = parseLookup(lines, tableTag, font, lookupMap)
			if lookupMap is not None:
				assert name not in lookupMap, "Duplicate lookup name: %s" % name
				lookupMap[name] = len(self.LookupList.Lookup)
			else:
				assert int(name) == len(self.LookupList.Lookup), "%d %d" % (name, len(self.Lookup))
			self.LookupList.Lookup.append(lookup)
		else:
			assert getattr(self, attr) is None, attr
			setattr(self, attr, parser(lines))
	if self.LookupList:
		self.LookupList.LookupCount = len(self.LookupList.Lookup)
	if lookupMap is not None:
		lookupMap.applyDeferredMappings()
	if featureMap is not None:
		featureMap.applyDeferredMappings()
	container.table = self
	return container
Example #30
0
def parseGSUBGPOS(lines, font, tableTag):
	container = ttLib.getTableClass(tableTag)()
	lookupMap = DeferredMapping()
	featureMap = DeferredMapping()
	assert tableTag in ('GSUB', 'GPOS')
	log.debug("Parsing %s", tableTag)
	self = getattr(ot, tableTag)()
	self.Version = 0x00010000
	fields = {
		'script table begin':
		('ScriptList',
		 lambda lines: parseScriptList (lines, featureMap)),
		'feature table begin':
		('FeatureList',
		 lambda lines: parseFeatureList (lines, lookupMap, featureMap)),
		'lookup':
		('LookupList',
		 None),
	}
	for attr,parser in fields.values():
		setattr(self, attr, None)
	while lines.peek() is not None:
		typ = lines.peek()[0].lower()
		if typ not in fields:
			log.debug('Skipping %s', lines.peek())
			next(lines)
			continue
		attr,parser = fields[typ]
		if typ == 'lookup':
			if self.LookupList is None:
				self.LookupList = ot.LookupList()
				self.LookupList.Lookup = []
			_, name, _ = lines.peek()
			lookup = parseLookup(lines, tableTag, font, lookupMap)
			if lookupMap is not None:
				assert name not in lookupMap, "Duplicate lookup name: %s" % name
				lookupMap[name] = len(self.LookupList.Lookup)
			else:
				assert int(name) == len(self.LookupList.Lookup), "%d %d" % (name, len(self.Lookup))
			self.LookupList.Lookup.append(lookup)
		else:
			assert getattr(self, attr) is None, attr
			setattr(self, attr, parser(lines))
	if self.LookupList:
		self.LookupList.LookupCount = len(self.LookupList.Lookup)
	if lookupMap is not None:
		lookupMap.applyDeferredMappings()
	if featureMap is not None:
		featureMap.applyDeferredMappings()
	container.table = self
	return container
Example #31
0
 def startElementHandler(self, name, attrs):
     stackSize = self.stackSize
     self.stackSize = stackSize + 1
     if not stackSize:
         if name <> "ttFont":
             raise TTXParseError, "illegal root tag: %s" % name
         sfntVersion = attrs.get("sfntVersion")
         if sfntVersion is not None:
             if len(sfntVersion) <> 4:
                 sfntVersion = safeEval('"' + sfntVersion + '"')
             self.ttFont.sfntVersion = sfntVersion
         self.contentStack.append([])
     elif stackSize == 1:
         subFile = attrs.get("src")
         if subFile is not None:
             subFile = os.path.join(os.path.dirname(self.fileName), subFile)
             importXML(self.ttFont, subFile, self.progress)
             self.contentStack.append([])
             return
         tag = ttLib.xmlToTag(name)
         msg = "Parsing '%s' table..." % tag
         if self.progress:
             self.progress.setlabel(msg)
         elif self.ttFont.verbose:
             ttLib.debugmsg(msg)
         else:
             print msg
         if tag == "GlyphOrder":
             tableClass = ttLib.GlyphOrder
         elif attrs.has_key("ERROR"):
             tableClass = DefaultTable
         else:
             tableClass = ttLib.getTableClass(tag)
             if tableClass is None:
                 tableClass = DefaultTable
         if tag == 'loca' and self.ttFont.has_key(tag):
             # Special-case the 'loca' table as we need the
             #    original if the 'glyf' table isn't recompiled.
             self.currentTable = self.ttFont[tag]
         else:
             self.currentTable = tableClass(tag)
             self.ttFont[tag] = self.currentTable
         self.contentStack.append([])
     elif stackSize == 2:
         self.contentStack.append([])
         self.root = (name, attrs, self.contentStack[-1])
     else:
         list = []
         self.contentStack[-1].append((name, attrs, list))
         self.contentStack.append(list)
Example #32
0
 def buildGDEF(self):
     gdef = otTables.GDEF()
     gdef.GlyphClassDef = self.buildGDEFGlyphClassDef_()
     gdef.AttachList = otl.buildAttachList(self.attachPoints_, self.glyphMap)
     gdef.LigCaretList = otl.buildLigCaretList(self.ligCaretCoords_, self.ligCaretPoints_, self.glyphMap)
     gdef.MarkAttachClassDef = self.buildGDEFMarkAttachClassDef_()
     gdef.MarkGlyphSetsDef = self.buildGDEFMarkGlyphSetsDef_()
     gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 1.0
     if any(
         (gdef.GlyphClassDef, gdef.AttachList, gdef.LigCaretList, gdef.MarkAttachClassDef, gdef.MarkGlyphSetsDef)
     ):
         result = getTableClass("GDEF")()
         result.table = gdef
         return result
     else:
         return None
Example #33
0
 def build(self):
     self.parseTree = Parser(self.featurefile_path).parse()
     self.parseTree.build(self)
     for tag in ('GPOS', 'GSUB'):
         table = self.makeTable(tag)
         if (table.ScriptList.ScriptCount > 0 or
                 table.FeatureList.FeatureCount > 0 or
                 table.LookupList.LookupCount > 0):
             fontTable = self.font[tag] = getTableClass(tag)()
             fontTable.table = table
         elif tag in self.font:
             del self.font[tag]
     gdef = self.makeGDEF()
     if gdef:
         self.font["GDEF"] = gdef
     elif "GDEF" in self.font:
         del self.font["GDEF"]
Example #34
0
 def _decompileTable(self, tag):
     """ Fetch table data, decompile it, and store it inside self.ttFont. """
     tag = Tag(tag)
     if tag not in self.tables:
         raise TTLibError("missing required table: %s" % tag)
     if self.ttFont.isLoaded(tag):
         return
     data = self.tables[tag].data
     if tag == 'loca':
         tableClass = WOFF2LocaTable
     elif tag == 'glyf':
         tableClass = WOFF2GlyfTable
     else:
         tableClass = getTableClass(tag)
     table = tableClass(tag)
     self.ttFont.tables[tag] = table
     table.decompile(data, self.ttFont)
Example #35
0
 def build(self):
     self.parseTree = Parser(self.featurefile_path).parse()
     self.parseTree.build(self)
     for tag in ('GPOS', 'GSUB'):
         table = self.makeTable(tag)
         if (table.ScriptList.ScriptCount > 0
                 or table.FeatureList.FeatureCount > 0
                 or table.LookupList.LookupCount > 0):
             fontTable = self.font[tag] = getTableClass(tag)()
             fontTable.table = table
         elif tag in self.font:
             del self.font[tag]
     gdef = self.makeGDEF()
     if gdef:
         self.font["GDEF"] = gdef
     elif "GDEF" in self.font:
         del self.font["GDEF"]
Example #36
0
def parseTable(lines, font, tableTag=None):
    debug("Parsing table")
    line = lines.peek()
    if line[0].split()[0] == "FontDame":
        next(lines)
        tag = line[0].split()[1].ljust(4)
        if tableTag is None:
            tableTag = tag
        else:
            assert tableTag == tag, (tableTag, tag)

    assert tableTag is not None, "Don't know what table to parse and data doesn't specify"

    container = ttLib.getTableClass(tableTag)()
    table = {"GSUB": parseGSUB, "GPOS": parseGPOS, "GDEF": parseGDEF}[tableTag](lines, font)
    container.table = table
    return container
Example #37
0
	def _decompileTable(self, tag):
		""" Fetch table data, decompile it, and store it inside self.ttFont. """
		tag = Tag(tag)
		if tag not in self.tables:
			raise TTLibError("missing required table: %s" % tag)
		if self.ttFont.isLoaded(tag):
			return
		data = self.tables[tag].data
		if tag == 'loca':
			tableClass = WOFF2LocaTable
		elif tag == 'glyf':
			tableClass = WOFF2GlyfTable
		else:
			tableClass = getTableClass(tag)
		table = tableClass(tag)
		self.ttFont.tables[tag] = table
		table.decompile(data, self.ttFont)
Example #38
0
 def buildGDEF(self):
     gdef = otTables.GDEF()
     gdef.GlyphClassDef = self.buildGDEFGlyphClassDef_()
     gdef.AttachList = \
         otl.buildAttachList(self.attachPoints_, self.glyphMap)
     gdef.LigCaretList = \
         otl.buildLigCaretList(self.ligCaretCoords_, self.ligCaretPoints_,
                               self.glyphMap)
     gdef.MarkAttachClassDef = self.buildGDEFMarkAttachClassDef_()
     gdef.MarkGlyphSetsDef = self.buildGDEFMarkGlyphSetsDef_()
     gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 1.0
     if any((gdef.GlyphClassDef, gdef.AttachList, gdef.LigCaretList,
             gdef.MarkAttachClassDef, gdef.MarkGlyphSetsDef)):
         result = getTableClass("GDEF")()
         result.table = gdef
         return result
     else:
         return None
Example #39
0
def parseTable(lines, font, tableTag=None):
	log.debug("Parsing table")
	line = lines.peek()
	if line[0].split()[0] == 'FontDame':
		next(lines)
		tag = line[0].split()[1].ljust(4)
		if tableTag is None:
			tableTag = tag
		else:
			assert tableTag == tag, (tableTag, tag)

	assert tableTag is not None, "Don't know what table to parse and data doesn't specify"

	container = ttLib.getTableClass(tableTag)()
	table = {'GSUB': parseGSUB,
		 'GPOS': parseGPOS,
		 'GDEF': parseGDEF,
		}[tableTag](lines, font)
	container.table = table
	return container
Example #40
0
def openOpenTypeFile(path, outFilePath, font_format, options):
    # If input font is CFF, build a dummy ttFont in memory.
    if font_format == "OTF":  # it is an OTF font, can process file directly
        ttFont = TTFont(path)
        if "CFF " not in ttFont:
            raise ACFontError("Font is not a CFF font <%s>." % path)
    elif font_format == "CFF":
        # now package the CFF font as an OTF font.
        with open(path, "rb") as ff:
            data = ff.read()

        ttFont = TTFont()
        cffClass = getTableClass('CFF ')
        ttFont['CFF '] = cffClass('CFF ')
        ttFont['CFF '].decompile(data, ttFont)
    else:
        raise ACFontError("Font file must be CFF or OTF file: %s." % path)

    fontData = CFFFontData(ttFont, path, outFilePath,
                           options.allowDecimalCoords, font_format)
    return fontData
Example #41
0
	def merge(self, fontfiles):

		mega = ttLib.TTFont()

		#
		# Settle on a mega glyph order.
		#
		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
		glyphOrders = [font.getGlyphOrder() for font in fonts]
		megaGlyphOrder = self._mergeGlyphOrders(glyphOrders)
		# Reload fonts and set new glyph names on them.
		# TODO Is it necessary to reload font?  I think it is.  At least
		# it's safer, in case tables were loaded to provide glyph names.
		fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
		for font,glyphOrder in zip(fonts, glyphOrders):
			font.setGlyphOrder(glyphOrder)
		mega.setGlyphOrder(megaGlyphOrder)

		for font in fonts:
			self._preMerge(font)

		allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
		allTags.remove('GlyphOrder')
		for tag in allTags:

			clazz = ttLib.getTableClass(tag)

			tables = [font.get(tag, NotImplemented) for font in fonts]
			table = clazz(tag).merge(self, tables)
			if table is not NotImplemented and table is not False:
				mega[tag] = table
				self.log("Merged '%s'." % tag)
			else:
				self.log("Dropped '%s'." % tag)
			self.log.lapse("merge '%s'" % tag)

		self._postMerge(mega)

		return mega
Example #42
0
def parseCmap(lines, font):
	container = ttLib.getTableClass('cmap')()
	log.debug("Parsing cmap")
	tables = []
	while lines.peek() is not None:
		lines.expect('cmap subtable %d' % len(tables))
		platId, encId, fmt, lang = [
			parseCmapId(lines, field)
			for field in ('platformID', 'encodingID', 'format', 'language')]
		table = cmap_classes[fmt](fmt)
		table.platformID = platId
		table.platEncID = encId
		table.language = lang
		table.cmap = {}
		line = next(lines)
		while line[0] != 'end subtable':
			table.cmap[int(line[0], 16)] = line[1]
			line = next(lines)
		tables.append(table)
	container.tableVersion = 0
	container.tables = tables
	return container
Example #43
0
def parseCmap(lines, font):
	container = ttLib.getTableClass('cmap')()
	log.debug("Parsing cmap")
	tables = []
	while lines.peek() is not None:
		lines.expect('cmap subtable %d' % len(tables))
		platId, encId, fmt, lang = [
			parseCmapId(lines, field)
			for field in ('platformID', 'encodingID', 'format', 'language')]
		table = cmap_classes[fmt](fmt)
		table.platformID = platId
		table.platEncID = encId
		table.language = lang
		table.cmap = {}
		line = next(lines)
		while line[0] != 'end subtable':
			table.cmap[int(line[0], 16)] = line[1]
			line = next(lines)
		tables.append(table)
	container.tableVersion = 0
	container.tables = tables
	return container
Example #44
0
@_add_method(DefaultTable, allowDefaultTable=True)
def merge(self, m, tables):
	if not hasattr(self, 'mergeMap'):
		log.info("Don't know how to merge '%s'.", self.tableTag)
		return NotImplemented

	logic = self.mergeMap

	if isinstance(logic, dict):
		return m.mergeObjects(self, self.mergeMap, tables)
	else:
		return logic(tables)


ttLib.getTableClass('maxp').mergeMap = {
	'*': max,
	'tableTag': equal,
	'tableVersion': equal,
	'numGlyphs': sum,
	'maxStorage': first,
	'maxFunctionDefs': first,
	'maxInstructionDefs': first,
	# TODO When we correctly merge hinting data, update these values:
	# maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
}

headFlagsMergeBitMap = {
	'size': 16,
	'*': bitwise_or,
	1: bitwise_and, # Baseline at y = 0
Example #45
0
 def build(self):
     parsetree = Parser(self.featurefile_path).parse()
     parsetree.build(self)
     for tag in ('GPOS', 'GSUB'):
         fontTable = self.font[tag] = getTableClass(tag)()
         fontTable.table = self.makeTable(tag)
Example #46
0
from fontTools import ttLib

superclass = ttLib.getTableClass("hmtx")

class table__v_m_t_x(superclass):
	
	headerTag = 'vhea'
	advanceName = 'height'
	sideBearingName = 'tsb'
	numberOfMetricsName = 'numberOfVMetrics'
Example #47
0
    # Yield sorted, non-overlapping (min, max) ranges of consecutive integers
    sorted_ints = iter(sorted(set(ints)))
    try:
        start = end = next(sorted_ints)
    except StopIteration:
        return
    for v in sorted_ints:
        if v - 1 == end:
            end = v
        else:
            yield (start, end)
            start = end = v
    yield (start, end)


@_add_method(ttLib.getTableClass("SVG "))
def subset_glyphs(self, s) -> bool:
    if etree is None:
        raise ModuleNotFoundError(
            "No module named 'lxml', required to subset SVG")

    # glyph names (before subsetting)
    glyph_order: List[str] = s.orig_glyph_order
    # map from glyph names to original glyph indices
    rev_orig_glyph_map: Dict[str, int] = s.reverseOrigGlyphMap
    # map from original to new glyph indices (after subsetting)
    glyph_index_map: Dict[int, int] = s.glyph_index_map

    new_docs: List[Tuple[bytes, int, int]] = []
    for doc, start, end in self.docList:
Example #48
0
from fontTools.misc.py23 import *
from fontTools.misc.testTools import FakeFont, getXML, parseXML
from fontTools.misc.textTools import deHexStr, hexStr
from fontTools.ttLib import TTLibError, getTableClass, getTableModule, newTable
import unittest
from fontTools.ttLib.tables.TupleVariation import TupleVariation


gvarClass = getTableClass("gvar")


GVAR_DATA = deHexStr(
    "0001 0000 "      #   0: majorVersion=1 minorVersion=0
    "0002 0000 "      #   4: axisCount=2 sharedTupleCount=0
    "0000001C "       #   8: offsetToSharedTuples=28
    "0003 0000 "      #  12: glyphCount=3 flags=0
    "0000001C "       #  16: offsetToGlyphVariationData=28
    "0000 0000 000C 002F " #  20: offsets=[0,0,12,47], times 2: [0,0,24,94],
    #                 #           +offsetToGlyphVariationData: [28,28,52,122]
    #
    # 28: Glyph variation data for glyph #0, ".notdef"
    # ------------------------------------------------
    # (no variation data for this glyph)
    #
    # 28: Glyph variation data for glyph #1, "space"
    # ----------------------------------------------
    "8001 000C "      #  28: tupleVariationCount=1|TUPLES_SHARE_POINT_NUMBERS, offsetToData=12(+28=40)
    "000A "           #  32: tvHeader[0].variationDataSize=10
    "8000 "           #  34: tvHeader[0].tupleIndex=EMBEDDED_PEAK
    "0000 2CCD "      #  36: tvHeader[0].peakTuple={wght:0.0, wdth:0.7}
    "00 "             #  40: all points
Example #49
0
from fontTools import ttLib

superclass = ttLib.getTableClass("fpgm")


class table__p_r_e_p(superclass):
    pass
Example #50
0
from __future__ import print_function, division, absolute_import, unicode_literals
from fontTools.misc.py23 import *
from fontTools.misc.testTools import FakeFont, getXML, parseXML
from fontTools.misc.textTools import deHexStr, hexStr
from fontTools.ttLib import TTLibError, getTableClass, getTableModule, newTable
import unittest
from fontTools.ttLib.tables.TupleVariation import TupleVariation


gvarClass = getTableClass("gvar")


GVAR_DATA = deHexStr(
    "0001 0000 "  #   0: majorVersion=1 minorVersion=0
    "0002 0000 "  #   4: axisCount=2 sharedTupleCount=0
    "0000001C "  #   8: offsetToSharedTuples=28
    "0003 0000 "  #  12: glyphCount=3 flags=0
    "0000001C "  #  16: offsetToGlyphVariationData=28
    "0000 0000 000C 002F "  #  20: offsets=[0,0,12,47], times 2: [0,0,24,94],
    #                 #           +offsetToGlyphVariationData: [28,28,52,122]
    #
    # 28: Glyph variation data for glyph #0, ".notdef"
    # ------------------------------------------------
    # (no variation data for this glyph)
    #
    # 28: Glyph variation data for glyph #1, "space"
    # ----------------------------------------------
    "0001 000C "  #  28: tupleVariationCount=1, offsetToData=12(+28=40)
    "000B "  #  32: tvHeader[0].variationDataSize=11
    "A000 "  #  34: tvHeader[0].tupleIndex=EMBEDDED_PEAK|PRIVATE_POINTS
    "0000 2CCD "  #  36: tvHeader[0].peakTuple={wght:0.0, wdth:0.7}
@_add_method(DefaultTable, allowDefaultTable=True)
def merge(self, m, tables):
	if not hasattr(self, 'mergeMap'):
		m.log("Don't know how to merge '%s'." % self.tableTag)
		return NotImplemented

	logic = self.mergeMap

	if isinstance(logic, dict):
		return m.mergeObjects(self, self.mergeMap, tables)
	else:
		return logic(tables)


ttLib.getTableClass('maxp').mergeMap = {
	'*': max,
	'tableTag': equal,
	'tableVersion': equal,
	'numGlyphs': sum,
	'maxStorage': first,
	'maxFunctionDefs': first,
	'maxInstructionDefs': first,
	# TODO When we correctly merge hinting data, update these values:
	# maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
}

headFlagsMergeBitMap = {
	'size': 16,
	'*': bitwise_or,
	1: bitwise_and, # Baseline at y = 0
Example #52
0
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools import ttLib

superclass = ttLib.getTableClass("fpgm")


class table__p_r_e_p(superclass):
    pass
Example #53
0
							  globalSubrs)
		self.components = components

	def op_endchar(self, index):
		args = self.popall()
		if len(args) >= 4:
			from fontTools.encodings.StandardEncoding import StandardEncoding
			# endchar can do seac accent bulding; The T2 spec says it's deprecated,
			# but recent software that shall remain nameless does output it.
			adx, ady, bchar, achar = args[-4:]
			baseGlyph = StandardEncoding[bchar]
			accentGlyph = StandardEncoding[achar]
			self.components.add(baseGlyph)
			self.components.add(accentGlyph)

@_add_method(ttLib.getTableClass('CFF '))
def closure_glyphs(self, s):
	cff = self.cff
	assert len(cff) == 1
	font = cff[cff.keys()[0]]
	glyphSet = font.CharStrings

	decompose = s.glyphs
	while decompose:
		components = set()
		for g in decompose:
			if g not in glyphSet:
				continue
			gl = glyphSet[g]

			subrs = getattr(gl.private, "Subrs", [])
Example #54
0
class WOFF2HmtxTable(getTableClass("hmtx")):

	def __init__(self, tag=None):
		self.tableTag = Tag(tag or 'hmtx')

	def reconstruct(self, data, ttFont):
		flags, = struct.unpack(">B", data[:1])
		data = data[1:]
		if flags & 0b11111100 != 0:
			raise TTLibError("Bits 2-7 of '%s' flags are reserved" % self.tableTag)

		# When bit 0 is _not_ set, the lsb[] array is present
		hasLsbArray = flags & 1 == 0
		# When bit 1 is _not_ set, the leftSideBearing[] array is present
		hasLeftSideBearingArray = flags & 2 == 0
		if hasLsbArray and hasLeftSideBearingArray:
			raise TTLibError(
				"either bits 0 or 1 (or both) must set in transformed '%s' flags"
				% self.tableTag
			)

		glyfTable = ttFont["glyf"]
		headerTable = ttFont["hhea"]
		glyphOrder = glyfTable.glyphOrder
		numGlyphs = len(glyphOrder)
		numberOfHMetrics = min(int(headerTable.numberOfHMetrics), numGlyphs)

		assert len(data) >= 2 * numberOfHMetrics
		advanceWidthArray = array.array("H", data[:2 * numberOfHMetrics])
		if sys.byteorder != "big":
			advanceWidthArray.byteswap()
		data = data[2 * numberOfHMetrics:]

		if hasLsbArray:
			assert len(data) >= 2 * numberOfHMetrics
			lsbArray = array.array("h", data[:2 * numberOfHMetrics])
			if sys.byteorder != "big":
				lsbArray.byteswap()
			data = data[2 * numberOfHMetrics:]
		else:
			# compute (proportional) glyphs' lsb from their xMin
			lsbArray = array.array("h")
			for i, glyphName in enumerate(glyphOrder):
				if i >= numberOfHMetrics:
					break
				glyph = glyfTable[glyphName]
				xMin = getattr(glyph, "xMin", 0)
				lsbArray.append(xMin)

		numberOfSideBearings = numGlyphs - numberOfHMetrics
		if hasLeftSideBearingArray:
			assert len(data) >= 2 * numberOfSideBearings
			leftSideBearingArray = array.array("h", data[:2 * numberOfSideBearings])
			if sys.byteorder != "big":
				leftSideBearingArray.byteswap()
			data = data[2 * numberOfSideBearings:]
		else:
			# compute (monospaced) glyphs' leftSideBearing from their xMin
			leftSideBearingArray = array.array("h")
			for i, glyphName in enumerate(glyphOrder):
				if i < numberOfHMetrics:
					continue
				glyph = glyfTable[glyphName]
				xMin = getattr(glyph, "xMin", 0)
				leftSideBearingArray.append(xMin)

		if data:
			raise TTLibError("too much '%s' table data" % self.tableTag)

		self.metrics = {}
		for i in range(numberOfHMetrics):
			glyphName = glyphOrder[i]
			advanceWidth, lsb = advanceWidthArray[i], lsbArray[i]
			self.metrics[glyphName] = (advanceWidth, lsb)
		lastAdvance = advanceWidthArray[-1]
		for i in range(numberOfSideBearings):
			glyphName = glyphOrder[i + numberOfHMetrics]
			self.metrics[glyphName] = (lastAdvance, leftSideBearingArray[i])

	def transform(self, ttFont):
		glyphOrder = ttFont.getGlyphOrder()
		glyf = ttFont["glyf"]
		hhea = ttFont["hhea"]
		numberOfHMetrics = hhea.numberOfHMetrics

		# check if any of the proportional glyphs has left sidebearings that
		# differ from their xMin bounding box values.
		hasLsbArray = False
		for i in range(numberOfHMetrics):
			glyphName = glyphOrder[i]
			lsb = self.metrics[glyphName][1]
			if lsb != getattr(glyf[glyphName], "xMin", 0):
				hasLsbArray = True
				break

		# do the same for the monospaced glyphs (if any) at the end of hmtx table
		hasLeftSideBearingArray = False
		for i in range(numberOfHMetrics, len(glyphOrder)):
			glyphName = glyphOrder[i]
			lsb = self.metrics[glyphName][1]
			if lsb != getattr(glyf[glyphName], "xMin", 0):
				hasLeftSideBearingArray = True
				break

		# if we need to encode both sidebearings arrays, then no transformation is
		# applicable, and we must use the untransformed hmtx data
		if hasLsbArray and hasLeftSideBearingArray:
			return

		# set bit 0 and 1 when the respective arrays are _not_ present
		flags = 0
		if not hasLsbArray:
			flags |= 1 << 0
		if not hasLeftSideBearingArray:
			flags |= 1 << 1

		data = struct.pack(">B", flags)

		advanceWidthArray = array.array(
			"H",
			[
				self.metrics[glyphName][0]
				for i, glyphName in enumerate(glyphOrder)
				if i < numberOfHMetrics
			]
		)
		if sys.byteorder != "big":
			advanceWidthArray.byteswap()
		data += advanceWidthArray.tobytes()

		if hasLsbArray:
			lsbArray = array.array(
				"h",
				[
					self.metrics[glyphName][1]
					for i, glyphName in enumerate(glyphOrder)
					if i < numberOfHMetrics
				]
			)
			if sys.byteorder != "big":
				lsbArray.byteswap()
			data += lsbArray.tobytes()

		if hasLeftSideBearingArray:
			leftSideBearingArray = array.array(
				"h",
				[
					self.metrics[glyphOrder[i]][1]
					for i in range(numberOfHMetrics, len(glyphOrder))
				]
			)
			if sys.byteorder != "big":
				leftSideBearingArray.byteswap()
			data += leftSideBearingArray.tobytes()

		return data
Example #55
0
 def setUp(self):
     self.font = font = ttLib.TTFont(recalcBBoxes=False,
                                     recalcTimestamp=False)
     font['head'] = ttLib.getTableClass('head')
     font['loca'] = WOFF2LocaTable()
     font['glyf'] = WOFF2GlyfTable()
Example #56
0
@_add_method(DefaultTable, allowDefaultTable=True)
def merge(self, m, tables):
	if not hasattr(self, 'mergeMap'):
		m.log("Don't know how to merge '%s'." % self.tableTag)
		return NotImplemented

	logic = self.mergeMap

	if isinstance(logic, dict):
		return m.mergeObjects(self, self.mergeMap, tables)
	else:
		return logic(tables)


ttLib.getTableClass('maxp').mergeMap = {
	'*': max,
	'tableTag': equal,
	'tableVersion': equal,
	'numGlyphs': sum,
	'maxStorage': first,
	'maxFunctionDefs': first,
	'maxInstructionDefs': first,
	# TODO When we correctly merge hinting data, update these values:
	# maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
}

headFlagsMergeMap = {
	'size': 16,
	'*': bitwise_or,
	1: bitwise_and, # Baseline at y = 0
Example #57
0
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools import ttLib

superclass = ttLib.getTableClass("TSI0")

class table_T_S_I__2(superclass):

	dependencies = ["TSI3"]
Example #58
0
from fontTools import ttLib

superclass = ttLib.getTableClass(b"TSI1")

class table_T_S_I__3(superclass):
	
	extras = {0xfffa: "reserved0", 0xfffb: "reserved1", 0xfffc: "reserved2", 0xfffd: "reserved3"}
	
	indextable = b"TSI2"


Example #59
0
							  globalSubrs)
		self.components = components

	def op_endchar(self, index):
		args = self.popall()
		if len(args) >= 4:
			from fontTools.encodings.StandardEncoding import StandardEncoding
			# endchar can do seac accent bulding; The T2 spec says it's deprecated,
			# but recent software that shall remain nameless does output it.
			adx, ady, bchar, achar = args[-4:]
			baseGlyph = StandardEncoding[bchar]
			accentGlyph = StandardEncoding[achar]
			self.components.add(baseGlyph)
			self.components.add(accentGlyph)

@_add_method(ttLib.getTableClass('CFF '))
def closure_glyphs(self, s):
	cff = self.cff
	assert len(cff) == 1
	font = cff[cff.keys()[0]]
	glyphSet = font.CharStrings

	decompose = s.glyphs
	while decompose:
		components = set()
		for g in decompose:
			if g not in glyphSet:
				continue
			gl = glyphSet[g]

			subrs = getattr(gl.private, "Subrs", [])
Example #60
0
class WOFF2GlyfTable(getTableClass('glyf')):
    """Decoder/Encoder for WOFF2 'glyf' table transform."""

    subStreams = ('nContourStream', 'nPointsStream', 'flagStream',
                  'glyphStream', 'compositeStream', 'bboxStream',
                  'instructionStream')

    def __init__(self, tag=None):
        self.tableTag = Tag(tag or 'glyf')

    def reconstruct(self, data, ttFont):
        """ Decompile transformed 'glyf' data. """
        inputDataSize = len(data)

        if inputDataSize < woff2GlyfTableFormatSize:
            raise TTLibError("not enough 'glyf' data")
        dummy, data = sstruct.unpack2(woff2GlyfTableFormat, data, self)
        offset = woff2GlyfTableFormatSize

        for stream in self.subStreams:
            size = getattr(self, stream + 'Size')
            setattr(self, stream, data[:size])
            data = data[size:]
            offset += size

        if offset != inputDataSize:
            raise TTLibError(
                "incorrect size of transformed 'glyf' table: expected %d, received %d bytes"
                % (offset, inputDataSize))

        bboxBitmapSize = ((self.numGlyphs + 31) >> 5) << 2
        bboxBitmap = self.bboxStream[:bboxBitmapSize]
        self.bboxBitmap = array.array('B', bboxBitmap)
        self.bboxStream = self.bboxStream[bboxBitmapSize:]

        self.nContourStream = array.array("h", self.nContourStream)
        if sys.byteorder != "big":
            self.nContourStream.byteswap()
        assert len(self.nContourStream) == self.numGlyphs

        if 'head' in ttFont:
            ttFont['head'].indexToLocFormat = self.indexFormat
        try:
            self.glyphOrder = ttFont.getGlyphOrder()
        except:
            self.glyphOrder = None
        if self.glyphOrder is None:
            self.glyphOrder = [".notdef"]
            self.glyphOrder.extend(
                ["glyph%.5d" % i for i in range(1, self.numGlyphs)])
        else:
            if len(self.glyphOrder) != self.numGlyphs:
                raise TTLibError(
                    "incorrect glyphOrder: expected %d glyphs, found %d" %
                    (len(self.glyphOrder), self.numGlyphs))

        glyphs = self.glyphs = {}
        for glyphID, glyphName in enumerate(self.glyphOrder):
            glyph = self._decodeGlyph(glyphID)
            glyphs[glyphName] = glyph

    def transform(self, ttFont):
        """ Return transformed 'glyf' data """
        self.numGlyphs = len(self.glyphs)
        if not hasattr(self, "glyphOrder"):
            try:
                self.glyphOrder = ttFont.getGlyphOrder()
            except:
                self.glyphOrder = None
            if self.glyphOrder is None:
                self.glyphOrder = [".notdef"]
                self.glyphOrder.extend(
                    ["glyph%.5d" % i for i in range(1, self.numGlyphs)])
        if len(self.glyphOrder) != self.numGlyphs:
            raise TTLibError(
                "incorrect glyphOrder: expected %d glyphs, found %d" %
                (len(self.glyphOrder), self.numGlyphs))

        if 'maxp' in ttFont:
            ttFont['maxp'].numGlyphs = self.numGlyphs
        self.indexFormat = ttFont['head'].indexToLocFormat

        for stream in self.subStreams:
            setattr(self, stream, b"")
        bboxBitmapSize = ((self.numGlyphs + 31) >> 5) << 2
        self.bboxBitmap = array.array('B', [0] * bboxBitmapSize)

        for glyphID in range(self.numGlyphs):
            self._encodeGlyph(glyphID)

        self.bboxStream = self.bboxBitmap.tostring() + self.bboxStream
        for stream in self.subStreams:
            setattr(self, stream + 'Size', len(getattr(self, stream)))
        self.version = 0
        data = sstruct.pack(woff2GlyfTableFormat, self)
        data += bytesjoin([getattr(self, s) for s in self.subStreams])
        return data

    def _decodeGlyph(self, glyphID):
        glyph = getTableModule('glyf').Glyph()
        glyph.numberOfContours = self.nContourStream[glyphID]
        if glyph.numberOfContours == 0:
            return glyph
        elif glyph.isComposite():
            self._decodeComponents(glyph)
        else:
            self._decodeCoordinates(glyph)
        self._decodeBBox(glyphID, glyph)
        return glyph

    def _decodeComponents(self, glyph):
        data = self.compositeStream
        glyph.components = []
        more = 1
        haveInstructions = 0
        while more:
            component = getTableModule('glyf').GlyphComponent()
            more, haveInstr, data = component.decompile(data, self)
            haveInstructions = haveInstructions | haveInstr
            glyph.components.append(component)
        self.compositeStream = data
        if haveInstructions:
            self._decodeInstructions(glyph)

    def _decodeCoordinates(self, glyph):
        data = self.nPointsStream
        endPtsOfContours = []
        endPoint = -1
        for i in range(glyph.numberOfContours):
            ptsOfContour, data = unpack255UShort(data)
            endPoint += ptsOfContour
            endPtsOfContours.append(endPoint)
        glyph.endPtsOfContours = endPtsOfContours
        self.nPointsStream = data
        self._decodeTriplets(glyph)
        self._decodeInstructions(glyph)

    def _decodeInstructions(self, glyph):
        glyphStream = self.glyphStream
        instructionStream = self.instructionStream
        instructionLength, glyphStream = unpack255UShort(glyphStream)
        glyph.program = ttProgram.Program()
        glyph.program.fromBytecode(instructionStream[:instructionLength])
        self.glyphStream = glyphStream
        self.instructionStream = instructionStream[instructionLength:]

    def _decodeBBox(self, glyphID, glyph):
        haveBBox = bool(self.bboxBitmap[glyphID >> 3]
                        & (0x80 >> (glyphID & 7)))
        if glyph.isComposite() and not haveBBox:
            raise TTLibError('no bbox values for composite glyph %d' % glyphID)
        if haveBBox:
            dummy, self.bboxStream = sstruct.unpack2(bboxFormat,
                                                     self.bboxStream, glyph)
        else:
            glyph.recalcBounds(self)

    def _decodeTriplets(self, glyph):
        def withSign(flag, baseval):
            assert 0 <= baseval and baseval < 65536, 'integer overflow'
            return baseval if flag & 1 else -baseval

        nPoints = glyph.endPtsOfContours[-1] + 1
        flagSize = nPoints
        if flagSize > len(self.flagStream):
            raise TTLibError("not enough 'flagStream' data")
        flagsData = self.flagStream[:flagSize]
        self.flagStream = self.flagStream[flagSize:]
        flags = array.array('B', flagsData)

        triplets = array.array('B', self.glyphStream)
        nTriplets = len(triplets)
        assert nPoints <= nTriplets

        x = 0
        y = 0
        glyph.coordinates = getTableModule('glyf').GlyphCoordinates.zeros(
            nPoints)
        glyph.flags = array.array("B")
        tripletIndex = 0
        for i in range(nPoints):
            flag = flags[i]
            onCurve = not bool(flag >> 7)
            flag &= 0x7f
            if flag < 84:
                nBytes = 1
            elif flag < 120:
                nBytes = 2
            elif flag < 124:
                nBytes = 3
            else:
                nBytes = 4
            assert ((tripletIndex + nBytes) <= nTriplets)
            if flag < 10:
                dx = 0
                dy = withSign(flag,
                              ((flag & 14) << 7) + triplets[tripletIndex])
            elif flag < 20:
                dx = withSign(flag, (((flag - 10) & 14) << 7) +
                              triplets[tripletIndex])
                dy = 0
            elif flag < 84:
                b0 = flag - 20
                b1 = triplets[tripletIndex]
                dx = withSign(flag, 1 + (b0 & 0x30) + (b1 >> 4))
                dy = withSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f))
            elif flag < 120:
                b0 = flag - 84
                dx = withSign(flag,
                              1 + ((b0 // 12) << 8) + triplets[tripletIndex])
                dy = withSign(
                    flag >> 1,
                    1 + (((b0 % 12) >> 2) << 8) + triplets[tripletIndex + 1])
            elif flag < 124:
                b2 = triplets[tripletIndex + 1]
                dx = withSign(flag, (triplets[tripletIndex] << 4) + (b2 >> 4))
                dy = withSign(flag >> 1,
                              ((b2 & 0x0f) << 8) + triplets[tripletIndex + 2])
            else:
                dx = withSign(flag, (triplets[tripletIndex] << 8) +
                              triplets[tripletIndex + 1])
                dy = withSign(flag >> 1, (triplets[tripletIndex + 2] << 8) +
                              triplets[tripletIndex + 3])
            tripletIndex += nBytes
            x += dx
            y += dy
            glyph.coordinates[i] = (x, y)
            glyph.flags.append(int(onCurve))
        bytesConsumed = tripletIndex
        self.glyphStream = self.glyphStream[bytesConsumed:]

    def _encodeGlyph(self, glyphID):
        glyphName = self.getGlyphName(glyphID)
        glyph = self[glyphName]
        self.nContourStream += struct.pack(">h", glyph.numberOfContours)
        if glyph.numberOfContours == 0:
            return
        elif glyph.isComposite():
            self._encodeComponents(glyph)
        else:
            self._encodeCoordinates(glyph)
        self._encodeBBox(glyphID, glyph)

    def _encodeComponents(self, glyph):
        lastcomponent = len(glyph.components) - 1
        more = 1
        haveInstructions = 0
        for i in range(len(glyph.components)):
            if i == lastcomponent:
                haveInstructions = hasattr(glyph, "program")
                more = 0
            component = glyph.components[i]
            self.compositeStream += component.compile(more, haveInstructions,
                                                      self)
        if haveInstructions:
            self._encodeInstructions(glyph)

    def _encodeCoordinates(self, glyph):
        lastEndPoint = -1
        for endPoint in glyph.endPtsOfContours:
            ptsOfContour = endPoint - lastEndPoint
            self.nPointsStream += pack255UShort(ptsOfContour)
            lastEndPoint = endPoint
        self._encodeTriplets(glyph)
        self._encodeInstructions(glyph)

    def _encodeInstructions(self, glyph):
        instructions = glyph.program.getBytecode()
        self.glyphStream += pack255UShort(len(instructions))
        self.instructionStream += instructions

    def _encodeBBox(self, glyphID, glyph):
        assert glyph.numberOfContours != 0, "empty glyph has no bbox"
        if not glyph.isComposite():
            # for simple glyphs, compare the encoded bounding box info with the calculated
            # values, and if they match omit the bounding box info
            currentBBox = glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax
            calculatedBBox = calcIntBounds(glyph.coordinates)
            if currentBBox == calculatedBBox:
                return
        self.bboxBitmap[glyphID >> 3] |= 0x80 >> (glyphID & 7)
        self.bboxStream += sstruct.pack(bboxFormat, glyph)

    def _encodeTriplets(self, glyph):
        assert len(glyph.coordinates) == len(glyph.flags)
        coordinates = glyph.coordinates.copy()
        coordinates.absoluteToRelative()

        flags = array.array('B')
        triplets = array.array('B')
        for i in range(len(coordinates)):
            onCurve = glyph.flags[i]
            x, y = coordinates[i]
            absX = abs(x)
            absY = abs(y)
            onCurveBit = 0 if onCurve else 128
            xSignBit = 0 if (x < 0) else 1
            ySignBit = 0 if (y < 0) else 1
            xySignBits = xSignBit + 2 * ySignBit

            if x == 0 and absY < 1280:
                flags.append(onCurveBit + ((absY & 0xf00) >> 7) + ySignBit)
                triplets.append(absY & 0xff)
            elif y == 0 and absX < 1280:
                flags.append(onCurveBit + 10 + ((absX & 0xf00) >> 7) +
                             xSignBit)
                triplets.append(absX & 0xff)
            elif absX < 65 and absY < 65:
                flags.append(onCurveBit + 20 + ((absX - 1) & 0x30) +
                             (((absY - 1) & 0x30) >> 2) + xySignBits)
                triplets.append((((absX - 1) & 0xf) << 4) | ((absY - 1) & 0xf))
            elif absX < 769 and absY < 769:
                flags.append(onCurveBit + 84 + 12 *
                             (((absX - 1) & 0x300) >> 8) +
                             (((absY - 1) & 0x300) >> 6) + xySignBits)
                triplets.append((absX - 1) & 0xff)
                triplets.append((absY - 1) & 0xff)
            elif absX < 4096 and absY < 4096:
                flags.append(onCurveBit + 120 + xySignBits)
                triplets.append(absX >> 4)
                triplets.append(((absX & 0xf) << 4) | (absY >> 8))
                triplets.append(absY & 0xff)
            else:
                flags.append(onCurveBit + 124 + xySignBits)
                triplets.append(absX >> 8)
                triplets.append(absX & 0xff)
                triplets.append(absY >> 8)
                triplets.append(absY & 0xff)

        self.flagStream += flags.tostring()
        self.glyphStream += triplets.tostring()