コード例 #1
0
ファイル: naive_warp.py プロジェクト: rsheeter/warp
def normalize_flag_aspect(svg, viewbox_size, width, height, right_margin,
                          top_margin):
    apply_viewbox_preserve_aspect_ratio(svg)

    current_box = _bbox(tuple(s.bounding_box() for s in svg.shapes()))

    # Try to keep overall proportions for the flags that are considerably
    # narrower or wider than the standard aspect ratio.
    aspect = current_box.w / current_box.h
    aspect /= STD_ASPECT
    aspect = sqrt(aspect)  # Discount the effect
    if 0.9 <= aspect <= 1.1:
        aspect = 1.0
    else:
        print("Non-standard aspect ratio:", aspect)

    xmin = _x_aspect(right_margin, aspect, viewbox_size)
    ymin = _y_aspect(top_margin, aspect, viewbox_size)
    xmax = _x_aspect(right_margin + width, aspect, viewbox_size)
    ymax = _y_aspect(top_margin + height, aspect, viewbox_size)
    new_box = Rect(xmin, ymin, xmax - xmin, ymax - ymin)

    affine = Affine2D.rect_to_rect(current_box, new_box)

    _picosvg_transform(svg, affine)

    square_viewbox = Rect(0, 0, viewbox_size, viewbox_size)
    svg.svg_root.attrib["viewBox"] = " ".join(ntos(v) for v in square_viewbox)
    for attr_name in ("width", "height"):
        if attr_name in svg.svg_root.attrib:
            del svg.svg_root.attrib[attr_name]
コード例 #2
0
ファイル: svg_transform.py プロジェクト: bby422/picosvg
 def rect_to_rect(cls, src: Rect, dst: Rect) -> "Affine2D":
     """ Return Affine2D set to scale and translate src Rect to dst Rect.
     The mapping completely fills dst, it does not preserve aspect ratio.
     """
     if src.empty():
         return cls.identity()
     if dst.empty():
         return cls(0, 0, 0, 0, 0, 0)
     sx = dst.w / src.w
     sy = dst.h / src.h
     tx = dst.x - src.x * sx
     ty = dst.y - src.y * sy
     return cls(sx, 0, 0, sy, tx, ty)
コード例 #3
0
ファイル: naive_warp.py プロジェクト: rsheeter/warp
def apply_viewbox_preserve_aspect_ratio(svg):
    """If viewport != viewBox apply the resulting transform and remove viewBox.
    Takes 'preserveAspectRatio' into account.
    E.g. The Qatar flag (QA.svg) needs this treatment.
    """
    svg_root = svg.svg_root
    width = svg_root.attrib.get("width")
    height = svg_root.attrib.get("height")
    if width is not None and height is not None and "viewBox" in svg_root.attrib:
        # ignore absolute length units; we're only interested in the relative size
        # of viewport vs viewbox here
        width = SVG_UNITS_RE.sub("", width)
        height = SVG_UNITS_RE.sub("", height)
        viewport = Rect(0, 0, float(width), float(height))
        viewbox = svg.view_box()
        if viewport != viewbox:
            transform = Affine2D.rect_to_rect(
                viewbox,
                viewport,
                svg_root.attrib.get("preserveAspectRatio", "xMidYMid"),
            )
            _picosvg_transform(svg, transform)
            del svg_root.attrib["viewBox"]
            if "preserveAspectRatio" in svg_root.attrib:
                del svg_root.attrib["preserveAspectRatio"]
