Esempio n. 1
0
def test_autohint_good_glyph(glyph):
    result = _psautohint.autohint(INFO, glyph)
    assert result == b"% foo\nsc\ned\n"
Esempio n. 2
0
def test_autohint_bad_args(args):
    with pytest.raises(TypeError):
        _psautohint.autohint(*args)
Esempio n. 3
0
def test_autohint_good_args():
    _psautohint.autohint(INFO, GLYPH)
Esempio n. 4
0
def test_autohint_too_many_counter_glyphs(info):
    _psautohint.autohint(info, GLYPH)
Esempio n. 5
0
def test_autohint_bad_glyph(glyph):
    with pytest.raises(_psautohint.error):
        _psautohint.autohint(INFO, glyph)
Esempio n. 6
0
def hintFile(options):
    global gLogFile
    gLogFile = options.logFile
    nameAliases = options.nameAliases

    path = options.inputPath
    fontFileName = os.path.basename(path)
    if not options.quiet:
        logMsg("Hinting font %s. Start time: %s." % (path, time.asctime()))

    try:
        # For UFO fonts only.
        # We always use the hash map, unless the user requested only report issues.
        useHashMap = not options.logOnly
        fontData = openFile(path, options.outputPath, useHashMap, options)
        fontData.allowDecimalCoords = options.allowDecimalCoords
        if options.writeToDefaultLayer and hasattr(
                fontData, "setWriteToDefault"):  # UFO fonts only
            fontData.setWriteToDefault()
    except (IOError, OSError):
        logMsg(
            traceback.format_exception_only(sys.exc_info()[0],
                                            sys.exc_info()[1])[-1])
        raise ACFontError("Error opening or reading from font file <%s>." %
                          fontFileName)
    except:
        logMsg(
            traceback.format_exception_only(sys.exc_info()[0],
                                            sys.exc_info()[1])[-1])
        raise ACFontError("Error parsing font file <%s>." % fontFileName)

    # filter specified list, if any, with font list.
    fontGlyphList = fontData.getGlyphList()
    glyphList = filterGlyphList(options, fontGlyphList, fontFileName)
    if not glyphList:
        raise ACFontError(
            "Error: selected glyph list is empty for font <%s>." %
            fontFileName)

    fontInfo = ""

    psName = fontData.getPSName()

    if (not options.logOnly) and options.usePlistFile:
        fontPlist, fontPlistFilePath, isNewPlistFile = openFontPlistFile(
            psName, os.path.dirname(path))
        if isNewPlistFile and not (options.hintAll or options.rehint):
            logMsg(
                "No hint info plist file was found, so all glyphs are unknown to autohint. To hint all glyphs, run autohint again with option -a to hint all glyphs unconditionally."
            )
            logMsg("Done with font %s. End time: %s." % (path, time.asctime()))
            fontData.close()
            return

    # Check counter glyphs, if any.
    if options.hCounterGlyphs or options.vCounterGlyphs:
        missingList = filter(lambda name: name not in fontGlyphList,
                             options.hCounterGlyphs + options.vCounterGlyphs)
        if missingList:
            logMsg(
                "\tError: glyph named in counter hint list file '%s' are not in font: %s"
                % (options.counterHintFile, missingList))

    # Build alignment zone string
    if (options.printDefaultFDDict):
        logMsg("Showing default FDDict Values:")
        fdDict = fontData.getFontInfo(psName, path, options.allow_no_blues,
                                      options.noFlex, options.vCounterGlyphs,
                                      options.hCounterGlyphs)
        printFontInfo(str(fdDict))
        fontData.close()
        return

    fdGlyphDict, fontDictList = fontData.getfdInfo(
        psName, path, options.allow_no_blues, options.noFlex,
        options.vCounterGlyphs, options.hCounterGlyphs, glyphList)

    if options.printFDDictList:
        # Print the user defined FontDicts, and exit.
        if fdGlyphDict:
            logMsg("Showing user-defined FontDict Values:")
            for fi in range(len(fontDictList)):
                fontDict = fontDictList[fi]
                logMsg("")
                logMsg(fontDict.DictName)
                printFontInfo(str(fontDict))
                gnameList = []
                itemList = fdGlyphDict.items()
                itemList.sort(cmpFDDictEntries)
                for gName, entry in itemList:
                    if entry[0] == fi:
                        gnameList.append(gName)
                logMsg("%d glyphs:" % len(gnameList))
                if len(gnameList) > 0:
                    gTxt = " ".join(gnameList)
                else:
                    gTxt = "None"
                logMsg(gTxt)
        else:
            logMsg("There are no user-defined FontDict Values.")
        fontData.close()
        return

    if fdGlyphDict == None:
        fdDict = fontDictList[0]
        fontInfo = fdDict.getFontInfo()
    else:
        if not options.verbose and not options.quiet:
            logMsg(
                "Note: Using alternate FDDict global values from fontinfo file for some glyphs. Remove option '-q' to see which dict is used for which glyphs."
            )

    # Get charstring for identifier in glyph-list
    isCID = fontData.isCID()
    lastFDIndex = None
    anyGlyphChanged = False
    pListChanged = False
    if isCID:
        options.noFlex = True

    if not options.verbose:
        dotCount = 0
        curTime = time.time()

    dotCount = 0
    seenGlyphCount = 0
    processedGlyphCount = 0
    for name in glyphList:
        prevACIdentifier = None
        seenGlyphCount += 1

        # Convert to bez format
        bezString, width, hasHints = fontData.convertToBez(
            name, options.verbose, options.hintAll)
        processedGlyphCount += 1
        if bezString == None:
            continue

        if "mt" not in bezString:
            # skip empty glyphs.
            continue
        # get new fontinfo string if FDarray index has changed,
        # as each FontDict has different alignment zones.
        gid = fontData.getGlyphID(name)
        if isCID:  #
            fdIndex = fontData.getfdIndex(gid)
            if not fdIndex == lastFDIndex:
                lastFDIndex = fdIndex
                fdDict = fontData.getFontInfo(psName, path,
                                              options.allow_no_blues,
                                              options.noFlex,
                                              options.vCounterGlyphs,
                                              options.hCounterGlyphs, fdIndex)
                fontInfo = fdDict.getFontInfo()
        else:
            if (fdGlyphDict != None):
                try:
                    fdIndex = fdGlyphDict[name][0]
                except KeyError:
                    # use default dict.
                    fdIndex = 0
                if lastFDIndex != fdIndex:
                    lastFDIndex = fdIndex
                    fdDict = fontDictList[fdIndex]
                    fontInfo = fdDict.getFontInfo()

        # 	Build autohint point list identifier

        oldBezString = ""
        oldHintBezString = ""
        if (not options.logOnly) and options.usePlistFile:
            # If the glyph is not in the plist file, then we skip it unless
            # kReHintUnknown is set.
            # If the glyph is in the plist file and the outline has changed,
            # we hint it.
            ACidentifier = makeACIdentifier(bezString)
            try:
                (prevACIdentifier, ACtime, oldBezString,
                 oldHintBezString) = fontPlist[kACIDKey][name]
            except ValueError:
                (prevACIdentifier, ACtime) = fontPlist[kACIDKey][name]
                oldBezString = oldHintBezString = ""
            except KeyError:
                # there wasn't an entry in tempList file, so we will add one.
                pListChanged = True
                if hasHints and not options.rehint:
                    # Glyphs is hinted, but not referenced in the plist file.
                    # Skip it unless options.rehint is seen
                    if not isNewPlistFile:
                        # Comment only if there is a plist file; otherwise, we'd
                        # be complaining for almost every glyph.
                        logMsg(
                            "%s Skipping glyph - it has hints, but it is not in the hint info plist file."
                            % nameAliases.get(name, name))
                        dotCount = 0
                    continue
            # there's an entry in the plist file and it matches what's in the font
            if prevACIdentifier and (prevACIdentifier == ACidentifier):
                if hasHints and not (options.hintAll or options.rehint):
                    continue
            else:
                pListChanged = True

        if options.verbose:
            if fdGlyphDict:
                logMsg("Hinting %s with fdDict %s." %
                       (nameAliases.get(name, name), fdDict.DictName))
            else:
                logMsg("Hinting %s." % nameAliases.get(name, name))
        elif not options.quiet:
            logMsg(".,")
            dotCount += 1
            if dotCount > 40:
                dotCount = 0
                logMsg(
                    "")  # I do this to never have more than 40 dots on a line.
                # This in turn give reasonable performance when calling autohint
                # in a subprocess and getting output with std.readline()

        # Call auto-hint library on bez string.
        #print("oldBezString", oldBezString)
        #print("")
        #print("bezString", bezString)

        if oldBezString != "" and oldBezString == bezString:
            newBezString = oldHintBezString
        else:
            newBezString = _psautohint.autohint(fontInfo.encode("ascii"),
                                                [bezString.encode("ascii")],
                                                options.verbose,
                                                options.allowChanges,
                                                not options.noHintSub,
                                                options.allowDecimalCoords)
            newBezString = newBezString[0].decode("ascii")

        if not newBezString:
            if not options.verbose and not options.quiet:
                logMsg("")
            raise ACHintError(
                "%s Error - failure in processing outline data." %
                nameAliases.get(name, name))

        if not (("ry" in newBezString[:200]) or ("rb" in newBezString[:200]) or
                ("rm" in newBezString[:200]) or ("rv" in newBezString[:200])):
            print("No hints added!")

        if options.logOnly:
            continue

        # Convert bez to charstring, and update CFF.
        anyGlyphChanged = True
        fontData.updateFromBez(newBezString, name, width, options.verbose)

        if options.usePlistFile:
            bezString = "%% %s\n%s" % (name, newBezString)
            ACidentifier = makeACIdentifier(bezString)
            # add glyph hint entry to plist file
            if options.allowChanges:
                if prevACIdentifier and (prevACIdentifier != ACidentifier):
                    logMsg("\t%s Glyph outline changed" %
                           nameAliases.get(name, name))
                    dotCount = 0

            fontPlist[kACIDKey][name] = (ACidentifier, time.asctime(),
                                         bezString, newBezString)

    if not options.verbose and not options.quiet:
        print("")  # print final new line after progress dots.

    if not options.logOnly:
        if anyGlyphChanged:
            if not options.quiet:
                logMsg("Saving font file with new hints..." + time.asctime())
            fontData.saveChanges()
        else:
            fontData.close()
            if options.usePlistFile:
                if options.rehint:
                    logMsg(
                        "No new hints. All glyphs had hints that matched the hint record file %s."
                        % (fontPlistFilePath))
                if options.hintAll:
                    logMsg(
                        "No new hints. All glyphs had hints that matched the hint history file %s, or were not in the history file and already had hints."
                        % (fontPlistFilePath))
                else:
                    logMsg("No new hints. All glyphs were already hinted.")
            else:
                logMsg("No glyphs were hinted.")
    if options.usePlistFile and (anyGlyphChanged or pListChanged):
        # save font plist file.
        fontPlist.write(fontPlistFilePath)
    if processedGlyphCount != seenGlyphCount:
        logMsg("Skipped %s of %s glyphs." %
               (seenGlyphCount - processedGlyphCount, seenGlyphCount))
    if not options.quiet:
        logMsg("Done with font %s. End time: %s." % (path, time.asctime()))