Ejemplo n.º 1
0
 def test_constructs(self):
     for name in ("Attach enum markClass language_required "
                  "GlyphClassDef LigatureCaretByIndex LigatureCaretByPos "
                  "lookup lookupflag").split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("%s.ttx" % name))
Ejemplo n.º 2
0
def main(args=None):
    parser = argparse.ArgumentParser(
        description="Use fontTools to compile OpenType feature files (*.fea).")
    parser.add_argument(
        "input_fea", metavar="FEATURES", help="Path to the feature file")
    parser.add_argument(
        "input_font", metavar="INPUT_FONT", help="Path to the input font")
    parser.add_argument(
        "-o", "--output", dest="output_font", metavar="OUTPUT_FONT",
        help="Path to the output font.")
    parser.add_argument(
        "-t", "--tables", metavar="TABLE_TAG", choices=Builder.supportedTables,
        nargs='+', help="Specify the table(s) to be built.")
    parser.add_argument(
        "-v", "--verbose", help="increase the logger verbosity. Multiple -v "
        "options are allowed.", action="count", default=0)
    options = parser.parse_args(args)

    levels = ["WARNING", "INFO", "DEBUG"]
    configLogger(level=levels[min(len(levels) - 1, options.verbose)])

    output_font = options.output_font or makeOutputFileName(options.input_font)
    log.info("Compiling features to '%s'" % (output_font))

    font = TTFont(options.input_font)
    addOpenTypeFeatures(font, options.input_fea, tables=options.tables)
    font.save(output_font)
def _layoutEngineOTLTablesRepresentationFactory(layoutEngine):
    import os
    import tempfile
    font = layoutEngine.font
    gdef = gsub = gpos = None
    if font.features.text:
        otf = TTFont()
        otf.setGlyphOrder(sorted(font.keys()))
        # XXX hack around fontTools only reading from disk
        fd, feaPath = tempfile.mkstemp()
        f = open(feaPath, "w")
        f.write(font.features.text)
        f.close()
        # compile with fontTools
        try:
            addOpenTypeFeatures(feaPath, otf)
        except:
            import traceback
            print(traceback.format_exc(5))
        finally:
            os.close(fd)
            os.remove(feaPath)
        if "GDEF" in otf:
            gdef = otf["GDEF"]
        if "GSUB" in otf:
            gsub = otf["GSUB"]
        if "GPOS" in otf:
            gpos = otf["GPOS"]
    return gdef, gsub, gpos
Ejemplo n.º 4
0
def main(args=None):
    parser = argparse.ArgumentParser(
        description="Use fontTools to compile OpenType feature files (*.fea).")
    parser.add_argument(
        "input_fea", metavar="FEATURES", help="Path to the feature file")
    parser.add_argument(
        "input_font", metavar="INPUT_FONT", help="Path to the input font")
    parser.add_argument(
        "-o", "--output", dest="output_font", metavar="OUTPUT_FONT",
        help="Path to the output font.")
    parser.add_argument(
        "-t", "--tables", metavar="TABLE_TAG", choices=Builder.supportedTables,
        nargs='+', help="Specify the table(s) to be built.")
    parser.add_argument(
        "-v", "--verbose", help="increase the logger verbosity. Multiple -v "
        "options are allowed.", action="count", default=0)
    options = parser.parse_args(args)

    levels = ["WARNING", "INFO", "DEBUG"]
    configLogger(level=levels[min(len(levels) - 1, options.verbose)])

    output_font = options.output_font or makeOutputFileName(options.input_font)
    log.info("Compiling features to '%s'" % (output_font))

    font = TTFont(options.input_font)
    addOpenTypeFeatures(font, options.input_fea, tables=options.tables)
    font.save(output_font)
Ejemplo n.º 5
0
 def build(self, featureFile):
     path = self.temp_path(suffix=".fea")
     with codecs.open(path, "wb", "utf-8") as outfile:
         outfile.write(featureFile)
     font = makeTTFont()
     addOpenTypeFeatures(path, font)
     return font
Ejemplo n.º 6
0
 def test_constructs(self):
     for name in ("Attach enum markClass language_required "
                  "GlyphClassDef LigatureCaretByIndex LigatureCaretByPos "
                  "lookup lookupflag").split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("%s.ttx" % name))
Ejemplo n.º 7
0
    def setupFile_featureTables(self):
        """
        Compile and return OpenType feature tables from the source.
        Raises a FeaLibError if the feature compilation was unsuccessful.

        **This should not be called externally.** Subclasses
        may override this method to handle the table compilation
        in a different way if desired.
        """

        if self.mtiFeaFiles is not None:
            for tag, feapath in self.mtiFeaFiles.items():
                with open(feapath) as feafile:
                    table = mtiLib.build(feafile, self.outline)
                    assert table.tableTag == tag
                    self.outline[tag] = table

        elif self.features.strip():
            if self.font.path is not None:
                self.features = forceAbsoluteIncludesInFeatures(self.features, self.font.path)
            fd, fea_path = tempfile.mkstemp()
            with open(fea_path, "w") as feafile:
                feafile.write(self.features)
            addOpenTypeFeatures(fea_path, self.outline)
            os.close(fd)
            os.remove(fea_path)
