Esempio n. 1
0
 def create_surface(self):
     bw, bh = self.size
     old_backing = self._backing
     #should we honour self.depth here?
     self._backing = None
     if bw==0 or bh==0:
         #this can happen during cleanup
         return None
     backing = ImageSurface(FORMAT_ARGB32, bw, bh)
     self._backing = backing
     cr = Context(backing)
     if self._alpha_enabled:
         cr.set_operator(OPERATOR_CLEAR)
         cr.set_source_rgba(1, 1, 1, 0)
     else:
         cr.set_operator(OPERATOR_SOURCE)
         cr.set_source_rgba(1, 1, 1, 1)
     cr.rectangle(0, 0, bw, bh)
     cr.fill()
     if COPY_OLD_BACKING and old_backing is not None:
         oldw, oldh = old_backing.get_width(), old_backing.get_height()
         sx, sy, dx, dy, w, h = self.gravity_copy_coords(oldw, oldh, bw, bh)
         cr.translate(dx-sx, dy-sy)
         cr.rectangle(sx, sy, w, h)
         cr.clip()
         cr.set_operator(OPERATOR_SOURCE)
         cr.set_source_surface(old_backing, 0, 0)
         cr.paint()
         backing.flush()
     return cr
Esempio n. 2
0
def svg_to_png(filename, icondata, w=128, h=128):
    if not SVG_TO_PNG:
        return None
    Rsvg = load_Rsvg()
    log("svg_to_png%s Rsvg=%s", (filename, "%i bytes" % len(icondata), w, h),
        Rsvg)
    if not Rsvg:
        return None
    try:
        from cairo import ImageSurface, Context, FORMAT_ARGB32  #pylint: disable=no-name-in-module, import-outside-toplevel
        #'\sinkscape:[a-zA-Z]*=["a-zA-Z0-9]*'
        img = ImageSurface(FORMAT_ARGB32, w, h)
        ctx = Context(img)
        handle = Rsvg.Handle.new_from_data(icondata)
        dim = handle.get_dimensions()
        ctx.scale(w / dim.width, h / dim.height)
        handle.render_cairo(ctx)
        del handle
        img.flush()
        buf = BytesIO()
        img.write_to_png(buf)
        icondata = buf.getvalue()
        buf.close()
        img.finish()
        return icondata
    except Exception:
        log("svg_to_png%s", (icondata, w, h), exc_info=True)
        if re.findall(INKSCAPE_RE, icondata):
            #try again after stripping the bogus inkscape attributes
            #as some rsvg versions can't handle that (ie: Debian Bullseye)
            icondata = re.sub(INKSCAPE_RE, b"", icondata)
            return svg_to_png(filename, icondata, w, h)
        if icondata.find(INKSCAPE_BROKEN_SODIPODI_DTD) > 0:
            icondata = icondata.replace(INKSCAPE_BROKEN_SODIPODI_DTD,
                                        INKSCAPE_SODIPODI_DTD)
            return svg_to_png(filename, icondata, w, h)
        log.error("Error: failed to convert svg icon")
        if filename:
            log.error(" '%s':", filename)
        log.error(" %i bytes, %s", len(icondata), ellipsizer(icondata))
        return None
