Ejemplo n.º 1
0
def contour() -> Contour:
    g = Glyph("a")
    pen = g.getPen()
    pen.moveTo((0, 0))
    pen.curveTo((10, 10), (10, 20), (0, 20))
    pen.closePath()
    return g.contours[0]
Ejemplo n.º 2
0
def test_insertGlyph():
    g = Glyph()
    pen = g.getPen()
    pen.moveTo((0, 0))
    pen.lineTo((1, 1))
    pen.lineTo((0, 1))
    pen.closePath()

    layer = Layer()
    layer.insertGlyph(g, "a")

    assert "a" in layer
    assert layer["a"].name == "a"
    assert layer["a"].contours == g.contours
    assert layer["a"] is not g

    layer.insertGlyph(g, "b")
    assert "b" in layer
    assert layer["b"].name == "b"
    assert layer["b"].contours == layer["a"].contours
    assert layer["b"] is not layer["a"]
    assert layer["b"] is not g

    assert g.name is None

    with pytest.raises(KeyError, match="glyph named 'a' already exists"):
        layer.insertGlyph(g, "a", overwrite=False)

    with pytest.raises(ValueError,
                       match=".*Glyph .* has no name; can't add it"):
        layer.insertGlyph(g)
Ejemplo n.º 3
0
def layer() -> Layer:
    a = Glyph("a")
    pen = a.getPen()
    pen.moveTo((0, 0))
    pen.curveTo((10, 10), (10, 20), (0, 20))
    pen.closePath()

    layer = Layer(glyphs=[a])
    return layer
Ejemplo n.º 4
0
def test_font_defcon_behavior(ufo_UbuTestData):
    font = ufo_UbuTestData

    font.newGlyph("b")
    assert "b" in font

    glyph = Glyph("c")
    font.addGlyph(glyph)
    assert font["c"] is glyph

    font.renameGlyph("c", "d")
    assert font["d"] is glyph
    assert font["d"].name == "d"

    guideline = Guideline(x=1)
    font.appendGuideline(guideline)
    assert font.info.guidelines[-1] is guideline

    font.appendGuideline({"y": 1, "name": "asdf"})
    assert font.info.guidelines[-1].name == "asdf"

    font.newLayer("abc")
    assert "abc" in font.layers

    font.renameLayer("abc", "def")
    assert "abc" not in font.layers
    assert "def" in font.layers
Ejemplo n.º 5
0
def addRCJKGlyphToVarCoUFO(
    ufo,
    rcjkGlyphSet,
    srcGlyphName,
    dstGlyphName,
    unicodes,
    renameTable,
    componentSourceGlyphSet,
    globalAxisNames,
):
    if renameTable is None:
        renameTable = {}
    rcjkGlyph = rcjkGlyphSet.getGlyph(srcGlyphName)
    if rcjkGlyph.components and not rcjkGlyph.outline.isEmpty():
        logger.warning(
            f"glyph {srcGlyphName} has both outlines and components")

    glyph = UGlyph(dstGlyphName)
    glyph.unicodes = unicodes
    glyph.width = max(0, rcjkGlyph.width)  # width can't be negative
    rcjkGlyphToVarCoGlyph(rcjkGlyph, glyph, renameTable,
                          componentSourceGlyphSet)

    if globalAxisNames is None:
        axisNameMapping = _makeAxisNameMapping(rcjkGlyph.axes)
        axisNames = set(axisNameMapping.values())
    else:
        axisNames = globalAxisNames

    for varIndex, rcjkVarGlyph in enumerate(rcjkGlyph.variations):
        location = rcjkVarGlyph.location
        location = normalizeLocation(location, rcjkGlyph.axes)
        if globalAxisNames is None:
            location = {axisNameMapping[k]: v for k, v in location.items()}
        sparseLocation = {k: v for k, v in location.items() if v != 0}
        layerName = layerNameFromLocation(sparseLocation, axisNames)
        assert layerName, (srcGlyphName, varIndex, location, rcjkGlyph.axes)
        layer = getUFOLayer(ufo, layerName)
        varGlyph = UGlyph(dstGlyphName)
        varGlyph.width = max(0, rcjkVarGlyph.width)  # width can't be negative
        rcjkGlyphToVarCoGlyph(rcjkVarGlyph, varGlyph, renameTable,
                              componentSourceGlyphSet)
        layer[dstGlyphName] = varGlyph

    ufo[dstGlyphName] = glyph
Ejemplo n.º 6
0
def test_structure_forbid_extra_keys(forbid_extra_keys: bool) -> None:
    conv = cattr.GenConverter(forbid_extra_keys=forbid_extra_keys)
    register_hooks(conv)
    data = {"name": "a", "foo": "bar"}
    if forbid_extra_keys:
        with pytest.raises(Exception,
                           match="Extra fields in constructor for .*: foo"):
            conv.structure(data, Glyph)
    else:
        assert conv.structure(data, Glyph) == Glyph(name="a")