コード例 #4
0
ファイル: svg_transform.py プロジェクト: yisibl/picosvg
    def rect_to_rect(
        cls,
        src: Rect,
        dst: Rect,
        preserveAspectRatio: str = "none",
    ) -> "Affine2D":
        """Return Affine2D set to scale and translate src Rect to dst Rect.
        By default the mapping completely fills dst, it does not preserve aspect ratio,
        unless the 'preserveAspectRatio' argument is used.
        See https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute for
        the list of values supported.
        """
        if src.empty():
            return cls.identity()
        if dst.empty():
            return cls(0, 0, 0, 0, 0, 0)

        # We follow the same process described in the SVG spec for computing the
        # equivalent scale + translation which maps from viewBox (src) to viewport (dst)
        # coordinates given the value of preserveAspectRatio.
        # https://www.w3.org/TR/SVG/coords.html#ComputingAViewportsTransform
        sx = dst.w / src.w
        sy = dst.h / src.h

        align, _, meetOrSlice = preserveAspectRatio.lower().strip().partition(
            " ")
        if (align not in cls._ALIGN_VALUES
                or meetOrSlice and meetOrSlice not in cls._MEET_OR_SLICE):
            raise ValueError(
                f"Invalid preserveAspectRatio: {preserveAspectRatio!r}")

        if align != "none":
            sx = sy = max(sx, sy) if "slice" in meetOrSlice else min(sx, sy)

        tx = dst.x - src.x * sx
        ty = dst.y - src.y * sy

        if "xmid" in align:
            tx += (dst.w - src.w * sx) / 2
        elif "xmax" in align:
            tx += dst.w - src.w * sx
        if "ymid" in align:
            ty += (dst.h - src.h * sy) / 2
        elif "ymax" in align:
            ty += dst.h - src.h * sy

        return cls(sx, 0, 0, sy, tx, ty)
コード例 #5
0
def update_symbol(symbol, ttfont, icon_name, symbol_wght_name):
    glyph_name = icon_font.resolve_ligature(ttfont, icon_name)
    upem = ttfont["head"].unitsPerEm
    # For Icon fonts, the Glyphs are Y shifted by upem and the Y axis is flipped.
    symbol.write_icon(
        symbol_wght_name,
        ttfont.getGlyphSet()[glyph_name],
        SVGPathPen(ttfont.getGlyphSet()),
        Rect(0, upem, upem, -upem),
    )
コード例 #6
0
ファイル: naive_warp.py プロジェクト: rsheeter/warp
def _bbox(boxes):
    min_corner = (9999, 9999)
    max_corner = (-9999, -9999)
    for box in boxes:
        min_corner = tuple(
            min(v1, v2) for v1, v2 in zip(min_corner, (box.x, box.y)))
        max_corner = tuple(
            max(v1, v2)
            for v1, v2 in zip(max_corner, (box.x + box.w, box.y + box.h)))

    return Rect(*min_corner,
                *(maxv - minv for minv, maxv in zip(min_corner, max_corner)))
コード例 #7
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
コード例 #8
0
def main(argv):
    icon_name = os.path.splitext(os.path.basename(FLAGS.out))[0]
    dest_region = Rect(0, 0, 120, 120)

    symbol = _new_symbol()

    for font_filename in argv[1:]:
        ttfont = ttLib.TTFont(font_filename)
        svg_path = _draw_svg_path(ttfont, icon_name, dest_region)
        ttfont.close()

        symbol_wght_name = font_filename.split(".")[-2]
        _write_icon(symbol, symbol_wght_name, svg_path)

    _drop_empty_icons(symbol)

    with open(FLAGS.out, "w") as f:
        f.write(symbol.tostring())
コード例 #9
0
def main(argv):
    if len(argv) > 2:
        sys.exit("Expected Only 1 non-flag Argument.")
    symbol = Symbol()
    pico = SVG.parse(argv[1]).topicosvg()
    main_svg = pico.xpath_one("//svg:svg")
    symbol.write_icon(
        _REQUIRED_SYMBOL,
        svgLib.SVGPath.fromstring(pico.tostring()),
        SVGPathPen(None),
        Rect(
            0,
            0,
            parse_float(main_svg.get("width")),
            parse_float(main_svg.get("height")),
        ),
    )
    symbol.drop_empty_icons()
    symbol.write_to(FLAGS.out)