Esempio n. 3
0
    def _to_png(self,
                font,
                font_position=None,
                dst=None,
                limit=800,
                size=1500,
                tab_width=1500,
                padding_characters=""):
        """Use HB, FreeType and Cairo to produce a png for a table.

        Parameters
        ----------
        font: DFont
        font_position: str
            Label indicating which font has been used. 
        dst: str
            Path to output image. If no path is given, return in-memory 
        """
        # TODO (M Foley) better packaging for pycairo, freetype-py
        # and uharfbuzz.
        # Users should be able to pip install these bindings without needing
        # to install the correct libs.

        # A special mention to the individuals who maintain these packages. Using
        # these dependencies has sped up the process of creating diff images
        # significantly. It's an incredible age we live in.
        y_tab = int(1500 / 25)
        x_tab = int(tab_width / 64)
        width, height = 1024, 200

        cells_per_row = int((width - x_tab) / x_tab)
        # Compute height of image
        x, y, baseline = x_tab, 0, 0
        for idx, row in enumerate(self._data[:limit]):
            x += x_tab

            if idx % cells_per_row == 0:
                y += y_tab
                x = x_tab
        height += y
        height += 100

        # draw image
        Z = ImageSurface(FORMAT_ARGB32, width, height)
        ctx = Context(Z)
        ctx.rectangle(0, 0, width, height)
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()

        # label image
        ctx.set_font_size(30)
        ctx.set_source_rgb(0.5, 0.5, 0.5)
        ctx.move_to(x_tab, 50)
        ctx.show_text("{}: {}".format(self.table_name, len(self._data)))
        ctx.move_to(x_tab, 100)
        if font_position:
            ctx.show_text("Font Set: {}".format(font_position))
        if len(self._data) > limit:
            ctx.set_font_size(20)
            ctx.move_to(x_tab, 150)
            ctx.show_text(
                "Warning: {} different items. Only showing most serious {}".
                format(len(self._data), limit))

        hb.ot_font_set_funcs(font.hbfont)

        # Draw glyphs
        x, y, baseline = x_tab, 200, 0
        x_pos = x_tab
        y_pos = 200
        for idx, row in enumerate(self._data[:limit]):
            string = "{}{}{}".format(padding_characters, row['string'],
                                     padding_characters)
            buf = self._shape_string(font, string, row['features'])
            char_info = buf.glyph_infos
            char_pos = buf.glyph_positions
            for info, pos in zip(char_info, char_pos):
                gid = info.codepoint
                font.ftfont.load_glyph(gid, flags=6)
                bitmap = font.ftslot.bitmap

                if bitmap.width > 0:
                    ctx.set_source_rgb(0, 0, 0)
                    glyph_surface = _make_image_surface(
                        font.ftfont.glyph.bitmap, copy=False)
                    ctx.set_source_surface(
                        glyph_surface,
                        x_pos + font.ftslot.bitmap_left + (pos.x_offset / 64.),
                        y_pos - font.ftslot.bitmap_top - (pos.y_offset / 64.))
                    glyph_surface.flush()
                    ctx.paint()
                x_pos += (pos.x_advance) / 64.
                y_pos += (pos.y_advance) / 64.

            x_pos += x_tab - (x_pos % x_tab)
            if idx % cells_per_row == 0:
                # add label
                if font_position:
                    ctx.set_source_rgb(0.5, 0.5, 0.5)
                    ctx.set_font_size(10)
                    ctx.move_to(width - 20, y_pos)
                    ctx.rotate(1.5708)
                    ctx.show_text(font_position)
                    ctx.set_source_rgb(0, 0, 0)
                    ctx.rotate(-1.5708)
                # Start a new row
                y_pos += y_tab
                x_pos = x_tab
        Z.flush()
        if dst:
            Z.write_to_png(dst)
        else:
            img = StringIO()
            Z.write_to_png(img)
            return Image.open(img)
