xref: /aosp_15_r20/external/fonttools/Lib/fontTools/varLib/interpolate_layout.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughes"""
2*e1fe3e4aSElliott HughesInterpolate OpenType Layout tables (GDEF / GPOS / GSUB).
3*e1fe3e4aSElliott Hughes"""
4*e1fe3e4aSElliott Hughes
5*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont
6*e1fe3e4aSElliott Hughesfrom fontTools.varLib import models, VarLibError, load_designspace, load_masters
7*e1fe3e4aSElliott Hughesfrom fontTools.varLib.merger import InstancerMerger
8*e1fe3e4aSElliott Hughesimport os.path
9*e1fe3e4aSElliott Hughesimport logging
10*e1fe3e4aSElliott Hughesfrom copy import deepcopy
11*e1fe3e4aSElliott Hughesfrom pprint import pformat
12*e1fe3e4aSElliott Hughes
13*e1fe3e4aSElliott Hugheslog = logging.getLogger("fontTools.varLib.interpolate_layout")
14*e1fe3e4aSElliott Hughes
15*e1fe3e4aSElliott Hughes
16*e1fe3e4aSElliott Hughesdef interpolate_layout(designspace, loc, master_finder=lambda s: s, mapped=False):
17*e1fe3e4aSElliott Hughes    """
18*e1fe3e4aSElliott Hughes    Interpolate GPOS from a designspace file and location.
19*e1fe3e4aSElliott Hughes
20*e1fe3e4aSElliott Hughes    If master_finder is set, it should be a callable that takes master
21*e1fe3e4aSElliott Hughes    filename as found in designspace file and map it to master font
22*e1fe3e4aSElliott Hughes    binary as to be opened (eg. .ttf or .otf).
23*e1fe3e4aSElliott Hughes
24*e1fe3e4aSElliott Hughes    If mapped is False (default), then location is mapped using the
25*e1fe3e4aSElliott Hughes    map element of the axes in designspace file.  If mapped is True,
26*e1fe3e4aSElliott Hughes    it is assumed that location is in designspace's internal space and
27*e1fe3e4aSElliott Hughes    no mapping is performed.
28*e1fe3e4aSElliott Hughes    """
29*e1fe3e4aSElliott Hughes    if hasattr(designspace, "sources"):  # Assume a DesignspaceDocument
30*e1fe3e4aSElliott Hughes        pass
31*e1fe3e4aSElliott Hughes    else:  # Assume a file path
32*e1fe3e4aSElliott Hughes        from fontTools.designspaceLib import DesignSpaceDocument
33*e1fe3e4aSElliott Hughes
34*e1fe3e4aSElliott Hughes        designspace = DesignSpaceDocument.fromfile(designspace)
35*e1fe3e4aSElliott Hughes
36*e1fe3e4aSElliott Hughes    ds = load_designspace(designspace)
37*e1fe3e4aSElliott Hughes    log.info("Building interpolated font")
38*e1fe3e4aSElliott Hughes
39*e1fe3e4aSElliott Hughes    log.info("Loading master fonts")
40*e1fe3e4aSElliott Hughes    master_fonts = load_masters(designspace, master_finder)
41*e1fe3e4aSElliott Hughes    font = deepcopy(master_fonts[ds.base_idx])
42*e1fe3e4aSElliott Hughes
43*e1fe3e4aSElliott Hughes    log.info("Location: %s", pformat(loc))
44*e1fe3e4aSElliott Hughes    if not mapped:
45*e1fe3e4aSElliott Hughes        loc = {name: ds.axes[name].map_forward(v) for name, v in loc.items()}
46*e1fe3e4aSElliott Hughes    log.info("Internal location: %s", pformat(loc))
47*e1fe3e4aSElliott Hughes    loc = models.normalizeLocation(loc, ds.internal_axis_supports)
48*e1fe3e4aSElliott Hughes    log.info("Normalized location: %s", pformat(loc))
49*e1fe3e4aSElliott Hughes
50*e1fe3e4aSElliott Hughes    # Assume single-model for now.
51*e1fe3e4aSElliott Hughes    model = models.VariationModel(ds.normalized_master_locs)
52*e1fe3e4aSElliott Hughes    assert 0 == model.mapping[ds.base_idx]
53*e1fe3e4aSElliott Hughes
54*e1fe3e4aSElliott Hughes    merger = InstancerMerger(font, model, loc)
55*e1fe3e4aSElliott Hughes
56*e1fe3e4aSElliott Hughes    log.info("Building interpolated tables")
57*e1fe3e4aSElliott Hughes    # TODO GSUB/GDEF
58*e1fe3e4aSElliott Hughes    merger.mergeTables(font, master_fonts, ["GPOS"])
59*e1fe3e4aSElliott Hughes    return font
60*e1fe3e4aSElliott Hughes
61*e1fe3e4aSElliott Hughes
62*e1fe3e4aSElliott Hughesdef main(args=None):
63*e1fe3e4aSElliott Hughes    """Interpolate GDEF/GPOS/GSUB tables for a point on a designspace"""
64*e1fe3e4aSElliott Hughes    from fontTools import configLogger
65*e1fe3e4aSElliott Hughes    import argparse
66*e1fe3e4aSElliott Hughes    import sys
67*e1fe3e4aSElliott Hughes
68*e1fe3e4aSElliott Hughes    parser = argparse.ArgumentParser(
69*e1fe3e4aSElliott Hughes        "fonttools varLib.interpolate_layout",
70*e1fe3e4aSElliott Hughes        description=main.__doc__,
71*e1fe3e4aSElliott Hughes    )
72*e1fe3e4aSElliott Hughes    parser.add_argument(
73*e1fe3e4aSElliott Hughes        "designspace_filename", metavar="DESIGNSPACE", help="Input TTF files"
74*e1fe3e4aSElliott Hughes    )
75*e1fe3e4aSElliott Hughes    parser.add_argument(
76*e1fe3e4aSElliott Hughes        "locations",
77*e1fe3e4aSElliott Hughes        metavar="LOCATION",
78*e1fe3e4aSElliott Hughes        type=str,
79*e1fe3e4aSElliott Hughes        nargs="+",
80*e1fe3e4aSElliott Hughes        help="Axis locations (e.g. wdth=120",
81*e1fe3e4aSElliott Hughes    )
82*e1fe3e4aSElliott Hughes    parser.add_argument(
83*e1fe3e4aSElliott Hughes        "-o",
84*e1fe3e4aSElliott Hughes        "--output",
85*e1fe3e4aSElliott Hughes        metavar="OUTPUT",
86*e1fe3e4aSElliott Hughes        help="Output font file (defaults to <designspacename>-instance.ttf)",
87*e1fe3e4aSElliott Hughes    )
88*e1fe3e4aSElliott Hughes    parser.add_argument(
89*e1fe3e4aSElliott Hughes        "-l",
90*e1fe3e4aSElliott Hughes        "--loglevel",
91*e1fe3e4aSElliott Hughes        metavar="LEVEL",
92*e1fe3e4aSElliott Hughes        default="INFO",
93*e1fe3e4aSElliott Hughes        help="Logging level (defaults to INFO)",
94*e1fe3e4aSElliott Hughes    )
95*e1fe3e4aSElliott Hughes
96*e1fe3e4aSElliott Hughes    args = parser.parse_args(args)
97*e1fe3e4aSElliott Hughes
98*e1fe3e4aSElliott Hughes    if not args.output:
99*e1fe3e4aSElliott Hughes        args.output = os.path.splitext(args.designspace_filename)[0] + "-instance.ttf"
100*e1fe3e4aSElliott Hughes
101*e1fe3e4aSElliott Hughes    configLogger(level=args.loglevel)
102*e1fe3e4aSElliott Hughes
103*e1fe3e4aSElliott Hughes    finder = lambda s: s.replace("master_ufo", "master_ttf_interpolatable").replace(
104*e1fe3e4aSElliott Hughes        ".ufo", ".ttf"
105*e1fe3e4aSElliott Hughes    )
106*e1fe3e4aSElliott Hughes
107*e1fe3e4aSElliott Hughes    loc = {}
108*e1fe3e4aSElliott Hughes    for arg in args.locations:
109*e1fe3e4aSElliott Hughes        tag, val = arg.split("=")
110*e1fe3e4aSElliott Hughes        loc[tag] = float(val)
111*e1fe3e4aSElliott Hughes
112*e1fe3e4aSElliott Hughes    font = interpolate_layout(args.designspace_filename, loc, finder)
113*e1fe3e4aSElliott Hughes    log.info("Saving font %s", args.output)
114*e1fe3e4aSElliott Hughes    font.save(args.output)
115*e1fe3e4aSElliott Hughes
116*e1fe3e4aSElliott Hughes
117*e1fe3e4aSElliott Hughesif __name__ == "__main__":
118*e1fe3e4aSElliott Hughes    import sys
119*e1fe3e4aSElliott Hughes
120*e1fe3e4aSElliott Hughes    if len(sys.argv) > 1:
121*e1fe3e4aSElliott Hughes        sys.exit(main())
122*e1fe3e4aSElliott Hughes    import doctest
123*e1fe3e4aSElliott Hughes
124*e1fe3e4aSElliott Hughes    sys.exit(doctest.testmod().failed)
125