Example #1
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()
Example #2
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()
Example #3
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)
Example #4
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)
Example #5
0
def drawAICBOutlines(data, pen, fitInside=(None, None, None, None), fixedScale=None):
    """
    Draw outline data from an eps.
    Returns True if the data was drawn, False if not.

    data = the EPS data (string)
    pen = a drawing pen
    fitInside = the maximum size that the outline can be drawn into.
        the function will transform the eps outlines to fit within this box.
        if you don't want to transform the data, send it a box of (None, None, None, None).
        it is also possible to transform based on only horizontal OR vertical parameters.
        Simply send a box formatted like: (None, -250, None, 750) to base the transform
        on vertical dimensions or like: (-1000, None, 1000, None) to base the transform
        on horizontal dimensions.
    fixedScale = a set scale factor for transforming the outline. If the resulting scaled outline
        is larger than the fit rect, the outline will be centered on those parameters.
    """
    from fontTools.pens.transformPen import TransformPen
    from fontTools.misc.arrayTools import calcBounds
    data = '\n'.join(data.splitlines())
    ##
    ## get the point data
    # FL follows the EPSF3.0 spec, but AI seems
    # to be using a different spec. AI puts the outline data
    # in layers. we can get around this by iterating over all lines
    # in the data and drawing points as we find them.
    contours = []
    previousOnCurve = None
    for line in data.splitlines():
        movetoMatch = moveto_RE.match(line)
        if movetoMatch:
            contours.append([])
            x = float(movetoMatch.group(1))
            y = float(movetoMatch.group(2))
            contours[-1].append(('move', [(x, y)]))
            previousOnCurve = (x, y)
            continue
        linetoMatch = lineto_RE.match(line)
        if linetoMatch:
            x = float(linetoMatch.group(1))
            y = float(linetoMatch.group(2))
            contours[-1].append(('line', [(x, y)]))
            previousOnCurve = (x, y)
            continue
        startCurveToMatch = startCurveTo_RE.match(line)
        if startCurveToMatch:
            x1 = float(startCurveToMatch.group(1))
            y1 = float(startCurveToMatch.group(2))
            x2 = float(startCurveToMatch.group(3))
            y2 = float(startCurveToMatch.group(4))
            contours[-1].append(('curve', [previousOnCurve, (x1, y1), (x2, y2)]))
            previousOnCurve = (x2, y2)
            continue
        endCurveToMatch = endCurveTo_RE.match(line)
        if endCurveToMatch:
            x1 = float(endCurveToMatch.group(1))
            y1 = float(endCurveToMatch.group(2))
            x2 = float(endCurveToMatch.group(3))
            y2 = float(endCurveToMatch.group(4))
            contours[-1].append(('curve', [(x1, y1), (x2, y2), (x2, y2)]))
            previousOnCurve = (x2, y2)
            continue
        curvetoMatch = curveto_RE.match(line)
        if curvetoMatch:
            x1 = float(curvetoMatch.group(1))
            y1 = float(curvetoMatch.group(2))
            x2 = float(curvetoMatch.group(3))
            y2 = float(curvetoMatch.group(4))
            x3 = float(curvetoMatch.group(5))
            y3 = float(curvetoMatch.group(6))
            contours[-1].append(('curve', [(x1, y1), (x2, y2), (x3, y3)]))
            previousOnCurve = (x3, y3)
            continue
    # no outline data. give up.
    if not contours:
        return False
    ## get the bounding box
    boundingBox = boundingBox_RE.findall(data)
    if boundingBox:
        # rudely assume that there is only one EPS level bounding box
        # (the spec says that it should be that way)
        boundingBox = [
                int(i.split('.')[0])    # FL writes floats in the bounding box
                for i in boundingBox[0].split(' ')
                ]
    # the EPS does not have a bounding box
    # or the EPS has a stated box of (0, 0, 0, 0)
    # (which AI seems to do for open paths!)
    # so, we get the bounds from a points array
    if not boundingBox or boundingBox == [0, 0, 0, 0]:
        points = []
        for contour in contours:
            for tp, pts in contour:
                points.extend(pts)
        boundingBox = calcBounds(points)
    ##
    ## determine if the outlines need to be transformed
    ## and set up the transformation pen.
    transform = _getRectTransform(fitInside, boundingBox, fixedScale)
    transformPen = TransformPen(pen, transform)
    ##
    ## finally, draw the points
    for contour in contours:
        haveClosedPath = False
        if len(contour) > 1:
            # filter out overlapping points at the
            # start and the end of the contour
            start = contour[0]
            end = contour[-1]
            if end[0] == 'line':
                startPoints = start[1]
                endPoints = end[1]
                if start[0] == end[0]:
                    contour = contour[:-1]
                    haveClosedPath = True
        for tp, pts in contour:
            if tp == 'move':
                transformPen.moveTo(pts[0])
            elif tp == 'line':
                transformPen.lineTo(pts[0])
            elif tp == 'curve':
                pt1, pt2, pt3 = pts
                transformPen.curveTo(pt1, pt2, pt3)
        transformPen.closePath()
        # XXX
        #if haveClosedPath:
        #   transformPen.closePath()
        #else:
        #   transformPen.endPath()
    return True
