def test_surface(): # TypeError: The Surface type cannot be instantiated test.raises(TypeError, "s = cairo.Surface()") if cairo.HAS_IMAGE_SURFACE: f, w, h = cairo.FORMAT_ARGB32, 100, 100 s = cairo.ImageSurface(f, w, h) assert s.get_format() == f assert s.get_width() == w assert s.get_height() == h if cairo.HAS_PDF_SURFACE: f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100 s = cairo.PDFSurface(f, w, h) if cairo.HAS_PS_SURFACE: f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100 s = cairo.PSSurface(f, w, h) if cairo.HAS_RECORDING_SURFACE: s = cairo.RecordingSurface(cairo.CONTENT_COLOR, None) s = cairo.RecordingSurface(cairo.CONTENT_COLOR, (1, 1, 10, 10)) if cairo.HAS_SVG_SURFACE: f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100 s = cairo.SVGSurface(f, w, h)
def test_recording_surface(): with pytest.raises(TypeError): cairo.RecordingSurface(cairo.CONTENT_COLOR, object()) with pytest.raises(TypeError): cairo.RecordingSurface() surface = cairo.RecordingSurface(cairo.CONTENT_COLOR, None) assert surface.ink_extents() == (0.0, 0.0, 0.0, 0.0)
def test_recording_surface_get_extents(): surface = cairo.RecordingSurface(cairo.CONTENT_COLOR, None) assert surface.get_extents() is None surface = cairo.RecordingSurface(cairo.CONTENT_COLOR, (1, 2, 3, 4)) assert surface.get_extents() == (1, 2, 3, 4) surface = cairo.RecordingSurface(cairo.CONTENT_COLOR, (1, 2, 3, 4)) surface.finish() assert surface.get_extents() == (1, 2, 3, 4)
def update_bounding_box(self, items: Collection[Item]) -> None: """Update the bounding boxes of the model items for this view, in model coordinates.""" painter = self._bounding_box_painter qtree = self._qtree c2v = self._matrix for item in items: surface = cairo.RecordingSurface(cairo.Content.COLOR_ALPHA, None) # type: ignore[arg-type] cr = cairo.Context(surface) painter.paint_item(item, cr) x, y, w, h = surface.ink_extents() vx, vy = c2v.transform_point(x, y) vw, vh = c2v.transform_distance(w, h) qtree.add(item=item, bounds=(vx, vy, vw, vh), data=(x, y, w, h)) if self._matrix_changed and self._model: for item in self._model.get_all_items(): if item not in items: bounds = self._qtree.get_data(item) x, y = c2v.transform_point(bounds[0], bounds[1]) w, h = c2v.transform_distance(bounds[2], bounds[3]) qtree.add(item=item, bounds=(x, y, w, h), data=bounds) self._matrix_changed = False
def Resize(self): if self.recording: self.recording_surface = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, None) self.recording_ctx = cairo.Context(self.recording_surface) else: self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.canvas.viewport.x, self.canvas.viewport.y) self.ctx = cairo.Context(self.surface)
def draw(self, fobj: typing.BinaryIO, scale = 1, options = { }): import cairo options["scale"] = scale layers: typing.DefaultDict[int, cairo.RecordingSurface] = collections.defaultdict( lambda: cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, None)) for sname in options.get("structures", self.keys()): structure = self[sname] layers = structure._draw(self, layers, options) # for some reason we cant paint these recording surfaces onto another recording surface to # determine the bounds of the whole picture, so we need to do this manually draw_surfs = [layers[i] for i in options.get("order", self.layers)] if len(draw_surfs) == 0: return dims = np.array([surf.ink_extents() for surf in draw_surfs]) dims[:, 2:] += dims[:, :2] x0, y0 = np.min(dims[:, :2], axis = 0) x1, y1 = np.max(dims[:, 2:], axis = 0) w, h = x1 - x0, y1 - y0 scale = 1 with cairo.PDFSurface(fobj, scale * w, scale * h) as surf: ctx = cairo.Context(surf) ctx.translate(0, scale * h) ctx.scale(scale, -scale) for layer_surf in draw_surfs: ctx.set_source_surface(layer_surf, -x0, -y0) ctx.paint()
def surface(self, flap_w: float, flap_halfh: float) -> cairo.RecordingSurface: # Create cairo Surface to write to rect = cairo.Rectangle(0, 0, int(mm_to_pt(flap_w)), int(mm_to_pt(flap_halfh * 2))) sfc = cairo.RecordingSurface(cairo.Content.COLOR_ALPHA, rect) # Prepare ctx = cairo.Context(sfc) ctx.scale(mm_to_pt(1), mm_to_pt(1)) ctx.select_font_face(self.font) font_h = int(flap_halfh * 1.5) ctx.set_font_size(int(font_h * self.scale)) # Trial run to get the width of the drawn character ctx.move_to(0, int(flap_halfh + font_h / 2)) ctx.show_text(self.char) x, y, width, height = sfc.ink_extents() # Clear surface ctx.save() ctx.set_operator(cairo.Operator.CLEAR) ctx.paint() ctx.restore() # Draw for real ctx.move_to(int((flap_w - pt_to_mm(width)) / 2 - pt_to_mm(x)), int(flap_halfh + font_h / 2) + self.offset) ctx.show_text(self.char) return sfc
def bounding_box(self, items: Collection[Item], cr: CairoContext) -> Rectangle: """Get the unified bounding box of the rendered items.""" surface = cairo.RecordingSurface(cairo.Content.COLOR_ALPHA, None) # type: ignore[arg-type] cr = cairo.Context(surface) self.paint(items, cr) return Rectangle(*surface.ink_extents())
def get_bound_cairo(self): # Not as tight as it could be.... ? import cairo surface = cairo.RecordingSurface(cairo.Content.COLOR_ALPHA, None) cxt = cairo.Context(surface) self.process_cairo(cxt) extents = surface.ink_extents() (ulx, uly, width, height) = extents llx, lly = ulx, -uly - height urx, ury = llx + width, lly + height return Bound(llx, lly, urx, ury)
def test_surface(): # TypeError: The Surface type cannot be instantiated pytest.raises(TypeError, "s = cairo.Surface()") f, w, h = cairo.FORMAT_ARGB32, 100, 100 s = cairo.ImageSurface(f, w, h) assert s.get_format() == f assert s.get_width() == w assert s.get_height() == h f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100 s = cairo.PDFSurface(f, w, h) f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100 s = cairo.PSSurface(f, w, h) s = cairo.RecordingSurface(cairo.CONTENT_COLOR, None) s = cairo.RecordingSurface(cairo.CONTENT_COLOR, (1, 1, 10, 10)) f, w, h = tfi.TemporaryFile(mode='w+b'), 100, 100 s = cairo.SVGSurface(f, w, h)
def _vertical_lines_pattern(color: Color): import cairo pat = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, cairo.Rectangle(0, 0, 3, 100)) ctx = cairo.Context(pat) ctx.set_line_width(.4) ctx.move_to(.5, 0) ctx.line_to(.5, 100) ctx.set_source_rgba(*color) ctx.stroke() return pat
def make_pattern(col): pattern_surface = \ cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (0, 0, 32, 4)) ctx = cairo.Context(pattern_surface) ctx.set_line_width(4) ctx.set_source_rgb(col[0],col[1],col[2]) ctx.move_to(0, 0) ctx.line_to(32, 0) ctx.stroke() pattern = cairo.SurfacePattern(pattern_surface) pattern.set_extend(cairo.EXTEND_REPEAT) return pattern
def text_extents_cairo(text): import cairo #surface = cairo.PDFSurface("/dev/null", 0, 0) # only in cairo 1.11.0: surface = cairo.RecordingSurface(cairo.Content.COLOR_ALPHA, None) cxt = cairo.Context(surface) #cxt.move_to(0., 0.) #cxt.show_text(text) #extents = surface.ink_extents() #(ulx, uly, width, height) = extents #print("get_bound", extents) # same as text_extents ... ex = cxt.text_extents(text) return ex
def make_pattern_circles(col): pattern_surface = \ cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (0, 0, 6, 6)) ctx = cairo.Context(pattern_surface) #ctx.set_line_width(4) ctx.set_source_rgb(col[0], col[1], col[2]) ctx.move_to(0, 0) ctx.arc(3, 3, 2.7, 0 * DEGREES, 360 * DEGREES) ctx.close_path() ctx.fill() pattern = cairo.SurfacePattern(pattern_surface) pattern.set_extend(cairo.EXTEND_REPEAT) return pattern
def north_west_lines_pattern(color: Color): import cairo pat = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, cairo.Rectangle(0, 0, 3, 3)) ctx = cairo.Context(pat) ctx.set_line_width(.4) ctx.move_to(-.2, 1.7) ctx.line_to(1.7, -.2) ctx.move_to(1.3, 3.2) ctx.line_to(3.2, 1.3) ctx.set_source_rgba(*color) ctx.stroke() return pat
def main(chute, args): pattern, size = chute.get_pattern() surface = cairo.RecordingSurface( cairo.CONTENT_COLOR_ALPHA, cairo.Rectangle(0, 0, util.mm_to_pt(size[0]), util.mm_to_pt(size[1]))) ctx = cairo.Context(surface) ctx.push_group() ctx.set_source(pattern) ctx.paint() pattern2 = ctx.pop_group() if args.paper_size: if args.typ == "svg": print( "ERROR: svg does not support multiple pages. Don't specify a paper size if you want to export svg file" ) if args.paper_size not in PAPER_SIZES.keys(): print(args.paper_size) print("Known Paper Sizes:") print(PAPER_SIZES) return tiler = CairoTiler(pattern2, size, overlap=10, paper_size=PAPER_SIZES[args.paper_size]) tiler.tile(args.output) else: if args.typ == "svg": surface = cairo.SVGSurface(args.output, util.mm_to_pt(size[0]), util.mm_to_pt(size[1])) elif args.typ == "pdf": surface = cairo.PDFSurface(args.output, util.mm_to_pt(size[0]), util.mm_to_pt(size[1])) ctx = cairo.Context(surface) ctx.set_source(pattern2) ctx.paint()
def test_from_recording_surface(): s = cairo.RecordingSurface(cairo.CONTENT_COLOR, None) ctx = cairo.Context(s) ctx.paint() f = io.BytesIO() dev = cairo.ScriptDevice(f) dev.from_recording_surface(s) dev.flush() assert b"paint" in f.getvalue() # already finished dev.finish() with pytest.raises(cairo.Error): dev.from_recording_surface(s) # only recording surfaces allowed image = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10) with pytest.raises(TypeError): dev.from_recording_surface(image) # No None allowed with pytest.raises(TypeError): dev.from_recording_surface(None)
def Render(self, obj, width, height): # create drawing surface = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, None) self.ctx = cairo.Context (surface) # draw all objects for node in obj.nodes: node.Render(self) s_x, s_y, s_width, s_height = surface.ink_extents() # create image with whole drawing content WIDTH, HEIGHT = 256, 256 img_surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, width, height) img_ctx = cairo.Context (img_surface) ratio = s_width decx = 0 decy = math.fabs(s_width-s_height)/2 if s_height>s_width: ratio = s_height decx = math.fabs(s_width-s_height)/2 decy = 0 if ratio>0: img_ctx.scale (WIDTH/ratio, HEIGHT/ratio) # Normalizing the canvas # draw background img_ctx.rectangle(0, 0, WIDTH*ratio, HEIGHT*ratio) img_ctx.set_source_rgb(self.background.r, self.background.g, self.background.b) img_ctx.fill() img_ctx.set_source_surface(surface, -s_x+decx, -s_y+decy) img_ctx.paint() return img_surface
from typing import List
def test_recording_surface(): surface = cairo.RecordingSurface(cairo.CONTENT_COLOR, cairo.Rectangle(1, 1, 10, 10)) assert isinstance(surface.get_extents(), cairo.Rectangle)
def __new__(cls, *args, **kwargs): import cairo return cairo.RecordingSurface(*args, **kwargs)
def main(): parser = argparse.ArgumentParser( description='Add ToUnicode tables to PDF files.') parser.add_argument('--outdir', default='tmp/sfd', type=str, help='Output .sfd files to this directory') parser.add_argument('pdfs', type=str, nargs='+', help='PDF files to process') args = parser.parse_args() fontnum = 0 for pdf in args.pdfs: print("Adding ToUnicode tables to PDF file {}".format(pdf)) with open(pdf, 'rb') as fobj: pdfdata = fobj.read() doc = PdfReader(fdata=pdfdata) doc.read_all() fonts = [ o for o in doc.indirect_objects.values() if hasattr(o, 'Type') and o.Type == '/Font' ] fonts = { font.FontDescriptor.FontName[1:]: font for font in fonts if font.FontDescriptor is not None } embedded_fonts = fontforge.fontsInFile(pdf) for fontname in embedded_fonts: if fontname not in fonts: print( "WARNING: font {} not found in pdf file".format(fontname)) continue print("Adding ToUnicode table to font {}".format(fontname)) font = fontforge.open('{}({})'.format(pdf, fontname)) fonts[fontname].ToUnicode = PdfDict() fonts[fontname].ToUnicode.stream = generate_tounicode( font, fonts[fontname]) # Need to save the modified font because fontforge won't read # ToUnicode when it converts to woff later. font.fontname = 'pretex{:06d}'.format(fontnum) font.save( os.path.join( args.outdir, '[{}]{}.sfd'.format(os.path.basename(pdf)[:-4], fontname))) fontnum += 1 PdfWriter(pdf, trailer=doc).write() # Measure extents for displayed equations pdfpath = os.path.realpath(os.path.dirname(pdf)) doc = poppler.document_new_from_file( 'file://{}'.format(os.path.realpath(pdf)), None) boxsize = os.path.join(pdfpath, 'boxsize.txt') with open(boxsize) as fobj: lines = fobj.readlines() with open(boxsize, 'w') as fobj: pageno = 0 for line in lines: if not (line.startswith('inline:') or line.startswith('display:')): fobj.write(line) continue pageno += 1 if not line.startswith('display:'): fobj.write(line) continue page = doc.get_page(pageno - 1) width, height = page.get_size() surf = cairo.RecordingSurface( cairo.Content.COLOR_ALPHA, cairo.Rectangle(0, 0, width, height)) ctx = cairo.Context(surf) page.render_for_printing(ctx) x, y, w, h = surf.ink_extents() fobj.write(line.strip() + '{},{},{},{}\n'.format(x, y, w, h))
def cairo_surf( tree: libginger.Tree, colour: ty.Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0), line_width: float = 1.0, font_size: int = 20, token_node_distance: int = 20, node_part_margin: int = None, label_shift: int = 5, arrow_shift: int = 6, energy: float = 0.5, ) -> cairo.RecordingSurface: r""" Render a tree in a cairo recording surface. ## Parameters - `colour` the colour of the drawing as an RGBA vector in $[0, 1]⁴$ - `line_width` the width of the lines, obviously - `font_size` the font size used - `token_node_distance` the horizontal spacing between two nodes - `node_part_margin` the vertical spacing between node attributes (default: `$⌈\texttt{token\_node\_distance`/3}⌉$`) - `label_shift` the space between arrow and their labels - `arrow_shift` the horizontal padding between arrows of opposite directions - `energy` the magnitude of the tangent at the origin of an arc etween two nodes is $E×d$ where $E$ is the energy and $d$ the distance between those nodes. Increase this to make the arcs go higher. """ if cairo is None: raise NotImplementedError if node_part_margin is None: node_part_margin = math.ceil(token_node_distance / 3) res = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, None) context = cairo.Context(res) context.set_font_size(font_size) # For every token, we need to take account the width of the largest of the stacked attributes : # `form`, `lemma`, `upostag` This dict associate every node to its Rect node_rects = {} # type: ty.Dict[libginger.Node, Rect] current_x = 0 for n in tree.word_sequence: parts_extents = [ context.text_extents(s if s is not None else "_") for s in (n.form, n.lemma, n.upostag) ] w = max(e[2] for e in parts_extents) h = sum(e[3] for e in parts_extents) + 3 * node_part_margin node_rects[n] = Rect(current_x, 0, w, h) current_x += w + token_node_distance # Normalise the height of the nodes to the largest part_height = math.ceil(max(h for _, _, _, h in node_rects.values()) / 3) nodes_height = 3 * part_height # And take into account in node rects node_rects = { n: Rect(x, y, w, nodes_height) for n, (x, y, w, h) in node_rects.items() } # Now draw context.set_source_rgba(*colour) context.set_line_width(line_width) # First draw the nodes context.move_to(0, 0) for node, (x, y, w, h) in ((n, node_rects[n]) for n in tree.word_sequence): parts = ( s if s is not None else "_" for s in (node.form, node.lemma, node.upostag) ) for i, p in enumerate(parts, start=1): margin = math.floor((w - context.text_extents(p)[2]) / 2) context.move_to(x + margin, y + i * part_height) context.show_text(p) context.stroke() # Find out the largest arc height # First, get the relations deps = [ (node.head, node, node.deprel) for node in tree.word_sequence if node.head is not tree.root ] # Now draw the arcs arrowhead_size = font_size / 3 for head, foot, tag in deps: # Arc head_rect, foot_rect = node_rects[head], node_rects[foot] start = Point( head_rect.x + head_rect.w / 2 + (-arrow_shift if foot.identifier < head.identifier else arrow_shift), head_rect.y, ) end = Point(foot_rect.x + foot_rect.w / 2, foot_rect.y - arrowhead_size) origin_speed = math.floor(abs(end.x - start.x) * energy) control1 = (start.x, start.y - origin_speed) control2 = (end.x, end.y - origin_speed) context.move_to(*start) context.curve_to(*control1, *control2, *end) arrow_extents = context.path_extents() context.stroke() arrowhead(context, arrowhead_size, end) tag_w = context.text_extents(tag)[2] context.move_to( (arrow_extents[0] + arrow_extents[2] - tag_w) / 2, arrow_extents[1] - label_shift, ) context.show_text(tag) # Root arrow head_rect = node_rects[ next(node for node in tree.word_sequence if node.head is tree.root) ] root_x = head_rect.x + head_rect.w / 2 _, root_y, *_ = res.ink_extents() root_w = context.text_extents("root")[2] - label_shift context.move_to(root_x - root_w / 2, root_y) context.show_text("root") context.move_to(root_x, root_y) context.line_to(root_x, head_rect.y) context.stroke() arrowhead(context, arrowhead_size, Point(root_x, head_rect.y)) return res
def create_surface(zoom=0): surface = cairo.RecordingSurface( cairo.Content.COLOR_ALPHA, cairo.Rectangle(0, 0, Renderer.BASE_WIDTH, Renderer.BASE_HEIGHT)) return surface
def process(pcbfile, side=pcbnew.F_Cu, netlist=None, output=None): refs = None # load netlist if netlist is not None: net = kicad_netlist_reader.netlist(netlist) refs = [x.getRef() for x in net.getInterestingComponents()] # build BOM print("Loading %s" % pcbfile) pcb = pcbnew.LoadBoard(pcbfile) bom_table = generate_bom(pcb, filter_layer=side, filter_refs=refs) footprints = load_footprints(pcb, layer=side) board_xmin, board_ymin, board_width, board_height = get_bbox(pcb) margin = Point(5, 15) text_margin = Point(5, 2) # Render all components into a base surface for performance base = cairo.RecordingSurface(cairo.Content.COLOR_ALPHA, None) ctx = cairo.Context(base) # Render Footprints render_footprints(pcb, ctx, footprints, BASE_STYLE) # Show Board Edge for context ctx.set_source_rgb(0, 0, 0) ctx.set_line_width(0.25) ctx.rectangle(board_xmin, board_ymin, board_width, board_height) ctx.stroke() # for each part group, print page to PDF fname_out = output if output is None: sidename = "top" if side == pcbnew.F_Cu else "bottom" fname_out = os.path.splitext( pcbfile)[0] + "_place_" + sidename + ".pdf" with cairo.PDFSurface(fname_out, 72, 72) as pdf: for i, bom_row in enumerate(bom_table): print("Plotting page (%d/%d)" % (i + 1, len(bom_table))) pdf.set_size((board_width + 2 * margin.x) * POINTS_PER_MM, (board_height + 2 * margin.y) * POINTS_PER_MM) ctx = pangocairo.CairoContext(cairo.Context(pdf)) # Scale from points to mm ctx.scale(POINTS_PER_MM, POINTS_PER_MM) # Render Text render_text( ctx, text_margin.x, margin.y - text_margin.y, board_width + 2 * margin.x - 2 * text_margin.x, "%dx %s, %s" % (len(bom_row.refs), bom_row.value, bom_row.footprint), align_bottom=True) render_text( ctx, text_margin.x, board_height + margin.y + text_margin.y, board_width + 2 * margin.x - 2 * text_margin.x, ", ".join( bom_row.refs)) # Offset within page for margins+header ctx.translate(margin.x, margin.y) # Set corner of board on kicad page to 0,0 ctx.translate(-board_xmin, -board_ymin) ctx.set_source_surface(base, 0.0, 0.0) ctx.paint() render_footprints( pcb, ctx, footprints, HIGHLIGHT_STYLE, include_only=bom_row.refs) pdf.show_page() print("Output written to %s" % fname_out)
def get_pattern(self): pattern_lines = self._get_pattern_path() line_right = spg.LineString(pattern_lines["right"]) line_top = spg.LineString(pattern_lines["top"]) line_left = spg.LineString(pattern_lines["left"]) line_bottom = spg.LineString(pattern_lines["bottom"]) coords = list() coords.extend(line_right.coords) coords.extend(line_top.coords) coords.extend(line_left.coords) coords.extend(line_bottom.coords) polygon = spg.Polygon(coords) ui, li = polygon.exterior.xy ui = np.array(ui) li = np.array(li) if self.joint_style == MitreType.none: if self.seam_allowance[0] > 0: polygon = self.add_seamallowance(polygon, line_right, self.seam_allowance[0]) if self.seam_allowance[1] > 0: polygon = self.add_seamallowance(polygon, line_top, self.seam_allowance[1]) if self.seam_allowance[2] > 0: polygon = self.add_seamallowance(polygon, line_left, self.seam_allowance[2]) if self.seam_allowance[3] > 0: polygon = self.add_seamallowance(polygon, line_bottom, self.seam_allowance[3]) elif len(set(self.seam_allowance)) == 1: if self.joint_style == MitreType.miter: jt = JOIN_STYLE.mitre elif self.joint_style == MitreType.bevel: jt = JOIN_STYLE.bevel elif self.joint_style == MitreType.round: jt = JOIN_STYLE.round polygon = polygon.buffer(self.seam_allowance[0], join_style=jt, mitre_limit=2) elif self.joint_style == MitreType.bevel: polygon = self.add_seamallowance_bevel( [line_right, line_top, line_left, line_bottom], self.seam_allowance) elif self.joint_style == MitreType.round: print( "ERROR: joint style \"round\" not supported for non uniform seam allowance. Please use bevel or none" ) elif self.joint_style == MitreType.miter: print( "ERROR: joint style \"miter\" not supported for non uniform seam allowance. Please use bevel or none" ) u, l = polygon.exterior.xy u = np.array(u) l = np.array(l) margins = (10, 10, 10, 10) l = l * -1 li = li * -1 pattern_extend = (np.min(u), np.min(l), np.max(u), np.max(l)) pattern_width = pattern_extend[2] - pattern_extend[0] pattern_height = pattern_extend[3] - pattern_extend[1] document_width = pattern_width + margins[0] + margins[1] document_height = pattern_height + margins[2] + margins[3] surface = cairo.RecordingSurface( cairo.CONTENT_COLOR_ALPHA, cairo.Rectangle(0, 0, mm_to_pt(document_width), mm_to_pt(document_height))) ctx = cairo.Context(surface) ctx.save() ctx.set_line_join(cairo.LINE_JOIN_ROUND) ctx.set_line_cap(cairo.LINE_CAP_ROUND) ctx.push_group() ctx.scale(mm_to_pt(1), mm_to_pt(1)) ctx.save() if self.grid: draw_grid(ctx, (0, 0), 10, 1, document_height, document_width) #draw seam allowance ctx.translate( -pattern_extend[0] + (document_width / 2 - pattern_width / 2), -pattern_extend[1] + (document_height / 2 - pattern_height / 2)) ctx.move_to(u[0], l[0]) for x, y in zip(u, l): ctx.line_to(x, y) ctx.close_path() ctx.set_source_rgb(.0, .0, .0) ctx.set_line_width(0.3) ctx.stroke() #draw pattern ctx.move_to(ui[0], li[0]) for x, y in zip(ui, li): ctx.line_to(x, y) ctx.close_path() ctx.set_source_rgb(1, .0, .0) ctx.set_line_width(0.3) ctx.stroke() ctx.restore() pattern = ctx.pop_group() return (pattern, (document_width, document_height))