Example #1
0
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)
Example #2
0
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)
Example #3
0
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 ""
Example #4
0
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)
Example #5
0
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 ""
Example #6
0
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)
Example #7
0
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)))
Example #8
0
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)
Example #9
0
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
Example #10
0
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
Example #11
0
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)))
Example #12
0
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"
Example #13
0
 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
Example #14
0
    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
Example #15
0
 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
Example #16
0
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)
Example #17
0
 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
Example #18
0
 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
Example #19
0
 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
Example #20
0
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
Example #21
0
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)
Example #23
0
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)
Example #24
0
    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
Example #25
0
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)
Example #26
0
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
Example #27
0
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
Example #28
0
    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
Example #29
0
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
Example #30
0
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
Example #31
0
 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)
Example #33
0
    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")
Example #34
0
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)
Example #35
0
File: build.py Project: qirh/amiri
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
Example #36
0
 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
Example #37
0
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)
Example #38
0
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)
Example #39
0
def fsdecode(path, encoding=sys.getfilesystemencoding()):
    return tounicode(path, encoding=encoding)
Example #40
0
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()))
Example #41
0

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
Example #43
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
Example #44
0
def _strip(txt):
    return (
        "".join(l.strip() for l in tounicode(txt, "utf-8").splitlines())
        if txt is not None
        else ""
    )
Example #45
0
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()))
Example #46
0
    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)