Ejemplo n.º 8
0
 def build(self, featureFile):
     path = self.temp_path(suffix=".fea")
     with codecs.open(path, "wb", "utf-8") as outfile:
         outfile.write(featureFile)
     font = makeTTFont()
     addOpenTypeFeatures(path, font)
     return font
Ejemplo n.º 9
0
 def build(self, featureFile):
     path = self.temp_path(suffix=".fea")
     with open(path, "w", encoding="utf-8") as outfile:
         outfile.write(featureFile)
     font = makeTTFont()
     addOpenTypeFeatures(font, path)
     return font
Ejemplo n.º 10
0
 def build(self, featureFile):
     path = self.temp_path(suffix=".fea")
     with open(path, "w", encoding="utf-8") as outfile:
         outfile.write(featureFile)
     font = makeTTFont()
     addOpenTypeFeatures(font, path)
     return font
Ejemplo n.º 11
0
def build(args):
    font, features = merge(args)

    build_encoded(font, features)

    with tempfile.NamedTemporaryFile(mode="r", suffix=args.out_file) as tmp:
        font.generate(tmp.name, flags=["round", "opentype"])
        ttfont = TTFont(tmp.name)

    try:
        builder.addOpenTypeFeatures(ttfont, features)
    except:
        with tempfile.NamedTemporaryFile(mode="w+", delete=False) as tmp:
            tmp.write(features.asFea())
            print("Failed! Inspect temporary file: %r" % tmp.name)
        raise

    # Filter-out useless Macintosh names
    ttfont["name"].names = [n for n in ttfont["name"].names if n.platformID != 1]

    # https://github.com/fontforge/fontforge/pull/3235
    # fontDirectionHint is deprecated and must be set to 2
    ttfont["head"].fontDirectionHint = 2
    # unset bits 6..10
    ttfont["head"].flags &= ~0x7e0

    # Drop useless table with timestamp
    if "FFTM" in ttfont:
        del ttfont["FFTM"]

    ttfont.save(args.out_file)
    def addFeatures(self, fea, clear=True):
        feaPath = tempfile.mktemp()
        f = open(feaPath, "w")
        f.write(fea)
        f.close()

        if clear:
            if "GSUB" in self.source:
                del self.source["GSUB"]
            if "GPOS" in self.source:
                del self.source["GPOS"]
        try:
            addOpenTypeFeatures(self.source, feaPath)
        except Exception:
            import traceback
            print(traceback.format_exc(5))
        finally:
            os.remove(feaPath)
            # XXX hacking into fontTools
            for tableName in ("GDEF", "GSUB", "GPOS"):
                if tableName in self.source:
                    table = self.source[tableName]
                    compiled = table.compile(self.source)
                    table.decompile(compiled, self.source)
            self.loadFeatures()
Ejemplo n.º 13
0
def compileGSUB(featureFile, glyphOrder):
    """ Compile and return a GSUB table from `featureFile` (feaLib
    FeatureFile), using the given `glyphOrder` (list of glyph names).
    """
    font = ttLib.TTFont()
    font.setGlyphOrder(glyphOrder)
    addOpenTypeFeatures(font, featureFile, tables={"GSUB"})
    return font.get("GSUB")
Ejemplo n.º 14
0
 def check_feature_file(self, name):
     font = makeTTFont()
     addOpenTypeFeatures(font, self.getpath("%s.fea" % name))
     self.expect_ttx(font, self.getpath("%s.ttx" % name))
     # Make sure we can produce binary OpenType tables, not just XML.
     for tag in ('GDEF', 'GSUB', 'GPOS'):
         if tag in font:
             font[tag].compile(font)
Ejemplo n.º 15
0
 def check_feature_file(self, name):
     font = makeTTFont()
     addOpenTypeFeatures(font, self.getpath("%s.fea" % name))
     self.expect_ttx(font, self.getpath("%s.ttx" % name))
     # Make sure we can produce binary OpenType tables, not just XML.
     for tag in ('GDEF', 'GSUB', 'GPOS'):
         if tag in font:
             font[tag].compile(font)
Ejemplo n.º 16
0
 def check_feature_file(self, name):
     font = makeTTFont()
     feapath = self.getpath("%s.fea" % name)
     addOpenTypeFeatures(font, feapath)
     self.expect_ttx(font, self.getpath("%s.ttx" % name))
     # Make sure we can produce binary OpenType tables, not just XML.
     for tag in ('GDEF', 'GSUB', 'GPOS'):
         if tag in font:
             font[tag].compile(font)
     debugttx = self.getpath("%s-debug.ttx" % name)
     if os.path.exists(debugttx):
         addOpenTypeFeatures(font, feapath, debug=True)
         self.expect_ttx(font, debugttx, replace = {"__PATH__": feapath})
