コード例 #1
0
ファイル: color_glyph.py プロジェクト: mavit/nanoemoji
def _get_gradient_transform(grad_el, shape_bbox, view_box, upem) -> Affine2D:
    transform = map_viewbox_to_font_emsquare(view_box, upem)

    gradient_units = grad_el.attrib.get("gradientUnits", "objectBoundingBox")
    if gradient_units == "objectBoundingBox":
        bbox_space = Rect(0, 0, 1, 1)
        bbox_transform = Affine2D.rect_to_rect(bbox_space, shape_bbox)
        transform = Affine2D.product(bbox_transform, transform)

    if "gradientTransform" in grad_el.attrib:
        gradient_transform = Affine2D.fromstring(
            grad_el.attrib["gradientTransform"])
        transform = Affine2D.product(gradient_transform, transform)

    return transform
コード例 #2
0
ファイル: svg.py プロジェクト: yisibl/picosvg
    def apply_transforms(self, inplace=False):
        """Naively transforms to shapes and removes the transform attribute.

        Naive: just applies any transform on a parent element.
        """
        if not inplace:
            svg = SVG(copy.deepcopy(self.svg_root))
            svg.apply_transforms(inplace=True)
            return svg

        self._update_etree()

        # figure out the sequence of transforms, if any, for each shape
        new_shapes = []
        for idx, (el, (shape, )) in enumerate(self._elements()):
            transform = Affine2D.identity()
            while el is not None:
                if "transform" in el.attrib:
                    transform = Affine2D.product(
                        transform, Affine2D.fromstring(el.attrib["transform"]))
                el = el.getparent()
            if transform == Affine2D.identity():
                continue
            new_shapes.append((idx, shape.apply_transform(transform)))

        for el_idx, new_shape in new_shapes:
            el, _ = self.elements[el_idx]
            self._set_element(el_idx, el, (new_shape, ))

        # destroy all transform attributes
        self.remove_attributes(["transform"],
                               xpath="//svg:*[@transform]",
                               inplace=True)

        return self
コード例 #3
0
ファイル: svg.py プロジェクト: yisibl/picosvg
 def _inherit_matrix_multiply(attrib, child, attr_name):
     group_transform = Affine2D.fromstring(attrib[attr_name])
     if attr_name in child.attrib:
         transform = Affine2D.fromstring(child.attrib[attr_name])
         transform = Affine2D.product(transform, group_transform)
     else:
         transform = group_transform
     if transform != Affine2D.identity():
         child.attrib[attr_name] = transform.tostring()
     else:
         del child.attrib[attr_name]
コード例 #4
0
ファイル: color_glyph.py プロジェクト: mavit/nanoemoji
def _parse_radial_gradient(grad_el, shape_bbox, view_box, upem):
    width, height = _get_gradient_units_relative_scale(grad_el, view_box)

    cx = _number_or_percentage(grad_el.attrib.get("cx", "50%"), width)
    cy = _number_or_percentage(grad_el.attrib.get("cy", "50%"), height)
    r = _number_or_percentage(grad_el.attrib.get("r", "50%"), width)

    raw_fx = grad_el.attrib.get("fx")
    fx = _number_or_percentage(raw_fx, width) if raw_fx is not None else cx
    raw_fy = grad_el.attrib.get("fy")
    fy = _number_or_percentage(raw_fy, height) if raw_fy is not None else cy
    fr = _number_or_percentage(grad_el.attrib.get("fr", "0%"), width)

    c0 = Point(fx, fy)
    r0 = fr
    c1 = Point(cx, cy)
    r1 = r

    transform = _get_gradient_transform(grad_el, shape_bbox, view_box, upem)

    # The optional Affine2x2 matrix of COLRv1.RadialGradient is used to transform
    # the circles into ellipses "around their centres": i.e. centres coordinates
    # are _not_ transformed by it. Thus we apply the full transform to them.
    c0 = transform.map_point(c0)
    c1 = transform.map_point(c1)

    # As for the circle radii (which are affected by Affine2x2), we only scale them
    # by the maximum of the (absolute) scale or skew.
    # Then in Affine2x2 we only store a "fraction" of the original transform, i.e.
    # multiplied by the inverse of the scale that we've already applied to the radii.
    # Especially when gradientUnits="objectBoundingBox", where circle positions and
    # radii are expressed using small floats in the range [0..1], this pre-scaling
    # helps reducing the inevitable rounding errors that arise from storing these
    # values as integers in COLRv1 tables.
    s = max(abs(v) for v in transform[:4])

    rscale = Affine2D(s, 0, 0, s, 0, 0)
    r0 = rscale.map_vector((r0, 0)).x
    r1 = rscale.map_vector((r1, 0)).x

    affine2x2 = Affine2D.product(rscale.inverse(), transform)

    gradient = {
        "c0": c0,
        "c1": c1,
        "r0": r0,
        "r1": r1,
        "affine2x2":
        (affine2x2[:4] if affine2x2 != Affine2D.identity() else None),
    }

    # TODO handle degenerate cases, fallback to solid, w/e

    return gradient