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 _draw_time(self): """Draw the time in colors (digital display). """ # TRANS: The format used to display the time for digital clock # You can add AM/PM indicator or use 12/24 format, for example # "%I:%M:%S %p". See # http://docs.python.org/lib/module-time.html for available # strftime formats If the display of the time is moving # horizontally, it means that the glyphs of the digits used in # the font don't have the same width. Try to use a Monospace # font. xgettext:no-python-format markup = _('<markup>\ <span lang="en" font_desc="Sans,Monospace Bold 96">\ <span foreground="#005FE4">%I</span>:\ <span foreground="#00B20D">%M</span>:\ <span foreground="#E6000A">%S</span>%p</span></markup>') # BUG: The following line kills Python 2.5 but is valid in 2.4 markup_time = self._time.strftime(markup) # markup_time = time.strftime(markup) cr = self.window.cairo_create() cr.set_source_rgba(*style.Color(self._COLOR_BLACK).get_rgba()) pango_layout = PangoCairo.create_layout( PangoCairo.create_context(cr)) d = int(self._center_y + 0.3 * self._radius) pango_layout.set_markup(markup_time) dx, dy = pango_layout.get_pixel_size() pango_layout.set_alignment(Pango.Alignment.CENTER) cr.translate(self._center_x - dx / 2.0, d - dy / 2.0) PangoCairo.show_layout(cr, pango_layout)
def write(c, text, name, size, centered=False, at_top=False): pc = PangoCairo.create_context(c) font = Pango.FontDescription(name) font.set_size(int(round(size * Pango.SCALE))) lo = PangoCairo.create_layout(pc) lo.set_font_description(font) lo.set_text('X', -1) baseline_offset = lo.get_baseline() / Pango.SCALE if not at_top: c.rel_move_to(0, -baseline_offset) lo.set_font_description(font) lo.set_text(text, -1) if hasattr(lo, 'get_logical_extents'): extents = lo.get_logical_extents() ex = extents.get_width() / Pango.SCALE else: ex = size ex *= len(text) if centered: c.rel_move_to(-ex / 2, 0) PangoCairo.update_layout(c, lo) PangoCairo.show_layout(c, lo) c.rel_move_to(ex, 0) if not at_top: c.rel_move_to(0, -baseline_offset)
def _render(self, ctx=None): if not self._doRender: return ctx = ctx or self._get_context() if GI: ctx = _UNSAFE_cairocffi_context_to_pycairo(ctx) # we build a PangoCairo context linked to cairo context # then we create a pango layout # we update the context as we already used a null one on the pre-rendering # supposedly there should not be a big performance penalty self._pang_ctx = PangoCairo.create_context(ctx) if self._fillcolor is not None: # Go to initial point (CORNER or CENTER): transform = self._call_transform_mode(self._transform) if GI: transform = pycairo.Matrix(*transform.as_tuple()) ctx.set_matrix(transform) ctx.translate(self.x, self.y-self.baseline) if self._outline is False: ctx.set_source_rgba(*self._fillcolor) PangoCairo.show_layout(ctx, self.layout) PangoCairo.update_layout(ctx, self.layout)
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 draw(self,sender, c, data=None): c.set_source_rgb(0,0,0) self.drawBackground(c) pc = PangoCairo.create_context(c) #c.set_antialias( cairo.Antialias.SUBPIXEL ) l = Pango.Layout(pc) l.set_font_description( Pango.FontDescription(self.font)) l.set_text( self.text, -1) l.set_alignment( Pango.Alignment.CENTER ) l.set_wrap( Pango.WrapMode.WORD ) l.set_width( (self.width-5) * Pango.SCALE ) l.set_height( (self.height-5) * Pango.SCALE ) l.set_ellipsize( Pango.EllipsizeMode.END ) w = 0 h = 0 w,h = l.get_pixel_size() c.move_to( 0, (self.height/2) - (h/2) ) c.set_source_rgb(0, 0, 0) PangoCairo.update_layout(c, l) PangoCairo.show_layout(c, l) import storage if(storage.window.focusedItem == self): c.set_source_rgb(0,0,200) c.move_to(0,0) c.rectangle(0,0,self.width-1,self.height-1) c.stroke() return False
def _render(self, ctx=None): if not self._doRender: return ctx = ctx or self._get_context() if GI: ctx = _UNSAFE_cairocffi_context_to_pycairo(ctx) # we build a PangoCairo context linked to cairo context # then we create a pango layout # we update the context as we already used a null one on the pre-rendering # supposedly there should not be a big performance penalty self._pang_ctx = PangoCairo.create_context(ctx) if self._fillcolor is not None: # Go to initial point (CORNER or CENTER): transform = self._call_transform_mode(self._transform) if GI: transform = pycairo.Matrix(*transform.as_tuple()) ctx.set_matrix(transform) ctx.translate(self.x, self.y - self.baseline) if self._outline is False: ctx.set_source_rgba(*self._fillcolor) PangoCairo.show_layout(ctx, self.layout) PangoCairo.update_layout(ctx, self.layout)
def create_params(self): self.surface = cairo.ImageSurface(cairo.FORMAT_A8, self.width, self.height) self.context = cairo.Context(self.surface) self.pc = PangoCairo.create_context(self.context) self.layout = PangoCairo.create_layout(self.context) self.layout.set_font_description(Pango.FontDescription(self.font)) self.font_desc = Pango.font_description_from_string(self.font)
def printPage(self, operation, context, page_nr): self.pangolayout = context.create_pango_layout() self.cairo_context = context.get_cairo_context() self.pangolayout.set_width(-1) self.PangoCairo = PangoCairo.create_context(self.cairo_context) getattr(self, self.drawfunction)(page_nr)
def draw_text_adjusted(ctx, text, x, y, width, height, max_char_number=None, text_color=(0, 0, 0, 1), align=Pango.Alignment.CENTER, width_adjust=0.7, height_adjust=0.8): """ Draw a text adjusted to a maximum character number Args: ctx (cairo.Context): The cairo context to use to draw. text (str): the text to draw. x/y (numbers): The position on the canvas. width/height (numbers): The area we want to write into (cairo units). max_char_number (number): If set a maximum character number. """ pc = PangoCairo.create_context(ctx) layout = PangoCairo.create_layout(ctx) layout.set_width(int(width_adjust * width * Pango.SCALE)) layout.set_alignment(align) fd = Pango.FontDescription("Georgia Bold") fd.set_size(Pango.SCALE) layout.set_font_description(fd) if max_char_number: # adjust size with the max character number layout.set_text('0'*max_char_number, -1) adjust_font_size(layout, fd, width_adjust*width, height_adjust*height) # set the real text layout.set_text(text, -1) if not max_char_number: adjust_font_size(layout, fd, width_adjust*width, height_adjust*height) # draw ink, logical = layout.get_extents() ctx.save() ctx.set_source_rgba(*text_color) if align == Pango.Alignment.CENTER: x = x - (ink.width/2.0)/Pango.SCALE - int(float(ink.x)/Pango.SCALE) y = y - (ink.height/2.0)/Pango.SCALE - int(float(ink.y)/Pango.SCALE) else: y = y - (ink.height/2.0)/Pango.SCALE - ink.y/Pango.SCALE ctx.translate(x, y) if align == Pango.Alignment.LEFT: # Hack to workaround what appears to be a Cairo bug: without # drawing a rectangle here, the translation above is not taken # into account for rendering the text. ctx.rectangle(0, 0, 0, 0) PangoCairo.update_layout(ctx, layout) PangoCairo.show_layout(ctx, layout) ctx.restore()
def canvas_draw(self, widget, cr): """ draw the word centered in the window. """ w, h = self.draw_size # center of the text cx, cy = w/2., h/2. # circle cr.set_line_width(9) cr.set_source_rgb(1.0, 1.0, 1.0) ctx = pangocairo.create_context(cr) layout = pango.Layout.new(ctx) prefixlayout = pango.Layout.new(ctx) desc = pango.font_description_from_string(self.font) for l in (layout, prefixlayout): l.set_font_description(desc) txt = self.current_word center = self.current_center if len(txt) > 0: markup = "%s<span foreground='red'>%s</span>%s" % tuple( t.replace("<", "<").replace(">", ">") for t in (txt[:center], txt[center], txt[center+1:])) prefix = txt[:center] else: markup = "" prefix = "" e, attr, txt, accel = pango.parse_markup(markup, -1, '\x00') layout.set_text(txt, -1) layout.set_attributes(attr) prefixlayout.set_text(prefix, -1) pangocairo.update_layout(cr, layout) pangocairo.update_layout(cr, prefixlayout) # text metrics _, txth = (x/1024. for x in layout.get_size()) prew, _ = (x/1024. for x in prefixlayout.get_size()) cr.move_to(cx - (prew), cy - txth/2) # render the text pangocairo.show_layout(cr, layout)
def pangocairo_create_context(cr): """ If python-gi-cairo is not installed, using PangoCairo.create_context dies with an unhelpful KeyError, check for that and output somethig useful. """ # TODO move this to core.backend try: return PangoCairo.create_context(cr) except KeyError as e: if e.args == ('could not find foreign type Context',): raise ShoebotInstallError("Error creating PangoCairo missing dependency: python-gi-cairo") else: raise
def pangocairo_create_context(cr): """ If python-gi-cairo is not installed, using PangoCairo.create_context dies with an unhelpful KeyError, check for that and output somethig useful. """ # TODO move this to core.backend try: return PangoCairo.create_context(cr) except KeyError as e: if e.args == ('could not find foreign type Context',): raise ShoebotInstallError("Error creating PangoCairo missing dependency: python-gi-cairo") else: raise
def canvas_draw(self, widget, cr): """ draw the word centered in the window. """ w, h = self.draw_size # center of the text cx, cy = w / 2., h / 2. # circle cr.set_line_width(9) cr.set_source_rgb(1.0, 1.0, 1.0) ctx = pangocairo.create_context(cr) layout = pango.Layout.new(ctx) prefixlayout = pango.Layout.new(ctx) desc = pango.font_description_from_string(self.font) for l in (layout, prefixlayout): l.set_font_description(desc) txt = self.current_word center = self.current_center if len(txt) > 0: markup = "%s<span foreground='red'>%s</span>%s" % tuple( t.replace("<", "<").replace(">", ">") for t in (txt[:center], txt[center], txt[center + 1:])) prefix = txt[:center] else: markup = "" prefix = "" e, attr, txt, accel = pango.parse_markup(markup, -1, '\x00') layout.set_text(txt, -1) layout.set_attributes(attr) prefixlayout.set_text(prefix, -1) pangocairo.update_layout(cr, layout) pangocairo.update_layout(cr, prefixlayout) # text metrics _, txth = (x / 1024. for x in layout.get_size()) prew, _ = (x / 1024. for x in prefixlayout.get_size()) cr.move_to(cx - (prew), cy - txth / 2) # render the text pangocairo.show_layout(cr, layout)
def pangocairo_create_context(cr): """ Create a PangoCairo context from a given pycairo context. If python-gi-cairo is not installed PangoCairo.create_context dies with an unhelpful KeyError, output a better error if that happens. """ # TODO move this to core.backend try: return PangoCairo.create_context(cr) except KeyError as e: if e.args == ("could not find foreign type Context", ): raise ShoebotInstallError( "Error creating PangoCairo missing dependency: python-gi-cairo" ) raise
def _marker(self, color, txt, lat, lon, ctx, dpi): marker_path = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', 'images', 'marker.svg')) fp = open(marker_path, 'r') data = fp.read() fp.close() if color[0] != '#': c = Color(color) color = c.hex_l data = data.replace('#000000', color) rsvg = Rsvg.Handle() svg = rsvg.new_from_data(data.encode()) x, y = self._latlon2xy(lat, lon, dpi) scale = (50.0 / svg.props.height) * (dpi / 72.0) x -= svg.props.width * scale / 2 y -= svg.props.height * scale ctx.save() ctx.translate(x, y) ctx.scale(scale, scale) svg.render_cairo(ctx) pc = PangoCairo.create_context(ctx) layout = PangoCairo.create_layout(ctx) fd = Pango.FontDescription('Droid Sans') fd.set_size(Pango.SCALE) layout.set_font_description(fd) layout.set_text(txt, -1) draw_utils.adjust_font_size(layout, fd, svg.props.width / 3, svg.props.width / 3) ink, logical = layout.get_extents() ctx.translate(svg.props.width / 2 - logical.width / svg.props.height, svg.props.height / 5) PangoCairo.update_layout(ctx, layout) PangoCairo.show_layout(ctx, layout) ctx.restore()
def __init__(self, font_family, font_size, weight=Pango.Weight.NORMAL, export_dir=None): font_descr = Pango.FontDescription() font_descr.set_family(font_family) font_descr.set_size(font_size * Pango.SCALE) font_descr.set_weight(weight) self._nullbuffer = bytearray(self.WIDTH*self.HEIGHT) self._buffer = bytearray(self.WIDTH*self.HEIGHT) self._surface = cairo.ImageSurface.create_for_data( self._buffer, cairo.FORMAT_A8, self.WIDTH, self.HEIGHT, self.WIDTH) self._cairo = cairo.Context(self._surface) self._pango = PangoCairo.create_context(self._cairo) PangoCairo.context_set_resolution(self._pango, 72) # self._pango.set_resolution(72.) self._layout = Pango.Layout(self._pango) self._layout.set_font_description(font_descr) self._export_dir = export_dir
def cairo_draws(widget, mything): # mything is the Cairo context. width = widget.get_allocated_width() mything.set_source_rgba(0, 0, 0, 1) # Sets the text colour. if rotation == 1: mything.translate(width - 2, 20) mything.rotate(math.radians(90)) else: mything.translate(10, 2) mypc = PangoCairo.create_context(mything) mypc.set_base_gravity( 4 ) # Pango gravity. South = 0, East = 1, North = 2, West = 3, Auto = 4. mylayout = Pango.Layout.new(mypc) mylayout.set_font_description( Pango.FontDescription("%s %s %s" % (font, fontstyle, fontsize))) mylayout.set_text(usertext, -1) PangoCairo.show_layout(mything, mylayout)
def use_pango_font(font, start, count, will_call_prepost=False): import gi gi.require_version('Pango', '1.0') gi.require_version('PangoCairo', '1.0') from gi.repository import Pango from gi.repository import PangoCairo #from gi.repository import Cairo as cairo import cairo fontDesc = Pango.FontDescription(font) a = array.array('b', itertools.repeat(0, 256 * 256)) surface = cairo.ImageSurface.create_for_data(a, cairo.FORMAT_A8, 256, 256) context = cairo.Context(surface) pango_context = PangoCairo.create_context(context) layout = PangoCairo.create_layout(context) fontmap = PangoCairo.font_map_get_default() font = fontmap.load_font(fontmap.create_context(), fontDesc) layout.set_font_description(fontDesc) metrics = font.get_metrics() descent = metrics.get_descent() d = descent / Pango.SCALE linespace = metrics.get_ascent() + metrics.get_descent() width = metrics.get_approximate_char_width() glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT) glPixelStorei(GL_UNPACK_SWAP_BYTES, 0) glPixelStorei(GL_UNPACK_LSB_FIRST, 1) glPixelStorei(GL_UNPACK_ROW_LENGTH, 256) glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 256) glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0) glPixelStorei(GL_UNPACK_SKIP_ROWS, 0) glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0) glPixelStorei(GL_UNPACK_ALIGNMENT, 1) glPixelZoom(1, -1) base = glGenLists(count) for i in range(count): ch = chr(start + i) layout.set_text(ch, -1) w, h = layout.get_size() context.save() context.new_path() context.rectangle(0, 0, 256, 256) context.set_source_rgba(0., 0., 0., 0.) context.set_operator(cairo.OPERATOR_SOURCE) context.paint() context.restore() context.save() context.set_source_rgba(1., 1., 1., 1.) context.set_operator(cairo.OPERATOR_SOURCE) context.move_to(0, 0) PangoCairo.update_context(context, pango_context) PangoCairo.show_layout(context, layout) context.restore() w, h = int(w / Pango.SCALE), int(h / Pango.SCALE) glNewList(base + i, GL_COMPILE) glBitmap(0, 0, 0, 0, 0, h - d, ''.encode()) #glDrawPixels(0, 0, 0, 0, 0, h-d, ''); if not will_call_prepost: pango_font_pre() if w and h: try: pass glDrawPixels(w, h, GL_LUMINANCE, GL_UNSIGNED_BYTE, a.tostring()) except Exception as e: print("glnav Exception ", e) glBitmap(0, 0, 0, 0, w, -h + d, ''.encode()) if not will_call_prepost: pango_font_post() glEndList() glPopClientAttrib() return base, int(width / Pango.SCALE), int(linespace / Pango.SCALE)
def render(self, dpi=UTILS.PT_PER_INCH): self.ctx.save() # Create a PangoCairo context for drawing to Cairo pc = PangoCairo.create_context(self.ctx) city_fd = Pango.FontDescription("DejaVu Sans Condensed Bold 18") header_fd = Pango.FontDescription("DejaVu Sans Condensed Bold 12") label_column_fd = Pango.FontDescription("DejaVu 6") city_layout, city_fascent, city_fheight, city_em = \ self._create_layout_with_font(self.ctx, pc, city_fd) header_layout, header_fascent, header_fheight, header_em = \ self._create_layout_with_font(self.ctx, pc, header_fd) label_layout, label_fascent, label_fheight, label_em = \ self._create_layout_with_font(self.ctx, pc, label_column_fd) column_layout, _, _, _ = \ self._create_layout_with_font(self.ctx, pc, label_column_fd) # By OCitysmap's convention, the default resolution is 72 dpi, # which maps to the default pangocairo resolution (96 dpi # according to pangocairo docs). If we want to render with # another resolution (different from 72), we have to scale the # pangocairo resolution accordingly: PangoCairo.context_set_resolution(city_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) PangoCairo.context_set_resolution(column_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) PangoCairo.context_set_resolution(label_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) PangoCairo.context_set_resolution(header_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) margin = label_em cityBlockHeight = city_fheight * 2 index_area_w_pt = self.rendering_area_w - 2 * self.print_bleed_pt - self.margin_inside_pt - self.margin_outside_pt city_layout.set_width( int(UTILS.convert_pt_to_dots((index_area_w_pt) * Pango.SCALE, dpi))) self._draw_page_stroke() self._draw_page_content_stroke() citiesWithEntries = { k: v for k, v in self.index_categories.items() if len(v) > 0 } # print only cities with entries cities = list(citiesWithEntries.keys()) cities.sort() margin_top = self.print_bleed_pt + self.margin_top_bottom_pt margin_top_page = margin_top offset_y = margin_top_page max_drawing_height = 0 city_index = -1 LOG.debug( "%f print_bleed_pt, %f print_safe_margin_pt, %f margin_top_bottom_pt, %f margin_top" % (self.print_bleed_pt, self.print_safe_margin_pt, self.margin_top_bottom_pt, margin_top)) page_full_available_h = self.rendering_area_h - 2 * self.print_bleed_pt - 2 * self.margin_top_bottom_pt content_width = self.rendering_area_w - 2 * Renderer.PRINT_BLEED_PT content_height = self.rendering_area_h - 2 * Renderer.PRINT_BLEED_PT margin_x = self.print_bleed_pt margin_y = self.print_bleed_pt if Renderer.DEBUG: # red stroke dash1: show area excluding bleed-difference self.ctx.save() self.ctx.set_source_rgba(1, 0.4, 0.4, .75) self.ctx.set_dash([1.0, 1.0], 1.0 / 2.0) self.ctx.rectangle(margin_x, margin_y, content_width, content_height) self.ctx.stroke() self.ctx.restore() content_width -= self.margin_inside_pt + self.margin_outside_pt content_height -= 2 * self.margin_top_bottom_pt margin_x += (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt) margin_y += self.margin_top_bottom_pt #LOG.debug(list(filter(lambda x: len(self.index_categories[cities[x]]) > 0, cities))) for city in cities: city_index = city_index + 1 margin_top_page += max_drawing_height # add max drawing height of previous city index_area_h_pt = self.rendering_area_h - self.print_bleed_pt - self.margin_top_bottom_pt - margin_top_page LOG.debug("============") LOG.debug( "printing index for city '%s'. available area: %f x %f, margin_top_page: %f" % (city, index_area_w_pt, index_area_h_pt, margin_top_page)) if (margin_top_page > (page_full_available_h * 4 / 5)): LOG.debug("NEW PAGE: margin_top_page (%f) > %f" % (margin_top_page, page_full_available_h * 4 / 5)) self._new_page() self._draw_page_stroke() self._draw_page_content_stroke() margin_top = self.print_bleed_pt + self.margin_top_bottom_pt margin_top_page = margin_top index_area_h_pt = self.rendering_area_h - self.print_bleed_pt - self.margin_top_bottom_pt - margin_top_page # full page height available now with this new page. city_header_height = 0 if len(cities) > 1: city_header_height = self._draw_page_header( self._i18n.isrtl(), self.ctx, pc, city_layout, UTILS.convert_pt_to_dots(city_fascent, dpi), UTILS.convert_pt_to_dots(cityBlockHeight, dpi), UTILS.convert_pt_to_dots( self.rendering_area_x + self.print_bleed_pt + (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt), dpi), UTILS.convert_pt_to_dots( margin_top_page + header_fascent / 2, dpi ), # baseline_y, original: self.rendering_area_y + header_fascent margin_top_page, #margin_top city) index_area_h_pt -= city_header_height margin_top_page += city_header_height # find largest label and location max_label_drawing_width = 0.0 max_location_drawing_width = 0.0 if False: self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) self.index_categories[city].append( self.index_categories[city][0]) if False: # Alle Kategorien, bis auf 1 entfernen while len(self.index_categories[city]) > 1: self.index_categories[city].pop(1) # Alle Einträge, bis auf 1, dieser Kategorie entferne #while len(self.index_categories[city][0].items)>4: # self.index_categories[city][0].items.pop(1) # Bei dieser Kategorie weitere Einträge hinzufügen for i in range(40): self.index_categories[city][0].items.append( self.index_categories[city][0].items[3]) # calculate height of categories #LOG.debug("number of categories: %d" % len(self.index_categories[city])) #LOG.debug("number of entries in first category: %d" % len(self.index_categories[city][0].items)) sum_height = 0 for category in self.index_categories[city]: sum_height += category.label_drawing_height(header_layout) #LOG.debug("adding height %f for category %s" % (category.label_drawing_height(header_layout), category.name)) for street in category.items: #LOG.debug("label_drawing_height of %s: %f" % (street.label, street.label_drawing_height(label_layout))) sum_height += street.label_drawing_height(label_layout) w = street.label_drawing_width(label_layout) if w > max_label_drawing_width: max_label_drawing_width = w #LOG.debug("new max_label_drawing_width: %f (%s)" % (max_label_drawing_width, street.label)) w = street.location_drawing_width(label_layout) if w > max_location_drawing_width: max_location_drawing_width = w #LOG.debug("new max_location_drawing_width: %f (%s)" % (max_location_drawing_width, street.location_str)) col_max_height = math.ceil(float(sum_height) / 4) + 40 LOG.debug( "sum_height: %f, %f per column (4) => col_max_height: %d" % (sum_height, float(sum_height) / 4, col_max_height)) # No street to render, bail out if max_label_drawing_width == 0.0: return #LOG.debug("max_label_drawing_width: %f" % max_label_drawing_width) #LOG.debug("max_location_drawing_width: %f" % max_location_drawing_width) # Find best number of columns # max_drawing_width = max_label_drawing_width + max_location_drawing_width + 2 * margin max_drawing_height = col_max_height needed_drawing_height = max_drawing_height if (index_area_h_pt < max_drawing_height): LOG.debug( "more height neededed (max_drawing_height: %f), than there is available on this page left (index_area_h_pt: %f). Setting max_drawing_height to index_area_h_pt" % (max_drawing_height, index_area_h_pt)) max_drawing_height = index_area_h_pt if Renderer.DEBUG: # green stroke dash3: show printable page area (after header) LOG.debug("Index - printable area (after header): %f x %f" % (index_area_w_pt, max_drawing_height)) self.ctx.save() self.ctx.set_source_rgba(0, 1, 0, .75) self.ctx.set_dash([3.0, 3.0], 3.0 / 2.0) self.ctx.rectangle( self.print_bleed_pt + (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt), margin_top_page, index_area_w_pt, max_drawing_height) self.ctx.stroke() self.ctx.restore() #LOG.debug("max_drawing_width: %f" % max_drawing_width) #columns_count = int(math.ceil(index_area_w_pt / max_drawing_width)) # following test should not be needed. No time to prove it. ;-) #if columns_count == 0: # columns_count = 1 #LOG.debug("number of columns: %d" % columns_count) columns_count = 4 # Gerald: fixed to 4. # We have now have several columns column_width = index_area_w_pt / columns_count #LOG.debug("column_width: %d" % column_width) column_layout.set_width( int( UTILS.convert_pt_to_dots( (column_width - margin - 5) * Pango.SCALE, dpi))) label_layout.set_width( int( UTILS.convert_pt_to_dots( (column_width - margin - max_location_drawing_width - 2 * label_em) * Pango.SCALE, dpi))) header_layout.set_width( int( UTILS.convert_pt_to_dots( (column_width - margin) * Pango.SCALE, dpi))) if not self._i18n.isrtl(): orig_offset_x = offset_x = margin / 2. orig_delta_x = delta_x = column_width else: orig_offset_x = offset_x = index_area_w_pt - column_width + margin / 2. orig_delta_x = delta_x = -column_width actual_n_cols = 0 # page number of first page self._draw_page_number() if Renderer.DEBUG: # light pink stroke dash 4: full column # temp: show index-area (inside grayed margin) LOG.debug( "pink: %f w -> index_area_w_pt, %f h -> index_area_h_pt" % (index_area_w_pt, index_area_h_pt)) self.ctx.save() self.ctx.set_source_rgba(.85, .25, .85, .75) self.ctx.set_dash([4.0, 4.0], 4.0 / 2.0) self.ctx.rectangle( self.print_bleed_pt + (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt) + (city_index % 4) * column_width, margin_top_page, column_width, max_drawing_height) self.ctx.stroke() self.ctx.restore() offset_y = margin_top_page # each city/category starts on the corresponding margin for category in self.index_categories[city]: if (offset_y + header_fheight + label_fheight + margin / 2. > (max_drawing_height + margin_top_page)): offset_y = margin_top_page offset_x += delta_x actual_n_cols += 1 if actual_n_cols == columns_count: self._new_page() self._draw_page_stroke() self._draw_page_content_stroke() actual_n_cols = 0 city_header_height = 0 # no city-header on the additional city-pages margin_top_page = margin_top offset_y = margin_top_page offset_x = orig_offset_x delta_x = orig_delta_x max_drawing_height = needed_drawing_height - max_drawing_height + margin_top_page # OR index_area_h_pt => its a new page, full index_area_h_pt is available now # LOG.debug( "NEW PAGE (before category %s). actual_n_cols == columns_count (%d). needed_drawing_height %d, max_drawing_height %d" % (category.name, columns_count, needed_drawing_height, max_drawing_height)) if Renderer.DEBUG: self.ctx.save() self.ctx.set_source_rgba(1, 0, 0, .75) self.ctx.set_dash([8.0, 8.0], 8.0 / 2.0) self.ctx.rectangle( self.rendering_area_x + (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt) + offset_x, offset_y, column_width, max_drawing_height) self.ctx.stroke() self.ctx.restore() category_height = category.label_drawing_height(header_layout) #LOG.debug("category %s, height draw %d | %d | %d | %d" % (category.name, category_height, header_fascent, UTILS.convert_pt_to_dots(header_fascent, dpi), header_fheight)) category.draw( self._i18n.isrtl(), self.ctx, pc, header_layout, UTILS.convert_pt_to_dots(header_fascent, dpi), UTILS.convert_pt_to_dots(header_fheight, dpi), UTILS.convert_pt_to_dots( self.rendering_area_x + (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt) + offset_x, dpi), UTILS.convert_pt_to_dots( self.rendering_area_y + offset_y + header_fascent, dpi)) offset_y += category_height for street in category.items: label_height = street.label_drawing_height(label_layout) if (offset_y + label_height + margin / 2. > (max_drawing_height + margin_top_page)): offset_y = margin_top_page offset_x += delta_x actual_n_cols += 1 if actual_n_cols == columns_count: LOG.debug( "NEW PAGE (before street %s). actual_n_cols %d == columns_count %d" % (street.label, actual_n_cols, columns_count)) self._new_page() self._draw_page_stroke() self._draw_page_content_stroke() actual_n_cols = 0 city_header_height = 0 margin_top_page = margin_top offset_y = margin_top_page offset_x = orig_offset_x delta_x = orig_delta_x max_drawing_height = needed_drawing_height - max_drawing_height + margin_top_page if Renderer.DEBUG: self.ctx.save() self.ctx.set_source_rgba(1, 0, 0, .75) self.ctx.set_dash([8.0, 8.0], 8.0 / 2.0) self.ctx.rectangle( self.rendering_area_x + (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt) + offset_x, offset_y, column_width, max_drawing_height) self.ctx.stroke() self.ctx.restore() self.ctx.set_source_rgb(0, 0, 0) if (street.color is None): self.ctx.set_source_rgb(0, 0, 0) else: color = tuple( int(street.color.lstrip('#')[i:i + 2], 16) / 255. for i in (0, 2, 4)) # draw colorized rectangle next to street self.ctx.save() self.ctx.set_source_rgb(color[0], color[1], color[2]) self.ctx.rectangle( self.rendering_area_x + (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt) + offset_x, self.rendering_area_y + offset_y, 5, label_height) self.ctx.fill() self.ctx.restore() # alternative: colorize street-text: # self.ctx.set_source_rgb(color[0],color[1],color[2]) street.draw( self._i18n.isrtl(), self.ctx, pc, column_layout, UTILS.convert_pt_to_dots(label_fascent, dpi), UTILS.convert_pt_to_dots(label_fheight, dpi), UTILS.convert_pt_to_dots( self.rendering_area_x + 5 + (self.margin_inside_pt if (self.index_page_num + self.page_offset) % 2 else self.margin_outside_pt) + offset_x, dpi), UTILS.convert_pt_to_dots( self.rendering_area_y + offset_y + label_fascent, dpi), label_layout, UTILS.convert_pt_to_dots(label_height, dpi), UTILS.convert_pt_to_dots(max_location_drawing_width, dpi)) offset_y += label_height self.ctx.restore()
def render_text(self, ctx): """ Add legion height to a Cairo surface """ if not self.show_height: return ctx.identity_matrix() ctx.scale\ ( ctx.get_target().get_width() , ctx.get_target().get_height() ) ctx.set_source_rgb(*self.font_bg_color) ctx.rectangle\ ( self.font_fg.location.x , self.font_fg.location.y , self.font_fg.width , self.font_fg.height ) ctx.fill() print("-----------------------------------") cairo_fontoptions = cairo.FontOptions() cairo_fontoptions.set_hint_metrics(cairo.HINT_METRICS_OFF) cairo_fontoptions.set_hint_style(cairo.HINT_STYLE_NONE) ctx.set_font_options(cairo_fontoptions) ctx.identity_matrix() pango_context = PangoCairo.create_context(ctx) l1 = Pango.Layout(pango_context) l1.set_font_description(self.font_description) l1.set_text("8",1) e1 = l1.get_extents()[0] print(e1.x / Pango.SCALE, e1.y / Pango.SCALE, e1.width / Pango.SCALE, e1.height / Pango.SCALE) PangoCairo.context_set_resolution(pango_context, 192) l1 = Pango.Layout(pango_context) l1.set_font_description(self.font_description) l1.set_text("8",1) e1 = l1.get_extents()[0] print(e1.x / Pango.SCALE, e1.y / Pango.SCALE, e1.width / Pango.SCALE, e1.height / Pango.SCALE) PangoCairo.context_set_resolution(pango_context, 48) l1 = Pango.Layout(pango_context) l1.set_font_description(self.font_description) l1.set_text("8",1) e1 = l1.get_extents()[0] print(e1.x / Pango.SCALE, e1.y / Pango.SCALE, e1.width / Pango.SCALE, e1.height / Pango.SCALE) PangoCairo.context_set_resolution(pango_context, 5) l1 = Pango.Layout(pango_context) l1.set_font_description(self.font_description) l1.set_text("8",1) e1 = l1.get_extents()[0] print(e1.x / Pango.SCALE, e1.y / Pango.SCALE, e1.width / Pango.SCALE, e1.height / Pango.SCALE) PangoCairo.context_set_resolution(pango_context, 96) l1 = Pango.Layout(pango_context) l1.set_font_description(self.font_description) l1.set_text("8",1) e1 = l1.get_extents()[0] print(e1.x / Pango.SCALE, e1.y / Pango.SCALE, e1.width / Pango.SCALE, e1.height / Pango.SCALE) print("-----------------------------------") ctx.set_font_options(cairo_fontoptions) ctx.identity_matrix() ctx.scale(*(1,)*2) layout = PangoCairo.create_layout(ctx) layout.set_font_description(self.font_description) layout.set_text("8",1) e1 = layout.get_extents()[0] print(e1.x / Pango.SCALE, e1.y / Pango.SCALE, e1.width / Pango.SCALE, e1.height / Pango.SCALE) ctx.identity_matrix() ctx.scale(*(10,)*2) layout = PangoCairo.create_layout(ctx) layout.set_font_description(self.font_description) layout.set_text("8",1) e1 = layout.get_extents()[0] print(e1.x / Pango.SCALE, e1.y / Pango.SCALE, e1.width / Pango.SCALE, e1.height / Pango.SCALE) ctx.identity_matrix() ctx.scale(*(1/25,)*2) layout = PangoCairo.create_layout(ctx) layout.set_font_description(self.font_description) layout.set_text("8",1) e1 = layout.get_extents()[0] print(e1.x / Pango.SCALE, e1.y / Pango.SCALE, e1.width / Pango.SCALE, e1.height / Pango.SCALE) print("-----------------------------------") ctx.identity_matrix() pango_context = PangoCairo.create_context(ctx) PangoCairo.context_set_resolution(pango_context, self.font_scale * 96) #layout = PangoCairo.create_layout(ctx) layout = Pango.Layout.new(pango_context) layout.set_font_description(self.font_description) layout.set_text(str(len(self.legion)), 1) #dimensions = get_max_device_ink_size("0123456789", PangoCairo.create_context(ctx)) #r1 = Rectangle((0,0), dimensions) #r1.scale_inscribe(self.font_limits) #r1[1:1] = self.font_limits[1:1] #print(r1) r = layout.get_extents()[0] rx = r.x / Pango.SCALE ry = r.y / Pango.SCALE rw = r.width / Pango.SCALE rh = r.height / Pango.SCALE print(rx, ry, rw, rh) print(self.font_fg, self.font_fg / self.font_scale) print(self.font_limits) ctx.move_to(self.font_fg.location.x*135, self.font_fg.location.y*135) #ctx.scale(self.font_scale, self.font_scale) ctx.set_source_rgb(*self.font_fg_color) PangoCairo.show_layout(ctx, layout)
def set_canvas(self, canvas): G15Text.set_canvas(self, canvas) self.__pango_cairo_context = pangocairo.create_context(self.canvas)
def renderText( text, font, variation ): """Render a unicode text into 32xH image and return a numpy array""" # global stats text = choice( leftPaddingChoices ) + text # print("'"+text+"'") surface = cairo.ImageSurface(cairo.FORMAT_A8, canvasWidth, imageHeight ) context = cairo.Context(surface) pc = PangoCairo.create_context(context) layout = PangoCairo.create_layout(context) if( font in fontDescCache ): fontDesc = fontDescCache[font] else: fontDesc = fontDescCache[font] = Pango.font_description_from_string( font ) layout.set_font_description(Pango.FontDescription( font )) layout.set_markup( '<span>' + text +'</span>', -1 ); inkRect, _ = layout.get_pixel_extents() actualW = inkRect.width; actualH = inkRect.height if( variation == VARIATIONS.fit_height ): # stats['fit'] +=1 # Fit-height is done by varying font size fontDesc = layout.get_font_description() fontDesc.set_size( int(fontDesc.get_size() * 0.9 * imageHeight/ actualH ) ) layout.set_font_description( fontDesc ) inkRect, _ = layout.get_pixel_extents() actualW = inkRect.width; actualH = inkRect.height elif( variation == VARIATIONS.random): # stats['tw'] +=1 # Random alignment means random rotation twist = choice( TWIST_CHOICES ) context.rotate( twist * math.atan( ( imageHeight - actualH )/ actualW ) ) if twist < 0: context.translate(0, imageHeight - actualH ) elif( variation == VARIATIONS.alighn_top ): # stats['top'] +=1 context.translate(0, -2) # Random alignment means random rotation # context.translate(0, imageHeight - actualH ) elif( variation == VARIATIONS.alighn_bottom ): # stats['bot'] +=1 # Random alignment means random rotation context.translate(0, imageHeight - actualH ) PangoCairo.show_layout(context, layout) data = surface.get_data() # import ipdb; ipdb.set_trace() if( actualW > targetW ): # print(' actualW > targetW ') # Resize image by shrinking the width of image data = np.frombuffer(data, dtype=np.uint8).reshape(( imageHeight, canvasWidth ))[:, :actualW + 10] data = Image.fromarray( data ).resize( ( targetW, imageHeight ), Image.BILINEAR ) data = np.array( data ) else: data = np.frombuffer(data, dtype=np.uint8).reshape(( imageHeight, canvasWidth ))[:, :targetW] data = np.invert( data ) # becuase pango will render white text on black bg = choice( bgChoices ) data = (data*bg).astype( np.uint8 ) # Add create a noise layer and merge with image ncc = choice(noiseSDChoices) # ncc = noiseSDChoices[ i % len( noiseSDChoices ) ] # print( ncc ) data = np.clip( data - random.normal( *ncc, data.shape ), 0, 255 ).astype(np.uint8) # import ipdb; ipdb.set_trace() # data = np.clip( data - random.multivariate_normal( [ ncc[0] ], [[ ncc[1]]], data.shape ).squeeze(), 0, 255 ).astype(np.uint8) return data
def do_tool_operation(self, operation): cairo_context = self.start_tool_operation(operation) font_fam = operation['font_fam'] font_size = operation['font_size'] * 2 entire_text = operation['text'] background_color = operation['rgba2'] text_x = operation['x'] text_y = operation['y'] font_description_string = font_fam if operation['is_italic']: font_description_string += " Italic" if operation['is_bold']: font_description_string += " Bold" font_description_string += " " + str(font_size) font = Pango.FontDescription(font_description_string) p_context = PangoCairo.create_context(cairo_context) layout = Pango.Layout(p_context) layout.set_font_description(font) if not operation['antialias']: font_options = cairo.FontOptions() font_options.set_antialias(cairo.Antialias.NONE) font_options.set_hint_metrics(cairo.HintMetrics.OFF) font_options.set_hint_style(cairo.HintStyle.FULL) PangoCairo.context_set_font_options(p_context, font_options) ######################################################################## # Draw background ###################################################### if operation['background'] == 'rectangle': lines = entire_text.split('\n') line_y = text_y for line_text in lines: line_y = self._op_bg_rectangle(cairo_context, layout, \ background_color, text_x, line_y, line_text) elif operation['background'] == 'shadow': dist = max(min(int(font_size / 16), 4), 1) cairo_context.set_source_rgba(*background_color) self._show_text_at_coords(cairo_context, layout, entire_text, \ text_x + dist, text_y + dist) elif operation['background'] == 'thin-outline': cairo_context.set_source_rgba(*background_color) dist = min(int(font_size / 16), 10) dist = max(dist, 2) for dx in range(-dist, dist): for dy in range(-dist, dist): if abs(dx) + abs(dy) <= dist * 1.5: self._show_text_at_coords(cairo_context, layout, \ entire_text, text_x + dx, text_y + dy) # these `for`s and this `if` should outline with an octogonal shape, # which is close enough to a smooth round outline imho. elif operation['background'] == 'thick-outline': cairo_context.set_source_rgba(*background_color) dist = int(font_size / 10) dist = max(dist, 2) for dx in range(-dist, dist): for dy in range(-dist, dist): if abs(dx) + abs(dy) <= dist * 1.5: self._show_text_at_coords(cairo_context, layout, \ entire_text, text_x + dx, text_y + dy) # looks better, but so much computation for bullshit... ######################################################################## # Draw text ############################################################ cairo_context.set_source_rgba(*operation['rgba1']) self._show_text_at_coords(cairo_context, layout, entire_text, \ text_x, text_y) self.non_destructive_show_modif()
def render(self, ctx, p_rendering_area, dpi=UTILS.PT_PER_INCH): """ Render the street and amenities index at the given (x,y) coordinates into the provided Cairo surface. The index must not be larger than the provided surface (use precompute_occupation_area() to adjust it). Args: ctx (cairo.Context): the cairo context to use for the rendering. rendering_area (IndexRenderingArea): the result from precompute_occupation_area(). dpi (number): resolution of the target device. """ rendering_area = p_rendering_area rendering_area.x = rendering_area.x + 1 rendering_area.y = rendering_area.y + 1 rendering_area.w = rendering_area.w - 2 rendering_area.h = rendering_area.h - 2 if not self._index_categories: raise commons.IndexEmptyError LOG.debug("Rendering the street index within %s at %sdpi..." % (rendering_area, dpi)) ## ## In the following, the algorithm only manipulates values ## expressed in 'pt'. Only the drawing-related functions will ## translate them to cairo units ## ctx.save() ctx.move_to(UTILS.convert_pt_to_dots(rendering_area.x, dpi), UTILS.convert_pt_to_dots(rendering_area.y, dpi)) # Create a PangoCairo context for drawing to Cairo pc = PangoCairo.create_context(ctx) header_fd = Pango.FontDescription( rendering_area.rendering_style.header_font_spec) label_fd = Pango.FontDescription( rendering_area.rendering_style.label_font_spec) header_layout, header_fascent, header_fheight, header_em = \ draw_utils.create_layout_with_font(ctx, header_fd) label_layout, label_fascent, label_fheight, label_em = \ draw_utils.create_layout_with_font(ctx, label_fd) #print "RENDER", header_layout, header_fascent, header_fheight, header_em #print "RENDER", label_layout, label_fascent, label_fheight, label_em # By OCitysmap's convention, the default resolution is 72 dpi, # which maps to the default pangocairo resolution (96 dpi # according to pangocairo docs). If we want to render with # another resolution (different from 72), we have to scale the # pangocairo resolution accordingly: PangoCairo.context_set_resolution(label_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) PangoCairo.context_set_resolution(header_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) # All this is because we want pango to have the exact same # behavior as with the default 72dpi resolution. If we instead # decided to call cairo::scale, then pango might choose # different font metrics which don't fit in the prepared # layout anymore... margin = label_em column_width = int(rendering_area.w / rendering_area.n_cols) label_layout.set_width( int( UTILS.convert_pt_to_dots((column_width - margin) * Pango.SCALE, dpi))) header_layout.set_width( int( UTILS.convert_pt_to_dots((column_width - margin) * Pango.SCALE, dpi))) if not self._i18n.isrtl(): offset_x = margin / 2. delta_x = column_width else: offset_x = rendering_area.w - column_width + margin / 2. delta_x = -column_width actual_n_cols = 1 offset_y = margin / 2. for category in self._index_categories: if (offset_y + header_fheight + label_fheight + margin / 2. > rendering_area.h): offset_y = margin / 2. offset_x += delta_x actual_n_cols += 1 height = category.draw( self._i18n.isrtl(), ctx, pc, header_layout, UTILS.convert_pt_to_dots(header_fascent, dpi), UTILS.convert_pt_to_dots(header_fheight, dpi), UTILS.convert_pt_to_dots(rendering_area.x + offset_x, dpi), UTILS.convert_pt_to_dots( rendering_area.y + offset_y + header_fascent, dpi)) offset_y += height * 72.0 / dpi for street in category.items: if (offset_y + label_fheight + margin / 2. > rendering_area.h): offset_y = margin / 2. offset_x += delta_x actual_n_cols += 1 street.draw( self._i18n.isrtl(), ctx, pc, label_layout, UTILS.convert_pt_to_dots(label_fascent, dpi), UTILS.convert_pt_to_dots(label_fheight, dpi), UTILS.convert_pt_to_dots(rendering_area.x + offset_x, dpi), UTILS.convert_pt_to_dots( rendering_area.y + offset_y + label_fascent, dpi)) offset_y += label_fheight # Restore original context ctx.restore() # Simple verification... if actual_n_cols < rendering_area.n_cols: LOG.warning( "Rounding/security margin lost some space (%d actual cols vs. allocated %d" % (actual_n_cols, rendering_area.n_cols)) if actual_n_cols > rendering_area.n_cols: LOG.warning( "Rounding/security margin used more space (%d actual cols vs. allocated %d" % (actual_n_cols, rendering_area.n_cols))
def render(self, dpi=UTILS.PT_PER_INCH): self.ctx.save() # Create a PangoCairo context for drawing to Cairo pc = PangoCairo.create_context(self.ctx) header_fd = Pango.FontDescription("Georgia Bold 12") label_column_fd = Pango.FontDescription("DejaVu 6") header_layout, header_fascent, header_fheight, header_em = \ self._create_layout_with_font(self.ctx, pc, header_fd) label_layout, label_fascent, label_fheight, label_em = \ self._create_layout_with_font(self.ctx, pc, label_column_fd) column_layout, _, _, _ = \ self._create_layout_with_font(self.ctx, pc, label_column_fd) # By OCitysmap's convention, the default resolution is 72 dpi, # which maps to the default pangocairo resolution (96 dpi # according to pangocairo docs). If we want to render with # another resolution (different from 72), we have to scale the # pangocairo resolution accordingly: PangoCairo.context_set_resolution(column_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) PangoCairo.context_set_resolution(label_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) PangoCairo.context_set_resolution(header_layout.get_context(), 96. * dpi / UTILS.PT_PER_INCH) margin = label_em # find largest label and location max_label_drawing_width = 0.0 max_location_drawing_width = 0.0 for category in self.index_categories: for street in category.items: w = street.label_drawing_width(label_layout) if w > max_label_drawing_width: max_label_drawing_width = w w = street.location_drawing_width(label_layout) if w > max_location_drawing_width: max_location_drawing_width = w # No street to render, bail out if max_label_drawing_width == 0.0: return # Find best number of columns max_drawing_width = \ max_label_drawing_width + max_location_drawing_width + 2 * margin max_drawing_height = self.rendering_area_h - PAGE_NUMBER_MARGIN_PT columns_count = int( math.ceil(self.rendering_area_w / max_drawing_width)) # following test should not be needed. No time to prove it. ;-) if columns_count == 0: columns_count = 1 # We have now have several columns column_width = self.rendering_area_w / columns_count column_layout.set_width( int( UTILS.convert_pt_to_dots((column_width - margin) * Pango.SCALE, dpi))) label_layout.set_width( int( UTILS.convert_pt_to_dots( (column_width - margin - max_location_drawing_width - 2 * label_em) * Pango.SCALE, dpi))) header_layout.set_width( int( UTILS.convert_pt_to_dots((column_width - margin) * Pango.SCALE, dpi))) if not self._i18n.isrtl(): orig_offset_x = offset_x = margin / 2. orig_delta_x = delta_x = column_width else: orig_offset_x = offset_x = \ self.rendering_area_w - column_width + margin/2. orig_delta_x = delta_x = -column_width actual_n_cols = 0 offset_y = margin / 2. # page number of first page self._draw_page_number() for category in self.index_categories: if (offset_y + header_fheight + label_fheight + margin / 2. > max_drawing_height): offset_y = margin / 2. offset_x += delta_x actual_n_cols += 1 if actual_n_cols == columns_count: self._new_page() actual_n_cols = 0 offset_y = margin / 2. offset_x = orig_offset_x delta_x = orig_delta_x category.draw( self._i18n.isrtl(), self.ctx, pc, header_layout, UTILS.convert_pt_to_dots(header_fascent, dpi), UTILS.convert_pt_to_dots(header_fheight, dpi), UTILS.convert_pt_to_dots(self.rendering_area_x + offset_x, dpi), UTILS.convert_pt_to_dots( self.rendering_area_y + offset_y + header_fascent, dpi)) offset_y += header_fheight for street in category.items: label_height = street.label_drawing_height(label_layout) if (offset_y + label_height + margin / 2. > max_drawing_height): offset_y = margin / 2. offset_x += delta_x actual_n_cols += 1 if actual_n_cols == columns_count: self._new_page() actual_n_cols = 0 offset_y = margin / 2. offset_x = orig_offset_x delta_x = orig_delta_x street.draw( self._i18n.isrtl(), self.ctx, pc, column_layout, UTILS.convert_pt_to_dots(label_fascent, dpi), UTILS.convert_pt_to_dots(label_fheight, dpi), UTILS.convert_pt_to_dots(self.rendering_area_x + offset_x, dpi), UTILS.convert_pt_to_dots( self.rendering_area_y + offset_y + label_fascent, dpi), label_layout, UTILS.convert_pt_to_dots(label_height, dpi), UTILS.convert_pt_to_dots(max_location_drawing_width, dpi)) offset_y += label_height self.ctx.restore()
def _draw_copyright_notice(self, ctx, w_dots, h_dots, notice=None, osm_date=None): """ Draw a copyright notice at current location and within the given w_dots*h_dots rectangle. Args: ctx (cairo.Context): The Cairo context to use to draw. w_dots,h_dots (number): Rectangle dimension (ciaro units). font_face (str): Pango font specification. notice (str): Optional notice to replace the default. """ today = datetime.date.today() if notice is None: notice = _(u'Copyright © %(year)d MapOSMatic/OCitySMap developers.') notice+= ' ' notice+= _(u'Map data © %(year)d OpenStreetMap contributors (see http://osm.org/copyright)') notice+= '\n' annotations = [] if self.rc.stylesheet.annotation != '': annotations.append(self.rc.stylesheet.annotation) for overlay in self._overlays: if overlay.annotation != '': annotations.append(overlay.annotation) if len(annotations) > 0: notice+= _(u'Map styles:') notice+= ' ' + '; '.join(annotations) + '\n' datasources = set() if self.rc.stylesheet.datasource != '': datasources.add(self.rc.stylesheet.datasource) for overlay in self._overlays: if overlay.datasource != '': datasources.add(overlay.datasource) if len(datasources) > 0: notice+= _(u'Additional data sources:') notice+= ' ' + '; '.join(list(datasources)) + '\n' notice+= _(u'Map rendered on: %(date)s. OSM data updated on: %(osmdate)s.') notice+= ' ' notice+= _(u'The map may be incomplete or inaccurate.') # We need the correct locale to be set for strftime(). prev_locale = locale.getlocale(locale.LC_TIME) try: locale.setlocale(locale.LC_TIME, self.rc.i18n.language_code()) except Exception: LOG.warning('error while setting LC_COLLATE to "%s"' % self.rc.i18n.language_code()) try: if osm_date is None: osm_date_str = _(u'unknown') else: osm_date_str = osm_date.strftime("%d %B %Y %H:%M") notice = notice % {'year': today.year, 'date': today.strftime("%d %B %Y"), 'osmdate': osm_date_str} finally: locale.setlocale(locale.LC_TIME, prev_locale) ctx.save() pc = PangoCairo.create_context(ctx) fd = Pango.FontDescription('DejaVu') fd.set_size(Pango.SCALE) layout = PangoCairo.create_layout(ctx) layout.set_font_description(fd) layout.set_text(notice, -1) draw_utils.adjust_font_size(layout, fd, w_dots, h_dots) PangoCairo.update_layout(ctx, layout) PangoCairo.show_layout(ctx, layout) ctx.restore()
def _draw_title(self, ctx, w_dots, h_dots, font_face): """ Draw the title at the current position inside a w_dots*h_dots rectangle. Args: ctx (cairo.Context): The Cairo context to use to draw. w_dots,h_dots (number): Rectangle dimension (ciaro units) font_face (str): Pango font specification. """ # Title background ctx.save() ctx.set_source_rgb(0.8, 0.9, 0.96) # TODO: make title bar color configurable? ctx.rectangle(0, 0, w_dots, h_dots) ctx.fill() ctx.restore() # Retrieve and paint the OSM logo ctx.save() grp, logo_width = self._get_osm_logo(ctx, 0.8*h_dots) if grp: ctx.translate(w_dots - logo_width - 0.1*h_dots, 0.1*h_dots) ctx.set_source(grp) ctx.paint_with_alpha(0.5) else: LOG.warning("OSM Logo not available.") logo_width = 0 ctx.restore() # Retrieve and paint the extra logo # TODO: logo_width2 = 0 if self.rc.poi_file: ctx.save() grp, logo_width2 = self._get_extra_logo(ctx, 0.8*h_dots) if grp: ctx.translate(0.4*h_dots, 0.1*h_dots) ctx.set_source(grp) ctx.paint_with_alpha(0.5) logo_width2 += 0.4*h_dots else: LOG.warning("Extra Logo not available.") logo_width2 = 0 ctx.restore() # Prepare the title pc = PangoCairo.create_context(ctx) layout = PangoCairo.create_layout(ctx) layout.set_width(int((w_dots - 0.1*w_dots - logo_width - logo_width2) * Pango.SCALE)) if not self.rc.i18n.isrtl(): layout.set_alignment(Pango.Alignment.LEFT) else: layout.set_alignment(Pango.Alignment.RIGHT) fd = Pango.FontDescription(font_face) fd.set_size(Pango.SCALE) layout.set_font_description(fd) layout.set_text(self.rc.title, -1) draw_utils.adjust_font_size(layout, fd, layout.get_width(), 0.8*h_dots) # Draw the title ctx.save() ctx.set_line_width(1) ctx.rectangle(0, 0, w_dots, h_dots) ctx.stroke() ctx.translate(0.4*h_dots + logo_width2, (h_dots - (layout.get_size()[1] / Pango.SCALE)) / 2.0) PangoCairo.update_layout(ctx, layout) PangoCairo.show_layout(ctx, layout) ctx.restore()
self.location_str = "%d, %s" % (self.page_number, self.location_str) if __name__ == "__main__": import cairo gi.require_version('PangoCairo', '1.0') from gi.repository import PangoCairo surface = cairo.PDFSurface('/tmp/idx_commons.pdf', 1000, 1000) ctx = cairo.Context(surface) pc = PangoCairo.create_context(ctx) font_desc = Pango.FontDescription('DejaVu') font_desc.set_size(12 * Pango.SCALE) layout = PangoCairo.create_layout(ctx) layout.set_font_description(font_desc) layout.set_width(200 * Pango.SCALE) font = layout.get_context().load_font(font_desc) font_metric = font.get_metrics() fascent = font_metric.get_ascent() / Pango.SCALE fheight = ((font_metric.get_ascent() + font_metric.get_descent()) / Pango.SCALE)
def precompute_occupation_area(self, surface, x, y, w, h, freedom_direction, alignment): """Prepare to render the street and amenities index at the given (x,y) coordinates into the provided Cairo surface. The index must not be larger than the provided width and height (in pixels). Nothing will be drawn on surface. Parameters ---------- surface : cairo.Surface The cairo surface to render into. x : int Horizontal origin position, in pixels. y : int Vertical origin position, in pixels. w : int Maximum usable width for the index, in dots (Cairo unit). h : int Maximum usable height for the index, in dots (Cairo unit). freedom_direction : str Freedom direction, can be 'width' or 'height'. See _compute_columns_split for more details. alignment : str 'top' or 'bottom' for a freedom_direction of 'height', 'left' or 'right' for 'width'. Tells which side to stick the index to. Returns ------- Returns the actual graphical IndexRenderingArea defining how and where the index should be rendered. Raise IndexDoesNotFitError when the provided area's surface is not enough to hold the index. """ if ((freedom_direction == 'height' and alignment not in ('top', 'bottom')) or (freedom_direction == 'width' and alignment not in ('left', 'right'))): raise ValueError('Incompatible freedom direction and alignment!') if not self._index_categories: raise commons.IndexEmptyError LOG.debug( "Determining index area within %dx%d+%d+%d aligned %s/%s..." % (w, h, x, y, alignment, freedom_direction)) # Create a PangoCairo context for drawing to Cairo ctx = cairo.Context(surface) pc = PangoCairo.create_context(ctx) # Iterate over the rendering_styles until we find a suitable layout rendering_style = None for rs in self._rendering_styles: LOG.debug("Trying index fit using %s..." % rs) try: n_cols, min_dimension \ = self._compute_columns_split(ctx, pc, rs, w, h, freedom_direction) # Great: index did fit OK ! rendering_style = rs break except IndexDoesNotFitError: # Index did not fit => try smaller... LOG.debug("Index %s too large: should try a smaller one." % rs) continue # Index really did not fit with any of the rendering styles ? if not rendering_style: raise IndexDoesNotFitError("Index does not fit in area") # Realign at bottom/top left/right if freedom_direction == 'height': index_width = w index_height = min_dimension elif freedom_direction == 'width': index_width = min_dimension index_height = h base_offset_x = 0 base_offset_y = 0 if alignment == 'bottom': base_offset_y = h - index_height if alignment == 'right': base_offset_x = w - index_width area = IndexRenderingArea(rendering_style, x + base_offset_x, y + base_offset_y, index_width, index_height, n_cols) LOG.debug("Will be able to render index in %s" % area) return area