Ejemplo n.º 17
0
def dlig2calt(fontPath, inplace=False):

    font = ttLib.TTFont(fontPath)

    unitWidth = font['hmtx']['space'][0] # 600 for most monospace fonts w/ UPM=1000

    # make "LIG" glyph
    font['glyf'].__setitem__('LIG', font['glyf']['space'])

    font['hmtx'].__setitem__('LIG', font['hmtx']['space'])

    # update code ligature widths to be single units with left overhang
    for glyphName in font.getGlyphNames():
        if font['hmtx'][glyphName][0] > unitWidth:

            decomposeAndRemoveOverlap(font, glyphName)

            # set width to space (e.g. 600), then offset left side to be negative
            oldWidth = font['hmtx'][glyphName][0]
            oldLSB = font['hmtx'][glyphName][1]
            widthDiff = oldWidth - unitWidth
            newLSB = oldLSB - widthDiff
            font['hmtx'].__setitem__(glyphName, (unitWidth, newLSB))

            # Adjust coordinates in glyf table
            coords = font['glyf'][glyphName].coordinates
            phantoms = font['glyf'].getPhantomPoints(glyphName, font)

            adjustedCoords = [(x-widthDiff, y) for x, y in coords]
            adjustedPhantoms = [(0,0), (600,0), phantoms[-2], phantoms[-1]]

            newCoords = adjustedCoords+adjustedPhantoms
            # print(glyphName, newCoords, font) # DEBUGGING
            font['glyf'].setCoordinates(glyphName, newCoords, font)


    # add new feature code, using calt rather than dlig
    builder.addOpenTypeFeatures(font,"font-data/features/calt-generated--code_fonts_only.fea")


    # save font
    if inplace:
        font.save(fontPath)
        print("\nCode ligatures are now on by default.\n")
    else:
        newPath = fontPath.replace('.ttf','.calt_ligs.ttf')
        font.save(newPath)
        print("Saved font with feature 'dlig' changed to 'calt' at ", newPath)
Ejemplo n.º 18
0
 def check_feature_file(self, name):
     font = makeTTFont()
     feapath = self.getpath("%s.fea" % name)
     addOpenTypeFeatures(font, feapath)
     self.expect_ttx(font, self.getpath("%s.ttx" % name))
     # Check that:
     # 1) tables do compile (only G* tables as long as we have a mock font)
     # 2) dumping after save-reload yields the same TTX dump as before
     for tag in ('GDEF', 'GSUB', 'GPOS'):
         if tag in font:
             data = font[tag].compile(font)
             font[tag].decompile(data, font)
     self.expect_ttx(font, self.getpath("%s.ttx" % name))
     # Optionally check a debug dump.
     debugttx = self.getpath("%s-debug.ttx" % name)
     if os.path.exists(debugttx):
         addOpenTypeFeatures(font, feapath, debug=True)
         self.expect_ttx(font, debugttx, replace={"__PATH__": feapath})
Ejemplo n.º 19
0
def compileGDEF(featureFile, glyphOrder):
    """Compile and return a GDEF table from `featureFile` (feaLib FeatureFile),
    using the given `glyphOrder` (list of glyph names).
    """
    from fontTools.feaLib.ast import TableBlock

    font = ttLib.TTFont()
    font.setGlyphOrder(glyphOrder)
    gdefDefined = False
    for statement in featureFile.statements:
        if isinstance(statement, TableBlock) and statement.name == "GDEF":
            gdefDefined = True

    if not gdefDefined:
        addOpenTypeFeatures(font, featureFile, tables={"GDEF", "GPOS", "GSUB"})
    else:
        addOpenTypeFeatures(font, featureFile, tables={"GDEF"})
    return font.get("GDEF")
Ejemplo n.º 20
0
 def check_feature_file(self, name):
     font = makeTTFont()
     if name.startswith("variable_"):
         font["name"] = newTable("name")
         addFvar(font, self.VARFONT_AXES, [])
         del font["name"]
     feapath = self.getpath("%s.fea" % name)
     addOpenTypeFeatures(font, feapath)
     self.expect_ttx(font, self.getpath("%s.ttx" % name))
     # Check that:
     # 1) tables do compile (only G* tables as long as we have a mock font)
     # 2) dumping after save-reload yields the same TTX dump as before
     for tag in ("GDEF", "GSUB", "GPOS"):
         if tag in font:
             data = font[tag].compile(font)
             font[tag].decompile(data, font)
     self.expect_ttx(font, self.getpath("%s.ttx" % name))
     # Optionally check a debug dump.
     debugttx = self.getpath("%s-debug.ttx" % name)
     if os.path.exists(debugttx):
         addOpenTypeFeatures(font, feapath, debug=True)
         self.expect_ttx(font, debugttx, replace={"__PATH__": feapath})
Ejemplo n.º 21
0
#!/usr/bin/env python

from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
import fontTools.feaLib.builder as feaLibBuilder
from fontTools.ttLib import TTFont
import sys

