def PrintReports(path, h_stem_list, v_stem_list, top_zone_list, bot_zone_list): items = ([h_stem_list, srtCnt], [v_stem_list, srtCnt], [top_zone_list, srtRevVal], [bot_zone_list, srtVal]) atime = time.asctime() suffixes = (".hstm.txt", ".vstm.txt", ".top.txt", ".bot.txt") titles = ( "Horizontal Stem List for %s on %s\n" % (path, atime), "Vertical Stem List for %s on %s\n" % (path, atime), "Top Zone List for %s on %s\n" % (path, atime), "Bottom Zone List for %s on %s\n" % (path, atime), ) headers = ( "Count\tWidth\tGlyph List\n", "Count\tWidth\tGlyph List\n", "Count\tTop Zone\tGlyph List\n", "Count\tBottom Zone\tGlyph List\n", ) for i, item in enumerate(items): rep_list, sortFunc = item if not rep_list: continue fName = '{}{}'.format(path, suffixes[i]) title = titles[i] header = headers[i] try: with open(fName, "w") as fp: fp.write(tounicode(title)) fp.write(tounicode(header)) for item in formatReport(rep_list, sortFunc): fp.write(tounicode(item)) print("Wrote %s" % fName) except (IOError, OSError): print("Error creating file %s!" % fName)
def PrintReports(path, h_stem_list, v_stem_list, top_zone_list, bot_zone_list): items = ([h_stem_list, srtCnt], [v_stem_list, srtCnt], [top_zone_list, srtRevVal], [bot_zone_list, srtVal]) atime = time.asctime() suffixes = (".hstm.txt", ".vstm.txt", ".top.txt", ".bot.txt") titles = ("Horizontal Stem List for %s on %s\n" % (path, atime), "Vertical Stem List for %s on %s\n" % (path, atime), "Top Zone List for %s on %s\n" % (path, atime), "Bottom Zone List for %s on %s\n" % (path, atime), ) headers = ("Count\tWidth\tGlyph List\n", "Count\tWidth\tGlyph List\n", "Count\tTop Zone\tGlyph List\n", "Count\tBottom Zone\tGlyph List\n", ) for i, item in enumerate(items): rep_list, sortFunc = item if not rep_list: continue fName = '{}{}'.format(path, suffixes[i]) title = titles[i] header = headers[i] try: with open(fName, "w") as fp: fp.write(tounicode(title)) fp.write(tounicode(header)) for item in formatReport(rep_list, sortFunc): fp.write(tounicode(item)) print("Wrote %s" % fName) except (IOError, OSError): print("Error creating file %s!" % fName)
def runShellCmd(cmd): try: p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdoutdata, _ = p.communicate() try: return tounicode(stdoutdata, encoding='utf-8') except UnicodeDecodeError: return tounicode(stdoutdata, encoding=sys.getfilesystemencoding()) except (subprocess.CalledProcessError, OSError) as err: msg = "Error executing command '%s'\n%s" % (cmd, err) print(msg) return ""
def normalizeStringForPostscript(s, allowSpaces=True): s = tounicode(s) normalized = [] for c in s: if c == " " and not allowSpaces: continue if c in _postscriptFontNameExceptions: continue if c not in _postscriptFontNameAllowed: # Use compatibility decomposed form, to keep parts in ascii c = unicodedata.normalize("NFKD", c) if not set(c) < _postscriptFontNameAllowed: c = tounicode(tobytes(c, errors="replace")) normalized.append(tostr(c)) return "".join(normalized)
def main(): try: options = getOptions() except ACOptionParseError as e: logMsg(e) return 1 # verify that all files exist. haveFiles = True for path in options.fileList: if not os.path.exists(path): logMsg("File does not exist: <%s>." % path) haveFiles = False if not haveFiles: return 1 for path in options.fileList: if options.new: if options.reportPath: reportPath = options.reportPath else: reportPath = path foundOne = checkReportExists(reportPath, options.doAlign) if foundOne: logMsg("Skipping %s, as a report already exists." % (path)) continue try: collectStemsFont(path, options) except (ACFontError, ufotools.UFOParseError) as e: logMsg("\t%s" % e) return 1 if options.debug: with open("rawdata.txt", "w") as fp: fp.write(tounicode('\n'.join(rawData)))
def hint_bez_glyph(info, glyph, allow_edit=True, allow_hint_sub=True, round_coordinates=True, report_zones=False, report_stems=False, report_all_stems=False, use_autohintexe=False): if use_autohintexe: hinted = _hint_with_autohintexe(info, glyph, allow_edit, allow_hint_sub, round_coordinates, report_zones, report_stems, report_all_stems) else: report = 0 if report_zones: report = 1 elif report_stems: report = 2 hinted = _psautohint.autohint(tobytes(info), tobytes(glyph), allow_edit, allow_hint_sub, round_coordinates, report, report_all_stems) return tounicode(hinted)
def parseLayoutFeatures(font): """ Parse OpenType layout features in the UFO and return a feaLib.ast.FeatureFile instance. """ featxt = tounicode(font.features.text or "", "utf-8") if not featxt: return ast.FeatureFile() buf = UnicodeIO(featxt) ufoPath = font.path includeDir = None if ufoPath is not None: # The UFO v3 specification says "Any include() statements must be relative to # the UFO path, not to the features.fea file itself". We set the `name` # attribute on the buffer to the actual feature file path, which feaLib will # pick up and use to attribute errors to the correct file, and explicitly set # the include directory to the parent of the UFO. ufoPath = os.path.normpath(ufoPath) buf.name = os.path.join(ufoPath, "features.fea") includeDir = os.path.dirname(ufoPath) glyphNames = set(font.keys()) try: parser = Parser(buf, glyphNames, includeDir=includeDir) doc = parser.parse() except IncludedFeaNotFound as e: if ufoPath and os.path.exists(os.path.join(ufoPath, e.args[0])): logger.warning("Please change the file name in the include(...); " "statement to be relative to the UFO itself, " "instead of relative to the 'features.fea' file " "contained in it.") raise return doc
def parseLayoutFeatures(font): """ Parse OpenType layout features in the UFO and return a feaLib.ast.FeatureFile instance. """ featxt = tounicode(font.features.text or "", "utf-8") if not featxt: return ast.FeatureFile() buf = UnicodeIO(featxt) # the path is used by the lexer to resolve 'include' statements # and print filename in error messages. For the UFO spec, this # should be the path of the UFO, not the inner features.fea: # https://github.com/unified-font-object/ufo-spec/issues/55 ufoPath = font.path if ufoPath is not None: buf.name = ufoPath glyphNames = set(font.keys()) try: parser = Parser(buf, glyphNames) doc = parser.parse() except IncludedFeaNotFound as e: if ufoPath and os.path.exists(os.path.join(ufoPath, e.args[0])): logger.warning("Please change the file name in the include(...); " "statement to be relative to the UFO itself, " "instead of relative to the 'features.fea' file " "contained in it.") raise return doc
def main(): try: options = getOptions() except ACOptionParseError as e: logMsg(e) return 1 # verify that all files exist. haveFiles = True for path in options.fileList: if not os.path.exists(path): logMsg( "File does not exist: <%s>." % path) haveFiles = False if not haveFiles: return 1 for path in options.fileList: if options.new: if options.reportPath: reportPath = options.reportPath else: reportPath = path foundOne = checkReportExists(reportPath, options.doAlign) if foundOne: logMsg( "Skipping %s, as a report already exists." % (path)) continue try: collectStemsFont(path, options) except (ACFontError, ufotools.UFOParseError) as e: logMsg( "\t%s" % e) return 1 if options.debug: with open("rawdata.txt", "w") as fp: fp.write(tounicode('\n'.join(rawData)))
def test_fontv_fonttools_lib_unicode(): test_string = tobytes("hello") test_string_str = tostr("hello") test_string_unicode = tounicode(test_string, 'utf-8') test_string_str_unicode = tounicode(test_string_str, 'utf-8') assert (isinstance(test_string, unicode)) is False if sys.version_info[0] == 2: assert (isinstance(test_string_str, unicode)) is False # str != unicode in Python 2 elif sys.version_info[0] == 3: assert (isinstance(test_string_str, unicode)) is True # str = unicode in Python 3 assert (isinstance(test_string_unicode, unicode) ) is True # after cast with fonttools function, Py2+3 = unicode assert (isinstance(test_string_str_unicode, unicode)) is True # ditto assert test_string_unicode == "hello"
def _set_name(self, value): value = tounicode(value) oldName = self._name if oldName != value: self._name = value data = dict(oldName=oldName, newName=value) self.postNotification(notification="Layer.NameChanged", data=data) self.dirty = True
def parse(self, text): """Do the parsing.""" text = tounicode(text, encoding='utf-8') result, i = self._parse(text, 0) if text[i:].strip(): self._fail('Unexpected trailing content', text, i) return result
def hint_bez_glyph(info, glyph, allow_edit=True, allow_hint_sub=True, round_coordinates=True): hinted = _psautohint.autohint(tobytes(info), tobytes(glyph), allow_edit, allow_hint_sub, round_coordinates) return tounicode(hinted)
def setupFeatures(self): ufo = self.ufo features = {} # includes the length of the "/" separator prefixLength = len(MTI_FEATURES_PREFIX) + 1 for fn in ufo.data.fileNames: if fn.startswith(MTI_FEATURES_PREFIX) and fn.endswith(".mti"): content = tounicode(ufo.data[fn], encoding="utf-8") features[fn[prefixLength:-4]] = content self.mtiFeatures = features
def stringToGlyphNames(self, string): glyphNames = [] for c in string: c = tounicode(c) v = ord(c) if v in self.cmap: glyphNames.append(self.cmap[v]) elif self.fallbackGlyph is not None: glyphNames.append(self.fallbackGlyph) return glyphNames
def get_shell_command_output(args, std_error=False): """ Runs a shell command and captures its output. To also capture standard error call with std_error=True. Returns a tuple; the first element will be True if the command was successful, and False otherwise. The second element will be a Unicode-encoded string, or None. """ stderr = subprocess.STDOUT if std_error else None try: bytes_output = subprocess.check_output(args, stderr=stderr) try: str_output = tounicode(bytes_output, encoding='utf-8') except UnicodeDecodeError: str_output = tounicode(bytes_output, encoding=sys.getfilesystemencoding()) return True, str_output except (subprocess.CalledProcessError, OSError) as err: print(err) return False, None
def normalizeStringForPostscript(s, allowSpaces=True): s = tounicode(s) normalized = [] for c in s: if c == " " and not allowSpaces: continue if c in _postscriptFontNameExceptions: continue if c not in _postscriptFontNameAllowed: c = unicodedata.normalize("NFKD", c) normalized.append(tostr(c)) return "".join(normalized)
def parse_into_object(self, res, text): """Parse data into an existing GSFont instance.""" text = tounicode(text, encoding='utf-8') m = self.start_dict_re.match(text, 0) if m: i = self._parse_dict_into_object(res, text, 1) else: self._fail('not correct file format', text, i) if text[i:].strip(): self._fail('Unexpected trailing content', text, i) return i
def _hint_with_autohintexe(info, glyph, allow_edit, allow_hint_sub, round_coordinates, report_zones, report_stems, report_all_stems): import subprocess edit = "" if allow_edit else "-e" hintsub = "" if allow_hint_sub else "-n" decimal = "" if round_coordinates else "-d" zones = "-ra" if report_zones else "" stems = "-rs" if report_stems else "" allstems = "-a" if report_all_stems else "" cmd = [ AUTOHINTEXE, edit, hintsub, decimal, zones, stems, allstems, "-D", "-i", info, "-b", glyph ] cmd = [a for a in cmd if a] # Filter out empty args, just in case. try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) outdata, errordata = p.communicate() # A bit of hack to parse the stderr output and route it through our # logger. if errordata: errordata = tounicode(errordata).strip().split("\n") for line in errordata: if ": " in line: level, msg = line.split(": ", 1) if level not in ("DEBUG", "INFO", "WARNING", "ERROR"): level, msg = "INFO", line getattr(log, level.lower())(msg) else: log.info(line) return tounicode(outdata) except (subprocess.CalledProcessError, OSError) as err: raise _psautohint.error(err)
def _dict_element(d: Mapping[str, PlistEncodable], ctx: SimpleNamespace) -> etree.Element: el = etree.Element("dict") items = d.items() if ctx.sort_keys: items = sorted(items) # type: ignore ctx.indent_level += 1 for key, value in items: if not isinstance(key, str): if ctx.skipkeys: continue raise TypeError("keys must be strings") k = etree.SubElement(el, "key") k.text = tounicode(key, "utf-8") el.append(_make_element(value, ctx)) ctx.indent_level -= 1 return el
def _dict_element(d, ctx): el = etree.Element("dict") items = d.items() if ctx.sort_keys: items = sorted(items) ctx.indent_level += 1 for key, value in items: if not isinstance(key, basestring): if ctx.skipkeys: continue raise TypeError("keys must be strings") k = etree.SubElement(el, "key") k.text = tounicode(key, "utf-8") el.append(_make_element(value, ctx)) ctx.indent_level -= 1 return el
def _get_repo_commit(self): """ Private method that makes a system git call via the GitPython library and returns a short git commit SHA1 hash string for the commit at HEAD using `git rev-list`. :return: (string) short git commit SHA1 hash string """ repo = Repo(get_git_root_path(self.fontpath)) gitpy = repo.git # git rev-list --abbrev-commit --max-count=1 --format="%h" HEAD - abbreviated unique sha1 for the repository # number of sha1 hex characters determined by git (addresses https://github.com/source-foundry/font-v/issues/2) full_git_sha_string = gitpy.rev_list("--abbrev-commit", "--max-count=1", '--format="%h"', "HEAD") unicode_full_sha_string = tounicode(full_git_sha_string) sha_string_list = unicode_full_sha_string.split("\n") final_sha_string = sha_string_list[1].replace('"', "") return final_sha_string
def buildTempDesignSpace(dsPath): """'dsPath' is the path to the original designspace file. Returns a modified designspace file (for generating temp UFO masters) and a list of paths to the UFO masters (that will be used for generating each master OTF). """ ds = ET.parse(dsPath).getroot() masterList = ds.find('sources').findall('source') # Delete any existing instances instances = ds.find('instances') if instances: ds.remove(instances) ds.append(XMLElement('instances')) instances = ds.find('instances') # Don't be wasteful, generate only the necessary temp masters temp_masters_to_generate = _determine_which_masters_to_generate(dsPath) # Populate the <instances> element with the information needed # for generating the temp master(s) master_paths = [] for i, master in enumerate(masterList): masterPath = master.attrib['filename'] if i not in temp_masters_to_generate: master_paths.append(masterPath) continue instance = XMLElement('instance') instance.append(master.find('location')) instance.append(XMLElement('kerning')) instance.append(XMLElement('info')) tempMasterPath = os.path.splitext(masterPath)[0] + kTempUFOExt master_paths.append(tempMasterPath) instance.attrib['filename'] = tempMasterPath ufo_path = os.path.join(os.path.dirname(dsPath), masterPath) ufo_info = defcon.Font(ufo_path).info instance.attrib['familyname'] = ufo_info.familyName instance.attrib['stylename'] = ufo_info.styleName instance.attrib['postscriptfontname'] = ufo_info.postscriptFontName instances.append(instance) tempDSPath = os.path.splitext(dsPath)[0] + kTempDSExt with open(tempDSPath, "w") as fp: fp.write(tounicode(xmlToString(ds))) return tempDSPath, master_paths
def _tounicode(s): """Test if a string is valid user input and decode it to unicode string using ASCII encoding if it's a bytes string. Reject all bytes/unicode input that contains non-XML characters. Reject all bytes input that contains non-ASCII characters. """ try: s = tounicode(s, encoding="ascii", errors="strict") except UnicodeDecodeError: raise ValueError( "Bytes strings can only contain ASCII characters. " "Use unicode strings for non-ASCII characters.") except AttributeError: _raise_serialization_error(s) if s and _invalid_xml_string.search(s): raise ValueError( "All strings must be XML compatible: Unicode or ASCII, " "no NULL bytes or control characters") return s
def makeCIDFontInfo(fontPath, cidfontinfoPath): cfiDict = {} for key in kCIDFontInfokeyList: cfiDict[key] = None cfiDict["Weight"] = "(Regular)" cfiDict["AdobeCopyright"] = "0" # get regular FontDict. command = "tx -0 \"%s\" 2>&1" % fontPath report = fdkutils.runShellCmd(command) if ("fatal" in report) or ("error" in report): print(report) raise FontInfoParseError("Failed to dump font dict using tx from " "font '%s'" % fontPath) for entry in TX_FIELDS: match = re.search(entry[0] + r"\s+(.+?)[\r\n]", report) if match: entry[2] = match.group(1) cfiDict["Registry"] = "Adobe" cfiDict["Ordering"] = "Identity" cfiDict["Supplement"] = "0" for entry in TX_FIELDS: if entry[2]: cfiDict[entry[1]] = entry[2] elif entry[1] in kRequiredCIDFontInfoFields: print("Error: did not find required info '%s' in tx dump of " "font '%s'." % (entry[1], fontPath)) try: with open(cidfontinfoPath, "w") as fp: for key in kCIDFontInfokeyList: value = cfiDict[key] if value is None: continue if value[0] == "\"": value = "(" + value[1:-1] + ")" string = tounicode("%s\t%s\n" % (key, value)) fp.write(string) except (IOError, OSError): raise FontInfoParseError( "Error. Could not open and write file '%s'" % cidfontinfoPath)
def setupFeatures(self): """ Make the features source. **This should not be called externally.** Subclasses may override this method to handle the file creation in a different way if desired. """ if self.featureWriters: featureFile = parseLayoutFeatures(self.ufo) for writer in self.featureWriters: writer.write(self.ufo, featureFile, compiler=self) # stringify AST to get correct line numbers in error messages self.features = featureFile.asFea() else: # no featureWriters, simply read existing features' text self.features = tounicode(self.ufo.features.text or "", "utf-8")
def makeCIDFontInfo(fontPath, cidfontinfoPath): cfiDict = {} for key in kCIDFontInfokeyList: cfiDict[key] = None cfiDict["Weight"] = "(Regular)" cfiDict["AdobeCopyright"] = "0" # get regular FontDict. command = "tx -0 \"%s\" 2>&1" % fontPath report = fdkutils.runShellCmd(command) if ("fatal" in report) or ("error" in report): print(report) raise FontInfoParseError("Failed to dump font dict using tx from " "font '%s'" % fontPath) for entry in TX_FIELDS: match = re.search(entry[0] + "\s+(.+?)[\r\n]", report) if match: entry[2] = match.group(1) cfiDict["Registry"] = "Adobe" cfiDict["Ordering"] = "Identity" cfiDict["Supplement"] = "0" for entry in TX_FIELDS: if entry[2]: cfiDict[entry[1]] = entry[2] elif entry[1] in kRequiredCIDFontInfoFields: print("Error: did not find required info '%s' in tx dump of " "font '%s'." % (entry[1], fontPath)) try: with open(cidfontinfoPath, "w") as fp: for key in kCIDFontInfokeyList: value = cfiDict[key] if value is None: continue if value[0] == "\"": value = "(" + value[1:-1] + ")" string = tounicode("%s\t%s\n" % (key, value)) fp.write(string) except (IOError, OSError): raise FontInfoParseError( "Error. Could not open and write file '%s'" % cidfontinfoPath)
def generateFeatures(font, args): """Generates feature text by merging feature file with mark positioning lookups (already in the font) and making sure they come after kerning lookups (from the feature file), which is required by Uniscribe to get correct mark positioning for kerned glyphs.""" oldfea = "" for lookup in font.gpos_lookups: oldfea += generateFeatureString(font, lookup) for lookup in font.gpos_lookups + font.gsub_lookups: font.removeLookup(lookup) # open feature file and insert the generated GPOS features in place of the # placeholder text with open(args.features) as f: o = StringIO() preprocessor = Preprocessor() if args.quran: preprocessor.define("QURAN") elif args.slant: preprocessor.define("ITALIC") preprocessor.parse(f) preprocessor.write(o) fea_text = tounicode(o.getvalue(), "utf-8") fea_text = fea_text.replace("{%anchors%}", oldfea) bases = [g.glyphname for g in font.glyphs() if g.glyphclass != "mark"] marks = [g.glyphname for g in font.glyphs() if g.glyphclass == "mark"] carets = {g.glyphname: g.lcarets for g in font.glyphs() if any(g.lcarets)} gdef = [] gdef.append("@GDEFBase = [%s];" % " ".join(bases)) gdef.append("@GDEFMark = [%s];" % " ".join(marks)) gdef.append("table GDEF {") gdef.append(" GlyphClassDef @GDEFBase, , @GDEFMark, ;") for k, v in carets.items(): gdef.append(" LigatureCaretByPos %s %s;" % (k, " ".join(map(str, v)))) gdef.append("} GDEF;") fea_text += "\n".join(gdef) return fea_text
def __init__(self, name, x, y, markClass=None): self.name = tounicode(name) self.x = x self.y = y isMark, key, number = parseAnchorName( name, markPrefix=self.markPrefix, ligaSeparator=self.ligaSeparator, ligaNumRE=self.ligaNumRE, ignoreRE=self.ignoreRE, ) if number is not None: if number < 1: raise ValueError( "ligature component indexes must start from 1") else: assert key, name self.isMark = isMark self.key = key self.number = number self.markClass = markClass
def updateInstance(options, fontInstancePath): """ Run checkoutlinesufo and autohint, unless explicitly suppressed. """ if options.doOverlapRemoval: logger.info("Doing overlap removal with checkoutlinesufo on %s ..." % fontInstancePath) co_args = ['-e', '-q', fontInstancePath] if options.no_round: co_args.insert(0, '-d') try: checkoutlinesUFO(co_args) except Exception: raise if options.doAutoHint: logger.info("Running autohint on %s ..." % fontInstancePath) logList = [] opList = ['-q', '-nb', fontInstancePath] if options.no_round: opList.insert(0, "-dec") opList.insert(0, 'autohint') proc = Popen(opList, stdout=PIPE) while 1: output = tounicode(proc.stdout.readline()) if output: logList.append(output) if proc.poll() is not None: output = proc.stdout.readline() if output: if options.verbose == 1: print(output, end='') logList.append(output) break log = "".join(logList) if not ("Done with font" in log): logger.error(log) logger.error("Error in autohinting %s" % fontInstancePath) raise(SnapShotError)
def main(argv): # command argument tests print(" ") if len(argv) < 2: sys.stderr.write( "[fontname.py] ERROR: you did not include enough arguments to the script." + os.linesep ) sys.stderr.write( "Usage: python fontname.py [FONT FAMILY NAME] [FONT PATH 1] <FONT PATH ...>" + os.linesep ) sys.exit(1) # begin parsing command line arguments try: font_name = tounicode(argv[0]) # the first argument is the new typeface name except UnicodeDecodeError as e: sys.stderr.write( "[fontname.py] ERROR: Unable to convert argument to Unicode. " + unicode(e) + os.linesep ) sys.exit(1) font_path_list = argv[ 1: ] # all remaining arguments on command line are file paths to fonts # iterate through all paths provided on command line and rename to `font_name` defined by user for font_path in font_path_list: # test for existence of font file on requested file path if not file_exists(font_path): sys.stderr.write( "[fontname.py] ERROR: the path '" + font_path + "' does not appear to be a valid file path." + os.linesep ) sys.exit(1) tt = ttLib.TTFont(font_path) namerecord_list = tt["name"].names variant = "" # determine font variant for this file path from name record nameID 2 for record in namerecord_list: if record.nameID == 2: variant = ( record.toUnicode() ) # cast to str type in Py 3, unicode type in Py 2 break # test that a variant name was found in the OpenType tables of the font if len(variant) == 0: sys.stderr.write( "[fontname.py] Unable to detect the font variant from the OpenType name table in '" + font_path + "'." + os.linesep ) sys.stderr.write("Unable to complete execution of the script.") sys.exit(1) else: # used for the Postscript name in the name table (no spaces allowed) postscript_font_name = font_name.replace(" ", "") # font family name nameID1_string = font_name # full font name nameID4_string = font_name + " " + variant # Postscript name # - no spaces allowed in family name or the PostScript suffix. should be dash delimited nameID6_string = postscript_font_name + "-" + variant.replace(" ", "") # modify the opentype table data in memory with updated values for record in namerecord_list: if record.nameID == 1: record.string = nameID1_string elif record.nameID == 4: record.string = nameID4_string elif record.nameID == 6: record.string = nameID6_string # write changes to the font file try: tt.save(font_path) print( "[OK] Updated '" + font_path + "' with the name '" + nameID4_string + "'" ) except Exception as e: sys.stderr.write( "[fontname.py] ERROR: unable to write new name to OpenType tables for '" + font_path + "'." + os.linesep ) sys.stderr.write(unicode(e)) sys.exit(1)
def fsdecode(path, encoding=sys.getfilesystemencoding()): return tounicode(path, encoding=encoding)
def collectStemsFont(path, options): # use fontTools library to open font and extract CFF table. # If error, skip font and report error. fontFileName = os.path.basename(path) logMsg("") if options.doAlign: logMsg( "Collecting alignment zones for font %s. Start time: %s." % (path, time.asctime())) else: logMsg( "Collecting stems for font %s. Start time: %s." % (path, time.asctime())) try: fontData = openFile(path) 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) tempBez = fdkutils.get_temp_file_path() tempReport = '{}{}'.format(tempBez, ".rpt") tempFI = fdkutils.get_temp_file_path() # open font plist file, if any. If not, create empty font plist. psName = fontData.getPSName() # build alignment zone string allow_no_blues = 1 noFlex = 0 vCounterGlyphs = hCounterGlyphs = [] fdGlyphDict, fontDictList = fontData.getfdInfo(psName, path, allow_no_blues, noFlex, vCounterGlyphs, hCounterGlyphs, glyphList) if fdGlyphDict == None: fdDict = fontDictList[0] with open(tempFI, "w") as fp: fp.write(tounicode(fdDict.getFontInfo())) else: if not options.verbose: 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.") removeHints = 1 isCID = fontData.isCID() lastFDIndex = None glyphReports = GlyphReports() if not options.verbose: dotCount = 0 curTime = time.time() for name in glyphList: if name == ".notdef": continue if options.verbose: logMsg("Checking %s." %name) else: newTime = time.time() if (newTime - curTime) > 1: print(".", end=' ') sys.stdout.flush() curTime = newTime dotCount +=1 if dotCount > 40: dotCount = 0 print("") # Convert to bez format bezString, width, hasHints = fontData.convertToBez(name, removeHints, options.verbose) if bezString == None: continue if "mt" not in bezString: # skip empty glyphs. continue # get new fontinfo string if FD array index has changed, as # 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) with open(tempFI, "w") as fp: fp.write(tounicode(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] with open(tempFI, "w") as fp: fp.write(tounicode(fdDict.getFontInfo())) glyphReports.startGlyphName(name) # Call auto-hint library on bez string. with open(tempBez, "w") as bp: bp.write(tounicode(bezString)) if options.doAlign: doAlign = "-ra" else: doAlign = "-rs" if options.allStems: allStems = "-a" else: allStems = "" command = AUTOHINTEXE + " -q %s %s -f \"%s\" \"%s\" 2>&1" % (doAlign, allStems, tempFI, tempBez) if options.debug: print(command) log = fdkutils.runShellCmd(command) if log: print(log) if "number terminator while" in log: print(tempBez) sys.exit() if os.path.exists(tempReport): with open(tempReport, "r", encoding='utf-8') as bp: report = bp.read() if options.debug: print("Wrote AC fontinfo data file to", tempFI) print("Wrote AC output rpt file to", tempReport) report.strip() if report: glyphReports.addGlyphReport(report) if options.debug: rawData.append(report) else: print("Error - failure in processing outline data") report = None h_stem_list, v_stem_list, top_zone_list, bot_zone_list = glyphReports.getReportLists() if options.reportPath: reportPath = options.reportPath else: reportPath = path PrintReports(reportPath, h_stem_list, v_stem_list, top_zone_list, bot_zone_list) fontData.close() logMsg( "Done with font %s. End time: %s." % (path, time.asctime()))
def test_autohint_good_args(): _psautohint.autohint(INFO, GLYPH) def test_autohintmm_good_args(): _psautohint.autohintmm(INFO, (GLYPH, GLYPH), (NAME, NAME)) @pytest.mark.parametrize( "args", [ [], # no arguments [INFO], # 1 argument [tounicode(INFO), GLYPH], # 1st is string not bytes [INFO, tounicode(GLYPH)], # 2nd is string not bytes [[INFO], tounicode(GLYPH)], # 1st is a list [INFO, [tounicode(GLYPH)]], # 2nd is a list ]) def test_autohint_bad_args(args): with pytest.raises(TypeError): _psautohint.autohint(*args) @pytest.mark.parametrize( "args", [ [], # no arguments [INFO], # 1 argument [INFO, (GLYPH, GLYPH)], # 2 arguments
def setupTable_OS2(self): """ Make the OS/2 table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["OS/2"] = os2 = newTable("OS/2") font = self.ufo os2.version = 0x0004 # average glyph width widths = [ glyph.width for glyph in self.allGlyphs.values() if glyph.width > 0 ] os2.xAvgCharWidth = _roundInt(sum(widths) / len(widths)) # weight and width classes os2.usWeightClass = getAttrWithFallback(font.info, "openTypeOS2WeightClass") os2.usWidthClass = getAttrWithFallback(font.info, "openTypeOS2WidthClass") # embedding os2.fsType = intListToNum( getAttrWithFallback(font.info, "openTypeOS2Type"), 0, 16) # subscript, superscript, strikeout values, taken from AFDKO: # FDK/Tools/Programs/makeotf/makeotf_lib/source/hotconv/hot.c unitsPerEm = getAttrWithFallback(font.info, "unitsPerEm") italicAngle = getAttrWithFallback(font.info, "italicAngle") xHeight = getAttrWithFallback(font.info, "xHeight") def adjustOffset(offset, angle): """Adjust Y offset based on italic angle, to get X offset.""" return offset * math.tan(math.radians(-angle)) if angle else 0 v = getAttrWithFallback(font.info, "openTypeOS2SubscriptXSize") if v is None: v = unitsPerEm * 0.65 os2.ySubscriptXSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptYSize") if v is None: v = unitsPerEm * 0.6 os2.ySubscriptYSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptYOffset") if v is None: v = unitsPerEm * 0.075 os2.ySubscriptYOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptXOffset") if v is None: v = adjustOffset(-os2.ySubscriptYOffset, italicAngle) os2.ySubscriptXOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptXSize") if v is None: v = os2.ySubscriptXSize os2.ySuperscriptXSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptYSize") if v is None: v = os2.ySubscriptYSize os2.ySuperscriptYSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptYOffset") if v is None: v = unitsPerEm * 0.35 os2.ySuperscriptYOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptXOffset") if v is None: v = adjustOffset(os2.ySuperscriptYOffset, italicAngle) os2.ySuperscriptXOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2StrikeoutSize") if v is None: v = getAttrWithFallback(font.info, "postscriptUnderlineThickness") os2.yStrikeoutSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2StrikeoutPosition") if v is None: v = xHeight * 0.6 if xHeight else unitsPerEm * 0.22 os2.yStrikeoutPosition = _roundInt(v) # family class ibmFontClass, ibmFontSubclass = getAttrWithFallback( font.info, "openTypeOS2FamilyClass") os2.sFamilyClass = (ibmFontClass << 8) + ibmFontSubclass # panose data = getAttrWithFallback(font.info, "openTypeOS2Panose") panose = Panose() panose.bFamilyType = data[0] panose.bSerifStyle = data[1] panose.bWeight = data[2] panose.bProportion = data[3] panose.bContrast = data[4] panose.bStrokeVariation = data[5] panose.bArmStyle = data[6] panose.bLetterForm = data[7] panose.bMidline = data[8] panose.bXHeight = data[9] os2.panose = panose # Unicode ranges uniRanges = getAttrWithFallback(font.info, "openTypeOS2UnicodeRanges") os2.ulUnicodeRange1 = intListToNum(uniRanges, 0, 32) os2.ulUnicodeRange2 = intListToNum(uniRanges, 32, 32) os2.ulUnicodeRange3 = intListToNum(uniRanges, 64, 32) os2.ulUnicodeRange4 = intListToNum(uniRanges, 96, 32) # codepage ranges codepageRanges = getAttrWithFallback(font.info, "openTypeOS2CodePageRanges") os2.ulCodePageRange1 = intListToNum(codepageRanges, 0, 32) os2.ulCodePageRange2 = intListToNum(codepageRanges, 32, 32) # vendor id os2.achVendID = tounicode(getAttrWithFallback(font.info, "openTypeOS2VendorID"), encoding="ascii", errors="ignore") # vertical metrics os2.sxHeight = _roundInt(getAttrWithFallback(font.info, "xHeight")) os2.sCapHeight = _roundInt(getAttrWithFallback(font.info, "capHeight")) os2.sTypoAscender = _roundInt( getAttrWithFallback(font.info, "openTypeOS2TypoAscender")) os2.sTypoDescender = _roundInt( getAttrWithFallback(font.info, "openTypeOS2TypoDescender")) os2.sTypoLineGap = _roundInt( getAttrWithFallback(font.info, "openTypeOS2TypoLineGap")) os2.usWinAscent = _roundInt( getAttrWithFallback(font.info, "openTypeOS2WinAscent")) os2.usWinDescent = _roundInt( getAttrWithFallback(font.info, "openTypeOS2WinDescent")) # style mapping selection = list(getAttrWithFallback(font.info, "openTypeOS2Selection")) styleMapStyleName = getAttrWithFallback(font.info, "styleMapStyleName") if styleMapStyleName == "regular": selection.append(6) elif styleMapStyleName == "bold": selection.append(5) elif styleMapStyleName == "italic": selection.append(0) elif styleMapStyleName == "bold italic": selection += [0, 5] os2.fsSelection = intListToNum(selection, 0, 16) # characetr indexes unicodes = [ i for i in self.unicodeToGlyphNameMapping.keys() if i is not None ] if unicodes: minIndex = min(unicodes) maxIndex = max(unicodes) else: # the font may have *no* unicode values (it really happens!) so # there needs to be a fallback. use 0xFFFF, as AFDKO does: # FDK/Tools/Programs/makeotf/makeotf_lib/source/hotconv/map.c minIndex = 0xFFFF maxIndex = 0xFFFF if maxIndex > 0xFFFF: # the spec says that 0xFFFF should be used # as the max if the max exceeds 0xFFFF maxIndex = 0xFFFF os2.fsFirstCharIndex = minIndex os2.fsLastCharIndex = maxIndex os2.usBreakChar = 32 os2.usDefaultChar = 0 # maximum contextual lookup length os2.usMaxContex = 0
def setupTable_OS2(self): """ Make the OS/2 table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["OS/2"] = os2 = newTable("OS/2") font = self.ufo os2.version = 0x0004 # average glyph width widths = [glyph.width for glyph in self.allGlyphs.values() if glyph.width > 0] os2.xAvgCharWidth = _roundInt(sum(widths) / len(widths)) # weight and width classes os2.usWeightClass = getAttrWithFallback(font.info, "openTypeOS2WeightClass") os2.usWidthClass = getAttrWithFallback(font.info, "openTypeOS2WidthClass") # embedding os2.fsType = intListToNum(getAttrWithFallback(font.info, "openTypeOS2Type"), 0, 16) # subscript v = getAttrWithFallback(font.info, "openTypeOS2SubscriptXSize") if v is None: v = 0 os2.ySubscriptXSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptYSize") if v is None: v = 0 os2.ySubscriptYSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptXOffset") if v is None: v = 0 os2.ySubscriptXOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptYOffset") if v is None: v = 0 os2.ySubscriptYOffset = _roundInt(v) # superscript v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptXSize") if v is None: v = 0 os2.ySuperscriptXSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptYSize") if v is None: v = 0 os2.ySuperscriptYSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptXOffset") if v is None: v = 0 os2.ySuperscriptXOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptYOffset") if v is None: v = 0 os2.ySuperscriptYOffset = _roundInt(v) # strikeout v = getAttrWithFallback(font.info, "openTypeOS2StrikeoutSize") if v is None: v = 0 os2.yStrikeoutSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2StrikeoutPosition") if v is None: v = 0 os2.yStrikeoutPosition = _roundInt(v) # family class ibmFontClass, ibmFontSubclass = getAttrWithFallback( font.info, "openTypeOS2FamilyClass") os2.sFamilyClass = (ibmFontClass << 8) + ibmFontSubclass # panose data = getAttrWithFallback(font.info, "openTypeOS2Panose") panose = Panose() panose.bFamilyType = data[0] panose.bSerifStyle = data[1] panose.bWeight = data[2] panose.bProportion = data[3] panose.bContrast = data[4] panose.bStrokeVariation = data[5] panose.bArmStyle = data[6] panose.bLetterForm = data[7] panose.bMidline = data[8] panose.bXHeight = data[9] os2.panose = panose # Unicode ranges uniRanges = getAttrWithFallback(font.info, "openTypeOS2UnicodeRanges") os2.ulUnicodeRange1 = intListToNum(uniRanges, 0, 32) os2.ulUnicodeRange2 = intListToNum(uniRanges, 32, 32) os2.ulUnicodeRange3 = intListToNum(uniRanges, 64, 32) os2.ulUnicodeRange4 = intListToNum(uniRanges, 96, 32) # codepage ranges codepageRanges = getAttrWithFallback(font.info, "openTypeOS2CodePageRanges") os2.ulCodePageRange1 = intListToNum(codepageRanges, 0, 32) os2.ulCodePageRange2 = intListToNum(codepageRanges, 32, 32) # vendor id os2.achVendID = tounicode( getAttrWithFallback(font.info, "openTypeOS2VendorID"), encoding="ascii", errors="ignore") # vertical metrics os2.sxHeight = _roundInt(getAttrWithFallback(font.info, "xHeight")) os2.sCapHeight = _roundInt(getAttrWithFallback(font.info, "capHeight")) os2.sTypoAscender = _roundInt(getAttrWithFallback(font.info, "openTypeOS2TypoAscender")) os2.sTypoDescender = _roundInt(getAttrWithFallback(font.info, "openTypeOS2TypoDescender")) os2.sTypoLineGap = _roundInt(getAttrWithFallback(font.info, "openTypeOS2TypoLineGap")) os2.usWinAscent = _roundInt(getAttrWithFallback(font.info, "openTypeOS2WinAscent")) os2.usWinDescent = _roundInt(getAttrWithFallback(font.info, "openTypeOS2WinDescent")) # style mapping selection = list(getAttrWithFallback(font.info, "openTypeOS2Selection")) styleMapStyleName = getAttrWithFallback(font.info, "styleMapStyleName") if styleMapStyleName == "regular": selection.append(6) elif styleMapStyleName == "bold": selection.append(5) elif styleMapStyleName == "italic": selection.append(0) elif styleMapStyleName == "bold italic": selection += [0, 5] os2.fsSelection = intListToNum(selection, 0, 16) # characetr indexes unicodes = [i for i in self.unicodeToGlyphNameMapping.keys() if i is not None] if unicodes: minIndex = min(unicodes) maxIndex = max(unicodes) else: # the font may have *no* unicode values # (it really happens!) so there needs # to be a fallback. use space for this. minIndex = 0x0020 maxIndex = 0x0020 if maxIndex > 0xFFFF: # the spec says that 0xFFFF should be used # as the max if the max exceeds 0xFFFF maxIndex = 0xFFFF os2.fsFirstCharIndex = minIndex os2.fsLastCharIndex = maxIndex os2.usBreakChar = 32 os2.usDefaultChar = 0 # maximum contextual lookup length os2.usMaxContex = 0
def _strip(txt): return ( "".join(l.strip() for l in tounicode(txt, "utf-8").splitlines()) if txt is not None else "" )
def hintFile(options): path = options.inputPath fontFileName = os.path.basename(path) logMsg("Hinting font %s. Start time: %s." % (path, time.asctime())) try: useHashMap = not options.logOnly # for UFO fonts only. We always use the hash map, unless the user has said to only report issues. fontData = openFile(path, options.outputPath, useHashMap) 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) # temp file names for input and output bez files, and for the fontinfo file. tempBez = fdkutils.get_temp_file_path() tempBezNew = '{}{}'.format(tempBez, NEWBEZ_SUFFIX) tempFI = fdkutils.get_temp_file_path() 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) parseFontInfoString(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) parseFontInfoString(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] with open(tempFI, "w") as fp: fp.write(tounicode(fdDict.getFontInfo())) else: if not options.verbose: 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.") # for identifier in glyph-list: # Get charstring. removeHints = 1 isCID = fontData.isCID() lastFDIndex = None reportCB = ACreport anyGlyphChanged = 0 pListChanged = 0 if isCID: options.noFlex = 1 if options.verbose: verboseArg = "" else: verboseArg = " -q" dotCount = 0 curTime = time.time() if options.allowChanges: suppressEditArg = "" else: suppressEditArg = " -e" if options.noHintSub: supressHintSubArg = " -n" else: supressHintSubArg = "" if options.allowDecimalCoords: decimalArg = " -d" else: decimalArg = "" dotCount = 0 seenGlyphCount = 0 processedGlyphCount = 0 for name in glyphList: prevACIdentifier = None seenGlyphCount +=1 # Convert to bez format bezString, width, hasHints = fontData.convertToBez(name, removeHints, options.verbose, options.hintAll) processedGlyphCount += 1 if bezString == None: continue if "mt" not in bezString: # skip empty glyphs. continue # get new fontinfo string if FD array index has changed, as # 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) with open(tempFI, "w") as fp: fp.write(tounicode(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] with open(tempFI, "w") as fp: fp.write(tounicode(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: pListChanged = 1 # Didn't have an entry in tempList file, so we will add one. 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." % aliasName(name)) dotCount = 0 continue if prevACIdentifier and (prevACIdentifier == ACidentifier): # there is an entry in the plist file and it matches what's in the font. if hasHints and not (options.hintAll or options.rehint): continue else: pListChanged = 1 if options.verbose: if fdGlyphDict: logMsg("Hinting %s with fdDict %s." % (aliasName(name), fdDict.DictName) ) else: logMsg("Hinting %s." % aliasName(name)) else: logMsg(".,") # Call auto-hint library on bez string. with open(tempBez, "wt") as bp: bp.write(tounicode(bezString)) #print "oldBezString", oldBezString #print "" #print "bezString", bezString if oldBezString != "" and oldBezString == bezString: newBezString = oldHintBezString else: if os.path.exists(tempBezNew): os.remove(tempBezNew) command = '"%s" %s%s%s%s -s %s -f "%s" "%s"' % (AUTOHINTEXE, verboseArg, suppressEditArg, supressHintSubArg, decimalArg, NEWBEZ_SUFFIX, tempFI, tempBez) if options.debug: print(command) report = fdkutils.runShellCmd(command) if report: if not options.verbose: logMsg("") # end series of "." logMsg(report) if os.path.exists(tempBezNew): bp = open(tempBezNew, "rt") newBezString = bp.read() bp.close() if options.debug: print("Wrote AC fontinfo data file to %s" % tempFI) print("Wrote AC output bez file to %s" % tempBezNew) else: os.remove(tempBezNew) else: newBezString = None if not newBezString: if not options.verbose: logMsg("") logMsg("%s Error - failure in processing outline data." % aliasName(name)) continue 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 = 1 fontData.updateFromBez(newBezString, name, width, options.verbose) if options.usePlistFile: bezString = "%% %s%s%s" % (name, os.linesep, 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" % aliasName(name)) dotCount = 0 fontPlist[kACIDKey][name] = (ACidentifier, time.asctime(), bezString, newBezString ) if not options.verbose: print() # print final new line after progress dots. if options.debug: print("Wrote input AC bez file to %s" % tempBez) if not options.logOnly: if anyGlyphChanged: 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)) logMsg("Done with font %s. End time: %s." % (path, time.asctime()))
def setupTable_name(self): """ Make the name table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ font = self.ufo self.otf["name"] = name = newTable("name") name.names = [] # Set name records from font.info.openTypeNameRecords for nameRecord in getAttrWithFallback( font.info, "openTypeNameRecords"): nameId = nameRecord["nameID"] platformId = nameRecord["platformID"] platEncId = nameRecord["encodingID"] langId = nameRecord["languageID"] # on Python 2, plistLib (used by ufoLib) returns unicode strings # only when plist data contain non-ascii characters, and returns # ascii-encoded bytes when it can. On the other hand, fontTools's # name table `setName` method wants unicode strings, so we must # decode them first nameVal = tounicode(nameRecord["string"], encoding='ascii') name.setName(nameVal, nameId, platformId, platEncId, langId) # Build name records familyName = getAttrWithFallback(font.info, "styleMapFamilyName") styleName = getAttrWithFallback(font.info, "styleMapStyleName").title() # If name ID 2 is "Regular", it can be omitted from name ID 4 fullName = familyName if styleName != "Regular": fullName += " %s" % styleName nameVals = { 0: getAttrWithFallback(font.info, "copyright"), 1: familyName, 2: styleName, 3: getAttrWithFallback(font.info, "openTypeNameUniqueID"), 4: fullName, 5: getAttrWithFallback(font.info, "openTypeNameVersion"), 6: getAttrWithFallback(font.info, "postscriptFontName"), 7: getAttrWithFallback(font.info, "trademark"), 8: getAttrWithFallback(font.info, "openTypeNameManufacturer"), 9: getAttrWithFallback(font.info, "openTypeNameDesigner"), 10: getAttrWithFallback(font.info, "openTypeNameDescription"), 11: getAttrWithFallback(font.info, "openTypeNameManufacturerURL"), 12: getAttrWithFallback(font.info, "openTypeNameDesignerURL"), 13: getAttrWithFallback(font.info, "openTypeNameLicense"), 14: getAttrWithFallback(font.info, "openTypeNameLicenseURL"), 16: getAttrWithFallback( font.info, "openTypeNamePreferredFamilyName"), 17: getAttrWithFallback( font.info, "openTypeNamePreferredSubfamilyName"), } # don't add typographic names if they are the same as the legacy ones if nameVals[1] == nameVals[16]: del nameVals[16] if nameVals[2] == nameVals[17]: del nameVals[17] # postscript font name if nameVals[6]: nameVals[6] = normalizeStringForPostscript(nameVals[6]) for nameId in sorted(nameVals.keys()): nameVal = nameVals[nameId] if not nameVal: continue nameVal = tounicode(nameVal, encoding='ascii') platformId = 3 platEncId = 10 if _isNonBMP(nameVal) else 1 langId = 0x409 # Set built name record if not set yet if name.getName(nameId, platformId, platEncId, langId): continue name.setName(nameVal, nameId, platformId, platEncId, langId)