コード例 #10
0
ファイル: color_glyph.py プロジェクト: yisibl/nanoemoji
def _get_gradient_transform(
    config: FontConfig,
    grad_el: etree.Element,
    shape_bbox: Rect,
    view_box: Rect,
    glyph_width: int,
) -> Affine2D:
    transform = map_viewbox_to_font_space(
        view_box, config.ascender, config.descender, glyph_width, config.transform
    )

    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.compose_ltr((bbox_transform, transform))

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

    return transform
コード例 #11
0
def _map_font_to_viewbox(upem: int, view_box: Rect):
    if view_box != Rect(0, 0, view_box.w, view_box.w):
        raise ValueError("We simply must have a SQUARE from 0,0")
    affine = color_glyph.map_viewbox_to_font_emsquare(Rect(0, 0, upem, upem),
                                                      view_box.w)
    return Transform(*affine)
コード例 #12
0
parser.add_argument("destdir")
parser.add_argument(
    "--round",
    dest="rounding_ndigits",
    metavar="NDIGITS",
    type=int,
    default=ROUNDING_NDIGITS,
    help="default: %(default)s",
)
parser.add_argument(
    "--viewbox-size",
    metavar="SIZE",
    type=int,
    default=VIEW_BOX_SIZE,
    help="default: %(default)s",
)

options = parser.parse_args(sys.argv[1:])

font = TTFont(options.fontfile)
viewbox = Rect(0, 0, options.viewbox_size, options.viewbox_size)

os.makedirs(options.destdir, exist_ok=True)

for glyph_name, svg in colr_to_svg(
        viewbox, font, rounding_ndigits=options.rounding_ndigits).items():
    output_file = os.path.join(options.destdir, f"{glyph_name}.svg")
    with open(output_file, "w") as f:
        f.write(svg.tostring(pretty_print=True))
    print(f"{output_file}")
コード例 #13
0
def parse_view_box(s: str) -> Rect:
    box = tuple(float(v) for v in re.split(r",|\s+", s))
    if len(box) != 4:
        raise ValueError(f"Unable to parse viewBox: {s!r}")
    return Rect(*box)
コード例 #14
0
 def bounding_box(self) -> Rect:
     x1, y1, x2, y2 = svg_pathops.bounding_box(self.as_cmd_seq())
     return Rect(x1, y1, x2 - x1, y2 - y1)
コード例 #15
0
"""Convert COLRv1 font to a set of SVG files, one per base color glyph."""
import os
import sys

# extend PYTHONPATH to include ../tests dir where colr_to_svg module is located
sys.path.insert(
    0, os.path.join(os.path.dirname(os.path.dirname(__file__)), "tests"))

from colr_to_svg import colr_to_svg
from fontTools.ttLib import TTFont
from picosvg.geometric_types import Rect
from lxml import etree

VIEW_BOX_SIZE = 128

try:
    fontfile, destdir = sys.argv[1:]
except ValueError:
    sys.exit("usage: ./colr2svg.py FONTFILE SVG_OUTPUT_DIR")

font = TTFont(fontfile)
viewbox = Rect(0, 0, VIEW_BOX_SIZE, VIEW_BOX_SIZE)

os.makedirs(destdir, exist_ok=True)

for glyph_name, svg in colr_to_svg(viewbox, font).items():
    output_file = os.path.join(destdir, f"{glyph_name}.svg")
    with open(output_file, "wb") as f:
        f.write(etree.tostring(svg.svg_root, pretty_print=True))
    print(f"{output_file}")
コード例 #16
0
def test_empty_rect():
    assert Rect(x=1, y=2, w=3, h=0).empty()
    assert Rect(x=1, y=2, w=0, h=3).empty()
コード例 #17
0
ファイル: symbol.py プロジェクト: EsoHasInsomnia/vf2symbols
 def _build_transformation(self, rect):
     return Affine2D.rect_to_rect(rect,
                                  Rect(0, 0, _SYMBOL_SIZE,
                                       _SYMBOL_SIZE)).translate(
                                           0, _SYMBOL_DY_MULTIPLE * rect.h)