def test_autohint_good_glyph(glyph): result = _psautohint.autohint(INFO, glyph) assert result == b"% foo\nsc\ned\n"
def test_autohint_bad_args(args): with pytest.raises(TypeError): _psautohint.autohint(*args)
def test_autohint_good_args(): _psautohint.autohint(INFO, GLYPH)
def test_autohint_too_many_counter_glyphs(info): _psautohint.autohint(info, GLYPH)
def test_autohint_bad_glyph(glyph): with pytest.raises(_psautohint.error): _psautohint.autohint(INFO, glyph)
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()))