Ejemplo n.º 7
0
def test_init_layer_with_glyphs_list():
    a = Glyph("a")
    b = Glyph("b")
    layer = Layer(glyphs=[a, b])

    assert layer["a"] is a
    assert layer["b"] is b

    with pytest.raises(KeyError, match=".*Glyph .* can't be added twice"):
        Layer(glyphs=[a, a])

    c = Glyph()
    with pytest.raises(ValueError, match=".*Glyph .* has no name"):
        Layer(glyphs=[c])

    with pytest.raises(KeyError, match="glyph named 'b' already exists"):
        Layer(glyphs=[a, b, Glyph("b")])

    with pytest.raises(TypeError, match="Expected Glyph, found int"):
        Layer(glyphs=[1])
Ejemplo n.º 8
0
def test_addGlyph():
    a = Glyph("a")

    layer = Layer()

    layer.addGlyph(a)

    assert "a" in layer
    assert layer["a"] is a

    with pytest.raises(KeyError, match="glyph named 'a' already exists"):
        layer.addGlyph(a)
Ejemplo n.º 9
0
 def addFlattenedGlyphsToUFO(self,
                             ufo,
                             location,
                             numDecimalsRounding=0,
                             characterSet=None,
                             glyphSet=None):
     if characterSet is not None and glyphSet is not None:
         raise TypeError("can't pass both characterSet and glyphSet")
     if numDecimalsRounding == 1:
         roundFunc = roundFuncOneDecimal
     elif numDecimalsRounding != 0:
         assert 0, numDecimalsRounding
     else:
         roundFunc = otRound
     revCmap = self.getGlyphNamesAndUnicodes()
     glyphNames = filterGlyphNames(sorted(revCmap))
     for glyphName in glyphNames:
         if glyphSet is not None:
             if glyphName not in glyphSet:
                 continue
         elif characterSet is not None:
             codePoints = set(revCmap[glyphName])
             if not codePoints & characterSet:
                 continue
         glyph = UGlyph(glyphName)
         glyph.unicodes = revCmap[glyphName]
         copyMarkColor(self.characterGlyphGlyphSet.getGlyph(glyphName),
                       glyph)
         pen = RoundingPointPen(glyph.getPointPen(), roundFunc)
         try:
             width = self.drawPointsCharacterGlyph(glyphName, location, pen)
         except InterpolationError as e:
             logger.warning(
                 f"glyph {glyphName} can't be interpolated ({e})")
         except Exception as e:
             logger.warning(f"glyph {glyphName} caused an error: {e!r}")
             raise
         else:
             glyph.width = max(0, width)  # can't be negative
             ufo[glyphName] = glyph
Ejemplo n.º 10
0
def _draw_glyph_extents(ufo: ufoLib2.Font, glyph: Glyph):
    # apparently on Mac (but not Linux) Chrome and Firefox end up relying on the
    # extents of the base layer to determine where the glyph might paint. If you
    # leave the base blank the COLR glyph never renders.

    # TODO we could narrow this to bbox to cover all layers

    pen = glyph.getPen()
    pen.moveTo((0, 0))
    pen.lineTo((ufo.info.unitsPerEm, ufo.info.unitsPerEm))
    pen.endPath()

    return glyph
Ejemplo n.º 11
0
def test_font_mapping_behavior(ufo_UbuTestData):
    font = ufo_UbuTestData

    assert font["a"] is font.layers.defaultLayer["a"]
    assert ("a" in font) == ("a" in font.layers.defaultLayer)
    assert len(font) == len(font.layers.defaultLayer)

    glyph = Glyph("b")
    font["b"] = glyph
    assert font["b"] is glyph
    assert font.layers.defaultLayer["b"] is glyph

    del font["a"]
    assert "a" not in font
    assert "a" not in font.layers.defaultLayer
Ejemplo n.º 12
0
def test_init_layer_with_glyphs_dict():
    a = Glyph()
    b = Glyph()

    layer = Layer("My Layer", {"a": a, "b": b})

    assert layer.name == "My Layer"
    assert "a" in layer
    assert layer["a"] is a
    assert a.name == "a"
    assert "b" in layer
    assert layer["b"] is b
    assert b.name == "b"

    with pytest.raises(
            ValueError,
            match="glyph has incorrect name: expected 'a', found 'b'"):
        Layer(glyphs={"a": b})

    with pytest.raises(KeyError, match=".*Glyph .* can't be added twice"):
        Layer(glyphs={"a": a, "b": a})

    with pytest.raises(TypeError, match="Expected Glyph, found int"):
        Layer(glyphs={"a": 1})
