def test_updateGlyphOrder_rename(self): font = Font(getTestFontPath()) self.assertEqual(font.glyphOrder, []) font.glyphOrder = sorted(font.keys()) self.assertEqual(font.glyphOrder, ["A", "B", "C"]) font.updateGlyphOrder(addedGlyph="new", removedGlyph="B") self.assertEqual(font.glyphOrder, ["A", "new", "C"])
def test_updateGlyphOrder_remove(self): font = Font(getTestFontPath()) self.assertEqual(font.glyphOrder, []) font.glyphOrder = ["test"] self.assertEqual(font.glyphOrder, ["test"]) font.updateGlyphOrder(removedGlyph="test") self.assertEqual(font.glyphOrder, [])
def test_glyphOrder(self): font = Font(getTestFontPath()) self.assertEqual(font.glyphOrder, []) font.glyphOrder = sorted(font.keys()) self.assertEqual(font.glyphOrder, ["A", "B", "C"]) layer = font.layers["public.default"] layer.newGlyph("X") self.assertEqual(sorted(layer.keys()), ["A", "B", "C", "X"]) self.assertEqual(font.glyphOrder, ["A", "B", "C", "X"]) del layer["A"] self.assertEqual(font.glyphOrder, ["A", "B", "C", "X"]) del layer["X"] self.assertEqual(font.glyphOrder, ["A", "B", "C"])
def test_glyphOrder(self): font = Font(getTestFontPath()) self.assertEqual(font.glyphOrder, []) font.glyphOrder = sorted(font.keys()) self.assertEqual(font.glyphOrder, ["A", "B", "C"]) layer = font.layers.defaultLayer layer.newGlyph("X") self.assertEqual(sorted(layer.keys()), ["A", "B", "C", "X"]) self.assertEqual(font.glyphOrder, ["A", "B", "C", "X"]) del layer["A"] self.assertEqual(font.glyphOrder, ["A", "B", "C", "X"]) del layer["X"] self.assertEqual(font.glyphOrder, ["A", "B", "C"]) layer["B"].name = "Y" self.assertEqual(font.glyphOrder, ["A", "Y", "C"])
def merge(args): arabic = Font(args.arabicfile) to_ufo_propagate_font_anchors(None, arabic) latin = Font(args.latinfile) addPlaceHolders(arabic) unicodes = [] for glyph in arabic: unicodes.extend(glyph.unicodes) for name in latin.glyphOrder: if name in ("space", "nbspace", "CR", "NULL", ".notdef"): continue glyph = latin[name] assert glyph.name not in arabic, glyph.name assert glyph.unicodes not in unicodes, glyph.unicodes # Strip anchors from f_ ligatures, there are broken. # See https://github.com/googlei18n/glyphsLib/issues/313 if name.startswith("f_"): glyph.anchors = {} arabic.insertGlyph(glyph) # Copy kerning and groups. arabic.groups.update(latin.groups) arabic.kerning.update(latin.kerning) for attr in ("xHeight", "capHeight"): value = getattr(latin.info, attr) if value is not None: setattr(arabic.info, attr, getattr(latin.info, attr)) # Merge Arabic and Latin features, making sure languagesystem statements # come first. langsys = [] statements = [] for font in (arabic, latin): featurefile = UnicodeIO(tounicode(font.features.text)) fea = parser.Parser(featurefile, []).parse() langsys += [ s for s in fea.statements if isinstance(s, ast.LanguageSystemStatement) ] statements += [ s for s in fea.statements if not isinstance(s, ast.LanguageSystemStatement) ] # Drop GDEF table, we want to generate one based on final features. statements = [s for s in statements if not isinstance(s, ast.TableBlock)] # Make sure DFLT is the first. langsys = sorted(langsys, key=attrgetter("script")) fea.statements = langsys + statements arabic.features.text = fea.asFea() glyphOrder = arabic.glyphOrder + latin.glyphOrder # Make sure we have a fixed glyph order by using the original Arabic and # Latin glyph order, not whatever we end up with after adding glyphs. arabic.glyphOrder = sorted(arabic.glyphOrder, key=glyphOrder.index) # Set metadata arabic.info.versionMajor, arabic.info.versionMinor = map( int, args.version.split(".")) arabic.info.copyright = u"Copyright © 2015-%s The Reem Kufi Project Authors." % datetime.now( ).year return arabic
def merge(args): """Merges Arabic and Latin fonts together, and messages the combined font a bit. Returns the combined font.""" ufo = Font(args.arabicfile) propagate_font_anchors(ufo) latin = Font(args.latinfile) # Parse the GlyphOrderAndAliasDB file for Unicode values and production # glyph names of the Latin glyphs. goadb = GOADBParser( os.path.dirname(args.latinfile) + "/../GlyphOrderAndAliasDB") ufo.lib[POSTSCRIPT_NAMES] = {} # Save original glyph order, used below. glyphOrder = ufo.glyphOrder + latin.glyphOrder # Generate production glyph names for Arabic glyphs, in case it differs # from working names. This will be used by ufo2ft to set the final glyph # names in the font file. for glyph in ufo: if glyph.unicode is not None: if glyph.unicode < 0xffff: postName = "uni%04X" % glyph.unicode else: postName = "u%06X" % glyph.unicode if postName != glyph.name: ufo.lib[POSTSCRIPT_NAMES][glyph.name] = postName # Populate the font’s feature text, we keep our main feature file out of # the UFO to share it between the fonts. features = ufo.features with open(args.feature_file) as feafile: fea = feafile.read() # Set Latin language system, ufo2ft will use it when generating kern # feature. features.text += fea.replace("#{languagesystems}", "languagesystem latn dflt;") features.text += generateStyleSets(ufo) for glyph in latin: if glyph.name in goadb.encodings: glyph.unicode = goadb.encodings[glyph.name] # Source Sans Pro has different advance widths for space and NBSP # glyphs, fix it. latin["nbspace"].width = latin["space"].width glyphs, components = collectGlyphs(latin, args.latin_subset) counter = Counter(components) uniqueComponents = set() for name in counter: if name not in glyphs: latin[name].unicode = None if counter[name] == 1: uniqueComponents.add(name) else: glyphs.add(name) # Set Latin production names ufo.lib[POSTSCRIPT_NAMES].update(goadb.names) # Copy Latin glyphs. for name in glyphs: glyph = latin[name] for component in glyph.components: if component.baseGlyph in uniqueComponents: glyph.decomposeComponent(component) # Remove anchors from spacing marks, otherwise ufo2ft will give them # mark glyph class which will cause HarfBuzz to zero their width. if glyph.unicode and unicodedata.category(unichr( glyph.unicode)) in ("Sk", "Lm"): for anchor in glyph.anchors: glyph.removeAnchor(anchor) # Add Arabic anchors to the dotted circle, we use an offset of 100 # units because the Latin anchors are too close to the glyph. offset = 100 if glyph.unicode == 0x25CC: for anchor in glyph.anchors: if anchor.name == "aboveLC": glyph.appendAnchor( dict(name="markAbove", x=anchor.x, y=anchor.y + offset)) glyph.appendAnchor( dict(name="hamzaAbove", x=anchor.x, y=anchor.y + offset)) if anchor.name == "belowLC": glyph.appendAnchor( dict(name="markBelow", x=anchor.x, y=anchor.y - offset)) glyph.appendAnchor( dict(name="hamzaBelow", x=anchor.x, y=anchor.y - offset)) # Break loudly if we have duplicated glyph in Latin and Arabic. # TODO should check duplicated Unicode values as well assert glyph.name not in ufo, glyph.name ufo.insertGlyph(glyph) # Copy kerning and groups. ufo.groups.update(latin.groups) ufo.kerning.update(latin.kerning) # We don’t set these in the Arabic font, so we just copy the Latin’s. for attr in ("xHeight", "capHeight"): value = getattr(latin.info, attr) if value is not None: setattr(ufo.info, attr, getattr(latin.info, attr)) # MutatorMath does not like multiple unicodes and will drop it entirely, # turning the glyph unencoded: # https://github.com/LettError/MutatorMath/issues/85 for glyph in ufo: assert not " " in glyph.unicodes # Make sure we don’t have glyphs with the same unicode value unicodes = [] for glyph in ufo: unicodes.extend(glyph.unicodes) duplicates = set([u for u in unicodes if unicodes.count(u) > 1]) assert len(duplicates) == 0, "Duplicate unicodes: %s " % ( ["%04X" % d for d in duplicates]) # Make sure we have a fixed glyph order by using the original Arabic and # Latin glyph order, not whatever we end up with after adding glyphs. ufo.glyphOrder = sorted(ufo.glyphOrder, key=glyphOrder.index) return ufo
def merge(args): """Merges Arabic and Latin fonts together, and messages the combined font a bit. Returns the combined font.""" ufo = Font(args.arabicfile) to_ufo_propagate_font_anchors(None, ufo) latin = Font(args.latinfile) ufo.lib[POSTSCRIPT_NAMES] = {} # Save original glyph order, used below. glyphOrder = ufo.glyphOrder + latin.glyphOrder # Generate production glyph names for Arabic glyphs, in case it differs # from working names. This will be used by ufo2ft to set the final glyph # names in the font file. for glyph in ufo: if glyph.unicode is not None: if glyph.unicode < 0xffff: postName = "uni%04X" % glyph.unicode else: postName = "u%06X" % glyph.unicode if postName != glyph.name: ufo.lib[POSTSCRIPT_NAMES][glyph.name] = postName # Merge Arabic and Latin features, making sure languagesystem statements # come first. features = ufo.features langsys = [] statements = [] for font in (ufo, latin): featurefile = os.path.join(font.path, "features.fea") fea = parser.Parser(featurefile, font.glyphOrder).parse() langsys += [s for s in fea.statements if isinstance(s, ast.LanguageSystemStatement)] statements += [s for s in fea.statements if not isinstance(s, ast.LanguageSystemStatement)] # We will regenerate kern, mark and mkmk features, and aalt is useless. statements = [s for s in statements if getattr(s, "name", None) not in ("aalt", "kern", "mark", "mkmk")] # These will be regenerated as well statements = [s for s in statements if not isinstance(s, ast.MarkClassDefinition)] # Drop tables in fea, we don’t want them. statements = [s for s in statements if not isinstance(s, ast.TableBlock)] # Make sure DFLT is the first. langsys = sorted(langsys, key=attrgetter("script")) fea.statements = langsys + statements features.text = fea.asFea() features.text += generateStyleSets(ufo) # Source Sans Pro has different advance widths for space and NBSP # glyphs, fix it. latin["nbspace"].width = latin["space"].width # Set Latin production names ufo.lib[POSTSCRIPT_NAMES].update(latin.lib[POSTSCRIPT_NAMES]) # Copy Latin glyphs. for name in latin.glyphOrder: glyph = latin[name] # Remove anchors from spacing marks, otherwise ufo2ft will give them # mark glyph class which will cause HarfBuzz to zero their width. if glyph.unicode and unicodedata.category(unichr(glyph.unicode)) in ("Sk", "Lm"): for anchor in glyph.anchors: glyph.removeAnchor(anchor) # Add Arabic anchors to the dotted circle, we use an offset of 100 # units because the Latin anchors are too close to the glyph. offset = 100 if glyph.unicode == 0x25CC: for anchor in glyph.anchors: if anchor.name == "aboveLC": glyph.appendAnchor(dict(name="markAbove", x=anchor.x, y=anchor.y + offset)) glyph.appendAnchor(dict(name="hamzaAbove", x=anchor.x, y=anchor.y + offset)) if anchor.name == "belowLC": glyph.appendAnchor(dict(name="markBelow", x=anchor.x, y=anchor.y - offset)) glyph.appendAnchor(dict(name="hamzaBelow", x=anchor.x, y=anchor.y - offset)) # Break loudly if we have duplicated glyph in Latin and Arabic. # TODO should check duplicated Unicode values as well assert glyph.name not in ufo, glyph.name ufo.insertGlyph(glyph) # Copy kerning and groups. ufo.groups.update(latin.groups) ufo.kerning.update(latin.kerning) # We don’t set these in the Arabic font, so we just copy the Latin’s. for attr in ("xHeight", "capHeight"): value = getattr(latin.info, attr) if value is not None: setattr(ufo.info, attr, getattr(latin.info, attr)) # Make sure we don’t have glyphs with the same unicode value unicodes = [] for glyph in ufo: unicodes.extend(glyph.unicodes) duplicates = set([u for u in unicodes if unicodes.count(u) > 1]) assert len(duplicates) == 0, "Duplicate unicodes: %s " % (["%04X" % d for d in duplicates]) # Make sure we have a fixed glyph order by using the original Arabic and # Latin glyph order, not whatever we end up with after adding glyphs. ufo.glyphOrder = sorted(ufo.glyphOrder, key=glyphOrder.index) return ufo
def merge(args): """Merges Arabic and Latin fonts together, and messages the combined font a bit. Returns the combined font.""" ufo = Font(args.arabicfile) to_ufo_propagate_font_anchors(None, ufo) latin = Font(args.latinfile) ufo.lib[POSTSCRIPT_NAMES] = {} # Save original glyph order, used below. glyphOrder = ufo.glyphOrder + latin.glyphOrder # Generate production glyph names for Arabic glyphs, in case it differs # from working names. This will be used by ufo2ft to set the final glyph # names in the font file. for glyph in ufo: if glyph.unicode is not None: if glyph.unicode < 0xffff: postName = "uni%04X" % glyph.unicode else: postName = "u%06X" % glyph.unicode if postName != glyph.name: ufo.lib[POSTSCRIPT_NAMES][glyph.name] = postName # Merge Arabic and Latin features, making sure languagesystem statements # come first. features = ufo.features langsys = [] statements = [] for font in (ufo, latin): featurefile = os.path.join(font.path, "features.fea") fea = parser.Parser(featurefile, font.glyphOrder).parse() langsys += [ s for s in fea.statements if isinstance(s, ast.LanguageSystemStatement) ] statements += [ s for s in fea.statements if not isinstance(s, ast.LanguageSystemStatement) ] # We will regenerate kern, mark and mkmk features, and aalt is useless. statements = [ s for s in statements if getattr(s, "name", None) not in ("aalt", "kern", "mark", "mkmk") ] # These will be regenerated as well statements = [ s for s in statements if not isinstance(s, ast.MarkClassDefinition) ] # Drop tables in fea, we don’t want them. statements = [s for s in statements if not isinstance(s, ast.TableBlock)] # Make sure DFLT is the first. langsys = sorted(langsys, key=attrgetter("script")) fea.statements = langsys + statements features.text = fea.asFea() features.text += generateStyleSets(ufo) # Source Sans Pro has different advance widths for space and NBSP # glyphs, fix it. latin["nbspace"].width = latin["space"].width # Set Latin production names ufo.lib[POSTSCRIPT_NAMES].update(latin.lib[POSTSCRIPT_NAMES]) # Copy Latin glyphs. for name in latin.glyphOrder: glyph = latin[name] # Remove anchors from spacing marks, otherwise ufo2ft will give them # mark glyph class which will cause HarfBuzz to zero their width. if glyph.unicode and unicodedata.category(unichr( glyph.unicode)) in ("Sk", "Lm"): for anchor in glyph.anchors: glyph.removeAnchor(anchor) # Add Arabic anchors to the dotted circle, we use an offset of 100 # units because the Latin anchors are too close to the glyph. offset = 100 if glyph.unicode == 0x25CC: for anchor in glyph.anchors: if anchor.name == "aboveLC": glyph.appendAnchor( dict(name="markAbove", x=anchor.x, y=anchor.y + offset)) glyph.appendAnchor( dict(name="hamzaAbove", x=anchor.x, y=anchor.y + offset)) if anchor.name == "belowLC": glyph.appendAnchor( dict(name="markBelow", x=anchor.x, y=anchor.y - offset)) glyph.appendAnchor( dict(name="hamzaBelow", x=anchor.x, y=anchor.y - offset)) # Break loudly if we have duplicated glyph in Latin and Arabic. # TODO should check duplicated Unicode values as well assert glyph.name not in ufo, glyph.name ufo.insertGlyph(glyph) # Copy kerning and groups. ufo.groups.update(latin.groups) ufo.kerning.update(latin.kerning) # We don’t set these in the Arabic font, so we just copy the Latin’s. for attr in ("xHeight", "capHeight"): value = getattr(latin.info, attr) if value is not None: setattr(ufo.info, attr, getattr(latin.info, attr)) # Make sure we don’t have glyphs with the same unicode value unicodes = [] for glyph in ufo: unicodes.extend(glyph.unicodes) duplicates = set([u for u in unicodes if unicodes.count(u) > 1]) assert len(duplicates) == 0, "Duplicate unicodes: %s " % ( ["%04X" % d for d in duplicates]) # Make sure we have a fixed glyph order by using the original Arabic and # Latin glyph order, not whatever we end up with after adding glyphs. ufo.glyphOrder = sorted(ufo.glyphOrder, key=glyphOrder.index) return ufo