if len(sys.argv) != 4:
    print("usage: apply-feature-file.py features.fea in.ttf out.ttf")
    sys.exit(1)

inputFeaturePath = sys.argv[1]
inputFontPath = sys.argv[2]
outputFontPath = sys.argv[3]

font = TTFont(inputFontPath)
feaLibBuilder.addOpenTypeFeatures(inputFeaturePath, font)
font.save(outputFontPath)
Ejemplo n.º 22
0
 def test_GSUB(self):
     for name in "2 3 6 8".split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("GSUB_%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("GSUB_%s.ttx" % name))
Ejemplo n.º 23
0
#!/usr/bin/env python

from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
import fontTools.feaLib.builder as feaLibBuilder
from fontTools.ttLib import TTFont
import sys

if len(sys.argv) != 4:
    print("usage: apply-feature-file.py features.fea in.ttf out.ttf")
    sys.exit(1)

inputFeaturePath = sys.argv[1]
inputFontPath = sys.argv[2]
outputFontPath = sys.argv[3]

font = TTFont(inputFontPath)
feaLibBuilder.addOpenTypeFeatures(font, inputFeaturePath)
font.save(outputFontPath)
Ejemplo n.º 24
0
deepcopy_glyph(font, ord('*'), ".cb")
# open/close it 
deepcopy_glyph(font, ord('*'), ".oi")
deepcopy_glyph(font, ord('*'), ".ci")
# open/close mono 
deepcopy_glyph(font, ord('`'), ".om")
deepcopy_glyph(font, ord('`'), ".cm")
# open/close boldit
deepcopy_glyph(font, ord('*'), ".obi")
deepcopy_glyph(font, ord('*'), ".cbi")
# open/close itbold
deepcopy_glyph(font, ord('*'), ".oib")
deepcopy_glyph(font, ord('*'), ".cib")
# open/close all
deepcopy_glyph(font, ord('*'), ".oa")
deepcopy_glyph(font, ord('*'), ".ca")
# hidden
deepcopy_glyph(font, ord('\\'), ".h")

font.generate('tmp.ttf')

font.close()
it.close()
bold.close()
boldit.close()
mono.close()

ft_font = TTFont('tmp.ttf')
addOpenTypeFeatures(ft_font, 'md.fea', tables=['GSUB'])
ft_font.save('fonts/output.ttf')
Ejemplo n.º 25
0
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
import fontTools.feaLib.builder as feaLibBuilder
from fontTools.ttLib import TTFont
from fontTools import configLogger
import sys
import argparse


parser = argparse.ArgumentParser(
    description="Use fontTools to compile OpenType features.")
parser.add_argument("input_fea", metavar="FEATURES",
                    help="Path to the feature file")
parser.add_argument("input_font", metavar="INPUT",
                    help="Path to the input font")
parser.add_argument("output_font", metavar="OUTPUT",
                    help="Path to the output font")
parser.add_argument("-v", "--verbose", help="increase the logger verbosity. "
                    "Multiple -v options are allowed.", action="count",
                    default=0)
options = parser.parse_args(sys.argv[1:])

levels = ["WARNING", "INFO", "DEBUG"]
configLogger(level=levels[min(len(levels) - 1, options.verbose)])


font = TTFont(options.input_font)
feaLibBuilder.addOpenTypeFeatures(font, options.input_fea)
font.save(options.output_font)
Ejemplo n.º 26
0
 def test_alternateSubst(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("GSUB_3.fea"), font)
     self.expect_ttx(font, self.getpath("GSUB_3.ttx"))
Ejemplo n.º 27
0
 def check_feature_file(self, name):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("%s.fea" % name), font)
     self.expect_ttx(font, self.getpath("%s.ttx" % name))
Ejemplo n.º 28
0
 def test_alternateSubst(self):
     font = TTFont()
     addOpenTypeFeatures(self.getpath("GSUB_2.fea"), font)
     self.expect_ttx(font, self.getpath("GSUB_2.ttx"))
Ejemplo n.º 29
0
 def test_lookup(self):
     font = TTFont()
     addOpenTypeFeatures(self.getpath("lookup.fea"), font)
     self.expect_ttx(font, self.getpath("lookup.ttx"))
Ejemplo n.º 30
0
 def test_language_required(self):
     font = TTFont()
     addOpenTypeFeatures(self.getpath("language_required.fea"), font)
     self.expect_ttx(font, self.getpath("language_required.ttx"))
Ejemplo n.º 31
0
 def test_spec5d2(self):
     # OpenType Feature File specification, section 5.d, example 2.
     font = TTFont()
     addOpenTypeFeatures(self.getpath("spec5d2.fea"), font)
     self.expect_ttx(font, self.getpath("spec5d2.ttx"))
Ejemplo n.º 32
0
 def test_spec4h1(self):
     # OpenType Feature File specification, section 4.h, example 1.
     font = TTFont()
     addOpenTypeFeatures(self.getpath("spec4h1.fea"), font)
     self.expect_ttx(font, self.getpath("spec4h1.ttx"))
