def draw_label(text, x, y): original = ctx.get_matrix() original_font = ctx.get_font_face() ctx.translate(x, y) font = cairo.ToyFontFace('', weight=1) ctx.set_source_rgba(0, 0, 0, 1) ctx.set_font_face(font) ctx.scale(1 / 15) # Default font size is 10 mm (1cm) ctx.show_text(text) ctx.fill() ctx.set_font_face(original_font) ctx.set_matrix(original)
def __init__(self, text, context, family, size, rgba, weight=cairo.FONT_WEIGHT_NORMAL, slant=cairo.FONT_SLANT_NORMAL): self.text = text self.size = size self.rgba = rgba self.face = cairo.ToyFontFace(family, weight=weight, slant=slant) context.save() self.set_font_on_context(context) self.x_bearing, self.y_bearing, self.width, self.height, _, _ = context.text_extents( text) context.restore()
def __init__(self, face, size): if self.regex_italic.search(face): self.slant = "italic" slant = cairo.FONT_SLANT_ITALIC face = self.regex_italic.sub("", face) else: self.slant = "normal" slant = cairo.FONT_SLANT_NORMAL if self.regex_bold.search(face): self.weight = "bold" weight = cairo.FONT_WEIGHT_BOLD face = self.regex_bold.sub("", face) else: self.weight = "normal" weight = cairo.FONT_WEIGHT_NORMAL self.family = face.strip() self.size = float(size) self.toy_font_face = cairo.ToyFontFace(self.family, slant, weight) self.scaled_font = cairo.ScaledFont( self.toy_font_face, cairo.Matrix(xx=self.size, yy=self.size) )
def draw_genomic_region( organism, chromosome, region_start, region_end, hits, draw_domains, draw_directions, out_file, highlighted_genes=frozenset(), exclude_genes=frozenset(), label=None, absolute_pixel_size=0, rna_bam=None ): '''Takes drawing settings from handle_args module for region or draw_gene, and draws figure. Parameters ---------- organism : organism genome is of chromosome : chromosome for feature being drawn region_start: bp position to start drawing figure on chromosome region_end : bp position to end drawing figure on chromosome hits : hit data for feature draw_domains : domain argument draw_directions : direction argument out_file : output directory highlighted_genes: set of genes to highlight in drawing label : label for figure absolute-pixel-size: absolute-pixel-size argument Writes ------ Figure(s) to png image file(s) ''' # TODO: there's an open issue of what to do when the label isn't provided. # Consider all usages and do it right. ignored_regions = organism.ignored_regions[chromosome] region_len = region_end - region_start track_height = 5 feature_height = 20 # label_height = 20 if label else 0 rna_track_height = 20 label_height = 20 width = 350 if absolute_pixel_size <= 0 else int(region_len / absolute_pixel_size) height = track_height * len(hits) + feature_height + label_height + track_height * 2 + \ (rna_track_height if rna_bam else 0) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) ctx = cairo.Context(surface) ctx.scale(width, height) # Normalizing the canvas # Draw white background ctx.rectangle(0, 0, 1, 1) ctx.set_source_rgb(1, 1, 1) ctx.fill() # Draw the tracks track_height_scaled = float(track_height) / height for track_ix, track_hits in enumerate(hits): track_y = track_ix * track_height / float(height) chrom_track = track_hits[chromosome] try: left_hit_ix = chrom_track.index(chrom_track.find_ge(region_start)) except ValueError: left_hit_ix = 0 try: right_hit_ix = chrom_track.index(chrom_track.find_gt(region_end)) except ValueError: right_hit_ix = len(chrom_track) relevant_hits = chrom_track[left_hit_ix:right_hit_ix] for pixel in range(width): left_bp = region_start + int(ceil(pixel * (region_len / float(width)))) right_bp = region_start + int(floor((pixel+1) * (region_len / float(width)))) reads_in_pixel = sum(h["hit_count"] for h in relevant_hits if left_bp <= h["hit_pos"] <= right_bp) if reads_in_pixel: ctx.rectangle((pixel / float(width)), track_y, 1.0 / width, track_height_scaled) color = max(0, 0.9 - reads_in_pixel / 100.0) ctx.set_source_rgb(color, color, color) ctx.fill() # Draw the feature: features_in_range = organism.feature_db.get_features_at_range(chromosome, (region_start, region_end)) feature_track_y = float(height - feature_height - label_height - track_height*2 - (rna_track_height if rna_bam else 0)) / height feature_track_height_scaled = float(feature_height) / height scale_bp = lambda bp: min(1.0, max(0.0, float(bp - region_start)) / region_len) for feature in features_in_range: if feature.standard_name in exclude_genes: continue # Draw the feature as a blue rectangle: feature_start = scale_bp(feature.start) feature_end = scale_bp(feature.stop) ctx.rectangle(feature_start, feature_track_y, feature_end - feature_start, feature_track_height_scaled) ctx.set_source_rgb(0.0, 100/255.0, 180/255.0) ctx.fill() # Draw introns: intron_track_y = feature_track_y + feature_track_height_scaled for intron_start, intron_end in feature.exons.complement(feature.start, feature.stop): intron_start = scale_bp(intron_start) intron_end = scale_bp(intron_end) ctx.rectangle(intron_start, intron_track_y, intron_end - intron_start, track_height_scaled) ctx.set_source_rgb(1, 1, 0) ctx.fill() # Draw its domains: if (feature.standard_name in highlighted_genes and draw_domains == GENES_HIGHLIGHTED) or \ draw_domains == GENES_ALL: domain_pad = 1.0/6 * feature_height domain_y = feature_track_y + domain_pad / height domain_height = 2.0/3 * feature_track_height_scaled for domain in feature.domains: domain_start = scale_bp(domain[0]) domain_end = scale_bp(domain[1]) ctx.rectangle(domain_start, domain_y, domain_end - domain_start, domain_height) ctx.set_source_rgb(0, 0, 0) ctx.fill() # Chop off a rectangle to indicate directionality: if (feature.standard_name in highlighted_genes and draw_directions == GENES_HIGHLIGHTED) or \ draw_directions == GENES_ALL: arrow_width = min(10.0 / width, feature_end - feature_start) # 10 pixels or the length of the gene if feature.strand == 'W': end = scale_bp(feature.stop) ctx.move_to(end - arrow_width, feature_track_y) ctx.line_to(end, feature_track_y) ctx.line_to(end, feature_track_y + 0.5 * feature_track_height_scaled) ctx.close_path() ctx.set_source_rgb(1, 1, 1) ctx.fill() ctx.move_to(end - arrow_width, feature_track_y + feature_track_height_scaled) ctx.line_to(end, feature_track_y + feature_track_height_scaled) ctx.line_to(end, feature_track_y + 0.5 * feature_track_height_scaled) ctx.close_path() ctx.set_source_rgb(1, 1, 1) ctx.fill() else: start = scale_bp(feature.start) ctx.move_to(start, feature_track_y) ctx.line_to(start + arrow_width, feature_track_y) ctx.line_to(start, feature_track_y + 0.5 * feature_track_height_scaled) ctx.close_path() ctx.set_source_rgb(1, 1, 1) ctx.fill() ctx.move_to(start, feature_track_y + feature_track_height_scaled) ctx.line_to(start + arrow_width, feature_track_y + feature_track_height_scaled) ctx.line_to(start, feature_track_y + 0.5 * feature_track_height_scaled) ctx.close_path() ctx.set_source_rgb(1, 1, 1) ctx.fill() if label is None:# and feature.standard_name in highlighted_genes: feature_name = feature.name ctx.set_font_matrix(cairo.Matrix(xx=15/float(width), yy=15/float(height))) (_x, _y, label_text_width, label_text_height, _dx, _dy) = ctx.text_extents(feature_name) label_track_y = (height - label_height) / float(height) ctx.move_to(feature_start + (feature_end - feature_start - label_text_width) / 2, label_track_y + label_text_height * 1.25) ctx.set_source_rgb(0, 0, 0) ctx.show_text(feature_name) # Draw ignored regions: ignored_track_y = feature_track_y + feature_track_height_scaled + track_height_scaled for ignored_start, ignored_stop in ignored_regions & RangeSet([(region_start, region_end)]): ignored_start = scale_bp(ignored_start) ignored_stop = scale_bp(ignored_stop) ctx.rectangle(ignored_start, ignored_track_y, ignored_stop - ignored_start, track_height_scaled) ctx.set_source_rgb(1, 0, 0) ctx.fill() # Add RNA if Calbicans: if rna_bam: rna_seq = pysam.AlignmentFile(rna_bam, "rb") aligned_reads = [0] * int(region_end - region_start) for aligned_read in rna_seq.fetch(chromosome, region_start, region_end): for p in aligned_read.get_reference_positions(): target_ix = int(p - region_start) if not (0 <= target_ix < len(aligned_reads)): continue aligned_reads[target_ix] += 1 max_aligned = float(max(aligned_reads)) if max_aligned > 0: rna_track_y = ignored_track_y + track_height_scaled rna_track_height_scaled = float(rna_track_height) / height for ix, nreads in enumerate(aligned_reads): ref_pos = ix + int(region_start) ctx.rectangle( scale_bp(ref_pos), rna_track_y + (rna_track_height_scaled - rna_track_height_scaled * (nreads / max_aligned)), scale_bp(ref_pos+1) - scale_bp(ref_pos), rna_track_height_scaled * (nreads / max_aligned) ) ctx.set_source_rgb(0, 0, 0) ctx.fill() # Draw the text: if label: ctx.set_font_matrix(cairo.Matrix(xx=15/float(width), yy=15/float(height))) (_x, _y, label_text_width, label_text_height, _dx, _dy) = ctx.text_extents(label) label_track_y = (height - label_height) / float(height) ctx.move_to((1.0 - label_text_width) / 2.0, label_track_y + label_text_height * 1.25) ctx.set_source_rgb(0, 0, 0) old_font = ctx.get_font_face() slanted_font = cairo.ToyFontFace(old_font.get_family(), cairo.FONT_SLANT_OBLIQUE, old_font.get_weight()) for element in label.split(" "): if element.startswith("Ca") or element.startswith("Sp") or element.startswith("Sc"): ctx.set_font_face(slanted_font) ctx.show_text(element) ctx.show_text(" ") ctx.set_font_face(old_font) ctx.set_font_face(old_font) # Output to PNG surface.write_to_png(out_file)
(item.shape.ellipse.start_angle / 360) * 2 * math.pi, (item.shape.ellipse.sweep_angle / 360) * 2 * math.pi) context.close_path() context.stroke() context.restore() elif item.type == papyrus_pb2.Item.Type.Value('Text'): context.save() context.set_font_size(item.text.weight) # Color argb = u32_to_4f(item.text.color) context.set_source_rgba(argb[1], argb[2], argb[3], argb[0]) context.move_to(cm_to_point(item.text.bounds.left), cm_to_point(item.text.bounds.top)) tw = int(item.text.weight) size_m = cairocffi.Matrix(tw, 0, 0, tw, 0, 0) scaledFont = cairocffi.ScaledFont( cairocffi.ToyFontFace("sans-serif"), size_m) glyphs = scaledFont.text_to_glyphs( cm_to_point(item.text.bounds.left), cm_to_point(item.text.bounds.bottom), item.text.text, False) context.show_glyphs(glyphs) context.restore() else: print(item) print("Item of type {} not supported".format( papyrus_pb2.Item.Type.Name(item.type))) surface.flush() surface.finish()
def convert_page(path, note_name, notebook_path, directory, pdf_file, page_number): page = papyrus_pb2.Page() # Open and parse papyrus page using protobuf page.ParseFromString(open(path, 'rb').read()) # Create a new pdf surface for drawing if page.background.width == 0 and page.background.height == 0: print("\tInfinite page!") max_x = 0 max_y = 0 for item in page.layer.item: bounds = None if item.type == papyrus_pb2.Item.Type.Value('Stroke'): bounds = item.stroke.bounds elif item.type == papyrus_pb2.Item.Type.Value('Shape'): if item.shape.type == 'Ellipse': bounds = item.shape.ellipse.bounds elif item.type == papyrus_pb2.Item.Type.Value('Text'): bounds = item.text.bounds else: print(item) if bounds is not None: if bounds.right > max_x: max_x = bounds.right if bounds.bottom > max_y: max_y = bounds.bottom page.background.width = max_x + 1 page.background.height = max_y + 1 note_name = titlesafe(note_name) print("\t%s" % note_name) note_path = directory + '/' + notebook_path + '/' + dirsafe(note_name) new_note_path = note_path num = 1 #while os.path.exists(new_note_path): # new_note_path = note_path + '(' + str(num) + ')' # num += 1 makedir(note_path) note_path = new_note_path pdfpath = note_path + '/pdf' makedir(pdfpath) pdffile = pdfpath + '/page' + str(page_number) + '.pdf' print("\tSource: %s\n\tOutput: %s" % (path, pdffile)) pdf_out = open(pdffile, 'w') surface = cairocffi.PDFSurface(pdf_out, cm_to_point(page.background.width), cm_to_point(page.background.height)) context = cairocffi.Context(surface) # Paint the page white context.set_source_rgba(0, 0, 0, 0) context.paint() for item in page.layer.item: if item.type == papyrus_pb2.Item.Type.Value('Stroke'): context.save() # Translate to reference_point (stroke origin) context.translate(cm_to_point(item.stroke.reference_point.x), cm_to_point(item.stroke.reference_point.y)) # Set source color argb = u32_to_4f(item.stroke.color) context.set_source_rgba(argb[1], argb[2], argb[3], argb[0]) # Set line width width = cm_to_point(item.stroke.weight) # Other parameter context.set_line_join(cairocffi.LINE_JOIN_ROUND) context.set_line_cap(cairocffi.LINE_CAP_ROUND) context.move_to(0, 0) if item.stroke.stroke_type == papyrus_pb2.Stroke.Highlight: context.push_group() context.set_source_rgba(argb[1], argb[2], argb[3], 1) context.fill_preserve() context.set_line_cap(cairocffi.LINE_CAP_SQUARE) for point in item.stroke.point: context.line_to(cm_to_point(point.x), cm_to_point(point.y)) if item.stroke.stroke_type == papyrus_pb2.Stroke.Highlight: context.set_line_width(width) #context. elif point.HasField('pressure'): context.set_line_width(width * point.pressure) else: context.set_line_width(width) context.stroke() context.move_to(cm_to_point(point.x), cm_to_point(point.y)) if item.stroke.stroke_type == papyrus_pb2.Stroke.Highlight: context.pop_group_to_source() context.paint_with_alpha(argb[0]) context.restore() elif item.type == papyrus_pb2.Item.Type.Value( 'Shape') and item.shape.ellipse is not None: width = item.shape.ellipse.weight * 0.3 context.save() context.new_sub_path() context.translate(cm_to_point(item.shape.ellipse.center_x), cm_to_point(item.shape.ellipse.center_y)) context.set_line_width(item.shape.ellipse.weight) argb = u32_to_4f(item.shape.ellipse.color) context.set_line_width(width) context.set_source_rgba(argb[1], argb[2], argb[3], argb[0]) context.scale(cm_to_point(item.shape.ellipse.radius_x), cm_to_point(item.shape.ellipse.radius_y)) context.arc(0, 0, 1, (item.shape.ellipse.start_angle / 360) * 2 * math.pi, (item.shape.ellipse.sweep_angle / 360) * 2 * math.pi) context.close_path() context.stroke() context.restore() elif item.type == papyrus_pb2.Item.Type.Value('Text'): context.save() context.set_font_size(item.text.weight) # Color argb = u32_to_4f(item.text.color) context.set_source_rgba(argb[1], argb[2], argb[3], argb[0]) context.move_to(cm_to_point(item.text.bounds.left), cm_to_point(item.text.bounds.top)) tw = int(item.text.weight) size_m = cairocffi.Matrix(tw, 0, 0, tw, 0, 0) scaledFont = cairocffi.ScaledFont( cairocffi.ToyFontFace("sans-serif"), size_m) glyphs = scaledFont.text_to_glyphs( cm_to_point(item.text.bounds.left), cm_to_point(item.text.bounds.bottom), item.text.text, False) context.show_glyphs(glyphs) context.restore() elif item.type == papyrus_pb2.Item.Type.Value('Image'): if (DEBUG): print("Got an image!") print(item.image.image_hash) # Convert JPEG image to PNG im = Image.open(base_directory + "data/imgs/" + item.image.image_hash) im = im.crop( (item.image.crop_bounds.left, item.image.crop_bounds.top, item.image.crop_bounds.right, item.image.crop_bounds.bottom)) im.save( base_directory + "data/imgs/" + item.image.image_hash + ".png", "PNG") im.close() matrix = cairocffi.Matrix() scale_x = cm_to_point(item.image.bounds.right - item.image.bounds.left) / ( item.image.crop_bounds.right - item.image.crop_bounds.left) scale_y = cm_to_point(item.image.bounds.bottom - item.image.bounds.top) / ( item.image.crop_bounds.bottom - item.image.crop_bounds.top) if (DEBUG): print("Scale X: %d" % (1 / scale_x)) print("Scale Y: %d" % (1 / scale_y)) print("Translate: %d" % cm_to_point(item.image.bounds.left)) matrix.scale(1 / scale_x, 1 / scale_y) matrix.translate(-cm_to_point(item.image.bounds.left), -cm_to_point(item.image.bounds.top)) im_surface = cairocffi.ImageSurface.create_from_png( base_directory + "./data/imgs/" + item.image.image_hash + ".png") im_surface_pattern = cairocffi.SurfacePattern(im_surface) im_surface_pattern.set_filter(cairocffi.FILTER_GOOD) im_surface_pattern.set_matrix(matrix) context.save() context.set_source(im_surface_pattern) context.rectangle( cm_to_point(item.image.bounds.left), cm_to_point(item.image.bounds.top), cm_to_point(item.image.bounds.right - item.image.bounds.left), cm_to_point(item.image.bounds.bottom - item.image.bounds.top)) context.fill() context.restore() else: print(item) print("Item of type {} not supported".format( papyrus_pb2.Item.Type.Name(item.type))) surface.flush() surface.finish() pdf_out.close() if page.background.HasField("pdf_background"): try: output_file = PdfFileWriter() input_file = PdfFileReader(file(pdffile, "rb")) pdf_file = PdfFileReader( file(base_directory + "data/docs/" + pdf_file, "rb")) pdf_page = pdf_file.getPage( page.background.pdf_background.page_number) input_page = input_file.getPage(0) pdf_page.mergePage(input_page) output_file.addPage(pdf_page) with open(pdffile + ".tmp", "wb") as outputStream: output_file.write(outputStream) os.rename(pdffile + ".tmp", pdffile) except: print( "\t%sUnable to merge PDFs - maybe the PDF was malformed? Result was %s%s" % (color.RED, sys.exc_info()[0], color.END)) print("") return pdffile
def render_text(self): imwid = LabelObjectCollection._TEXTURE_SIZE # Create a memory surface to render to and a drawing context img = cairo.ImageSurface(cairo.FORMAT_ARGB32, imwid, imwid) ctx = cairo.Context(img) # Get the font, make it a color italic = cairo.FONT_SLANT_ITALIC if self._italic else cairo.FONT_SLANT_NORMAL bold = cairo.FONT_WEIGHT_BOLD if self._bold else cairo.FONT_WEIGHT_NORMAL font = cairo.ToyFontFace(self._font, italic, bold) ctx.set_font_face(font) # sys.stderr.write("Setting text color to r={}, g={}, b={}\n" # .format(self.color[0], self.color[1], self.color[2])) ctx.set_source_rgb(self.color[0], self.color[1], self.color[2]) # Figure out how big the word is going to be with font size 100 ctx.set_font_size(100) scf = cairo.ScaledFont(font, ctx.get_font_matrix(), ctx.get_matrix(), cairo.FontOptions()) xbear, ybear, wid, hei, xadv, yadv = scf.text_extents(self._text) # We are given self._height as the point size of the font,self._border in # points as the distance from the edge of the word to the # border, and self._linewidth in points as the width of the border # line. We want to just fit this into a imwid × imwid image. Figure out # what point size fsize we should tell Cairo to make this work if self._box: fullwid = wid + 2 * ((self._border + self._linewidth) * (100. / self._height)) fullhei = hei + 2 * ((self._border + self._linewidth) * (100. / self._height)) else: fullwid = wid fullhei = hei if fullwid > fullhei: fsize = imwid / fullwid * 100 else: fsize = imwid / fullhei * 100 ctx.set_font_size(fsize) bordersp = self._border * fsize / self._height linew = self._linewidth * fsize / self._height # Get a path representing the text, and figure out how big it is ctx.text_path(self._text) x0, y0, x1, y1 = ctx.fill_extents() # Set the position offset from the center of # the image by half the size of the path, # get a new path at the right place, # figure it what it covers on the image, # and draw it. xpos = imwid // 2 - (x1 - x0) / 2 ypos = imwid - (y1 + bordersp + linew if self._box else y1) ctx.new_path() ctx.move_to(xpos, ypos) ctx.text_path(self._text) x0, y0, x1, y1 = ctx.fill_extents() ctx.fill() if self._box: if self._linecolor is not None: ctx.set_source_rgb(self._linecolor[0], self._linecolor[1], self._linecolor[2]) udx, udy = ctx.device_to_user_distance(linew, linew) if udx > udy: ctx.set_line_width(udx) else: ctx.set_line_width(udy) ctx.move_to(x0 - bordersp, y0 - bordersp) ctx.line_to(x1 + bordersp, y0 - bordersp) ctx.line_to(x1 + bordersp, y1 + bordersp) ctx.line_to(x0 - bordersp, y1 + bordersp) ctx.close_path() ctx.stroke() # Make sure the image is fully drawn img.flush() data = numpy.frombuffer(img.get_data(), dtype=numpy.ubyte) data.shape = (imwid, imwid, 4) # Move the colors around to what OpenGL needs if LabelObject._is_little_endian: self.texturedata[:, :, 0] = data[:, :, 2] self.texturedata[:, :, 1] = data[:, :, 1] self.texturedata[:, :, 2] = data[:, :, 0] self.texturedata[:, :, 3] = data[:, :, 3] else: self.texturedata[:, :, 0:2] = data[:, :, 1:3] self.texturedata[:, :, 3] = data[:, :, 0] # Figure out what the width and height of the polygon should be. # They get stored in fullwid and fullhei (the renderer will use # those two fields) factor = 1. if self._units == "pixels": raise Exception("Pixel units for labels isn't implemented") elif self.units == "centidisplay": factor = 0.01 self.glxoff = factor * self._xoff self.glyoff = factor * self._yoff # For the size of the polygon: factor * self._refheight is supposed # to be the world-space unit height of 12 pixels on the image # (for a self._height-point font). # fullheight units / imwid pixels = refheight units / 12 points # But my effective refheight is refheight * height / fsize # # so fullheight = imwid * refheight * height / (12 * fsize) self.fullhei = factor * self._refheight * imwid * self._height / ( 12 * fsize) self.fullwid = self.fullhei