Esempio n. 1
0
def add_paths(layer, t, additional_transform=Identity):
    commands = ""
    for p in layer.paths:
        svgpen = SVGPathPen(None)
        transformpen = TransformPen(svgpen, t)
        componenttransformpen = TransformPen(transformpen, additional_transform)
        p.draw(componenttransformpen)
        commands += svgpen.getCommands()
    return commands
Esempio n. 2
0
 def draw(self, pen):
     if self.transform:
         pen = TransformPen(pen, self.transform)
     pb = PathBuilder()
     # xpath | doesn't seem to reliable work so just walk it
     for el in self.root.iter():
         pb.add_path_from_element(el)
     original_pen = pen
     for path, transform in zip(pb.paths, pb.transforms):
         if transform:
             pen = TransformPen(original_pen, transform)
         else:
             pen = original_pen
         parse_path(path, pen)
Esempio n. 3
0
def draw_svg_path(path: SVGPath,
                  pen: AbstractPen,
                  transform: Optional[Affine2D] = None):
    """Draw SVGPath using a FontTools Segment Pen."""
    if transform is not None:
        pen = TransformPen(pen, transform)

    # In SVG sub-paths are implicitly open when they don't end with "Z"; in FT pens
    # the end of each sub-path must be marked explicitly with either pen.endPath()
    # for open paths or closePath() for closed ones.
    closed = True
    for cmd, args in path.as_cmd_seq():
        if cmd == "M":
            if not closed:
                pen.endPath()
            closed = False

        # pens expect args as 2-tuples; we use the 'grouper' itertools recipe
        # https://docs.python.org/3.8/library/itertools.html#recipes
        assert len(args) % 2 == 0
        points = itertools.zip_longest(*([iter(args)] * 2))

        getattr(pen, _SVG_CMD_TO_PEN_METHOD[cmd])(*points)

        if cmd == "Z":
            closed = True

    if not closed:
        pen.endPath()
Esempio n. 4
0
    def __init__(self, dat, h):
        super().__init__()
        self.dat = dat
        self.path = skia.Path()

        tp = TransformPen(self, (1, 0, 0, -1, 0, h))
        dat.replay(tp)
Esempio n. 5
0
    def draw(self, scale=0.01, cm=False):
        self.dat.scale(scale, point=False)
        self.page = self.page.scale(scale)
        b = self.dat.bounds()
        limits = Rect(0, 0, 11, 8.5)
        if cm:
            limits = limits.scale(2.54)
        if b.x >= 0 and b.y >= 0 and b.w <= limits.w and b.h <= limits.h:
            print("Drawing!")
        else:
            print("Too big!", b)
            return False

        ad = axidraw.AxiDraw()
        ad.interactive()
        ad.options.units = 1 if cm else 0
        ad.options.speed_pendown = 10
        ad.options.speed_penup = 50
        ad.options.pen_rate_raise = 10

        ad.connect()
        ad.penup()
        self.ad = ad
        tp = TransformPen(self, (1, 0, 0, -1, 0, self.page.h))
        self.dat.replay(tp)
        time.sleep(MOVE_DELAY)
        ad.penup()
        ad.moveto(0, 0)
        ad.disconnect()
Esempio n. 6
0
def main(argv):
    if len(argv) != 2:
        sys.stderr.write(
            "Please enter paths to two font files for center of mass comparisons"
            + os.linesep)
        sys.exit(1)

    test_glyphs = ('uni002B', 'uni002E')

    font1 = TTFont(argv[0])
    font2 = TTFont(argv[1])

    test_fonts = (font1, font2)

    for glyph_name in test_glyphs:
        print("\nGLYPH: " + glyph_name)
        x = 0
        for font in test_fonts:
            x += 1
            print("Font " + str(x))
            this_glyphset = font.getGlyphSet()
            glyph_obj = this_glyphset[glyph_name]
            stats_pen = StatisticsPen(glyphset=this_glyphset)
            upem = font['head'].unitsPerEm
            transformer = TransformPen(stats_pen, Scale(1. / upem))
            glyph_obj.draw(transformer)

            for item in ['area', 'meanX', 'meanY']:
                print("%s: %g" % (item, getattr(stats_pen, item)))

            print(" ")

        print(" ")
