def __contains__(self, glyphName): if self._revCmap is not None and glyphName in self._revCmap: return True if glyphName in self._glyphs: return True fileName = userNameToFileName(glyphName, suffix=".glif") glyphPath = self._path / fileName return glyphPath.exists()
def makeFileName(self, fileName): """ Make a file system legal version of **fileName**. """ fileName = unicode(fileName) suffix = "" if fileName.lower().endswith(".png"): suffix = fileName[-4:] fileName = fileName[:-4] existing = set([i.lower() for i in self.fileNames]) return userNameToFileName(fileName, existing, suffix=suffix)
def extractGlyphNameAndUnicodes(data, fileName=None): m = _glyphNamePat.search(data) if m is None: raise ValueError( f"invalid .glif file, glyph name not found ({fileName})") glyphName = m.group(1).decode("utf-8") if fileName is not None: refFileName = userNameToFileName(glyphName, suffix=".glif") if refFileName != fileName: logger.warning( "actual file name does not match predicted file name: " f"{refFileName} {fileName} {glyphName}") unicodes = [int(u, 16) for u in _unicodePat.findall(data)] return glyphName, unicodes
def nameToFileName(name): return userNameToFileName(name)
def test_userNameToFileName(self): self.assertEqual(userNameToFileName("a"), "a") self.assertEqual(userNameToFileName("A"), "A_") self.assertEqual(userNameToFileName("AE"), "A_E_") self.assertEqual(userNameToFileName("Ae"), "A_e") self.assertEqual(userNameToFileName("ae"), "ae") self.assertEqual(userNameToFileName("aE"), "aE_") self.assertEqual(userNameToFileName("a.alt"), "a.alt") self.assertEqual(userNameToFileName("A.alt"), "A_.alt") self.assertEqual(userNameToFileName("A.Alt"), "A_.A_lt") self.assertEqual(userNameToFileName("A.aLt"), "A_.aL_t") self.assertEqual(userNameToFileName("A.alT"), "A_.alT_") self.assertEqual(userNameToFileName("T_H"), "T__H_") self.assertEqual(userNameToFileName("T_h"), "T__h") self.assertEqual(userNameToFileName("t_h"), "t_h") self.assertEqual(userNameToFileName("F_F_I"), "F__F__I_") self.assertEqual(userNameToFileName("f_f_i"), "f_f_i") self.assertEqual(userNameToFileName("Aacute_V.swash"), "A_acute_V_.swash") self.assertEqual(userNameToFileName(".notdef"), "_notdef") self.assertEqual(userNameToFileName("con"), "_con") self.assertEqual(userNameToFileName("CON"), "C_O_N_") self.assertEqual(userNameToFileName("con.alt"), "_con.alt") self.assertEqual(userNameToFileName("alt.con"), "alt._con")
def test_userNameToFileName_ValueError(self): with self.assertRaises(ValueError): userNameToFileName(b"a") with self.assertRaises(ValueError): userNameToFileName({"a"}) with self.assertRaises(ValueError): userNameToFileName(("a",)) with self.assertRaises(ValueError): userNameToFileName(["a"]) with self.assertRaises(ValueError): userNameToFileName(["a"]) with self.assertRaises(ValueError): userNameToFileName(b"\xd8\x00")
def main(): global maxInstructions, quietcount # First read the command-line arguments. At minimum we need the inputfile. argparser = argparse.ArgumentParser( prog='xgridfit', description= 'Compile XML into TrueType instructions and add them to a font.') argparser.add_argument('-v', '--version', action='version', version='Xgridfit ' + __version__) argparser.add_argument( '-e', '--expand', action="store_true", help="Convert file to expanded syntax, save, and exit") argparser.add_argument( '-c', '--compact', action="store_true", help="Convert file to compact syntax, save, and exit") argparser.add_argument('-n', '--novalidation', action="store_true", help="Skip validation of the Xgridfit program") argparser.add_argument('--nocompilation', action="store_true", help="Skip compilation of the Xgridfit program") argparser.add_argument( '--nocompact', action="store_true", help="Do not compact glyph programs (can help with debugging)") argparser.add_argument('-m', '--merge', action="store_true", help="Merge Xgridfit with existing instructions") argparser.add_argument( '-r', '--replaceprep', action="store_true", help= "Whether to replace the existing prep table or append the new one (use with --merge)" ) argparser.add_argument( '--initgraphics', choices=['yes', 'no'], help= "Whether to initialize graphics-tracking variables at the beginning of glyph program" ) argparser.add_argument( '-a', '--assume_y', choices=['yes', 'no'], help="Whether compiler should assume that your hints are all vertical") argparser.add_argument( '-q', '--quiet', action="count", default=0, help="No progress messages (-qq to suppress warnings too)") argparser.add_argument('-g', '--glyphlist', help="List of glyphs to compile") argparser.add_argument('-i', '--inputfont', action='store', type=str, help="The font file to add instructions to") argparser.add_argument('-o', '--outputfont', action='store', type=str, help="The font file to write") argparser.add_argument('-s', '--saveprograms', action="store_true", help="Save generated instructions to text files") argparser.add_argument( '-f', '--coordinatefuzz', type=int, default=1, help= "Error tolerance for points identified by coordinates (default is 1)") argparser.add_argument("inputfile", help='Xgridfit (XML) file to process.') argparser.add_argument( "outputfile", nargs='?', help="Filename for options (e.g. --expand) that produce text output") args = argparser.parse_args() inputfile = args.inputfile outputfile = args.outputfile inputfont = args.inputfont outputfont = args.outputfont skipval = args.novalidation skipcomp = args.nocompilation expandonly = args.expand compactonly = args.compact mergemode = args.merge quietcount = args.quiet initgraphics = args.initgraphics assume_y = args.assume_y glyphlist = args.glyphlist replaceprep = args.replaceprep saveprograms = args.saveprograms nocompact = args.nocompact cfuzz = args.coordinatefuzz if quietcount < 1: print("Opening the Xgridfit file ...") if cfuzz > 1: coordinateFuzz = cfuzz xgffile = etree.parse(inputfile) # We'll need namespaces ns = { "xgf": "http://xgridfit.sourceforge.net/Xgridfit2", "xi": "http://www.w3.org/2001/XInclude", "xsl": "http://www.w3.org/1999/XSL/Transform" } # Do xinclude if this is a multipart file if len(xgffile.xpath("/xgf:xgridfit/xi:include", namespaces=ns)): xgffile.xinclude() # Next determine whether we are using long tagnames or short. Best way # is to find out which tag is used for the required <pre-program> (<prep>) # element. If we don't find it, print an error message and exit. Here's # where we validate too; and if we're only expanding or compacting a file, # do that and exit before we go to the trouble of opening the font. if quietcount < 1: print("Validating ...") if len(xgffile.xpath("/xgf:xgridfit/xgf:prep", namespaces=ns)): # first validate validate(xgffile, "compact", skipval) # as we can't use the compact syntax, always expand if quietcount < 1: print("Expanding compact to normal syntax ...") xslfile = get_file_path("XSL/expand.xsl") etransform = etree.XSLT(etree.parse(xslfile)) xgffile = etransform(xgffile) if expandonly: tstr = str(xgffile) tstr = tstr.replace('xgf:', '') tstr = tstr.replace( 'xmlns:xgf="http://xgridfit.sourceforge.net/Xgridfit2"', '') if outputfile: of = open(outputfile, "w") of.write(tstr) of.close() else: print(tstr) sys.exit(0) elif len(xgffile.xpath("/xgf:xgridfit/xgf:pre-program", namespaces=ns)): validate(xgffile, "normal", skipval) if compactonly: xslfile = get_file_path("XSL/compact.xsl") etransform = etree.XSLT(etree.parse(xslfile)) xgffile = etransform(xgffile) tstr = str(xgffile) tstr = tstr.replace('xgf:', '') tstr = tstr.replace( 'xmlns:xgf="http://xgridfit.sourceforge.net/Xgridfit2"', '') tstr = tstr.replace(' >', '>') if outputfile: of = open(outputfile, "w") of.write(tstr) of.close() else: print(tstr) sys.exit(0) else: print( "The xgridfit program must contain a pre-program (prep) element,") print("even if it's empty.") sys.exit(1) if skipcomp and quietcount < 1: print("Skipping compilation") sys.exit(0) # Now open the font. If we're in merge-mode, we need to know some things # about the current state of it; otherwise we just wipe it. if quietcount < 1: print("Opening and evaluating the font ...") if not inputfont: inputfont = xgffile.xpath("/xgf:xgridfit/xgf:inputfont/text()", namespaces=ns)[0] if not inputfont: print("Need the filename of a font to read. Use the --inputfont") print("command-line argument or the <inputfont> element in your") print("Xgridfit file.") sys.exit(1) thisFont = ttLib.TTFont(inputfont) functionBase = 0 # Offset to account for functions in existing font cvtBase = 0 # Offset to account for CVs in existing font storageBase = 0 # Offset to account for storage in existing font maxStack = 256 # Our (generous) default stack size twilightBase = 0 # Offset to account for twilight space in existing font if mergemode: maxInstructions = max(maxInstructions, thisFont['maxp'].maxSizeOfInstructions) storageBase = thisFont['maxp'].maxStorage maxStack = max(maxStack, thisFont['maxp'].maxStackElements) functionBase = thisFont['maxp'].maxFunctionDefs twilightBase = thisFont['maxp'].maxTwilightPoints try: cvtBase = len(getattr(thisFont['cvt '], 'values')) except: cvtBase = 0 else: wipe_font(thisFont) # Get the xsl file, change some defaults (only relevant in merge-mode), # and get a transform object xslfile = etree.parse(get_file_path("XSL/xgridfit-ft.xsl")) if mergemode: xslfile.xpath("/xsl:stylesheet/xsl:param[@name='function-base']", namespaces=ns)[0].attrib['select'] = str(functionBase) xslfile.xpath("/xsl:stylesheet/xsl:param[@name='cvt-base']", namespaces=ns)[0].attrib['select'] = str(cvtBase) xslfile.xpath("/xsl:stylesheet/xsl:param[@name='storage-base']", namespaces=ns)[0].attrib['select'] = str(storageBase) etransform = etree.XSLT(xslfile) # Get a list of the glyphs to compile if quietcount < 1: print("Getting glyph list ...") if glyphlist: # a list passed in as a command-line argument glyph_list = glyphlist else: # all the glyph programs in the file glyph_list = str(etransform(xgffile, **{"get-glyph-list": "'yes'"})) glyph_list = list(glyph_list.split(" ")) no_compact_list = str( etransform(xgffile, **{"get-no-compact-list": "'yes'"})) if no_compact_list == None: no_compact_list = [] else: no_compact_list = list(no_compact_list.split(" ")) # Now that we have a glyph list, we can make the coordinate index # and substitute point numbers for coordinates. coordinateIndex = make_coordinate_index(glyph_list, thisFont) coordinates_to_points(glyph_list, xgffile, coordinateIndex, ns) # Back to the xgf file. We're also going to need a list of stack-safe # functions in this font. if quietcount < 1: print("Getting list of safe function calls ...") safe_calls = etransform(xgffile, **{"stack-safe-list": "'yes'"}) safe_calls = literal_eval(str(safe_calls)) # Get cvt if quietcount < 1: print("Building control-value table ...") cvt_list = str(etransform(xgffile, **{"get-cvt-list": "'yes'"})) cvt_list = literal_eval("[" + cvt_list + "]") install_cvt(thisFont, cvt_list, cvtBase) # Test whether we have a cvar element (i.e. this is a variable font) cvar_count = len(xgffile.xpath("/xgf:xgridfit/xgf:cvar", namespaces=ns)) if cvar_count > 0: if quietcount < 1: print("Building cvar table ...") tuple_store = literal_eval( str(etransform(xgffile, **{"get-cvar": "'yes'"}))) install_cvar(thisFont, tuple_store, mergemode, cvtBase) if quietcount < 1: print("Building fpgm table ...") predef_functions = int( xslfile.xpath( "/xsl:stylesheet/xsl:variable[@name='predefined-functions']", namespaces=ns)[0].attrib['select']) maxFunction = etransform(xgffile, **{"function-count": "'yes'"}) maxFunction = int(maxFunction) + predef_functions + functionBase fpgm_code = etransform(xgffile, **{"fpgm-only": "'yes'"}) install_functions(thisFont, fpgm_code, functionBase) if saveprograms: instf = open("fpgm.instructions", "w") instf.write(str(fpgm_code)) instf.close if quietcount < 1: print("Building prep table ...") prep_code = etransform(xgffile, **{"prep-only": "'yes'"}) install_prep(thisFont, prep_code, mergemode, replaceprep) if saveprograms: instf = open("prep.instructions", "w") instf.write(str(prep_code)) instf.close # Now loop through the glyphs for which there is code. cycler = 0 for g in glyph_list: if quietcount < 1: print("Processing glyph " + g) elif quietcount < 2 and cycler == 4: print(".", end=" ", flush=True) cycler += 1 if cycler == 5: cycler = 0 try: gt = "'" + g + "'" glyph_args = {'singleGlyphId': gt} if initgraphics: glyph_args['init_graphics'] = "'" + initgraphics + "'" if assume_y: glyph_args['assume-always-y'] = "'" + assume_y + "'" g_inst = etransform(xgffile, **glyph_args) except Exception as e: print(e) for entry in etransform.error_log: print('message from line %s, col %s: %s' % (entry.line, entry.column, entry.message)) sys.exit(1) if nocompact or g in no_compact_list: g_inst_final = str(g_inst) else: g_inst_final = compact_instructions(str(g_inst), safe_calls) install_glyph_program(g, thisFont, g_inst_final) if saveprograms: gfn = userNameToFileName(g) + ".instructions" gfnfile = open(gfn, "w") if nocompact or g in no_compact_list: gfnfile.write(g_inst_final) else: gfnfile.write("Uncompacted:\n\n") gfnfile.write(str(g_inst)) gfnfile.write("\n\nCompacted:\n\n") gfnfile.write(g_inst_final) gfnfile.close print("") if quietcount < 1: print("Hinted " + str(len(glyph_list)) + " glyphs.") print("Cleaning up and writing the new font") thisFont['maxp'].maxSizeOfInstructions = maxInstructions + 50 thisFont['maxp'].maxTwilightPoints = twilightBase + 25 thisFont['maxp'].maxStorage = storageBase + 64 thisFont['maxp'].maxStackElements = maxStack thisFont['maxp'].maxFunctionDefs = maxFunction thisFont['head'].flags |= 0b0000000000001000 if skipcomp: if quietcount < 1: print( "As --nocompilation flag is set, exiting without writing font file." ) sys.exit(0) if not outputfont: outputfont = str( xgffile.xpath("/xgf:xgridfit/xgf:outputfont/text()", namespaces=ns)[0]) if not outputfont: print("Need the filename of a font to write. Use the --outputfont") print("command-line argument or the <outputfont> element in your") print("Xgridfit file.") sys.exit(1) thisFont.save(outputfont, 1)
def getGlyphRaw(self, glyphName): fileName = userNameToFileName(glyphName, suffix=".glif") glifPath = self._path / fileName if not glifPath.exists(): raise GlyphNotFoundError(f"{glyphName}") return RCJKGlyph.loadFromGLIF(glifPath)