示例#1
0
 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()
示例#2
0
 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)
示例#3
0
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
示例#4
0
 def nameToFileName(name):
     return userNameToFileName(name)
示例#5
0
 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")
示例#6
0
 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")
示例#7
0
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)
示例#8
0
 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)