Ejemplo n.º 13
0
def test_renameGlyph() -> None:
    g = Glyph()

    layer = Layer(glyphs={"a": g})
    assert g.name == "a"

    layer.renameGlyph("a", "a")  # no-op
    assert g.name == "a"

    layer.renameGlyph("a", "b")
    assert g.name == "b"

    layer.insertGlyph(g, "a")

    with pytest.raises(KeyError, match="target glyph named 'a' already exists"):
        layer.renameGlyph("b", "a")
Ejemplo n.º 14
0
def test_custom_type_overrides() -> None:
    conv = cattr.GenConverter(
        type_overrides={Image: cattr.override(omit=True)})
    register_hooks(conv)

    # check that Glyph.image attribute (of type Image) is omitted
    assert conv.unstructure(Glyph()) == {
        "width": 0,
        "height": 0,
        "unicodes": [],
        "lib": {},
        "anchors": [],
        "components": [],
        "contours": [],
        "guidelines": [],
    }
Ejemplo n.º 15
0
def layer() -> Layer:
    a = Glyph("a")
    pen = a.getPen()
    pen.moveTo((8, 0))
    pen.lineTo((18, 0))
    pen.lineTo((18, 20))
    pen.lineTo((8, 20))
    pen.closePath()
    a.width = 30
    a.appendAnchor({"x": 10, "y": 30, "name": "top"})

    b = Glyph("b", width=a.width, components=[Component("a", (1, 0, 0, 1, 2, -5))])

    layer = Layer(glyphs=[a, b])
    return layer
Ejemplo n.º 16
0
def _draw_glyph_extents(ufo: ufoLib2.Font, glyph: Glyph,
                        bounds: Tuple[float, float, float, float]):
    # apparently on Mac (but not Linux) Chrome and Firefox end up relying on the
    # extents of the base layer to determine where the glyph might paint. If you
    # leave the base blank the COLR glyph never renders.

    if rectArea(bounds) == 0:
        return

    start, end = bounds[:2], bounds[2:]

    pen = glyph.getPen()
    pen.moveTo(start)
    pen.lineTo(end)
    pen.endPath()

    return glyph
Ejemplo n.º 17
0
def test_glyph_get_margins(layer):
    a = layer["a"]

    # for simple contour glyphs without components, layer is optional/unused
    assert a.getLeftMargin() == 8
    assert a.getLeftMargin(layer) == 8
    assert a.getRightMargin() == 12
    assert a.getRightMargin(layer) == 12
    assert a.getBottomMargin() == 0
    assert a.getBottomMargin(layer) == 0
    assert a.getTopMargin() == -20
    assert a.getTopMargin(layer) == -20

    a.verticalOrigin = 20
    assert a.getBottomMargin() == -20
    assert a.getBottomMargin(layer) == -20
    assert a.getTopMargin() == 0
    assert a.getTopMargin(layer) == 0

    b = layer["b"]
    # for composite glyphs, layer is required
    for m in ("Left", "Right", "Top", "Bottom"):
        with pytest.raises(TypeError,
                           match="layer is required to compute bounds"):
            getattr(b, f"get{m}Margin")()

    assert b.getLeftMargin(layer) == 10
    assert b.getRightMargin(layer) == 10
    assert b.getBottomMargin(layer) == -5
    assert b.getTopMargin(layer) == -15

    b.verticalOrigin = 15
    assert b.getBottomMargin(layer) == -20
    assert b.getTopMargin(layer) == 0

    c = Glyph()  # empty glyph
    assert c.getLeftMargin() is None
    assert c.getRightMargin() is None
    assert c.getBottomMargin() is None
    assert c.getTopMargin() is None
Ejemplo n.º 18
0
def test_glyph_get_bounds_empty():
    g = Glyph()
    assert g.getBounds() is None
    assert g.getControlBounds() is None
Ejemplo n.º 19
0
def test_glyph_without_name():
    assert Glyph().name is None
