Пример #1
0
def test_main(tmpdir: Path):
    """Check that calling the main function on an input TTF works."""
    glyphs = ".notdef space A Aacute B D".split()
    features = """
    @A = [A Aacute];
    @B = [B D];
    feature kern {
        pos @A @B -50;
    } kern;
    """
    fb = FontBuilder(1000)
    fb.setupGlyphOrder(glyphs)
    addOpenTypeFeaturesFromString(fb.font, features)
    input = tmpdir / "in.ttf"
    fb.save(str(input))
    output = tmpdir / "out.ttf"
    run(
        [
            "fonttools",
            "otlLib.optimize",
            "--gpos-compact-mode",
            "5",
            str(input),
            "-o",
            str(output),
        ],
        check=True,
    )
    assert output.exists()
Пример #2
0
def test_optimization_mode(
    caplog,
    blocks: List[Tuple[int, int]],
    mode: Optional[int],
    expected_subtables: int,
    expected_bytes: int,
):
    """Check that the optimizations are off by default, and that increasing
    the optimization level creates more subtables and a smaller byte size.
    """
    caplog.set_level(logging.DEBUG)

    glyphs, features = get_kerning_by_blocks(blocks)
    glyphs = [".notdef space"] + glyphs

    env = {}
    if mode is not None:
        # NOTE: activating this optimization via the environment variable is
        # experimental and may not be supported once an alternative mechanism
        # is in place. See: https://github.com/fonttools/fonttools/issues/2349
        env["FONTTOOLS_GPOS_COMPACT_MODE"] = str(mode)
    with set_env(**env):
        fb = FontBuilder(1000)
        fb.setupGlyphOrder(glyphs)
        addOpenTypeFeaturesFromString(fb.font, features)
        assert expected_subtables == count_pairpos_subtables(fb.font)
        assert expected_bytes == count_pairpos_bytes(fb.font)