Ejemplo n.º 33
0
 def test_spec5fi1(self):
     # OpenType Feature File specification, section 5.f.i, example 1.
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("spec5fi1.fea"), font)
     self.expect_ttx(font, self.getpath("spec5fi1.ttx"))
Ejemplo n.º 34
0
 def test_build_pre_parsed_ast_featurefile(self):
     f = StringIO("feature liga {sub f i by f_i;} liga;")
     tree = Parser(f).parse()
     font = makeTTFont()
     addOpenTypeFeatures(font, tree)
     assert "GSUB" in font
Ejemplo n.º 35
0
 def test_spec4h1(self):
     # OpenType Feature File specification, section 4.h, example 1.
     font = TTFont()
     addOpenTypeFeatures(self.getpath("spec4h1.fea"), font)
     self.expect_ttx(font, self.getpath("spec4h1.ttx"))
Ejemplo n.º 36
0
 def test_spec5d2(self):
     # OpenType Feature File specification, section 5.d, example 2.
     font = TTFont()
     addOpenTypeFeatures(self.getpath("spec5d2.fea"), font)
     self.expect_ttx(font, self.getpath("spec5d2.ttx"))
Ejemplo n.º 37
0
# -*- coding: utf-8 -*-

from fontTools import ttLib
from fontTools.feaLib.builder import addOpenTypeFeatures
import argparse
import sys

parser = argparse.ArgumentParser()
parser.add_argument('in_font')
parser.add_argument('out_font')
options = parser.parse_args(sys.argv[1:])

# Apply generated feature file to the unlinked font file
ricty = ttLib.TTFont(options.in_font)
addOpenTypeFeatures(ricty, 'ligatures.fea')
ricty.save(options.out_font)
Ejemplo n.º 38
0
 def test_build_pre_parsed_ast_featurefile(self):
     f = UnicodeIO("feature liga {sub f i by f_i;} liga;")
     tree = Parser(f).parse()
     font = makeTTFont()
     addOpenTypeFeatures(font, tree)
     assert "GSUB" in font
Ejemplo n.º 39
0
 def test_multipleSubst(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("GSUB_2.fea"), font)
     self.expect_ttx(font, self.getpath("GSUB_2.ttx"))
Ejemplo n.º 40
0
def patch_one_font(font, rename_font, add_underlines, shift_amount, squish,
                   squish_all, add_commas, spaceless_commas, debug_annotate,
                   do_decimals, group, sub_font):
    font.encoding = 'ISO10646'

    if group:
        add_underlines = False
        shift_amount = 100
        squish = 0.85
        squish_all = True

    mod_name = 'N'
    if add_commas:
        if spaceless_commas:
            mod_name += 'onoCommas'
        else:
            mod_name += 'ommas'
    if add_underlines:
        mod_name += 'umderline'
    if sub_font is not None:
        mod_name += 'Sub'
    # Cleaner name for what I expect to be a common combination
    if shift_amount == 100 and squish == 0.85 and squish_all:
        mod_name += 'Group'
    else:
        if shift_amount != 0:
            mod_name += 'Shift{}'.format(shift_amount)
        if squish != 1.0:
            squish_s = '{}'.format(squish)
            mod_name += 'Squish{}'.format(squish_s.replace('.', 'p'))
            if squish_all:
                mod_name += 'All'
    if debug_annotate:
        mod_name += 'Debug'
    if not do_decimals:
        mod_name += 'NoDecimals'

    # Rename font
    if rename_font:
        font.familyname += ' with ' + mod_name
        font.fullname += ' with ' + mod_name
        fontname, style = FONT_NAME_RE.match(font.fontname).groups()
        font.fontname = fontname + 'With' + mod_name
        if style is not None:
            font.fontname += style
        font.appendSFNTName('English (US)', 'Preferred Family',
                            font.familyname)
        font.appendSFNTName('English (US)', 'Compatible Full', font.fullname)

    digit_names = [
        font[code].glyphname for code in range(ord('0'),
                                               ord('9') + 1)
    ]
    test_names = [
        font[code].glyphname for code in range(ord('A'),
                                               ord('J') + 1)
    ]
    underscore_name = font[ord('_')].glyphname
    dot_name = font[ord('.')].glyphname
    # print(digit_names)

    if sub_font is not None:
        sub_font = fontforge.open(sub_font.name)

    underscore_layer = font[underscore_name].layers[1]

    # 0xE900 starts an area spanning until 0xF000 that as far as I can tell nothing
    # popular uses. I checked the Apple glyph browser and Nerd Font.
    # Uses an array because of python closure capture semantics
    encoding_alloc = [0xE900]

    def make_copy(src_font, loc, to_name, add_underscore, add_comma, shift,
                  squish, annotate_with):
        encoding = encoding_alloc[0]
        src_font.selection.select(loc)
        src_font.copy()
        font.selection.select(encoding)
        font.paste()
        glyph = font[encoding]
        glyph.glyphname = to_name
        if squish != 1.0:
            glyph.layers[1] = squish_layer(glyph.layers[1], squish)
        if shift != 0:
            glyph.layers[1] = shift_layer(glyph.layers[1], shift)
        if add_underscore:
            glyph.layers[1] += underscore_layer
        if add_comma:
            add_comma_to(glyph, font[ord(',')], spaceless_commas)
        if annotate_with is not None:
            annotate_glyph(glyph, annotate_with)
        encoding_alloc[0] += 1

    for copy_i in range(0, NUM_DIGIT_COPIES):
        for digit_i in range(0, 10):
            shift = 0
            if copy_i % 3 == 0:
                shift = -shift_amount
            elif copy_i % 3 == 2:
                shift = shift_amount
            in_alternating_group = (copy_i >= 3 and copy_i < 6)
            add_underscore = add_underlines and in_alternating_group
            add_comma = add_commas and (copy_i == 3 or copy_i == 6)
            annotate_with = font[
                digit_names[copy_i]] if debug_annotate else None
            use_sub_font = (sub_font is not None) and in_alternating_group
            src_font = sub_font if use_sub_font else font
            make_copy(src_font, digit_names[digit_i],
                      'nd{}.{}'.format(copy_i, digit_i), add_underscore,
                      add_comma, shift, squish, annotate_with)

    if squish_all and squish != 1.0:
        for digit in digit_names:
            glyph = font[digit]
            glyph.layers[1] = squish_layer(glyph.layers[1], squish)

    gen_feature(digit_names, underscore_name, dot_name, do_decimals)

    font.generate('out/tmp.ttf')
    ft_font = TTFont('out/tmp.ttf')
    addOpenTypeFeatures(ft_font, 'mods.fea', tables=['GSUB'])
    # replacement to comply with SIL Open Font License
    out_name = font.fullname.replace('Source ', 'Sauce ')
    ft_font.save(out_path(out_name))
    print("> Created '{}'".format(out_name))

    if sub_font is not None:
        sub_font.close()

    return out_name