Ejemplo n.º 20
0
     Guideline(x=1,
               y=2,
               angle=45.0,
               name="baz",
               color="0,1,0.5,1",
               identifier="0001"),
     {
         "x": 1,
         "y": 2,
         "angle": 45.0,
         "name": "baz",
         "color": "0,1,0.5,1",
         "identifier": "0001",
     },
 ),
 (Glyph(), {}),
 (
     Glyph(
         "a",
         width=1000,
         height=800,
         unicodes=[97],
         lib=Lib(foo=b"bar"),
         note="Latin lowercase 'a'",
         anchors=[Anchor(100, 200, "top"),
                  Anchor(100, -50, "bottom")],
         contours=[
             Contour([
                 Point(0, 0, "line"),
                 Point(1, 1, "line"),
                 Point(1, 0, "line")
Ejemplo n.º 21
0
def test_glyph_repr():
    g = Glyph()
    assert repr(g) == f"<ufoLib2.objects.glyph.Glyph at {hex(id(g))}>"

    g = Glyph("a")
    assert repr(g) == f"<ufoLib2.objects.glyph.Glyph 'a' at {hex(id(g))}>"
Ejemplo n.º 22
0
def test_glyph_get_bounds():
    a = Glyph("a")
    pen = a.getPen()
    pen.moveTo((0, 0))
    pen.curveTo((10, 10), (10, 20), (0, 20))
    pen.closePath()

    b = Glyph("b", components=[Component("a", (1, 0, 0, 1, -50, 100))])

    layer = Layer(glyphs=[a, b])

    assert a.getBounds(layer) == BoundingBox(xMin=0, yMin=0, xMax=7.5, yMax=20)

    assert a.getControlBounds(layer) == BoundingBox(xMin=0,
                                                    yMin=0,
                                                    xMax=10,
                                                    yMax=20)

    with pytest.raises(
            TypeError,
            match="layer is required to compute bounds of components"):
        b.getBounds()
    with pytest.raises(
            TypeError,
            match="layer is required to compute bounds of components"):
        b.getControlBounds()

    assert b.getBounds(layer) == (-50, 100, -42.5, 120
                                  )  # namedtuple is a tuple
    assert b.getControlBounds(layer) == (-50, 100, -40, 120)
Ejemplo n.º 23
0
def test_glyph_defcon_behavior():
    glyph = Glyph()
    glyph.appendAnchor(Anchor(1, 2, "top"))
    glyph.appendAnchor({"x": 3, "y": 4, "name": "bottom"})
    assert glyph.anchors == [Anchor(1, 2, "top"), Anchor(3, 4, "bottom")]

    glyph = Glyph()
    glyph.appendContour(Contour([Point(1, 2)]))
    assert glyph.contours == [Contour([Point(1, 2)])]

    glyph = Glyph()
    glyph.appendGuideline(Guideline(x=1))
    glyph.appendGuideline({"x": 2})
    assert glyph.guidelines == [Guideline(x=1), Guideline(x=2)]
Ejemplo n.º 24
0
def test_copyDataFromGlyph(ufo_UbuTestData):
    font = ufo_UbuTestData

    a = font["a"]
    a.height = 500
    a.image = Image("a.png")
    a.note = "a note"
    a.lib = {"bar": [3, 2, 1]}
    a.anchors = [Anchor(250, 0, "bottom")]
    a.guidelines = [Guideline(y=500)]
    a.components = [Component("A")]

    b = Glyph("b")
    b.width = 350
    b.height = 1000
    b.image = Image("b.png")
    b.note = "b note"
    b.lib = {"foo": [1, 2, 3]}
    b.anchors = [Anchor(350, 800, "top")]
    b.guidelines = [Guideline(x=50)]

    assert b.name != a.name
    assert b.width != a.width
    assert b.height != a.height
    assert b.unicodes != a.unicodes
    assert b.image != a.image
    assert b.note != a.note
    assert b.lib != a.lib
    assert b.anchors != a.anchors
    assert b.guidelines != a.guidelines
    assert b.contours != a.contours
    assert b.components != a.components

    def _assert_equal_but_distinct_objects(glyph1, glyph2):
        assert glyph1.width == glyph2.width
        assert glyph1.height == glyph2.height
        assert glyph1.unicodes == glyph2.unicodes
        assert glyph1.unicodes is not glyph2.unicodes
        assert glyph1.image == glyph2.image
        assert glyph1.image is not glyph2.image
        assert glyph1.note == glyph2.note
        assert glyph1.lib == glyph2.lib
        assert glyph1.lib is not glyph2.lib
        assert glyph1.lib["bar"] == glyph2.lib["bar"]
        assert glyph1.lib["bar"] is not glyph2.lib["bar"]
        assert glyph1.anchors == glyph2.anchors
        assert glyph1.anchors is not glyph2.anchors
        assert glyph1.anchors[0] is not glyph2.anchors[0]
        assert glyph1.guidelines == glyph2.guidelines
        assert glyph1.guidelines is not glyph2.guidelines
        assert glyph1.guidelines[0] is not glyph2.guidelines[0]
        assert glyph1.contours == glyph2.contours
        assert glyph1.contours is not glyph2.contours
        assert glyph1.contours[0] is not glyph2.contours[0]
        assert glyph1.components == glyph2.components
        assert glyph1.components is not glyph2.components
        assert glyph1.components[0] is not glyph2.components[0]

    b.copyDataFromGlyph(a)
    assert b.name != a.name
    _assert_equal_but_distinct_objects(b, a)

    c = a.copy()
    assert c.name == a.name
    _assert_equal_but_distinct_objects(c, a)

    d = a.copy(name="d")
    assert d.name == "d"
    _assert_equal_but_distinct_objects(d, a)