Example #1
0
def colrv1_path(tmp_path):
    base_glyph_names = ["uni%04X" % i for i in range(0xE000, 0xE000 + 10)]
    layer_glyph_names = ["glyph%05d" % i for i in range(10, 20)]
    glyph_order = [".notdef"] + base_glyph_names + layer_glyph_names

    pen = TTGlyphPen(glyphSet=None)
    pen.moveTo((0, 0))
    pen.lineTo((0, 500))
    pen.lineTo((500, 500))
    pen.lineTo((500, 0))
    pen.closePath()
    glyph = pen.glyph()
    glyphs = {g: glyph for g in glyph_order}

    fb = FontBuilder(unitsPerEm=1024, isTTF=True)
    fb.setupGlyphOrder(glyph_order)
    fb.setupCharacterMap(
        {int(name[3:], 16): name
         for name in base_glyph_names})
    fb.setupGlyf(glyphs)
    fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order})
    fb.setupHorizontalHeader()
    fb.setupOS2()
    fb.setupPost()
    fb.setupNameTable({"familyName": "TestCOLRv1", "styleName": "Regular"})

    fb.setupCOLR(
        {
            "uniE000": (
                ot.PaintFormat.PaintColrLayers,
                [
                    {
                        "Format": ot.PaintFormat.PaintGlyph,
                        "Paint": (ot.PaintFormat.PaintSolid, 0),
                        "Glyph": "glyph00010",
                    },
                    {
                        "Format": ot.PaintFormat.PaintGlyph,
                        "Paint": (ot.PaintFormat.PaintSolid, (2, 0.3)),
                        "Glyph": "glyph00011",
                    },
                ],
            ),
            "uniE001": (
                ot.PaintFormat.PaintColrLayers,
                [
                    {
                        "Format": ot.PaintFormat.PaintTransform,
                        "Paint": {
                            "Format": ot.PaintFormat.PaintGlyph,
                            "Paint": {
                                "Format": ot.PaintFormat.PaintRadialGradient,
                                "x0": 250,
                                "y0": 250,
                                "r0": 250,
                                "x1": 200,
                                "y1": 200,
                                "r1": 0,
                                "ColorLine": {
                                    "ColorStop": [(0.0, 1), (1.0, 2)],
                                    "Extend": "repeat",
                                },
                            },
                            "Glyph": "glyph00012",
                        },
                        "Transform": (0.7071, 0.7071, -0.7071, 0.7071, 0, 0),
                    },
                    {
                        "Format": ot.PaintFormat.PaintGlyph,
                        "Paint": (ot.PaintFormat.PaintSolid, (1, 0.5)),
                        "Glyph": "glyph00013",
                    },
                ],
            ),
            "uniE002": (
                ot.PaintFormat.PaintColrLayers,
                [
                    {
                        "Format": ot.PaintFormat.PaintGlyph,
                        "Paint": {
                            "Format": ot.PaintFormat.PaintLinearGradient,
                            "x0": 0,
                            "y0": 0,
                            "x1": 500,
                            "y1": 500,
                            "x2": -500,
                            "y2": 500,
                            "ColorLine": {
                                "ColorStop": [(0.0, 1), (1.0, 2)]
                            },
                        },
                        "Glyph": "glyph00014",
                    },
                    {
                        "Format": ot.PaintFormat.PaintTransform,
                        "Paint": {
                            "Format": ot.PaintFormat.PaintGlyph,
                            "Paint": (ot.PaintFormat.PaintSolid, 1),
                            "Glyph": "glyph00015",
                        },
                        "Transform": (1, 0, 0, 1, 400, 400),
                    },
                ],
            ),
            "uniE003": {
                "Format": ot.PaintFormat.PaintRotate,
                "Paint": {
                    "Format": ot.PaintFormat.PaintColrGlyph,
                    "Glyph": "uniE001",
                },
                "angle": 45,
                "centerX": 250,
                "centerY": 250,
            },
            "uniE004": [
                ("glyph00016", 1),
                ("glyph00017", 2),
            ],
        }, )
    fb.setupCPAL(
        [
            [
                (1.0, 0.0, 0.0, 1.0),  # red
                (0.0, 1.0, 0.0, 1.0),  # green
                (0.0, 0.0, 1.0, 1.0),  # blue
            ],
        ], )

    output_path = tmp_path / "TestCOLRv1.ttf"
    fb.save(output_path)

    return output_path
