def test_component_getBounds(layer: Layer) -> None: assert Component("a", (1, 0, 0, 1, 0, 0)).getBounds(layer) == (0, 0, 7.5, 20) assert Component("a", (1, 0, 0, 1, -5, 0)).getBounds(layer) == (-5, 0, 2.5, 20) assert Component("a", (1, 0, 0, 1, 0, 5)).getBounds(layer) == (0, 5, 7.5, 25)
def test_component_getControlBounds(layer): assert Component("a", (1, 0, 0, 1, 0, 0)).getControlBounds(layer) == (0, 0, 10, 20) assert Component("a", (1, 0, 0, 1, -5, 0)).getControlBounds(layer) == (-5, 0, 5, 20) assert Component("a", (1, 0, 0, 1, 0, 5)).getControlBounds(layer) == (0, 5, 10, 25)
def _glyf_ufo(ufo, color_glyphs): # glyphs by reuse_key glyphs = {} for color_glyph in color_glyphs: logging.debug( "%s %s %s", ufo.info.familyName, color_glyph.glyph_name, color_glyph.transform_for_font_space(), ) parent_glyph = ufo.get(color_glyph.glyph_name) for painted_layer in color_glyph.as_painted_layers(): # if we've seen this shape before reuse it reuse_key = _inter_glyph_reuse_key(painted_layer) if reuse_key not in glyphs: glyph = _create_glyph(color_glyph, painted_layer) glyphs[reuse_key] = glyph else: glyph = glyphs[reuse_key] parent_glyph.components.append(Component(baseGlyph=glyph.name)) # No great reason to keep single-component glyphs around if len(parent_glyph.components) == 1: component = ufo[parent_glyph.components[0].baseGlyph] del ufo[component.name] ufo[color_glyph.glyph_name] = component assert component.name == color_glyph.glyph_name
def _create_transformed_glyph(color_glyph: ColorGlyph, paint: PaintGlyph, transform: Affine2D) -> Glyph: glyph = _init_glyph(color_glyph) glyph.components.append( Component(baseGlyph=paint.glyph, transformation=transform)) color_glyph.ufo.glyphOrder += [glyph.name] return glyph
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)
def _create_glyph(color_glyph: ColorGlyph, painted_layer: PaintedLayer) -> Glyph: ufo = color_glyph.ufo glyph = ufo.newGlyph( _next_name(ufo, lambda i: f"{color_glyph.glyph_name}.{i}")) glyph_names = [glyph.name] glyph.width = ufo.info.unitsPerEm svg_units_to_font_units = color_glyph.transform_for_font_space() if painted_layer.reuses: # Shape repeats, form a composite base_glyph = ufo.newGlyph( _next_name(ufo, lambda i: f"{glyph.name}.component.{i}")) glyph_names.append(base_glyph.name) draw_svg_path(painted_layer.path, base_glyph.getPen(), svg_units_to_font_units) glyph.components.append( Component(baseGlyph=base_glyph.name, transformation=Affine2D.identity())) for transform in painted_layer.reuses: # We already redrew the component into font space, don't redo it # scale x/y translation and flip y movement to match font space transform = transform._replace( e=transform.e * svg_units_to_font_units.a, f=transform.f * svg_units_to_font_units.d, ) glyph.components.append( Component(baseGlyph=base_glyph.name, transformation=transform)) else: # Not a composite, just draw directly on the glyph draw_svg_path(painted_layer.path, glyph.getPen(), svg_units_to_font_units) ufo.glyphOrder += glyph_names return glyph
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
def _glyf_ufo(config: FontConfig, ufo: ufoLib2.Font, color_glyphs: Tuple[ColorGlyph, ...]): # We want to mutate our view of color_glyphs color_glyphs = list(color_glyphs) # glyphs by reuse_key glyph_cache = GlyphReuseCache(config.reuse_tolerance) glyph_uses = Counter() for i, color_glyph in enumerate(color_glyphs): logging.debug( "%s %s %s", ufo.info.familyName, color_glyph.glyph_name, color_glyph.transform_for_font_space(), ) parent_glyph = ufo[color_glyph.glyph_name] # generate glyphs for PaintGlyph's and assign glyph names color_glyphs[i] = color_glyph = _migrate_paths_to_ufo_glyphs( color_glyph, glyph_cache) for root in color_glyph.painted_layers: for context in root.breadth_first(): # For 'glyf' just dump anything that isn't a PaintGlyph if not isinstance(context.paint, PaintGlyph): continue paint_glyph = cast(PaintGlyph, context.paint) glyph = ufo.get(paint_glyph.glyph) parent_glyph.components.append( Component(baseGlyph=glyph.name, transformation=context.transform)) glyph_uses[glyph.name] += 1 # No great reason to keep single-component glyphs around (unless reused) for color_glyph in color_glyphs: parent_glyph = ufo[color_glyph.glyph_name] if (len(parent_glyph.components) == 1 and glyph_uses[parent_glyph.components[0].baseGlyph] == 1): component = ufo[parent_glyph.components[0].baseGlyph] del ufo[component.name] component.unicode = parent_glyph.unicode ufo[color_glyph.glyph_name] = component assert component.name == color_glyph.glyph_name
(Point(0, 0), { "x": 0, "y": 0 }), ( Point(1.5, -2.1, "qcurve", True, "foobar", "12345"), { "x": 1.5, "y": -2.1, "type": "qcurve", "smooth": True, "name": "foobar", "identifier": "12345", }, ), (Component("a"), { "baseGlyph": "a" }), ( Component("b", Transform(1, 0, 0, 0.5, 10, 20), "abcd1234"), { "baseGlyph": "b", "transformation": (1, 0, 0, 0.5, 10, 20), "identifier": "abcd1234", }, ), (Contour(), {}), ( Contour([Point(0, 0), Point(1, 2)], "zzzz"), { "points": [{
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)
def test_component_not_in_layer(layer: Layer) -> None: with pytest.raises(KeyError, match="b"): Component("b", (1, 0, 0, 1, 0, 0)).getBounds(layer) with pytest.raises(KeyError, match="b"): Component("b", (1, 0, 0, 1, 0, 0)).getControlBounds(layer)