Example #6
0
def drawAICBOutlines(data,
                     pen,
                     fitInside=(None, None, None, None),
                     fixedScale=None):
    """
    Draw outline data from an eps.
    Returns True if the data was drawn, False if not.

    data = the EPS data (string)
    pen = a drawing pen
    fitInside = the maximum size that the outline can be drawn into.
        the function will transform the eps outlines to fit within this box.
        if you don't want to transform the data, send it a box of (None, None, None, None).
        it is also possible to transform based on only horizontal OR vertical parameters.
        Simply send a box formatted like: (None, -250, None, 750) to base the transform
        on vertical dimensions or like: (-1000, None, 1000, None) to base the transform
        on horizontal dimensions.
    fixedScale = a set scale factor for transforming the outline. If the resulting scaled outline
        is larger than the fit rect, the outline will be centered on those parameters.
    """
    from fontTools.pens.transformPen import TransformPen
    from fontTools.misc.arrayTools import calcBounds
    data = '\n'.join(data.splitlines())
    ##
    ## get the point data
    # FL follows the EPSF3.0 spec, but AI seems
    # to be using a different spec. AI puts the outline data
    # in layers. we can get around this by iterating over all lines
    # in the data and drawing points as we find them.
    contours = []
    previousOnCurve = None
    for line in data.splitlines():
        movetoMatch = moveto_RE.match(line)
        if movetoMatch:
            contours.append([])
            x = float(movetoMatch.group(1))
            y = float(movetoMatch.group(2))
            contours[-1].append(('move', [(x, y)]))
            previousOnCurve = (x, y)
            continue
        linetoMatch = lineto_RE.match(line)
        if linetoMatch:
            x = float(linetoMatch.group(1))
            y = float(linetoMatch.group(2))
            contours[-1].append(('line', [(x, y)]))
            previousOnCurve = (x, y)
            continue
        startCurveToMatch = startCurveTo_RE.match(line)
        if startCurveToMatch:
            x1 = float(startCurveToMatch.group(1))
            y1 = float(startCurveToMatch.group(2))
            x2 = float(startCurveToMatch.group(3))
            y2 = float(startCurveToMatch.group(4))
            contours[-1].append(
                ('curve', [previousOnCurve, (x1, y1), (x2, y2)]))
            previousOnCurve = (x2, y2)
            continue
        endCurveToMatch = endCurveTo_RE.match(line)
        if endCurveToMatch:
            x1 = float(endCurveToMatch.group(1))
            y1 = float(endCurveToMatch.group(2))
            x2 = float(endCurveToMatch.group(3))
            y2 = float(endCurveToMatch.group(4))
            contours[-1].append(('curve', [(x1, y1), (x2, y2), (x2, y2)]))
            previousOnCurve = (x2, y2)
            continue
        curvetoMatch = curveto_RE.match(line)
        if curvetoMatch:
            x1 = float(curvetoMatch.group(1))
            y1 = float(curvetoMatch.group(2))
            x2 = float(curvetoMatch.group(3))
            y2 = float(curvetoMatch.group(4))
            x3 = float(curvetoMatch.group(5))
            y3 = float(curvetoMatch.group(6))
            contours[-1].append(('curve', [(x1, y1), (x2, y2), (x3, y3)]))
            previousOnCurve = (x3, y3)
            continue
    # no outline data. give up.
    if not contours:
        return False
    ## get the bounding box
    boundingBox = boundingBox_RE.findall(data)
    if boundingBox:
        # rudely assume that there is only one EPS level bounding box
        # (the spec says that it should be that way)
        boundingBox = [
            int(i.split('.')[0])  # FL writes floats in the bounding box
            for i in boundingBox[0].split(' ')
        ]
    # the EPS does not have a bounding box
    # or the EPS has a stated box of (0, 0, 0, 0)
    # (which AI seems to do for open paths!)
    # so, we get the bounds from a points array
    if not boundingBox or boundingBox == [0, 0, 0, 0]:
        points = []
        for contour in contours:
            for tp, pts in contour:
                points.extend(pts)
        boundingBox = calcBounds(points)
    ##
    ## determine if the outlines need to be transformed
    ## and set up the transformation pen.
    transform = _getRectTransform(fitInside, boundingBox, fixedScale)
    transformPen = TransformPen(pen, transform)
    ##
    ## finally, draw the points
    for contour in contours:
        haveClosedPath = False
        if len(contour) > 1:
            # filter out overlapping points at the
            # start and the end of the contour
            start = contour[0]
            end = contour[-1]
            if end[0] == 'line':
                startPoints = start[1]
                endPoints = end[1]
                if start[0] == end[0]:
                    contour = contour[:-1]
                    haveClosedPath = True
        for tp, pts in contour:
            if tp == 'move':
                transformPen.moveTo(pts[0])
            elif tp == 'line':
                transformPen.lineTo(pts[0])
            elif tp == 'curve':
                pt1, pt2, pt3 = pts
                transformPen.curveTo(pt1, pt2, pt3)
        transformPen.closePath()
        # XXX
        #if haveClosedPath:
        #   transformPen.closePath()
        #else:
        #   transformPen.endPath()
    return True