Пример #3
0
    def buildTables(self):
        """
        Compile 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 not self.features:
            return

        # the path is used by the lexer to follow 'include' statements;
        # if we generated some automatic features, includes have already been
        # resolved, and we work from a string which does't exist on disk
        path = self.ufo.path if not self.featureWriters else None
        try:
            addOpenTypeFeaturesFromString(self.ttFont,
                                          self.features,
                                          filename=path)
        except FeatureLibError:
            if path is None:
                # if compilation fails, create temporary file for inspection
                data = self.features.encode("utf-8")
                with NamedTemporaryFile(delete=False) as tmp:
                    tmp.write(data)
                logger.error("Compilation failed! Inspect temporary file: %r",
                             tmp.name)
            raise
 def compile_font(self, path, suffix, temp_dir, features=None):
     ttx_filename = os.path.basename(path)
     savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix))
     font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
     font.importXML(path)
     if features:
         addOpenTypeFeaturesFromString(font, features)
     font.save(savepath, reorderTables=None)
     return font, savepath
Пример #5
0
 def compile_font(self, path, suffix, temp_dir, features=None):
     ttx_filename = os.path.basename(path)
     savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix))
     font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
     font.importXML(path)
     if features:
         addOpenTypeFeaturesFromString(font, features)
     font.save(savepath, reorderTables=None)
     return font, savepath
Пример #6
0
def generateFont(options, font, feastring):
    if os.environ.get("SOURCE_DATE_EPOCH") is None:
        os.environ["SOURCE_DATE_EPOCH"] = "0"

    fea = generateFeatures(font, args.features)
    fea += feastring

    flags = []
    if args.output.endswith(".ttf"):
        flags += ["opentype", "dummy-dsig", "omit-instructions"]

    font.selection.all()
    font.correctReferences()
    font.selection.none()

    # fix some common font issues
    validateGlyphs(font)

    updateInfo(font, args.version)

    font.generate(args.output, flags=flags)

    try:
        ttfont = TTFont(args.output)
        addOpenTypeFeaturesFromString(ttfont, fea)

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

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

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

        ttfont.save(args.output)
    except:
        with NamedTemporaryFile(delete=False) as tmp:
            tmp.write(fea.encode("utf-8"))
            print("Failed! Inspect temporary file: %r" % tmp.name)
            os.remove(args.output)
        raise
Пример #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.mtiFeatures is not None:
            for tag, features in self.mtiFeatures.items():
                table = mtiLib.build(features.splitlines(), self.outline)
                assert table.tableTag == tag
                self.outline[tag] = table

        elif self.features.strip():
            # the path to features.fea is only used by the lexer to resolve
            # the relative "include" statements
            if self.font.path is not None:
                feapath = os.path.join(self.font.path, "features.fea")
            else:
                # in-memory UFO has no path, can't do 'include' either
                feapath = None

            # save generated features to a temp file if things go wrong...
            data = tobytes(self.features, encoding="utf-8")
            with NamedTemporaryFile(delete=False) as tmp:
                tmp.write(data)

            # if compilation succedes or fails for unrelated reasons, clean
            # up the temporary file
            try:
                addOpenTypeFeaturesFromString(self.outline,
                                              self.features,
                                              filename=feapath)
            except feaLib.error.FeatureLibError:
                logger.error("Compilation failed! Inspect temporary file: %r",
                             tmp.name)
                raise
            except:
                os.remove(tmp.name)
                raise
            else:
                os.remove(tmp.name)
Пример #8
0
def _layoutEngineOTLTablesRepresentationFactory(layoutEngine):
    font = layoutEngine.font
    gdef = gsub = gpos = None
    if font.features.text:
        otf = TTFont()
        otf.setGlyphOrder(sorted(font.keys()))
        # compile with fontTools
        try:
            addOpenTypeFeaturesFromString(otf, font.features.text)
        except:
            import traceback
            print(traceback.format_exc(5))
        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
Пример #9
0
def test_max_ctx_calc_features():
    glyphs = '.notdef space A B C a b c'.split()
    features = """
    lookup GSUB_EXT useExtension {
        sub a by b;
    } GSUB_EXT;

    lookup GPOS_EXT useExtension {
        pos a b -10;
    } GPOS_EXT;

    feature sub1 {
        sub A by a;
        sub A B by b;
        sub A B C by c;
        sub [A B] C by c;
        sub [A B] C [A B] by c;
        sub A by A B;
        sub A' C by A B;
        sub a' by b;
        sub a' b by c;
        sub a from [A B C];
        rsub a by b;
        rsub a' by b;
        rsub a b' by c;
        rsub a b' c by A;
        rsub [a b] c' by A;
        rsub [a b] c' [a b] by B;
        lookup GSUB_EXT;
    } sub1;

    feature pos1 {
        pos A 20;
        pos A B -50;
        pos A B' 10 C;
        lookup GPOS_EXT;
    } pos1;
    """
    font = TTFont()
    font.setGlyphOrder(glyphs)
    addOpenTypeFeaturesFromString(font, features)

    assert maxCtxFont(font) == 3
Пример #10
0
def test_max_ctx_calc_features():
    glyphs = '.notdef space A B C a b c'.split()
    features = """
    lookup GSUB_EXT useExtension {
        sub a by b;
    } GSUB_EXT;

    lookup GPOS_EXT useExtension {
        pos a b -10;
    } GPOS_EXT;

    feature sub1 {
        sub A by a;
        sub A B by b;
        sub A B C by c;
        sub [A B] C by c;
        sub [A B] C [A B] by c;
        sub A by A B;
        sub A' C by A B;
        sub a' by b;
        sub a' b by c;
        sub a from [A B C];
        rsub a by b;
        rsub a' by b;
        rsub a b' by c;
        rsub a b' c by A;
        rsub [a b] c' by A;
        rsub [a b] c' [a b] by B;
        lookup GSUB_EXT;
    } sub1;

    feature pos1 {
        pos A 20;
        pos A B -50;
        pos A B' 10 C;
        lookup GPOS_EXT;
    } pos1;
    """
    font = TTFont()
    font.setGlyphOrder(glyphs)
    addOpenTypeFeaturesFromString(font, features)

    assert maxCtxFont(font) == 3
Пример #11
0
def _layoutEngineOTLTablesRepresentationFactory(layoutEngine):
    font = layoutEngine.font
    gdef = gsub = gpos = None
    if font.features.text:
        otf = TTFont()
        otf.setGlyphOrder(sorted(font.keys()))
        # compile with fontTools
        try:
            addOpenTypeFeaturesFromString(otf, font.features.text)
        except:
            import traceback
            print(traceback.format_exc(5))
        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
Пример #12
0
        def add_rclt(font, savepath):
            features = """
            languagesystem DFLT dflt;
            languagesystem latn dflt;
            languagesystem latn NLD;

            feature rclt {
                script latn;
                language NLD;
                lookup A {
                    sub uni0041 by uni0061;
                } A;
                language dflt;
                lookup B {
                    sub uni0041 by uni0061;
                } B;
            } rclt;
            """
            addOpenTypeFeaturesFromString(font, features)
            font.save(savepath)
Пример #13
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():
            feapath = os.path.join(self.font.path, "features.fea") if self.font.path is not None else None
            addOpenTypeFeaturesFromString(self.outline, self.features,
                                          filename=feapath)
Пример #14
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():
            feapath = os.path.join(self.font.path, "features.fea")
            addOpenTypeFeaturesFromString(self.outline, self.features,
                                          filename=feapath)
Пример #15
0
def test_optimization_mode(
    caplog,
    blocks: List[Tuple[int, int]],
    level: Optional[int],
    expected_subtables: int,
    expected_bytes: int,
):
    """Check that the optimizations are off by default, and that increasing
    the optimization level creates more subtables and a smaller byte size.
    """
    caplog.set_level(logging.DEBUG)

    glyphs, features = get_kerning_by_blocks(blocks)
    glyphs = [".notdef space"] + glyphs

    fb = FontBuilder(1000)
    if level is not None:
        fb.font.cfg["fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL"] = level
    fb.setupGlyphOrder(glyphs)
    addOpenTypeFeaturesFromString(fb.font, features)
    assert expected_subtables == count_pairpos_subtables(fb.font)
    assert expected_bytes == count_pairpos_bytes(fb.font)
Пример #16
0
 def build(self, featureFile, tables=None):
     font = makeTTFont()
     addOpenTypeFeaturesFromString(font, featureFile, tables=tables)
     return font
Пример #17
0
from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
from fontTools.ttLib import TTFont
import sys


font = TTFont(sys.argv[1])
destination = sys.argv[2]
print(f'Writing on {destination}')

while True:
  length = int(sys.stdin.buffer.readline().decode("utf-8"))
  print(f'Reading {length} bytes')
  feature = sys.stdin.buffer.read(length).decode("utf-8")
  print(f'Read |{feature}|')
  try:
    addOpenTypeFeaturesFromString(font, feature)
    font.save(destination)
    print("OK")
  except Exception as e:
    print(f'{e}')
Пример #18
0
def make_font(feature_source, fea_type='fea'):
    """Return font with GSUB compiled from given source.

    Adds a bunch of filler tables so the font can be saved if needed, for
    debugging purposes.
    """

    # copied from fontTools' feaLib/builder_test.
    glyphs = """
        .notdef space slash fraction semicolon period comma ampersand
        quotedblleft quotedblright quoteleft quoteright
        zero one two three four five six seven eight nine
        zero.oldstyle one.oldstyle two.oldstyle three.oldstyle
        four.oldstyle five.oldstyle six.oldstyle seven.oldstyle
        eight.oldstyle nine.oldstyle onequarter onehalf threequarters
        onesuperior twosuperior threesuperior ordfeminine ordmasculine
        A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
        a b c d e f g h i j k l m n o p q r s t u v w x y z
        A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc
        N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc
        A.alt1 A.alt2 A.alt3 B.alt1 B.alt2 B.alt3 C.alt1 C.alt2 C.alt3
        a.alt1 a.alt2 a.alt3 a.end b.alt c.mid d.alt d.mid
        e.begin e.mid e.end m.begin n.end s.end z.end
        Eng Eng.alt1 Eng.alt2 Eng.alt3
        A.swash B.swash C.swash D.swash E.swash F.swash G.swash H.swash
        I.swash J.swash K.swash L.swash M.swash N.swash O.swash P.swash
        Q.swash R.swash S.swash T.swash U.swash V.swash W.swash X.swash
        Y.swash Z.swash
        f_l c_h c_k c_s c_t f_f f_f_i f_f_l f_i o_f_f_i s_t f_i.begin
        a_n_d T_h T_h.swash germandbls ydieresis yacute breve
        grave acute dieresis macron circumflex cedilla umlaut ogonek caron
        damma hamza sukun kasratan lam_meem_jeem noon.final noon.initial
        by feature lookup sub table
    """.split()
    font = TTFont()
    font.setGlyphOrder(glyphs)
    glyph_order = font.getGlyphOrder()

    font['cmap'] = cmap = newTable('cmap')
    table = cmap_format_4(4)
    table.platformID = 3
    table.platEncID = 1
    table.language = 0
    table.cmap = {AGL2UV[n]: n for n in glyph_order if n in AGL2UV}
    cmap.tableVersion = 0
    cmap.tables = [table]

    font['glyf'] = glyf = newTable('glyf')
    glyf.glyphs = {}
    glyf.glyphOrder = glyph_order
    for name in glyph_order:
        pen = TTGlyphPen(None)
        glyf[name] = pen.glyph()

    font['head'] = head = newTable('head')
    head.tableVersion = 1.0
    head.fontRevision = 1.0
    head.flags = head.checkSumAdjustment = head.magicNumber =\
        head.created = head.modified = head.macStyle = head.lowestRecPPEM =\
        head.fontDirectionHint = head.indexToLocFormat =\
        head.glyphDataFormat =\
        head.xMin = head.xMax = head.yMin = head.yMax = 0
    head.unitsPerEm = 1000

    font['hhea'] = hhea = newTable('hhea')
    hhea.tableVersion = 0x00010000
    hhea.ascent = hhea.descent = hhea.lineGap =\
        hhea.caretSlopeRise = hhea.caretSlopeRun = hhea.caretOffset =\
        hhea.reserved0 = hhea.reserved1 = hhea.reserved2 = hhea.reserved3 =\
        hhea.metricDataFormat = hhea.advanceWidthMax = hhea.xMaxExtent =\
        hhea.minLeftSideBearing = hhea.minRightSideBearing =\
        hhea.numberOfHMetrics = 0

    font['hmtx'] = hmtx = newTable('hmtx')
    hmtx.metrics = {}
    for name in glyph_order:
        hmtx[name] = (600, 50)

    font['loca'] = newTable('loca')

    font['maxp'] = maxp = newTable('maxp')
    maxp.tableVersion = 0x00005000
    maxp.numGlyphs = 0

    font['post'] = post = newTable('post')
    post.formatType = 2.0
    post.extraNames = []
    post.mapping = {}
    post.glyphOrder = glyph_order
    post.italicAngle = post.underlinePosition = post.underlineThickness =\
        post.isFixedPitch = post.minMemType42 = post.maxMemType42 =\
        post.minMemType1 = post.maxMemType1 = 0

    if fea_type == 'fea':
        addOpenTypeFeaturesFromString(font, feature_source)
    elif fea_type == 'mti':
        font['GSUB'] = mtiLib.build(UnicodeIO(feature_source), font)

    return font
Пример #19
0
def make_font(feature_source, fea_type="fea"):
    """Return font with GSUB compiled from given source.

    Adds a bunch of filler tables so the font can be saved if needed, for
    debugging purposes.
    """

    # copied from fontTools' feaLib/builder_test.
    glyphs = """
        .notdef space slash fraction semicolon period comma ampersand
        quotedblleft quotedblright quoteleft quoteright
        zero one two three four five six seven eight nine
        zero.oldstyle one.oldstyle two.oldstyle three.oldstyle
        four.oldstyle five.oldstyle six.oldstyle seven.oldstyle
        eight.oldstyle nine.oldstyle onequarter onehalf threequarters
        onesuperior twosuperior threesuperior ordfeminine ordmasculine
        A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
        a b c d e f g h i j k l m n o p q r s t u v w x y z
        A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc
        N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc
        A.alt1 A.alt2 A.alt3 B.alt1 B.alt2 B.alt3 C.alt1 C.alt2 C.alt3
        a.alt1 a.alt2 a.alt3 a.end b.alt c.mid d.alt d.mid
        e.begin e.mid e.end m.begin n.end s.end z.end
        Eng Eng.alt1 Eng.alt2 Eng.alt3
        A.swash B.swash C.swash D.swash E.swash F.swash G.swash H.swash
        I.swash J.swash K.swash L.swash M.swash N.swash O.swash P.swash
        Q.swash R.swash S.swash T.swash U.swash V.swash W.swash X.swash
        Y.swash Z.swash
        f_l c_h c_k c_s c_t f_f f_f_i f_f_l f_i o_f_f_i s_t f_i.begin
        a_n_d T_h T_h.swash germandbls ydieresis yacute breve
        grave acute dieresis macron circumflex cedilla umlaut ogonek caron
        damma hamza sukun kasratan lam_meem_jeem noon.final noon.initial
        by feature lookup sub table
    """.split()
    font = TTFont()
    font.setGlyphOrder(glyphs)
    glyph_order = font.getGlyphOrder()

    font["cmap"] = cmap = newTable("cmap")
    table = cmap_format_4(4)
    table.platformID = 3
    table.platEncID = 1
    table.language = 0
    table.cmap = {AGL2UV[n]: n for n in glyph_order if n in AGL2UV}
    cmap.tableVersion = 0
    cmap.tables = [table]

    font["glyf"] = glyf = newTable("glyf")
    glyf.glyphs = {}
    glyf.glyphOrder = glyph_order
    for name in glyph_order:
        pen = TTGlyphPen(None)
        glyf[name] = pen.glyph()

    font["head"] = head = newTable("head")
    head.tableVersion = 1.0
    head.fontRevision = 1.0
    head.flags = (
        head.checkSumAdjustment
    ) = (
        head.magicNumber
    ) = (
        head.created
    ) = (
        head.modified
    ) = (
        head.macStyle
    ) = (
        head.lowestRecPPEM
    ) = (
        head.fontDirectionHint
    ) = (
        head.indexToLocFormat
    ) = head.glyphDataFormat = head.xMin = head.xMax = head.yMin = head.yMax = 0
    head.unitsPerEm = 1000

    font["hhea"] = hhea = newTable("hhea")
    hhea.tableVersion = 0x00010000
    hhea.ascent = (
        hhea.descent
    ) = (
        hhea.lineGap
    ) = (
        hhea.caretSlopeRise
    ) = (
        hhea.caretSlopeRun
    ) = (
        hhea.caretOffset
    ) = (
        hhea.reserved0
    ) = (
        hhea.reserved1
    ) = (
        hhea.reserved2
    ) = (
        hhea.reserved3
    ) = (
        hhea.metricDataFormat
    ) = (
        hhea.advanceWidthMax
    ) = (
        hhea.xMaxExtent
    ) = hhea.minLeftSideBearing = hhea.minRightSideBearing = hhea.numberOfHMetrics = 0

    font["hmtx"] = hmtx = newTable("hmtx")
    hmtx.metrics = {}
    for name in glyph_order:
        hmtx[name] = (600, 50)

    font["loca"] = newTable("loca")

    font["maxp"] = maxp = newTable("maxp")
    maxp.tableVersion = 0x00005000
    maxp.numGlyphs = 0

    font["post"] = post = newTable("post")
    post.formatType = 2.0
    post.extraNames = []
    post.mapping = {}
    post.glyphOrder = glyph_order
    post.italicAngle = (
        post.underlinePosition
    ) = (
        post.underlineThickness
    ) = (
        post.isFixedPitch
    ) = post.minMemType42 = post.maxMemType42 = post.minMemType1 = post.maxMemType1 = 0

    if fea_type == "fea":
        addOpenTypeFeaturesFromString(font, feature_source)
    elif fea_type == "mti":
        font["GSUB"] = mtiLib.build(UnicodeIO(feature_source), font)

    return font
Пример #20
0
         print('  Renaming features glyphs')
         with open(Path(INDEX_DIR / f'{config["inputFile"]}.json'),
                   'r') as file:
             glyph_id_map = json.load(file)
         glyph_id_map_keys = sorted(glyph_id_map.keys(), reverse=True)
         for glyph_name in glyph_id_map_keys:
             regex = rf'(^|\s|\{{|\[|\\)({glyph_name})(\s|\}}|\]|\'|\;|$)'
             if re.search(regex, final_string) is not None:
                 glyph_id = glyph_id_map[glyph_name]
                 new_glyph_name = input_font.getGlyphName(glyph_id)
                 # Run twice for instances that are sandwiched between two other instances
                 for i in range(2):
                     final_string = re.sub(regex,
                                           f'\g<1>{new_glyph_name}\g<3>',
                                           final_string)
         with open(Path(TEMP_DIR / 'features.fea'), 'w') as features_file:
             features_file.write(final_string)
         # Set up substitution functionality
         print('  Adding features set to output font')
         addOpenTypeFeaturesFromString(output_font, final_string)
         # Save font
         print('  Saving output font to TTF file')
         output_font.save(output_file_path)
         output_font.close()
         print(f'Completed font "{output_file_path}"')
     else:
         print(f'Skipping missing input file "{input_file_path}"')
 # Perform cleanup tasks
 print('Cleaning up directories')
 rmtree(TEMP_DIR)
 print('All build tasks are complete')
Пример #21
0
def test_max_ctx_calc_no_features():
    font = TTFont()
    assert maxCtxFont(font) == 0
    font.setGlyphOrder(['.notdef'])
    addOpenTypeFeaturesFromString(font, '')
    assert maxCtxFont(font) == 0
Пример #22
0
def test_ensureDecompiled(lazy):
    # test that no matter the lazy value, ensureDecompiled decompiles all tables
    font = TTFont()
    font.importXML(os.path.join(DATA_DIR, "TestTTF-Regular.ttx"))
    # test font has no OTL so we add some, as an example of otData-driven tables
    addOpenTypeFeaturesFromString(
        font,
        """
        feature calt {
            sub period' period' period' space by ellipsis;
        } calt;

        feature dist {
            pos period period -30;
        } dist;
        """
    )
    # also add an additional cmap subtable that will be lazily-loaded
    cm = CmapSubtable.newSubtable(14)
    cm.platformID = 0
    cm.platEncID = 5
    cm.language = 0
    cm.cmap = {}
    cm.uvsDict = {0xFE00: [(0x002e, None)]}
    font["cmap"].tables.append(cm)

    # save and reload, potentially lazily
    buf = io.BytesIO()
    font.save(buf)
    buf.seek(0)
    font = TTFont(buf, lazy=lazy)

    # check no table is loaded until/unless requested, no matter the laziness
    for tag in font.keys():
        assert not font.isLoaded(tag)

    if lazy is not False:
        # additional cmap doesn't get decompiled automatically unless lazy=False;
        # can't use hasattr or else cmap's maginc __getattr__ kicks in...
        cm = next(st for st in font["cmap"].tables if st.__dict__["format"] == 14)
        assert cm.data is not None
        assert "uvsDict" not in cm.__dict__
        # glyf glyphs are not expanded unless lazy=False
        assert font["glyf"].glyphs["period"].data is not None
        assert not hasattr(font["glyf"].glyphs["period"], "coordinates")

    if lazy is True:
        # OTL tables hold a 'reader' to lazily load when lazy=True
        assert "reader" in font["GSUB"].table.LookupList.__dict__
        assert "reader" in font["GPOS"].table.LookupList.__dict__

    font.ensureDecompiled()

    # all tables are decompiled now
    for tag in font.keys():
        assert font.isLoaded(tag)
    # including the additional cmap
    cm = next(st for st in font["cmap"].tables if st.__dict__["format"] == 14)
    assert cm.data is None
    assert "uvsDict" in cm.__dict__
    # expanded glyf glyphs lost the 'data' attribute
    assert not hasattr(font["glyf"].glyphs["period"], "data")
    assert hasattr(font["glyf"].glyphs["period"], "coordinates")
    # and OTL tables have read their 'reader'
    assert "reader" not in font["GSUB"].table.LookupList.__dict__
    assert "Lookup" in font["GSUB"].table.LookupList.__dict__
    assert "reader" not in font["GPOS"].table.LookupList.__dict__
    assert "Lookup" in font["GPOS"].table.LookupList.__dict__
Пример #23
0
def test_max_ctx_calc_no_features():
    font = TTFont()
    assert maxCtxFont(font) == 0
    font.setGlyphOrder(['.notdef'])
    addOpenTypeFeaturesFromString(font, '')
    assert maxCtxFont(font) == 0
Пример #24
0
 def build(self, featureFile, tables=None):
     font = makeTTFont()
     addOpenTypeFeaturesFromString(font, featureFile, tables=tables)
     return font