Exemplo n.º 1
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)
Exemplo n.º 2
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
Exemplo n.º 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)
Exemplo n.º 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)
Exemplo n.º 5
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)
Exemplo n.º 6
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()
Exemplo n.º 7
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(" ")
Exemplo n.º 8
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
Exemplo n.º 9
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
Exemplo n.º 10
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()
Exemplo n.º 11
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)
Exemplo 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()
Exemplo n.º 13
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
Exemplo n.º 14
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)
Exemplo n.º 15
0
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
Exemplo n.º 16
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()
Exemplo n.º 17
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
Exemplo n.º 18
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)
Exemplo n.º 19
0
 def addComponent(self, glyphName, transformation):
     try:
         glyph = self.glyphSet[glyphName]
     except KeyError:
         return
     else:
         tPen = TransformPen(self.pen, transformation)
         glyph.draw(tPen)
Exemplo n.º 20
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)
Exemplo n.º 21
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
Exemplo n.º 22
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)
Exemplo n.º 23
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)
Exemplo n.º 24
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
Exemplo n.º 25
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
Exemplo n.º 26
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
Exemplo 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)
Exemplo 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]
Exemplo 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)
Exemplo 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)
Exemplo n.º 31
0
    def __init__(self, name, d, advwidth, advheight, transform=Identity):
        self.name = name

        advwidth *= abs(transform[0])
        advheight *= abs(transform[3])
        self.advwidth = advwidth
        self.advheight = advheight

        pen = T2CharStringPen(advwidth, None)
        tpen = TransformPen(pen, transform)
        parse_path(d, tpen)
        self.charstring = pen.getCharString()
Exemplo n.º 32
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