Ejemplo n.º 41
0
 def test_reverseChainingSingleSubst(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("GSUB_8.fea"), font)
     self.expect_ttx(font, self.getpath("GSUB_8.ttx"))
Ejemplo n.º 42
0
from fontTools.ttLib import TTFont
from fontTools import configLogger
import sys
import argparse

parser = argparse.ArgumentParser(
    description="Use fontTools to compile OpenType features.")
parser.add_argument("input_fea",
                    metavar="FEATURES",
                    help="Path to the feature file")
parser.add_argument("input_font",
                    metavar="INPUT",
                    help="Path to the input font")
parser.add_argument("output_font",
                    metavar="OUTPUT",
                    help="Path to the output font")
parser.add_argument("-v",
                    "--verbose",
                    help="increase the logger verbosity. "
                    "Multiple -v options are allowed.",
                    action="count",
                    default=0)
options = parser.parse_args(sys.argv[1:])

levels = ["WARNING", "INFO", "DEBUG"]
configLogger(level=levels[min(len(levels) - 1, options.verbose)])

font = TTFont(options.input_font)
feaLibBuilder.addOpenTypeFeatures(font, options.input_fea)
font.save(options.output_font)
Ejemplo n.º 43
0
 def test_markClass(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("markClass.fea"), font)
     self.expect_ttx(font, self.getpath("markClass.ttx"))