def processFonts(font_path):

    # Load the fonts and collect their glyph sets
    font = ttLib.TTFont(font_path)
    gSet = font.getGlyphSet()
    glyphNamesList = gSet.keys()
    font.close()

    # Confirm that there's something to process
    if not glyphNamesList:
        print("The fonts and options provided can't produce any SVG files.",
              file=sys.stdout)
        return

    web_font_dict = dict()
    # Generate the SVGs
    for gName in glyphNamesList:

        pen = SVGPen(gSet)
        tpen = TransformPen(pen, (1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
        glyph = gSet[gName]
        glyph.draw(tpen)
        d = pen.d
        # Skip glyphs with no contours
        if not len(d):
            continue

        web_font_dict[gName] = d  # (gname, d)

    font.close()

    return web_font_dict
Esempio n. 8
0
def draw(layer, instance, pen):
    pen = PointToSegmentPen(pen)

    for path in layer.paths:
        nodes = list(path.nodes)

        pen.beginPath()
        if nodes:
            if not path.closed:
                node = nodes.pop(0)
                assert node.type == "line", "Open path starts with off-curve points"
                pen.addPoint(tuple(node.position), segmentType="move")
            else:
                # In Glyphs.app, the starting node of a closed contour is always
                # stored at the end of the nodes list.
                nodes.insert(0, nodes.pop())
            for node in nodes:
                node_type = node.type
                if node_type not in ["line", "curve", "qcurve"]:
                    node_type = None
                pen.addPoint(tuple(node.position), segmentType=node_type, smooth=node.smooth)
        pen.endPath();

    for component in layer.components:
        componentLayer = getLayer(component.component, instance)
        transform = component.transform.value
        componentPen = pen.pen
        if transform != DEFAULT_TRANSFORM:
            componentPen = TransformPen(pen.pen, transform)
            xx, xy, yx, yy = transform[:4]
            if xx * yy - xy * yx < 0:
                componentPen = ReverseContourPen(componentPen)
        draw(componentLayer, instance, componentPen)

    return pen.pen
Esempio n. 9
0
    def glyph(self, componentFlags=0x4):
        assert self._isClosed(), "Didn't close last contour."

        components = []
        for glyphName, transformation in self.components:
            if self.points:
                # can't have both, so decompose the glyph
                tpen = TransformPen(self, transformation)
                self.glyphSet[glyphName].draw(tpen)
                continue

            component = GlyphComponent()
            component.glyphName = glyphName
            if transformation[:4] != (1, 0, 0, 1):
                component.transform = (transformation[:2], transformation[2:4])
            component.x, component.y = transformation[4:]
            component.flags = componentFlags
            components.append(component)

        glyph = Glyph()
        glyph.coordinates = GlyphCoordinates(self.points)
        glyph.endPtsOfContours = self.endPts
        glyph.flags = array("B", self.types)
        self.init()

        if components:
            glyph.components = components
            glyph.numberOfContours = -1
        else:
            glyph.numberOfContours = len(glyph.endPtsOfContours)
            glyph.program = ttProgram.Program()
            glyph.program.fromBytecode(b"")

        return glyph
Esempio n. 10
0
def _deepCopyContours(glyphSet, parent, component, transformation):
    """Copy contours from component to parent, including nested components."""

    for nestedComponent in component.components:
        try:
            nestedBaseGlyph = glyphSet[nestedComponent.baseGlyph]
        except KeyError:
            logger.warning(
                "dropping non-existent component '%s' in glyph '%s'",
                nestedComponent.baseGlyph, parent.name)
        else:
            _deepCopyContours(
                glyphSet, parent, nestedBaseGlyph,
                transformation.transform(nestedComponent.transformation))

    if component != parent:
        if transformation == Identity:
            pen = parent.getPen()
        else:
            pen = TransformPen(parent.getPen(), transformation)
            # if the transformation has a negative determinant, it will
            # reverse the contour direction of the component
            xx, xy, yx, yy = transformation[:4]
            if xx * yy - xy * yx < 0:
                pen = ReverseContourPen(pen)

        component.draw(pen)
Esempio n. 11
0
    def _buildComponents(self, componentFlags):
        if self.handleOverflowingTransforms:
            # we can't encode transform values > 2 or < -2 in F2Dot14,
            # so we must decompose the glyph if any transform exceeds these
            overflowing = any(s > 2 or s < -2
                              for (glyphName, transformation) in self.components
                              for s in transformation[:4])
        components = []
        for glyphName, transformation in self.components:
            if glyphName not in self.glyphSet:
                self.log.warning(
                    "skipped non-existing component '%s'", glyphName
                )
                continue
            if (self.points or
                    (self.handleOverflowingTransforms and overflowing)):
                # can't have both coordinates and components, so decompose
                tpen = TransformPen(self, transformation)
                self.glyphSet[glyphName].draw(tpen)
                continue

            component = GlyphComponent()
            component.glyphName = glyphName
            component.x, component.y = transformation[4:]
            transformation = transformation[:4]
            if transformation != (1, 0, 0, 1):
                if (self.handleOverflowingTransforms and
                        any(MAX_F2DOT14 < s <= 2 for s in transformation)):
                    # clamp values ~= +2.0 so we can keep the component
                    transformation = tuple(MAX_F2DOT14 if MAX_F2DOT14 < s <= 2
                                           else s for s in transformation)
                component.transform = (transformation[:2], transformation[2:])
            component.flags = componentFlags
            components.append(component)
        return components
Esempio n. 12
0
    def draw(self, scale=0.01, dry=True, cm=False):
        if dry:
            with viewer() as v:
                dp = DATPen().record(self.dat).attr(fill=None, stroke=0)
                v.send(SVGPen.Composite([dp], self.page), self.page)
        else:
            self.dat.scale(scale, center=False)
            self.page = self.page.scale(scale)
            b = self.dat.bounds()
            limits = Rect(0, 0, 11, 8.5)
            if cm:
                limits = limits.scale(2.54)
            if b.x >= 0 and b.y >= 0 and b.w <= limits.w and b.h <= limits.h:
                print("Drawing!")
            else:
                print("Too big!", b)
                return False
            ad = axidraw.AxiDraw()
            ad.interactive()
            ad.options.units = 1 if cm else 0
            ad.options.speed_pendown = 50
            ad.options.speed_penup = 50

            ad.connect()
            ad.penup()
            self.ad = ad
            tp = TransformPen(self, (1, 0, 0, -1, 0, self.page.h))
            self.dat.replay(tp)
            time.sleep(MOVE_DELAY)
            ad.penup()
            ad.moveto(0, 0)
            ad.disconnect()
Esempio n. 13
0
	def draw(self, pen):
		if self.transform:
			pen = TransformPen(pen, self.transform)
		for path in self.paths:
			current_pos = None
			for s in path:
				if current_pos != s.start:
					if current_pos is not None:
						pen.closePath()
					pen.moveTo((s.start.real, s.start.imag))
				if isinstance(s, Line):
					pen.lineTo((s.end.real, s.end.imag))
				elif isinstance(s, CubicBezier):
					pen.curveTo(
						(s.control1.real, s.control1.imag),
						(s.control2.real, s.control2.imag),
						(s.end.real, s.end.imag))
				elif isinstance(s, QuadraticBezier):
					pen.qCurveTo(
						(s.control.real, s.control.imag),
						(s.end.real, s.end.imag))
				#else:
					# TODO convert Arc segments to bezier?
					#raise NotImplementedError(s)
				current_pos = s.end
			pen.closePath()
Esempio n. 14
0
def makeSVGShape(glyph, name=None, width=None, opacity=None):
    attrs = {
        'id':
        'mathShape',
        'title':
        "None",
        'xmlns':
        "http://www.w3.org/2000/svg",
        'xmlns:xlink':
        "http://www.w3.org/1999/xlink",
        'xml:space':
        'preserve',
        'style':
        "fill-rule:nonzero;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;",
    }
    # try to get the bounds from the bounds layer.
    # if that does not work, get it from the glyph itself.
    bounds = None
    try:
        boundsGlyph = glyph.getLayer('bounds')
        if boundsGlyph is not None:
            bounds = boundsGlyph.box
            # print 'using bounds from bounds layer'
    except:
        pass
        # print 'using bounds from glyph'
    if bounds is None:
        boundsPen = BoundsPen({})
        glyph.draw(boundsPen)
        bounds = boundsPen.bounds

    xOffset = 0
    yOffset = 0
    attrs['id'] = name
    if width is None:
        attrs['width'] = "100%"
    else:
        attrs['width'] = width
    if name is not None:
        attrs['name'] = name
    else:
        attrs['name'] = glyph.name
    if opacity is not None:
        attrs['fill-opacity'] = "%3.3f" % opacity

    t = Transform()
    # print bounds, -(bounds[3]-bounds[1])
    t = t.scale(1, -1)
    t = t.translate(0, -bounds[3])
    vb = (0, 0, glyph.width, bounds[3] - bounds[1])
    attrs['viewBox'] = "%3.3f %3.3f %3.3f %3.3f" % (vb[0], vb[1], vb[2], vb[3])
    attrs['enable-background'] = attrs['viewBox']
    sPen = MathImageSVGPathPen({})
    tPen = TransformPen(sPen, t)
    glyph.draw(tPen)
    path = "<path d=\"%s\"/>" % (sPen.getCommands())
    tag = "<svg %s>%s</svg>" % (" ".join(
        ["%s=\"%s\"" % (k, v) for k, v in attrs.items()]), path)
    return vb, tag
Esempio n. 15
0
 def __init__(self, dat, h):
     super().__init__(None)
     self.defs = []
     self.uses = []
     self.dat = dat
     self.h = h
     tp = TransformPen(self, (1, 0, 0, -1, 0, h))
     dat.round_to(0.1).replay(tp)
Esempio n. 16
0
 def addComponent(self, name, transform):
     pen = self
     if transform != Identity:
         pen = TransformPen(pen, transform)
         xx, xy, yx, yy = transform[:4]
         if xx * yy - xy * yx < 0:
             pen = ReverseContourPen(pen)
     self._layerSet[name].draw(pen)
Esempio n. 17
0
 def addComponent(self, glyphName, transformation):
     try:
         glyph = self.glyphSet[glyphName]
     except KeyError:
         return
     else:
         tPen = TransformPen(self.pen, transformation)
         glyph.draw(tPen)
Esempio n. 18
0
    def glyph(self, componentFlags=0x4):
        assert self._isClosed(), "Didn't close last contour."

        if self.handleOverflowingTransforms:
            # we can't encode transform values > 2 or < -2 in F2Dot14,
            # so we must decompose the glyph if any transform exceeds these
            overflowing = any(s > 2 or s < -2
                              for (glyphName,
                                   transformation) in self.components
                              for s in transformation[:4])

        components = []
        for glyphName, transformation in self.components:
            if (self.points
                    or (self.handleOverflowingTransforms and overflowing)):
                # can't have both coordinates and components, so decompose
                try:
                    baseGlyph = self.glyphSet[glyphName]
                except KeyError:
                    self.log.debug(
                        "can't decompose non-existing component '%s'; skipped",
                        glyphName)
                    continue
                else:
                    tpen = TransformPen(self, transformation)
                    baseGlyph.draw(tpen)
                    continue

            component = GlyphComponent()
            component.glyphName = glyphName
            component.x, component.y = transformation[4:]
            transformation = transformation[:4]
            if transformation != (1, 0, 0, 1):
                if (self.handleOverflowingTransforms
                        and any(MAX_F2DOT14 < s <= 2 for s in transformation)):
                    # clamp values ~= +2.0 so we can keep the component
                    transformation = tuple(
                        MAX_F2DOT14 if MAX_F2DOT14 < s <= 2 else s
                        for s in transformation)
                component.transform = (transformation[:2], transformation[2:])
            component.flags = componentFlags
            components.append(component)

        glyph = Glyph()
        glyph.coordinates = GlyphCoordinates(self.points)
        glyph.endPtsOfContours = self.endPts
        glyph.flags = array("B", self.types)
        self.init()

        if components:
            glyph.components = components
            glyph.numberOfContours = -1
        else:
            glyph.numberOfContours = len(glyph.endPtsOfContours)
            glyph.program = ttProgram.Program()
            glyph.program.fromBytecode(b"")

        return glyph
Esempio n. 19
0
def _draw_svg_path(ttfont, icon_name, dest_region):
    glyph_name = icon_font.resolve_ligature(ttfont, icon_name)
    upem = ttfont["head"].unitsPerEm
    transform = _map_font_to_viewbox(upem, dest_region)
    transform = transform.translate(0, 0.7942 * upem)

    svg_pen = SVGPathPen(ttfont.getGlyphSet())
    ttfont.getGlyphSet()[glyph_name].draw(TransformPen(svg_pen, transform))
    return " ".join(svg_pen._commands)
Esempio n. 20
0
 def svg(self, file, gid, rect=Rect(0, 0, 0, 100)):
     """WIP; attempt to read an svg file into the pen"""
     from bs4 import BeautifulSoup
     with open(file, "r") as f:
         soup = BeautifulSoup(f.read(), features="lxml")
         tp = TransformPen(self, (1, 0, 0, -1, 0, rect.h))
         for path in soup.find(id=gid).find_all("path"):
             parse_path(path.get("d"), tp)
     return self
Esempio n. 21
0
 def transform(self, transform, transformFrame=True):
     """Perform an arbitrary transformation on the pen, using the fontTools `Transform` class."""
     op = RecordingPen()
     tp = TransformPen(op, transform)
     self.replay(tp)
     self.value = op.value
     if transformFrame and self.frame:
         self.frame = self.frame.transform(transform)
     return self
Esempio n. 22
0
 def addComponent(self, name, transform):
     layer = self.glyphSet[name]
     pen = self.pen
     if layer.components and not layer.paths:
         if transform != Identity:
             pen = TransformPen(pen, transform)
         layer.draw(FlattenComponentsPen(pen, self.glyphSet))
     else:
         pen.addComponent(name, transform)
Esempio n. 23
0
def trianglePath(x, y, size, angle):
    thirdSize = size / 3
    pen = QtPen({})
    tPen = TransformPen(pen, Identity.rotate(angle))
    tPen.moveTo((-thirdSize, size))
    tPen.lineTo((-thirdSize, -size))
    tPen.lineTo((2 * thirdSize, 0))
    tPen.closePath()
    return pen.path.translated(x, y)
Esempio n. 24
0
def _transformed_glyph_bounds(
        ufo: ufoLib2.Font, glyph_name: str,
        transform: Affine2D) -> Optional[Tuple[float, float, float, float]]:
    glyph = ufo[glyph_name]
    pen = bounds_pen = ControlBoundsPen(ufo)
    if not transform.almost_equals(Affine2D.identity()):
        pen = TransformPen(bounds_pen, transform)
    glyph.draw(pen)
    return bounds_pen.bounds
Esempio n. 25
0
 def draw(self, pen):
     if self.transform:
         pen = TransformPen(pen, self.transform)
     pb = PathBuilder()
     # xpath | doesn't seem to reliable work so just walk it
     for el in self.root.iter():
         pb.add_path_from_element(el)
     for path in pb.paths:
         parse_path(path, pen)
Esempio n. 26
0
def _colr_ufo(colr_version, ufo, color_glyphs):
    # Sort colors so the index into colors == index into CPAL palette.
    # We only store opaque colors in CPAL for CORLv1, as 'transparency' is
    # encoded separately.
    colors = sorted(
        set(c if colr_version == 0 else c.opaque()
            for c in chain.from_iterable(g.colors() for g in color_glyphs)))
    logging.debug("colors %s", colors)

    # KISS; use a single global palette
    ufo.lib[ufo2ft.constants.COLOR_PALETTES_KEY] = [[
        c.to_ufo_color() for c in colors
    ]]

    # We created glyph_name on the default layer for the base glyph
    # Now create glyph_name on layers 0..N-1 for the colored layers
    for color_glyph in color_glyphs:
        # For COLRv0, paint is just the palette index
        # For COLRv1, it's a data structure describing paint
        layer_to_paint = []
        svg_units_to_font_units = color_glyph.transform_for_font_space()
        for idx, (paint, path) in enumerate(color_glyph.as_painted_layers()):
            glyph_layer = _layer(ufo, idx)

            if colr_version == 0:
                # COLRv0: draw using the first available color on the glyph_layer
                # Results for gradients will be suboptimal :)
                color = next(paint.colors())
                layer_to_paint.append((glyph_layer.name, colors.index(color)))

            elif colr_version == 1:
                # COLRv0: fill in gradient paint structures
                layer_to_paint.append(
                    (glyph_layer.name, paint.to_ufo_paint(colors)))

            else:
                raise ValueError(f"Unsupported COLR version: {colr_version}")

            # we've got a colored layer, put a glyph on it
            glyph = glyph_layer.newGlyph(color_glyph.glyph_name)
            glyph.width = ufo.info.unitsPerEm

            pen = TransformPen(glyph.getPen(), svg_units_to_font_units)
            skia_path(path, color_glyph.nsvg.tolerance).draw(pen)

        # each base glyph contains a list of (layer.name, paint info) in z-order
        base_glyph = ufo.get(color_glyph.glyph_name)
        base_glyph.lib[
            ufo2ft.constants.COLOR_LAYER_MAPPING_KEY] = layer_to_paint

        # 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.
        pen = base_glyph.getPen()
        pen.moveTo((0, 0))
        pen.lineTo((ufo.info.unitsPerEm, ufo.info.unitsPerEm))
        pen.endPath()
Esempio n. 27
0
 def addComponent(self, baseGlyph, transformation):
     from fontTools.pens.transformPen import TransformPen
     if self.glyphSet is None:
         return
     if baseGlyph in self.glyphSet:
         glyph = self.glyphSet[baseGlyph]
         if glyph is None:
             return
         tPen = TransformPen(self, transformation)
         glyph.draw(tPen)
Esempio n. 28
0
 def _penAttr(self, attr):
     internalName = '_' + attr
     if internalName not in self.__dict__:
         Pen = globals()[attr + 'Pen']
         pen = transformer = Pen(glyphset=self._glyphset)
         if self._transform:
             transformer = TransformPen(pen, self._transform)
         self._glyph.draw(transformer)
         self.__dict__[internalName] = pen.value
     return self.__dict__[internalName]
Esempio n. 29
0
def deepCopyContours(glyphSet,
                     parent,
                     composite,
                     transformation,
                     specificComponents=None):
    """Copy contours from component to parent, including nested components.

    specificComponent: an optional list of glyph name strings. If not passed or
    None, decompose all components of a glyph unconditionally and completely. If
    passed, only completely decompose components whose baseGlyph is in the list.
    """

    for nestedComponent in composite.components:
        # Because this function works recursively, test at each turn if we are going to
        # recurse into a specificComponent. If so, set the specificComponents argument
        # to None so we unconditionally decompose the possibly nested component
        # completely.
        specificComponentsEffective = specificComponents
        if specificComponentsEffective:
            if nestedComponent.baseGlyph not in specificComponentsEffective:
                continue
            else:
                specificComponentsEffective = None

        try:
            nestedBaseGlyph = glyphSet[nestedComponent.baseGlyph]
        except KeyError:
            logger.warning(
                "dropping non-existent component '%s' in glyph '%s'",
                nestedComponent.baseGlyph,
                parent.name,
            )
        else:
            deepCopyContours(
                glyphSet,
                parent,
                nestedBaseGlyph,
                transformation.transform(nestedComponent.transformation),
                specificComponents=specificComponentsEffective,
            )

    # Check if there are any contours to copy before instantiating pens.
    if composite != parent and len(composite):
        if transformation == Identity:
            pen = parent.getPen()
        else:
            pen = TransformPen(parent.getPen(), transformation)
            # if the transformation has a negative determinant, it will
            # reverse the contour direction of the component
            xx, xy, yx, yy = transformation[:4]
            if xx * yy - xy * yx < 0:
                pen = ReverseContourPen(pen)

        for contour in composite:
            contour.draw(pen)
Esempio n. 30
0
def _draw_svg_path(ttfont, icon_name, dest_region):
    glyph_name = icon_font.resolve_ligature(ttfont, icon_name)
    upem = ttfont["head"].unitsPerEm
    transform = _map_font_to_viewbox(upem, dest_region)
    # TODO(rsheeter) should use better targeting :)
    # move up by 80% of upem to hit target box
    transform = transform.translate(0, 0.8 * upem)

    svg_pen = SVGPathPen(ttfont.getGlyphSet())
    ttfont.getGlyphSet()[glyph_name].draw(TransformPen(svg_pen, transform))
    return " ".join(svg_pen._commands)