Example #2
0
def build(instance, opts):
    font = instance.parent
    master = font.masters[0]

    glyphOrder = []
    advanceWidths = {}
    characterMap = {}
    charStrings = {}
    colorLayers = {}
    for glyph in font.glyphs:
        if not glyph.export:
            continue
        name = glyph.name
        for layer in glyph.layers:
            if layer.name.startswith("Color "):
                _, index = layer.name.split(" ")
                if name not in colorLayers:
                    colorLayers[name] = []
                colorLayers[name].append((name, int(index)))

        glyphOrder.append(name)
        if glyph.unicode:
            characterMap[int(glyph.unicode, 16)] = name

        layer = getLayer(glyph, instance)
        charStrings[name] = draw(layer, instance).getCharString()
        advanceWidths[name] = layer.width

    # XXX
    glyphOrder.pop(glyphOrder.index(".notdef"))
    glyphOrder.pop(glyphOrder.index("space"))
    glyphOrder.insert(0, ".notdef")
    glyphOrder.insert(1, "space")

    version = float(opts.version)

    vendor = font.customParameters["vendorID"]
    names = {
        "copyright": font.copyright,
        "familyName": instance.familyName,
        "styleName": instance.name,
        "uniqueFontIdentifier": f"{version:.03f};{vendor};{instance.fontName}",
        "fullName": instance.fullName,
        "version": f"Version {version:.03f}",
        "psName": instance.fontName,
        "manufacturer": font.manufacturer,
        "designer": font.designer,
        "vendorURL": font.manufacturerURL,
        "designerURL": font.designerURL,
        "licenseDescription": font.customParameters["license"],
        "licenseInfoURL": font.customParameters["licenseURL"],
        "sampleText": font.customParameters["sampleText"],
    }

    fb = FontBuilder(font.upm, isTTF=False)
    fb.updateHead(fontRevision=version)
    fb.setupGlyphOrder(glyphOrder)
    fb.setupCharacterMap(characterMap)
    fb.setupNameTable(names, mac=False)
    fb.setupHorizontalHeader(ascent=master.ascender,
                             descent=master.descender,
                             lineGap=master.customParameters['hheaLineGap'])

    if opts.debug:
        fb.setupCFF(names["psName"], {}, charStrings, {})
    else:
        fb.setupCFF2(charStrings)

    metrics = {}
    for name, width in advanceWidths.items():
        bounds = charStrings[name].calcBounds(None) or [0]
        metrics[name] = (width, bounds[0])
    fb.setupHorizontalMetrics(metrics)

    fb.setupPost()

    fea = makeFeatures(instance, master, opts)
    fb.addOpenTypeFeatures(fea)

    palettes = master.customParameters["Color Palettes"]
    palettes = [[tuple(int(v) / 255 for v in c.split(",")) for c in p]
                for p in palettes]
    fb.setupCPAL(palettes)
    fb.setupCOLR(colorLayers)

    instance.font = fb.font
    axes = [
        instance.weightValue,
        instance.widthValue,
        instance.customValue,
        instance.customValue1,
        instance.customValue2,
        instance.customValue3,
    ]
    instance.axes = {}
    for i, axis in enumerate(font.customParameters["Axes"]):
        instance.axes[axis["Tag"]] = axes[i]

    if opts.debug:
        fb.font.save(f"{instance.fontName}.otf")
        fb.font.saveXML(f"{instance.fontName}.ttx")

    return fb.font
