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)
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
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)
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)
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)
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()
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 _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
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
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()
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)
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()
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
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)
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
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()
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
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)
def addComponent(self, glyphName, transformation): try: glyph = self.glyphSet[glyphName] except KeyError: return else: tPen = TransformPen(self.pen, transformation) glyph.draw(tPen)
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)
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
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)
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)
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
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
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
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)
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]
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)
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)
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()
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