def _init_state(): global surface surface = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, None) _state["ctx"] = cairo.Context(surface) _state["color"] = (0, 0, 0, 1) _state["depth"] = 0 _state["cnt_elements"] = 0
def path(self): if not self._pang_ctx: self._pre_render() # here we create a new cairo.Context in order to hold the pathdata tempCairoContext = cairo.Context(cairo.RecordingSurface(cairo.CONTENT_ALPHA, None)) if GI: tempCairoContext = _UNSAFE_cairocffi_context_to_pycairo(tempCairoContext) tempCairoContext.move_to(self.x, self.y - self.baseline) # in here we create a pangoCairoContext in order to display layout on it # supposedly showlayout should work, but it fills the path instead, # therefore we use layout_path instead to render the layout to pangoCairoContext # tempCairoContext.show_layout(self.layout) PangoCairo.layout_path(tempCairoContext, self.layout) # here we extract the path from the temporal cairo.Context we used to draw on the previous step pathdata = tempCairoContext.copy_path() # creates a BezierPath instance for storing new shoebot path p = BezierPath(self._bot) # parsing of cairo path to build a shoebot path for item in pathdata: cmd = item[0] args = item[1] if cmd == PATH_MOVE_TO: p.moveto(*args) elif cmd == PATH_LINE_TO: p.lineto(*args) elif cmd == PATH_CURVE_TO: p.curveto(*args) elif cmd == PATH_CLOSE_PATH: p.closepath() # cairo function for freeing path memory return p
def _pre_render(self): # we use a new CairoContext to pre render the text rs = cairo.RecordingSurface(cairo.CONTENT_ALPHA, None) cr = cairo.Context(rs) if GI: cr = _UNSAFE_cairocffi_context_to_pycairo(cr) self._pang_ctx = PangoCairo.create_context(cr) self.layout = PangoCairo.create_layout(cr) # layout line spacing # TODO: the behaviour is not the same as nodebox yet # self.layout.set_spacing(int(((self._lineheight-1)*self._fontsize)*Pango.SCALE)) #pango requires an int casting # we pass pango font description and the text to the pango layout self.layout.set_font_description(self._fontface) self.layout.set_text(self.text, -1) # check if max text width is set and pass it to pango layout # text will wrap, meanwhile it checks if and indent has to be applied # indent is subordinated to width because it makes no sense on a single-line text block if self.width: self.layout.set_width(int(self.width) * Pango.SCALE) if self._indent: self.layout.set_indent(self._indent * Pango.SCALE) # set text alignment if self._align == "right": self.layout.set_alignment(Pango.Alignment.RIGHT) elif self._align == "center": self.layout.set_alignment(Pango.Alignment.CENTER) elif self._align == "justify": self.layout.set_alignment(Pango.Alignment.LEFT) self.layout.set_justify(True) else: self.layout.set_alignment(Pango.Alignment.LEFT)
def silt_pattern(): p_surface = \ cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (0, 0, 32, 8)) ctx = cairo.Context(p_surface) ctx.set_line_width(.5) ctx.set_line_cap(cairo.LINE_CAP_ROUND) r = 0.5 sc = 2 yoffs = 1 ctx.set_source_rgb(0, 0, 0) def dot(x, y): ctx.arc(x * sc, y * sc + yoffs, r, 0, 2 * pi) ctx.fill() def hz_line(x0, x1, y): ctx.move_to(x0 * sc, y * sc + yoffs) ctx.line_to(x1 * sc, y * sc + yoffs) ctx.stroke() hz_line(0.2, 7.8, 1) hz_line(8.2, 15.8, 3) for x in 10, 12, 14: dot(x, 1) for x in 2, 4, 6: dot(x, 3) pattern = cairo.SurfacePattern(p_surface) pattern.set_extend(cairo.EXTEND_REPEAT) return pattern
def _reset_surface(self): """This creates a fresh surface and cairo context.""" self.surface = cairocffi.RecordingSurface( cairocffi.CONTENT_COLOR_ALPHA, None, ) self.ctx = self.new_ctx()
def _reset_surface(self): if self.surface is not None: self.surface.finish() self.surface = cairocffi.RecordingSurface( cairocffi.CONTENT_COLOR_ALPHA, None, ) self.ctx = self.new_ctx()
def create_rcontext(self, size, frame): ''' Creates a recording surface for the bot to draw on ''' width, height = size if self.get_window() and not self.size: self.set_size_request(*size) self.size = size meta_surface = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (0, 0, width, height)) ctx = cairo.Context(meta_surface) return ctx
def burrow_pattern(): p_surface = \ cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (0, 0, 36, 36)) ctx = cairo.Context(p_surface) random.seed(17) ctx.set_source_rgb(0, 0, 0) for x in range(0, 4): for y in range(0, 2): burrow(ctx, 4.5 + x * 24 + random.random() * 8, 9 + y * 36 + random.random() * 8 + x * 8, 2) pattern = cairo.SurfacePattern(p_surface) pattern.set_extend(cairo.EXTEND_REPEAT) return pattern
def contains(self, x, y): ''' Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached. ''' if self._bounds: return self._bounds record_surface = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (-1, -1, 1, 1)) dummy_ctx = cairo.Context(record_surface) self._traverse(dummy_ctx) in_fill = dummy_ctx.in_fill(x, y) return in_fill
def _get_bounds(self): ''' Return cached bounds of this Grob. If bounds are not cached, render to a meta surface, and keep the meta surface and bounds cached. ''' if self._bounds: return self._bounds record_surface = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (-1, -1, 1, 1)) dummy_ctx = cairo.Context(record_surface) self._traverse(dummy_ctx) self._bounds = dummy_ctx.path_extents() return self._bounds
def sand_pattern(): p_surface = \ cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, (0, 0, 36, 36)) p_ctx = cairo.Context(p_surface) r = 0.5 wiggle = 3 sc = 3.6 offs = 1.8 random.seed(11) p_ctx.set_source_rgb(0, 0, 0) for x in range(0, 10): for y in range(0, 10): p_ctx.arc(offs + x * sc + random.random() * wiggle - wiggle / 2, offs + y * sc + random.random() * wiggle - wiggle / 2, r, 0, 2 * pi) p_ctx.fill() # for x in range(0,2): # for y in range(0,2): # burrow(p_ctx, 4.5+x*18+random.random()*8.-8./2., # 9+y*18+random.random()*8.-8./2., 2) pattern = cairo.SurfacePattern(p_surface) pattern.set_extend(cairo.EXTEND_REPEAT) return pattern
def textWithoutNewlinesToLines(self, text: str, equal_widths=True, hyphenate=True) -> List[Line]: if text.strip() == "": return [Line(0, self.params.line_height)] ans = [] layouts = [(word, self.createLayout(word)) for word in text.split(" ")] while layouts: surf = cairo.RecordingSurface(cairo.CONTENT_ALPHA, None) context = cairo.Context(surf) context.set_source_rgb(0, 0, 0) i = 0 il = [] wsum = 0 for word, layout in layouts: _, _, w, h = getLayoutExtent(layout) w, h = self._fixXY(w, h) if wsum + len( il ) * self.params.min_word_gap + w > self.params.line_width: if hyphenate and voikko and "-" not in word: syllables = re.split(r"-", voikko.hyphenate(word)) for j in range(len(syllables), 0, -1): text = "".join(syllables[0:j]) if len(stripMarkup(text)) <= 1: break new_l = self.createLayout(text + "-") _, _, new_w, new_h = getLayoutExtent(new_l) new_w, new_h = self._fixXY(new_w, new_h) if wsum + len( il ) * self.params.min_word_gap + new_w <= self.params.line_width: il.append(new_l) wsum += new_w layout.set_markup( fixMarkup("".join(syllables[j:]))) break word_gap = (self.params.line_width - wsum) / ( len(il) - 1) if len(il) > 1 else self.params.min_word_gap break il.append(layout) wsum += w i += 1 else: word_gap = self.params.min_word_gap if i == 0: _, _, w, h = getLayoutExtent(layouts[0][1]) w, h = self._fixXY(w, h) il.append(layouts[0][1]) wsum = w i += 1 x = 0 if self.params.text_align == "center": word_gap = self.params.min_word_gap x = (self.params.line_width - wsum - len(il) * self.params.min_word_gap) / 2 width = -word_gap height = self.params.line_height for l in il: context.translate(*self._fixXY(x, 0)) #pangocairo.update_context(context, l.get_context()) pangocairo.show_layout(context, l) context.translate(*self._fixXY(-x, 0)) _, _, w, h = getLayoutExtent(l) w, h = self._fixXY(w, h) x += word_gap + w width += word_gap + w if h > height: height = h if self.params.text_align == "center": width = self.params.line_width del layouts[:i] if height != self.params.line_height: print(f"Liian pitkä rivi: {height} {repr(text)}") ans.append(TextLine(surf, width, height, indent=self.params.indent)) # Aseta rivien leveystiedot yhdenmukaiseksi sarakealgoritmia varten if equal_widths: width = max(line.width for line in ans) for line in ans: line.width = width return ans
def _get_context(self): self._ctx = self._ctx or cairo.Context(cairo.RecordingSurface(cairo.CONTENT_ALPHA, None)) return self._ctx
def _create_surface(self, width, height): cairo_surface = cairocffi.RecordingSurface( cairocffi.CONTENT_COLOR_ALPHA, (0, 0, width, height)) return cairo_surface, width, height
import cairocffi as cairo recording = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, None) rcontext = cairo.Context(recording) with rcontext: rcontext.set_source_rgba(0, 0, 0, 0) rcontext.paint() rcontext.move_to(0, 14) rcontext.set_font_size(14) text = "*****@*****.**" rcontext.show_text(text) xoffset, yoffset, xsize, ysize = recording.ink_extents() print xoffset, yoffset, xsize, ysize surface = cairo.SVGSurface("content/extra/email_address.svg", xsize + xoffset * 2, ysize + yoffset * 2) context = cairo.Context(surface) context.set_source_surface(recording, 0, 0) context.paint()
def __init__(self, bot, path=None, x=0, y=0, width=None, height=None, alpha=1.0, data=None, pathmode=CORNER, **kwargs): Grob.__init__(self, bot) ColorMixin.__init__(self, **kwargs) self.x = x self.y = y self.width = width self.height = height self.alpha = alpha self.path = path self.data = data self._pathmode = pathmode sh = sw = None # Surface Height and Width if isinstance(self.data, cairo.ImageSurface): sw = self.data.get_width() sh = self.data.get_height() self._imagesurface = self.data else: # checks if image data is passed in command call, in this case it wraps # the data in a StringIO oject in order to use it as a file # the data itself must contain an entire image, not just pixel data # it can be useful for example to retrieve images from the web without # writing temp files (e.g. using nodebox's web library, see example 1 of the library) # if no data is passed the path is used to open a local file if self.data is None: surfaceref = self._surface_cache.get(path) if surfaceref: imagesurface = surfaceref.surface sw = imagesurface.get_width() sh = imagesurface.get_height() elif os.path.splitext(path)[1].lower() == '.svg' and rsvg is not None: handle = rsvg.Handle(path) sw, sh = handle.get_dimension_data()[:2] imagesurface = cairo.RecordingSurface(cairo.CONTENT_COLOR_ALPHA, 0, 0, sw, sh) ctx = cairo.Context(imagesurface) handle.render_cairo(ctx) elif os.path.splitext(path)[1].lower() == '.png': imagesurface = cairo.ImageSurface.create_from_png(path) sw = imagesurface.get_width() sh = imagesurface.get_height() else: img = PILImage.open(path) if img.mode != 'RGBA': img = img.convert("RGBA") sw, sh = img.size # Would be nice to not have to do some of these conversions :-\ bgra_data = img.tobytes('raw', 'BGRA', 0, 1) bgra_array = array.array('B', bgra_data) imagesurface = cairo.ImageSurface.create_for_data(bgra_array, cairo.FORMAT_ARGB32, sw, sh, sw * 4) self._surface_cache[path] = SurfaceRef(imagesurface) else: img = PILImage.open(StringIO(self.data)) if img.mode != 'RGBA': img = img.convert("RGBA") sw, sh = img.size # Would be nice to not have to do some of these conversions :-\ bgra_data = img.tobytes('raw', 'BGRA', 0, 1) bgra_array = array.array('B', bgra_data) imagesurface = cairo.ImageSurface.create_for_data(bgra_array, cairo.FORMAT_ARGB32, sw, sh, sw * 4) if width is not None or height is not None: if width: wscale = float(width) / sw else: wscale = 1.0 if height: hscale = float(height) / sh else: if width: hscale = wscale else: hscale = 1.0 self._transform.scale(wscale, hscale) self.width = width or sw self.height = height or sh self._imagesurface = imagesurface self._deferred_render()