def make_label(text, filename, size=12, angle=0):
    '''
    Parameters:
    -----------
    text : string
        Text to be displayed
    filename : string
        Path to a font
    size : int
        Font size in 1/64th points
    angle : float
        Text angle in degrees
    '''
    face = Face(filename)
    face.set_char_size( size*64 )
    # FT_Angle is a 16.16 fixed-point value expressed in degrees.
    angle = FT_Angle(angle * 65536)
    matrix  = FT_Matrix( FT_Cos( angle ),
                         - FT_Sin( angle ),
                         FT_Sin( angle ) ,
                         FT_Cos( angle ) )
    flags = FT_LOAD_RENDER
    pen = FT_Vector(0,0)
    FT_Set_Transform( face._FT_Face, byref(matrix), byref(pen) )
    previous = 0
    xmin, xmax = 0, 0
    ymin, ymax = 0, 0
    for c in text:
        face.load_char(c, flags)
        kerning = face.get_kerning(previous, c)
        previous = c
        bitmap = face.glyph.bitmap
        pitch  = face.glyph.bitmap.pitch
        width  = face.glyph.bitmap.width
        rows   = face.glyph.bitmap.rows
        top    = face.glyph.bitmap_top
        left   = face.glyph.bitmap_left
        pen.x += kerning.x
        x0 = (pen.x >> 6) + left
        x1 = x0 + width
        y0 = (pen.y >> 6) - (rows - top)
        y1 = y0 + rows
        xmin, xmax = min(xmin, x0),  max(xmax, x1)
        ymin, ymax = min(ymin, y0), max(ymax, y1)
        pen.x += face.glyph.advance.x
        pen.y += face.glyph.advance.y

    L = ImageSurface(FORMAT_A8, xmax-xmin, ymax-ymin)
    previous = 0
    pen.x, pen.y = 0, 0
    ctx = Context(L)
    for c in text:
        face.load_char(c, flags)
        kerning = face.get_kerning(previous, c)
        previous = c
        bitmap = face.glyph.bitmap
        pitch  = face.glyph.bitmap.pitch
        width  = face.glyph.bitmap.width
        rows   = face.glyph.bitmap.rows
        top    = face.glyph.bitmap_top
        left   = face.glyph.bitmap_left
        pen.x += kerning.x
        x = (pen.x >> 6) - xmin + left
        y = - (pen.y >> 6) + ymax - top
        if (width > 0):
            glyph_surface = make_image_surface(face.glyph.bitmap)
            ctx.set_source_surface(glyph_surface, x, y)
            ctx.paint()
        pen.x += face.glyph.advance.x
        pen.y += face.glyph.advance.y

    L.flush()
    return L
 angle = random.randint(-25,25)
 try:
     L = make_label('Hello', './Vera.ttf', size, angle=angle)
 except NotImplementedError:
     raise SystemExit("For python 3.x, you need pycairo >= 1.11+ (from https://github.com/pygobject/pycairo)")
 h = L.get_height()
 w = L.get_width()
 if h < H and w < W:
     x0 = W//2 + (random.uniform()-.1)*50
     y0 = H//2 + (random.uniform()-.1)*50
     for dx,dy in spiral():
         c = .25+.75*random.random()
         x = int(x0+dx)
         y = int(y0+dy)
         checked = False
         I.flush()
         if not (x <= w//2 or y <= h//2 or x >= (W-w//2) or y >= (H-h//2)):
             ndI = ndarray(shape=(h,w), buffer=I.get_data(), dtype=ubyte, order='C',
                           offset=(x-w//2) + I.get_stride() * (y-h//2),
                           strides=[I.get_stride(), 1])
             ndL = ndarray(shape=(h,w), buffer=L.get_data(), dtype=ubyte, order='C',
                           strides=[L.get_stride(), 1])
             if ((ndI * ndL).sum() == 0):
                checked = True
         new_region = RectangleInt(x-w//2, y-h//2, w, h)
         if  (checked or ( drawn_regions.contains_rectangle(new_region) == REGION_OVERLAP_OUT )):
             ctxI.set_source_surface(L, 0, 0)
             pattern = ctxI.get_source()
             scalematrix = Matrix()
             scalematrix.scale(1.0,1.0)
             scalematrix.translate(w//2 - x, h//2 - y)
Esempio n. 6
0
    Z = ImageSurface(FORMAT_A8, int(round(height+0.5)), int(round(width+0.5)))
ctx = Context(Z)

if (wantRotate):
    ctx.set_matrix(Matrix(xx=0.0,xy=-1.0,yx=1.0,yy=0.0,x0=height))
# Second pass for actual rendering
x, y = -sc(glyph_extents[0].x_bearing + positions[0].x_offset), height + sc(min_iy)
if (emulate_default):
    x = 16
    y = sc(font_extents.asscender) + 16
if (wantTTB):
    x = sc(max_ix)
    y = sc(max_iy)
for info,pos,extent in zip(infos, positions, glyph_extents):
    face.load_glyph(info.codepoint)
    x += sc(extent.x_bearing + pos.x_offset)
    y -= sc(extent.y_bearing + pos.y_offset)
    # cairo does not like zero-width bitmap from the space character!
    if (face.glyph.bitmap.width > 0):
        glyph_surface = make_image_surface(face.glyph.bitmap)
        ctx.set_source_surface(glyph_surface, x, y)
        ctx.paint()
    x += sc(pos.x_advance - extent.x_bearing - pos.x_offset)
    y -= sc(pos.y_advance - extent.y_bearing - pos.y_offset)
Z.flush()
Z.write_to_png("hb-view.png")
Image.open("hb-view.png").show()

### Derived from freetype-py:examples/hello-world-cairo.py ends.
#####################################################################
            [tags.pop() for x in range(len(segment) - 1)]
        VERTS.extend(verts)
        CODES.extend(codes)
        start = end+1

    ctx.new_path()
    ctx.set_source_rgba(1,1,0, 0.5)
    i = 0
    while (i < len(CODES)):
        if (CODES[i] == MOVETO):
            ctx.move_to(VERTS[i][0],VERTS[i][1])
            i += 1
        elif (CODES[i] == LINETO):
            ctx.line_to(VERTS[i][0],VERTS[i][1])
            i += 1
        elif (CODES[i] == CURVE3):
            ctx.curve_to(VERTS[i][0],VERTS[i][1],
                         VERTS[i+1][0],VERTS[i+1][1], # undocumented
                         VERTS[i+1][0],VERTS[i+1][1])
            i += 2
        elif (CODES[i] == CURVE4):
            ctx.curve_to(VERTS[i][0],VERTS[i][1],
                         VERTS[i+1][0],VERTS[i+1][1],
                         VERTS[i+2][0],VERTS[i+2][1])
            i += 3
    ctx.fill()

    surface.flush()
    surface.write_to_png("glyph-vector-cairo.png")
    Image.open("glyph-vector-cairo.png").show()
Esempio n. 8
0
 def paint_to_image(self, bw, bh, background, foreground, antialias=ANTIALIAS_NONE):
     img = ImageSurface(FORMAT_RGB24, bw, bh)
     icr = Context(img)
     self.paint_pattern(icr, 0, 0, antialias, None, background, foreground)
     img.flush()
     return img
Esempio n. 9
0
        baseline = max(baseline, max(0,-(slot.bitmap_top-bitmap.rows)))
        kerning = face.get_kerning(previous, c)
        width += (slot.advance.x >> 6) + (kerning.x >> 6)
        previous = c

    Z = ImageSurface(FORMAT_A8, width, height)
    ctx = Context(Z)

    # Second pass for actual rendering
    x, y = 0, 0
    previous = 0
    for c in text:
        face.load_char(c)
        bitmap = slot.bitmap
        top = slot.bitmap_top
        left = slot.bitmap_left
        w,h = bitmap.width, bitmap.rows
        y = height-baseline-top
        kerning = face.get_kerning(previous, c)
        x += (kerning.x >> 6)
        # cairo does not like zero-width bitmap from the space character!
        if (bitmap.width > 0):
            glyph_surface = make_image_surface(face.glyph.bitmap)
            ctx.set_source_surface(glyph_surface, x, y)
            ctx.paint()
        x += (slot.advance.x >> 6)
        previous = c
    Z.flush()
    Z.write_to_png("hello-world-cairo.png")
    Image.open("hello-world-cairo.png").show()
Esempio n. 10
0
class CairoBackingBase(WindowBackingBase):

    HAS_ALPHA = envbool("XPRA_ALPHA", True)

    def __init__(self, wid, window_alpha, _pixel_depth=0):
        super().__init__(wid, window_alpha and self.HAS_ALPHA)
        self.idle_add = GLib.idle_add
        self.size = 0, 0
        self.render_size = 0, 0

    def init(self, ww: int, wh: int, bw: int, bh: int):
        mod = self.size != (bw, bh) or self.render_size != (ww, wh)
        self.size = bw, bh
        self.render_size = ww, wh
        if mod:
            self.create_surface()

    def get_info(self):
        info = super().get_info()
        info.update({
            "type": "Cairo",
            "rgb-formats": self.RGB_MODES,
        })
        return info

    def create_surface(self):
        bw, bh = self.size
        old_backing = self._backing
        #should we honour self.depth here?
        self._backing = None
        if bw == 0 or bh == 0:
            #this can happen during cleanup
            return None
        self._backing = ImageSurface(FORMAT_ARGB32, bw, bh)
        cr = Context(self._backing)
        cr.set_operator(OPERATOR_CLEAR)
        cr.set_source_rgba(1, 1, 1, 1)
        cr.rectangle(0, 0, bw, bh)
        cr.fill()
        if old_backing is not None:
            oldw, oldh = old_backing.get_width(), old_backing.get_height()
            sx, sy, dx, dy, w, h = self.gravity_copy_coords(oldw, oldh, bw, bh)
            cr.translate(dx - sx, dy - sy)
            cr.rectangle(sx, sy, w, h)
            cr.fill()
            cr.set_operator(OPERATOR_SOURCE)
            cr.set_source_surface(old_backing, 0, 0)
            cr.paint()
            self._backing.flush()
        return cr

    def close(self):
        if self._backing:
            self._backing.finish()
        WindowBackingBase.close(self)

    def cairo_paint_pixbuf(self, pixbuf, x: int, y: int, options):
        """ must be called from UI thread """
        log("source pixbuf: %s", pixbuf)
        w, h = pixbuf.get_width(), pixbuf.get_height()
        self.cairo_paint_from_source(Gdk.cairo_set_source_pixbuf, pixbuf, x, y,
                                     w, h, w, h, options)

    def cairo_paint_surface(self, img_surface, x: int, y: int, width: int,
                            height: int, options):
        iw, ih = img_surface.get_width(), img_surface.get_height()
        log("source image surface: %s", (
            img_surface.get_format(),
            iw,
            ih,
            img_surface.get_stride(),
            img_surface.get_content(),
        ))

        def set_source_surface(gc, surface, sx, sy):
            gc.set_source_surface(surface, sx, sy)

        self.cairo_paint_from_source(set_source_surface, img_surface, x, y, iw,
                                     ih, width, height, options)

    def cairo_paint_from_source(self, set_source_fn, source, x: int, y: int,
                                iw: int, ih: int, width: int, height: int,
                                options):
        """ must be called from UI thread """
        log("cairo_paint_surface%s backing=%s, paint box line width=%i",
            (set_source_fn, source, x, y, iw, ih, width, height, options),
            self._backing, self.paint_box_line_width)
        gc = Context(self._backing)
        if self.paint_box_line_width:
            gc.save()

        gc.rectangle(x, y, width, height)
        gc.clip()

        gc.set_operator(OPERATOR_CLEAR)
        gc.rectangle(x, y, width, height)
        gc.fill()

        gc.set_operator(OPERATOR_SOURCE)
        gc.translate(x, y)
        if iw != width or ih != height:
            gc.scale(width / iw, height / ih)
        gc.rectangle(0, 0, width, height)
        set_source_fn(gc, source, 0, 0)
        gc.paint()

        if self.paint_box_line_width:
            gc.restore()
            encoding = options.get("encoding")
            self.cairo_paint_box(gc, encoding, x, y, width, height)

    def cairo_paint_box(self, gc, encoding, x, y, w, h):
        color = get_paint_box_color(encoding)
        gc.set_line_width(self.paint_box_line_width)
        gc.set_source_rgba(*color)
        gc.rectangle(x, y, w, h)
        gc.stroke()

    def _do_paint_rgb16(self, img_data, x, y, width, height, render_width,
                        render_height, rowstride, options):
        return self._do_paint_rgb(FORMAT_RGB16_565, False, img_data, x, y,
                                  width, height, render_width, render_height,
                                  rowstride, options)

    def _do_paint_rgb24(self, img_data, x: int, y: int, width: int,
                        height: int, render_width: int, render_height: int,
                        rowstride: int, options):
        return self._do_paint_rgb(FORMAT_RGB24, False, img_data, x, y, width,
                                  height, render_width, render_height,
                                  rowstride, options)

    def _do_paint_rgb30(self, img_data, x, y, width, height, render_width,
                        render_height, rowstride, options):
        return self._do_paint_rgb(FORMAT_RGB30, True, img_data, x, y, width,
                                  height, render_width, render_height,
                                  rowstride, options)

    def _do_paint_rgb32(self, img_data, x: int, y: int, width: int,
                        height: int, render_width: int, render_height: int,
                        rowstride: int, options):
        if self._alpha_enabled:
            cformat = FORMAT_ARGB32
        else:
            cformat = FORMAT_RGB24
        return self._do_paint_rgb(cformat, True, img_data, x, y, width, height,
                                  render_width, render_height, rowstride,
                                  options)

    def _do_paint_rgb(self, *args):
        raise NotImplementedError()

    def get_encoding_properties(self):
        props = WindowBackingBase.get_encoding_properties(self)
        if SCROLL_ENCODING:
            props["encoding.scrolling"] = True
        return props

    def paint_scroll(self, img_data, options, callbacks):
        self.idle_add(self.do_paint_scroll, img_data, callbacks)

    def do_paint_scroll(self, scrolls, callbacks):
        old_backing = self._backing
        gc = self.create_surface()
        if not gc:
            fire_paint_callbacks(callbacks, False, message="no context")
            return
        gc.set_operator(OPERATOR_SOURCE)
        for sx, sy, sw, sh, xdelta, ydelta in scrolls:
            gc.set_source_surface(old_backing, xdelta, ydelta)
            x = sx + xdelta
            y = sy + ydelta
            gc.rectangle(x, y, sw, sh)
            gc.fill()
            if self.paint_box_line_width > 0:
                self.cairo_paint_box(gc, "scroll", x, y, sw, sh)
        del gc
        self._backing.flush()
        fire_paint_callbacks(callbacks)

    def nasty_rgb_via_png_paint(self, cairo_format, has_alpha: bool, img_data,
                                x: int, y: int, width: int, height: int,
                                rowstride: int, rgb_format):
        log.warn("nasty_rgb_via_png_paint%s",
                 (cairo_format, has_alpha, len(img_data), x, y, width, height,
                  rowstride, rgb_format))
        #PIL fallback
        from PIL import Image
        if has_alpha:
            oformat = "RGBA"
        else:
            oformat = "RGB"
        #use frombytes rather than frombuffer to be compatible with python3 new-style buffers
        #this is slower, but since this codepath is already dreadfully slow, we don't care
        bdata = memoryview_to_bytes(img_data)
        src_format = rgb_format.replace("X", "A")
        try:
            img = Image.frombytes(oformat, (width, height), bdata, "raw",
                                  src_format, rowstride, 1)
        except ValueError as e:
            log("PIL Image frombytes:", exc_info=True)
            raise Exception("failed to parse raw %s data as %s to %s: %s" %
                            (rgb_format, src_format, oformat, e)) from None
        #This is insane, the code below should work, but it doesn't:
        # img_data = bytearray(img.tostring('raw', oformat, 0, 1))
        # pixbuf = new_from_data(img_data, COLORSPACE_RGB, True, 8, width, height, rowstride)
        # success = self.cairo_paint_pixbuf(pixbuf, x, y)
        #So we still rountrip via PNG:
        from io import BytesIO
        png = BytesIO()
        img.save(png, format="PNG")
        reader = BytesIO(png.getvalue())
        png.close()
        img = ImageSurface.create_from_png(reader)
        self.cairo_paint_surface(img, x, y, width, height, {})
        return True

    def cairo_draw(self, context):
        log(
            "cairo_draw: size=%s, render-size=%s, offsets=%s, pointer_overlay=%s",
            self.size, self.render_size, self.offsets, self.pointer_overlay)
        if self._backing is None:
            return
        #try:
        #    log("clip rectangles=%s", context.copy_clip_rectangle_list())
        #except:
        #    log.error("clip:", exc_info=True)
        ww, wh = self.render_size
        w, h = self.size
        if ww == 0 or w == 0 or wh == 0 or h == 0:
            return
        if w != ww or h != wh:
            context.scale(ww / w, wh / h)
        x, y = self.offsets[:2]
        if x != 0 or y != 0:
            context.translate(x, y)
        context.set_operator(OPERATOR_SOURCE)
        context.set_source_surface(self._backing, 0, 0)
        context.paint()
        if self.pointer_overlay and self.cursor_data:
            px, py, _size, start_time = self.pointer_overlay[2:]
            spx = round(w * px / ww)
            spy = round(h * py / wh)
            cairo_paint_pointer_overlay(context, self.cursor_data, x + spx,
                                        y + spy, start_time)