Ejemplo n.º 44
0
 def test_GPOS(self):
     for name in "1 2 2b 3 4 5 6 8".split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("GPOS_%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("GPOS_%s.ttx" % name))
Ejemplo n.º 45
0
 def test_reverseChainingSingleSubst(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("GSUB_8.fea"), font)
     self.expect_ttx(font, self.getpath("GSUB_8.ttx"))
Ejemplo n.º 46
0
 def test_spec(self):
     for name in ("4h1 5d1 5d2 5fi1 5fi2 5fi3 5fi4 5h1 "
                  "6d2 6e 6f 6h_ii").split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("spec%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("spec%s.ttx" % name))
Ejemplo n.º 47
0
 def test_multipleSubst(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("GSUB_2.fea"), font)
     self.expect_ttx(font, self.getpath("GSUB_2.ttx"))
Ejemplo n.º 48
0
 def test_GPOS(self):
     for name in "1 2 3".split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("GPOS_%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("GPOS_%s.ttx" % name))
Ejemplo n.º 49
0
 def test_GPOS_type1(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("GPOS_1.fea"), font)
     self.expect_ttx(font, self.getpath("GPOS_1.ttx"))
Ejemplo n.º 50
0
def make_font(file_paths, out_dir, revision, gsub_path, gpos_path, uvs_lst):
    cmap, gorder, validated_fpaths = {}, deque(), []
    # build glyph order
    for fpath in file_paths:
        # derive glyph name from file name
        gname = os.path.splitext(os.path.basename(fpath))[0]  # trim extension
        # validate glyph name
        if not glyph_name_is_valid(gname, fpath):
            continue
        # skip any duplicates and 'space'
        if gname in gorder or gname == 'space':
            log.warning("Skipped file '{}'. The glyph name derived from it "
                        "is either a duplicate or 'space'.".format(fpath))
            continue
        # limit the length of glyph name to 31 chars
        if len(gname) > 31:
            num = 0
            trimmed_gname = get_trimmed_glyph_name(gname, num)
            while trimmed_gname in gorder:
                num += 1
                trimmed_gname = get_trimmed_glyph_name(trimmed_gname, num)
            gorder.append(trimmed_gname)
            log.warning("Glyph name '{}' was trimmed to 31 characters: "
                        "'{}'".format(gname, trimmed_gname))
        else:
            gorder.append(gname)
        validated_fpaths.append(fpath)

        # add to cmap
        if RE_UNICODE.match(gname):
            uni_int = int(gname[1:], 16)  # trim leading 'u'
            cmap[uni_int] = gname

    fb = FontBuilder(UPM, isTTF=False)
    fb.font['head'].fontRevision = float(revision)
    fb.font['head'].lowestRecPPEM = 12

    cs_dict = {}
    cs_cache = {}
    for i, svg_file_path in enumerate(validated_fpaths):
        svg_file_realpath = os.path.realpath(svg_file_path)

        if svg_file_realpath not in cs_cache:
            svg_size = get_svg_size(svg_file_realpath)
            if svg_size is None:
                cs_dict[gorder[i]] = SPACE_CHARSTRING
                continue

            pen = T2CharStringPen(EMOJI_H_ADV, None)
            svg = SVGPath(svg_file_realpath,
                          transform=(EMOJI_SIZE / svg_size, 0, 0,
                                     -EMOJI_SIZE / svg_size,
                                     (EMOJI_H_ADV * .5) - (EMOJI_SIZE * .5),
                                     EMOJI_H_ADV * ABOVE_BASELINE))
            svg.draw(pen)
            cs = pen.getCharString()
            cs_cache[svg_file_realpath] = cs
        else:
            cs = cs_cache.get(svg_file_realpath)

        cs_dict[gorder[i]] = cs

    # add '.notdef', 'space' and zero-width joiner
    pen = T2CharStringPen(EMOJI_H_ADV, None)
    draw_notdef(pen)
    gorder.extendleft(reversed(['.notdef', 'space', 'ZWJ']))
    cs_dict.update({
        '.notdef': pen.getCharString(),
        'space': SPACE_CHARSTRING,
        'ZWJ': SPACE_CHARSTRING,
    })
    cmap.update({
        32: 'space',  # U+0020
        160: 'space',  # U+00A0
        8205: 'ZWJ',  # U+200D
    })

    # add TAG LATIN LETTER glyphs and mappings
    for cdpt in TAG_LAT_LETTR:
        tag_gname = f'u{cdpt}'
        gorder.append(tag_gname)
        cs_dict[tag_gname] = SPACE_CHARSTRING
        cmap[int(cdpt, 16)] = tag_gname

    fb.setupGlyphOrder(list(gorder))  # parts of FontTools require a list
    fb.setupCharacterMap(cmap, uvs=uvs_lst)
    fb.setupCFF(
        PS_NAME, {
            'version': revision,
            'Notice': TRADEMARK,
            'Copyright': COPYRIGHT,
            'FullName': FULL_NAME,
            'FamilyName': FAMILY_NAME,
            'Weight': STYLE_NAME
        }, cs_dict, {})

    glyphs_bearings = {}
    for gname, cs in cs_dict.items():
        gbbox = cs.calcBounds(None)
        if gbbox:
            xmin, ymin, _, ymax = gbbox
            if ymax > ASCENT:
                log.warning("Top of glyph '{}' may get clipped. "
                            "Glyph's ymax={}; Font's ascent={}".format(
                                gname, ymax, ASCENT))
            if ymin < DESCENT:
                log.warning("Bottom of glyph '{}' may get clipped. "
                            "Glyph's ymin={}; Font's descent={}".format(
                                gname, ymin, DESCENT))
            lsb = xmin
            tsb = EMOJI_V_ADV - ymax - EMOJI_H_ADV * (1 - ABOVE_BASELINE)
            glyphs_bearings[gname] = (lsb, tsb)
        else:
            glyphs_bearings[gname] = (0, 0)

    h_metrics = {}
    v_metrics = {}
    for gname in gorder:
        h_metrics[gname] = (EMOJI_H_ADV, glyphs_bearings[gname][0])
        v_metrics[gname] = (EMOJI_V_ADV, glyphs_bearings[gname][1])
    fb.setupHorizontalMetrics(h_metrics)
    fb.setupVerticalMetrics(v_metrics)

    fb.setupHorizontalHeader(ascent=ASCENT, descent=DESCENT)

    v_ascent = EMOJI_H_ADV // 2
    v_descent = EMOJI_H_ADV - v_ascent
    fb.setupVerticalHeader(ascent=v_ascent,
                           descent=-v_descent,
                           caretSlopeRun=1)

    VERSION_STRING = 'Version {};{}'.format(revision, VENDOR)
    UNIQUE_ID = '{};{};{}'.format(revision, VENDOR, PS_NAME)
    name_strings = dict(
        copyright=COPYRIGHT,  # ID 0
        familyName=FAMILY_NAME,  # ID 1
        styleName=STYLE_NAME,  # ID 2
        uniqueFontIdentifier=UNIQUE_ID,  # ID 3
        fullName=FULL_NAME,  # ID 4
        version=VERSION_STRING,  # ID 5
        psName=PS_NAME,  # ID 6
        trademark=TRADEMARK,  # ID 7
        manufacturer=MANUFACTURER,  # ID 8
        designer=DESIGNER,  # ID 9
        vendorURL=VENDOR_URL,  # ID 11
        designerURL=DESIGNER_URL,  # ID 12
        licenseDescription=LICENSE,  # ID 13
        licenseInfoURL=LICENSE_URL,  # ID 14
    )
    fb.setupNameTable(name_strings, mac=False)

    fb.setupOS2(
        fsType=FSTYPE,
        achVendID=VENDOR,
        fsSelection=0x0040,  # REGULAR
        usWinAscent=ASCENT,
        usWinDescent=-DESCENT,
        sTypoAscender=ASCENT,
        sTypoDescender=DESCENT,
        sCapHeight=ASCENT,
        ulCodePageRange1=(1 << 1))  # set 1st CP bit

    if gsub_path:
        addOpenTypeFeatures(fb.font, gsub_path, tables=['GSUB'])

    if gpos_path:
        addOpenTypeFeatures(fb.font, gpos_path, tables=['GPOS'])

    fb.setupPost(isFixedPitch=1,
                 underlinePosition=UNDERLINE_POSITION,
                 underlineThickness=UNDERLINE_THICKNESS)

    fb.setupDummyDSIG()

    fb.save(os.path.join(out_dir, '{}.otf'.format(PS_NAME)))
Ejemplo n.º 51
0
 def test_spec(self):
     for name in "4h1 5d1 5d2 5fi1 5h1".split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("spec%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("spec%s.ttx" % name))
Ejemplo n.º 52
0
 def test_spec(self):
     for name in ("4h1 5d1 5d2 5fi1 5fi2 5fi3 5fi4 5h1 "
                  "6d2 6e 6f 6h_ii").split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("spec%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("spec%s.ttx" % name))
Ejemplo n.º 53
0
 def test_markClass(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("markClass.fea"), font)
     self.expect_ttx(font, self.getpath("markClass.ttx"))
Ejemplo n.º 54
0
 def test_language_required(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("language_required.fea"), font)
     self.expect_ttx(font, self.getpath("language_required.ttx"))
Ejemplo n.º 55
0
 def test_lookup(self):
     font = makeTTFont()
     addOpenTypeFeatures(self.getpath("lookup.fea"), font)
     self.expect_ttx(font, self.getpath("lookup.ttx"))
Ejemplo n.º 56
0
def main(args=None):
    """Add features from a feature file (.fea) into a OTF font"""
    parser = argparse.ArgumentParser(
        description="Use fontTools to compile OpenType feature files (*.fea).")
    parser.add_argument("input_fea",
                        metavar="FEATURES",
                        help="Path to the feature file")
    parser.add_argument("input_font",
                        metavar="INPUT_FONT",
                        help="Path to the input font")
    parser.add_argument(
        "-o",
        "--output",
        dest="output_font",
        metavar="OUTPUT_FONT",
        help="Path to the output font.",
    )
    parser.add_argument(
        "-t",
        "--tables",
        metavar="TABLE_TAG",
        choices=Builder.supportedTables,
        nargs="+",
        help="Specify the table(s) to be built.",
    )
    parser.add_argument(
        "-d",
        "--debug",
        action="store_true",
        help="Add source-level debugging information to font.",
    )
    parser.add_argument(
        "-v",
        "--verbose",
        help="increase the logger verbosity. Multiple -v "
        "options are allowed.",
        action="count",
        default=0,
    )
    parser.add_argument("--traceback",
                        help="show traceback for exceptions.",
                        action="store_true")
    options = parser.parse_args(args)

    levels = ["WARNING", "INFO", "DEBUG"]
    configLogger(level=levels[min(len(levels) - 1, options.verbose)])

    output_font = options.output_font or makeOutputFileName(options.input_font)
    log.info("Compiling features to '%s'" % (output_font))

    font = TTFont(options.input_font)
    try:
        addOpenTypeFeatures(font,
                            options.input_fea,
                            tables=options.tables,
                            debug=options.debug)
    except FeatureLibError as e:
        if options.traceback:
            raise
        log.error(e)
    font.save(output_font)
Ejemplo n.º 57
0
 def test_constructs(self):
     for name in ("enum markClass language_required "
                  "lookup lookupflag").split():
         font = makeTTFont()
         addOpenTypeFeatures(self.getpath("%s.fea" % name), font)
         self.expect_ttx(font, self.getpath("%s.ttx" % name))