def __init__(self, family, name): self.family = family self.name = name self.style_scheme = kit.constants.STYLES_ITF self.vertical_metrics_strategy = "ITF" current_year = datetime.date.today().year if self.family.initial_release_year: self.release_year_range = str(self.family.initial_release_year) if current_year > self.family.initial_release_year: self.release_year_range += "-{}".format(str(current_year)) else: self.release_year_range = str(current_year) self.tables = {} self.tables["name"] = { 0: kit.fallback( self.family.info.copyright, "Copyright {} Indian Type Foundry. All rights reserved.". format(self.release_year_range), ), 7: "{} is a trademark of the Indian Type Foundry.".format( self.family.trademark), 8: "Indian Type Foundry", 9: self.family.info.openTypeNameDesigner, 10: self.family.info.openTypeNameDescription, 11: "https://indiantypefoundry.com", 12: self.family.info.openTypeNameDesignerURL, 13: "This Font Software is protected under domestic and international trademark and copyright law. You agree to identify the ITF fonts by name and credit the ITF's ownership of the trademarks and copyrights in any design or production credits.", 14: "https://indiantypefoundry.com/licensing", 19: self.family.script.sample_text, } self.tables["OS/2"] = { "fsType": kit.fallback(self.family.info.openTypeOS2Type, 0), "Panose": "0 0 0 0 0 0 0 0 0 0", "Vendor": "ITFO", } self.override()
def __init__( self, trademark=None, name_script_independent=None, script_name="Latin", append_script_name=False, name=None, client_name=None, source_tag=None, initial_release_year=None, ): self.trademark = trademark self.name_script_independent = kit.fallback(name_script_independent, self.trademark) self.script = kit.constants.SCRIPT_NAMES_TO_SCRIPTS.get(script_name) if name: self.name = name else: self.name = self.name_script_independent if script_name and append_script_name: self.name += " " + script_name self.name_postscript = kit.remove_illegal_chars_for_postscript_name_part( self.name) self.masters = None self.styles = None self.info = kit.patched.defcon.Font().info self.client_name = client_name self.source_tag = source_tag self.initial_release_year = initial_release_year
def client_override(self): if self.name == "Google Fonts": self.tables["name"].update({ 0: hindkit.fallback( self.family.info.copyright, "Copyright {} Struckby ([email protected])".format( self.release_year_range), ), 7: None, 8: "Struckby", 9: "Saumya Kishore and Sanchit Sawaria", 11: "https://struckby.co", 13: "This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL", 14: "http://scripts.sil.org/OFL", }) self.tables["OS/2"].update({ "Vendor": None, })
def __init__( self, name, abbr, tags, aliases=None, sample_text=None, is_indic=False, unicode_range_bits=None, ): self.name = name self.abbr = abbr self.tags = tags self.aliases = kit.fallback(aliases, []) self.sample_text = sample_text self.is_indic = is_indic self.unicode_range_bits = kit.fallback(unicode_range_bits, [])
def bases_alive(self): return kit.fallback( self._bases_alive, [ self.font[i] for i in self.font.groups.get(self.CLASS_NAME_BASES_ALIVE, []) if ".mU" not in i ], )
def __init__( self, name, file_format=None, abstract_directory=None, project=None, family=None, extra_filenames=None, ): self.name = name self.file_format = file_format if project and family is None: self.project = project self.family = self.project.family else: self.project = project self.family = family if abstract_directory is None and self.project: self.abstract_directory = kit.Project.directories["sources"] else: self.abstract_directory = abstract_directory if self.family and self.family.source_tag: self.abstract_directory_variant = os.path.join( self.abstract_directory, self.family.source_tag) if os.path.exists(self.abstract_directory_variant): self.abstract_directory = self.abstract_directory_variant self.extra_filenames = kit.fallback(extra_filenames, self._extra_filenames) self.file_group = [] for filename in self.extra_filenames[0] + [self.name ] + self.extra_filenames[1]: if filename == self.name: self.file_group.append(self) else: f = kit.BaseFile( filename, file_format=self.file_format, abstract_directory=self.abstract_directory, family=self.family, ) self.file_group.append(f) self.counter = 0 self._filename = None self._extension = None self._filename_with_extension = None self._directory = None self._path = None
def generate(self): lines = [] if self.project.options["prepare_mark_positioning"]: f = kit.filters glyph_classes = [ (WriteFeaturesMarkFDK.kCombMarksClassName, f.marks, None), ] if self.project.options["match_mI_variants"]: m = FeatureMatches glyph_classes.extend([ (m.CLASS_NAME_mI_VARIANTS, f.mI_variants, None), (m.CLASS_NAME_BASES_ALIVE, f.bases_alive, m.BASE_NAMES_ALIVE), (m.CLASS_NAME_BASES_FOR_LONG_mII, f.bases_for_long_mII, m.BASE_NAMES_FOR_LONG_mII), ]) if self.project.options["match_mI_variants"] > 1: glyph_classes.extend([ (m.CLASS_NAME_BASES_DEAD, f.bases_dead, m.BASE_NAMES_DEAD), ]) font_0 = self.project.products[0].style.open() glyph_order = self.project.glyph_data.glyph_order for class_name, filter_function, overriding in glyph_classes: glyph_names = kit.fallback( overriding, [ i.name for i in font_0 if filter_function(self.project.family, i) ], ) glyph_names = self.sort_names(glyph_names, glyph_order) if glyph_names: font_0.groups.update({class_name: glyph_names}) lines.extend( self.compose_glyph_class_def_lines(class_name, glyph_names)) font_0.save() for style in (i.style for i in self.project.products[1:]): font = style.open() font.groups.update(font_0.groups) font.save() if lines: with open(self.get_path(), "w") as f: f.writelines(i + "\n" for i in lines)
def override(self): if self.name == "Google Fonts": self.style_scheme = kit.constants.STYLES_ITF_CamelCase self.vertical_metrics_strategy = "Google Fonts" self.tables["name"].update({ 0: kit.fallback( self.family.info.copyright, "Copyright {} Indian Type Foundry ([email protected])".format(self.release_year_range), ), 7: None, 13: "This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL", 14: "http://scripts.sil.org/OFL", })
def set_styles(self, scheme=None): scheme = kit.fallback(scheme, self.get_client_data().style_scheme) self.styles = [ kit.Style(self, name, location, weight_and_width_class) for name, location, weight_and_width_class in scheme ] if self.masters is None: self.masters = [] for master in self.masters: for style in self.styles: if style.location == master.location: style.master = master break
def __init__(self, project, name=None, style=None): if style: abstract_directory = style.abstract_directory else: abstract_directory = kit.Project.directories["features"] super(BaseFeature, self).__init__( name=kit.fallback(name, self._name), file_format="FEA", project=project, extra_filenames=self._extra_filenames, abstract_directory=abstract_directory, ) self.style = style
def filename(self): return kit.fallback(self._filename, self.full_name_postscript)
def filename(self): return kit.fallback(self._filename, self.name)
def set_masters(self, scheme=None): scheme = kit.fallback(scheme, [("Light", 0), ("Bold", 100)]) self.masters = [ kit.Master(self, name, location) for name, location in scheme ]
def full_name_postscript(self): return kit.fallback( self._full_name_postscript, self.family.name_postscript + "-" + self.name_postscript, )
def extension(self): return kit.fallback( self._extension, self.file_format.lower() if self.file_format else None, )
def filename_with_extension(self): return kit.fallback( self._filename_with_extension, self.filename + (("." + self.extension) if self.extension else ""), )
def full_name(self): return kit.fallback(self._full_name, self.family.name + " " + self.name)
def _finalize_options(self): parser = argparse.ArgumentParser( description= "execute `AFDKOPython build.py` to run stages as specified in build.py, or append arguments to override." ) parser.add_argument( "--test", action="store_true", help="run a minimum and fast build process.", ) parser.add_argument( "--stages", action="store", help= '"1" for "prepare_masters", "2" for "prepare_styles", "3" for "prepare_features", and "4" for "compile".', ) parser.add_argument( "--options", action="store", help= '"0" for none, "1" for "makeinstances", "2" for "checkoutlines", and "3" for "autohint".', ) self.args = parser.parse_args() if self.args.stages: stages = str(self.args.stages) self.options["prepare_masters"] = "1" in stages self.options["prepare_styles"] = "2" in stages self.options["prepare_features"] = "3" in stages self.options["compile"] = "4" in stages if self.args.options: options = str(self.args.options) self.options["run_makeinstances"] = "1" in options self.options["run_checkoutlines"] = "2" in options self.options["run_autohint"] = "3" in options if self.args.test: self.options["run_makeinstances"] = False self.options["run_checkoutlines"] = False self.options["run_autohint"] = False self.options["build_ttf"] = False styles = self.family.styles if self.family.masters: if not self.options["run_makeinstances"]: styles = [i for i in self.family.styles if i.master] else: self.options["prepare_masters"] = False self.options["run_makeinstances"] = False if not styles: self.options["prepare_styles"] = False self.products = [i.produce(self, file_format="OTF") for i in styles] if self.options["build_ttf"]: self.products.extend( i.produce(self, file_format="TTF", subsidiary=True) for i in styles) if not self.products: self.options["compile"] = False for product in self.products: directory_parts = [ "TEST" if self.args.test else None, self.family.name_postscript, self.fontrevision, self.target_tag, product.file_format, ] product.abstract_directory = os.path.join( product.abstract_directory, "-".join([_f for _f in directory_parts if _f]), ) if self.options["match_mI_variants"]: self.abbrs_of_scripts_to_match_mI_variants = [ kit.constants.SCRIPT_NAMES_TO_SCRIPTS[i].abbr for i in kit.fallback( self.options["match_mI_variants_for_scripts"], [self.family.script.name], ) ] if len(self.abbrs_of_scripts_to_match_mI_variants) == 1: self.script_abbr_current = self.abbrs_of_scripts_to_match_mI_variants[ 0] else: raise NotImplementedError( "[NOT IMPLEMENTED] Can't match mI variants for more than one script." )
def get_directory(self, temp=True): directory = self.abstract_directory if temp: directory = kit.Project.temp(directory) return kit.fallback(self._directory, directory)
def filename(self): return kit.fallback(self._filename, "font")
def import_from_font( self, source_path, target_path = None, import_glyphs = True, glyph_names_included = None, glyph_names_excluded = None, glyph_renaming_map = None, import_anchors = False, import_kerning = False, import_blue_zones = False, import_x_and_cap_heights = False, ): g_names_included = kit.fallback(glyph_names_included, []) g_names_excluded = kit.fallback(glyph_names_excluded, []) g_names_included = set(g_names_included) g_names_excluded = set(g_names_excluded) if glyph_renaming_map is not None: self.glyph_renaming_map.update(glyph_renaming_map) if import_glyphs and source_path.endswith((".ufo", ".vfb")): source_file = BaseFont( family = self.family, abstract_directory = kit.Project.directories["misc"], file_format = source_path[-3:].upper(), ) source_file._path = source_path source_font = source_file.open() elif import_kerning and source_path.endswith(".fea"): kern_fea_reader = getKerningPairsFromFEA.FEAKernReader([source_path]) source_font = kit.patched.defcon.Font() source_font.groups.update(kern_fea_reader.kernClasses) kern_classes_reversed = {tuple(v): k for k, v in kern_fea_reader.kernClasses.items()} if len(kern_fea_reader.kernClasses) != len(kern_classes_reversed): raise SystemExit() for enum, (left, right), value in reversed(kern_fea_reader.foundKerningPairs): pair = [] for side in left, right: parts = side.split() if tuple(parts) in kern_classes_reversed: parts = [kern_classes_reversed[tuple(parts)]] pair.append(parts) pairs = list(itertools.product(*pair)) for pair in pairs: source_font.kerning[pair] = int(value) else: raise SystemExit("The format of {} is not supported.".format(source_path)) if target_path: self._path = target_path target_font = self.open() if import_blue_zones: source_blue_values = source_font.info.postscriptBlueValues source_other_blues = source_font.info.postscriptOtherBlues target_blue_values = target_font.info.postscriptBlueValues target_other_blues = target_font.info.postscriptOtherBlues blue_values = [min(source_blue_values[0], target_blue_values[0]), 0] blue_values.extend(source_blue_values[2:4] + target_blue_values[2:4] + source_blue_values[6:10]) other_blues = source_other_blues target_font.info.postscriptBlueValues = blue_values target_font.info.postscriptOtherBlues = other_blues print(source_other_blues, source_blue_values) print(target_other_blues, target_blue_values) print(other_blues, blue_values) if import_x_and_cap_heights: self.x_height = source_font.info.xHeight self.cap_height = source_font.info.capHeight if g_names_included: g_names_importing = g_names_included else: g_names_importing = set(source_font.keys()) g_names_importing_renamed = { self.glyph_renaming_map.get(i, i) for i in g_names_importing } if import_glyphs and g_names_importing: print("\n[NOTE] Importing glyphs from `{}` to `{}`:".format(source_path, self.name)) if g_names_excluded: g_names_importing.difference_update(g_names_excluded) print("Excluding: {}".format(", ".join(g_names_excluded))) g_names_importing_renamed g_names_already_existing = g_names_importing_renamed.intersection(set(target_font.keys())) if g_names_already_existing: g_names_importing.difference_update(g_names_already_existing) print("Already existing; will not overwrite: {}".format(", ".join(g_names_already_existing))) g_names_importing = ( [i for i in source_font.glyphOrder if i in g_names_importing] + [i for i in g_names_importing if i not in source_font.glyphOrder] ) for source_g_name in g_names_importing: source_g = source_font[source_g_name] if not import_anchors: source_g.clearAnchors() for component in source_g.components: if component.baseGlyph not in g_names_importing: source_g.decomposeComponent(component) print("(decomposed {} in {})".format(component.baseGlyph, source_g_name), end=" ") target_g_name = self.glyph_renaming_map.get(source_g_name, source_g_name) target_font.newGlyph(target_g_name) target_g = target_font[target_g_name] target_g.copyDataFromGlyph(source_g) if target_g_name == source_g_name: print(target_g_name, end=", ") else: print("{} -> {}".format(source_g_name, target_g_name), end=", ") print() # TODO: Component reference and glyph group reference both need to be updated. if import_kerning and source_font.kerning: target_font.groups.update(source_font.groups) target_font.kerning.update(source_font.kerning) print("\n[NOTE] Imported kerning.")
def get_path(self, temp=True): return kit.fallback( self._path, os.path.join(self.get_directory(temp=temp), self.filename_with_extension), )
def bases_dead(self): return kit.fallback(self._bases_dead, [ self.font[i] for i in self.font.groups.get(self.CLASS_NAME_BASES_DEAD, []) ])
def style_linking_family_name(self): return kit.fallback(self._style_linking_family_name, self.full_name)
def __init__( self, family, target_tag=None, release_commit=None, # (65535, 999) fontrevision="1.000", options={}, ): self.family = family self.family.project = self self.target_tag = kit.fallback(target_tag, self.family.source_tag) if release_commit: release, commit = release_commit self.version = Version(release, commit, 1) self.version_last = Version(None, None, None) version_record_path = kit.relative_to_cwd("version{}.txt".format( "-" + self.target_tag if self.target_tag else "")) try: with open(version_record_path, "r") as f: for line in f.read().splitlines(): k, _, v = line.partition(" ") setattr(self.version_last, k, int(v)) except IOError as e: if e.errno == errno.ENOENT: pass else: raise if (self.version.release, self.version.commit) == (self.version_last.release, self.version_last.commit): self.version.build = self.version_last.build + 1 with open(version_record_path, "w") as f: for k in ["release", "commit", "build"]: f.write("{} {}\n".format(k, getattr(self.version, k))) self.fontrevision = "{}.{}".format( self.version.release, str(self.version.commit).zfill(3), ) self.version_string = "{}b{}".format( self.fontrevision, self.version.build, ) else: self.version = None self.version_last = None self.fontrevision = fontrevision self.version_string = None # (light_min, light_max), (bold_min, bold_max) self.adjustment_for_matching_mI_variants = None self.abbrs_of_scripts_to_match_mI_variants = [] self.script_abbr_current = None self.options = { "prepare_masters": True, "prepare_styles": True, "prepare_features": True, "compile": True, "prepare_kerning": False, "prepare_mark_positioning": False, "prepare_mark_to_mark_positioning": True, "match_mI_variants": 0, "match_mI_variants_for_scripts": None, "position_marks_for_mI_variants": False, "run_makeinstances": True, "do_normalize": True, "run_checkoutlines": True, "run_autohint": False, "build_ttf": False, "override_GDEF": True, "override_x_and_cap_heights": False, "do_style_linking": False, "use_mac_name_records": False, "use_os_2_version_4": False, "prefer_typo_metrics": False, "is_width_weight_slope_only": False, "additional_unicode_range_bits": [], "additional_code_pages": [], } self.options.update(options) self.glyph_data = kit.GlyphData() self.designspace = kit.DesignSpace(self) self.fmndb = kit.Fmndb(self) self._finalize_options()
def name_postscript(self): return kit.fallback(self._name_postscript, kit.remove_illegal_chars_for_postscript_name_part(self.name))