Example #3
0
def build(instance, opts, glyphOrder):
    font = instance.parent
    master = font.masters[0]

    advanceWidths = {}
    characterMap = {}
    charStrings = {}
    colorLayers = {}
    for name in glyphOrder:
        glyph = font.glyphs[name]
        if not glyph.export:
            continue
        for layer in glyph.layers:
            if "colorPalette" in layer.attributes:
                index = layer.attributes["colorPalette"]
                if name not in colorLayers:
                    colorLayers[name] = []
                if layer.layerId == layer.associatedMasterId:  # master layer
                    colorLayers[name].append((name, int(index)))
                else:
                    assert False, "can’t handle non-master color layers"

        if glyph.unicode:
            characterMap[int(glyph.unicode, 16)] = name

        layer = getLayer(glyph, instance)
        charStrings[name] = draw(layer, instance).getCharString()
        advanceWidths[name] = layer.width

    # XXX
    glyphOrder.pop(glyphOrder.index(".notdef"))
    glyphOrder.pop(glyphOrder.index("space"))
    glyphOrder.insert(0, ".notdef")
    glyphOrder.insert(1, "space")

    version = float(opts.version)

    vendor = get_property(font, "vendorID")
    names = {
        "copyright": font.copyright,
        "familyName": instance.familyName,
        "styleName": instance.name,
        "uniqueFontIdentifier": f"{version:.03f};{vendor};{instance.fontName}",
        "fullName": instance.fullName,
        "version": f"Version {version:.03f}",
        "psName": instance.fontName,
        "manufacturer": font.manufacturer,
        "designer": font.designer,
        "vendorURL": font.manufacturerURL,
        "designerURL": font.designerURL,
        "licenseDescription": get_property(font, "licenses"),
        "licenseInfoURL": get_property(font, "licenseURL"),
        "sampleText": get_property(font, "sampleTexts"),
    }

    fb = FontBuilder(font.upm, isTTF=False)
    date = font.date.replace(tzinfo=datetime.timezone.utc)
    stat = opts.glyphs.stat()
    fb.updateHead(
        fontRevision=version,
        created=int(date.timestamp()) - mac_epoch_diff,
        modified=int(stat.st_mtime) - mac_epoch_diff,
    )
    fb.setupGlyphOrder(glyphOrder)
    fb.setupCharacterMap(characterMap)
    fb.setupNameTable(names, mac=False)
    fb.setupHorizontalHeader(
        ascent=master.ascender,
        descent=master.descender,
        lineGap=master.customParameters["hheaLineGap"],
    )

    if opts.debug:
        fb.setupCFF(names["psName"], {}, charStrings, {})
        fb.font["CFF "].compile(fb.font)
    else:
        fb.setupCFF2(charStrings)

    metrics = {}
    for name, width in advanceWidths.items():
        bounds = charStrings[name].calcBounds(None) or [0]
        metrics[name] = (width, bounds[0])
    fb.setupHorizontalMetrics(metrics)

    fb.setupPost(
        underlinePosition=master.customParameters["underlinePosition"],
        underlineThickness=master.customParameters["underlineThickness"],
    )

    # Compile to get font bbox
    fb.font["head"].compile(fb.font)

    codePages = [
        CODEPAGE_RANGES[v] for v in font.customParameters["codePageRanges"]
    ]
    fb.setupOS2(
        version=4,
        sTypoAscender=master.ascender,
        sTypoDescender=master.descender,
        sTypoLineGap=master.customParameters["typoLineGap"],
        usWinAscent=fb.font["head"].yMax,
        usWinDescent=-fb.font["head"].yMin,
        sxHeight=master.xHeight,
        sCapHeight=master.capHeight,
        achVendID=vendor,
        fsType=calcBits(font.customParameters["fsType"], 0, 16),
        fsSelection=calcFsSelection(instance),
        ulUnicodeRange1=calcBits(font.customParameters["unicodeRanges"], 0,
                                 32),
        ulCodePageRange1=calcBits(codePages, 0, 32),
    )

    fea = makeFeatures(instance, master, opts, glyphOrder)
    fb.addOpenTypeFeatures(fea)

    palettes = master.customParameters["Color Palettes"]
    palettes = [[tuple(v / 255 for v in c) for c in p] for p in palettes]
    fb.setupCPAL(palettes)
    fb.setupCOLR(colorLayers)

    instance.font = fb.font
    if opts.debug:
        fb.font.save(f"{instance.fontName}.otf")

    return fb.font